Compare commits

...
Author SHA1 Message Date
Dax Raad c6e589c142 fix(protocol): remove version prefixes from operation IDs 2026-08-25 10:05:07 -04:00
19 changed files with 70 additions and 61 deletions
@@ -18,6 +18,7 @@ type OpenApiResponse = {
readonly content?: Record<string, { readonly schema?: OpenApiSchema }>
}
type OpenApiOperation = {
readonly operationId?: string
readonly parameters?: ReadonlyArray<{
readonly name: string
readonly in: string
@@ -70,6 +71,14 @@ function isBuiltInEndpointError(name: string) {
}
describe("PublicApi OpenAPI v2 errors", () => {
test("omits version prefixes from operation IDs", () => {
const spec = OpenApi.fromApi(PublicApi) as OpenApiSpec
expect(v2Operations(spec).map(({ operation }) => operation.operationId)).not.toContainEqual(
expect.stringMatching(/v2/i),
)
})
test("includes plugin-facing core schemas", () => {
const spec = OpenApi.fromApi(PublicApi) as OpenApiSpec
+1 -1
View File
@@ -12,7 +12,7 @@ export const AgentGroup = HttpApiGroup.make("server.agent").add(
.annotateMerge(locationQueryOpenApi)
.annotateMerge(
OpenApi.annotations({
identifier: "v2.agent.list",
identifier: "agent.list",
summary: "List agents",
description: "Retrieve currently registered agents.",
}),
+1 -1
View File
@@ -13,7 +13,7 @@ export const CommandGroup = HttpApiGroup.make("server.command")
.annotateMerge(locationQueryOpenApi)
.annotateMerge(
OpenApi.annotations({
identifier: "v2.command.list",
identifier: "command.list",
summary: "List commands",
description: "Retrieve currently registered commands.",
}),
+2 -2
View File
@@ -14,7 +14,7 @@ export const CredentialGroup = HttpApiGroup.make("server.credential")
.annotateMerge(locationQueryOpenApi)
.annotateMerge(
OpenApi.annotations({
identifier: "v2.credential.update",
identifier: "credential.update",
summary: "Update credential",
description: "Update a stored credential label.",
}),
@@ -29,7 +29,7 @@ export const CredentialGroup = HttpApiGroup.make("server.credential")
.annotateMerge(locationQueryOpenApi)
.annotateMerge(
OpenApi.annotations({
identifier: "v2.credential.remove",
identifier: "credential.remove",
summary: "Remove credential",
description: "Remove a stored integration credential.",
}),
+1 -1
View File
@@ -36,7 +36,7 @@ const make = <const Definitions extends ReadonlyArray<Definition>>(definitions:
success: HttpApiSchema.StreamSse({ data: EventSchema }),
}).annotateMerge(
OpenApi.annotations({
identifier: "v2.event.subscribe",
identifier: "event.subscribe",
summary: "Subscribe to events",
description: "Subscribe to native event payloads for the server.",
}),
+3 -3
View File
@@ -26,7 +26,7 @@ export const FileSystemGroup = HttpApiGroup.make("server.fs")
.annotateMerge(locationQueryOpenApi)
.annotateMerge(
OpenApi.annotations({
identifier: "v2.fs.read",
identifier: "fs.read",
summary: "Read file",
description: "Serve one file relative to the requested location.",
}),
@@ -40,7 +40,7 @@ export const FileSystemGroup = HttpApiGroup.make("server.fs")
.annotateMerge(locationQueryOpenApi)
.annotateMerge(
OpenApi.annotations({
identifier: "v2.fs.list",
identifier: "fs.list",
summary: "List directory",
description: "List direct children of one directory relative to the requested location.",
}),
@@ -54,7 +54,7 @@ export const FileSystemGroup = HttpApiGroup.make("server.fs")
.annotateMerge(locationQueryOpenApi)
.annotateMerge(
OpenApi.annotations({
identifier: "v2.fs.find",
identifier: "fs.find",
summary: "Find files",
description: "Find recursively ranked filesystem entries relative to the requested location.",
}),
+1 -1
View File
@@ -6,7 +6,7 @@ export const HealthGroup = HttpApiGroup.make("server.health").add(
success: Schema.Struct({ healthy: Schema.Literal(true) }),
}).annotateMerge(
OpenApi.annotations({
identifier: "v2.health.get",
identifier: "health.get",
summary: "Check server health",
description: "Check whether the API server is ready to accept requests.",
}),
+7 -7
View File
@@ -16,7 +16,7 @@ export const IntegrationGroup = HttpApiGroup.make("server.integration")
.annotateMerge(locationQueryOpenApi)
.annotateMerge(
OpenApi.annotations({
identifier: "v2.integration.list",
identifier: "integration.list",
summary: "List integrations",
description: "Retrieve available integrations and their authentication methods.",
}),
@@ -31,7 +31,7 @@ export const IntegrationGroup = HttpApiGroup.make("server.integration")
.annotateMerge(locationQueryOpenApi)
.annotateMerge(
OpenApi.annotations({
identifier: "v2.integration.get",
identifier: "integration.get",
summary: "Get integration",
description: "Retrieve one integration and its authentication methods.",
}),
@@ -51,7 +51,7 @@ export const IntegrationGroup = HttpApiGroup.make("server.integration")
.annotateMerge(locationQueryOpenApi)
.annotateMerge(
OpenApi.annotations({
identifier: "v2.integration.connect.key",
identifier: "integration.connect.key",
summary: "Connect with key",
description: "Run a key authentication method and store the resulting credential.",
}),
@@ -72,7 +72,7 @@ export const IntegrationGroup = HttpApiGroup.make("server.integration")
.annotateMerge(locationQueryOpenApi)
.annotateMerge(
OpenApi.annotations({
identifier: "v2.integration.connect.oauth",
identifier: "integration.connect.oauth",
summary: "Begin OAuth connection",
description: "Start an OAuth attempt and return the authorization details.",
}),
@@ -87,7 +87,7 @@ export const IntegrationGroup = HttpApiGroup.make("server.integration")
.annotateMerge(locationQueryOpenApi)
.annotateMerge(
OpenApi.annotations({
identifier: "v2.integration.attempt.status",
identifier: "integration.attempt.status",
summary: "Get OAuth attempt status",
description: "Poll the current status of an OAuth attempt.",
}),
@@ -104,7 +104,7 @@ export const IntegrationGroup = HttpApiGroup.make("server.integration")
.annotateMerge(locationQueryOpenApi)
.annotateMerge(
OpenApi.annotations({
identifier: "v2.integration.attempt.complete",
identifier: "integration.attempt.complete",
summary: "Complete OAuth connection",
description: "Complete a code-based OAuth attempt and store the resulting credential.",
}),
@@ -119,7 +119,7 @@ export const IntegrationGroup = HttpApiGroup.make("server.integration")
.annotateMerge(locationQueryOpenApi)
.annotateMerge(
OpenApi.annotations({
identifier: "v2.integration.attempt.cancel",
identifier: "integration.attempt.cancel",
summary: "Cancel OAuth connection",
description: "Cancel an OAuth attempt and release its resources.",
}),
+1 -1
View File
@@ -34,7 +34,7 @@ export const LocationGroup = HttpApiGroup.make("server.location").add(
.annotateMerge(locationQueryOpenApi)
.annotateMerge(
OpenApi.annotations({
identifier: "v2.location.get",
identifier: "location.get",
summary: "Get location",
description: "Resolve the requested location or the server default location.",
}),
+1 -1
View File
@@ -36,7 +36,7 @@ export const MessageGroup = HttpApiGroup.make("server.message")
error: [InvalidCursorError, SessionNotFoundError, UnknownError],
}).annotateMerge(
OpenApi.annotations({
identifier: "v2.session.messages",
identifier: "session.messages",
summary: "Get session messages",
description:
"Retrieve projected messages for a session. Items keep the requested order across pages; use cursor.next or cursor.previous to move through the ordered timeline.",
+1 -1
View File
@@ -15,7 +15,7 @@ export const ModelGroup = HttpApiGroup.make("server.model")
.annotateMerge(locationQueryOpenApi)
.annotateMerge(
OpenApi.annotations({
identifier: "v2.model.list",
identifier: "model.list",
summary: "List models",
description: "Retrieve available models ordered by release date.",
}),
+7 -7
View File
@@ -27,7 +27,7 @@ export const makePermissionGroup = <
.annotateMerge(locationQueryOpenApi)
.annotateMerge(
OpenApi.annotations({
identifier: "v2.permission.request.list",
identifier: "permission.request.list",
summary: "List pending permission requests",
description: "Retrieve pending permission requests for a location.",
}),
@@ -39,7 +39,7 @@ export const makePermissionGroup = <
success: Schema.Struct({ data: Schema.Array(PermissionSaved.Info) }),
}).annotateMerge(
OpenApi.annotations({
identifier: "v2.permission.saved.list",
identifier: "permission.saved.list",
summary: "List saved permissions",
description: "Retrieve saved permissions, optionally filtered by project.",
}),
@@ -51,7 +51,7 @@ export const makePermissionGroup = <
success: HttpApiSchema.NoContent,
}).annotateMerge(
OpenApi.annotations({
identifier: "v2.permission.saved.remove",
identifier: "permission.saved.remove",
summary: "Remove saved permission",
description: "Remove a saved permission by ID.",
}),
@@ -79,7 +79,7 @@ export const makePermissionGroup = <
.middleware(sessionLocationMiddleware)
.annotateMerge(
OpenApi.annotations({
identifier: "v2.session.permission.create",
identifier: "session.permission.create",
summary: "Create permission request",
description: "Evaluate and, when approval is required, create a permission request for a session.",
}),
@@ -94,7 +94,7 @@ export const makePermissionGroup = <
.middleware(sessionLocationMiddleware)
.annotateMerge(
OpenApi.annotations({
identifier: "v2.session.permission.list",
identifier: "session.permission.list",
summary: "List session permission requests",
description: "Retrieve pending permission requests owned by a session.",
}),
@@ -109,7 +109,7 @@ export const makePermissionGroup = <
.middleware(sessionLocationMiddleware)
.annotateMerge(
OpenApi.annotations({
identifier: "v2.session.permission.get",
identifier: "session.permission.get",
summary: "Get permission request",
description: "Retrieve a pending permission request owned by a session.",
}),
@@ -128,7 +128,7 @@ export const makePermissionGroup = <
.middleware(sessionLocationMiddleware)
.annotateMerge(
OpenApi.annotations({
identifier: "v2.session.permission.reply",
identifier: "session.permission.reply",
summary: "Reply to pending permission request",
description: "Respond to a pending permission request owned by a session.",
}),
+3 -3
View File
@@ -30,7 +30,7 @@ export const ProjectCopyGroup = HttpApiGroup.make("server.projectCopy")
error: ProjectCopyError,
})
.annotateMerge(locationQueryOpenApi)
.annotateMerge(OpenApi.annotations({ identifier: "v2.projectCopy.create" })),
.annotateMerge(OpenApi.annotations({ identifier: "projectCopy.create" })),
)
.add(
HttpApiEndpoint.delete("projectCopy.remove", root, {
@@ -41,7 +41,7 @@ export const ProjectCopyGroup = HttpApiGroup.make("server.projectCopy")
error: ProjectCopyError,
})
.annotateMerge(locationQueryOpenApi)
.annotateMerge(OpenApi.annotations({ identifier: "v2.projectCopy.remove" })),
.annotateMerge(OpenApi.annotations({ identifier: "projectCopy.remove" })),
)
.add(
HttpApiEndpoint.post("projectCopy.refresh", `${root}/refresh`, {
@@ -51,6 +51,6 @@ export const ProjectCopyGroup = HttpApiGroup.make("server.projectCopy")
error: ProjectCopyError,
})
.annotateMerge(locationQueryOpenApi)
.annotateMerge(OpenApi.annotations({ identifier: "v2.projectCopy.refresh" })),
.annotateMerge(OpenApi.annotations({ identifier: "projectCopy.refresh" })),
)
.annotateMerge(OpenApi.annotations({ title: "projectCopy", description: "Project copy management routes." }))
+2 -2
View File
@@ -15,7 +15,7 @@ export const ProviderGroup = HttpApiGroup.make("server.provider")
.annotateMerge(locationQueryOpenApi)
.annotateMerge(
OpenApi.annotations({
identifier: "v2.provider.list",
identifier: "provider.list",
summary: "List providers",
description: "Retrieve active AI providers so clients can show provider availability and configuration.",
}),
@@ -31,7 +31,7 @@ export const ProviderGroup = HttpApiGroup.make("server.provider")
.annotateMerge(locationQueryOpenApi)
.annotateMerge(
OpenApi.annotations({
identifier: "v2.provider.get",
identifier: "provider.get",
summary: "Get provider",
description: "Retrieve a single AI provider so clients can inspect its availability and endpoint settings.",
}),
+7 -7
View File
@@ -27,7 +27,7 @@ export const PtyGroup = HttpApiGroup.make("server.pty")
.annotateMerge(locationQueryOpenApi)
.annotateMerge(
OpenApi.annotations({
identifier: "v2.pty.list",
identifier: "pty.list",
summary: "List PTY sessions",
description: "List PTY sessions for a location, including exited sessions retained until removal.",
}),
@@ -42,7 +42,7 @@ export const PtyGroup = HttpApiGroup.make("server.pty")
.annotateMerge(locationQueryOpenApi)
.annotateMerge(
OpenApi.annotations({
identifier: "v2.pty.create",
identifier: "pty.create",
summary: "Create PTY session",
description: "Create a pseudo-terminal session for a location.",
}),
@@ -58,7 +58,7 @@ export const PtyGroup = HttpApiGroup.make("server.pty")
.annotateMerge(locationQueryOpenApi)
.annotateMerge(
OpenApi.annotations({
identifier: "v2.pty.get",
identifier: "pty.get",
summary: "Get PTY session",
description: "Get one PTY session, including its exit code once exited.",
}),
@@ -75,7 +75,7 @@ export const PtyGroup = HttpApiGroup.make("server.pty")
.annotateMerge(locationQueryOpenApi)
.annotateMerge(
OpenApi.annotations({
identifier: "v2.pty.update",
identifier: "pty.update",
summary: "Update PTY session",
description: "Update the title or viewport size of one PTY session.",
}),
@@ -91,7 +91,7 @@ export const PtyGroup = HttpApiGroup.make("server.pty")
.annotateMerge(locationQueryOpenApi)
.annotateMerge(
OpenApi.annotations({
identifier: "v2.pty.remove",
identifier: "pty.remove",
summary: "Remove PTY session",
description: "Terminate and remove one PTY session.",
}),
@@ -107,7 +107,7 @@ export const PtyGroup = HttpApiGroup.make("server.pty")
.annotateMerge(locationQueryOpenApi)
.annotateMerge(
OpenApi.annotations({
identifier: "v2.pty.connectToken",
identifier: "pty.connectToken",
summary: "Create PTY WebSocket token",
description: "Create a short-lived single-use ticket for opening a PTY WebSocket connection.",
}),
@@ -122,7 +122,7 @@ export const PtyGroup = HttpApiGroup.make("server.pty")
error: [ForbiddenError, PtyNotFoundError],
}).annotateMerge(
OpenApi.annotations({
identifier: "v2.pty.connect",
identifier: "pty.connect",
summary: "Connect to PTY session",
description: "Establish a WebSocket connection streaming PTY output and accepting terminal input.",
transform: (operation) => ({
+4 -4
View File
@@ -24,7 +24,7 @@ export const makeQuestionGroup = <
.annotateMerge(locationQueryOpenApi)
.annotateMerge(
OpenApi.annotations({
identifier: "v2.question.request.list",
identifier: "question.request.list",
summary: "List pending question requests",
description: "Retrieve pending question requests for a location.",
}),
@@ -42,7 +42,7 @@ export const makeQuestionGroup = <
.middleware(sessionLocationMiddleware)
.annotateMerge(
OpenApi.annotations({
identifier: "v2.session.question.list",
identifier: "session.question.list",
summary: "List session question requests",
description: "Retrieve pending question requests owned by a session.",
}),
@@ -58,7 +58,7 @@ export const makeQuestionGroup = <
.middleware(sessionLocationMiddleware)
.annotateMerge(
OpenApi.annotations({
identifier: "v2.session.question.reply",
identifier: "session.question.reply",
summary: "Reply to pending question request",
description: "Answer a pending question request owned by a session.",
}),
@@ -73,7 +73,7 @@ export const makeQuestionGroup = <
.middleware(sessionLocationMiddleware)
.annotateMerge(
OpenApi.annotations({
identifier: "v2.session.question.reject",
identifier: "session.question.reject",
summary: "Reject pending question request",
description: "Reject a pending question request owned by a session.",
}),
+1 -1
View File
@@ -13,7 +13,7 @@ export const ReferenceGroup = HttpApiGroup.make("server.reference")
.annotateMerge(locationQueryOpenApi)
.annotateMerge(
OpenApi.annotations({
identifier: "v2.reference.list",
identifier: "reference.list",
summary: "List references",
description: "List references available in the requested location.",
}),
+17 -17
View File
@@ -118,7 +118,7 @@ export const makeSessionGroup = <I extends HttpApiMiddleware.AnyId, S>(sessionLo
error: [InvalidCursorError, InvalidRequestError],
}).annotateMerge(
OpenApi.annotations({
identifier: "v2.session.list",
identifier: "session.list",
summary: "List sessions",
description:
"Retrieve sessions in the requested order. Items keep that order across pages; use cursor.next or cursor.previous to move through the ordered list.",
@@ -136,7 +136,7 @@ export const makeSessionGroup = <I extends HttpApiMiddleware.AnyId, S>(sessionLo
success: Schema.Struct({ data: Session.Info }),
}).annotateMerge(
OpenApi.annotations({
identifier: "v2.session.create",
identifier: "session.create",
summary: "Create session",
description: "Create a session at the requested location.",
}),
@@ -147,7 +147,7 @@ export const makeSessionGroup = <I extends HttpApiMiddleware.AnyId, S>(sessionLo
success: Schema.Struct({ data: Schema.Record(Session.ID, SessionActive) }),
}).annotateMerge(
OpenApi.annotations({
identifier: "v2.session.active",
identifier: "session.active",
summary: "List active sessions",
description:
"Retrieve foreground Session drains currently owned by this OpenCode process. Sessions absent from the result are inactive.",
@@ -163,7 +163,7 @@ export const makeSessionGroup = <I extends HttpApiMiddleware.AnyId, S>(sessionLo
.middleware(sessionLocationMiddleware)
.annotateMerge(
OpenApi.annotations({
identifier: "v2.session.get",
identifier: "session.get",
summary: "Get session",
description: "Retrieve a session by ID.",
}),
@@ -179,7 +179,7 @@ export const makeSessionGroup = <I extends HttpApiMiddleware.AnyId, S>(sessionLo
.middleware(sessionLocationMiddleware)
.annotateMerge(
OpenApi.annotations({
identifier: "v2.session.switchAgent",
identifier: "session.switchAgent",
summary: "Switch session agent",
description: "Switch the agent used by subsequent provider turns.",
}),
@@ -195,7 +195,7 @@ export const makeSessionGroup = <I extends HttpApiMiddleware.AnyId, S>(sessionLo
.middleware(sessionLocationMiddleware)
.annotateMerge(
OpenApi.annotations({
identifier: "v2.session.switchModel",
identifier: "session.switchModel",
summary: "Switch session model",
description: "Switch the model used by subsequent provider turns.",
}),
@@ -216,7 +216,7 @@ export const makeSessionGroup = <I extends HttpApiMiddleware.AnyId, S>(sessionLo
.middleware(sessionLocationMiddleware)
.annotateMerge(
OpenApi.annotations({
identifier: "v2.session.prompt",
identifier: "session.prompt",
summary: "Send message",
description: "Durably admit one session input and schedule agent-loop execution unless resume is false.",
}),
@@ -231,7 +231,7 @@ export const makeSessionGroup = <I extends HttpApiMiddleware.AnyId, S>(sessionLo
.middleware(sessionLocationMiddleware)
.annotateMerge(
OpenApi.annotations({
identifier: "v2.session.compact",
identifier: "session.compact",
summary: "Compact session",
description: "Compact a session conversation.",
}),
@@ -246,7 +246,7 @@ export const makeSessionGroup = <I extends HttpApiMiddleware.AnyId, S>(sessionLo
.middleware(sessionLocationMiddleware)
.annotateMerge(
OpenApi.annotations({
identifier: "v2.session.wait",
identifier: "session.wait",
summary: "Wait for session",
description: "Wait for a session agent loop to become idle.",
}),
@@ -262,7 +262,7 @@ export const makeSessionGroup = <I extends HttpApiMiddleware.AnyId, S>(sessionLo
.middleware(sessionLocationMiddleware)
.annotateMerge(
OpenApi.annotations({
identifier: "v2.session.revert.stage",
identifier: "session.revert.stage",
summary: "Stage session revert",
description: "Stage or move a reversible session boundary and optionally apply its file changes.",
}),
@@ -275,7 +275,7 @@ export const makeSessionGroup = <I extends HttpApiMiddleware.AnyId, S>(sessionLo
error: [SessionNotFoundError, UnknownError],
})
.middleware(sessionLocationMiddleware)
.annotateMerge(OpenApi.annotations({ identifier: "v2.session.revert.clear", summary: "Clear staged revert" })),
.annotateMerge(OpenApi.annotations({ identifier: "session.revert.clear", summary: "Clear staged revert" })),
)
.add(
HttpApiEndpoint.post("session.revert.commit", "/api/session/:sessionID/revert/commit", {
@@ -285,7 +285,7 @@ export const makeSessionGroup = <I extends HttpApiMiddleware.AnyId, S>(sessionLo
})
.middleware(sessionLocationMiddleware)
.annotateMerge(
OpenApi.annotations({ identifier: "v2.session.revert.commit", summary: "Commit staged revert" }),
OpenApi.annotations({ identifier: "session.revert.commit", summary: "Commit staged revert" }),
),
)
.add(
@@ -297,7 +297,7 @@ export const makeSessionGroup = <I extends HttpApiMiddleware.AnyId, S>(sessionLo
.middleware(sessionLocationMiddleware)
.annotateMerge(
OpenApi.annotations({
identifier: "v2.session.context",
identifier: "session.context",
summary: "Get session context",
description: "Retrieve the active context messages for a session (all messages after the last compaction).",
}),
@@ -316,7 +316,7 @@ export const makeSessionGroup = <I extends HttpApiMiddleware.AnyId, S>(sessionLo
.middleware(sessionLocationMiddleware)
.annotateMerge(
OpenApi.annotations({
identifier: "v2.session.history",
identifier: "session.history",
summary: "Get session history",
description:
"Read one finite page of public durable Session events after an exclusive aggregate sequence. Newly committed events may appear on later pages.",
@@ -335,7 +335,7 @@ export const makeSessionGroup = <I extends HttpApiMiddleware.AnyId, S>(sessionLo
.middleware(sessionLocationMiddleware)
.annotateMerge(
OpenApi.annotations({
identifier: "v2.session.events",
identifier: "session.events",
summary: "Subscribe to session events",
description: "Replay durable events after an aggregate sequence, then continue with new durable events.",
}),
@@ -350,7 +350,7 @@ export const makeSessionGroup = <I extends HttpApiMiddleware.AnyId, S>(sessionLo
.middleware(sessionLocationMiddleware)
.annotateMerge(
OpenApi.annotations({
identifier: "v2.session.interrupt",
identifier: "session.interrupt",
summary: "Interrupt session execution",
description: "Interrupt active execution owned by this OpenCode process. Idle interruption is a no-op.",
}),
@@ -365,7 +365,7 @@ export const makeSessionGroup = <I extends HttpApiMiddleware.AnyId, S>(sessionLo
.middleware(sessionLocationMiddleware)
.annotateMerge(
OpenApi.annotations({
identifier: "v2.session.message",
identifier: "session.message",
summary: "Get session message",
description: "Retrieve one projected message owned by the Session.",
}),
+1 -1
View File
@@ -13,7 +13,7 @@ export const SkillGroup = HttpApiGroup.make("server.skill")
.annotateMerge(locationQueryOpenApi)
.annotateMerge(
OpenApi.annotations({
identifier: "v2.skill.list",
identifier: "skill.list",
summary: "List skills",
description: "Retrieve currently registered skills.",
}),