浏览代码

feat(client): generate complete protocol client (#34164)

Kit Langton 2 月之前
父节点
当前提交
dfeb1b5051

+ 1 - 1
packages/client/README.md

@@ -7,7 +7,7 @@ Private generation target for clients derived directly from OpenCode's authorita
 - `@opencode-ai/client`: zero-Effect Promise client using `fetch`.
 - `@opencode-ai/client/effect`: rich Effect network client using an environment-provided `HttpClient`.
 
-The generated surface starts with the Session group from Server's concrete API. The build compiler reads `@opencode-ai/server/api`; the generated Effect runtime imports a client-local projection built from Protocol, with a generation-equivalence test preventing transport drift. Run `bun run generate` after changing the contract and `bun run check:generated` to detect committed-output drift.
+The generated surface includes every standard HTTP group from Server's concrete API. The build compiler reads `@opencode-ai/server/api`; the generated Effect runtime imports a client-local projection built from Protocol, with a generation-equivalence test preventing transport drift. Custom transports such as the PTY WebSocket connection remain outside the generic HTTP client. Run `bun run generate` after changing the contract and `bun run check:generated` to detect committed-output drift.
 
 The Effect entrypoint uses canonical decoded values such as `Session.ID`, `Location.Ref`, and `Prompt`. These datatypes come from the lightweight `@opencode-ai/schema` package and are re-exported so callers depend only on the client surface. Protocol owns endpoint construction and middleware placement; Server supplies the concrete middleware keys used by the build-time API.
 

+ 2 - 4
packages/client/script/build.ts

@@ -1,12 +1,10 @@
 import { NodeFileSystem } from "@effect/platform-node"
 import { compile, emitEffectImported, emitPromise, write } from "@opencode-ai/httpapi-codegen"
-import { ClientApi } from "../src/contract"
+import { ClientApi, endpointNames, groupNames, omitEndpoints } from "../src/contract"
 import { Effect } from "effect"
 import { fileURLToPath } from "url"
 
-const contract = compile(ClientApi, {
-  groupNames: { "server.session": "sessions", "server.event": "events" },
-})
+const contract = compile(ClientApi, { groupNames, endpointNames, omitEndpoints })
 
 await Effect.runPromise(
   Effect.all(

+ 37 - 5
packages/client/src/contract.ts

@@ -1,6 +1,6 @@
 import { makeDefaultApi } from "@opencode-ai/protocol/api"
 import { InvalidRequestError, SessionNotFoundError } from "@opencode-ai/protocol/errors"
-import { HttpApi, HttpApiMiddleware } from "effect/unstable/httpapi"
+import { HttpApiMiddleware } from "effect/unstable/httpapi"
 
 class LocationMiddleware extends HttpApiMiddleware.Service<LocationMiddleware>()(
   "@opencode-ai/client/LocationMiddleware",
@@ -11,11 +11,43 @@ class SessionLocationMiddleware extends HttpApiMiddleware.Service<SessionLocatio
   { error: [InvalidRequestError, SessionNotFoundError] },
 ) {}
 
-const Api = makeDefaultApi({
+export const ClientApi = makeDefaultApi({
   locationMiddleware: LocationMiddleware,
   sessionLocationMiddleware: SessionLocationMiddleware,
 })
 
-export const SessionGroup = Api.groups["server.session"]
-export const EventGroup = Api.groups["server.event"]
-export const ClientApi = HttpApi.make("opencode-client").add(SessionGroup).add(EventGroup)
+export const groupNames = {
+  "server.health": "health",
+  "server.location": "location",
+  "server.agent": "agents",
+  "server.session": "sessions",
+  "server.message": "messages",
+  "server.model": "models",
+  "server.provider": "providers",
+  "server.integration": "integrations",
+  "server.credential": "credentials",
+  "server.permission": "permissions",
+  "server.fs": "files",
+  "server.command": "commands",
+  "server.skill": "skills",
+  "server.event": "events",
+  "server.pty": "ptys",
+  "server.question": "questions",
+  "server.reference": "references",
+  "server.projectCopy": "projectCopies",
+} as const
+
+export const endpointNames = {
+  "session.messages": "list",
+  "integration.connect.key": "connectKey",
+  "integration.connect.oauth": "connectOauth",
+  "integration.attempt.status": "attemptStatus",
+  "integration.attempt.complete": "attemptComplete",
+  "integration.attempt.cancel": "attemptCancel",
+  "permission.request.list": "listRequests",
+  "permission.saved.list": "listSaved",
+  "permission.saved.remove": "removeSaved",
+  "question.request.list": "listRequests",
+} as const
+
+export const omitEndpoints = new Set(["fs.read", "pty.connect", "pty.connectToken"])

+ 12 - 0
packages/client/src/effect.ts

@@ -2,12 +2,24 @@
 // Core or Server. Preserve these datatype exports so internal model reorganizations do not require caller migrations.
 export * from "./generated-effect/index"
 export { Agent } from "@opencode-ai/schema/agent"
+export { Command } from "@opencode-ai/schema/command"
+export { Credential } from "@opencode-ai/schema/credential"
+export { FileSystem } from "@opencode-ai/schema/filesystem"
+export { Integration } from "@opencode-ai/schema/integration"
 export { Location } from "@opencode-ai/schema/location"
 export { Model } from "@opencode-ai/schema/model"
+export { Permission } from "@opencode-ai/schema/permission"
+export { PermissionSaved } from "@opencode-ai/schema/permission-saved"
+export { Project } from "@opencode-ai/schema/project"
+export { ProjectCopy } from "@opencode-ai/schema/project-copy"
 export { Provider } from "@opencode-ai/schema/provider"
+export { Pty } from "@opencode-ai/schema/pty"
+export { Question } from "@opencode-ai/schema/question"
+export { Reference } from "@opencode-ai/schema/reference"
 export { AbsolutePath, RelativePath } from "@opencode-ai/schema/schema"
 export { Session } from "@opencode-ai/schema/session"
 export { SessionInput } from "@opencode-ai/schema/session-input"
 export { SessionMessage } from "@opencode-ai/schema/session-message"
+export { Skill } from "@opencode-ai/schema/skill"
 export { Prompt } from "@opencode-ai/schema/prompt"
 export type { OpenCodeEvent } from "@opencode-ai/protocol/groups/event"

+ 608 - 129
packages/client/src/generated-effect/client.ts

@@ -13,202 +13,502 @@ const mapClientError = <E>(error: E) =>
     ? new ClientError({ cause: error })
     : error
 
-type Endpoint0_0Request = Parameters<RawClient["server.session"]["session.list"]>[0]
-type Endpoint0_0Input = {
-  readonly workspace?: Endpoint0_0Request["query"]["workspace"]
-  readonly limit?: Endpoint0_0Request["query"]["limit"]
-  readonly order?: Endpoint0_0Request["query"]["order"]
-  readonly search?: Endpoint0_0Request["query"]["search"]
-  readonly directory?: Endpoint0_0Request["query"]["directory"]
-  readonly project?: Endpoint0_0Request["query"]["project"]
-  readonly subpath?: Endpoint0_0Request["query"]["subpath"]
-  readonly cursor?: Endpoint0_0Request["query"]["cursor"]
-}
-const Endpoint0_0 = (raw: RawClient["server.session"]) => (input?: Endpoint0_0Input) =>
+const Endpoint0_0 = (raw: RawClient["server.health"]) => () =>
+  raw["health.get"]({}).pipe(Effect.mapError(mapClientError))
+
+const adaptGroup0 = (raw: RawClient["server.health"]) => ({ get: Endpoint0_0(raw) })
+
+type Endpoint1_0Request = Parameters<RawClient["server.location"]["location.get"]>[0]
+type Endpoint1_0Input = { readonly location?: Endpoint1_0Request["query"]["location"] }
+const Endpoint1_0 = (raw: RawClient["server.location"]) => (input?: Endpoint1_0Input) =>
+  raw["location.get"]({ query: { location: input?.["location"] } }).pipe(Effect.mapError(mapClientError))
+
+const adaptGroup1 = (raw: RawClient["server.location"]) => ({ get: Endpoint1_0(raw) })
+
+type Endpoint2_0Request = Parameters<RawClient["server.agent"]["agent.list"]>[0]
+type Endpoint2_0Input = { readonly location?: Endpoint2_0Request["query"]["location"] }
+const Endpoint2_0 = (raw: RawClient["server.agent"]) => (input?: Endpoint2_0Input) =>
+  raw["agent.list"]({ query: { location: input?.["location"] } }).pipe(Effect.mapError(mapClientError))
+
+const adaptGroup2 = (raw: RawClient["server.agent"]) => ({ list: Endpoint2_0(raw) })
+
+type Endpoint3_0Request = Parameters<RawClient["server.session"]["session.list"]>[0]
+type Endpoint3_0Input = {
+  readonly workspace?: Endpoint3_0Request["query"]["workspace"]
+  readonly limit?: Endpoint3_0Request["query"]["limit"]
+  readonly order?: Endpoint3_0Request["query"]["order"]
+  readonly search?: Endpoint3_0Request["query"]["search"]
+  readonly directory?: Endpoint3_0Request["query"]["directory"]
+  readonly project?: Endpoint3_0Request["query"]["project"]
+  readonly subpath?: Endpoint3_0Request["query"]["subpath"]
+  readonly cursor?: Endpoint3_0Request["query"]["cursor"]
+}
+const Endpoint3_0 = (raw: RawClient["server.session"]) => (input?: Endpoint3_0Input) =>
   raw["session.list"]({
     query: {
-      workspace: input?.workspace,
-      limit: input?.limit,
-      order: input?.order,
-      search: input?.search,
-      directory: input?.directory,
-      project: input?.project,
-      subpath: input?.subpath,
-      cursor: input?.cursor,
+      workspace: input?.["workspace"],
+      limit: input?.["limit"],
+      order: input?.["order"],
+      search: input?.["search"],
+      directory: input?.["directory"],
+      project: input?.["project"],
+      subpath: input?.["subpath"],
+      cursor: input?.["cursor"],
     },
   }).pipe(Effect.mapError(mapClientError))
 
-type Endpoint0_1Request = Parameters<RawClient["server.session"]["session.create"]>[0]
-type Endpoint0_1Input = {
-  readonly id?: Endpoint0_1Request["payload"]["id"]
-  readonly agent?: Endpoint0_1Request["payload"]["agent"]
-  readonly model?: Endpoint0_1Request["payload"]["model"]
-  readonly location?: Endpoint0_1Request["payload"]["location"]
+type Endpoint3_1Request = Parameters<RawClient["server.session"]["session.create"]>[0]
+type Endpoint3_1Input = {
+  readonly id?: Endpoint3_1Request["payload"]["id"]
+  readonly agent?: Endpoint3_1Request["payload"]["agent"]
+  readonly model?: Endpoint3_1Request["payload"]["model"]
+  readonly location?: Endpoint3_1Request["payload"]["location"]
 }
-const Endpoint0_1 = (raw: RawClient["server.session"]) => (input?: Endpoint0_1Input) =>
+const Endpoint3_1 = (raw: RawClient["server.session"]) => (input?: Endpoint3_1Input) =>
   raw["session.create"]({
-    payload: { id: input?.id, agent: input?.agent, model: input?.model, location: input?.location },
+    payload: { id: input?.["id"], agent: input?.["agent"], model: input?.["model"], location: input?.["location"] },
   }).pipe(
     Effect.mapError(mapClientError),
     Effect.map((value) => value.data),
   )
 
-const Endpoint0_2 = (raw: RawClient["server.session"]) => () =>
+const Endpoint3_2 = (raw: RawClient["server.session"]) => () =>
   raw["session.active"]({}).pipe(
     Effect.mapError(mapClientError),
     Effect.map((value) => value.data),
   )
 
-type Endpoint0_3Request = Parameters<RawClient["server.session"]["session.get"]>[0]
-type Endpoint0_3Input = { readonly sessionID: Endpoint0_3Request["params"]["sessionID"] }
-const Endpoint0_3 = (raw: RawClient["server.session"]) => (input: Endpoint0_3Input) =>
-  raw["session.get"]({ params: { sessionID: input.sessionID } }).pipe(
+type Endpoint3_3Request = Parameters<RawClient["server.session"]["session.get"]>[0]
+type Endpoint3_3Input = { readonly sessionID: Endpoint3_3Request["params"]["sessionID"] }
+const Endpoint3_3 = (raw: RawClient["server.session"]) => (input: Endpoint3_3Input) =>
+  raw["session.get"]({ params: { sessionID: input["sessionID"] } }).pipe(
     Effect.mapError(mapClientError),
     Effect.map((value) => value.data),
   )
 
-type Endpoint0_4Request = Parameters<RawClient["server.session"]["session.switchAgent"]>[0]
-type Endpoint0_4Input = {
-  readonly sessionID: Endpoint0_4Request["params"]["sessionID"]
-  readonly agent: Endpoint0_4Request["payload"]["agent"]
+type Endpoint3_4Request = Parameters<RawClient["server.session"]["session.switchAgent"]>[0]
+type Endpoint3_4Input = {
+  readonly sessionID: Endpoint3_4Request["params"]["sessionID"]
+  readonly agent: Endpoint3_4Request["payload"]["agent"]
 }
-const Endpoint0_4 = (raw: RawClient["server.session"]) => (input: Endpoint0_4Input) =>
-  raw["session.switchAgent"]({ params: { sessionID: input.sessionID }, payload: { agent: input.agent } }).pipe(
+const Endpoint3_4 = (raw: RawClient["server.session"]) => (input: Endpoint3_4Input) =>
+  raw["session.switchAgent"]({ params: { sessionID: input["sessionID"] }, payload: { agent: input["agent"] } }).pipe(
     Effect.mapError(mapClientError),
   )
 
-type Endpoint0_5Request = Parameters<RawClient["server.session"]["session.switchModel"]>[0]
-type Endpoint0_5Input = {
-  readonly sessionID: Endpoint0_5Request["params"]["sessionID"]
-  readonly model: Endpoint0_5Request["payload"]["model"]
+type Endpoint3_5Request = Parameters<RawClient["server.session"]["session.switchModel"]>[0]
+type Endpoint3_5Input = {
+  readonly sessionID: Endpoint3_5Request["params"]["sessionID"]
+  readonly model: Endpoint3_5Request["payload"]["model"]
 }
-const Endpoint0_5 = (raw: RawClient["server.session"]) => (input: Endpoint0_5Input) =>
-  raw["session.switchModel"]({ params: { sessionID: input.sessionID }, payload: { model: input.model } }).pipe(
+const Endpoint3_5 = (raw: RawClient["server.session"]) => (input: Endpoint3_5Input) =>
+  raw["session.switchModel"]({ params: { sessionID: input["sessionID"] }, payload: { model: input["model"] } }).pipe(
     Effect.mapError(mapClientError),
   )
 
-type Endpoint0_6Request = Parameters<RawClient["server.session"]["session.prompt"]>[0]
-type Endpoint0_6Input = {
-  readonly sessionID: Endpoint0_6Request["params"]["sessionID"]
-  readonly id?: Endpoint0_6Request["payload"]["id"]
-  readonly prompt: Endpoint0_6Request["payload"]["prompt"]
-  readonly delivery?: Endpoint0_6Request["payload"]["delivery"]
-  readonly resume?: Endpoint0_6Request["payload"]["resume"]
+type Endpoint3_6Request = Parameters<RawClient["server.session"]["session.prompt"]>[0]
+type Endpoint3_6Input = {
+  readonly sessionID: Endpoint3_6Request["params"]["sessionID"]
+  readonly id?: Endpoint3_6Request["payload"]["id"]
+  readonly prompt: Endpoint3_6Request["payload"]["prompt"]
+  readonly delivery?: Endpoint3_6Request["payload"]["delivery"]
+  readonly resume?: Endpoint3_6Request["payload"]["resume"]
 }
-const Endpoint0_6 = (raw: RawClient["server.session"]) => (input: Endpoint0_6Input) =>
+const Endpoint3_6 = (raw: RawClient["server.session"]) => (input: Endpoint3_6Input) =>
   raw["session.prompt"]({
-    params: { sessionID: input.sessionID },
-    payload: { id: input.id, prompt: input.prompt, delivery: input.delivery, resume: input.resume },
+    params: { sessionID: input["sessionID"] },
+    payload: { id: input["id"], prompt: input["prompt"], delivery: input["delivery"], resume: input["resume"] },
   }).pipe(
     Effect.mapError(mapClientError),
     Effect.map((value) => value.data),
   )
 
-type Endpoint0_7Request = Parameters<RawClient["server.session"]["session.compact"]>[0]
-type Endpoint0_7Input = { readonly sessionID: Endpoint0_7Request["params"]["sessionID"] }
-const Endpoint0_7 = (raw: RawClient["server.session"]) => (input: Endpoint0_7Input) =>
-  raw["session.compact"]({ params: { sessionID: input.sessionID } }).pipe(Effect.mapError(mapClientError))
+type Endpoint3_7Request = Parameters<RawClient["server.session"]["session.compact"]>[0]
+type Endpoint3_7Input = { readonly sessionID: Endpoint3_7Request["params"]["sessionID"] }
+const Endpoint3_7 = (raw: RawClient["server.session"]) => (input: Endpoint3_7Input) =>
+  raw["session.compact"]({ params: { sessionID: input["sessionID"] } }).pipe(Effect.mapError(mapClientError))
 
-type Endpoint0_8Request = Parameters<RawClient["server.session"]["session.wait"]>[0]
-type Endpoint0_8Input = { readonly sessionID: Endpoint0_8Request["params"]["sessionID"] }
-const Endpoint0_8 = (raw: RawClient["server.session"]) => (input: Endpoint0_8Input) =>
-  raw["session.wait"]({ params: { sessionID: input.sessionID } }).pipe(Effect.mapError(mapClientError))
+type Endpoint3_8Request = Parameters<RawClient["server.session"]["session.wait"]>[0]
+type Endpoint3_8Input = { readonly sessionID: Endpoint3_8Request["params"]["sessionID"] }
+const Endpoint3_8 = (raw: RawClient["server.session"]) => (input: Endpoint3_8Input) =>
+  raw["session.wait"]({ params: { sessionID: input["sessionID"] } }).pipe(Effect.mapError(mapClientError))
 
-type Endpoint0_9Request = Parameters<RawClient["server.session"]["session.revert.stage"]>[0]
-type Endpoint0_9Input = {
-  readonly sessionID: Endpoint0_9Request["params"]["sessionID"]
-  readonly messageID: Endpoint0_9Request["payload"]["messageID"]
-  readonly files?: Endpoint0_9Request["payload"]["files"]
+type Endpoint3_9Request = Parameters<RawClient["server.session"]["session.revert.stage"]>[0]
+type Endpoint3_9Input = {
+  readonly sessionID: Endpoint3_9Request["params"]["sessionID"]
+  readonly messageID: Endpoint3_9Request["payload"]["messageID"]
+  readonly files?: Endpoint3_9Request["payload"]["files"]
 }
-const Endpoint0_9 = (raw: RawClient["server.session"]) => (input: Endpoint0_9Input) =>
+const Endpoint3_9 = (raw: RawClient["server.session"]) => (input: Endpoint3_9Input) =>
   raw["session.revert.stage"]({
-    params: { sessionID: input.sessionID },
-    payload: { messageID: input.messageID, files: input.files },
+    params: { sessionID: input["sessionID"] },
+    payload: { messageID: input["messageID"], files: input["files"] },
   }).pipe(
     Effect.mapError(mapClientError),
     Effect.map((value) => value.data),
   )
 
-type Endpoint0_10Request = Parameters<RawClient["server.session"]["session.revert.clear"]>[0]
-type Endpoint0_10Input = { readonly sessionID: Endpoint0_10Request["params"]["sessionID"] }
-const Endpoint0_10 = (raw: RawClient["server.session"]) => (input: Endpoint0_10Input) =>
-  raw["session.revert.clear"]({ params: { sessionID: input.sessionID } }).pipe(Effect.mapError(mapClientError))
+type Endpoint3_10Request = Parameters<RawClient["server.session"]["session.revert.clear"]>[0]
+type Endpoint3_10Input = { readonly sessionID: Endpoint3_10Request["params"]["sessionID"] }
+const Endpoint3_10 = (raw: RawClient["server.session"]) => (input: Endpoint3_10Input) =>
+  raw["session.revert.clear"]({ params: { sessionID: input["sessionID"] } }).pipe(Effect.mapError(mapClientError))
 
-type Endpoint0_11Request = Parameters<RawClient["server.session"]["session.revert.commit"]>[0]
-type Endpoint0_11Input = { readonly sessionID: Endpoint0_11Request["params"]["sessionID"] }
-const Endpoint0_11 = (raw: RawClient["server.session"]) => (input: Endpoint0_11Input) =>
-  raw["session.revert.commit"]({ params: { sessionID: input.sessionID } }).pipe(Effect.mapError(mapClientError))
+type Endpoint3_11Request = Parameters<RawClient["server.session"]["session.revert.commit"]>[0]
+type Endpoint3_11Input = { readonly sessionID: Endpoint3_11Request["params"]["sessionID"] }
+const Endpoint3_11 = (raw: RawClient["server.session"]) => (input: Endpoint3_11Input) =>
+  raw["session.revert.commit"]({ params: { sessionID: input["sessionID"] } }).pipe(Effect.mapError(mapClientError))
 
-type Endpoint0_12Request = Parameters<RawClient["server.session"]["session.context"]>[0]
-type Endpoint0_12Input = { readonly sessionID: Endpoint0_12Request["params"]["sessionID"] }
-const Endpoint0_12 = (raw: RawClient["server.session"]) => (input: Endpoint0_12Input) =>
-  raw["session.context"]({ params: { sessionID: input.sessionID } }).pipe(
+type Endpoint3_12Request = Parameters<RawClient["server.session"]["session.context"]>[0]
+type Endpoint3_12Input = { readonly sessionID: Endpoint3_12Request["params"]["sessionID"] }
+const Endpoint3_12 = (raw: RawClient["server.session"]) => (input: Endpoint3_12Input) =>
+  raw["session.context"]({ params: { sessionID: input["sessionID"] } }).pipe(
     Effect.mapError(mapClientError),
     Effect.map((value) => value.data),
   )
 
-type Endpoint0_13Request = Parameters<RawClient["server.session"]["session.history"]>[0]
-type Endpoint0_13Input = {
-  readonly sessionID: Endpoint0_13Request["params"]["sessionID"]
-  readonly limit?: Endpoint0_13Request["query"]["limit"]
-  readonly after?: Endpoint0_13Request["query"]["after"]
+type Endpoint3_13Request = Parameters<RawClient["server.session"]["session.history"]>[0]
+type Endpoint3_13Input = {
+  readonly sessionID: Endpoint3_13Request["params"]["sessionID"]
+  readonly limit?: Endpoint3_13Request["query"]["limit"]
+  readonly after?: Endpoint3_13Request["query"]["after"]
 }
-const Endpoint0_13 = (raw: RawClient["server.session"]) => (input: Endpoint0_13Input) =>
+const Endpoint3_13 = (raw: RawClient["server.session"]) => (input: Endpoint3_13Input) =>
   raw["session.history"]({
-    params: { sessionID: input.sessionID },
-    query: { limit: input.limit, after: input.after },
+    params: { sessionID: input["sessionID"] },
+    query: { limit: input["limit"], after: input["after"] },
   }).pipe(Effect.mapError(mapClientError))
 
-type Endpoint0_14Request = Parameters<RawClient["server.session"]["session.events"]>[0]
-type Endpoint0_14Input = {
-  readonly sessionID: Endpoint0_14Request["params"]["sessionID"]
-  readonly after?: Endpoint0_14Request["query"]["after"]
+type Endpoint3_14Request = Parameters<RawClient["server.session"]["session.events"]>[0]
+type Endpoint3_14Input = {
+  readonly sessionID: Endpoint3_14Request["params"]["sessionID"]
+  readonly after?: Endpoint3_14Request["query"]["after"]
 }
-const Endpoint0_14 = (raw: RawClient["server.session"]) => (input: Endpoint0_14Input) =>
+const Endpoint3_14 = (raw: RawClient["server.session"]) => (input: Endpoint3_14Input) =>
   Stream.unwrap(
-    raw["session.events"]({ params: { sessionID: input.sessionID }, query: { after: input.after } }).pipe(
+    raw["session.events"]({ params: { sessionID: input["sessionID"] }, query: { after: input["after"] } }).pipe(
       Effect.mapError(mapClientError),
       Effect.map((stream) => stream.pipe(Stream.mapError(mapClientError))),
     ),
   )
 
-type Endpoint0_15Request = Parameters<RawClient["server.session"]["session.interrupt"]>[0]
-type Endpoint0_15Input = { readonly sessionID: Endpoint0_15Request["params"]["sessionID"] }
-const Endpoint0_15 = (raw: RawClient["server.session"]) => (input: Endpoint0_15Input) =>
-  raw["session.interrupt"]({ params: { sessionID: input.sessionID } }).pipe(Effect.mapError(mapClientError))
+type Endpoint3_15Request = Parameters<RawClient["server.session"]["session.interrupt"]>[0]
+type Endpoint3_15Input = { readonly sessionID: Endpoint3_15Request["params"]["sessionID"] }
+const Endpoint3_15 = (raw: RawClient["server.session"]) => (input: Endpoint3_15Input) =>
+  raw["session.interrupt"]({ params: { sessionID: input["sessionID"] } }).pipe(Effect.mapError(mapClientError))
 
-type Endpoint0_16Request = Parameters<RawClient["server.session"]["session.message"]>[0]
-type Endpoint0_16Input = {
-  readonly sessionID: Endpoint0_16Request["params"]["sessionID"]
-  readonly messageID: Endpoint0_16Request["params"]["messageID"]
+type Endpoint3_16Request = Parameters<RawClient["server.session"]["session.message"]>[0]
+type Endpoint3_16Input = {
+  readonly sessionID: Endpoint3_16Request["params"]["sessionID"]
+  readonly messageID: Endpoint3_16Request["params"]["messageID"]
 }
-const Endpoint0_16 = (raw: RawClient["server.session"]) => (input: Endpoint0_16Input) =>
-  raw["session.message"]({ params: { sessionID: input.sessionID, messageID: input.messageID } }).pipe(
+const Endpoint3_16 = (raw: RawClient["server.session"]) => (input: Endpoint3_16Input) =>
+  raw["session.message"]({ params: { sessionID: input["sessionID"], messageID: input["messageID"] } }).pipe(
     Effect.mapError(mapClientError),
     Effect.map((value) => value.data),
   )
 
-const adaptGroup0 = (raw: RawClient["server.session"]) => ({
-  list: Endpoint0_0(raw),
-  create: Endpoint0_1(raw),
-  active: Endpoint0_2(raw),
-  get: Endpoint0_3(raw),
-  switchAgent: Endpoint0_4(raw),
-  switchModel: Endpoint0_5(raw),
-  prompt: Endpoint0_6(raw),
-  compact: Endpoint0_7(raw),
-  wait: Endpoint0_8(raw),
-  stage: Endpoint0_9(raw),
-  clear: Endpoint0_10(raw),
-  commit: Endpoint0_11(raw),
-  context: Endpoint0_12(raw),
-  history: Endpoint0_13(raw),
-  events: Endpoint0_14(raw),
-  interrupt: Endpoint0_15(raw),
-  message: Endpoint0_16(raw),
+const adaptGroup3 = (raw: RawClient["server.session"]) => ({
+  list: Endpoint3_0(raw),
+  create: Endpoint3_1(raw),
+  active: Endpoint3_2(raw),
+  get: Endpoint3_3(raw),
+  switchAgent: Endpoint3_4(raw),
+  switchModel: Endpoint3_5(raw),
+  prompt: Endpoint3_6(raw),
+  compact: Endpoint3_7(raw),
+  wait: Endpoint3_8(raw),
+  stage: Endpoint3_9(raw),
+  clear: Endpoint3_10(raw),
+  commit: Endpoint3_11(raw),
+  context: Endpoint3_12(raw),
+  history: Endpoint3_13(raw),
+  events: Endpoint3_14(raw),
+  interrupt: Endpoint3_15(raw),
+  message: Endpoint3_16(raw),
+})
+
+type Endpoint4_0Request = Parameters<RawClient["server.message"]["session.messages"]>[0]
+type Endpoint4_0Input = {
+  readonly sessionID: Endpoint4_0Request["params"]["sessionID"]
+  readonly limit?: Endpoint4_0Request["query"]["limit"]
+  readonly order?: Endpoint4_0Request["query"]["order"]
+  readonly cursor?: Endpoint4_0Request["query"]["cursor"]
+}
+const Endpoint4_0 = (raw: RawClient["server.message"]) => (input: Endpoint4_0Input) =>
+  raw["session.messages"]({
+    params: { sessionID: input["sessionID"] },
+    query: { limit: input["limit"], order: input["order"], cursor: input["cursor"] },
+  }).pipe(Effect.mapError(mapClientError))
+
+const adaptGroup4 = (raw: RawClient["server.message"]) => ({ list: Endpoint4_0(raw) })
+
+type Endpoint5_0Request = Parameters<RawClient["server.model"]["model.list"]>[0]
+type Endpoint5_0Input = { readonly location?: Endpoint5_0Request["query"]["location"] }
+const Endpoint5_0 = (raw: RawClient["server.model"]) => (input?: Endpoint5_0Input) =>
+  raw["model.list"]({ query: { location: input?.["location"] } }).pipe(Effect.mapError(mapClientError))
+
+const adaptGroup5 = (raw: RawClient["server.model"]) => ({ list: Endpoint5_0(raw) })
+
+type Endpoint6_0Request = Parameters<RawClient["server.provider"]["provider.list"]>[0]
+type Endpoint6_0Input = { readonly location?: Endpoint6_0Request["query"]["location"] }
+const Endpoint6_0 = (raw: RawClient["server.provider"]) => (input?: Endpoint6_0Input) =>
+  raw["provider.list"]({ query: { location: input?.["location"] } }).pipe(Effect.mapError(mapClientError))
+
+type Endpoint6_1Request = Parameters<RawClient["server.provider"]["provider.get"]>[0]
+type Endpoint6_1Input = {
+  readonly providerID: Endpoint6_1Request["params"]["providerID"]
+  readonly location?: Endpoint6_1Request["query"]["location"]
+}
+const Endpoint6_1 = (raw: RawClient["server.provider"]) => (input: Endpoint6_1Input) =>
+  raw["provider.get"]({ params: { providerID: input["providerID"] }, query: { location: input["location"] } }).pipe(
+    Effect.mapError(mapClientError),
+  )
+
+const adaptGroup6 = (raw: RawClient["server.provider"]) => ({ list: Endpoint6_0(raw), get: Endpoint6_1(raw) })
+
+type Endpoint7_0Request = Parameters<RawClient["server.integration"]["integration.list"]>[0]
+type Endpoint7_0Input = { readonly location?: Endpoint7_0Request["query"]["location"] }
+const Endpoint7_0 = (raw: RawClient["server.integration"]) => (input?: Endpoint7_0Input) =>
+  raw["integration.list"]({ query: { location: input?.["location"] } }).pipe(Effect.mapError(mapClientError))
+
+type Endpoint7_1Request = Parameters<RawClient["server.integration"]["integration.get"]>[0]
+type Endpoint7_1Input = {
+  readonly integrationID: Endpoint7_1Request["params"]["integrationID"]
+  readonly location?: Endpoint7_1Request["query"]["location"]
+}
+const Endpoint7_1 = (raw: RawClient["server.integration"]) => (input: Endpoint7_1Input) =>
+  raw["integration.get"]({
+    params: { integrationID: input["integrationID"] },
+    query: { location: input["location"] },
+  }).pipe(Effect.mapError(mapClientError))
+
+type Endpoint7_2Request = Parameters<RawClient["server.integration"]["integration.connect.key"]>[0]
+type Endpoint7_2Input = {
+  readonly integrationID: Endpoint7_2Request["params"]["integrationID"]
+  readonly location?: Endpoint7_2Request["query"]["location"]
+  readonly key: Endpoint7_2Request["payload"]["key"]
+  readonly label?: Endpoint7_2Request["payload"]["label"]
+}
+const Endpoint7_2 = (raw: RawClient["server.integration"]) => (input: Endpoint7_2Input) =>
+  raw["integration.connect.key"]({
+    params: { integrationID: input["integrationID"] },
+    query: { location: input["location"] },
+    payload: { key: input["key"], label: input["label"] },
+  }).pipe(Effect.mapError(mapClientError))
+
+type Endpoint7_3Request = Parameters<RawClient["server.integration"]["integration.connect.oauth"]>[0]
+type Endpoint7_3Input = {
+  readonly integrationID: Endpoint7_3Request["params"]["integrationID"]
+  readonly location?: Endpoint7_3Request["query"]["location"]
+  readonly methodID: Endpoint7_3Request["payload"]["methodID"]
+  readonly inputs: Endpoint7_3Request["payload"]["inputs"]
+  readonly label?: Endpoint7_3Request["payload"]["label"]
+}
+const Endpoint7_3 = (raw: RawClient["server.integration"]) => (input: Endpoint7_3Input) =>
+  raw["integration.connect.oauth"]({
+    params: { integrationID: input["integrationID"] },
+    query: { location: input["location"] },
+    payload: { methodID: input["methodID"], inputs: input["inputs"], label: input["label"] },
+  }).pipe(Effect.mapError(mapClientError))
+
+type Endpoint7_4Request = Parameters<RawClient["server.integration"]["integration.attempt.status"]>[0]
+type Endpoint7_4Input = {
+  readonly attemptID: Endpoint7_4Request["params"]["attemptID"]
+  readonly location?: Endpoint7_4Request["query"]["location"]
+}
+const Endpoint7_4 = (raw: RawClient["server.integration"]) => (input: Endpoint7_4Input) =>
+  raw["integration.attempt.status"]({
+    params: { attemptID: input["attemptID"] },
+    query: { location: input["location"] },
+  }).pipe(Effect.mapError(mapClientError))
+
+type Endpoint7_5Request = Parameters<RawClient["server.integration"]["integration.attempt.complete"]>[0]
+type Endpoint7_5Input = {
+  readonly attemptID: Endpoint7_5Request["params"]["attemptID"]
+  readonly location?: Endpoint7_5Request["query"]["location"]
+  readonly code?: Endpoint7_5Request["payload"]["code"]
+}
+const Endpoint7_5 = (raw: RawClient["server.integration"]) => (input: Endpoint7_5Input) =>
+  raw["integration.attempt.complete"]({
+    params: { attemptID: input["attemptID"] },
+    query: { location: input["location"] },
+    payload: { code: input["code"] },
+  }).pipe(Effect.mapError(mapClientError))
+
+type Endpoint7_6Request = Parameters<RawClient["server.integration"]["integration.attempt.cancel"]>[0]
+type Endpoint7_6Input = {
+  readonly attemptID: Endpoint7_6Request["params"]["attemptID"]
+  readonly location?: Endpoint7_6Request["query"]["location"]
+}
+const Endpoint7_6 = (raw: RawClient["server.integration"]) => (input: Endpoint7_6Input) =>
+  raw["integration.attempt.cancel"]({
+    params: { attemptID: input["attemptID"] },
+    query: { location: input["location"] },
+  }).pipe(Effect.mapError(mapClientError))
+
+const adaptGroup7 = (raw: RawClient["server.integration"]) => ({
+  list: Endpoint7_0(raw),
+  get: Endpoint7_1(raw),
+  connectKey: Endpoint7_2(raw),
+  connectOauth: Endpoint7_3(raw),
+  attemptStatus: Endpoint7_4(raw),
+  attemptComplete: Endpoint7_5(raw),
+  attemptCancel: Endpoint7_6(raw),
 })
 
-const Endpoint1_0 = (raw: RawClient["server.event"]) => () =>
+type Endpoint8_0Request = Parameters<RawClient["server.credential"]["credential.update"]>[0]
+type Endpoint8_0Input = {
+  readonly credentialID: Endpoint8_0Request["params"]["credentialID"]
+  readonly location?: Endpoint8_0Request["query"]["location"]
+  readonly label: Endpoint8_0Request["payload"]["label"]
+}
+const Endpoint8_0 = (raw: RawClient["server.credential"]) => (input: Endpoint8_0Input) =>
+  raw["credential.update"]({
+    params: { credentialID: input["credentialID"] },
+    query: { location: input["location"] },
+    payload: { label: input["label"] },
+  }).pipe(Effect.mapError(mapClientError))
+
+type Endpoint8_1Request = Parameters<RawClient["server.credential"]["credential.remove"]>[0]
+type Endpoint8_1Input = {
+  readonly credentialID: Endpoint8_1Request["params"]["credentialID"]
+  readonly location?: Endpoint8_1Request["query"]["location"]
+}
+const Endpoint8_1 = (raw: RawClient["server.credential"]) => (input: Endpoint8_1Input) =>
+  raw["credential.remove"]({
+    params: { credentialID: input["credentialID"] },
+    query: { location: input["location"] },
+  }).pipe(Effect.mapError(mapClientError))
+
+const adaptGroup8 = (raw: RawClient["server.credential"]) => ({ update: Endpoint8_0(raw), remove: Endpoint8_1(raw) })
+
+type Endpoint9_0Request = Parameters<RawClient["server.permission"]["permission.request.list"]>[0]
+type Endpoint9_0Input = { readonly location?: Endpoint9_0Request["query"]["location"] }
+const Endpoint9_0 = (raw: RawClient["server.permission"]) => (input?: Endpoint9_0Input) =>
+  raw["permission.request.list"]({ query: { location: input?.["location"] } }).pipe(Effect.mapError(mapClientError))
+
+type Endpoint9_1Request = Parameters<RawClient["server.permission"]["permission.saved.list"]>[0]
+type Endpoint9_1Input = { readonly projectID?: Endpoint9_1Request["query"]["projectID"] }
+const Endpoint9_1 = (raw: RawClient["server.permission"]) => (input?: Endpoint9_1Input) =>
+  raw["permission.saved.list"]({ query: { projectID: input?.["projectID"] } }).pipe(
+    Effect.mapError(mapClientError),
+    Effect.map((value) => value.data),
+  )
+
+type Endpoint9_2Request = Parameters<RawClient["server.permission"]["permission.saved.remove"]>[0]
+type Endpoint9_2Input = { readonly id: Endpoint9_2Request["params"]["id"] }
+const Endpoint9_2 = (raw: RawClient["server.permission"]) => (input: Endpoint9_2Input) =>
+  raw["permission.saved.remove"]({ params: { id: input["id"] } }).pipe(Effect.mapError(mapClientError))
+
+type Endpoint9_3Request = Parameters<RawClient["server.permission"]["session.permission.create"]>[0]
+type Endpoint9_3Input = {
+  readonly sessionID: Endpoint9_3Request["params"]["sessionID"]
+  readonly id?: Endpoint9_3Request["payload"]["id"]
+  readonly action: Endpoint9_3Request["payload"]["action"]
+  readonly resources: Endpoint9_3Request["payload"]["resources"]
+  readonly save?: Endpoint9_3Request["payload"]["save"]
+  readonly metadata?: Endpoint9_3Request["payload"]["metadata"]
+  readonly source?: Endpoint9_3Request["payload"]["source"]
+  readonly agent?: Endpoint9_3Request["payload"]["agent"]
+}
+const Endpoint9_3 = (raw: RawClient["server.permission"]) => (input: Endpoint9_3Input) =>
+  raw["session.permission.create"]({
+    params: { sessionID: input["sessionID"] },
+    payload: {
+      id: input["id"],
+      action: input["action"],
+      resources: input["resources"],
+      save: input["save"],
+      metadata: input["metadata"],
+      source: input["source"],
+      agent: input["agent"],
+    },
+  }).pipe(
+    Effect.mapError(mapClientError),
+    Effect.map((value) => value.data),
+  )
+
+type Endpoint9_4Request = Parameters<RawClient["server.permission"]["session.permission.list"]>[0]
+type Endpoint9_4Input = { readonly sessionID: Endpoint9_4Request["params"]["sessionID"] }
+const Endpoint9_4 = (raw: RawClient["server.permission"]) => (input: Endpoint9_4Input) =>
+  raw["session.permission.list"]({ params: { sessionID: input["sessionID"] } }).pipe(
+    Effect.mapError(mapClientError),
+    Effect.map((value) => value.data),
+  )
+
+type Endpoint9_5Request = Parameters<RawClient["server.permission"]["session.permission.get"]>[0]
+type Endpoint9_5Input = {
+  readonly sessionID: Endpoint9_5Request["params"]["sessionID"]
+  readonly requestID: Endpoint9_5Request["params"]["requestID"]
+}
+const Endpoint9_5 = (raw: RawClient["server.permission"]) => (input: Endpoint9_5Input) =>
+  raw["session.permission.get"]({ params: { sessionID: input["sessionID"], requestID: input["requestID"] } }).pipe(
+    Effect.mapError(mapClientError),
+    Effect.map((value) => value.data),
+  )
+
+type Endpoint9_6Request = Parameters<RawClient["server.permission"]["session.permission.reply"]>[0]
+type Endpoint9_6Input = {
+  readonly sessionID: Endpoint9_6Request["params"]["sessionID"]
+  readonly requestID: Endpoint9_6Request["params"]["requestID"]
+  readonly reply: Endpoint9_6Request["payload"]["reply"]
+  readonly message?: Endpoint9_6Request["payload"]["message"]
+}
+const Endpoint9_6 = (raw: RawClient["server.permission"]) => (input: Endpoint9_6Input) =>
+  raw["session.permission.reply"]({
+    params: { sessionID: input["sessionID"], requestID: input["requestID"] },
+    payload: { reply: input["reply"], message: input["message"] },
+  }).pipe(Effect.mapError(mapClientError))
+
+const adaptGroup9 = (raw: RawClient["server.permission"]) => ({
+  listRequests: Endpoint9_0(raw),
+  listSaved: Endpoint9_1(raw),
+  removeSaved: Endpoint9_2(raw),
+  create: Endpoint9_3(raw),
+  list: Endpoint9_4(raw),
+  get: Endpoint9_5(raw),
+  reply: Endpoint9_6(raw),
+})
+
+type Endpoint10_0Request = Parameters<RawClient["server.fs"]["fs.list"]>[0]
+type Endpoint10_0Input = {
+  readonly location?: Endpoint10_0Request["query"]["location"]
+  readonly path?: Endpoint10_0Request["query"]["path"]
+}
+const Endpoint10_0 = (raw: RawClient["server.fs"]) => (input?: Endpoint10_0Input) =>
+  raw["fs.list"]({ query: { location: input?.["location"], path: input?.["path"] } }).pipe(
+    Effect.mapError(mapClientError),
+  )
+
+type Endpoint10_1Request = Parameters<RawClient["server.fs"]["fs.find"]>[0]
+type Endpoint10_1Input = {
+  readonly location?: Endpoint10_1Request["query"]["location"]
+  readonly query: Endpoint10_1Request["query"]["query"]
+  readonly type?: Endpoint10_1Request["query"]["type"]
+  readonly limit?: Endpoint10_1Request["query"]["limit"]
+}
+const Endpoint10_1 = (raw: RawClient["server.fs"]) => (input: Endpoint10_1Input) =>
+  raw["fs.find"]({
+    query: { location: input["location"], query: input["query"], type: input["type"], limit: input["limit"] },
+  }).pipe(Effect.mapError(mapClientError))
+
+const adaptGroup10 = (raw: RawClient["server.fs"]) => ({ list: Endpoint10_0(raw), find: Endpoint10_1(raw) })
+
+type Endpoint11_0Request = Parameters<RawClient["server.command"]["command.list"]>[0]
+type Endpoint11_0Input = { readonly location?: Endpoint11_0Request["query"]["location"] }
+const Endpoint11_0 = (raw: RawClient["server.command"]) => (input?: Endpoint11_0Input) =>
+  raw["command.list"]({ query: { location: input?.["location"] } }).pipe(Effect.mapError(mapClientError))
+
+const adaptGroup11 = (raw: RawClient["server.command"]) => ({ list: Endpoint11_0(raw) })
+
+type Endpoint12_0Request = Parameters<RawClient["server.skill"]["skill.list"]>[0]
+type Endpoint12_0Input = { readonly location?: Endpoint12_0Request["query"]["location"] }
+const Endpoint12_0 = (raw: RawClient["server.skill"]) => (input?: Endpoint12_0Input) =>
+  raw["skill.list"]({ query: { location: input?.["location"] } }).pipe(Effect.mapError(mapClientError))
+
+const adaptGroup12 = (raw: RawClient["server.skill"]) => ({ list: Endpoint12_0(raw) })
+
+const Endpoint13_0 = (raw: RawClient["server.event"]) => () =>
   Stream.unwrap(
     raw["event.subscribe"]({}).pipe(
       Effect.mapError(mapClientError),
@@ -216,11 +516,190 @@ const Endpoint1_0 = (raw: RawClient["server.event"]) => () =>
     ),
   )
 
-const adaptGroup1 = (raw: RawClient["server.event"]) => ({ subscribe: Endpoint1_0(raw) })
+const adaptGroup13 = (raw: RawClient["server.event"]) => ({ subscribe: Endpoint13_0(raw) })
+
+type Endpoint14_0Request = Parameters<RawClient["server.pty"]["pty.list"]>[0]
+type Endpoint14_0Input = { readonly location?: Endpoint14_0Request["query"]["location"] }
+const Endpoint14_0 = (raw: RawClient["server.pty"]) => (input?: Endpoint14_0Input) =>
+  raw["pty.list"]({ query: { location: input?.["location"] } }).pipe(Effect.mapError(mapClientError))
+
+type Endpoint14_1Request = Parameters<RawClient["server.pty"]["pty.create"]>[0]
+type Endpoint14_1Input = {
+  readonly location?: Endpoint14_1Request["query"]["location"]
+  readonly command?: Endpoint14_1Request["payload"]["command"]
+  readonly args?: Endpoint14_1Request["payload"]["args"]
+  readonly cwd?: Endpoint14_1Request["payload"]["cwd"]
+  readonly title?: Endpoint14_1Request["payload"]["title"]
+  readonly env?: Endpoint14_1Request["payload"]["env"]
+}
+const Endpoint14_1 = (raw: RawClient["server.pty"]) => (input?: Endpoint14_1Input) =>
+  raw["pty.create"]({
+    query: { location: input?.["location"] },
+    payload: {
+      command: input?.["command"],
+      args: input?.["args"],
+      cwd: input?.["cwd"],
+      title: input?.["title"],
+      env: input?.["env"],
+    },
+  }).pipe(Effect.mapError(mapClientError))
+
+type Endpoint14_2Request = Parameters<RawClient["server.pty"]["pty.get"]>[0]
+type Endpoint14_2Input = {
+  readonly ptyID: Endpoint14_2Request["params"]["ptyID"]
+  readonly location?: Endpoint14_2Request["query"]["location"]
+}
+const Endpoint14_2 = (raw: RawClient["server.pty"]) => (input: Endpoint14_2Input) =>
+  raw["pty.get"]({ params: { ptyID: input["ptyID"] }, query: { location: input["location"] } }).pipe(
+    Effect.mapError(mapClientError),
+  )
+
+type Endpoint14_3Request = Parameters<RawClient["server.pty"]["pty.update"]>[0]
+type Endpoint14_3Input = {
+  readonly ptyID: Endpoint14_3Request["params"]["ptyID"]
+  readonly location?: Endpoint14_3Request["query"]["location"]
+  readonly title?: Endpoint14_3Request["payload"]["title"]
+  readonly size?: Endpoint14_3Request["payload"]["size"]
+}
+const Endpoint14_3 = (raw: RawClient["server.pty"]) => (input: Endpoint14_3Input) =>
+  raw["pty.update"]({
+    params: { ptyID: input["ptyID"] },
+    query: { location: input["location"] },
+    payload: { title: input["title"], size: input["size"] },
+  }).pipe(Effect.mapError(mapClientError))
+
+type Endpoint14_4Request = Parameters<RawClient["server.pty"]["pty.remove"]>[0]
+type Endpoint14_4Input = {
+  readonly ptyID: Endpoint14_4Request["params"]["ptyID"]
+  readonly location?: Endpoint14_4Request["query"]["location"]
+}
+const Endpoint14_4 = (raw: RawClient["server.pty"]) => (input: Endpoint14_4Input) =>
+  raw["pty.remove"]({ params: { ptyID: input["ptyID"] }, query: { location: input["location"] } }).pipe(
+    Effect.mapError(mapClientError),
+  )
+
+const adaptGroup14 = (raw: RawClient["server.pty"]) => ({
+  list: Endpoint14_0(raw),
+  create: Endpoint14_1(raw),
+  get: Endpoint14_2(raw),
+  update: Endpoint14_3(raw),
+  remove: Endpoint14_4(raw),
+})
+
+type Endpoint15_0Request = Parameters<RawClient["server.question"]["question.request.list"]>[0]
+type Endpoint15_0Input = { readonly location?: Endpoint15_0Request["query"]["location"] }
+const Endpoint15_0 = (raw: RawClient["server.question"]) => (input?: Endpoint15_0Input) =>
+  raw["question.request.list"]({ query: { location: input?.["location"] } }).pipe(Effect.mapError(mapClientError))
+
+type Endpoint15_1Request = Parameters<RawClient["server.question"]["session.question.list"]>[0]
+type Endpoint15_1Input = { readonly sessionID: Endpoint15_1Request["params"]["sessionID"] }
+const Endpoint15_1 = (raw: RawClient["server.question"]) => (input: Endpoint15_1Input) =>
+  raw["session.question.list"]({ params: { sessionID: input["sessionID"] } }).pipe(
+    Effect.mapError(mapClientError),
+    Effect.map((value) => value.data),
+  )
+
+type Endpoint15_2Request = Parameters<RawClient["server.question"]["session.question.reply"]>[0]
+type Endpoint15_2Input = {
+  readonly sessionID: Endpoint15_2Request["params"]["sessionID"]
+  readonly requestID: Endpoint15_2Request["params"]["requestID"]
+  readonly answers: Endpoint15_2Request["payload"]["answers"]
+}
+const Endpoint15_2 = (raw: RawClient["server.question"]) => (input: Endpoint15_2Input) =>
+  raw["session.question.reply"]({
+    params: { sessionID: input["sessionID"], requestID: input["requestID"] },
+    payload: { answers: input["answers"] },
+  }).pipe(Effect.mapError(mapClientError))
+
+type Endpoint15_3Request = Parameters<RawClient["server.question"]["session.question.reject"]>[0]
+type Endpoint15_3Input = {
+  readonly sessionID: Endpoint15_3Request["params"]["sessionID"]
+  readonly requestID: Endpoint15_3Request["params"]["requestID"]
+}
+const Endpoint15_3 = (raw: RawClient["server.question"]) => (input: Endpoint15_3Input) =>
+  raw["session.question.reject"]({ params: { sessionID: input["sessionID"], requestID: input["requestID"] } }).pipe(
+    Effect.mapError(mapClientError),
+  )
+
+const adaptGroup15 = (raw: RawClient["server.question"]) => ({
+  listRequests: Endpoint15_0(raw),
+  list: Endpoint15_1(raw),
+  reply: Endpoint15_2(raw),
+  reject: Endpoint15_3(raw),
+})
+
+type Endpoint16_0Request = Parameters<RawClient["server.reference"]["reference.list"]>[0]
+type Endpoint16_0Input = { readonly location?: Endpoint16_0Request["query"]["location"] }
+const Endpoint16_0 = (raw: RawClient["server.reference"]) => (input?: Endpoint16_0Input) =>
+  raw["reference.list"]({ query: { location: input?.["location"] } }).pipe(Effect.mapError(mapClientError))
+
+const adaptGroup16 = (raw: RawClient["server.reference"]) => ({ list: Endpoint16_0(raw) })
+
+type Endpoint17_0Request = Parameters<RawClient["server.projectCopy"]["projectCopy.create"]>[0]
+type Endpoint17_0Input = {
+  readonly projectID: Endpoint17_0Request["params"]["projectID"]
+  readonly location?: Endpoint17_0Request["query"]["location"]
+  readonly strategy: Endpoint17_0Request["payload"]["strategy"]
+  readonly directory: Endpoint17_0Request["payload"]["directory"]
+  readonly name?: Endpoint17_0Request["payload"]["name"]
+}
+const Endpoint17_0 = (raw: RawClient["server.projectCopy"]) => (input: Endpoint17_0Input) =>
+  raw["projectCopy.create"]({
+    params: { projectID: input["projectID"] },
+    query: { location: input["location"] },
+    payload: { strategy: input["strategy"], directory: input["directory"], name: input["name"] },
+  }).pipe(Effect.mapError(mapClientError))
+
+type Endpoint17_1Request = Parameters<RawClient["server.projectCopy"]["projectCopy.remove"]>[0]
+type Endpoint17_1Input = {
+  readonly projectID: Endpoint17_1Request["params"]["projectID"]
+  readonly location?: Endpoint17_1Request["query"]["location"]
+  readonly directory: Endpoint17_1Request["payload"]["directory"]
+  readonly force: Endpoint17_1Request["payload"]["force"]
+}
+const Endpoint17_1 = (raw: RawClient["server.projectCopy"]) => (input: Endpoint17_1Input) =>
+  raw["projectCopy.remove"]({
+    params: { projectID: input["projectID"] },
+    query: { location: input["location"] },
+    payload: { directory: input["directory"], force: input["force"] },
+  }).pipe(Effect.mapError(mapClientError))
+
+type Endpoint17_2Request = Parameters<RawClient["server.projectCopy"]["projectCopy.refresh"]>[0]
+type Endpoint17_2Input = {
+  readonly projectID: Endpoint17_2Request["params"]["projectID"]
+  readonly location?: Endpoint17_2Request["query"]["location"]
+}
+const Endpoint17_2 = (raw: RawClient["server.projectCopy"]) => (input: Endpoint17_2Input) =>
+  raw["projectCopy.refresh"]({
+    params: { projectID: input["projectID"] },
+    query: { location: input["location"] },
+  }).pipe(Effect.mapError(mapClientError))
+
+const adaptGroup17 = (raw: RawClient["server.projectCopy"]) => ({
+  create: Endpoint17_0(raw),
+  remove: Endpoint17_1(raw),
+  refresh: Endpoint17_2(raw),
+})
 
 const adaptClient = (raw: RawClient) => ({
-  sessions: adaptGroup0(raw["server.session"]),
-  events: adaptGroup1(raw["server.event"]),
+  health: adaptGroup0(raw["server.health"]),
+  location: adaptGroup1(raw["server.location"]),
+  agents: adaptGroup2(raw["server.agent"]),
+  sessions: adaptGroup3(raw["server.session"]),
+  messages: adaptGroup4(raw["server.message"]),
+  models: adaptGroup5(raw["server.model"]),
+  providers: adaptGroup6(raw["server.provider"]),
+  integrations: adaptGroup7(raw["server.integration"]),
+  credentials: adaptGroup8(raw["server.credential"]),
+  permissions: adaptGroup9(raw["server.permission"]),
+  files: adaptGroup10(raw["server.fs"]),
+  commands: adaptGroup11(raw["server.command"]),
+  skills: adaptGroup12(raw["server.skill"]),
+  events: adaptGroup13(raw["server.event"]),
+  ptys: adaptGroup14(raw["server.pty"]),
+  questions: adaptGroup15(raw["server.question"]),
+  references: adaptGroup16(raw["server.reference"]),
+  projectCopies: adaptGroup17(raw["server.projectCopy"]),
 })
 
 export const make = (options?: { readonly baseUrl?: URL | string }) =>

+ 621 - 15
packages/client/src/generated/client.ts

@@ -1,4 +1,9 @@
 import type {
+  HealthGetOutput,
+  LocationGetInput,
+  LocationGetOutput,
+  AgentsListInput,
+  AgentsListOutput,
   SessionsListInput,
   SessionsListOutput,
   SessionsCreateInput,
@@ -32,7 +37,81 @@ import type {
   SessionsInterruptOutput,
   SessionsMessageInput,
   SessionsMessageOutput,
+  MessagesListInput,
+  MessagesListOutput,
+  ModelsListInput,
+  ModelsListOutput,
+  ProvidersListInput,
+  ProvidersListOutput,
+  ProvidersGetInput,
+  ProvidersGetOutput,
+  IntegrationsListInput,
+  IntegrationsListOutput,
+  IntegrationsGetInput,
+  IntegrationsGetOutput,
+  IntegrationsConnectKeyInput,
+  IntegrationsConnectKeyOutput,
+  IntegrationsConnectOauthInput,
+  IntegrationsConnectOauthOutput,
+  IntegrationsAttemptStatusInput,
+  IntegrationsAttemptStatusOutput,
+  IntegrationsAttemptCompleteInput,
+  IntegrationsAttemptCompleteOutput,
+  IntegrationsAttemptCancelInput,
+  IntegrationsAttemptCancelOutput,
+  CredentialsUpdateInput,
+  CredentialsUpdateOutput,
+  CredentialsRemoveInput,
+  CredentialsRemoveOutput,
+  PermissionsListRequestsInput,
+  PermissionsListRequestsOutput,
+  PermissionsListSavedInput,
+  PermissionsListSavedOutput,
+  PermissionsRemoveSavedInput,
+  PermissionsRemoveSavedOutput,
+  PermissionsCreateInput,
+  PermissionsCreateOutput,
+  PermissionsListInput,
+  PermissionsListOutput,
+  PermissionsGetInput,
+  PermissionsGetOutput,
+  PermissionsReplyInput,
+  PermissionsReplyOutput,
+  FilesListInput,
+  FilesListOutput,
+  FilesFindInput,
+  FilesFindOutput,
+  CommandsListInput,
+  CommandsListOutput,
+  SkillsListInput,
+  SkillsListOutput,
   EventsSubscribeOutput,
+  PtysListInput,
+  PtysListOutput,
+  PtysCreateInput,
+  PtysCreateOutput,
+  PtysGetInput,
+  PtysGetOutput,
+  PtysUpdateInput,
+  PtysUpdateOutput,
+  PtysRemoveInput,
+  PtysRemoveOutput,
+  QuestionsListRequestsInput,
+  QuestionsListRequestsOutput,
+  QuestionsListInput,
+  QuestionsListOutput,
+  QuestionsReplyInput,
+  QuestionsReplyOutput,
+  QuestionsRejectInput,
+  QuestionsRejectOutput,
+  ReferencesListInput,
+  ReferencesListOutput,
+  ProjectCopiesCreateInput,
+  ProjectCopiesCreateOutput,
+  ProjectCopiesRemoveInput,
+  ProjectCopiesRemoveOutput,
+  ProjectCopiesRefreshInput,
+  ProjectCopiesRefreshOutput,
 } from "./types"
 import { ClientError } from "./client-error"
 
@@ -168,6 +247,41 @@ export function make(options: ClientOptions) {
   })
 
   return {
+    health: {
+      get: (requestOptions?: RequestOptions) =>
+        request<HealthGetOutput>(
+          { method: "GET", path: `/api/health`, successStatus: 200, declaredStatuses: [401, 400], empty: false },
+          requestOptions,
+        ),
+    },
+    location: {
+      get: (input?: LocationGetInput, requestOptions?: RequestOptions) =>
+        request<LocationGetOutput>(
+          {
+            method: "GET",
+            path: `/api/location`,
+            query: { location: input?.["location"] },
+            successStatus: 200,
+            declaredStatuses: [401, 400],
+            empty: false,
+          },
+          requestOptions,
+        ),
+    },
+    agents: {
+      list: (input?: AgentsListInput, requestOptions?: RequestOptions) =>
+        request<AgentsListOutput>(
+          {
+            method: "GET",
+            path: `/api/agent`,
+            query: { location: input?.["location"] },
+            successStatus: 200,
+            declaredStatuses: [401, 400],
+            empty: false,
+          },
+          requestOptions,
+        ),
+    },
     sessions: {
       list: (input?: SessionsListInput, requestOptions?: RequestOptions) =>
         request<SessionsListOutput>(
@@ -175,14 +289,14 @@ export function make(options: ClientOptions) {
             method: "GET",
             path: `/api/session`,
             query: {
-              workspace: input?.workspace,
-              limit: input?.limit,
-              order: input?.order,
-              search: input?.search,
-              directory: input?.directory,
-              project: input?.project,
-              subpath: input?.subpath,
-              cursor: input?.cursor,
+              workspace: input?.["workspace"],
+              limit: input?.["limit"],
+              order: input?.["order"],
+              search: input?.["search"],
+              directory: input?.["directory"],
+              project: input?.["project"],
+              subpath: input?.["subpath"],
+              cursor: input?.["cursor"],
             },
             successStatus: 200,
             declaredStatuses: [400, 401],
@@ -195,7 +309,12 @@ export function make(options: ClientOptions) {
           {
             method: "POST",
             path: `/api/session`,
-            body: { id: input?.id, agent: input?.agent, model: input?.model, location: input?.location },
+            body: {
+              id: input?.["id"],
+              agent: input?.["agent"],
+              model: input?.["model"],
+              location: input?.["location"],
+            },
             successStatus: 200,
             declaredStatuses: [401, 400],
             empty: false,
@@ -229,7 +348,7 @@ export function make(options: ClientOptions) {
           {
             method: "POST",
             path: `/api/session/${encodeURIComponent(input.sessionID)}/agent`,
-            body: { agent: input.agent },
+            body: { agent: input["agent"] },
             successStatus: 204,
             declaredStatuses: [404, 400, 401],
             empty: true,
@@ -241,7 +360,7 @@ export function make(options: ClientOptions) {
           {
             method: "POST",
             path: `/api/session/${encodeURIComponent(input.sessionID)}/model`,
-            body: { model: input.model },
+            body: { model: input["model"] },
             successStatus: 204,
             declaredStatuses: [404, 400, 401],
             empty: true,
@@ -253,7 +372,7 @@ export function make(options: ClientOptions) {
           {
             method: "POST",
             path: `/api/session/${encodeURIComponent(input.sessionID)}/prompt`,
-            body: { id: input.id, prompt: input.prompt, delivery: input.delivery, resume: input.resume },
+            body: { id: input["id"], prompt: input["prompt"], delivery: input["delivery"], resume: input["resume"] },
             successStatus: 200,
             declaredStatuses: [409, 404, 400, 401],
             empty: false,
@@ -287,7 +406,7 @@ export function make(options: ClientOptions) {
           {
             method: "POST",
             path: `/api/session/${encodeURIComponent(input.sessionID)}/revert/stage`,
-            body: { messageID: input.messageID, files: input.files },
+            body: { messageID: input["messageID"], files: input["files"] },
             successStatus: 200,
             declaredStatuses: [404, 500, 400, 401],
             empty: false,
@@ -332,7 +451,7 @@ export function make(options: ClientOptions) {
           {
             method: "GET",
             path: `/api/session/${encodeURIComponent(input.sessionID)}/history`,
-            query: { limit: input.limit, after: input.after },
+            query: { limit: input["limit"], after: input["after"] },
             successStatus: 200,
             declaredStatuses: [404, 400, 401],
             empty: false,
@@ -344,7 +463,7 @@ export function make(options: ClientOptions) {
           {
             method: "GET",
             path: `/api/session/${encodeURIComponent(input.sessionID)}/event`,
-            query: { after: input.after },
+            query: { after: input["after"] },
             successStatus: 200,
             declaredStatuses: [404, 400, 401],
             empty: false,
@@ -374,6 +493,321 @@ export function make(options: ClientOptions) {
           requestOptions,
         ).then((value) => value.data),
     },
+    messages: {
+      list: (input: MessagesListInput, requestOptions?: RequestOptions) =>
+        request<MessagesListOutput>(
+          {
+            method: "GET",
+            path: `/api/session/${encodeURIComponent(input.sessionID)}/message`,
+            query: { limit: input["limit"], order: input["order"], cursor: input["cursor"] },
+            successStatus: 200,
+            declaredStatuses: [400, 404, 500, 401],
+            empty: false,
+          },
+          requestOptions,
+        ),
+    },
+    models: {
+      list: (input?: ModelsListInput, requestOptions?: RequestOptions) =>
+        request<ModelsListOutput>(
+          {
+            method: "GET",
+            path: `/api/model`,
+            query: { location: input?.["location"] },
+            successStatus: 200,
+            declaredStatuses: [503, 401, 400],
+            empty: false,
+          },
+          requestOptions,
+        ),
+    },
+    providers: {
+      list: (input?: ProvidersListInput, requestOptions?: RequestOptions) =>
+        request<ProvidersListOutput>(
+          {
+            method: "GET",
+            path: `/api/provider`,
+            query: { location: input?.["location"] },
+            successStatus: 200,
+            declaredStatuses: [503, 401, 400],
+            empty: false,
+          },
+          requestOptions,
+        ),
+      get: (input: ProvidersGetInput, requestOptions?: RequestOptions) =>
+        request<ProvidersGetOutput>(
+          {
+            method: "GET",
+            path: `/api/provider/${encodeURIComponent(input.providerID)}`,
+            query: { location: input["location"] },
+            successStatus: 200,
+            declaredStatuses: [404, 503, 401, 400],
+            empty: false,
+          },
+          requestOptions,
+        ),
+    },
+    integrations: {
+      list: (input?: IntegrationsListInput, requestOptions?: RequestOptions) =>
+        request<IntegrationsListOutput>(
+          {
+            method: "GET",
+            path: `/api/integration`,
+            query: { location: input?.["location"] },
+            successStatus: 200,
+            declaredStatuses: [401, 400],
+            empty: false,
+          },
+          requestOptions,
+        ),
+      get: (input: IntegrationsGetInput, requestOptions?: RequestOptions) =>
+        request<IntegrationsGetOutput>(
+          {
+            method: "GET",
+            path: `/api/integration/${encodeURIComponent(input.integrationID)}`,
+            query: { location: input["location"] },
+            successStatus: 200,
+            declaredStatuses: [401, 400],
+            empty: false,
+          },
+          requestOptions,
+        ),
+      connectKey: (input: IntegrationsConnectKeyInput, requestOptions?: RequestOptions) =>
+        request<IntegrationsConnectKeyOutput>(
+          {
+            method: "POST",
+            path: `/api/integration/${encodeURIComponent(input.integrationID)}/connect/key`,
+            query: { location: input["location"] },
+            body: { key: input["key"], label: input["label"] },
+            successStatus: 204,
+            declaredStatuses: [400, 401],
+            empty: true,
+          },
+          requestOptions,
+        ),
+      connectOauth: (input: IntegrationsConnectOauthInput, requestOptions?: RequestOptions) =>
+        request<IntegrationsConnectOauthOutput>(
+          {
+            method: "POST",
+            path: `/api/integration/${encodeURIComponent(input.integrationID)}/connect/oauth`,
+            query: { location: input["location"] },
+            body: { methodID: input["methodID"], inputs: input["inputs"], label: input["label"] },
+            successStatus: 200,
+            declaredStatuses: [400, 401],
+            empty: false,
+          },
+          requestOptions,
+        ),
+      attemptStatus: (input: IntegrationsAttemptStatusInput, requestOptions?: RequestOptions) =>
+        request<IntegrationsAttemptStatusOutput>(
+          {
+            method: "GET",
+            path: `/api/integration/attempt/${encodeURIComponent(input.attemptID)}`,
+            query: { location: input["location"] },
+            successStatus: 200,
+            declaredStatuses: [401, 400],
+            empty: false,
+          },
+          requestOptions,
+        ),
+      attemptComplete: (input: IntegrationsAttemptCompleteInput, requestOptions?: RequestOptions) =>
+        request<IntegrationsAttemptCompleteOutput>(
+          {
+            method: "POST",
+            path: `/api/integration/attempt/${encodeURIComponent(input.attemptID)}/complete`,
+            query: { location: input["location"] },
+            body: { code: input["code"] },
+            successStatus: 204,
+            declaredStatuses: [400, 401],
+            empty: true,
+          },
+          requestOptions,
+        ),
+      attemptCancel: (input: IntegrationsAttemptCancelInput, requestOptions?: RequestOptions) =>
+        request<IntegrationsAttemptCancelOutput>(
+          {
+            method: "DELETE",
+            path: `/api/integration/attempt/${encodeURIComponent(input.attemptID)}`,
+            query: { location: input["location"] },
+            successStatus: 204,
+            declaredStatuses: [401, 400],
+            empty: true,
+          },
+          requestOptions,
+        ),
+    },
+    credentials: {
+      update: (input: CredentialsUpdateInput, requestOptions?: RequestOptions) =>
+        request<CredentialsUpdateOutput>(
+          {
+            method: "PATCH",
+            path: `/api/credential/${encodeURIComponent(input.credentialID)}`,
+            query: { location: input["location"] },
+            body: { label: input["label"] },
+            successStatus: 204,
+            declaredStatuses: [401, 400],
+            empty: true,
+          },
+          requestOptions,
+        ),
+      remove: (input: CredentialsRemoveInput, requestOptions?: RequestOptions) =>
+        request<CredentialsRemoveOutput>(
+          {
+            method: "DELETE",
+            path: `/api/credential/${encodeURIComponent(input.credentialID)}`,
+            query: { location: input["location"] },
+            successStatus: 204,
+            declaredStatuses: [401, 400],
+            empty: true,
+          },
+          requestOptions,
+        ),
+    },
+    permissions: {
+      listRequests: (input?: PermissionsListRequestsInput, requestOptions?: RequestOptions) =>
+        request<PermissionsListRequestsOutput>(
+          {
+            method: "GET",
+            path: `/api/permission/request`,
+            query: { location: input?.["location"] },
+            successStatus: 200,
+            declaredStatuses: [401, 400],
+            empty: false,
+          },
+          requestOptions,
+        ),
+      listSaved: (input?: PermissionsListSavedInput, requestOptions?: RequestOptions) =>
+        request<{ readonly data: PermissionsListSavedOutput }>(
+          {
+            method: "GET",
+            path: `/api/permission/saved`,
+            query: { projectID: input?.["projectID"] },
+            successStatus: 200,
+            declaredStatuses: [401, 400],
+            empty: false,
+          },
+          requestOptions,
+        ).then((value) => value.data),
+      removeSaved: (input: PermissionsRemoveSavedInput, requestOptions?: RequestOptions) =>
+        request<PermissionsRemoveSavedOutput>(
+          {
+            method: "DELETE",
+            path: `/api/permission/saved/${encodeURIComponent(input.id)}`,
+            successStatus: 204,
+            declaredStatuses: [401, 400],
+            empty: true,
+          },
+          requestOptions,
+        ),
+      create: (input: PermissionsCreateInput, requestOptions?: RequestOptions) =>
+        request<{ readonly data: PermissionsCreateOutput }>(
+          {
+            method: "POST",
+            path: `/api/session/${encodeURIComponent(input.sessionID)}/permission`,
+            body: {
+              id: input["id"],
+              action: input["action"],
+              resources: input["resources"],
+              save: input["save"],
+              metadata: input["metadata"],
+              source: input["source"],
+              agent: input["agent"],
+            },
+            successStatus: 200,
+            declaredStatuses: [404, 400, 401],
+            empty: false,
+          },
+          requestOptions,
+        ).then((value) => value.data),
+      list: (input: PermissionsListInput, requestOptions?: RequestOptions) =>
+        request<{ readonly data: PermissionsListOutput }>(
+          {
+            method: "GET",
+            path: `/api/session/${encodeURIComponent(input.sessionID)}/permission`,
+            successStatus: 200,
+            declaredStatuses: [404, 400, 401],
+            empty: false,
+          },
+          requestOptions,
+        ).then((value) => value.data),
+      get: (input: PermissionsGetInput, requestOptions?: RequestOptions) =>
+        request<{ readonly data: PermissionsGetOutput }>(
+          {
+            method: "GET",
+            path: `/api/session/${encodeURIComponent(input.sessionID)}/permission/${encodeURIComponent(input.requestID)}`,
+            successStatus: 200,
+            declaredStatuses: [404, 400, 401],
+            empty: false,
+          },
+          requestOptions,
+        ).then((value) => value.data),
+      reply: (input: PermissionsReplyInput, requestOptions?: RequestOptions) =>
+        request<PermissionsReplyOutput>(
+          {
+            method: "POST",
+            path: `/api/session/${encodeURIComponent(input.sessionID)}/permission/${encodeURIComponent(input.requestID)}/reply`,
+            body: { reply: input["reply"], message: input["message"] },
+            successStatus: 204,
+            declaredStatuses: [404, 400, 401],
+            empty: true,
+          },
+          requestOptions,
+        ),
+    },
+    files: {
+      list: (input?: FilesListInput, requestOptions?: RequestOptions) =>
+        request<FilesListOutput>(
+          {
+            method: "GET",
+            path: `/api/fs/list`,
+            query: { location: input?.["location"], path: input?.["path"] },
+            successStatus: 200,
+            declaredStatuses: [401, 400],
+            empty: false,
+          },
+          requestOptions,
+        ),
+      find: (input: FilesFindInput, requestOptions?: RequestOptions) =>
+        request<FilesFindOutput>(
+          {
+            method: "GET",
+            path: `/api/fs/find`,
+            query: { location: input["location"], query: input["query"], type: input["type"], limit: input["limit"] },
+            successStatus: 200,
+            declaredStatuses: [401, 400],
+            empty: false,
+          },
+          requestOptions,
+        ),
+    },
+    commands: {
+      list: (input?: CommandsListInput, requestOptions?: RequestOptions) =>
+        request<CommandsListOutput>(
+          {
+            method: "GET",
+            path: `/api/command`,
+            query: { location: input?.["location"] },
+            successStatus: 200,
+            declaredStatuses: [401, 400],
+            empty: false,
+          },
+          requestOptions,
+        ),
+    },
+    skills: {
+      list: (input?: SkillsListInput, requestOptions?: RequestOptions) =>
+        request<SkillsListOutput>(
+          {
+            method: "GET",
+            path: `/api/skill`,
+            query: { location: input?.["location"] },
+            successStatus: 200,
+            declaredStatuses: [401, 400],
+            empty: false,
+          },
+          requestOptions,
+        ),
+    },
     events: {
       subscribe: (requestOptions?: RequestOptions): AsyncIterable<EventsSubscribeOutput> =>
         sse<EventsSubscribeOutput>(
@@ -381,6 +815,178 @@ export function make(options: ClientOptions) {
           requestOptions,
         ),
     },
+    ptys: {
+      list: (input?: PtysListInput, requestOptions?: RequestOptions) =>
+        request<PtysListOutput>(
+          {
+            method: "GET",
+            path: `/api/pty`,
+            query: { location: input?.["location"] },
+            successStatus: 200,
+            declaredStatuses: [401, 400],
+            empty: false,
+          },
+          requestOptions,
+        ),
+      create: (input?: PtysCreateInput, requestOptions?: RequestOptions) =>
+        request<PtysCreateOutput>(
+          {
+            method: "POST",
+            path: `/api/pty`,
+            query: { location: input?.["location"] },
+            body: {
+              command: input?.["command"],
+              args: input?.["args"],
+              cwd: input?.["cwd"],
+              title: input?.["title"],
+              env: input?.["env"],
+            },
+            successStatus: 200,
+            declaredStatuses: [401, 400],
+            empty: false,
+          },
+          requestOptions,
+        ),
+      get: (input: PtysGetInput, requestOptions?: RequestOptions) =>
+        request<PtysGetOutput>(
+          {
+            method: "GET",
+            path: `/api/pty/${encodeURIComponent(input.ptyID)}`,
+            query: { location: input["location"] },
+            successStatus: 200,
+            declaredStatuses: [404, 401, 400],
+            empty: false,
+          },
+          requestOptions,
+        ),
+      update: (input: PtysUpdateInput, requestOptions?: RequestOptions) =>
+        request<PtysUpdateOutput>(
+          {
+            method: "PUT",
+            path: `/api/pty/${encodeURIComponent(input.ptyID)}`,
+            query: { location: input["location"] },
+            body: { title: input["title"], size: input["size"] },
+            successStatus: 200,
+            declaredStatuses: [404, 401, 400],
+            empty: false,
+          },
+          requestOptions,
+        ),
+      remove: (input: PtysRemoveInput, requestOptions?: RequestOptions) =>
+        request<PtysRemoveOutput>(
+          {
+            method: "DELETE",
+            path: `/api/pty/${encodeURIComponent(input.ptyID)}`,
+            query: { location: input["location"] },
+            successStatus: 204,
+            declaredStatuses: [404, 401, 400],
+            empty: true,
+          },
+          requestOptions,
+        ),
+    },
+    questions: {
+      listRequests: (input?: QuestionsListRequestsInput, requestOptions?: RequestOptions) =>
+        request<QuestionsListRequestsOutput>(
+          {
+            method: "GET",
+            path: `/api/question/request`,
+            query: { location: input?.["location"] },
+            successStatus: 200,
+            declaredStatuses: [401, 400],
+            empty: false,
+          },
+          requestOptions,
+        ),
+      list: (input: QuestionsListInput, requestOptions?: RequestOptions) =>
+        request<{ readonly data: QuestionsListOutput }>(
+          {
+            method: "GET",
+            path: `/api/session/${encodeURIComponent(input.sessionID)}/question`,
+            successStatus: 200,
+            declaredStatuses: [404, 400, 401],
+            empty: false,
+          },
+          requestOptions,
+        ).then((value) => value.data),
+      reply: (input: QuestionsReplyInput, requestOptions?: RequestOptions) =>
+        request<QuestionsReplyOutput>(
+          {
+            method: "POST",
+            path: `/api/session/${encodeURIComponent(input.sessionID)}/question/${encodeURIComponent(input.requestID)}/reply`,
+            body: { answers: input["answers"] },
+            successStatus: 204,
+            declaredStatuses: [404, 400, 401],
+            empty: true,
+          },
+          requestOptions,
+        ),
+      reject: (input: QuestionsRejectInput, requestOptions?: RequestOptions) =>
+        request<QuestionsRejectOutput>(
+          {
+            method: "POST",
+            path: `/api/session/${encodeURIComponent(input.sessionID)}/question/${encodeURIComponent(input.requestID)}/reject`,
+            successStatus: 204,
+            declaredStatuses: [404, 400, 401],
+            empty: true,
+          },
+          requestOptions,
+        ),
+    },
+    references: {
+      list: (input?: ReferencesListInput, requestOptions?: RequestOptions) =>
+        request<ReferencesListOutput>(
+          {
+            method: "GET",
+            path: `/api/reference`,
+            query: { location: input?.["location"] },
+            successStatus: 200,
+            declaredStatuses: [401, 400],
+            empty: false,
+          },
+          requestOptions,
+        ),
+    },
+    projectCopies: {
+      create: (input: ProjectCopiesCreateInput, requestOptions?: RequestOptions) =>
+        request<ProjectCopiesCreateOutput>(
+          {
+            method: "POST",
+            path: `/experimental/project/${encodeURIComponent(input.projectID)}/copy`,
+            query: { location: input["location"] },
+            body: { strategy: input["strategy"], directory: input["directory"], name: input["name"] },
+            successStatus: 200,
+            declaredStatuses: [400, 401],
+            empty: false,
+          },
+          requestOptions,
+        ),
+      remove: (input: ProjectCopiesRemoveInput, requestOptions?: RequestOptions) =>
+        request<ProjectCopiesRemoveOutput>(
+          {
+            method: "DELETE",
+            path: `/experimental/project/${encodeURIComponent(input.projectID)}/copy`,
+            query: { location: input["location"] },
+            body: { directory: input["directory"], force: input["force"] },
+            successStatus: 204,
+            declaredStatuses: [400, 401],
+            empty: true,
+          },
+          requestOptions,
+        ),
+      refresh: (input: ProjectCopiesRefreshInput, requestOptions?: RequestOptions) =>
+        request<ProjectCopiesRefreshOutput>(
+          {
+            method: "POST",
+            path: `/experimental/project/${encodeURIComponent(input.projectID)}/copy/refresh`,
+            query: { location: input["location"] },
+            successStatus: 204,
+            declaredStatuses: [400, 401],
+            empty: true,
+          },
+          requestOptions,
+        ),
+    },
   }
 }
 

+ 1146 - 13
packages/client/src/generated/types.ts

@@ -8,9 +8,9 @@ export type JsonValue =
   | ReadonlyArray<JsonValue>
   | { readonly [key: string]: JsonValue }
 
-export type InvalidCursorError = { readonly _tag: "InvalidCursorError"; readonly message: string }
-export const isInvalidCursorError = (value: unknown): value is InvalidCursorError =>
-  typeof value === "object" && value !== null && "_tag" in value && value._tag === "InvalidCursorError"
+export type UnauthorizedError = { readonly _tag: "UnauthorizedError"; readonly message: string }
+export const isUnauthorizedError = (value: unknown): value is UnauthorizedError =>
+  typeof value === "object" && value !== null && "_tag" in value && value["_tag"] === "UnauthorizedError"
 
 export type InvalidRequestError = {
   readonly _tag: "InvalidRequestError"
@@ -19,11 +19,11 @@ export type InvalidRequestError = {
   readonly field?: string | undefined
 }
 export const isInvalidRequestError = (value: unknown): value is InvalidRequestError =>
-  typeof value === "object" && value !== null && "_tag" in value && value._tag === "InvalidRequestError"
+  typeof value === "object" && value !== null && "_tag" in value && value["_tag"] === "InvalidRequestError"
 
-export type UnauthorizedError = { readonly _tag: "UnauthorizedError"; readonly message: string }
-export const isUnauthorizedError = (value: unknown): value is UnauthorizedError =>
-  typeof value === "object" && value !== null && "_tag" in value && value._tag === "UnauthorizedError"
+export type InvalidCursorError = { readonly _tag: "InvalidCursorError"; readonly message: string }
+export const isInvalidCursorError = (value: unknown): value is InvalidCursorError =>
+  typeof value === "object" && value !== null && "_tag" in value && value["_tag"] === "InvalidCursorError"
 
 export type SessionNotFoundError = {
   readonly _tag: "SessionNotFoundError"
@@ -31,7 +31,7 @@ export type SessionNotFoundError = {
   readonly message: string
 }
 export const isSessionNotFoundError = (value: unknown): value is SessionNotFoundError =>
-  typeof value === "object" && value !== null && "_tag" in value && value._tag === "SessionNotFoundError"
+  typeof value === "object" && value !== null && "_tag" in value && value["_tag"] === "SessionNotFoundError"
 
 export type ConflictError = {
   readonly _tag: "ConflictError"
@@ -39,7 +39,7 @@ export type ConflictError = {
   readonly resource?: string | undefined
 }
 export const isConflictError = (value: unknown): value is ConflictError =>
-  typeof value === "object" && value !== null && "_tag" in value && value._tag === "ConflictError"
+  typeof value === "object" && value !== null && "_tag" in value && value["_tag"] === "ConflictError"
 
 export type ServiceUnavailableError = {
   readonly _tag: "ServiceUnavailableError"
@@ -47,7 +47,7 @@ export type ServiceUnavailableError = {
   readonly service?: string | undefined
 }
 export const isServiceUnavailableError = (value: unknown): value is ServiceUnavailableError =>
-  typeof value === "object" && value !== null && "_tag" in value && value._tag === "ServiceUnavailableError"
+  typeof value === "object" && value !== null && "_tag" in value && value["_tag"] === "ServiceUnavailableError"
 
 export type MessageNotFoundError = {
   readonly _tag: "MessageNotFoundError"
@@ -56,7 +56,7 @@ export type MessageNotFoundError = {
   readonly message: string
 }
 export const isMessageNotFoundError = (value: unknown): value is MessageNotFoundError =>
-  typeof value === "object" && value !== null && "_tag" in value && value._tag === "MessageNotFoundError"
+  typeof value === "object" && value !== null && "_tag" in value && value["_tag"] === "MessageNotFoundError"
 
 export type UnknownError = {
   readonly _tag: "UnknownError"
@@ -64,7 +64,89 @@ export type UnknownError = {
   readonly ref?: string | undefined
 }
 export const isUnknownError = (value: unknown): value is UnknownError =>
-  typeof value === "object" && value !== null && "_tag" in value && value._tag === "UnknownError"
+  typeof value === "object" && value !== null && "_tag" in value && value["_tag"] === "UnknownError"
+
+export type ProviderNotFoundError = {
+  readonly _tag: "ProviderNotFoundError"
+  readonly providerID: string
+  readonly message: string
+}
+export const isProviderNotFoundError = (value: unknown): value is ProviderNotFoundError =>
+  typeof value === "object" && value !== null && "_tag" in value && value["_tag"] === "ProviderNotFoundError"
+
+export type PermissionNotFoundError = {
+  readonly _tag: "PermissionNotFoundError"
+  readonly requestID: string
+  readonly message: string
+}
+export const isPermissionNotFoundError = (value: unknown): value is PermissionNotFoundError =>
+  typeof value === "object" && value !== null && "_tag" in value && value["_tag"] === "PermissionNotFoundError"
+
+export type PtyNotFoundError = { readonly _tag: "PtyNotFoundError"; readonly ptyID: string; readonly message: string }
+export const isPtyNotFoundError = (value: unknown): value is PtyNotFoundError =>
+  typeof value === "object" && value !== null && "_tag" in value && value["_tag"] === "PtyNotFoundError"
+
+export type QuestionNotFoundError = {
+  readonly _tag: "QuestionNotFoundError"
+  readonly requestID: string
+  readonly message: string
+}
+export const isQuestionNotFoundError = (value: unknown): value is QuestionNotFoundError =>
+  typeof value === "object" && value !== null && "_tag" in value && value["_tag"] === "QuestionNotFoundError"
+
+export type ProjectCopyError = {
+  readonly name: "ProjectCopyError"
+  readonly data: { readonly message: string; readonly forceRequired?: boolean | undefined }
+}
+export const isProjectCopyError = (value: unknown): value is ProjectCopyError =>
+  typeof value === "object" && value !== null && "name" in value && value["name"] === "ProjectCopyError"
+
+export type HealthGetOutput = { readonly healthy: true }
+
+export type LocationGetInput = {
+  readonly location?: {
+    readonly location?: { readonly directory?: string | undefined; readonly workspace?: string | undefined } | undefined
+  }["location"]
+}
+
+export type LocationGetOutput = {
+  readonly directory: string
+  readonly workspaceID?: string
+  readonly project: { readonly id: string; readonly directory: string }
+}
+
+export type AgentsListInput = {
+  readonly location?: {
+    readonly location?: { readonly directory?: string | undefined; readonly workspace?: string | undefined } | undefined
+  }["location"]
+}
+
+export type AgentsListOutput = {
+  readonly location: {
+    readonly directory: string
+    readonly workspaceID?: string
+    readonly project: { readonly id: string; readonly directory: string }
+  }
+  readonly data: ReadonlyArray<{
+    readonly id: string
+    readonly model?: { readonly id: string; readonly providerID: string; readonly variant?: string }
+    readonly request: {
+      readonly headers: { readonly [x: string]: string }
+      readonly body: { readonly [x: string]: JsonValue }
+    }
+    readonly system?: string
+    readonly description?: string
+    readonly mode: "subagent" | "primary" | "all"
+    readonly hidden: boolean
+    readonly color?: string | "primary" | "secondary" | "accent" | "success" | "warning" | "error" | "info"
+    readonly steps?: number
+    readonly permissions: ReadonlyArray<{
+      readonly action: string
+      readonly resource: string
+      readonly effect: "allow" | "deny" | "ask"
+    }>
+  }>
+}
 
 export type SessionsListInput = {
   readonly workspace?: {
@@ -1671,4 +1753,1055 @@ export type SessionsMessageOutput = {
       }
 }["data"]
 
-export type EventsSubscribeOutput = OpenCodeEventEncoded
+export type MessagesListInput = {
+  readonly sessionID: { readonly sessionID: string }["sessionID"]
+  readonly limit?: {
+    readonly limit?: number | undefined
+    readonly order?: "asc" | "desc" | undefined
+    readonly cursor?: string | undefined
+  }["limit"]
+  readonly order?: {
+    readonly limit?: number | undefined
+    readonly order?: "asc" | "desc" | undefined
+    readonly cursor?: string | undefined
+  }["order"]
+  readonly cursor?: {
+    readonly limit?: number | undefined
+    readonly order?: "asc" | "desc" | undefined
+    readonly cursor?: string | undefined
+  }["cursor"]
+}
+
+export type MessagesListOutput = {
+  readonly data: ReadonlyArray<
+    | {
+        readonly id: string
+        readonly metadata?: { readonly [x: string]: JsonValue }
+        readonly time: { readonly created: number }
+        readonly type: "agent-switched"
+        readonly agent: string
+      }
+    | {
+        readonly id: string
+        readonly metadata?: { readonly [x: string]: JsonValue }
+        readonly time: { readonly created: number }
+        readonly type: "model-switched"
+        readonly model: { readonly id: string; readonly providerID: string; readonly variant?: string }
+      }
+    | {
+        readonly id: string
+        readonly metadata?: { readonly [x: string]: JsonValue }
+        readonly time: { readonly created: number }
+        readonly text: string
+        readonly files?: ReadonlyArray<{
+          readonly uri: string
+          readonly mime: string
+          readonly name?: string
+          readonly description?: string
+          readonly source?: { readonly start: number; readonly end: number; readonly text: string }
+        }>
+        readonly agents?: ReadonlyArray<{
+          readonly name: string
+          readonly source?: { readonly start: number; readonly end: number; readonly text: string }
+        }>
+        readonly type: "user"
+      }
+    | {
+        readonly id: string
+        readonly metadata?: { readonly [x: string]: JsonValue }
+        readonly time: { readonly created: number }
+        readonly sessionID: string
+        readonly text: string
+        readonly type: "synthetic"
+      }
+    | {
+        readonly id: string
+        readonly metadata?: { readonly [x: string]: JsonValue }
+        readonly time: { readonly created: number }
+        readonly type: "system"
+        readonly text: string
+      }
+    | {
+        readonly id: string
+        readonly metadata?: { readonly [x: string]: JsonValue }
+        readonly time: { readonly created: number; readonly completed?: number }
+        readonly type: "shell"
+        readonly callID: string
+        readonly command: string
+        readonly output: string
+      }
+    | {
+        readonly id: string
+        readonly metadata?: { readonly [x: string]: JsonValue }
+        readonly time: { readonly created: number; readonly completed?: number }
+        readonly type: "assistant"
+        readonly agent: string
+        readonly model: { readonly id: string; readonly providerID: string; readonly variant?: string }
+        readonly content: ReadonlyArray<
+          | { readonly type: "text"; readonly id: string; readonly text: string }
+          | {
+              readonly type: "reasoning"
+              readonly id: string
+              readonly text: string
+              readonly providerMetadata?: { readonly [x: string]: { readonly [x: string]: JsonValue } }
+              readonly time?: { readonly created: number; readonly completed?: number }
+            }
+          | {
+              readonly type: "tool"
+              readonly id: string
+              readonly name: string
+              readonly provider?: {
+                readonly executed: boolean
+                readonly metadata?: { readonly [x: string]: { readonly [x: string]: JsonValue } }
+                readonly resultMetadata?: { readonly [x: string]: { readonly [x: string]: JsonValue } }
+              }
+              readonly state:
+                | { readonly status: "pending"; readonly input: string }
+                | {
+                    readonly status: "running"
+                    readonly input: { readonly [x: string]: JsonValue }
+                    readonly structured: { readonly [x: string]: JsonValue }
+                    readonly content: ReadonlyArray<
+                      | { readonly type: "text"; readonly text: string }
+                      | { readonly type: "file"; readonly uri: string; readonly mime: string; readonly name?: string }
+                    >
+                  }
+                | {
+                    readonly status: "completed"
+                    readonly input: { readonly [x: string]: JsonValue }
+                    readonly attachments?: ReadonlyArray<{
+                      readonly uri: string
+                      readonly mime: string
+                      readonly name?: string
+                      readonly description?: string
+                      readonly source?: { readonly start: number; readonly end: number; readonly text: string }
+                    }>
+                    readonly content: ReadonlyArray<
+                      | { readonly type: "text"; readonly text: string }
+                      | { readonly type: "file"; readonly uri: string; readonly mime: string; readonly name?: string }
+                    >
+                    readonly outputPaths?: ReadonlyArray<string>
+                    readonly structured: { readonly [x: string]: JsonValue }
+                    readonly result?: JsonValue
+                  }
+                | {
+                    readonly status: "error"
+                    readonly input: { readonly [x: string]: JsonValue }
+                    readonly content: ReadonlyArray<
+                      | { readonly type: "text"; readonly text: string }
+                      | { readonly type: "file"; readonly uri: string; readonly mime: string; readonly name?: string }
+                    >
+                    readonly structured: { readonly [x: string]: JsonValue }
+                    readonly error: { readonly type: "unknown"; readonly message: string }
+                    readonly result?: JsonValue
+                  }
+              readonly time: {
+                readonly created: number
+                readonly ran?: number
+                readonly completed?: number
+                readonly pruned?: number
+              }
+            }
+        >
+        readonly snapshot?: { readonly start?: string; readonly end?: string; readonly files?: ReadonlyArray<string> }
+        readonly finish?: string
+        readonly cost?: number
+        readonly tokens?: {
+          readonly input: number
+          readonly output: number
+          readonly reasoning: number
+          readonly cache: { readonly read: number; readonly write: number }
+        }
+        readonly error?: { readonly type: "unknown"; readonly message: string }
+      }
+    | {
+        readonly type: "compaction"
+        readonly reason: "auto" | "manual"
+        readonly summary: string
+        readonly recent: string
+        readonly id: string
+        readonly metadata?: { readonly [x: string]: JsonValue }
+        readonly time: { readonly created: number }
+      }
+  >
+  readonly cursor: { readonly previous?: string | null; readonly next?: string | null }
+}
+
+export type ModelsListInput = {
+  readonly location?: {
+    readonly location?: { readonly directory?: string | undefined; readonly workspace?: string | undefined } | undefined
+  }["location"]
+}
+
+export type ModelsListOutput = {
+  readonly location: {
+    readonly directory: string
+    readonly workspaceID?: string
+    readonly project: { readonly id: string; readonly directory: string }
+  }
+  readonly data: ReadonlyArray<{
+    readonly id: string
+    readonly providerID: string
+    readonly family?: string
+    readonly name: string
+    readonly api:
+      | {
+          readonly id: string
+          readonly type: "aisdk"
+          readonly package: string
+          readonly url?: string
+          readonly settings?: { readonly [x: string]: JsonValue }
+        }
+      | {
+          readonly id: string
+          readonly type: "native"
+          readonly url?: string
+          readonly settings: { readonly [x: string]: JsonValue }
+        }
+    readonly capabilities: {
+      readonly tools: boolean
+      readonly input: ReadonlyArray<string>
+      readonly output: ReadonlyArray<string>
+    }
+    readonly request: {
+      readonly headers: { readonly [x: string]: string }
+      readonly body: { readonly [x: string]: JsonValue }
+      readonly variant?: string
+    }
+    readonly variants: ReadonlyArray<{
+      readonly id: string
+      readonly headers: { readonly [x: string]: string }
+      readonly body: { readonly [x: string]: JsonValue }
+    }>
+    readonly time: { readonly released: number }
+    readonly cost: ReadonlyArray<{
+      readonly tier?: { readonly type: "context"; readonly size: number }
+      readonly input: number
+      readonly output: number
+      readonly cache: { readonly read: number; readonly write: number }
+    }>
+    readonly status: "alpha" | "beta" | "deprecated" | "active"
+    readonly enabled: boolean
+    readonly limit: { readonly context: number; readonly input?: number; readonly output: number }
+  }>
+}
+
+export type ProvidersListInput = {
+  readonly location?: {
+    readonly location?: { readonly directory?: string | undefined; readonly workspace?: string | undefined } | undefined
+  }["location"]
+}
+
+export type ProvidersListOutput = {
+  readonly location: {
+    readonly directory: string
+    readonly workspaceID?: string
+    readonly project: { readonly id: string; readonly directory: string }
+  }
+  readonly data: ReadonlyArray<{
+    readonly id: string
+    readonly integrationID?: string
+    readonly name: string
+    readonly disabled?: boolean
+    readonly api:
+      | {
+          readonly type: "aisdk"
+          readonly package: string
+          readonly url?: string
+          readonly settings?: { readonly [x: string]: JsonValue }
+        }
+      | { readonly type: "native"; readonly url?: string; readonly settings: { readonly [x: string]: JsonValue } }
+    readonly request: {
+      readonly headers: { readonly [x: string]: string }
+      readonly body: { readonly [x: string]: JsonValue }
+    }
+  }>
+}
+
+export type ProvidersGetInput = {
+  readonly providerID: { readonly providerID: string }["providerID"]
+  readonly location?: {
+    readonly location?: { readonly directory?: string | undefined; readonly workspace?: string | undefined } | undefined
+  }["location"]
+}
+
+export type ProvidersGetOutput = {
+  readonly location: {
+    readonly directory: string
+    readonly workspaceID?: string
+    readonly project: { readonly id: string; readonly directory: string }
+  }
+  readonly data: {
+    readonly id: string
+    readonly integrationID?: string
+    readonly name: string
+    readonly disabled?: boolean
+    readonly api:
+      | {
+          readonly type: "aisdk"
+          readonly package: string
+          readonly url?: string
+          readonly settings?: { readonly [x: string]: JsonValue }
+        }
+      | { readonly type: "native"; readonly url?: string; readonly settings: { readonly [x: string]: JsonValue } }
+    readonly request: {
+      readonly headers: { readonly [x: string]: string }
+      readonly body: { readonly [x: string]: JsonValue }
+    }
+  }
+}
+
+export type IntegrationsListInput = {
+  readonly location?: {
+    readonly location?: { readonly directory?: string | undefined; readonly workspace?: string | undefined } | undefined
+  }["location"]
+}
+
+export type IntegrationsListOutput = {
+  readonly location: {
+    readonly directory: string
+    readonly workspaceID?: string
+    readonly project: { readonly id: string; readonly directory: string }
+  }
+  readonly data: ReadonlyArray<{
+    readonly id: string
+    readonly name: string
+    readonly methods: ReadonlyArray<
+      | {
+          readonly id: string
+          readonly type: "oauth"
+          readonly label: string
+          readonly prompts?: ReadonlyArray<
+            | {
+                readonly type: "text"
+                readonly key: string
+                readonly message: string
+                readonly placeholder?: string
+                readonly when?: { readonly key: string; readonly op: "eq" | "neq"; readonly value: string }
+              }
+            | {
+                readonly type: "select"
+                readonly key: string
+                readonly message: string
+                readonly options: ReadonlyArray<{
+                  readonly label: string
+                  readonly value: string
+                  readonly hint?: string
+                }>
+                readonly when?: { readonly key: string; readonly op: "eq" | "neq"; readonly value: string }
+              }
+          >
+        }
+      | { readonly type: "key"; readonly label?: string }
+      | { readonly type: "env"; readonly names: ReadonlyArray<string> }
+    >
+    readonly connections: ReadonlyArray<
+      | { readonly type: "credential"; readonly id: string; readonly label: string }
+      | { readonly type: "env"; readonly name: string }
+    >
+  }>
+}
+
+export type IntegrationsGetInput = {
+  readonly integrationID: { readonly integrationID: string }["integrationID"]
+  readonly location?: {
+    readonly location?: { readonly directory?: string | undefined; readonly workspace?: string | undefined } | undefined
+  }["location"]
+}
+
+export type IntegrationsGetOutput = {
+  readonly location: {
+    readonly directory: string
+    readonly workspaceID?: string
+    readonly project: { readonly id: string; readonly directory: string }
+  }
+  readonly data: {
+    readonly id: string
+    readonly name: string
+    readonly methods: ReadonlyArray<
+      | {
+          readonly id: string
+          readonly type: "oauth"
+          readonly label: string
+          readonly prompts?: ReadonlyArray<
+            | {
+                readonly type: "text"
+                readonly key: string
+                readonly message: string
+                readonly placeholder?: string
+                readonly when?: { readonly key: string; readonly op: "eq" | "neq"; readonly value: string }
+              }
+            | {
+                readonly type: "select"
+                readonly key: string
+                readonly message: string
+                readonly options: ReadonlyArray<{
+                  readonly label: string
+                  readonly value: string
+                  readonly hint?: string
+                }>
+                readonly when?: { readonly key: string; readonly op: "eq" | "neq"; readonly value: string }
+              }
+          >
+        }
+      | { readonly type: "key"; readonly label?: string }
+      | { readonly type: "env"; readonly names: ReadonlyArray<string> }
+    >
+    readonly connections: ReadonlyArray<
+      | { readonly type: "credential"; readonly id: string; readonly label: string }
+      | { readonly type: "env"; readonly name: string }
+    >
+  } | null
+}
+
+export type IntegrationsConnectKeyInput = {
+  readonly integrationID: { readonly integrationID: string }["integrationID"]
+  readonly location?: {
+    readonly location?: { readonly directory?: string | undefined; readonly workspace?: string | undefined } | undefined
+  }["location"]
+  readonly key: { readonly key: string; readonly label?: string | undefined }["key"]
+  readonly label?: { readonly key: string; readonly label?: string | undefined }["label"]
+}
+
+export type IntegrationsConnectKeyOutput = void
+
+export type IntegrationsConnectOauthInput = {
+  readonly integrationID: { readonly integrationID: string }["integrationID"]
+  readonly location?: {
+    readonly location?: { readonly directory?: string | undefined; readonly workspace?: string | undefined } | undefined
+  }["location"]
+  readonly methodID: {
+    readonly methodID: string
+    readonly inputs: { readonly [x: string]: string }
+    readonly label?: string | undefined
+  }["methodID"]
+  readonly inputs: {
+    readonly methodID: string
+    readonly inputs: { readonly [x: string]: string }
+    readonly label?: string | undefined
+  }["inputs"]
+  readonly label?: {
+    readonly methodID: string
+    readonly inputs: { readonly [x: string]: string }
+    readonly label?: string | undefined
+  }["label"]
+}
+
+export type IntegrationsConnectOauthOutput = {
+  readonly location: {
+    readonly directory: string
+    readonly workspaceID?: string
+    readonly project: { readonly id: string; readonly directory: string }
+  }
+  readonly data: {
+    readonly attemptID: string
+    readonly url: string
+    readonly instructions: string
+    readonly mode: "auto" | "code"
+    readonly time: {
+      readonly created: number | "Infinity" | "-Infinity" | "NaN"
+      readonly expires: number | "Infinity" | "-Infinity" | "NaN"
+    }
+  }
+}
+
+export type IntegrationsAttemptStatusInput = {
+  readonly attemptID: { readonly attemptID: string }["attemptID"]
+  readonly location?: {
+    readonly location?: { readonly directory?: string | undefined; readonly workspace?: string | undefined } | undefined
+  }["location"]
+}
+
+export type IntegrationsAttemptStatusOutput = {
+  readonly location: {
+    readonly directory: string
+    readonly workspaceID?: string
+    readonly project: { readonly id: string; readonly directory: string }
+  }
+  readonly data:
+    | {
+        readonly status: "pending"
+        readonly time: {
+          readonly created: number | "Infinity" | "-Infinity" | "NaN"
+          readonly expires: number | "Infinity" | "-Infinity" | "NaN"
+        }
+      }
+    | {
+        readonly status: "complete"
+        readonly time: {
+          readonly created: number | "Infinity" | "-Infinity" | "NaN"
+          readonly expires: number | "Infinity" | "-Infinity" | "NaN"
+        }
+      }
+    | {
+        readonly status: "failed"
+        readonly message: string
+        readonly time: {
+          readonly created: number | "Infinity" | "-Infinity" | "NaN"
+          readonly expires: number | "Infinity" | "-Infinity" | "NaN"
+        }
+      }
+    | {
+        readonly status: "expired"
+        readonly time: {
+          readonly created: number | "Infinity" | "-Infinity" | "NaN"
+          readonly expires: number | "Infinity" | "-Infinity" | "NaN"
+        }
+      }
+}
+
+export type IntegrationsAttemptCompleteInput = {
+  readonly attemptID: { readonly attemptID: string }["attemptID"]
+  readonly location?: {
+    readonly location?: { readonly directory?: string | undefined; readonly workspace?: string | undefined } | undefined
+  }["location"]
+  readonly code?: { readonly code?: string | undefined }["code"]
+}
+
+export type IntegrationsAttemptCompleteOutput = void
+
+export type IntegrationsAttemptCancelInput = {
+  readonly attemptID: { readonly attemptID: string }["attemptID"]
+  readonly location?: {
+    readonly location?: { readonly directory?: string | undefined; readonly workspace?: string | undefined } | undefined
+  }["location"]
+}
+
+export type IntegrationsAttemptCancelOutput = void
+
+export type CredentialsUpdateInput = {
+  readonly credentialID: { readonly credentialID: string }["credentialID"]
+  readonly location?: {
+    readonly location?: { readonly directory?: string | undefined; readonly workspace?: string | undefined } | undefined
+  }["location"]
+  readonly label: { readonly label: string }["label"]
+}
+
+export type CredentialsUpdateOutput = void
+
+export type CredentialsRemoveInput = {
+  readonly credentialID: { readonly credentialID: string }["credentialID"]
+  readonly location?: {
+    readonly location?: { readonly directory?: string | undefined; readonly workspace?: string | undefined } | undefined
+  }["location"]
+}
+
+export type CredentialsRemoveOutput = void
+
+export type PermissionsListRequestsInput = {
+  readonly location?: {
+    readonly location?: { readonly directory?: string | undefined; readonly workspace?: string | undefined } | undefined
+  }["location"]
+}
+
+export type PermissionsListRequestsOutput = {
+  readonly location: {
+    readonly directory: string
+    readonly workspaceID?: string
+    readonly project: { readonly id: string; readonly directory: string }
+  }
+  readonly data: ReadonlyArray<{
+    readonly id: string
+    readonly sessionID: string
+    readonly action: string
+    readonly resources: ReadonlyArray<string>
+    readonly save?: ReadonlyArray<string>
+    readonly metadata?: { readonly [x: string]: JsonValue }
+    readonly source?: { readonly type: "tool"; readonly messageID: string; readonly callID: string }
+  }>
+}
+
+export type PermissionsListSavedInput = {
+  readonly projectID?: { readonly projectID?: string | undefined }["projectID"]
+}
+
+export type PermissionsListSavedOutput = {
+  readonly data: ReadonlyArray<{
+    readonly id: string
+    readonly projectID: string
+    readonly action: string
+    readonly resource: string
+  }>
+}["data"]
+
+export type PermissionsRemoveSavedInput = { readonly id: { readonly id: string }["id"] }
+
+export type PermissionsRemoveSavedOutput = void
+
+export type PermissionsCreateInput = {
+  readonly sessionID: { readonly sessionID: string }["sessionID"]
+  readonly id?: {
+    readonly id?: string | null
+    readonly action: string
+    readonly resources: ReadonlyArray<string>
+    readonly save?: ReadonlyArray<string>
+    readonly metadata?: { readonly [x: string]: JsonValue }
+    readonly source?: { readonly type: "tool"; readonly messageID: string; readonly callID: string }
+    readonly agent?: string | null
+  }["id"]
+  readonly action: {
+    readonly id?: string | null
+    readonly action: string
+    readonly resources: ReadonlyArray<string>
+    readonly save?: ReadonlyArray<string>
+    readonly metadata?: { readonly [x: string]: JsonValue }
+    readonly source?: { readonly type: "tool"; readonly messageID: string; readonly callID: string }
+    readonly agent?: string | null
+  }["action"]
+  readonly resources: {
+    readonly id?: string | null
+    readonly action: string
+    readonly resources: ReadonlyArray<string>
+    readonly save?: ReadonlyArray<string>
+    readonly metadata?: { readonly [x: string]: JsonValue }
+    readonly source?: { readonly type: "tool"; readonly messageID: string; readonly callID: string }
+    readonly agent?: string | null
+  }["resources"]
+  readonly save?: {
+    readonly id?: string | null
+    readonly action: string
+    readonly resources: ReadonlyArray<string>
+    readonly save?: ReadonlyArray<string>
+    readonly metadata?: { readonly [x: string]: JsonValue }
+    readonly source?: { readonly type: "tool"; readonly messageID: string; readonly callID: string }
+    readonly agent?: string | null
+  }["save"]
+  readonly metadata?: {
+    readonly id?: string | null
+    readonly action: string
+    readonly resources: ReadonlyArray<string>
+    readonly save?: ReadonlyArray<string>
+    readonly metadata?: { readonly [x: string]: JsonValue }
+    readonly source?: { readonly type: "tool"; readonly messageID: string; readonly callID: string }
+    readonly agent?: string | null
+  }["metadata"]
+  readonly source?: {
+    readonly id?: string | null
+    readonly action: string
+    readonly resources: ReadonlyArray<string>
+    readonly save?: ReadonlyArray<string>
+    readonly metadata?: { readonly [x: string]: JsonValue }
+    readonly source?: { readonly type: "tool"; readonly messageID: string; readonly callID: string }
+    readonly agent?: string | null
+  }["source"]
+  readonly agent?: {
+    readonly id?: string | null
+    readonly action: string
+    readonly resources: ReadonlyArray<string>
+    readonly save?: ReadonlyArray<string>
+    readonly metadata?: { readonly [x: string]: JsonValue }
+    readonly source?: { readonly type: "tool"; readonly messageID: string; readonly callID: string }
+    readonly agent?: string | null
+  }["agent"]
+}
+
+export type PermissionsCreateOutput = {
+  readonly data: { readonly id: string; readonly effect: "allow" | "deny" | "ask" }
+}["data"]
+
+export type PermissionsListInput = { readonly sessionID: { readonly sessionID: string }["sessionID"] }
+
+export type PermissionsListOutput = {
+  readonly data: ReadonlyArray<{
+    readonly id: string
+    readonly sessionID: string
+    readonly action: string
+    readonly resources: ReadonlyArray<string>
+    readonly save?: ReadonlyArray<string>
+    readonly metadata?: { readonly [x: string]: JsonValue }
+    readonly source?: { readonly type: "tool"; readonly messageID: string; readonly callID: string }
+  }>
+}["data"]
+
+export type PermissionsGetInput = {
+  readonly sessionID: { readonly sessionID: string; readonly requestID: string }["sessionID"]
+  readonly requestID: { readonly sessionID: string; readonly requestID: string }["requestID"]
+}
+
+export type PermissionsGetOutput = {
+  readonly data: {
+    readonly id: string
+    readonly sessionID: string
+    readonly action: string
+    readonly resources: ReadonlyArray<string>
+    readonly save?: ReadonlyArray<string>
+    readonly metadata?: { readonly [x: string]: JsonValue }
+    readonly source?: { readonly type: "tool"; readonly messageID: string; readonly callID: string }
+  }
+}["data"]
+
+export type PermissionsReplyInput = {
+  readonly sessionID: { readonly sessionID: string; readonly requestID: string }["sessionID"]
+  readonly requestID: { readonly sessionID: string; readonly requestID: string }["requestID"]
+  readonly reply: { readonly reply: "once" | "always" | "reject"; readonly message?: string | undefined }["reply"]
+  readonly message?: { readonly reply: "once" | "always" | "reject"; readonly message?: string | undefined }["message"]
+}
+
+export type PermissionsReplyOutput = void
+
+export type FilesListInput = {
+  readonly location?: {
+    readonly location?: { readonly directory?: string | undefined; readonly workspace?: string | undefined } | undefined
+    readonly path?: string | undefined
+  }["location"]
+  readonly path?: {
+    readonly location?: { readonly directory?: string | undefined; readonly workspace?: string | undefined } | undefined
+    readonly path?: string | undefined
+  }["path"]
+}
+
+export type FilesListOutput = {
+  readonly location: {
+    readonly directory: string
+    readonly workspaceID?: string
+    readonly project: { readonly id: string; readonly directory: string }
+  }
+  readonly data: ReadonlyArray<{ readonly path: string; readonly type: "file" | "directory" }>
+}
+
+export type FilesFindInput = {
+  readonly location?: {
+    readonly location?: { readonly directory?: string | undefined; readonly workspace?: string | undefined } | undefined
+    readonly query: string
+    readonly type?: "file" | "directory" | undefined
+    readonly limit?: number | undefined
+  }["location"]
+  readonly query: {
+    readonly location?: { readonly directory?: string | undefined; readonly workspace?: string | undefined } | undefined
+    readonly query: string
+    readonly type?: "file" | "directory" | undefined
+    readonly limit?: number | undefined
+  }["query"]
+  readonly type?: {
+    readonly location?: { readonly directory?: string | undefined; readonly workspace?: string | undefined } | undefined
+    readonly query: string
+    readonly type?: "file" | "directory" | undefined
+    readonly limit?: number | undefined
+  }["type"]
+  readonly limit?: {
+    readonly location?: { readonly directory?: string | undefined; readonly workspace?: string | undefined } | undefined
+    readonly query: string
+    readonly type?: "file" | "directory" | undefined
+    readonly limit?: number | undefined
+  }["limit"]
+}
+
+export type FilesFindOutput = {
+  readonly location: {
+    readonly directory: string
+    readonly workspaceID?: string
+    readonly project: { readonly id: string; readonly directory: string }
+  }
+  readonly data: ReadonlyArray<{ readonly path: string; readonly type: "file" | "directory" }>
+}
+
+export type CommandsListInput = {
+  readonly location?: {
+    readonly location?: { readonly directory?: string | undefined; readonly workspace?: string | undefined } | undefined
+  }["location"]
+}
+
+export type CommandsListOutput = {
+  readonly location: {
+    readonly directory: string
+    readonly workspaceID?: string
+    readonly project: { readonly id: string; readonly directory: string }
+  }
+  readonly data: ReadonlyArray<{
+    readonly name: string
+    readonly template: string
+    readonly description?: string
+    readonly agent?: string
+    readonly model?: { readonly id: string; readonly providerID: string; readonly variant?: string }
+    readonly subtask?: boolean
+  }>
+}
+
+export type SkillsListInput = {
+  readonly location?: {
+    readonly location?: { readonly directory?: string | undefined; readonly workspace?: string | undefined } | undefined
+  }["location"]
+}
+
+export type SkillsListOutput = {
+  readonly location: {
+    readonly directory: string
+    readonly workspaceID?: string
+    readonly project: { readonly id: string; readonly directory: string }
+  }
+  readonly data: ReadonlyArray<{
+    readonly name: string
+    readonly description?: string
+    readonly slash?: boolean
+    readonly location: string
+    readonly content: string
+  }>
+}
+
+export type EventsSubscribeOutput = OpenCodeEventEncoded
+
+export type PtysListInput = {
+  readonly location?: {
+    readonly location?: { readonly directory?: string | undefined; readonly workspace?: string | undefined } | undefined
+  }["location"]
+}
+
+export type PtysListOutput = {
+  readonly location: {
+    readonly directory: string
+    readonly workspaceID?: string
+    readonly project: { readonly id: string; readonly directory: string }
+  }
+  readonly data: ReadonlyArray<{
+    readonly id: string
+    readonly title: string
+    readonly command: string
+    readonly args: ReadonlyArray<string>
+    readonly cwd: string
+    readonly status: "running" | "exited"
+    readonly pid: number
+    readonly exitCode?: number
+  }>
+}
+
+export type PtysCreateInput = {
+  readonly location?: {
+    readonly location?: { readonly directory?: string | undefined; readonly workspace?: string | undefined } | undefined
+  }["location"]
+  readonly command?: {
+    readonly command?: string
+    readonly args?: ReadonlyArray<string>
+    readonly cwd?: string
+    readonly title?: string
+    readonly env?: { readonly [x: string]: string }
+  }["command"]
+  readonly args?: {
+    readonly command?: string
+    readonly args?: ReadonlyArray<string>
+    readonly cwd?: string
+    readonly title?: string
+    readonly env?: { readonly [x: string]: string }
+  }["args"]
+  readonly cwd?: {
+    readonly command?: string
+    readonly args?: ReadonlyArray<string>
+    readonly cwd?: string
+    readonly title?: string
+    readonly env?: { readonly [x: string]: string }
+  }["cwd"]
+  readonly title?: {
+    readonly command?: string
+    readonly args?: ReadonlyArray<string>
+    readonly cwd?: string
+    readonly title?: string
+    readonly env?: { readonly [x: string]: string }
+  }["title"]
+  readonly env?: {
+    readonly command?: string
+    readonly args?: ReadonlyArray<string>
+    readonly cwd?: string
+    readonly title?: string
+    readonly env?: { readonly [x: string]: string }
+  }["env"]
+}
+
+export type PtysCreateOutput = {
+  readonly location: {
+    readonly directory: string
+    readonly workspaceID?: string
+    readonly project: { readonly id: string; readonly directory: string }
+  }
+  readonly data: {
+    readonly id: string
+    readonly title: string
+    readonly command: string
+    readonly args: ReadonlyArray<string>
+    readonly cwd: string
+    readonly status: "running" | "exited"
+    readonly pid: number
+    readonly exitCode?: number
+  }
+}
+
+export type PtysGetInput = {
+  readonly ptyID: { readonly ptyID: string }["ptyID"]
+  readonly location?: {
+    readonly location?: { readonly directory?: string | undefined; readonly workspace?: string | undefined } | undefined
+  }["location"]
+}
+
+export type PtysGetOutput = {
+  readonly location: {
+    readonly directory: string
+    readonly workspaceID?: string
+    readonly project: { readonly id: string; readonly directory: string }
+  }
+  readonly data: {
+    readonly id: string
+    readonly title: string
+    readonly command: string
+    readonly args: ReadonlyArray<string>
+    readonly cwd: string
+    readonly status: "running" | "exited"
+    readonly pid: number
+    readonly exitCode?: number
+  }
+}
+
+export type PtysUpdateInput = {
+  readonly ptyID: { readonly ptyID: string }["ptyID"]
+  readonly location?: {
+    readonly location?: { readonly directory?: string | undefined; readonly workspace?: string | undefined } | undefined
+  }["location"]
+  readonly title?: {
+    readonly title?: string
+    readonly size?: { readonly rows: number; readonly cols: number }
+  }["title"]
+  readonly size?: { readonly title?: string; readonly size?: { readonly rows: number; readonly cols: number } }["size"]
+}
+
+export type PtysUpdateOutput = {
+  readonly location: {
+    readonly directory: string
+    readonly workspaceID?: string
+    readonly project: { readonly id: string; readonly directory: string }
+  }
+  readonly data: {
+    readonly id: string
+    readonly title: string
+    readonly command: string
+    readonly args: ReadonlyArray<string>
+    readonly cwd: string
+    readonly status: "running" | "exited"
+    readonly pid: number
+    readonly exitCode?: number
+  }
+}
+
+export type PtysRemoveInput = {
+  readonly ptyID: { readonly ptyID: string }["ptyID"]
+  readonly location?: {
+    readonly location?: { readonly directory?: string | undefined; readonly workspace?: string | undefined } | undefined
+  }["location"]
+}
+
+export type PtysRemoveOutput = void
+
+export type QuestionsListRequestsInput = {
+  readonly location?: {
+    readonly location?: { readonly directory?: string | undefined; readonly workspace?: string | undefined } | undefined
+  }["location"]
+}
+
+export type QuestionsListRequestsOutput = {
+  readonly location: {
+    readonly directory: string
+    readonly workspaceID?: string
+    readonly project: { readonly id: string; readonly directory: string }
+  }
+  readonly data: ReadonlyArray<{
+    readonly id: string
+    readonly sessionID: string
+    readonly questions: ReadonlyArray<{
+      readonly question: string
+      readonly header: string
+      readonly options: ReadonlyArray<{ readonly label: string; readonly description: string }>
+      readonly multiple?: boolean
+      readonly custom?: boolean
+    }>
+    readonly tool?: { readonly messageID: string; readonly callID: string }
+  }>
+}
+
+export type QuestionsListInput = { readonly sessionID: { readonly sessionID: string }["sessionID"] }
+
+export type QuestionsListOutput = {
+  readonly data: ReadonlyArray<{
+    readonly id: string
+    readonly sessionID: string
+    readonly questions: ReadonlyArray<{
+      readonly question: string
+      readonly header: string
+      readonly options: ReadonlyArray<{ readonly label: string; readonly description: string }>
+      readonly multiple?: boolean
+      readonly custom?: boolean
+    }>
+    readonly tool?: { readonly messageID: string; readonly callID: string }
+  }>
+}["data"]
+
+export type QuestionsReplyInput = {
+  readonly sessionID: { readonly sessionID: string; readonly requestID: string }["sessionID"]
+  readonly requestID: { readonly sessionID: string; readonly requestID: string }["requestID"]
+  readonly answers: { readonly answers: ReadonlyArray<ReadonlyArray<string>> }["answers"]
+}
+
+export type QuestionsReplyOutput = void
+
+export type QuestionsRejectInput = {
+  readonly sessionID: { readonly sessionID: string; readonly requestID: string }["sessionID"]
+  readonly requestID: { readonly sessionID: string; readonly requestID: string }["requestID"]
+}
+
+export type QuestionsRejectOutput = void
+
+export type ReferencesListInput = {
+  readonly location?: {
+    readonly location?: { readonly directory?: string | undefined; readonly workspace?: string | undefined } | undefined
+  }["location"]
+}
+
+export type ReferencesListOutput = {
+  readonly location: {
+    readonly directory: string
+    readonly workspaceID?: string
+    readonly project: { readonly id: string; readonly directory: string }
+  }
+  readonly data: ReadonlyArray<{
+    readonly name: string
+    readonly path: string
+    readonly description?: string
+    readonly hidden?: boolean
+    readonly source:
+      | { readonly type: "local"; readonly path: string; readonly description?: string; readonly hidden?: boolean }
+      | {
+          readonly type: "git"
+          readonly repository: string
+          readonly branch?: string
+          readonly description?: string
+          readonly hidden?: boolean
+        }
+  }>
+}
+
+export type ProjectCopiesCreateInput = {
+  readonly projectID: { readonly projectID: string }["projectID"]
+  readonly location?: {
+    readonly location?: { readonly directory?: string | undefined; readonly workspace?: string | undefined } | undefined
+  }["location"]
+  readonly strategy: { readonly strategy: string; readonly directory: string; readonly name?: string }["strategy"]
+  readonly directory: { readonly strategy: string; readonly directory: string; readonly name?: string }["directory"]
+  readonly name?: { readonly strategy: string; readonly directory: string; readonly name?: string }["name"]
+}
+
+export type ProjectCopiesCreateOutput = { readonly directory: string }
+
+export type ProjectCopiesRemoveInput = {
+  readonly projectID: { readonly projectID: string }["projectID"]
+  readonly location?: {
+    readonly location?: { readonly directory?: string | undefined; readonly workspace?: string | undefined } | undefined
+  }["location"]
+  readonly directory: { readonly directory: string; readonly force: boolean }["directory"]
+  readonly force: { readonly directory: string; readonly force: boolean }["force"]
+}
+
+export type ProjectCopiesRemoveOutput = void
+
+export type ProjectCopiesRefreshInput = {
+  readonly projectID: { readonly projectID: string }["projectID"]
+  readonly location?: {
+    readonly location?: { readonly directory?: string | undefined; readonly workspace?: string | undefined } | undefined
+  }["location"]
+}
+
+export type ProjectCopiesRefreshOutput = void

+ 5 - 8
packages/client/test/contract-identity.test.ts

@@ -19,8 +19,7 @@ import { SessionMessage } from "@opencode-ai/schema/session-message"
 import { Workspace } from "@opencode-ai/schema/workspace"
 import { Api } from "@opencode-ai/server/api"
 import { compile, emitPromise } from "@opencode-ai/httpapi-codegen"
-import { HttpApi } from "effect/unstable/httpapi"
-import { EventGroup, SessionGroup } from "../src/contract"
+import { ClientApi, endpointNames, groupNames, omitEndpoints } from "../src/contract"
 
 test("Core and Server reuse the authoritative Schema and Protocol values", () => {
   expect(AgentV2.ID).toBe(Agent.ID)
@@ -31,18 +30,16 @@ test("Core and Server reuse the authoritative Schema and Protocol values", () =>
   expect(CoreSessionMessage.Message).toBe(SessionMessage.Message)
   expect(CorePrompt).toBe(Prompt)
   expect(Api.groups["server.session"].identifier).toBe("server.session")
-  expect(SessionGroup.identifier).toBe(Api.groups["server.session"].identifier)
-  expect(EventGroup.identifier).toBe(Api.groups["server.event"].identifier)
+  expect(Object.keys(ClientApi.groups)).toEqual(Object.keys(Api.groups))
   expect(Session.ID.create()).toStartWith("ses_")
   expect(Project.ID.global).toBe("global")
   expect(Provider.ID.anthropic).toBe("anthropic")
   expect(Workspace.ID.create()).toStartWith("wrk_")
 })
 
-test("client and Server Session contracts generate identically", () => {
-  const options = { groupNames: { "server.session": "sessions" } }
-  const server = compile(HttpApi.make("server").add(Api.groups["server.session"]), options)
-  const client = compile(HttpApi.make("client").add(SessionGroup), options)
+test("client and Server contracts generate identically", () => {
+  const server = compile(Api, { groupNames, endpointNames, omitEndpoints })
+  const client = compile(ClientApi, { groupNames, endpointNames, omitEndpoints })
 
   expect(emitPromise(client)).toEqual(emitPromise(server))
 })

+ 37 - 0
packages/client/test/promise.test.ts

@@ -1,6 +1,43 @@
 import { expect, test } from "bun:test"
 import { isSessionNotFoundError, isUnauthorizedError, OpenCode } from "../src"
 
+test("exposes every standard HTTP API group", () => {
+  const client = OpenCode.make({ baseUrl: "http://localhost:3000" })
+
+  expect(Object.keys(client)).toEqual([
+    "health",
+    "location",
+    "agents",
+    "sessions",
+    "messages",
+    "models",
+    "providers",
+    "integrations",
+    "credentials",
+    "permissions",
+    "files",
+    "commands",
+    "skills",
+    "events",
+    "ptys",
+    "questions",
+    "references",
+    "projectCopies",
+  ])
+  expect(Object.keys(client.messages)).toEqual(["list"])
+  expect(Object.keys(client.integrations)).toEqual([
+    "list",
+    "get",
+    "connectKey",
+    "connectOauth",
+    "attemptStatus",
+    "attemptComplete",
+    "attemptCancel",
+  ])
+  expect(Object.keys(client.files)).toEqual(["list", "find"])
+  expect(Object.keys(client.ptys)).toEqual(["list", "create", "get", "update", "remove"])
+})
+
 test("sessions.get returns the wire projection", async () => {
   const client = OpenCode.make({
     baseUrl: "http://localhost:3000",

+ 40 - 26
packages/httpapi-codegen/src/index.ts

@@ -50,7 +50,7 @@ export type Endpoint = {
   readonly operation: Operation
   readonly input: ReadonlyArray<InputField & { readonly optional: boolean }>
   readonly unwrapData: boolean
-  readonly errors: ReadonlyArray<Schema.Top>
+  readonly errors: ReadonlyArray<{ readonly status: number; readonly schema: Schema.Top }>
   readonly successes: ReadonlyArray<Schema.Top>
   readonly effectPortable: boolean
 }
@@ -75,7 +75,11 @@ const manifestName = ".httpapi-codegen.json"
 
 export function compile<Id extends string, Groups extends HttpApiGroup.Any>(
   api: HttpApi.HttpApi<Id, Groups>,
-  options?: { readonly groupNames?: Readonly<Record<string, string>> },
+  options?: {
+    readonly groupNames?: Readonly<Record<string, string>>
+    readonly endpointNames?: Readonly<Record<string, string>>
+    readonly omitEndpoints?: ReadonlySet<string>
+  },
 ): Contract {
   const endpoints: Array<Endpoint> = []
   const portable = new Map<SchemaAST.AST, boolean>()
@@ -83,6 +87,7 @@ export function compile<Id extends string, Groups extends HttpApiGroup.Any>(
   HttpApi.reflect(api, {
     onGroup() {},
     onEndpoint({ endpoint, errors, group, middleware }) {
+      if (options?.omitEndpoints?.has(endpoint.name)) return
       const groupName = options?.groupNames?.[group.identifier] ?? group.identifier
       const name = `${groupName}.${endpoint.name}`
       const required = Array.from(middleware).find((item) => item.requiredForClient)
@@ -103,8 +108,8 @@ export function compile<Id extends string, Groups extends HttpApiGroup.Any>(
       }
       const payloads = sourcePayloads.map((schema) => normalizeTransport(schema, "payload", endpoint, name)!)
       const success = normalizeTransport(successSchemas[0], "success", endpoint, name)!
-      const errorSchemas = Array.from(errors.values()).flatMap((schemas) =>
-        schemas.map((schema) => normalizeTransport(schema, "error", endpoint, name)!),
+      const errorSchemas = Array.from(errors).flatMap(([status, schemas]) =>
+        schemas.map((schema) => ({ status, ...normalizeTransport(schema, "error", endpoint, name)! })),
       )
       const inputs = [
         ...inputFields(params?.schema, "params", name),
@@ -124,7 +129,7 @@ export function compile<Id extends string, Groups extends HttpApiGroup.Any>(
         ...(headers === undefined ? [] : [[`${name}.headers`, headers.schema] as const]),
         ...payloads.map((item) => [`${name}.payload`, item.schema] as const),
         ...responseSchemas(success.schema, `${name}.success`),
-        ...errorSchemas.map((item) => [`${name}.error`, item.schema] as const),
+        ...errorSchemas.map((item) => [`${name}.error.${item.status}`, item.schema] as const),
       ]
       const effectPortable =
         [params, query, headers, ...payloads, success, ...errorSchemas].every(
@@ -146,11 +151,11 @@ export function compile<Id extends string, Groups extends HttpApiGroup.Any>(
         input: inputs,
         unwrapData: isDataEnvelope(success.schema),
         successes: [success.schema],
-        errors: errorSchemas.map((item) => item.schema),
+        errors: errorSchemas.map((item) => ({ status: item.status, schema: item.schema })),
         effectPortable,
         operation: {
           group: groupName,
-          name: clientEndpointName(endpoint.name),
+          name: options?.endpointNames?.[endpoint.name] ?? clientEndpointName(endpoint.name),
           input: inputs.map(({ name, source }) => ({ name, source })),
           inputMode: inputs.length === 0 ? "none" : inputs.every((field) => field.optional) ? "optional" : "required",
           success: isStreamSchema(success.schema)
@@ -287,10 +292,10 @@ function assertPromiseEndpoint(endpoint: Endpoint) {
     throw new GenerationError({ reason: `Unsupported Promise success encoding: ${name}` })
   }
   for (const error of endpoint.errors) {
-    if (taggedErrorFields(error) === undefined) {
-      throw new GenerationError({ reason: `Promise error must be tagged: ${name}` })
+    if (declaredErrorFields(error.schema) === undefined) {
+      throw new GenerationError({ reason: `Promise error must have a literal discriminator: ${name}` })
     }
-    if ((resolveHttpApiEncoding(error.ast)?._tag ?? "Json") !== "Json") {
+    if ((resolveHttpApiEncoding(error.schema.ast)?._tag ?? "Json") !== "Json") {
       throw new GenerationError({ reason: `Unsupported Promise error encoding: ${name}` })
     }
   }
@@ -332,7 +337,7 @@ function renderImportedEffectFiles(
           const fields = item.input.filter((field) => field.source === source)
           if (fields.length === 0) return []
           return [
-            `${source}: { ${fields.map((field) => `${JSON.stringify(field.name)}: input${item.operation.inputMode === "optional" ? "?." : "."}${field.name}`).join(", ")} }`,
+            `${source}: { ${fields.map((field) => `${JSON.stringify(field.name)}: input${item.operation.inputMode === "optional" ? "?." : ""}[${JSON.stringify(field.name)}]`).join(", ")} }`,
           ]
         })
         .join(", ")
@@ -429,8 +434,8 @@ function renderPromiseTypes(
   const errors = new Map(
     groups.flatMap((group) =>
       group.endpoints.flatMap((endpoint) =>
-        endpoint.errors.flatMap((schema) => {
-          const tagged = taggedErrorFields(schema)
+        endpoint.errors.flatMap((error) => {
+          const tagged = declaredErrorFields(error.schema)
           return tagged === undefined ? [] : [[tagged.tag, tagged] as const]
         }),
       ),
@@ -440,7 +445,7 @@ function renderPromiseTypes(
     const fields = error.fields
       .map(([name, schema, optional]) => `readonly ${JSON.stringify(name)}${optional ? "?" : ""}: ${typeOf(schema)}`)
       .join("; ")
-    return `export type ${error.identifier} = { readonly _tag: ${JSON.stringify(error.tag)}; ${fields} }\nexport const is${error.identifier} = (value: unknown): value is ${error.identifier} => typeof value === "object" && value !== null && "_tag" in value && value._tag === ${JSON.stringify(error.tag)}`
+    return `export type ${error.identifier} = { readonly ${JSON.stringify(error.key)}: ${JSON.stringify(error.tag)}; ${fields} }\nexport const is${error.identifier} = (value: unknown): value is ${error.identifier} => typeof value === "object" && value !== null && ${JSON.stringify(error.key)} in value && value[${JSON.stringify(error.key)}] === ${JSON.stringify(error.tag)}`
   })
   const operations = groups
     .flatMap((group) =>
@@ -499,7 +504,8 @@ function renderPromiseClient(groups: ReadonlyArray<Group>) {
           ? "requestOptions?: RequestOptions"
           : `input${endpoint.operation.inputMode === "optional" ? "?" : ""}: ${prefix}Input, requestOptions?: RequestOptions`
       const path = promisePath(endpoint.endpoint.path, endpoint.input)
-      const access = (name: string) => `input${endpoint.operation.inputMode === "optional" ? "?." : "."}${name}`
+      const access = (name: string) =>
+        `input${endpoint.operation.inputMode === "optional" ? "?." : ""}[${JSON.stringify(name)}]`
       const part = (source: InputField["source"]) => {
         const inputs = endpoint.input.filter((field) => field.source === source)
         return inputs.length === 0
@@ -511,11 +517,7 @@ function renderPromiseClient(groups: ReadonlyArray<Group>) {
         endpoint.headers === undefined ? undefined : `headers: ${part("headers")}`,
         endpoint.payloads.length === 0 ? undefined : `body: ${part("payload")}`,
       ].filter((value): value is string => value !== undefined)
-      const declaredStatuses = [
-        ...new Set(
-          endpoint.errors.map((schema) => resolveHttpApiStatus(schema.ast)).filter((status) => status !== undefined),
-        ),
-      ]
+      const declaredStatuses = [...new Set(endpoint.errors.map((error) => error.status))]
       const descriptor = `{ method: ${JSON.stringify(endpoint.endpoint.method)}, path: ${path}${parts.length === 0 ? "" : `, ${parts.join(", ")}`}, successStatus: ${resolveHttpApiStatus(endpoint.successes[0].ast) ?? 200}, declaredStatuses: [${declaredStatuses.join(", ")}], empty: ${endpoint.operation.success === "void"} }`
       if (endpoint.operation.success === "stream") {
         const success = endpoint.successes[0]
@@ -567,9 +569,12 @@ function structuralType(schema: Schema.Top) {
   )
   const expand = (type: string, seen = new Set<string>()): string => {
     for (const [reference, value] of references) {
-      if (!type.includes(reference)) continue
-      if (seen.has(reference)) throw new GenerationError({ reason: "Recursive Promise types are not implemented" })
-      type = type.replaceAll(reference, `(${expand(value, new Set([...seen, reference]))})`)
+      const pattern = `(?<![A-Za-z0-9_$.'"])${reference.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?![A-Za-z0-9_$.'"])`
+      if (!new RegExp(pattern).test(type)) continue
+      if (seen.has(reference)) {
+        throw new GenerationError({ reason: `Recursive Promise types are not implemented: ${reference}` })
+      }
+      type = type.replace(new RegExp(pattern, "g"), `(${expand(value, new Set([...seen, reference]))})`)
     }
     return type
   }
@@ -579,6 +584,7 @@ function structuralType(schema: Schema.Top) {
 }
 
 function promisePath(path: string, input: ReadonlyArray<InputField>) {
+  if (path.includes("*")) throw new GenerationError({ reason: `Unsupported Promise path wildcard: ${path}` })
   const fields = new Set(input.filter((field) => field.source === "params").map((field) => field.name))
   const segments = path.split(/(:[A-Za-z_][A-Za-z0-9_]*)/g).filter(Boolean)
   const template = segments
@@ -932,18 +938,26 @@ function serializable(value: unknown): boolean {
 }
 
 function taggedErrorFields(schema: Schema.Top) {
+  const fields = declaredErrorFields(schema)
+  return fields?.key === "_tag" ? fields : undefined
+}
+
+function declaredErrorFields(schema: Schema.Top) {
   if (!SchemaAST.isDeclaration(schema.ast) || schema.ast.annotations?.["~effect/Schema/Class"] === undefined) {
     return undefined
   }
   const fields = schema.ast.typeParameters[0]
   if (!SchemaAST.isObjects(fields) || fields.indexSignatures.length > 0) return undefined
-  const tag = fields.propertySignatures.find((field) => field.name === "_tag")?.type
+  const key = fields.propertySignatures.find((field) => field.name === "_tag" || field.name === "name")?.name
+  if (key !== "_tag" && key !== "name") return undefined
+  const tag = fields.propertySignatures.find((field) => field.name === key)?.type
   if (tag === undefined || !SchemaAST.isLiteral(tag) || typeof tag.literal !== "string") return undefined
   return {
+    key,
     tag: tag.literal,
     identifier: SchemaAST.resolveIdentifier(schema.ast) ?? tag.literal,
     fields: fields.propertySignatures.flatMap((field) =>
-      field.name === "_tag" || typeof field.name !== "string"
+      field.name === key || typeof field.name !== "string"
         ? []
         : [[field.name, Schema.make(field.type), SchemaAST.isOptional(field.type)] as const],
     ),
@@ -1014,7 +1028,7 @@ function renderGroup(group: Group, groupIndex: number) {
     const headers = addSlot(endpointHeaders, `${prefix}Headers`)
     const payloads = endpointPayloads.map((schema, index) => addSlot(schema, `${prefix}Payload${index}`)!)
     const success = renderSuccess(successes[0], `${prefix}Success`)
-    const errorSlots = errors.map((schema, index) => addSlot(schema, `${prefix}Error${index}`)!)
+    const errorSlots = errors.map((error, index) => addSlot(error.schema, `${prefix}Error${index}`)!)
     const options = [
       params === undefined ? undefined : `params: ${params.name}`,
       query === undefined ? undefined : `query: ${query.name}`,

+ 105 - 0
packages/httpapi-codegen/test/generate.test.ts

@@ -138,6 +138,48 @@ describe("HttpApiCodegen.generate", () => {
     expect(contract.groups[0]?.endpoints[0]?.operation).toMatchObject({ group: "sessions", name: "get" })
   })
 
+  test("supports explicit public endpoint names", () => {
+    const source = HttpApi.make("test").add(
+      HttpApiGroup.make("server.permission")
+        .add(HttpApiEndpoint.get("permission.request.list", "/request", { success: Schema.String }))
+        .add(HttpApiEndpoint.get("session.permission.list", "/session", { success: Schema.String })),
+    )
+    const contract = compileContract(source, {
+      endpointNames: { "permission.request.list": "listRequests" },
+    })
+
+    expect(contract.groups[0]?.endpoints.map((endpoint) => endpoint.operation.name)).toEqual(["listRequests", "list"])
+  })
+
+  test("omits custom transport endpoints", () => {
+    const source = HttpApi.make("test").add(
+      HttpApiGroup.make("server.pty")
+        .add(HttpApiEndpoint.get("pty.get", "/pty", { success: Schema.String }))
+        .add(HttpApiEndpoint.get("pty.connect", "/pty/connect", { success: Schema.Boolean })),
+    )
+    const contract = compileContract(source, { omitEndpoints: new Set(["pty.connect"]) })
+
+    expect(contract.groups[0]?.endpoints.map((endpoint) => endpoint.endpoint.name)).toEqual(["pty.get"])
+  })
+
+  test("uses bracket access for input field names", () => {
+    const source = api(
+      HttpApiEndpoint.post("token", "/token", {
+        headers: { "x-example-token": Schema.Literal("1") },
+        success: Schema.String,
+      }),
+    )
+    const contract = compileContract(source)
+    const promise = emitPromise(contract).files.find((file) => file.path === "client.ts")?.content
+    const effect = emitEffectImported(contract, {
+      module: "@example/api",
+      endpoints: { "session.token": "Token" },
+    }).files.find((file) => file.path === "client.ts")?.content
+
+    expect(promise).toContain('"x-example-token": input["x-example-token"]')
+    expect(effect).toContain('"x-example-token": input["x-example-token"]')
+  })
+
   test("rejects consumer group name collisions", () => {
     const source = HttpApi.make("test")
       .add(HttpApiGroup.make("first").add(HttpApiEndpoint.get("one", "/one", { success: Schema.String })))
@@ -184,6 +226,33 @@ describe("HttpApiCodegen.generate", () => {
     )
   })
 
+  test("supports name-discriminated Promise errors", () => {
+    class NamedError extends Schema.ErrorClass<NamedError>("NamedError")(
+      { name: Schema.Literal("NamedError"), message: Schema.String },
+      { httpApiStatus: 400 },
+    ) {}
+    const output = emitPromise(
+      compileContract(
+        api(HttpApiEndpoint.get("get", "/session", { success: Schema.NumberFromString, error: NamedError })),
+      ),
+    )
+    const types = output.files.find((file) => file.path === "types.ts")?.content
+
+    expect(types).toContain('readonly "name": "NamedError"')
+    expect(types).toContain('"name" in value && value["name"] === "NamedError"')
+  })
+
+  test("preserves reflected default error statuses", () => {
+    class MissingStatus extends Schema.TaggedErrorClass<MissingStatus>()("MissingStatus", {
+      message: Schema.String,
+    }) {}
+    const output = emitPromise(
+      compileContract(api(HttpApiEndpoint.get("get", "/session", { success: Schema.String, error: MissingStatus }))),
+    )
+
+    expect(output.files.find((file) => file.path === "client.ts")?.content).toContain("declaredStatuses: [500]")
+  })
+
   test("erases brands from Promise wire types", () => {
     const output = emitPromise(
       compileContract(
@@ -218,6 +287,26 @@ describe("HttpApiCodegen.generate", () => {
     )
   })
 
+  test("expands Promise references only at identifier boundaries", () => {
+    const Session = Schema.Struct({ name: Schema.Literal("Session"), id: Schema.String }).annotate({
+      identifier: "Session",
+    })
+    const SessionID = Schema.String.annotate({ identifier: "SessionID" })
+    const output = emitPromise(
+      compileContract(
+        api(
+          HttpApiEndpoint.get("get", "/session", {
+            success: Schema.Struct({ session: Session, sessionID: SessionID }),
+          }),
+        ),
+      ),
+    )
+
+    expect(output.files.find((file) => file.path === "types.ts")?.content).toContain(
+      'readonly "session": ({ readonly "name": "Session", readonly "id": string })',
+    )
+  })
+
   test("emits Effect Json schemas as standalone Promise types", () => {
     const output = emitPromise(
       compileContract(
@@ -265,6 +354,22 @@ describe("HttpApiCodegen.generate", () => {
       ),
     ).toThrow("Unsupported Promise success encoding: session.text")
 
+    expect(() =>
+      emitPromise(
+        compileContract(
+          api(
+            HttpApiEndpoint.get("binary", "/binary", {
+              success: Schema.Uint8Array.pipe(HttpApiSchema.asUint8Array()),
+            }),
+          ),
+        ),
+      ),
+    ).toThrow("Unsupported Promise success encoding: session.binary")
+
+    expect(() =>
+      emitPromise(compileContract(api(HttpApiEndpoint.get("read", "/file/*", { success: Schema.String })))),
+    ).toThrow("Unsupported Promise path wildcard: /file/*")
+
     expect(() =>
       emitPromise(
         compileContract(