Răsfoiți Sursa

Source HTTP API ID path patterns (#26623)

Kit Langton 3 luni în urmă
părinte
comite
67b9c9c027

+ 5 - 5
packages/opencode/specs/openapi-translation-cleanup.md

@@ -105,11 +105,11 @@ Verification:
 
 Concrete first targets:
 
-- `sessionID`
-- `messageID`
-- `partID`
-- `permissionID`
-- `ptyID`
+- `[x]` `sessionID`
+- `[x]` `messageID`
+- `[x]` `partID`
+- `[x]` `permissionID`
+- `[x]` `ptyID`
 
 Leave ambiguous route-local `id` overrides for workspace routes until they are renamed or explicitly typed in endpoint params.
 

+ 3 - 1
packages/opencode/src/pty/schema.ts

@@ -4,7 +4,9 @@ import { Identifier } from "@/id/id"
 import { zod, ZodOverride } from "@opencode-ai/core/effect-zod"
 import { withStatics } from "@opencode-ai/core/schema"
 
-const ptyIdSchema = Schema.String.annotate({ [ZodOverride]: Identifier.schema("pty") }).pipe(Schema.brand("PtyID"))
+const ptyIdSchema = Schema.String.check(Schema.isStartsWith("pty"))
+  .annotate({ [ZodOverride]: Identifier.schema("pty") })
+  .pipe(Schema.brand("PtyID"))
 
 export type PtyID = typeof ptyIdSchema.Type
 

+ 0 - 11
packages/opencode/src/server/routes/instance/httpapi/public.ts

@@ -69,14 +69,6 @@ const QueryParameterSchemas: Record<string, OpenApiSchema> = {
   "GET /api/session/{sessionID}/message limit": { type: "number" },
 }
 
-const PathParameterSchemas: Record<string, OpenApiSchema> = {
-  sessionID: { type: "string", pattern: "^ses.*" },
-  messageID: { type: "string", pattern: "^msg.*" },
-  partID: { type: "string", pattern: "^prt.*" },
-  permissionID: { type: "string", pattern: "^per.*" },
-  ptyID: { type: "string", pattern: "^pty.*" },
-}
-
 const LegacyComponentDescriptions: Record<string, string> = {
   LogLevel: "Log level",
   ServerConfig: "Server configuration for opencode serve and web commands",
@@ -506,11 +498,8 @@ function normalizeParameter(param: OpenApiParameter, route: string) {
 }
 
 function pathParameterSchema(route: string, name: string) {
-  if (name in PathParameterSchemas) return PathParameterSchemas[name]
   if (name === "id" && route.startsWith("DELETE /experimental/workspace/")) return { type: "string", pattern: "^wrk.*" }
   if (name === "id" && route.startsWith("POST /experimental/workspace/")) return { type: "string", pattern: "^wrk.*" }
-  if (name === "requestID" && route.startsWith("POST /permission/")) return { type: "string", pattern: "^per.*" }
-  if (name === "requestID" && route.startsWith("POST /question/")) return { type: "string", pattern: "^que.*" }
   return undefined
 }
 

+ 33 - 21
packages/opencode/src/session/schema.ts

@@ -4,32 +4,44 @@ import { Identifier } from "@/id/id"
 import { zod, ZodOverride } from "@opencode-ai/core/effect-zod"
 import { withStatics } from "@opencode-ai/core/schema"
 
-export const SessionID = Schema.String.annotate({ [ZodOverride]: Identifier.schema("session") }).pipe(
-  Schema.brand("SessionID"),
-  withStatics((s) => ({
-    descending: (id?: string) => s.make(Identifier.descending("session", id)),
-    zod: zod(s),
-  })),
-)
+export const SessionID = Schema.String.check(Schema.isStartsWith("ses"))
+  .annotate({
+    [ZodOverride]: Identifier.schema("session"),
+  })
+  .pipe(
+    Schema.brand("SessionID"),
+    withStatics((s) => ({
+      descending: (id?: string) => s.make(Identifier.descending("session", id)),
+      zod: zod(s),
+    })),
+  )
 
 export type SessionID = Schema.Schema.Type<typeof SessionID>
 
-export const MessageID = Schema.String.annotate({ [ZodOverride]: Identifier.schema("message") }).pipe(
-  Schema.brand("MessageID"),
-  withStatics((s) => ({
-    ascending: (id?: string) => s.make(Identifier.ascending("message", id)),
-    zod: zod(s),
-  })),
-)
+export const MessageID = Schema.String.check(Schema.isStartsWith("msg"))
+  .annotate({
+    [ZodOverride]: Identifier.schema("message"),
+  })
+  .pipe(
+    Schema.brand("MessageID"),
+    withStatics((s) => ({
+      ascending: (id?: string) => s.make(Identifier.ascending("message", id)),
+      zod: zod(s),
+    })),
+  )
 
 export type MessageID = Schema.Schema.Type<typeof MessageID>
 
-export const PartID = Schema.String.annotate({ [ZodOverride]: Identifier.schema("part") }).pipe(
-  Schema.brand("PartID"),
-  withStatics((s) => ({
-    ascending: (id?: string) => s.make(Identifier.ascending("part", id)),
-    zod: zod(s),
-  })),
-)
+export const PartID = Schema.String.check(Schema.isStartsWith("prt"))
+  .annotate({
+    [ZodOverride]: Identifier.schema("part"),
+  })
+  .pipe(
+    Schema.brand("PartID"),
+    withStatics((s) => ({
+      ascending: (id?: string) => s.make(Identifier.ascending("part", id)),
+      zod: zod(s),
+    })),
+  )
 
 export type PartID = Schema.Schema.Type<typeof PartID>

+ 12 - 12
packages/opencode/test/cli/github-action.test.ts

@@ -7,8 +7,8 @@ import { SessionID, MessageID, PartID } from "../../src/session/schema"
 function createTextPart(text: string): MessageV2.Part {
   return {
     id: PartID.ascending(),
-    sessionID: SessionID.make("s"),
-    messageID: MessageID.make("m"),
+    sessionID: SessionID.make("ses_test"),
+    messageID: MessageID.make("msg_test"),
     type: "text" as const,
     text,
   }
@@ -17,8 +17,8 @@ function createTextPart(text: string): MessageV2.Part {
 function createReasoningPart(text: string): MessageV2.Part {
   return {
     id: PartID.ascending(),
-    sessionID: SessionID.make("s"),
-    messageID: MessageID.make("m"),
+    sessionID: SessionID.make("ses_test"),
+    messageID: MessageID.make("msg_test"),
     type: "reasoning" as const,
     text,
     time: { start: 0 },
@@ -29,8 +29,8 @@ function createToolPart(tool: string, title: string, status: "completed" | "runn
   if (status === "completed") {
     return {
       id: PartID.ascending(),
-      sessionID: SessionID.make("s"),
-      messageID: MessageID.make("m"),
+      sessionID: SessionID.make("ses_test"),
+      messageID: MessageID.make("msg_test"),
       type: "tool" as const,
       callID: "c1",
       tool,
@@ -46,8 +46,8 @@ function createToolPart(tool: string, title: string, status: "completed" | "runn
   }
   return {
     id: PartID.ascending(),
-    sessionID: SessionID.make("s"),
-    messageID: MessageID.make("m"),
+    sessionID: SessionID.make("ses_test"),
+    messageID: MessageID.make("msg_test"),
     type: "tool" as const,
     callID: "c1",
     tool,
@@ -62,8 +62,8 @@ function createToolPart(tool: string, title: string, status: "completed" | "runn
 function createStepStartPart(): MessageV2.Part {
   return {
     id: PartID.ascending(),
-    sessionID: SessionID.make("s"),
-    messageID: MessageID.make("m"),
+    sessionID: SessionID.make("ses_test"),
+    messageID: MessageID.make("msg_test"),
     type: "step-start" as const,
   }
 }
@@ -71,8 +71,8 @@ function createStepStartPart(): MessageV2.Part {
 function createStepFinishPart(): MessageV2.Part {
   return {
     id: PartID.ascending(),
-    sessionID: SessionID.make("s"),
-    messageID: MessageID.make("m"),
+    sessionID: SessionID.make("ses_test"),
+    messageID: MessageID.make("msg_test"),
     type: "step-finish" as const,
     reason: "done",
     cost: 0,

+ 7 - 6
packages/opencode/test/project/migrate-global.test.ts

@@ -22,8 +22,9 @@ function run<A>(fn: (svc: Project.Interface) => Effect.Effect<A>) {
   )
 }
 
-function uid() {
-  return SessionID.make(crypto.randomUUID())
+function legacySessionID() {
+  // Global-session migration covers persisted IDs from before prefixed session IDs.
+  return crypto.randomUUID() as SessionID
 }
 
 function seed(opts: { id: SessionID; dir: string; project: ProjectID }) {
@@ -73,7 +74,7 @@ describe("migrateFromGlobal", () => {
     expect(pre.id).toBe(ProjectID.global)
 
     // 2. Seed a session under "global" with matching directory
-    const id = uid()
+    const id = legacySessionID()
     seed({ id, dir: tmp.path, project: ProjectID.global })
 
     // 3. Make a commit so the project gets a real ID
@@ -100,7 +101,7 @@ describe("migrateFromGlobal", () => {
     // 3. Seed a session under "global" with matching directory.
     //    This simulates a session created before git init that wasn't
     //    present when the real project row was first created.
-    const id = uid()
+    const id = legacySessionID()
     seed({ id, dir: tmp.path, project: ProjectID.global })
 
     // 4. Call fromDirectory again — project row already exists,
@@ -121,7 +122,7 @@ describe("migrateFromGlobal", () => {
 
     // Legacy sessions may lack a directory value.
     // Without a matching origin directory, they should remain global.
-    const id = uid()
+    const id = legacySessionID()
     seed({ id, dir: "", project: ProjectID.global })
 
     await run((svc) => svc.fromDirectory(tmp.path))
@@ -139,7 +140,7 @@ describe("migrateFromGlobal", () => {
     ensureGlobal()
 
     // Seed a session under "global" but for a DIFFERENT directory
-    const id = uid()
+    const id = legacySessionID()
     seed({ id, dir: "/some/other/dir", project: ProjectID.global })
 
     await run((svc) => svc.fromDirectory(tmp.path))

+ 34 - 1
packages/opencode/test/server/httpapi-query-schema-drift.test.ts

@@ -22,6 +22,7 @@ import {
   MessagesQuery,
   SessionPaths,
 } from "../../src/server/routes/instance/httpapi/groups/session"
+import { PtyPaths } from "../../src/server/routes/instance/httpapi/groups/pty"
 import { MessagesQuery as V2MessagesQuery } from "../../src/server/routes/instance/httpapi/groups/v2/message"
 import { SessionsQuery as V2SessionsQuery } from "../../src/server/routes/instance/httpapi/groups/v2/session"
 import { QueryBoolean } from "../../src/server/routes/instance/httpapi/groups/query"
@@ -33,7 +34,12 @@ const originalWorkspaces = Flag.OPENCODE_EXPERIMENTAL_WORKSPACES
 
 type Method = "get" | "post" | "put" | "delete" | "patch"
 type QuerySchema = { readonly fields: Record<string, unknown> }
-type OpenApiSchema = { readonly maximum?: number; readonly minimum?: number; readonly type?: string }
+type OpenApiSchema = {
+  readonly maximum?: number
+  readonly minimum?: number
+  readonly pattern?: string
+  readonly type?: string
+}
 type OpenApiParameter = { readonly name: string; readonly in: string; readonly schema?: OpenApiSchema }
 type OpenApiOperation = { readonly parameters?: readonly OpenApiParameter[] }
 
@@ -68,6 +74,16 @@ const numericSdkQueryParams = [
   { method: "get", path: "/api/session/:sessionID/message", name: "limit", schema: { type: "number" } },
 ] satisfies Array<{ method: Method; path: string; name: string; schema: OpenApiSchema }>
 
+const pathParamPatterns = [
+  { method: "get", path: SessionPaths.get, name: "sessionID", pattern: "^ses" },
+  { method: "get", path: SessionPaths.message, name: "messageID", pattern: "^msg" },
+  { method: "patch", path: SessionPaths.updatePart, name: "partID", pattern: "^prt" },
+  { method: "post", path: SessionPaths.permissions, name: "permissionID", pattern: "^per" },
+  { method: "post", path: "/permission/:requestID/reply", name: "requestID", pattern: "^per" },
+  { method: "post", path: "/question/:requestID/reply", name: "requestID", pattern: "^que" },
+  { method: "put", path: PtyPaths.update, name: "ptyID", pattern: "^pty" },
+] satisfies Array<{ method: Method; path: string; name: string; pattern: string }>
+
 function app() {
   return Server.Default().app
 }
@@ -98,6 +114,10 @@ function queryParameter(operation: OpenApiOperation | undefined, name: string) {
   return (operation?.parameters ?? []).find((param) => param.in === "query" && param.name === name)
 }
 
+function pathParameter(operation: OpenApiOperation | undefined, name: string) {
+  return (operation?.parameters ?? []).find((param) => param.in === "path" && param.name === name)
+}
+
 function assertAdvertisedQueryParamsAreRuntimeFields(input: {
   readonly method: Method
   readonly operation: OpenApiOperation | undefined
@@ -173,6 +193,19 @@ describe("httpapi query schema drift", () => {
     }),
   )
 
+  it.effect(
+    "OpenAPI path parameter patterns come from runtime schemas",
+    Effect.sync(() => {
+      const spec = OpenApi.fromApi(PublicApi)
+      for (const expected of pathParamPatterns) {
+        expect(
+          pathParameter(spec.paths[openApiPath(expected.path)]?.[expected.method], expected.name)?.schema,
+          `${expected.method.toUpperCase()} ${expected.path} ${expected.name}`,
+        ).toEqual({ type: "string", pattern: expected.pattern })
+      }
+    }),
+  )
+
   it.effect(
     "drift assertion catches spec-only workspace query params",
     Effect.sync(() => {

+ 1 - 1
packages/opencode/test/server/httpapi-ui.test.ts

@@ -287,7 +287,7 @@ describe("HttpApi UI fallback", () => {
   })
 
   test("keeps matched API routes ahead of the UI fallback", async () => {
-    const response = await Server.Default().app.request("/session/nope")
+    const response = await Server.Default().app.request("/session/ses_nope")
 
     expect(response.status).toBe(404)
   })

+ 8 - 8
packages/opencode/test/session/instruction.test.ts

@@ -61,7 +61,7 @@ const tmpWithFiles = (files: Record<string, string>) =>
 
 function loaded(filepath: string): MessageV2.WithParts[] {
   const sessionID = SessionID.make("session-loaded-1")
-  const messageID = MessageID.make("message-loaded-1")
+  const messageID = MessageID.make("msg_message-loaded-1")
 
   return [
     {
@@ -78,7 +78,7 @@ function loaded(filepath: string): MessageV2.WithParts[] {
       },
       parts: [
         {
-          id: PartID.make("part-loaded-1"),
+          id: PartID.make("prt_part-loaded-1"),
           messageID,
           sessionID,
           type: "tool",
@@ -106,7 +106,7 @@ describe("Instruction.resolve", () => {
         const system = yield* svc.systemPaths()
         expect(system.has(path.join(dir, "AGENTS.md"))).toBe(true)
 
-        const results = yield* svc.resolve([], path.join(dir, "src", "file.ts"), MessageID.make("message-test-1"))
+        const results = yield* svc.resolve([], path.join(dir, "src", "file.ts"), MessageID.make("msg_message-test-1"))
         expect(results).toEqual([])
       }),
     ),
@@ -122,7 +122,7 @@ describe("Instruction.resolve", () => {
         const results = yield* svc.resolve(
           [],
           path.join(dir, "subdir", "nested", "file.ts"),
-          MessageID.make("message-test-2"),
+          MessageID.make("msg_message-test-2"),
         )
         expect(results.length).toBe(1)
         expect(results[0].filepath).toBe(path.join(dir, "subdir", "AGENTS.md"))
@@ -138,7 +138,7 @@ describe("Instruction.resolve", () => {
         const system = yield* svc.systemPaths()
         expect(system.has(filepath)).toBe(false)
 
-        const results = yield* svc.resolve([], filepath, MessageID.make("message-test-3"))
+        const results = yield* svc.resolve([], filepath, MessageID.make("msg_message-test-3"))
         expect(results).toEqual([])
       }),
     ),
@@ -149,7 +149,7 @@ describe("Instruction.resolve", () => {
       Effect.gen(function* () {
         const svc = yield* Instruction.Service
         const filepath = path.join(dir, "subdir", "nested", "file.ts")
-        const id = MessageID.make("message-claim-1")
+        const id = MessageID.make("msg_message-claim-1")
 
         const first = yield* svc.resolve([], filepath, id)
         const second = yield* svc.resolve([], filepath, id)
@@ -166,7 +166,7 @@ describe("Instruction.resolve", () => {
       Effect.gen(function* () {
         const svc = yield* Instruction.Service
         const filepath = path.join(dir, "subdir", "nested", "file.ts")
-        const id = MessageID.make("message-claim-2")
+        const id = MessageID.make("msg_message-claim-2")
 
         const first = yield* svc.resolve([], filepath, id)
         yield* svc.clear(id)
@@ -185,7 +185,7 @@ describe("Instruction.resolve", () => {
         const svc = yield* Instruction.Service
         const agents = path.join(dir, "subdir", "AGENTS.md")
         const filepath = path.join(dir, "subdir", "nested", "file.ts")
-        const id = MessageID.make("message-claim-3")
+        const id = MessageID.make("msg_message-claim-3")
 
         const results = yield* svc.resolve(loaded(agents), filepath, id)
         expect(results).toEqual([])

+ 8 - 8
packages/opencode/test/session/llm.test.ts

@@ -354,7 +354,7 @@ describe("session.llm.stream", () => {
         } satisfies Agent.Info
 
         const user = {
-          id: MessageID.make("user-1"),
+          id: MessageID.make("msg_user-1"),
           sessionID,
           role: "user",
           time: { created: Date.now() },
@@ -438,7 +438,7 @@ describe("session.llm.stream", () => {
           permission: [{ permission: "*", pattern: "*", action: "allow" }],
         } satisfies Agent.Info
         const user = {
-          id: MessageID.make("user-service-abort"),
+          id: MessageID.make("msg_user-service-abort"),
           sessionID,
           role: "user",
           time: { created: Date.now() },
@@ -529,7 +529,7 @@ describe("session.llm.stream", () => {
         } satisfies Agent.Info
 
         const user = {
-          id: MessageID.make("user-tools"),
+          id: MessageID.make("msg_user-tools"),
           sessionID,
           role: "user",
           time: { created: Date.now() },
@@ -644,7 +644,7 @@ describe("session.llm.stream", () => {
         } satisfies Agent.Info
 
         const user = {
-          id: MessageID.make("user-2"),
+          id: MessageID.make("msg_user-2"),
           sessionID,
           role: "user",
           time: { created: Date.now() },
@@ -759,7 +759,7 @@ describe("session.llm.stream", () => {
         } satisfies Agent.Info
 
         const user = {
-          id: MessageID.make("user-data-url"),
+          id: MessageID.make("msg_user-data-url"),
           sessionID,
           role: "user",
           time: { created: Date.now() },
@@ -880,7 +880,7 @@ describe("session.llm.stream", () => {
         } satisfies Agent.Info
 
         const user = {
-          id: MessageID.make("user-3"),
+          id: MessageID.make("msg_user-3"),
           sessionID,
           role: "user",
           time: { created: Date.now() },
@@ -995,7 +995,7 @@ describe("session.llm.stream", () => {
           permission: [{ permission: "*", pattern: "*", action: "allow" }],
         } satisfies Agent.Info
         const user = {
-          id: MessageID.make("user-anthropic-tools"),
+          id: MessageID.make("msg_user-anthropic-tools"),
           sessionID,
           role: "user",
           time: { created: Date.now() },
@@ -1239,7 +1239,7 @@ describe("session.llm.stream", () => {
         } satisfies Agent.Info
 
         const user = {
-          id: MessageID.make("user-4"),
+          id: MessageID.make("msg_user-4"),
           sessionID,
           role: "user",
           time: { created: Date.now() },

+ 2 - 2
packages/opencode/test/session/message-v2.test.ts

@@ -102,9 +102,9 @@ function assistantInfo(
 
 function basePart(messageID: string, id: string) {
   return {
-    id: PartID.make(id),
+    id: PartID.make(id.startsWith("prt") ? id : `prt_${id}`),
     sessionID,
-    messageID: MessageID.make(messageID),
+    messageID: MessageID.make(messageID.startsWith("msg") ? messageID : `msg_${messageID}`),
   }
 }
 

+ 1 - 1
packages/opencode/test/tool/apply_patch.test.ts

@@ -27,7 +27,7 @@ const runtime = ManagedRuntime.make(
 
 const baseCtx = {
   sessionID: SessionID.make("ses_test"),
-  messageID: MessageID.make(""),
+  messageID: MessageID.make("msg_test"),
   callID: "",
   agent: "build",
   abort: AbortSignal.any([]),

+ 1 - 1
packages/opencode/test/tool/edit.test.ts

@@ -17,7 +17,7 @@ import { SessionID, MessageID } from "../../src/session/schema"
 
 const ctx = {
   sessionID: SessionID.make("ses_test-edit-session"),
-  messageID: MessageID.make(""),
+  messageID: MessageID.make("msg_test"),
   callID: "",
   agent: "build",
   abort: AbortSignal.any([]),

+ 1 - 1
packages/opencode/test/tool/external-directory.test.ts

@@ -12,7 +12,7 @@ import { SessionID, MessageID } from "../../src/session/schema"
 
 const baseCtx: Omit<Tool.Context, "ask"> = {
   sessionID: SessionID.make("ses_test"),
-  messageID: MessageID.make(""),
+  messageID: MessageID.make("msg_test"),
   callID: "",
   agent: "build",
   abort: AbortSignal.any([]),

+ 1 - 1
packages/opencode/test/tool/glob.test.ts

@@ -23,7 +23,7 @@ const it = testEffect(
 
 const ctx = {
   sessionID: SessionID.make("ses_test"),
-  messageID: MessageID.make(""),
+  messageID: MessageID.make("msg_test"),
   callID: "",
   agent: "build",
   abort: AbortSignal.any([]),

+ 1 - 1
packages/opencode/test/tool/grep.test.ts

@@ -23,7 +23,7 @@ const it = testEffect(
 
 const ctx = {
   sessionID: SessionID.make("ses_test"),
-  messageID: MessageID.make(""),
+  messageID: MessageID.make("msg_test"),
   callID: "",
   agent: "build",
   abort: AbortSignal.any([]),

+ 1 - 1
packages/opencode/test/tool/lsp.test.ts

@@ -20,7 +20,7 @@ afterEach(async () => {
 
 const ctx = {
   sessionID: SessionID.make("ses_test"),
-  messageID: MessageID.make(""),
+  messageID: MessageID.make("msg_test"),
   callID: "",
   agent: "build",
   abort: AbortSignal.any([]),

+ 1 - 1
packages/opencode/test/tool/question.test.ts

@@ -10,7 +10,7 @@ import { testEffect } from "../lib/effect"
 
 const ctx = {
   sessionID: SessionID.make("ses_test-session"),
-  messageID: MessageID.make("test-message"),
+  messageID: MessageID.make("msg_test-message"),
   callID: "test-call",
   agent: "test-agent",
   abort: AbortSignal.any([]),

+ 1 - 1
packages/opencode/test/tool/read.test.ts

@@ -24,7 +24,7 @@ afterEach(async () => {
 
 const ctx = {
   sessionID: SessionID.make("ses_test"),
-  messageID: MessageID.make(""),
+  messageID: MessageID.make("msg_test"),
   callID: "",
   agent: "build",
   abort: AbortSignal.any([]),

+ 1 - 1
packages/opencode/test/tool/repo_clone.test.ts

@@ -19,7 +19,7 @@ afterEach(async () => {
 
 const ctx = {
   sessionID: SessionID.make("ses_test"),
-  messageID: MessageID.make(""),
+  messageID: MessageID.make("msg_test"),
   callID: "",
   agent: "scout",
   abort: AbortSignal.any([]),

+ 1 - 1
packages/opencode/test/tool/repo_overview.test.ts

@@ -18,7 +18,7 @@ afterEach(async () => {
 
 const ctx = {
   sessionID: SessionID.make("ses_test"),
-  messageID: MessageID.make(""),
+  messageID: MessageID.make("msg_test"),
   callID: "",
   agent: "scout",
   abort: AbortSignal.any([]),

+ 1 - 1
packages/opencode/test/tool/shell.test.ts

@@ -36,7 +36,7 @@ const initShell = initBash
 
 const ctx = {
   sessionID: SessionID.make("ses_test"),
-  messageID: MessageID.make(""),
+  messageID: MessageID.make("msg_test"),
   callID: "",
   agent: "build",
   abort: AbortSignal.any([]),

+ 1 - 1
packages/opencode/test/tool/skill.test.ts

@@ -14,7 +14,7 @@ import { testEffect } from "../lib/effect"
 
 const baseCtx: Omit<Tool.Context, "ask"> = {
   sessionID: SessionID.make("ses_test"),
-  messageID: MessageID.make(""),
+  messageID: MessageID.make("msg_test"),
   callID: "",
   agent: "build",
   abort: AbortSignal.any([]),

+ 1 - 1
packages/opencode/test/tool/webfetch.test.ts

@@ -13,7 +13,7 @@ const projectRoot = path.join(import.meta.dir, "../..")
 
 const ctx = {
   sessionID: SessionID.make("ses_test"),
-  messageID: MessageID.make("message"),
+  messageID: MessageID.make("msg_message"),
   callID: "",
   agent: "build",
   abort: AbortSignal.any([]),

+ 1 - 1
packages/opencode/test/tool/write.test.ts

@@ -18,7 +18,7 @@ import { testEffect } from "../lib/effect"
 
 const ctx = {
   sessionID: SessionID.make("ses_test-write-session"),
-  messageID: MessageID.make(""),
+  messageID: MessageID.make("msg_test"),
   callID: "",
   agent: "build",
   abort: AbortSignal.any([]),