opencode-agent[bot] преди 4 месеца
родител
ревизия
df147b65fd
променени са 25 файла, в които са добавени 971 реда и са изтрити 708 реда
  1. 6 2
      packages/opencode/src/server/routes/instance/httpapi/groups/pty.ts
  2. 43 12
      packages/opencode/src/server/routes/instance/httpapi/groups/tui.ts
  3. 1 3
      packages/opencode/src/server/routes/instance/httpapi/groups/workspace.ts
  4. 20 20
      packages/opencode/src/server/routes/instance/httpapi/handlers/config.ts
  5. 20 20
      packages/opencode/src/server/routes/instance/httpapi/handlers/control.ts
  6. 123 123
      packages/opencode/src/server/routes/instance/httpapi/handlers/experimental.ts
  7. 44 44
      packages/opencode/src/server/routes/instance/httpapi/handlers/file.ts
  8. 77 77
      packages/opencode/src/server/routes/instance/httpapi/handlers/global.ts
  9. 54 54
      packages/opencode/src/server/routes/instance/httpapi/handlers/instance.ts
  10. 51 51
      packages/opencode/src/server/routes/instance/httpapi/handlers/mcp.ts
  11. 17 17
      packages/opencode/src/server/routes/instance/httpapi/handlers/permission.ts
  12. 27 27
      packages/opencode/src/server/routes/instance/httpapi/handlers/project.ts
  13. 70 70
      packages/opencode/src/server/routes/instance/httpapi/handlers/provider.ts
  14. 3 1
      packages/opencode/src/server/routes/instance/httpapi/handlers/pty.ts
  15. 20 20
      packages/opencode/src/server/routes/instance/httpapi/handlers/question.ts
  16. 49 34
      packages/opencode/src/server/routes/instance/httpapi/handlers/session.ts
  17. 36 15
      packages/opencode/src/server/routes/instance/httpapi/instance-context.ts
  18. 4 1
      packages/opencode/src/server/routes/instance/httpapi/lifecycle.ts
  19. 21 13
      packages/opencode/src/server/routes/instance/httpapi/public.ts
  20. 7 2
      packages/opencode/src/server/server.ts
  21. 3 1
      packages/opencode/src/session/message-v2.ts
  22. 0 2
      packages/opencode/src/util/schema.ts
  23. 3 3
      packages/opencode/test/server/httpapi-bridge.test.ts
  24. 5 1
      packages/opencode/test/server/httpapi-workspace.test.ts
  25. 267 95
      packages/sdk/openapi.json

+ 6 - 2
packages/opencode/src/server/routes/instance/httpapi/groups/pty.ts

@@ -29,14 +29,18 @@ export const PtyApi = HttpApi.make("pty")
   .add(
   .add(
     HttpApiGroup.make("pty")
     HttpApiGroup.make("pty")
       .add(
       .add(
-        HttpApiEndpoint.get("shells", PtyPaths.shells, { success: described(Schema.Array(ShellItem), "List of shells") }).annotateMerge(
+        HttpApiEndpoint.get("shells", PtyPaths.shells, {
+          success: described(Schema.Array(ShellItem), "List of shells"),
+        }).annotateMerge(
           OpenApi.annotations({
           OpenApi.annotations({
             identifier: "pty.shells",
             identifier: "pty.shells",
             summary: "List available shells",
             summary: "List available shells",
             description: "Get a list of available shells on the system.",
             description: "Get a list of available shells on the system.",
           }),
           }),
         ),
         ),
-        HttpApiEndpoint.get("list", PtyPaths.list, { success: described(Schema.Array(Pty.Info), "List of sessions") }).annotateMerge(
+        HttpApiEndpoint.get("list", PtyPaths.list, {
+          success: described(Schema.Array(Pty.Info), "List of sessions"),
+        }).annotateMerge(
           OpenApi.annotations({
           OpenApi.annotations({
             identifier: "pty.list",
             identifier: "pty.list",
             summary: "List PTY sessions",
             summary: "List PTY sessions",

+ 43 - 12
packages/opencode/src/server/routes/instance/httpapi/groups/tui.ts

@@ -11,11 +11,28 @@ export const TuiRequestPayload = Schema.Struct({
   path: Schema.String,
   path: Schema.String,
   body: Schema.Unknown,
   body: Schema.Unknown,
 })
 })
-const EventTuiPromptAppend = Schema.Struct({ type: Schema.Literal(TuiEvent.PromptAppend.type), properties: TuiEvent.PromptAppend.properties }).annotate({ identifier: "EventTuiPromptAppend" })
-const EventTuiCommandExecute = Schema.Struct({ type: Schema.Literal(TuiEvent.CommandExecute.type), properties: TuiEvent.CommandExecute.properties }).annotate({ identifier: "EventTuiCommandExecute" })
-const EventTuiToastShow = Schema.Struct({ type: Schema.Literal(TuiEvent.ToastShow.type), properties: TuiEvent.ToastShow.properties }).annotate({ identifier: "EventTuiToastShow" })
-const EventTuiSessionSelect = Schema.Struct({ type: Schema.Literal(TuiEvent.SessionSelect.type), properties: TuiEvent.SessionSelect.properties }).annotate({ identifier: "EventTuiSessionSelect" })
-export const TuiPublishPayload = Schema.Union([EventTuiPromptAppend, EventTuiCommandExecute, EventTuiToastShow, EventTuiSessionSelect])
+const EventTuiPromptAppend = Schema.Struct({
+  type: Schema.Literal(TuiEvent.PromptAppend.type),
+  properties: TuiEvent.PromptAppend.properties,
+}).annotate({ identifier: "EventTuiPromptAppend" })
+const EventTuiCommandExecute = Schema.Struct({
+  type: Schema.Literal(TuiEvent.CommandExecute.type),
+  properties: TuiEvent.CommandExecute.properties,
+}).annotate({ identifier: "EventTuiCommandExecute" })
+const EventTuiToastShow = Schema.Struct({
+  type: Schema.Literal(TuiEvent.ToastShow.type),
+  properties: TuiEvent.ToastShow.properties,
+}).annotate({ identifier: "EventTuiToastShow" })
+const EventTuiSessionSelect = Schema.Struct({
+  type: Schema.Literal(TuiEvent.SessionSelect.type),
+  properties: TuiEvent.SessionSelect.properties,
+}).annotate({ identifier: "EventTuiSessionSelect" })
+export const TuiPublishPayload = Schema.Union([
+  EventTuiPromptAppend,
+  EventTuiCommandExecute,
+  EventTuiToastShow,
+  EventTuiSessionSelect,
+])
 
 
 export const TuiPaths = {
 export const TuiPaths = {
   appendPrompt: `${root}/append-prompt`,
   appendPrompt: `${root}/append-prompt`,
@@ -48,42 +65,54 @@ export const TuiApi = HttpApi.make("tui")
             description: "Append prompt to the TUI.",
             description: "Append prompt to the TUI.",
           }),
           }),
         ),
         ),
-        HttpApiEndpoint.post("openHelp", TuiPaths.openHelp, { success: described(Schema.Boolean, "Help dialog opened successfully") }).annotateMerge(
+        HttpApiEndpoint.post("openHelp", TuiPaths.openHelp, {
+          success: described(Schema.Boolean, "Help dialog opened successfully"),
+        }).annotateMerge(
           OpenApi.annotations({
           OpenApi.annotations({
             identifier: "tui.openHelp",
             identifier: "tui.openHelp",
             summary: "Open help dialog",
             summary: "Open help dialog",
             description: "Open the help dialog in the TUI to display user assistance information.",
             description: "Open the help dialog in the TUI to display user assistance information.",
           }),
           }),
         ),
         ),
-        HttpApiEndpoint.post("openSessions", TuiPaths.openSessions, { success: described(Schema.Boolean, "Session dialog opened successfully") }).annotateMerge(
+        HttpApiEndpoint.post("openSessions", TuiPaths.openSessions, {
+          success: described(Schema.Boolean, "Session dialog opened successfully"),
+        }).annotateMerge(
           OpenApi.annotations({
           OpenApi.annotations({
             identifier: "tui.openSessions",
             identifier: "tui.openSessions",
             summary: "Open sessions dialog",
             summary: "Open sessions dialog",
             description: "Open the session dialog.",
             description: "Open the session dialog.",
           }),
           }),
         ),
         ),
-        HttpApiEndpoint.post("openThemes", TuiPaths.openThemes, { success: described(Schema.Boolean, "Theme dialog opened successfully") }).annotateMerge(
+        HttpApiEndpoint.post("openThemes", TuiPaths.openThemes, {
+          success: described(Schema.Boolean, "Theme dialog opened successfully"),
+        }).annotateMerge(
           OpenApi.annotations({
           OpenApi.annotations({
             identifier: "tui.openThemes",
             identifier: "tui.openThemes",
             summary: "Open themes dialog",
             summary: "Open themes dialog",
             description: "Open the theme dialog.",
             description: "Open the theme dialog.",
           }),
           }),
         ),
         ),
-        HttpApiEndpoint.post("openModels", TuiPaths.openModels, { success: described(Schema.Boolean, "Model dialog opened successfully") }).annotateMerge(
+        HttpApiEndpoint.post("openModels", TuiPaths.openModels, {
+          success: described(Schema.Boolean, "Model dialog opened successfully"),
+        }).annotateMerge(
           OpenApi.annotations({
           OpenApi.annotations({
             identifier: "tui.openModels",
             identifier: "tui.openModels",
             summary: "Open models dialog",
             summary: "Open models dialog",
             description: "Open the model dialog.",
             description: "Open the model dialog.",
           }),
           }),
         ),
         ),
-        HttpApiEndpoint.post("submitPrompt", TuiPaths.submitPrompt, { success: described(Schema.Boolean, "Prompt submitted successfully") }).annotateMerge(
+        HttpApiEndpoint.post("submitPrompt", TuiPaths.submitPrompt, {
+          success: described(Schema.Boolean, "Prompt submitted successfully"),
+        }).annotateMerge(
           OpenApi.annotations({
           OpenApi.annotations({
             identifier: "tui.submitPrompt",
             identifier: "tui.submitPrompt",
             summary: "Submit TUI prompt",
             summary: "Submit TUI prompt",
             description: "Submit the prompt.",
             description: "Submit the prompt.",
           }),
           }),
         ),
         ),
-        HttpApiEndpoint.post("clearPrompt", TuiPaths.clearPrompt, { success: described(Schema.Boolean, "Prompt cleared successfully") }).annotateMerge(
+        HttpApiEndpoint.post("clearPrompt", TuiPaths.clearPrompt, {
+          success: described(Schema.Boolean, "Prompt cleared successfully"),
+        }).annotateMerge(
           OpenApi.annotations({
           OpenApi.annotations({
             identifier: "tui.clearPrompt",
             identifier: "tui.clearPrompt",
             summary: "Clear TUI prompt",
             summary: "Clear TUI prompt",
@@ -133,7 +162,9 @@ export const TuiApi = HttpApi.make("tui")
             description: "Navigate the TUI to display the specified session.",
             description: "Navigate the TUI to display the specified session.",
           }),
           }),
         ),
         ),
-        HttpApiEndpoint.get("controlNext", TuiPaths.controlNext, { success: described(TuiRequestPayload, "Next TUI request") }).annotateMerge(
+        HttpApiEndpoint.get("controlNext", TuiPaths.controlNext, {
+          success: described(TuiRequestPayload, "Next TUI request"),
+        }).annotateMerge(
           OpenApi.annotations({
           OpenApi.annotations({
             identifier: "tui.control.next",
             identifier: "tui.control.next",
             summary: "Get next TUI request",
             summary: "Get next TUI request",

+ 1 - 3
packages/opencode/src/server/routes/instance/httpapi/groups/workspace.ts

@@ -9,9 +9,7 @@ import { described } from "./metadata"
 
 
 const root = "/experimental/workspace"
 const root = "/experimental/workspace"
 export const CreatePayload = Schema.Struct(Struct.omit(Workspace.CreateInput.fields, ["projectID"]))
 export const CreatePayload = Schema.Struct(Struct.omit(Workspace.CreateInput.fields, ["projectID"]))
-export const SessionRestorePayload = Schema.Struct(
-  Struct.omit(Workspace.SessionRestoreInput.fields, ["workspaceID"]),
-)
+export const SessionRestorePayload = Schema.Struct(Struct.omit(Workspace.SessionRestoreInput.fields, ["workspaceID"]))
 export const SessionRestoreResponse = Schema.Struct({
 export const SessionRestoreResponse = Schema.Struct({
   total: NonNegativeInt,
   total: NonNegativeInt,
 })
 })

+ 20 - 20
packages/opencode/src/server/routes/instance/httpapi/handlers/config.ts

@@ -7,28 +7,28 @@ import { InstanceHttpApi } from "../api"
 import { markInstanceForDisposal } from "../lifecycle"
 import { markInstanceForDisposal } from "../lifecycle"
 
 
 export const configHandlers = HttpApiBuilder.group(InstanceHttpApi, "config", (handlers) =>
 export const configHandlers = HttpApiBuilder.group(InstanceHttpApi, "config", (handlers) =>
-    Effect.gen(function* () {
-      const providerSvc = yield* Provider.Service
-      const configSvc = yield* Config.Service
+  Effect.gen(function* () {
+    const providerSvc = yield* Provider.Service
+    const configSvc = yield* Config.Service
 
 
-      const get = Effect.fn("ConfigHttpApi.get")(function* () {
-        return yield* configSvc.get()
-      })
+    const get = Effect.fn("ConfigHttpApi.get")(function* () {
+      return yield* configSvc.get()
+    })
 
 
-      const update = Effect.fn("ConfigHttpApi.update")(function* (ctx) {
-        yield* configSvc.update(ctx.payload, { dispose: false })
-        yield* markInstanceForDisposal(yield* InstanceState.context)
-        return ctx.payload
-      })
+    const update = Effect.fn("ConfigHttpApi.update")(function* (ctx) {
+      yield* configSvc.update(ctx.payload, { dispose: false })
+      yield* markInstanceForDisposal(yield* InstanceState.context)
+      return ctx.payload
+    })
 
 
-      const providers = Effect.fn("ConfigHttpApi.providers")(function* () {
-        const providers = yield* providerSvc.list()
-        return {
-          providers: Object.values(providers),
-          default: Provider.defaultModelIDs(providers),
-        }
-      })
+    const providers = Effect.fn("ConfigHttpApi.providers")(function* () {
+      const providers = yield* providerSvc.list()
+      return {
+        providers: Object.values(providers),
+        default: Provider.defaultModelIDs(providers),
+      }
+    })
 
 
-      return handlers.handle("get", get).handle("update", update).handle("providers", providers)
-    }),
+    return handlers.handle("get", get).handle("update", update).handle("providers", providers)
+  }),
 )
 )

+ 20 - 20
packages/opencode/src/server/routes/instance/httpapi/handlers/control.ts

@@ -7,28 +7,28 @@ import { RootHttpApi } from "../api"
 import { LogInput } from "../groups/control"
 import { LogInput } from "../groups/control"
 
 
 export const controlHandlers = HttpApiBuilder.group(RootHttpApi, "control", (handlers) =>
 export const controlHandlers = HttpApiBuilder.group(RootHttpApi, "control", (handlers) =>
-    Effect.gen(function* () {
-      const auth = yield* Auth.Service
+  Effect.gen(function* () {
+    const auth = yield* Auth.Service
 
 
-      const authSet = Effect.fn("ControlHttpApi.authSet")(function* (ctx: {
-        params: { providerID: ProviderID }
-        payload: Auth.Info
-      }) {
-        yield* auth.set(ctx.params.providerID, ctx.payload).pipe(Effect.orDie)
-        return true
-      })
+    const authSet = Effect.fn("ControlHttpApi.authSet")(function* (ctx: {
+      params: { providerID: ProviderID }
+      payload: Auth.Info
+    }) {
+      yield* auth.set(ctx.params.providerID, ctx.payload).pipe(Effect.orDie)
+      return true
+    })
 
 
-      const authRemove = Effect.fn("ControlHttpApi.authRemove")(function* (ctx: { params: { providerID: ProviderID } }) {
-        yield* auth.remove(ctx.params.providerID).pipe(Effect.orDie)
-        return true
-      })
+    const authRemove = Effect.fn("ControlHttpApi.authRemove")(function* (ctx: { params: { providerID: ProviderID } }) {
+      yield* auth.remove(ctx.params.providerID).pipe(Effect.orDie)
+      return true
+    })
 
 
-      const log = Effect.fn("ControlHttpApi.log")(function* (ctx: { payload: typeof LogInput.Type }) {
-        const logger = Log.create({ service: ctx.payload.service })
-        logger[ctx.payload.level](ctx.payload.message, ctx.payload.extra)
-        return true
-      })
+    const log = Effect.fn("ControlHttpApi.log")(function* (ctx: { payload: typeof LogInput.Type }) {
+      const logger = Log.create({ service: ctx.payload.service })
+      logger[ctx.payload.level](ctx.payload.message, ctx.payload.extra)
+      return true
+    })
 
 
-      return handlers.handle("authSet", authSet).handle("authRemove", authRemove).handle("log", log)
-    }),
+    return handlers.handle("authSet", authSet).handle("authRemove", authRemove).handle("log", log)
+  }),
 )
 )

+ 123 - 123
packages/opencode/src/server/routes/instance/httpapi/handlers/experimental.ts

@@ -15,141 +15,141 @@ import { InstanceHttpApi } from "../api"
 import { ConsoleSwitchPayload, SessionListQuery, ToolListQuery } from "../groups/experimental"
 import { ConsoleSwitchPayload, SessionListQuery, ToolListQuery } from "../groups/experimental"
 
 
 export const experimentalHandlers = HttpApiBuilder.group(InstanceHttpApi, "experimental", (handlers) =>
 export const experimentalHandlers = HttpApiBuilder.group(InstanceHttpApi, "experimental", (handlers) =>
-    Effect.gen(function* () {
-      const account = yield* Account.Service
-      const agents = yield* Agent.Service
-      const config = yield* Config.Service
-      const mcp = yield* MCP.Service
-      const project = yield* Project.Service
-      const registry = yield* ToolRegistry.Service
-      const worktreeSvc = yield* Worktree.Service
+  Effect.gen(function* () {
+    const account = yield* Account.Service
+    const agents = yield* Agent.Service
+    const config = yield* Config.Service
+    const mcp = yield* MCP.Service
+    const project = yield* Project.Service
+    const registry = yield* ToolRegistry.Service
+    const worktreeSvc = yield* Worktree.Service
 
 
-      const getConsole = Effect.fn("ExperimentalHttpApi.console")(function* () {
-        const [state, groups] = yield* Effect.all(
-          [config.getConsoleState(), account.orgsByAccount().pipe(Effect.orDie)],
-          {
-            concurrency: "unbounded",
-          },
-        )
-        return {
-          consoleManagedProviders: state.consoleManagedProviders,
-          ...(state.activeOrgName ? { activeOrgName: state.activeOrgName } : {}),
-          switchableOrgCount: groups.reduce((count, group) => count + group.orgs.length, 0),
-        }
-      })
+    const getConsole = Effect.fn("ExperimentalHttpApi.console")(function* () {
+      const [state, groups] = yield* Effect.all(
+        [config.getConsoleState(), account.orgsByAccount().pipe(Effect.orDie)],
+        {
+          concurrency: "unbounded",
+        },
+      )
+      return {
+        consoleManagedProviders: state.consoleManagedProviders,
+        ...(state.activeOrgName ? { activeOrgName: state.activeOrgName } : {}),
+        switchableOrgCount: groups.reduce((count, group) => count + group.orgs.length, 0),
+      }
+    })
 
 
-      const listConsoleOrgs = Effect.fn("ExperimentalHttpApi.consoleOrgs")(function* () {
-        const [groups, active] = yield* Effect.all(
-          [account.orgsByAccount().pipe(Effect.orDie), account.active().pipe(Effect.orDie)],
-          {
-            concurrency: "unbounded",
-          },
-        )
-        const info = Option.getOrUndefined(active)
-        return {
-          orgs: groups.flatMap((group) =>
-            group.orgs.map((org) => ({
-              accountID: group.account.id,
-              accountEmail: group.account.email,
-              accountUrl: group.account.url,
-              orgID: org.id,
-              orgName: org.name,
-              active: !!info && info.id === group.account.id && info.active_org_id === org.id,
-            })),
-          ),
-        }
-      })
+    const listConsoleOrgs = Effect.fn("ExperimentalHttpApi.consoleOrgs")(function* () {
+      const [groups, active] = yield* Effect.all(
+        [account.orgsByAccount().pipe(Effect.orDie), account.active().pipe(Effect.orDie)],
+        {
+          concurrency: "unbounded",
+        },
+      )
+      const info = Option.getOrUndefined(active)
+      return {
+        orgs: groups.flatMap((group) =>
+          group.orgs.map((org) => ({
+            accountID: group.account.id,
+            accountEmail: group.account.email,
+            accountUrl: group.account.url,
+            orgID: org.id,
+            orgName: org.name,
+            active: !!info && info.id === group.account.id && info.active_org_id === org.id,
+          })),
+        ),
+      }
+    })
 
 
-      const switchConsole = Effect.fn("ExperimentalHttpApi.consoleSwitch")(function* (ctx: {
-        payload: typeof ConsoleSwitchPayload.Type
-      }) {
-        yield* account
-          .use(ctx.payload.accountID, Option.some(ctx.payload.orgID))
-          .pipe(Effect.catch(() => Effect.fail(new HttpApiError.BadRequest({}))))
-        return true
-      })
+    const switchConsole = Effect.fn("ExperimentalHttpApi.consoleSwitch")(function* (ctx: {
+      payload: typeof ConsoleSwitchPayload.Type
+    }) {
+      yield* account
+        .use(ctx.payload.accountID, Option.some(ctx.payload.orgID))
+        .pipe(Effect.catch(() => Effect.fail(new HttpApiError.BadRequest({}))))
+      return true
+    })
 
 
-      const tool = Effect.fn("ExperimentalHttpApi.tool")(function* (ctx: { query: typeof ToolListQuery.Type }) {
-        const list = yield* registry.tools({
-          providerID: ctx.query.provider,
-          modelID: ctx.query.model,
-          agent: yield* agents.get(yield* agents.defaultAgent()),
-        })
-        return list.map((item) => ({
-          id: item.id,
-          description: item.description,
-          parameters: EffectZod.toJsonSchema(item.parameters),
-        }))
+    const tool = Effect.fn("ExperimentalHttpApi.tool")(function* (ctx: { query: typeof ToolListQuery.Type }) {
+      const list = yield* registry.tools({
+        providerID: ctx.query.provider,
+        modelID: ctx.query.model,
+        agent: yield* agents.get(yield* agents.defaultAgent()),
       })
       })
+      return list.map((item) => ({
+        id: item.id,
+        description: item.description,
+        parameters: EffectZod.toJsonSchema(item.parameters),
+      }))
+    })
 
 
-      const toolIDs = Effect.fn("ExperimentalHttpApi.toolIDs")(function* () {
-        return yield* registry.ids()
-      })
+    const toolIDs = Effect.fn("ExperimentalHttpApi.toolIDs")(function* () {
+      return yield* registry.ids()
+    })
 
 
-      const worktree = Effect.fn("ExperimentalHttpApi.worktree")(function* () {
-        const ctx = yield* InstanceState.context
-        return yield* project.sandboxes(ctx.project.id)
-      })
+    const worktree = Effect.fn("ExperimentalHttpApi.worktree")(function* () {
+      const ctx = yield* InstanceState.context
+      return yield* project.sandboxes(ctx.project.id)
+    })
 
 
-      const worktreeCreate = Effect.fn("ExperimentalHttpApi.worktreeCreate")(function* (ctx: {
-        payload: Worktree.CreateInput | undefined
-      }) {
-        return yield* worktreeSvc.create(ctx.payload)
-      })
+    const worktreeCreate = Effect.fn("ExperimentalHttpApi.worktreeCreate")(function* (ctx: {
+      payload: Worktree.CreateInput | undefined
+    }) {
+      return yield* worktreeSvc.create(ctx.payload)
+    })
 
 
-      const worktreeRemove = Effect.fn("ExperimentalHttpApi.worktreeRemove")(function* (input: {
-        payload: Worktree.RemoveInput
-      }) {
-        const ctx = yield* InstanceState.context
-        yield* worktreeSvc.remove(input.payload)
-        yield* project.removeSandbox(ctx.project.id, input.payload.directory)
-        return true
-      })
+    const worktreeRemove = Effect.fn("ExperimentalHttpApi.worktreeRemove")(function* (input: {
+      payload: Worktree.RemoveInput
+    }) {
+      const ctx = yield* InstanceState.context
+      yield* worktreeSvc.remove(input.payload)
+      yield* project.removeSandbox(ctx.project.id, input.payload.directory)
+      return true
+    })
 
 
-      const worktreeReset = Effect.fn("ExperimentalHttpApi.worktreeReset")(function* (ctx: {
-        payload: Worktree.ResetInput
-      }) {
-        yield* worktreeSvc.reset(ctx.payload)
-        return true
-      })
+    const worktreeReset = Effect.fn("ExperimentalHttpApi.worktreeReset")(function* (ctx: {
+      payload: Worktree.ResetInput
+    }) {
+      yield* worktreeSvc.reset(ctx.payload)
+      return true
+    })
 
 
-      const session = Effect.fn("ExperimentalHttpApi.session")(function* (ctx: { query: typeof SessionListQuery.Type }) {
-        const limit = ctx.query.limit ?? 100
-        const sessions = Array.from(
-          Session.listGlobal({
-            directory: ctx.query.directory,
-            roots: ctx.query.roots,
-            start: ctx.query.start,
-            cursor: ctx.query.cursor,
-            search: ctx.query.search,
-            limit: limit + 1,
-            archived: ctx.query.archived,
-          }),
-        )
-        const list = sessions.length > limit ? sessions.slice(0, limit) : sessions
-        return HttpServerResponse.jsonUnsafe(list, {
-          headers:
-            sessions.length > limit && list.length > 0
-              ? { "x-next-cursor": String(list[list.length - 1].time.updated) }
-              : undefined,
-        })
+    const session = Effect.fn("ExperimentalHttpApi.session")(function* (ctx: { query: typeof SessionListQuery.Type }) {
+      const limit = ctx.query.limit ?? 100
+      const sessions = Array.from(
+        Session.listGlobal({
+          directory: ctx.query.directory,
+          roots: ctx.query.roots,
+          start: ctx.query.start,
+          cursor: ctx.query.cursor,
+          search: ctx.query.search,
+          limit: limit + 1,
+          archived: ctx.query.archived,
+        }),
+      )
+      const list = sessions.length > limit ? sessions.slice(0, limit) : sessions
+      return HttpServerResponse.jsonUnsafe(list, {
+        headers:
+          sessions.length > limit && list.length > 0
+            ? { "x-next-cursor": String(list[list.length - 1].time.updated) }
+            : undefined,
       })
       })
+    })
 
 
-      const resource = Effect.fn("ExperimentalHttpApi.resource")(function* () {
-        return yield* mcp.resources()
-      })
+    const resource = Effect.fn("ExperimentalHttpApi.resource")(function* () {
+      return yield* mcp.resources()
+    })
 
 
-      return handlers
-        .handle("console", getConsole)
-        .handle("consoleOrgs", listConsoleOrgs)
-        .handle("consoleSwitch", switchConsole)
-        .handle("tool", tool)
-        .handle("toolIDs", toolIDs)
-        .handle("worktree", worktree)
-        .handle("worktreeCreate", worktreeCreate)
-        .handle("worktreeRemove", worktreeRemove)
-        .handle("worktreeReset", worktreeReset)
-        .handle("session", session)
-        .handle("resource", resource)
-    }),
+    return handlers
+      .handle("console", getConsole)
+      .handle("consoleOrgs", listConsoleOrgs)
+      .handle("consoleSwitch", switchConsole)
+      .handle("tool", tool)
+      .handle("toolIDs", toolIDs)
+      .handle("worktree", worktree)
+      .handle("worktreeCreate", worktreeCreate)
+      .handle("worktreeRemove", worktreeRemove)
+      .handle("worktreeReset", worktreeReset)
+      .handle("session", session)
+      .handle("resource", resource)
+  }),
 )
 )

+ 44 - 44
packages/opencode/src/server/routes/instance/httpapi/handlers/file.ts

@@ -6,49 +6,49 @@ import { HttpApiBuilder } from "effect/unstable/httpapi"
 import { InstanceHttpApi } from "../api"
 import { InstanceHttpApi } from "../api"
 
 
 export const fileHandlers = HttpApiBuilder.group(InstanceHttpApi, "file", (handlers) =>
 export const fileHandlers = HttpApiBuilder.group(InstanceHttpApi, "file", (handlers) =>
-    Effect.gen(function* () {
-      const svc = yield* File.Service
-      const ripgrep = yield* Ripgrep.Service
-
-      const findText = Effect.fn("FileHttpApi.findText")(function* (ctx: { query: { pattern: string } }) {
-        return (yield* ripgrep
-          .search({ cwd: (yield* InstanceState.context).directory, pattern: ctx.query.pattern, limit: 10 })
-          .pipe(Effect.orDie)).items
+  Effect.gen(function* () {
+    const svc = yield* File.Service
+    const ripgrep = yield* Ripgrep.Service
+
+    const findText = Effect.fn("FileHttpApi.findText")(function* (ctx: { query: { pattern: string } }) {
+      return (yield* ripgrep
+        .search({ cwd: (yield* InstanceState.context).directory, pattern: ctx.query.pattern, limit: 10 })
+        .pipe(Effect.orDie)).items
+    })
+
+    const findFile = Effect.fn("FileHttpApi.findFile")(function* (ctx: {
+      query: { query: string; dirs?: "true" | "false"; type?: "file" | "directory"; limit?: number }
+    }) {
+      return yield* svc.search({
+        query: ctx.query.query,
+        limit: ctx.query.limit ?? 10,
+        dirs: ctx.query.dirs !== "false",
+        type: ctx.query.type,
       })
       })
-
-      const findFile = Effect.fn("FileHttpApi.findFile")(function* (ctx: {
-        query: { query: string; dirs?: "true" | "false"; type?: "file" | "directory"; limit?: number }
-      }) {
-        return yield* svc.search({
-          query: ctx.query.query,
-          limit: ctx.query.limit ?? 10,
-          dirs: ctx.query.dirs !== "false",
-          type: ctx.query.type,
-        })
-      })
-
-      const findSymbol = Effect.fn("FileHttpApi.findSymbol")(function* () {
-        return []
-      })
-
-      const list = Effect.fn("FileHttpApi.list")(function* (ctx: { query: { path: string } }) {
-        return yield* svc.list(ctx.query.path)
-      })
-
-      const content = Effect.fn("FileHttpApi.content")(function* (ctx: { query: { path: string } }) {
-        return yield* svc.read(ctx.query.path)
-      })
-
-      const status = Effect.fn("FileHttpApi.status")(function* () {
-        return yield* svc.status()
-      })
-
-      return handlers
-        .handle("findText", findText)
-        .handle("findFile", findFile)
-        .handle("findSymbol", findSymbol)
-        .handle("list", list)
-        .handle("content", content)
-        .handle("status", status)
-    }),
+    })
+
+    const findSymbol = Effect.fn("FileHttpApi.findSymbol")(function* () {
+      return []
+    })
+
+    const list = Effect.fn("FileHttpApi.list")(function* (ctx: { query: { path: string } }) {
+      return yield* svc.list(ctx.query.path)
+    })
+
+    const content = Effect.fn("FileHttpApi.content")(function* (ctx: { query: { path: string } }) {
+      return yield* svc.read(ctx.query.path)
+    })
+
+    const status = Effect.fn("FileHttpApi.status")(function* () {
+      return yield* svc.status()
+    })
+
+    return handlers
+      .handle("findText", findText)
+      .handle("findFile", findFile)
+      .handle("findSymbol", findSymbol)
+      .handle("list", list)
+      .handle("content", content)
+      .handle("status", status)
+  }),
 )
 )

+ 77 - 77
packages/opencode/src/server/routes/instance/httpapi/handlers/global.ts

@@ -65,92 +65,92 @@ function eventResponse() {
 }
 }
 
 
 export const globalHandlers = HttpApiBuilder.group(RootHttpApi, "global", (handlers) =>
 export const globalHandlers = HttpApiBuilder.group(RootHttpApi, "global", (handlers) =>
-    Effect.gen(function* () {
-      const config = yield* Config.Service
-      const installation = yield* Installation.Service
+  Effect.gen(function* () {
+    const config = yield* Config.Service
+    const installation = yield* Installation.Service
 
 
-      const health = Effect.fn("GlobalHttpApi.health")(function* () {
-        return { healthy: true as const, version: InstallationVersion }
-      })
+    const health = Effect.fn("GlobalHttpApi.health")(function* () {
+      return { healthy: true as const, version: InstallationVersion }
+    })
 
 
-      const event = Effect.fn("GlobalHttpApi.event")(function* () {
-        return eventResponse()
-      })
+    const event = Effect.fn("GlobalHttpApi.event")(function* () {
+      return eventResponse()
+    })
 
 
-      const configGet = Effect.fn("GlobalHttpApi.configGet")(function* () {
-        return yield* config.getGlobal()
-      })
+    const configGet = Effect.fn("GlobalHttpApi.configGet")(function* () {
+      return yield* config.getGlobal()
+    })
 
 
-      const configUpdate = Effect.fn("GlobalHttpApi.configUpdate")(function* (ctx) {
-        return yield* config.updateGlobal(ctx.payload)
-      })
+    const configUpdate = Effect.fn("GlobalHttpApi.configUpdate")(function* (ctx) {
+      return yield* config.updateGlobal(ctx.payload)
+    })
 
 
-      const dispose = Effect.fn("GlobalHttpApi.dispose")(function* () {
-        yield* Effect.promise(() => Instance.disposeAll())
-        GlobalBus.emit("event", {
-          directory: "global",
-          payload: { type: "global.disposed", properties: {} },
-        })
-        return true
+    const dispose = Effect.fn("GlobalHttpApi.dispose")(function* () {
+      yield* Effect.promise(() => Instance.disposeAll())
+      GlobalBus.emit("event", {
+        directory: "global",
+        payload: { type: "global.disposed", properties: {} },
       })
       })
+      return true
+    })
 
 
-      const upgrade = Effect.fn("GlobalHttpApi.upgrade")(function* (ctx: { payload: typeof GlobalUpgradeInput.Type }) {
-        const method = yield* installation.method()
-        if (method === "unknown") {
-          return {
-            status: 400,
-            body: { success: false as const, error: "Unknown installation method" },
-          }
+    const upgrade = Effect.fn("GlobalHttpApi.upgrade")(function* (ctx: { payload: typeof GlobalUpgradeInput.Type }) {
+      const method = yield* installation.method()
+      if (method === "unknown") {
+        return {
+          status: 400,
+          body: { success: false as const, error: "Unknown installation method" },
         }
         }
-        const target = ctx.payload.target || (yield* installation.latest(method))
-        const result = yield* installation.upgrade(method, target).pipe(
-          Effect.as({ status: 200, body: { success: true as const, version: target } }),
-          Effect.catch((err) =>
-            Effect.succeed({
-              status: 500,
-              body: {
-                success: false as const,
-                error: err instanceof Error ? err.message : String(err),
-              },
-            }),
-          ),
-        )
-        if (!result.body.success) return result
-        GlobalBus.emit("event", {
-          directory: "global",
-          payload: {
-            type: Installation.Event.Updated.type,
-            properties: { version: target },
-          },
-        })
-        return result
+      }
+      const target = ctx.payload.target || (yield* installation.latest(method))
+      const result = yield* installation.upgrade(method, target).pipe(
+        Effect.as({ status: 200, body: { success: true as const, version: target } }),
+        Effect.catch((err) =>
+          Effect.succeed({
+            status: 500,
+            body: {
+              success: false as const,
+              error: err instanceof Error ? err.message : String(err),
+            },
+          }),
+        ),
+      )
+      if (!result.body.success) return result
+      GlobalBus.emit("event", {
+        directory: "global",
+        payload: {
+          type: Installation.Event.Updated.type,
+          properties: { version: target },
+        },
       })
       })
+      return result
+    })
 
 
-      const upgradeRaw = Effect.fn("GlobalHttpApi.upgradeRaw")(function* (ctx: {
-        request: HttpServerRequest.HttpServerRequest
-      }) {
-        const body = yield* Effect.orDie(ctx.request.text)
-        const json = parseBody(body)
-        if (json === undefined) {
-          return HttpServerResponse.jsonUnsafe({ success: false, error: "Invalid request body" }, { status: 400 })
-        }
-        const payload = yield* Schema.decodeUnknownEffect(GlobalUpgradeInput)(json).pipe(
-          Effect.map((payload) => ({ valid: true as const, payload })),
-          Effect.catch(() => Effect.succeed({ valid: false as const })),
-        )
-        if (!payload.valid) {
-          return HttpServerResponse.jsonUnsafe({ success: false, error: "Invalid request body" }, { status: 400 })
-        }
-        const result = yield* upgrade({ payload: payload.payload })
-        return HttpServerResponse.jsonUnsafe(result.body, { status: result.status })
-      })
+    const upgradeRaw = Effect.fn("GlobalHttpApi.upgradeRaw")(function* (ctx: {
+      request: HttpServerRequest.HttpServerRequest
+    }) {
+      const body = yield* Effect.orDie(ctx.request.text)
+      const json = parseBody(body)
+      if (json === undefined) {
+        return HttpServerResponse.jsonUnsafe({ success: false, error: "Invalid request body" }, { status: 400 })
+      }
+      const payload = yield* Schema.decodeUnknownEffect(GlobalUpgradeInput)(json).pipe(
+        Effect.map((payload) => ({ valid: true as const, payload })),
+        Effect.catch(() => Effect.succeed({ valid: false as const })),
+      )
+      if (!payload.valid) {
+        return HttpServerResponse.jsonUnsafe({ success: false, error: "Invalid request body" }, { status: 400 })
+      }
+      const result = yield* upgrade({ payload: payload.payload })
+      return HttpServerResponse.jsonUnsafe(result.body, { status: result.status })
+    })
 
 
-      return handlers
-        .handle("health", health)
-        .handleRaw("event", event)
-        .handle("configGet", configGet)
-        .handle("configUpdate", configUpdate)
-        .handle("dispose", dispose)
-        .handleRaw("upgrade", upgradeRaw)
-    }),
+    return handlers
+      .handle("health", health)
+      .handleRaw("event", event)
+      .handle("configGet", configGet)
+      .handle("configUpdate", configUpdate)
+      .handle("dispose", dispose)
+      .handleRaw("upgrade", upgradeRaw)
+  }),
 )
 )

+ 54 - 54
packages/opencode/src/server/routes/instance/httpapi/handlers/instance.ts

@@ -12,68 +12,68 @@ import { InstanceHttpApi } from "../api"
 import { markInstanceForDisposal } from "../lifecycle"
 import { markInstanceForDisposal } from "../lifecycle"
 
 
 export const instanceHandlers = HttpApiBuilder.group(InstanceHttpApi, "instance", (handlers) =>
 export const instanceHandlers = HttpApiBuilder.group(InstanceHttpApi, "instance", (handlers) =>
-    Effect.gen(function* () {
-      const agent = yield* Agent.Service
-      const command = yield* Command.Service
-      const format = yield* Format.Service
-      const lsp = yield* LSP.Service
-      const skill = yield* Skill.Service
-      const vcs = yield* Vcs.Service
+  Effect.gen(function* () {
+    const agent = yield* Agent.Service
+    const command = yield* Command.Service
+    const format = yield* Format.Service
+    const lsp = yield* LSP.Service
+    const skill = yield* Skill.Service
+    const vcs = yield* Vcs.Service
 
 
-      const dispose = Effect.fn("InstanceHttpApi.dispose")(function* () {
-        yield* markInstanceForDisposal(yield* InstanceState.context)
-        return true
-      })
+    const dispose = Effect.fn("InstanceHttpApi.dispose")(function* () {
+      yield* markInstanceForDisposal(yield* InstanceState.context)
+      return true
+    })
 
 
-      const getPath = Effect.fn("InstanceHttpApi.path")(function* () {
-        const ctx = yield* InstanceState.context
-        return {
-          home: Global.Path.home,
-          state: Global.Path.state,
-          config: Global.Path.config,
-          worktree: ctx.worktree,
-          directory: ctx.directory,
-        }
-      })
+    const getPath = Effect.fn("InstanceHttpApi.path")(function* () {
+      const ctx = yield* InstanceState.context
+      return {
+        home: Global.Path.home,
+        state: Global.Path.state,
+        config: Global.Path.config,
+        worktree: ctx.worktree,
+        directory: ctx.directory,
+      }
+    })
 
 
-      const getVcs = Effect.fn("InstanceHttpApi.vcs")(function* () {
-        const [branch, default_branch] = yield* Effect.all([vcs.branch(), vcs.defaultBranch()], { concurrency: 2 })
-        return { branch, default_branch }
-      })
+    const getVcs = Effect.fn("InstanceHttpApi.vcs")(function* () {
+      const [branch, default_branch] = yield* Effect.all([vcs.branch(), vcs.defaultBranch()], { concurrency: 2 })
+      return { branch, default_branch }
+    })
 
 
-      const getVcsDiff = Effect.fn("InstanceHttpApi.vcsDiff")(function* (ctx: { query: { mode: Vcs.Mode } }) {
-        return yield* vcs.diff(ctx.query.mode)
-      })
+    const getVcsDiff = Effect.fn("InstanceHttpApi.vcsDiff")(function* (ctx: { query: { mode: Vcs.Mode } }) {
+      return yield* vcs.diff(ctx.query.mode)
+    })
 
 
-      const getCommand = Effect.fn("InstanceHttpApi.command")(function* () {
-        return yield* command.list()
-      })
+    const getCommand = Effect.fn("InstanceHttpApi.command")(function* () {
+      return yield* command.list()
+    })
 
 
-      const getAgent = Effect.fn("InstanceHttpApi.agent")(function* () {
-        return yield* agent.list()
-      })
+    const getAgent = Effect.fn("InstanceHttpApi.agent")(function* () {
+      return yield* agent.list()
+    })
 
 
-      const getSkill = Effect.fn("InstanceHttpApi.skill")(function* () {
-        return yield* skill.all()
-      })
+    const getSkill = Effect.fn("InstanceHttpApi.skill")(function* () {
+      return yield* skill.all()
+    })
 
 
-      const getLsp = Effect.fn("InstanceHttpApi.lsp")(function* () {
-        return yield* lsp.status()
-      })
+    const getLsp = Effect.fn("InstanceHttpApi.lsp")(function* () {
+      return yield* lsp.status()
+    })
 
 
-      const getFormatter = Effect.fn("InstanceHttpApi.formatter")(function* () {
-        return yield* format.status()
-      })
+    const getFormatter = Effect.fn("InstanceHttpApi.formatter")(function* () {
+      return yield* format.status()
+    })
 
 
-      return handlers
-        .handle("dispose", dispose)
-        .handle("path", getPath)
-        .handle("vcs", getVcs)
-        .handle("vcsDiff", getVcsDiff)
-        .handle("command", getCommand)
-        .handle("agent", getAgent)
-        .handle("skill", getSkill)
-        .handle("lsp", getLsp)
-        .handle("formatter", getFormatter)
-    }),
+    return handlers
+      .handle("dispose", dispose)
+      .handle("path", getPath)
+      .handle("vcs", getVcs)
+      .handle("vcsDiff", getVcsDiff)
+      .handle("command", getCommand)
+      .handle("agent", getAgent)
+      .handle("skill", getSkill)
+      .handle("lsp", getLsp)
+      .handle("formatter", getFormatter)
+  }),
 )
 )

+ 51 - 51
packages/opencode/src/server/routes/instance/httpapi/handlers/mcp.ts

@@ -5,64 +5,64 @@ import { InstanceHttpApi } from "../api"
 import { AddPayload, AuthCallbackPayload, StatusMap, UnsupportedOAuthError } from "../groups/mcp"
 import { AddPayload, AuthCallbackPayload, StatusMap, UnsupportedOAuthError } from "../groups/mcp"
 
 
 export const mcpHandlers = HttpApiBuilder.group(InstanceHttpApi, "mcp", (handlers) =>
 export const mcpHandlers = HttpApiBuilder.group(InstanceHttpApi, "mcp", (handlers) =>
-    Effect.gen(function* () {
-      const mcp = yield* MCP.Service
+  Effect.gen(function* () {
+    const mcp = yield* MCP.Service
 
 
-      const status = Effect.fn("McpHttpApi.status")(function* () {
-        return yield* mcp.status()
-      })
+    const status = Effect.fn("McpHttpApi.status")(function* () {
+      return yield* mcp.status()
+    })
 
 
-      const add = Effect.fn("McpHttpApi.add")(function* (ctx: { payload: typeof AddPayload.Type }) {
-        const result = (yield* mcp.add(ctx.payload.name, ctx.payload.config)).status
-        return yield* Schema.decodeUnknownEffect(StatusMap)(
-          "status" in result ? { [ctx.payload.name]: result } : result,
-        ).pipe(Effect.mapError(() => new HttpApiError.BadRequest({})))
-      })
+    const add = Effect.fn("McpHttpApi.add")(function* (ctx: { payload: typeof AddPayload.Type }) {
+      const result = (yield* mcp.add(ctx.payload.name, ctx.payload.config)).status
+      return yield* Schema.decodeUnknownEffect(StatusMap)(
+        "status" in result ? { [ctx.payload.name]: result } : result,
+      ).pipe(Effect.mapError(() => new HttpApiError.BadRequest({})))
+    })
 
 
-      const authStart = Effect.fn("McpHttpApi.authStart")(function* (ctx: { params: { name: string } }) {
-        if (!(yield* mcp.supportsOAuth(ctx.params.name))) {
-          return yield* new UnsupportedOAuthError({ error: `MCP server ${ctx.params.name} does not support OAuth` })
-        }
-        return yield* mcp.startAuth(ctx.params.name)
-      })
+    const authStart = Effect.fn("McpHttpApi.authStart")(function* (ctx: { params: { name: string } }) {
+      if (!(yield* mcp.supportsOAuth(ctx.params.name))) {
+        return yield* new UnsupportedOAuthError({ error: `MCP server ${ctx.params.name} does not support OAuth` })
+      }
+      return yield* mcp.startAuth(ctx.params.name)
+    })
 
 
-      const authCallback = Effect.fn("McpHttpApi.authCallback")(function* (ctx: {
-        params: { name: string }
-        payload: typeof AuthCallbackPayload.Type
-      }) {
-        return yield* mcp.finishAuth(ctx.params.name, ctx.payload.code)
-      })
+    const authCallback = Effect.fn("McpHttpApi.authCallback")(function* (ctx: {
+      params: { name: string }
+      payload: typeof AuthCallbackPayload.Type
+    }) {
+      return yield* mcp.finishAuth(ctx.params.name, ctx.payload.code)
+    })
 
 
-      const authAuthenticate = Effect.fn("McpHttpApi.authAuthenticate")(function* (ctx: { params: { name: string } }) {
-        if (!(yield* mcp.supportsOAuth(ctx.params.name))) {
-          return yield* new UnsupportedOAuthError({ error: `MCP server ${ctx.params.name} does not support OAuth` })
-        }
-        return yield* mcp.authenticate(ctx.params.name)
-      })
+    const authAuthenticate = Effect.fn("McpHttpApi.authAuthenticate")(function* (ctx: { params: { name: string } }) {
+      if (!(yield* mcp.supportsOAuth(ctx.params.name))) {
+        return yield* new UnsupportedOAuthError({ error: `MCP server ${ctx.params.name} does not support OAuth` })
+      }
+      return yield* mcp.authenticate(ctx.params.name)
+    })
 
 
-      const authRemove = Effect.fn("McpHttpApi.authRemove")(function* (ctx: { params: { name: string } }) {
-        yield* mcp.removeAuth(ctx.params.name)
-        return { success: true as const }
-      })
+    const authRemove = Effect.fn("McpHttpApi.authRemove")(function* (ctx: { params: { name: string } }) {
+      yield* mcp.removeAuth(ctx.params.name)
+      return { success: true as const }
+    })
 
 
-      const connect = Effect.fn("McpHttpApi.connect")(function* (ctx: { params: { name: string } }) {
-        yield* mcp.connect(ctx.params.name)
-        return true
-      })
+    const connect = Effect.fn("McpHttpApi.connect")(function* (ctx: { params: { name: string } }) {
+      yield* mcp.connect(ctx.params.name)
+      return true
+    })
 
 
-      const disconnect = Effect.fn("McpHttpApi.disconnect")(function* (ctx: { params: { name: string } }) {
-        yield* mcp.disconnect(ctx.params.name)
-        return true
-      })
+    const disconnect = Effect.fn("McpHttpApi.disconnect")(function* (ctx: { params: { name: string } }) {
+      yield* mcp.disconnect(ctx.params.name)
+      return true
+    })
 
 
-      return handlers
-        .handle("status", status)
-        .handle("add", add)
-        .handle("authStart", authStart)
-        .handle("authCallback", authCallback)
-        .handle("authAuthenticate", authAuthenticate)
-        .handle("authRemove", authRemove)
-        .handle("connect", connect)
-        .handle("disconnect", disconnect)
-    }),
+    return handlers
+      .handle("status", status)
+      .handle("add", add)
+      .handle("authStart", authStart)
+      .handle("authCallback", authCallback)
+      .handle("authAuthenticate", authAuthenticate)
+      .handle("authRemove", authRemove)
+      .handle("connect", connect)
+      .handle("disconnect", disconnect)
+  }),
 )
 )

+ 17 - 17
packages/opencode/src/server/routes/instance/httpapi/handlers/permission.ts

@@ -5,25 +5,25 @@ import { HttpApiBuilder } from "effect/unstable/httpapi"
 import { InstanceHttpApi } from "../api"
 import { InstanceHttpApi } from "../api"
 
 
 export const permissionHandlers = HttpApiBuilder.group(InstanceHttpApi, "permission", (handlers) =>
 export const permissionHandlers = HttpApiBuilder.group(InstanceHttpApi, "permission", (handlers) =>
-    Effect.gen(function* () {
-      const svc = yield* Permission.Service
+  Effect.gen(function* () {
+    const svc = yield* Permission.Service
 
 
-      const list = Effect.fn("PermissionHttpApi.list")(function* () {
-        return yield* svc.list()
-      })
+    const list = Effect.fn("PermissionHttpApi.list")(function* () {
+      return yield* svc.list()
+    })
 
 
-      const reply = Effect.fn("PermissionHttpApi.reply")(function* (ctx: {
-        params: { requestID: PermissionID }
-        payload: Permission.ReplyBody
-      }) {
-        yield* svc.reply({
-          requestID: ctx.params.requestID,
-          reply: ctx.payload.reply,
-          message: ctx.payload.message,
-        })
-        return true
+    const reply = Effect.fn("PermissionHttpApi.reply")(function* (ctx: {
+      params: { requestID: PermissionID }
+      payload: Permission.ReplyBody
+    }) {
+      yield* svc.reply({
+        requestID: ctx.params.requestID,
+        reply: ctx.payload.reply,
+        message: ctx.payload.message,
       })
       })
+      return true
+    })
 
 
-      return handlers.handle("list", list).handle("reply", reply)
-    }),
+    return handlers.handle("list", list).handle("reply", reply)
+  }),
 )
 )

+ 27 - 27
packages/opencode/src/server/routes/instance/httpapi/handlers/project.ts

@@ -9,38 +9,38 @@ import { InstanceHttpApi } from "../api"
 import { markInstanceForReload } from "../lifecycle"
 import { markInstanceForReload } from "../lifecycle"
 
 
 export const projectHandlers = HttpApiBuilder.group(InstanceHttpApi, "project", (handlers) =>
 export const projectHandlers = HttpApiBuilder.group(InstanceHttpApi, "project", (handlers) =>
-    Effect.gen(function* () {
-      const svc = yield* Project.Service
+  Effect.gen(function* () {
+    const svc = yield* Project.Service
 
 
-      const list = Effect.fn("ProjectHttpApi.list")(function* () {
-        return yield* svc.list()
-      })
+    const list = Effect.fn("ProjectHttpApi.list")(function* () {
+      return yield* svc.list()
+    })
 
 
-      const current = Effect.fn("ProjectHttpApi.current")(function* () {
-        return (yield* InstanceState.context).project
-      })
+    const current = Effect.fn("ProjectHttpApi.current")(function* () {
+      return (yield* InstanceState.context).project
+    })
 
 
-      const initGit = Effect.fn("ProjectHttpApi.initGit")(function* () {
-        const ctx = yield* InstanceState.context
-        const next = yield* svc.initGit({ directory: ctx.directory, project: ctx.project })
-        if (next.id === ctx.project.id && next.vcs === ctx.project.vcs && next.worktree === ctx.project.worktree)
-          return next
-        yield* markInstanceForReload(ctx, {
-          directory: ctx.directory,
-          worktree: ctx.directory,
-          project: next,
-          init: () => AppRuntime.runPromise(InstanceBootstrap),
-        })
+    const initGit = Effect.fn("ProjectHttpApi.initGit")(function* () {
+      const ctx = yield* InstanceState.context
+      const next = yield* svc.initGit({ directory: ctx.directory, project: ctx.project })
+      if (next.id === ctx.project.id && next.vcs === ctx.project.vcs && next.worktree === ctx.project.worktree)
         return next
         return next
+      yield* markInstanceForReload(ctx, {
+        directory: ctx.directory,
+        worktree: ctx.directory,
+        project: next,
+        init: () => AppRuntime.runPromise(InstanceBootstrap),
       })
       })
+      return next
+    })
 
 
-      const update = Effect.fn("ProjectHttpApi.update")(function* (ctx: {
-        params: { projectID: ProjectID }
-        payload: Project.UpdatePayload
-      }) {
-        return yield* svc.update({ ...ctx.payload, projectID: ctx.params.projectID })
-      })
+    const update = Effect.fn("ProjectHttpApi.update")(function* (ctx: {
+      params: { projectID: ProjectID }
+      payload: Project.UpdatePayload
+    }) {
+      return yield* svc.update({ ...ctx.payload, projectID: ctx.params.projectID })
+    })
 
 
-      return handlers.handle("list", list).handle("current", current).handle("initGit", initGit).handle("update", update)
-    }),
+    return handlers.handle("list", list).handle("current", current).handle("initGit", initGit).handle("update", update)
+  }),
 )
 )

+ 70 - 70
packages/opencode/src/server/routes/instance/httpapi/handlers/provider.ts

@@ -10,80 +10,80 @@ import { HttpApiBuilder, HttpApiError } from "effect/unstable/httpapi"
 import { InstanceHttpApi } from "../api"
 import { InstanceHttpApi } from "../api"
 
 
 export const providerHandlers = HttpApiBuilder.group(InstanceHttpApi, "provider", (handlers) =>
 export const providerHandlers = HttpApiBuilder.group(InstanceHttpApi, "provider", (handlers) =>
-    Effect.gen(function* () {
-      const cfg = yield* Config.Service
-      const provider = yield* Provider.Service
-      const svc = yield* ProviderAuth.Service
+  Effect.gen(function* () {
+    const cfg = yield* Config.Service
+    const provider = yield* Provider.Service
+    const svc = yield* ProviderAuth.Service
 
 
-      const list = Effect.fn("ProviderHttpApi.list")(function* () {
-        const config = yield* cfg.get()
-        const all = yield* Effect.promise(() => ModelsDev.get())
-        const disabled = new Set(config.disabled_providers ?? [])
-        const enabled = config.enabled_providers ? new Set(config.enabled_providers) : undefined
-        const filtered: Record<string, (typeof all)[string]> = {}
-        for (const [key, value] of Object.entries(all)) {
-          if ((enabled ? enabled.has(key) : true) && !disabled.has(key)) filtered[key] = value
-        }
-        const connected = yield* provider.list()
-        const providers = Object.assign(
-          mapValues(filtered, (item) => Provider.fromModelsDevProvider(item)),
-          connected,
-        )
-        return {
-          all: Object.values(providers),
-          default: Provider.defaultModelIDs(providers),
-          connected: Object.keys(connected),
-        }
-      })
+    const list = Effect.fn("ProviderHttpApi.list")(function* () {
+      const config = yield* cfg.get()
+      const all = yield* Effect.promise(() => ModelsDev.get())
+      const disabled = new Set(config.disabled_providers ?? [])
+      const enabled = config.enabled_providers ? new Set(config.enabled_providers) : undefined
+      const filtered: Record<string, (typeof all)[string]> = {}
+      for (const [key, value] of Object.entries(all)) {
+        if ((enabled ? enabled.has(key) : true) && !disabled.has(key)) filtered[key] = value
+      }
+      const connected = yield* provider.list()
+      const providers = Object.assign(
+        mapValues(filtered, (item) => Provider.fromModelsDevProvider(item)),
+        connected,
+      )
+      return {
+        all: Object.values(providers),
+        default: Provider.defaultModelIDs(providers),
+        connected: Object.keys(connected),
+      }
+    })
 
 
-      const auth = Effect.fn("ProviderHttpApi.auth")(function* () {
-        return yield* svc.methods()
-      })
+    const auth = Effect.fn("ProviderHttpApi.auth")(function* () {
+      return yield* svc.methods()
+    })
 
 
-      const authorize = Effect.fn("ProviderHttpApi.authorize")(function* (ctx: {
-        params: { providerID: ProviderID }
-        payload: ProviderAuth.AuthorizeInput
-      }) {
-        return yield* svc
-          .authorize({
-            providerID: ctx.params.providerID,
-            method: ctx.payload.method,
-            inputs: ctx.payload.inputs,
-          })
-          .pipe(Effect.catch(() => Effect.fail(new HttpApiError.BadRequest({}))))
-      })
+    const authorize = Effect.fn("ProviderHttpApi.authorize")(function* (ctx: {
+      params: { providerID: ProviderID }
+      payload: ProviderAuth.AuthorizeInput
+    }) {
+      return yield* svc
+        .authorize({
+          providerID: ctx.params.providerID,
+          method: ctx.payload.method,
+          inputs: ctx.payload.inputs,
+        })
+        .pipe(Effect.catch(() => Effect.fail(new HttpApiError.BadRequest({}))))
+    })
 
 
-      const authorizeRaw = Effect.fn("ProviderHttpApi.authorizeRaw")(function* (ctx: {
-        params: { providerID: ProviderID }
-        request: HttpServerRequest.HttpServerRequest
-      }) {
-        const body = yield* Effect.orDie(ctx.request.text)
-        const payload = yield* Schema.decodeUnknownEffect(Schema.fromJsonString(ProviderAuth.AuthorizeInput))(body).pipe(
-          Effect.mapError(() => new HttpApiError.BadRequest({})),
-        )
-        const result = yield* authorize({ params: ctx.params, payload })
-        if (result === undefined) return HttpServerResponse.empty({ status: 200 })
-        return HttpServerResponse.jsonUnsafe(result)
-      })
+    const authorizeRaw = Effect.fn("ProviderHttpApi.authorizeRaw")(function* (ctx: {
+      params: { providerID: ProviderID }
+      request: HttpServerRequest.HttpServerRequest
+    }) {
+      const body = yield* Effect.orDie(ctx.request.text)
+      const payload = yield* Schema.decodeUnknownEffect(Schema.fromJsonString(ProviderAuth.AuthorizeInput))(body).pipe(
+        Effect.mapError(() => new HttpApiError.BadRequest({})),
+      )
+      const result = yield* authorize({ params: ctx.params, payload })
+      if (result === undefined) return HttpServerResponse.empty({ status: 200 })
+      return HttpServerResponse.jsonUnsafe(result)
+    })
 
 
-      const callback = Effect.fn("ProviderHttpApi.callback")(function* (ctx: {
-        params: { providerID: ProviderID }
-        payload: ProviderAuth.CallbackInput
-      }) {
-        yield* svc
-          .callback({
-            providerID: ctx.params.providerID,
-            method: ctx.payload.method,
-            code: ctx.payload.code,
-          })
-          .pipe(Effect.catch(() => Effect.fail(new HttpApiError.BadRequest({}))))
-        return true
-      })
+    const callback = Effect.fn("ProviderHttpApi.callback")(function* (ctx: {
+      params: { providerID: ProviderID }
+      payload: ProviderAuth.CallbackInput
+    }) {
+      yield* svc
+        .callback({
+          providerID: ctx.params.providerID,
+          method: ctx.payload.method,
+          code: ctx.payload.code,
+        })
+        .pipe(Effect.catch(() => Effect.fail(new HttpApiError.BadRequest({}))))
+      return true
+    })
 
 
-      return handlers
-        .handle("list", list)
-        .handle("auth", auth)
-        .handleRaw("authorize", authorizeRaw)
-        .handle("callback", callback)
-    }),
+    return handlers
+      .handle("list", list)
+      .handle("auth", auth)
+      .handleRaw("authorize", authorizeRaw)
+      .handle("callback", callback)
+  }),
 )
 )

+ 3 - 1
packages/opencode/src/server/routes/instance/httpapi/handlers/pty.ts

@@ -88,7 +88,9 @@ export const ptyConnectRoute = HttpRouter.add(
       },
       },
       send: (data: string | Uint8Array | ArrayBuffer) => {
       send: (data: string | Uint8Array | ArrayBuffer) => {
         if (closed) return
         if (closed) return
-        Effect.runFork(write(data instanceof ArrayBuffer ? new Uint8Array(data) : data).pipe(Effect.catch(() => Effect.void)))
+        Effect.runFork(
+          write(data instanceof ArrayBuffer ? new Uint8Array(data) : data).pipe(Effect.catch(() => Effect.void)),
+        )
       },
       },
       close: (code?: number, reason?: string) => {
       close: (code?: number, reason?: string) => {
         if (closed) return
         if (closed) return

+ 20 - 20
packages/opencode/src/server/routes/instance/httpapi/handlers/question.ts

@@ -5,29 +5,29 @@ import { HttpApiBuilder } from "effect/unstable/httpapi"
 import { InstanceHttpApi } from "../api"
 import { InstanceHttpApi } from "../api"
 
 
 export const questionHandlers = HttpApiBuilder.group(InstanceHttpApi, "question", (handlers) =>
 export const questionHandlers = HttpApiBuilder.group(InstanceHttpApi, "question", (handlers) =>
-    Effect.gen(function* () {
-      const svc = yield* Question.Service
+  Effect.gen(function* () {
+    const svc = yield* Question.Service
 
 
-      const list = Effect.fn("QuestionHttpApi.list")(function* () {
-        return yield* svc.list()
-      })
+    const list = Effect.fn("QuestionHttpApi.list")(function* () {
+      return yield* svc.list()
+    })
 
 
-      const reply = Effect.fn("QuestionHttpApi.reply")(function* (ctx: {
-        params: { requestID: QuestionID }
-        payload: Question.Reply
-      }) {
-        yield* svc.reply({
-          requestID: ctx.params.requestID,
-          answers: ctx.payload.answers,
-        })
-        return true
+    const reply = Effect.fn("QuestionHttpApi.reply")(function* (ctx: {
+      params: { requestID: QuestionID }
+      payload: Question.Reply
+    }) {
+      yield* svc.reply({
+        requestID: ctx.params.requestID,
+        answers: ctx.payload.answers,
       })
       })
+      return true
+    })
 
 
-      const reject = Effect.fn("QuestionHttpApi.reject")(function* (ctx: { params: { requestID: QuestionID } }) {
-        yield* svc.reject(ctx.params.requestID)
-        return true
-      })
+    const reject = Effect.fn("QuestionHttpApi.reject")(function* (ctx: { params: { requestID: QuestionID } }) {
+      yield* svc.reject(ctx.params.requestID)
+      return true
+    })
 
 
-      return handlers.handle("list", list).handle("reply", reply).handle("reject", reject)
-    }),
+    return handlers.handle("list", list).handle("reply", reply).handle("reject", reject)
+  }),
 )
 )

+ 49 - 34
packages/opencode/src/server/routes/instance/httpapi/handlers/session.ts

@@ -25,7 +25,20 @@ import * as Stream from "effect/Stream"
 import { HttpServerRequest, HttpServerResponse } from "effect/unstable/http"
 import { HttpServerRequest, HttpServerResponse } from "effect/unstable/http"
 import { HttpApiBuilder, HttpApiError, HttpApiSchema } from "effect/unstable/httpapi"
 import { HttpApiBuilder, HttpApiError, HttpApiSchema } from "effect/unstable/httpapi"
 import { InstanceHttpApi } from "../api"
 import { InstanceHttpApi } from "../api"
-import { CommandPayload, DiffQuery, ForkPayload, InitPayload, ListQuery, MessagesQuery, PermissionResponsePayload, PromptPayload, RevertPayload, ShellPayload, SummarizePayload, UpdatePayload } from "../groups/session"
+import {
+  CommandPayload,
+  DiffQuery,
+  ForkPayload,
+  InitPayload,
+  ListQuery,
+  MessagesQuery,
+  PermissionResponsePayload,
+  PromptPayload,
+  RevertPayload,
+  ShellPayload,
+  SummarizePayload,
+  UpdatePayload,
+} from "../groups/session"
 
 
 const log = Log.create({ service: "server" })
 const log = Log.create({ service: "server" })
 
 
@@ -88,40 +101,42 @@ export const sessionHandlers = HttpApiBuilder.group(InstanceHttpApi, "session",
       params: { sessionID: SessionID }
       params: { sessionID: SessionID }
       query: typeof MessagesQuery.Type
       query: typeof MessagesQuery.Type
     }) {
     }) {
-      return yield* mapNotFound(Effect.gen(function* () {
-        if (ctx.query.before && ctx.query.limit === undefined) return yield* new HttpApiError.BadRequest({})
-        if (ctx.query.before) {
-          const before = ctx.query.before
-          yield* Effect.try({
-            try: () => MessageV2.cursor.decode(before),
-            catch: () => new HttpApiError.BadRequest({}),
-          })
-        }
-        if (ctx.query.limit === undefined || ctx.query.limit === 0) {
+      return yield* mapNotFound(
+        Effect.gen(function* () {
+          if (ctx.query.before && ctx.query.limit === undefined) return yield* new HttpApiError.BadRequest({})
+          if (ctx.query.before) {
+            const before = ctx.query.before
+            yield* Effect.try({
+              try: () => MessageV2.cursor.decode(before),
+              catch: () => new HttpApiError.BadRequest({}),
+            })
+          }
+          if (ctx.query.limit === undefined || ctx.query.limit === 0) {
+            yield* session.get(ctx.params.sessionID)
+            return yield* session.messages({ sessionID: ctx.params.sessionID })
+          }
+
           yield* session.get(ctx.params.sessionID)
           yield* session.get(ctx.params.sessionID)
-          return yield* session.messages({ sessionID: ctx.params.sessionID })
-        }
-
-        yield* session.get(ctx.params.sessionID)
-        const page = MessageV2.page({
-          sessionID: ctx.params.sessionID,
-          limit: ctx.query.limit,
-          before: ctx.query.before,
-        })
-        if (!page.cursor) return page.items
-
-        const request = yield* HttpServerRequest.HttpServerRequest
-        const url = new URL(request.url, "http://localhost")
-        url.searchParams.set("limit", ctx.query.limit.toString())
-        url.searchParams.set("before", page.cursor)
-        return HttpServerResponse.jsonUnsafe(page.items, {
-          headers: {
-            "Access-Control-Expose-Headers": "Link, X-Next-Cursor",
-            Link: `<${url.toString()}>; rel="next"`,
-            "X-Next-Cursor": page.cursor,
-          },
-        })
-      }))
+          const page = MessageV2.page({
+            sessionID: ctx.params.sessionID,
+            limit: ctx.query.limit,
+            before: ctx.query.before,
+          })
+          if (!page.cursor) return page.items
+
+          const request = yield* HttpServerRequest.HttpServerRequest
+          const url = new URL(request.url, "http://localhost")
+          url.searchParams.set("limit", ctx.query.limit.toString())
+          url.searchParams.set("before", page.cursor)
+          return HttpServerResponse.jsonUnsafe(page.items, {
+            headers: {
+              "Access-Control-Expose-Headers": "Link, X-Next-Cursor",
+              Link: `<${url.toString()}>; rel="next"`,
+              "X-Next-Cursor": page.cursor,
+            },
+          })
+        }),
+      )
     })
     })
 
 
     const message = Effect.fn("SessionHttpApi.message")(function* (ctx: {
     const message = Effect.fn("SessionHttpApi.message")(function* (ctx: {

+ 36 - 15
packages/opencode/src/server/routes/instance/httpapi/instance-context.ts

@@ -19,11 +19,12 @@ import * as Socket from "effect/unstable/socket/Socket"
 
 
 type HandlerEffect = Effect.Effect<HttpServerResponse.HttpServerResponse, unhandled, never>
 type HandlerEffect = Effect.Effect<HttpServerResponse.HttpServerResponse, unhandled, never>
 
 
-export class InstanceContextMiddleware extends HttpApiMiddleware.Service<InstanceContextMiddleware, {
-  requires: Session.Service
-}>()(
-  "@opencode/ExperimentalHttpApiInstanceContext",
-) {}
+export class InstanceContextMiddleware extends HttpApiMiddleware.Service<
+  InstanceContextMiddleware,
+  {
+    requires: Session.Service
+  }
+>()("@opencode/ExperimentalHttpApiInstanceContext") {}
 
 
 function decode(input: string) {
 function decode(input: string) {
   try {
   try {
@@ -53,9 +54,14 @@ function requestHeaders(request: HttpServerRequest.HttpServerRequest) {
   return sourceRequest(request).headers
   return sourceRequest(request).headers
 }
 }
 
 
-function writeSocket(write: (data: string | Uint8Array | Socket.CloseEvent) => Effect.Effect<void, unknown>, data: unknown) {
+function writeSocket(
+  write: (data: string | Uint8Array | Socket.CloseEvent) => Effect.Effect<void, unknown>,
+  data: unknown,
+) {
   if (data instanceof Blob) {
   if (data instanceof Blob) {
-    void data.arrayBuffer().then((buffer) => Effect.runFork(write(new Uint8Array(buffer)).pipe(Effect.catch(() => Effect.void))))
+    void data
+      .arrayBuffer()
+      .then((buffer) => Effect.runFork(write(new Uint8Array(buffer)).pipe(Effect.catch(() => Effect.void))))
     return
     return
   }
   }
   if (typeof data === "string" || data instanceof Uint8Array) {
   if (typeof data === "string" || data instanceof Uint8Array) {
@@ -78,7 +84,8 @@ function proxyWebSocket(request: HttpServerRequest.HttpServerRequest, target: st
       queue.length = 0
       queue.length = 0
     }
     }
     remote.onmessage = (event) => writeSocket(write, event.data)
     remote.onmessage = (event) => writeSocket(write, event.data)
-    remote.onerror = () => Effect.runFork(write(new Socket.CloseEvent(1011, "proxy error")).pipe(Effect.catch(() => Effect.void)))
+    remote.onerror = () =>
+      Effect.runFork(write(new Socket.CloseEvent(1011, "proxy error")).pipe(Effect.catch(() => Effect.void)))
     remote.onclose = (event) =>
     remote.onclose = (event) =>
       Effect.runFork(write(new Socket.CloseEvent(event.code, event.reason)).pipe(Effect.catch(() => Effect.void)))
       Effect.runFork(write(new Socket.CloseEvent(event.code, event.reason)).pipe(Effect.catch(() => Effect.void)))
 
 
@@ -109,7 +116,9 @@ function proxyRemote(
   const url = workspaceProxyURL(target.url, requestURL)
   const url = workspaceProxyURL(target.url, requestURL)
   const source = sourceRequest(request)
   const source = sourceRequest(request)
   if (source.headers.get("upgrade")?.toLowerCase() === "websocket") return proxyWebSocket(request, url)
   if (source.headers.get("upgrade")?.toLowerCase() === "websocket") return proxyWebSocket(request, url)
-  return Effect.promise(() => ServerProxy.http(url, target.headers, source, workspace.id)).pipe(Effect.map(HttpServerResponse.raw))
+  return Effect.promise(() => ServerProxy.http(url, target.headers, source, workspace.id)).pipe(
+    Effect.map(HttpServerResponse.raw),
+  )
 }
 }
 
 
 function requestContext() {
 function requestContext() {
@@ -118,14 +127,19 @@ function requestContext() {
   )
   )
 }
 }
 
 
-function provideRequestContext(effect: HandlerEffect, request: HttpServerRequest.HttpServerRequest, sessionWorkspaceID?: WorkspaceID) {
+function provideRequestContext(
+  effect: HandlerEffect,
+  request: HttpServerRequest.HttpServerRequest,
+  sessionWorkspaceID?: WorkspaceID,
+) {
   return Effect.gen(function* () {
   return Effect.gen(function* () {
     const url = new URL(request.url, "http://localhost")
     const url = new URL(request.url, "http://localhost")
     const headers = requestHeaders(request)
     const headers = requestHeaders(request)
     const envWorkspaceID = Flag.OPENCODE_WORKSPACE_ID ? WorkspaceID.make(Flag.OPENCODE_WORKSPACE_ID) : undefined
     const envWorkspaceID = Flag.OPENCODE_WORKSPACE_ID ? WorkspaceID.make(Flag.OPENCODE_WORKSPACE_ID) : undefined
     const workspaceParam = url.searchParams.get("workspace")
     const workspaceParam = url.searchParams.get("workspace")
     const workspaceID = sessionWorkspaceID ?? (workspaceParam ? WorkspaceID.make(workspaceParam) : undefined)
     const workspaceID = sessionWorkspaceID ?? (workspaceParam ? WorkspaceID.make(workspaceParam) : undefined)
-    const workspace = workspaceID && !envWorkspaceID ? yield* Effect.promise(() => Workspace.get(workspaceID)) : undefined
+    const workspace =
+      workspaceID && !envWorkspaceID ? yield* Effect.promise(() => Workspace.get(workspaceID)) : undefined
 
 
     if (workspaceID && !workspace && !envWorkspaceID) {
     if (workspaceID && !workspace && !envWorkspaceID) {
       return HttpServerResponse.text(`Workspace not found: ${workspaceID}`, {
       return HttpServerResponse.text(`Workspace not found: ${workspaceID}`, {
@@ -134,7 +148,12 @@ function provideRequestContext(effect: HandlerEffect, request: HttpServerRequest
       })
       })
     }
     }
 
 
-    if (workspace && !isLocalWorkspaceRoute(request.method, url.pathname) && !url.pathname.startsWith("/console") && !envWorkspaceID) {
+    if (
+      workspace &&
+      !isLocalWorkspaceRoute(request.method, url.pathname) &&
+      !url.pathname.startsWith("/console") &&
+      !envWorkspaceID
+    ) {
       const adaptor = yield* Effect.promise(() => getAdaptor(workspace.projectID, workspace.type))
       const adaptor = yield* Effect.promise(() => getAdaptor(workspace.projectID, workspace.type))
       const target = yield* Effect.promise(() => Promise.resolve(adaptor.target(workspace)))
       const target = yield* Effect.promise(() => Promise.resolve(adaptor.target(workspace)))
       if (target.type === "remote") return yield* proxyRemote(request, workspace, target, url)
       if (target.type === "remote") return yield* proxyRemote(request, workspace, target, url)
@@ -186,6 +205,8 @@ export const instanceContextLayer = Layer.succeed(
   InstanceContextMiddleware.of((effect) => provideInstanceContext(effect)),
   InstanceContextMiddleware.of((effect) => provideInstanceContext(effect)),
 )
 )
 
 
-export const instanceRouterLayer = HttpRouter.middleware()(Effect.succeed((effect) =>
-  requestContext().pipe(Effect.flatMap((request) => provideRequestContext(effect, request))),
-)).layer
+export const instanceRouterLayer = HttpRouter.middleware()(
+  Effect.succeed((effect) =>
+    requestContext().pipe(Effect.flatMap((request) => provideRequestContext(effect, request))),
+  ),
+).layer

+ 4 - 1
packages/opencode/src/server/routes/instance/httpapi/lifecycle.ts

@@ -14,7 +14,10 @@ export const markInstanceForDisposal = (ctx: InstanceContext) =>
 
 
 export const markInstanceForReload = (ctx: InstanceContext, next: Parameters<typeof Instance.reload>[0]) =>
 export const markInstanceForReload = (ctx: InstanceContext, next: Parameters<typeof Instance.reload>[0]) =>
   HttpEffect.appendPreResponseHandler((_request, response) =>
   HttpEffect.appendPreResponseHandler((_request, response) =>
-    Effect.as(Effect.uninterruptible(Effect.promise(() => Instance.restore(ctx, () => Instance.reload(next)))), response),
+    Effect.as(
+      Effect.uninterruptible(Effect.promise(() => Instance.restore(ctx, () => Instance.reload(next)))),
+      response,
+    ),
   )
   )
 
 
 export const disposeMiddleware: HttpMiddleware.HttpMiddleware = (effect) =>
 export const disposeMiddleware: HttpMiddleware.HttpMiddleware = (effect) =>

+ 21 - 13
packages/opencode/src/server/routes/instance/httpapi/public.ts

@@ -126,8 +126,13 @@ function matchLegacyOpenApi(input: Record<string, unknown>) {
           // Workspace creation fields `branch` and `extra` are Schema.NullOr —
           // Workspace creation fields `branch` and `extra` are Schema.NullOr —
           // genuinely nullable, not just optional. Re-add the null that the
           // genuinely nullable, not just optional. Re-add the null that the
           // component-level strip above removed.
           // component-level strip above removed.
-          const ref = operation.requestBody.content?.["application/json"]?.schema?.$ref?.replace("#/components/schemas/", "")
-          const properties = ref ? spec.components?.schemas?.[ref]?.properties : operation.requestBody.content?.["application/json"]?.schema?.properties
+          const ref = operation.requestBody.content?.["application/json"]?.schema?.$ref?.replace(
+            "#/components/schemas/",
+            "",
+          )
+          const properties = ref
+            ? spec.components?.schemas?.[ref]?.properties
+            : operation.requestBody.content?.["application/json"]?.schema?.properties
           if (properties?.branch) properties.branch = { anyOf: [properties.branch, { type: "null" }] }
           if (properties?.branch) properties.branch = { anyOf: [properties.branch, { type: "null" }] }
           if (properties?.extra) properties.extra = { anyOf: [properties.extra, { type: "null" }] }
           if (properties?.extra) properties.extra = { anyOf: [properties.extra, { type: "null" }] }
         }
         }
@@ -150,7 +155,10 @@ function matchLegacyOpenApi(input: Record<string, unknown>) {
           description: "Event stream",
           description: "Event stream",
           content: {
           content: {
             "text/event-stream": {
             "text/event-stream": {
-              schema: path === "/event" ? { $ref: "#/components/schemas/Event" } : { $ref: "#/components/schemas/GlobalEvent" },
+              schema:
+                path === "/event"
+                  ? { $ref: "#/components/schemas/Event" }
+                  : { $ref: "#/components/schemas/GlobalEvent" },
             },
             },
           },
           },
         }
         }
@@ -251,7 +259,8 @@ function applyLegacySchemaOverrides(spec: OpenApiSpec) {
     schemas.Workspace.properties.directory = nullable(schemas.Workspace.properties.directory)
     schemas.Workspace.properties.directory = nullable(schemas.Workspace.properties.directory)
     schemas.Workspace.properties.extra = nullable(schemas.Workspace.properties.extra)
     schemas.Workspace.properties.extra = nullable(schemas.Workspace.properties.extra)
   }
   }
-  if (schemas.GlobalSession?.properties?.project) schemas.GlobalSession.properties.project = nullable(schemas.GlobalSession.properties.project)
+  if (schemas.GlobalSession?.properties?.project)
+    schemas.GlobalSession.properties.project = nullable(schemas.GlobalSession.properties.project)
   const providerOptions = schemas.ProviderConfig?.properties?.options
   const providerOptions = schemas.ProviderConfig?.properties?.options
   if (providerOptions) providerOptions.additionalProperties = {}
   if (providerOptions) providerOptions.additionalProperties = {}
   const model = schemas.ProviderConfig?.properties?.models?.additionalProperties
   const model = schemas.ProviderConfig?.properties?.models?.additionalProperties
@@ -486,12 +495,11 @@ function normalizeParameter(param: OpenApiParameter, route: string) {
   param.schema = stripOptionalNull(param.schema)
   param.schema = stripOptionalNull(param.schema)
 }
 }
 
 
-export const PublicApi = OpenCodeHttpApi
-  .annotateMerge(
-    OpenApi.annotations({
-      title: "opencode",
-      version: "1.0.0",
-      description: "opencode api",
-      transform: matchLegacyOpenApi,
-    }),
-  )
+export const PublicApi = OpenCodeHttpApi.annotateMerge(
+  OpenApi.annotations({
+    title: "opencode",
+    version: "1.0.0",
+    description: "opencode api",
+    transform: matchLegacyOpenApi,
+  }),
+)

+ 7 - 2
packages/opencode/src/server/server.ts

@@ -38,7 +38,9 @@ type ServerApp = {
   request(input: string | URL | Request, init?: RequestInit): Response | Promise<Response>
   request(input: string | URL | Request, init?: RequestInit): Response | Promise<Response>
 }
 }
 
 
-const DefaultHono = lazy(() => withBackend({ backend: "hono", reason: "stable" }, createHono({}, { backend: "hono", reason: "stable" })))
+const DefaultHono = lazy(() =>
+  withBackend({ backend: "hono", reason: "stable" }, createHono({}, { backend: "hono", reason: "stable" })),
+)
 const DefaultHttpApi = lazy(() => createDefaultHttpApi())
 const DefaultHttpApi = lazy(() => createDefaultHttpApi())
 
 
 function select() {
 function select() {
@@ -86,7 +88,10 @@ function createHttpApi() {
   }
   }
 }
 }
 
 
-function createHono(opts: { cors?: string[] }, selection: ServerBackend.Selection = ServerBackend.force(select(), "hono")) {
+function createHono(
+  opts: { cors?: string[] },
+  selection: ServerBackend.Selection = ServerBackend.force(select(), "hono"),
+) {
   const backendAttributes = ServerBackend.attributes(selection)
   const backendAttributes = ServerBackend.attributes(selection)
   const app = new Hono()
   const app = new Hono()
     .onError(ErrorMiddleware)
     .onError(ErrorMiddleware)

+ 3 - 1
packages/opencode/src/session/message-v2.ts

@@ -461,7 +461,9 @@ type AssistantError = z.infer<typeof AssistantErrorZod>
 // Effect Schema for the same union — used by HttpApi OpenAPI generation.
 // Effect Schema for the same union — used by HttpApi OpenAPI generation.
 const AssistantErrorSchema = Schema.Union([
 const AssistantErrorSchema = Schema.Union([
   AuthError.EffectSchema,
   AuthError.EffectSchema,
-  Schema.Struct({ name: Schema.Literal("UnknownError"), data: Schema.Struct({ message: Schema.String }) }).annotate({ identifier: "UnknownError" }),
+  Schema.Struct({ name: Schema.Literal("UnknownError"), data: Schema.Struct({ message: Schema.String }) }).annotate({
+    identifier: "UnknownError",
+  }),
   OutputLengthError.EffectSchema,
   OutputLengthError.EffectSchema,
   AbortedError.EffectSchema,
   AbortedError.EffectSchema,
   StructuredOutputError.EffectSchema,
   StructuredOutputError.EffectSchema,

+ 0 - 2
packages/opencode/src/util/schema.ts

@@ -11,8 +11,6 @@ export const PositiveInt = Schema.Int.check(Schema.isGreaterThan(0))
  */
  */
 export const NonNegativeInt = Schema.Int.check(Schema.isGreaterThanOrEqualTo(0))
 export const NonNegativeInt = Schema.Int.check(Schema.isGreaterThanOrEqualTo(0))
 
 
-
-
 /**
 /**
  * Optional public JSON field that can hold explicit `undefined` on the type
  * Optional public JSON field that can hold explicit `undefined` on the type
  * side but encodes it as an omitted key, matching legacy `JSON.stringify`.
  * side but encodes it as an omitted key, matching legacy `JSON.stringify`.

+ 3 - 3
packages/opencode/test/server/httpapi-bridge.test.ts

@@ -126,9 +126,9 @@ function requestBodyKey(spec: OpenApiSpec, body: unknown) {
 
 
 function requestBodySchemaKind(spec: OpenApiSpec, schema: OpenApiSchema | undefined) {
 function requestBodySchemaKind(spec: OpenApiSpec, schema: OpenApiSchema | undefined) {
   if (!schema) return ""
   if (!schema) return ""
-  const resolved = (schema.$ref ? spec.components?.schemas?.[schema.$ref.replace("#/components/schemas/", "")] : schema) as
-    | OpenApiSchema
-    | undefined
+  const resolved = (
+    schema.$ref ? spec.components?.schemas?.[schema.$ref.replace("#/components/schemas/", "")] : schema
+  ) as OpenApiSchema | undefined
   if (resolved?.properties) return "object"
   if (resolved?.properties) return "object"
   if (resolved?.anyOf ?? resolved?.oneOf ?? resolved?.allOf) return "object"
   if (resolved?.anyOf ?? resolved?.oneOf ?? resolved?.allOf) return "object"
   return resolved?.type ?? schema.type ?? "inline"
   return resolved?.type ?? schema.type ?? "inline"

+ 5 - 1
packages/opencode/test/server/httpapi-workspace.test.ts

@@ -214,7 +214,11 @@ describe("workspace HttpApi", () => {
     const workspace = await Instance.provide({
     const workspace = await Instance.provide({
       directory: tmp.path,
       directory: tmp.path,
       fn: async () => {
       fn: async () => {
-        registerAdaptor(Instance.project.id, "remote-target", remoteAdaptor(path.join(tmp.path, ".remote"), "https://remote.test/base"))
+        registerAdaptor(
+          Instance.project.id,
+          "remote-target",
+          remoteAdaptor(path.join(tmp.path, ".remote"), "https://remote.test/base"),
+        )
         return Workspace.create({
         return Workspace.create({
           type: "remote-target",
           type: "remote-target",
           branch: null,
           branch: null,

+ 267 - 95
packages/sdk/openapi.json

@@ -1339,10 +1339,14 @@
                     "type": "object",
                     "type": "object",
                     "properties": {
                     "properties": {
                       "rows": {
                       "rows": {
-                        "type": "number"
+                        "type": "integer",
+                        "exclusiveMinimum": 0,
+                        "maximum": 9007199254740991
                       },
                       },
                       "cols": {
                       "cols": {
-                        "type": "number"
+                        "type": "integer",
+                        "exclusiveMinimum": 0,
+                        "maximum": 9007199254740991
                       }
                       }
                     },
                     },
                     "required": ["rows", "cols"]
                     "required": ["rows", "cols"]
@@ -5595,10 +5599,14 @@
                         "required": ["text"]
                         "required": ["text"]
                       },
                       },
                       "line_number": {
                       "line_number": {
-                        "type": "number"
+                        "type": "integer",
+                        "minimum": 0,
+                        "maximum": 9007199254740991
                       },
                       },
                       "absolute_offset": {
                       "absolute_offset": {
-                        "type": "number"
+                        "type": "integer",
+                        "minimum": 0,
+                        "maximum": 9007199254740991
                       },
                       },
                       "submatches": {
                       "submatches": {
                         "type": "array",
                         "type": "array",
@@ -5615,10 +5623,14 @@
                               "required": ["text"]
                               "required": ["text"]
                             },
                             },
                             "start": {
                             "start": {
-                              "type": "number"
+                              "type": "integer",
+                              "minimum": 0,
+                              "maximum": 9007199254740991
                             },
                             },
                             "end": {
                             "end": {
-                              "type": "number"
+                              "type": "integer",
+                              "minimum": 0,
+                              "maximum": 9007199254740991
                             }
                             }
                           },
                           },
                           "required": ["match", "start", "end"]
                           "required": ["match", "start", "end"]
@@ -6901,7 +6913,9 @@
                   },
                   },
                   "duration": {
                   "duration": {
                     "description": "Duration in milliseconds",
                     "description": "Duration in milliseconds",
-                    "type": "number"
+                    "type": "integer",
+                    "exclusiveMinimum": 0,
+                    "maximum": 9007199254740991
                   }
                   }
                 },
                 },
                 "required": ["message", "variant"]
                 "required": ["message", "variant"]
@@ -7621,13 +7635,19 @@
             "type": "object",
             "type": "object",
             "properties": {
             "properties": {
               "created": {
               "created": {
-                "type": "number"
+                "type": "integer",
+                "minimum": 0,
+                "maximum": 9007199254740991
               },
               },
               "updated": {
               "updated": {
-                "type": "number"
+                "type": "integer",
+                "minimum": 0,
+                "maximum": 9007199254740991
               },
               },
               "initialized": {
               "initialized": {
-                "type": "number"
+                "type": "integer",
+                "minimum": 0,
+                "maximum": 9007199254740991
               }
               }
             },
             },
             "required": ["created", "updated"]
             "required": ["created", "updated"]
@@ -7913,10 +7933,14 @@
             "type": "string"
             "type": "string"
           },
           },
           "additions": {
           "additions": {
-            "type": "number"
+            "type": "integer",
+            "minimum": 0,
+            "maximum": 9007199254740991
           },
           },
           "deletions": {
           "deletions": {
-            "type": "number"
+            "type": "integer",
+            "minimum": 0,
+            "maximum": 9007199254740991
           },
           },
           "status": {
           "status": {
             "type": "string",
             "type": "string",
@@ -8039,7 +8063,9 @@
                 "type": "string"
                 "type": "string"
               },
               },
               "retries": {
               "retries": {
-                "type": "number"
+                "type": "integer",
+                "minimum": 0,
+                "maximum": 9007199254740991
               }
               }
             },
             },
             "required": ["message", "retries"]
             "required": ["message", "retries"]
@@ -8083,7 +8109,9 @@
                 "type": "string"
                 "type": "string"
               },
               },
               "statusCode": {
               "statusCode": {
-                "type": "number"
+                "type": "integer",
+                "minimum": 0,
+                "maximum": 9007199254740991
               },
               },
               "isRetryable": {
               "isRetryable": {
                 "type": "boolean"
                 "type": "boolean"
@@ -8420,13 +8448,17 @@
                 "const": "retry"
                 "const": "retry"
               },
               },
               "attempt": {
               "attempt": {
-                "type": "number"
+                "type": "integer",
+                "minimum": 0,
+                "maximum": 9007199254740991
               },
               },
               "message": {
               "message": {
                 "type": "string"
                 "type": "string"
               },
               },
               "next": {
               "next": {
-                "type": "number"
+                "type": "integer",
+                "minimum": 0,
+                "maximum": 9007199254740991
               }
               }
             },
             },
             "required": ["type", "attempt", "message", "next"]
             "required": ["type", "attempt", "message", "next"]
@@ -8591,7 +8623,9 @@
               },
               },
               "duration": {
               "duration": {
                 "description": "Duration in milliseconds",
                 "description": "Duration in milliseconds",
-                "type": "number"
+                "type": "integer",
+                "exclusiveMinimum": 0,
+                "maximum": 9007199254740991
               }
               }
             },
             },
             "required": ["message", "variant"]
             "required": ["message", "variant"]
@@ -8777,7 +8811,9 @@
             "enum": ["running", "exited"]
             "enum": ["running", "exited"]
           },
           },
           "pid": {
           "pid": {
-            "type": "number"
+            "type": "integer",
+            "exclusiveMinimum": 0,
+            "maximum": 9007199254740991
           }
           }
         },
         },
         "required": ["id", "title", "command", "args", "cwd", "status", "pid"]
         "required": ["id", "title", "command", "args", "cwd", "status", "pid"]
@@ -8835,7 +8871,9 @@
                 "pattern": "^pty.*"
                 "pattern": "^pty.*"
               },
               },
               "exitCode": {
               "exitCode": {
-                "type": "number"
+                "type": "integer",
+                "minimum": 0,
+                "maximum": 9007199254740991
               }
               }
             },
             },
             "required": ["id", "exitCode"]
             "required": ["id", "exitCode"]
@@ -9024,7 +9062,9 @@
             "type": "object",
             "type": "object",
             "properties": {
             "properties": {
               "created": {
               "created": {
-                "type": "number"
+                "type": "integer",
+                "minimum": 0,
+                "maximum": 9007199254740991
               }
               }
             },
             },
             "required": ["created"]
             "required": ["created"]
@@ -9102,10 +9142,14 @@
             "type": "object",
             "type": "object",
             "properties": {
             "properties": {
               "created": {
               "created": {
-                "type": "number"
+                "type": "integer",
+                "minimum": 0,
+                "maximum": 9007199254740991
               },
               },
               "completed": {
               "completed": {
-                "type": "number"
+                "type": "integer",
+                "minimum": 0,
+                "maximum": 9007199254740991
               }
               }
             },
             },
             "required": ["created"]
             "required": ["created"]
@@ -9173,25 +9217,37 @@
             "type": "object",
             "type": "object",
             "properties": {
             "properties": {
               "total": {
               "total": {
-                "type": "number"
+                "type": "integer",
+                "minimum": 0,
+                "maximum": 9007199254740991
               },
               },
               "input": {
               "input": {
-                "type": "number"
+                "type": "integer",
+                "minimum": 0,
+                "maximum": 9007199254740991
               },
               },
               "output": {
               "output": {
-                "type": "number"
+                "type": "integer",
+                "minimum": 0,
+                "maximum": 9007199254740991
               },
               },
               "reasoning": {
               "reasoning": {
-                "type": "number"
+                "type": "integer",
+                "minimum": 0,
+                "maximum": 9007199254740991
               },
               },
               "cache": {
               "cache": {
                 "type": "object",
                 "type": "object",
                 "properties": {
                 "properties": {
                   "read": {
                   "read": {
-                    "type": "number"
+                    "type": "integer",
+                    "minimum": 0,
+                    "maximum": 9007199254740991
                   },
                   },
                   "write": {
                   "write": {
-                    "type": "number"
+                    "type": "integer",
+                    "minimum": 0,
+                    "maximum": 9007199254740991
                   }
                   }
                 },
                 },
                 "required": ["read", "write"]
                 "required": ["read", "write"]
@@ -9311,10 +9367,14 @@
             "type": "object",
             "type": "object",
             "properties": {
             "properties": {
               "start": {
               "start": {
-                "type": "number"
+                "type": "integer",
+                "minimum": 0,
+                "maximum": 9007199254740991
               },
               },
               "end": {
               "end": {
-                "type": "number"
+                "type": "integer",
+                "minimum": 0,
+                "maximum": 9007199254740991
               }
               }
             },
             },
             "required": ["start"]
             "required": ["start"]
@@ -9408,10 +9468,14 @@
             "type": "object",
             "type": "object",
             "properties": {
             "properties": {
               "start": {
               "start": {
-                "type": "number"
+                "type": "integer",
+                "minimum": 0,
+                "maximum": 9007199254740991
               },
               },
               "end": {
               "end": {
-                "type": "number"
+                "type": "integer",
+                "minimum": 0,
+                "maximum": 9007199254740991
               }
               }
             },
             },
             "required": ["start"]
             "required": ["start"]
@@ -9427,12 +9491,12 @@
           },
           },
           "start": {
           "start": {
             "type": "integer",
             "type": "integer",
-            "minimum": -9007199254740991,
+            "minimum": 0,
             "maximum": 9007199254740991
             "maximum": 9007199254740991
           },
           },
           "end": {
           "end": {
             "type": "integer",
             "type": "integer",
-            "minimum": -9007199254740991,
+            "minimum": 0,
             "maximum": 9007199254740991
             "maximum": 9007199254740991
           }
           }
         },
         },
@@ -9461,10 +9525,14 @@
             "type": "object",
             "type": "object",
             "properties": {
             "properties": {
               "line": {
               "line": {
-                "type": "number"
+                "type": "integer",
+                "minimum": 0,
+                "maximum": 9007199254740991
               },
               },
               "character": {
               "character": {
-                "type": "number"
+                "type": "integer",
+                "minimum": 0,
+                "maximum": 9007199254740991
               }
               }
             },
             },
             "required": ["line", "character"]
             "required": ["line", "character"]
@@ -9473,10 +9541,14 @@
             "type": "object",
             "type": "object",
             "properties": {
             "properties": {
               "line": {
               "line": {
-                "type": "number"
+                "type": "integer",
+                "minimum": 0,
+                "maximum": 9007199254740991
               },
               },
               "character": {
               "character": {
-                "type": "number"
+                "type": "integer",
+                "minimum": 0,
+                "maximum": 9007199254740991
               }
               }
             },
             },
             "required": ["line", "character"]
             "required": ["line", "character"]
@@ -9505,7 +9577,7 @@
           },
           },
           "kind": {
           "kind": {
             "type": "integer",
             "type": "integer",
-            "minimum": -9007199254740991,
+            "minimum": 0,
             "maximum": 9007199254740991
             "maximum": 9007199254740991
           }
           }
         },
         },
@@ -9625,7 +9697,9 @@
             "type": "object",
             "type": "object",
             "properties": {
             "properties": {
               "start": {
               "start": {
-                "type": "number"
+                "type": "integer",
+                "minimum": 0,
+                "maximum": 9007199254740991
               }
               }
             },
             },
             "required": ["start"]
             "required": ["start"]
@@ -9664,13 +9738,19 @@
             "type": "object",
             "type": "object",
             "properties": {
             "properties": {
               "start": {
               "start": {
-                "type": "number"
+                "type": "integer",
+                "minimum": 0,
+                "maximum": 9007199254740991
               },
               },
               "end": {
               "end": {
-                "type": "number"
+                "type": "integer",
+                "minimum": 0,
+                "maximum": 9007199254740991
               },
               },
               "compacted": {
               "compacted": {
-                "type": "number"
+                "type": "integer",
+                "minimum": 0,
+                "maximum": 9007199254740991
               }
               }
             },
             },
             "required": ["start", "end"]
             "required": ["start", "end"]
@@ -9712,10 +9792,14 @@
             "type": "object",
             "type": "object",
             "properties": {
             "properties": {
               "start": {
               "start": {
-                "type": "number"
+                "type": "integer",
+                "minimum": 0,
+                "maximum": 9007199254740991
               },
               },
               "end": {
               "end": {
-                "type": "number"
+                "type": "integer",
+                "minimum": 0,
+                "maximum": 9007199254740991
               }
               }
             },
             },
             "required": ["start", "end"]
             "required": ["start", "end"]
@@ -9834,25 +9918,37 @@
             "type": "object",
             "type": "object",
             "properties": {
             "properties": {
               "total": {
               "total": {
-                "type": "number"
+                "type": "integer",
+                "minimum": 0,
+                "maximum": 9007199254740991
               },
               },
               "input": {
               "input": {
-                "type": "number"
+                "type": "integer",
+                "minimum": 0,
+                "maximum": 9007199254740991
               },
               },
               "output": {
               "output": {
-                "type": "number"
+                "type": "integer",
+                "minimum": 0,
+                "maximum": 9007199254740991
               },
               },
               "reasoning": {
               "reasoning": {
-                "type": "number"
+                "type": "integer",
+                "minimum": 0,
+                "maximum": 9007199254740991
               },
               },
               "cache": {
               "cache": {
                 "type": "object",
                 "type": "object",
                 "properties": {
                 "properties": {
                   "read": {
                   "read": {
-                    "type": "number"
+                    "type": "integer",
+                    "minimum": 0,
+                    "maximum": 9007199254740991
                   },
                   },
                   "write": {
                   "write": {
-                    "type": "number"
+                    "type": "integer",
+                    "minimum": 0,
+                    "maximum": 9007199254740991
                   }
                   }
                 },
                 },
                 "required": ["read", "write"]
                 "required": ["read", "write"]
@@ -9949,12 +10045,12 @@
               },
               },
               "start": {
               "start": {
                 "type": "integer",
                 "type": "integer",
-                "minimum": -9007199254740991,
+                "minimum": 0,
                 "maximum": 9007199254740991
                 "maximum": 9007199254740991
               },
               },
               "end": {
               "end": {
                 "type": "integer",
                 "type": "integer",
-                "minimum": -9007199254740991,
+                "minimum": 0,
                 "maximum": 9007199254740991
                 "maximum": 9007199254740991
               }
               }
             },
             },
@@ -9983,7 +10079,9 @@
             "const": "retry"
             "const": "retry"
           },
           },
           "attempt": {
           "attempt": {
-            "type": "number"
+            "type": "integer",
+            "minimum": 0,
+            "maximum": 9007199254740991
           },
           },
           "error": {
           "error": {
             "$ref": "#/components/schemas/APIError"
             "$ref": "#/components/schemas/APIError"
@@ -9992,7 +10090,9 @@
             "type": "object",
             "type": "object",
             "properties": {
             "properties": {
               "created": {
               "created": {
-                "type": "number"
+                "type": "integer",
+                "minimum": 0,
+                "maximum": 9007199254740991
               }
               }
             },
             },
             "required": ["created"]
             "required": ["created"]
@@ -10090,7 +10190,9 @@
                 "$ref": "#/components/schemas/Part"
                 "$ref": "#/components/schemas/Part"
               },
               },
               "time": {
               "time": {
-                "type": "number"
+                "type": "integer",
+                "minimum": 0,
+                "maximum": 9007199254740991
               }
               }
             },
             },
             "required": ["sessionID", "part", "time"]
             "required": ["sessionID", "part", "time"]
@@ -10182,13 +10284,19 @@
             "type": "object",
             "type": "object",
             "properties": {
             "properties": {
               "additions": {
               "additions": {
-                "type": "number"
+                "type": "integer",
+                "minimum": 0,
+                "maximum": 9007199254740991
               },
               },
               "deletions": {
               "deletions": {
-                "type": "number"
+                "type": "integer",
+                "minimum": 0,
+                "maximum": 9007199254740991
               },
               },
               "files": {
               "files": {
-                "type": "number"
+                "type": "integer",
+                "minimum": 0,
+                "maximum": 9007199254740991
               },
               },
               "diffs": {
               "diffs": {
                 "type": "array",
                 "type": "array",
@@ -10218,16 +10326,24 @@
             "type": "object",
             "type": "object",
             "properties": {
             "properties": {
               "created": {
               "created": {
-                "type": "number"
+                "type": "integer",
+                "minimum": 0,
+                "maximum": 9007199254740991
               },
               },
               "updated": {
               "updated": {
-                "type": "number"
+                "type": "integer",
+                "minimum": 0,
+                "maximum": 9007199254740991
               },
               },
               "compacting": {
               "compacting": {
-                "type": "number"
+                "type": "integer",
+                "minimum": 0,
+                "maximum": 9007199254740991
               },
               },
               "archived": {
               "archived": {
-                "type": "number"
+                "type": "integer",
+                "minimum": 0,
+                "maximum": 9007199254740991
               }
               }
             },
             },
             "required": ["created", "updated"]
             "required": ["created", "updated"]
@@ -10434,7 +10550,9 @@
                 "$ref": "#/components/schemas/Part"
                 "$ref": "#/components/schemas/Part"
               },
               },
               "time": {
               "time": {
-                "type": "number"
+                "type": "integer",
+                "minimum": 0,
+                "maximum": 9007199254740991
               }
               }
             },
             },
             "required": ["sessionID", "part", "time"]
             "required": ["sessionID", "part", "time"]
@@ -10631,13 +10749,19 @@
                         "type": "object",
                         "type": "object",
                         "properties": {
                         "properties": {
                           "additions": {
                           "additions": {
-                            "type": "number"
+                            "type": "integer",
+                            "minimum": 0,
+                            "maximum": 9007199254740991
                           },
                           },
                           "deletions": {
                           "deletions": {
-                            "type": "number"
+                            "type": "integer",
+                            "minimum": 0,
+                            "maximum": 9007199254740991
                           },
                           },
                           "files": {
                           "files": {
-                            "type": "number"
+                            "type": "integer",
+                            "minimum": 0,
+                            "maximum": 9007199254740991
                           },
                           },
                           "diffs": {
                           "diffs": {
                             "type": "array",
                             "type": "array",
@@ -10694,7 +10818,9 @@
                       "created": {
                       "created": {
                         "anyOf": [
                         "anyOf": [
                           {
                           {
-                            "type": "number"
+                            "type": "integer",
+                            "minimum": 0,
+                            "maximum": 9007199254740991
                           },
                           },
                           {
                           {
                             "type": "null"
                             "type": "null"
@@ -10704,7 +10830,9 @@
                       "updated": {
                       "updated": {
                         "anyOf": [
                         "anyOf": [
                           {
                           {
-                            "type": "number"
+                            "type": "integer",
+                            "minimum": 0,
+                            "maximum": 9007199254740991
                           },
                           },
                           {
                           {
                             "type": "null"
                             "type": "null"
@@ -10714,7 +10842,9 @@
                       "compacting": {
                       "compacting": {
                         "anyOf": [
                         "anyOf": [
                           {
                           {
-                            "type": "number"
+                            "type": "integer",
+                            "minimum": 0,
+                            "maximum": 9007199254740991
                           },
                           },
                           {
                           {
                             "type": "null"
                             "type": "null"
@@ -10724,7 +10854,9 @@
                       "archived": {
                       "archived": {
                         "anyOf": [
                         "anyOf": [
                           {
                           {
-                            "type": "number"
+                            "type": "integer",
+                            "minimum": 0,
+                            "maximum": 9007199254740991
                           },
                           },
                           {
                           {
                             "type": "null"
                             "type": "null"
@@ -11481,7 +11613,9 @@
           },
           },
           "timeout": {
           "timeout": {
             "description": "Timeout in ms for MCP server requests. Defaults to 5000 (5 seconds) if not specified.",
             "description": "Timeout in ms for MCP server requests. Defaults to 5000 (5 seconds) if not specified.",
-            "type": "number"
+            "type": "integer",
+            "exclusiveMinimum": 0,
+            "maximum": 9007199254740991
           }
           }
         },
         },
         "required": ["type", "command"]
         "required": ["type", "command"]
@@ -11547,7 +11681,9 @@
           },
           },
           "timeout": {
           "timeout": {
             "description": "Timeout in ms for MCP server requests. Defaults to 5000 (5 seconds) if not specified.",
             "description": "Timeout in ms for MCP server requests. Defaults to 5000 (5 seconds) if not specified.",
-            "type": "number"
+            "type": "integer",
+            "exclusiveMinimum": 0,
+            "maximum": 9007199254740991
           }
           }
         },
         },
         "required": ["type", "url"]
         "required": ["type", "url"]
@@ -12055,7 +12191,9 @@
             "type": "string"
             "type": "string"
           },
           },
           "expires": {
           "expires": {
-            "type": "number"
+            "type": "integer",
+            "minimum": 0,
+            "maximum": 9007199254740991
           },
           },
           "accountId": {
           "accountId": {
             "type": "string"
             "type": "string"
@@ -12458,7 +12596,9 @@
             "type": "string"
             "type": "string"
           },
           },
           "switchableOrgCount": {
           "switchableOrgCount": {
-            "type": "number"
+            "type": "integer",
+            "minimum": 0,
+            "maximum": 9007199254740991
           }
           }
         },
         },
         "required": ["consoleManagedProviders", "switchableOrgCount"]
         "required": ["consoleManagedProviders", "switchableOrgCount"]
@@ -12579,13 +12719,19 @@
             "type": "object",
             "type": "object",
             "properties": {
             "properties": {
               "additions": {
               "additions": {
-                "type": "number"
+                "type": "integer",
+                "minimum": 0,
+                "maximum": 9007199254740991
               },
               },
               "deletions": {
               "deletions": {
-                "type": "number"
+                "type": "integer",
+                "minimum": 0,
+                "maximum": 9007199254740991
               },
               },
               "files": {
               "files": {
-                "type": "number"
+                "type": "integer",
+                "minimum": 0,
+                "maximum": 9007199254740991
               },
               },
               "diffs": {
               "diffs": {
                 "type": "array",
                 "type": "array",
@@ -12615,16 +12761,24 @@
             "type": "object",
             "type": "object",
             "properties": {
             "properties": {
               "created": {
               "created": {
-                "type": "number"
+                "type": "integer",
+                "minimum": 0,
+                "maximum": 9007199254740991
               },
               },
               "updated": {
               "updated": {
-                "type": "number"
+                "type": "integer",
+                "minimum": 0,
+                "maximum": 9007199254740991
               },
               },
               "compacting": {
               "compacting": {
-                "type": "number"
+                "type": "integer",
+                "minimum": 0,
+                "maximum": 9007199254740991
               },
               },
               "archived": {
               "archived": {
-                "type": "number"
+                "type": "integer",
+                "minimum": 0,
+                "maximum": 9007199254740991
               }
               }
             },
             },
             "required": ["created", "updated"]
             "required": ["created", "updated"]
@@ -12710,10 +12864,14 @@
             "type": "object",
             "type": "object",
             "properties": {
             "properties": {
               "start": {
               "start": {
-                "type": "number"
+                "type": "integer",
+                "minimum": 0,
+                "maximum": 9007199254740991
               },
               },
               "end": {
               "end": {
-                "type": "number"
+                "type": "integer",
+                "minimum": 0,
+                "maximum": 9007199254740991
               }
               }
             },
             },
             "required": ["start"]
             "required": ["start"]
@@ -12776,12 +12934,12 @@
               },
               },
               "start": {
               "start": {
                 "type": "integer",
                 "type": "integer",
-                "minimum": -9007199254740991,
+                "minimum": 0,
                 "maximum": 9007199254740991
                 "maximum": 9007199254740991
               },
               },
               "end": {
               "end": {
                 "type": "integer",
                 "type": "integer",
-                "minimum": -9007199254740991,
+                "minimum": 0,
                 "maximum": 9007199254740991
                 "maximum": 9007199254740991
               }
               }
             },
             },
@@ -12956,7 +13114,9 @@
             "type": "string"
             "type": "string"
           },
           },
           "kind": {
           "kind": {
-            "type": "number"
+            "type": "integer",
+            "minimum": 0,
+            "maximum": 9007199254740991
           },
           },
           "location": {
           "location": {
             "type": "object",
             "type": "object",
@@ -13029,16 +13189,24 @@
                   "type": "object",
                   "type": "object",
                   "properties": {
                   "properties": {
                     "oldStart": {
                     "oldStart": {
-                      "type": "number"
+                      "type": "integer",
+                      "minimum": 0,
+                      "maximum": 9007199254740991
                     },
                     },
                     "oldLines": {
                     "oldLines": {
-                      "type": "number"
+                      "type": "integer",
+                      "minimum": 0,
+                      "maximum": 9007199254740991
                     },
                     },
                     "newStart": {
                     "newStart": {
-                      "type": "number"
+                      "type": "integer",
+                      "minimum": 0,
+                      "maximum": 9007199254740991
                     },
                     },
                     "newLines": {
                     "newLines": {
-                      "type": "number"
+                      "type": "integer",
+                      "minimum": 0,
+                      "maximum": 9007199254740991
                     },
                     },
                     "lines": {
                     "lines": {
                       "type": "array",
                       "type": "array",
@@ -13074,12 +13242,12 @@
           },
           },
           "added": {
           "added": {
             "type": "integer",
             "type": "integer",
-            "minimum": -9007199254740991,
+            "minimum": 0,
             "maximum": 9007199254740991
             "maximum": 9007199254740991
           },
           },
           "removed": {
           "removed": {
             "type": "integer",
             "type": "integer",
-            "minimum": -9007199254740991,
+            "minimum": 0,
             "maximum": 9007199254740991
             "maximum": 9007199254740991
           },
           },
           "status": {
           "status": {
@@ -13360,10 +13528,14 @@
             "type": "string"
             "type": "string"
           },
           },
           "additions": {
           "additions": {
-            "type": "number"
+            "type": "integer",
+            "minimum": 0,
+            "maximum": 9007199254740991
           },
           },
           "deletions": {
           "deletions": {
-            "type": "number"
+            "type": "integer",
+            "minimum": 0,
+            "maximum": 9007199254740991
           },
           },
           "status": {
           "status": {
             "type": "string",
             "type": "string",