Compare commits

...
Author SHA1 Message Date
kommander 9f4e8b97a8 feat(sdk): expose session resume through API 2026-10-01 12:43:14 +00:00
13 changed files with 1585 additions and 49 deletions

No files matched your search

+5
View File
@@ -329,6 +329,10 @@ export type SessionCompactInput = {
export type SessionCompactOutput = SessionInbox.Compaction
export type SessionCompactOperation<E = never> = (input: SessionCompactInput) => Effect.Effect<SessionCompactOutput, E>
export type SessionResumeInput = { readonly sessionID: Session.ID }
export type SessionResumeOutput = void
export type SessionResumeOperation<E = never> = (input: SessionResumeInput) => Effect.Effect<SessionResumeOutput, E>
export type SessionWaitInput = { readonly sessionID: Session.ID }
export type SessionWaitOutput = void
export type SessionWaitOperation<E = never> = (input: SessionWaitInput) => Effect.Effect<SessionWaitOutput, E>
@@ -1458,6 +1462,7 @@ export interface SessionApi<E = never> {
readonly synthetic: SessionSyntheticOperation<E>
readonly shell: SessionShellOperation<E>
readonly compact: SessionCompactOperation<E>
readonly resume: SessionResumeOperation<E>
readonly wait: SessionWaitOperation<E>
readonly revert: {
readonly stage: SessionRevertStageOperation<E>
@@ -59,6 +59,8 @@ import type {
SessionShellOutput,
SessionCompactInput,
SessionCompactOutput,
SessionResumeInput,
SessionResumeOutput,
SessionWaitInput,
SessionWaitOutput,
SessionRevertStageInput,
@@ -564,6 +566,11 @@ const EndpointSessionCompact = (raw: RawClient["server.session"]) => (input: Ses
),
)
const EndpointSessionResume = (raw: RawClient["server.session"]) => (input: SessionResumeInput) =>
preserveEffect<SessionResumeOutput>()(
raw["session.resume"]({ params: { sessionID: input["sessionID"] } }).pipe(Effect.mapError(mapClientError)),
)
const EndpointSessionWait = (raw: RawClient["server.session"]) => (input: SessionWaitInput) =>
preserveEffect<SessionWaitOutput>()(
raw["session.wait"]({ params: { sessionID: input["sessionID"] } }).pipe(Effect.mapError(mapClientError)),
@@ -777,6 +784,7 @@ const adaptGroupSession = (raw: RawClient["server.session"]) => ({
synthetic: EndpointSessionSynthetic(raw),
shell: EndpointSessionShell(raw),
compact: EndpointSessionCompact(raw),
resume: EndpointSessionResume(raw),
wait: EndpointSessionWait(raw),
revert: {
stage: EndpointSessionRevertStage(raw),
@@ -53,6 +53,8 @@ import type {
SessionShellOutput,
SessionCompactInput,
SessionCompactOutput,
SessionResumeInput,
SessionResumeOutput,
SessionWaitInput,
SessionWaitOutput,
SessionRevertStageInput,
@@ -803,6 +805,17 @@ export function make(options: ClientOptions) {
},
requestOptions,
).then((value) => value.data),
resume: (input: SessionResumeInput, requestOptions?: RequestOptions) =>
request<SessionResumeOutput>(
{
method: "POST",
path: `/api/session/${encodeURIComponent(input.sessionID)}/resume`,
successStatus: 204,
declaredStatuses: [400, 401, 404, 503],
empty: true,
},
requestOptions,
),
wait: (input: SessionWaitInput, requestOptions?: RequestOptions) =>
request<SessionWaitOutput>(
{
@@ -4494,6 +4494,10 @@ export type SessionCompactInput = {
export type SessionCompactOutput = { data: SessionInboxCompaction }["data"]
export type SessionResumeInput = { readonly sessionID: { readonly sessionID: string }["sessionID"] }
export type SessionResumeOutput = void
export type SessionWaitInput = { readonly sessionID: { readonly sessionID: string }["sessionID"] }
export type SessionWaitOutput = void
+498 -16
View File
@@ -48,6 +48,106 @@
"summary": "Get server info"
}
},
"/api/pair": {
"post": {
"tags": ["server"],
"operationId": "server.pair",
"parameters": [],
"security": [],
"responses": {
"200": {
"description": "PairingCode",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/PairingCode"
}
}
}
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestErrorEncoded"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedErrorEncoded"
}
}
}
}
},
"description": "Create a short-lived, single-use code for a /auth/connect/:code pairing link.",
"summary": "Create pairing code"
}
},
"/auth/connect/{code}": {
"get": {
"tags": ["server"],
"operationId": "server.connect",
"parameters": [
{
"name": "code",
"in": "path",
"schema": {
"type": "string"
},
"required": true
}
],
"security": [],
"responses": {
"200": {
"description": "PairingSession",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/PairingSession"
}
}
}
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestErrorEncoded"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"anyOf": [
{
"$ref": "#/components/schemas/UnauthorizedErrorEncoded"
},
{
"$ref": "#/components/schemas/UnauthorizedErrorEncoded"
}
]
}
}
}
}
},
"description": "Redeem a pairing code. Browsers receive a session cookie and a redirect to the web app; requests that accept JSON receive a session token to use as the password.",
"summary": "Redeem pairing code"
}
},
"/api/location": {
"get": {
"tags": ["location"],
@@ -886,9 +986,19 @@
}
}
}
},
"404": {
"description": "SessionNotFoundError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/SessionNotFoundErrorEncoded"
}
}
}
}
},
"description": "Create a session at the requested location.",
"description": "Create a session at the requested location. A parentID creates a linked child session at its parent's location.",
"summary": "Create session",
"requestBody": {
"content": {
@@ -907,6 +1017,17 @@
}
]
},
"parentID": {
"anyOf": [
{
"type": "string",
"pattern": "^ses"
},
{
"type": "null"
}
]
},
"title": {
"anyOf": [
{
@@ -2676,6 +2797,78 @@
}
}
},
"/api/session/{sessionID}/resume": {
"post": {
"tags": ["session"],
"operationId": "session.resume",
"parameters": [
{
"name": "sessionID",
"in": "path",
"schema": {
"type": "string",
"pattern": "^ses"
},
"required": true
}
],
"security": [],
"responses": {
"204": {
"description": "<No Content>"
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestErrorEncoded"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedErrorEncoded"
}
}
}
},
"404": {
"description": "SessionNotFoundError",
"content": {
"application/json": {
"schema": {
"anyOf": [
{
"$ref": "#/components/schemas/SessionNotFoundErrorEncoded"
},
{
"$ref": "#/components/schemas/SessionNotFoundErrorEncoded"
}
]
}
}
}
},
"503": {
"description": "ServiceUnavailableError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ServiceUnavailableErrorEncoded"
}
}
}
}
},
"description": "Start or join session execution without admitting a new input. Waits for the execution to settle.",
"summary": "Resume session"
}
},
"/api/experimental/session/{sessionID}/wait": {
"post": {
"tags": ["session"],
@@ -4508,6 +4701,21 @@
"pattern": "^frm_"
},
"required": true
},
{
"name": "message",
"in": "query",
"schema": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"required": false
}
],
"security": [],
@@ -4591,21 +4799,6 @@
"pattern": "^frm_"
},
"required": true
},
{
"name": "message",
"in": "query",
"schema": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"required": false
}
],
"security": [],
@@ -7197,6 +7390,125 @@
"summary": "List MCP resources"
}
},
"/api/credential": {
"get": {
"tags": ["credential"],
"operationId": "credential.list",
"parameters": [],
"security": [],
"responses": {
"200": {
"description": "Success",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"data": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Credential.Entry"
}
}
},
"required": ["data"],
"additionalProperties": false
}
}
}
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestErrorEncoded"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedErrorEncoded"
}
}
}
}
},
"description": "List every stored integration credential, including its secret value.",
"summary": "List credentials"
},
"post": {
"tags": ["credential"],
"operationId": "credential.create",
"parameters": [],
"security": [],
"responses": {
"200": {
"description": "Success",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"data": {
"$ref": "#/components/schemas/Credential.Entry"
}
},
"required": ["data"],
"additionalProperties": false
}
}
}
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestErrorEncoded"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedErrorEncoded"
}
}
}
},
"409": {
"description": "ConflictError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ConflictErrorEncoded"
}
}
}
}
},
"description": "Store an integration credential. It becomes the integration's active credential unless activate is false and the integration already has one. Fails with a conflict when the requested ID already exists.",
"summary": "Create credential",
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Credential.CreateInput"
}
}
},
"required": true
}
}
},
"/api/credential/{credentialID}": {
"patch": {
"tags": ["credential"],
@@ -13631,6 +13943,9 @@
"method": {
"type": "string",
"enum": ["key", "oauth"]
},
"status": {
"$ref": "#/components/schemas/Connection.Status"
}
},
"required": ["type", "id", "label", "method"],
@@ -13645,6 +13960,9 @@
},
"name": {
"type": "string"
},
"status": {
"$ref": "#/components/schemas/Connection.Status"
}
},
"required": ["type", "name"],
@@ -13660,6 +13978,128 @@
}
]
},
"Connection.Status": {
"type": "object",
"properties": {
"status": {
"anyOf": [
{
"type": "string",
"enum": ["needs_auth"]
}
]
},
"message": {
"type": "string"
},
"url": {
"type": "string"
}
},
"required": ["status", "message"],
"additionalProperties": false
},
"Credential.CreateInput": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"integrationID": {
"type": "string"
},
"label": {
"type": "string"
},
"value": {
"$ref": "#/components/schemas/Credential.Value"
},
"activate": {
"type": "boolean"
}
},
"required": ["integrationID", "value"],
"additionalProperties": false
},
"Credential.Entry": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"integrationID": {
"type": "string"
},
"label": {
"type": "string"
},
"active": {
"type": "boolean"
},
"value": {
"$ref": "#/components/schemas/Credential.Value"
}
},
"required": ["id", "integrationID", "label", "active", "value"],
"additionalProperties": false
},
"Credential.Key": {
"type": "object",
"properties": {
"type": {
"type": "string",
"enum": ["key"]
},
"key": {
"type": "string"
},
"metadata": {
"type": "object"
},
"configuration": {
"$ref": "#/components/schemas/Form.Answer_1"
}
},
"required": ["type", "key"],
"additionalProperties": false
},
"Credential.OAuth": {
"type": "object",
"properties": {
"type": {
"type": "string",
"enum": ["oauth"]
},
"methodID": {
"type": "string"
},
"refresh": {
"type": "string"
},
"access": {
"type": "string"
},
"expires": {
"type": "integer",
"minimum": 0
},
"metadata": {
"type": "object"
}
},
"required": ["type", "methodID", "refresh", "access", "expires"],
"additionalProperties": false
},
"Credential.Value": {
"anyOf": [
{
"$ref": "#/components/schemas/Credential.OAuth"
},
{
"$ref": "#/components/schemas/Credential.Key"
}
]
},
"FileDiff.Info": {
"type": "object",
"properties": {
@@ -13746,6 +14186,12 @@
"$ref": "#/components/schemas/Form.Value"
}
},
"Form.Answer_1": {
"type": "object",
"additionalProperties": {
"$ref": "#/components/schemas/Form.Value"
}
},
"Form.BooleanField": {
"type": "object",
"properties": {
@@ -15709,6 +16155,29 @@
"Money.USDPerMillionTokens": {
"type": "number"
},
"PairingCode": {
"type": "object",
"properties": {
"code": {
"type": "string"
},
"expires_in": {
"type": "integer"
}
},
"required": ["code", "expires_in"],
"additionalProperties": false
},
"PairingSession": {
"type": "object",
"properties": {
"token": {
"type": "string"
}
},
"required": ["token"],
"additionalProperties": false
},
"Permission.Effect": {
"type": "string",
"enum": ["allow", "deny", "ask"]
@@ -18234,6 +18703,16 @@
"type": "integer",
"minimum": 100,
"maximum": 599
},
"response": {
"type": "object",
"properties": {
"body": {
"type": "string"
}
},
"required": ["body"],
"additionalProperties": false
}
},
"required": ["type", "message"],
@@ -18653,6 +19132,9 @@
"exit": {
"type": "number"
},
"signal": {
"type": "string"
},
"metadata": {
"type": "object"
},
+16
View File
@@ -515,6 +515,22 @@ export const makeSessionGroup = <
}),
),
)
.add(
HttpApiEndpoint.post("session.resume", "/api/session/:sessionID/resume", {
params: { sessionID: Session.ID },
success: HttpApiSchema.NoContent,
error: [SessionNotFoundError, ServiceUnavailableError],
})
.middleware(sessionLocationMiddleware)
.annotateMerge(
OpenApi.annotations({
identifier: "session.resume",
summary: "Resume session",
description:
"Start or join session execution without admitting a new input. Waits for the execution to settle.",
}),
),
)
.add(
HttpApiEndpoint.post("session.wait", "/api/experimental/session/:sessionID/wait", {
params: { sessionID: Session.ID },
+4
View File
@@ -27,6 +27,10 @@ test("Promise host uses the embedded router", async () => {
const missingSessionID = Session.ID.create()
const missing = await opencode.sessions.get({ sessionID: missingSessionID }).catch((error: unknown) => error)
expect(missing).toMatchObject({ _tag: "SessionNotFoundError", sessionID: missingSessionID })
const missingResume = await opencode.sessions
.resume({ sessionID: missingSessionID })
.catch((error: unknown) => error)
expect(missingResume).toMatchObject({ _tag: "SessionNotFoundError", sessionID: missingSessionID })
} finally {
await opencode.close()
await opencode.close()
+15
View File
@@ -448,6 +448,21 @@ export const SessionHandler = HttpApiBuilder.group(Api, "server.session", (handl
}
}),
)
.handle(
"session.resume",
Effect.fn(function* (ctx) {
yield* session
.resume(ctx.params.sessionID)
.pipe(
Effect.mapError((error) =>
error._tag === "Session.NotFoundError"
? missingSession(error)
: new ServiceUnavailableError({ message: error.message, service: "session execution" }),
),
)
return HttpApiSchema.NoContent.make()
}),
)
.handle(
"session.wait",
Effect.fn(function* (ctx) {
@@ -210,9 +210,11 @@ it.live(
for (const config of configs) {
yield* llm.push(TestLLM.tool(`call_${config.tool}`, config.tool, {}), TestLLM.text(config.id, config.tool))
yield* sessions.resume(config.id)
const response = yield* request(`/api/session/${config.id}/resume`, {})
expect(response.status).toBe(204)
}
expect(executed).toEqual([first.id, second.id])
expect((yield* request(`/api/session/${Session.ID.create()}/resume`, {})).status).toBe(404)
for (const config of configs) {
yield* llm.push(TestLLM.text(`generated ${config.id}`, config.tool))
+498 -16
View File
@@ -48,6 +48,106 @@
"summary": "Get server info"
}
},
"/api/pair": {
"post": {
"tags": ["server"],
"operationId": "server.pair",
"parameters": [],
"security": [],
"responses": {
"200": {
"description": "PairingCode",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/PairingCode"
}
}
}
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestErrorEncoded"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedErrorEncoded"
}
}
}
}
},
"description": "Create a short-lived, single-use code for a /auth/connect/:code pairing link.",
"summary": "Create pairing code"
}
},
"/auth/connect/{code}": {
"get": {
"tags": ["server"],
"operationId": "server.connect",
"parameters": [
{
"name": "code",
"in": "path",
"schema": {
"type": "string"
},
"required": true
}
],
"security": [],
"responses": {
"200": {
"description": "PairingSession",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/PairingSession"
}
}
}
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestErrorEncoded"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"anyOf": [
{
"$ref": "#/components/schemas/UnauthorizedErrorEncoded"
},
{
"$ref": "#/components/schemas/UnauthorizedErrorEncoded"
}
]
}
}
}
}
},
"description": "Redeem a pairing code. Browsers receive a session cookie and a redirect to the web app; requests that accept JSON receive a session token to use as the password.",
"summary": "Redeem pairing code"
}
},
"/api/location": {
"get": {
"tags": ["location"],
@@ -886,9 +986,19 @@
}
}
}
},
"404": {
"description": "SessionNotFoundError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/SessionNotFoundErrorEncoded"
}
}
}
}
},
"description": "Create a session at the requested location.",
"description": "Create a session at the requested location. A parentID creates a linked child session at its parent's location.",
"summary": "Create session",
"requestBody": {
"content": {
@@ -907,6 +1017,17 @@
}
]
},
"parentID": {
"anyOf": [
{
"type": "string",
"pattern": "^ses"
},
{
"type": "null"
}
]
},
"title": {
"anyOf": [
{
@@ -2676,6 +2797,78 @@
}
}
},
"/api/session/{sessionID}/resume": {
"post": {
"tags": ["session"],
"operationId": "session.resume",
"parameters": [
{
"name": "sessionID",
"in": "path",
"schema": {
"type": "string",
"pattern": "^ses"
},
"required": true
}
],
"security": [],
"responses": {
"204": {
"description": "<No Content>"
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestErrorEncoded"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedErrorEncoded"
}
}
}
},
"404": {
"description": "SessionNotFoundError",
"content": {
"application/json": {
"schema": {
"anyOf": [
{
"$ref": "#/components/schemas/SessionNotFoundErrorEncoded"
},
{
"$ref": "#/components/schemas/SessionNotFoundErrorEncoded"
}
]
}
}
}
},
"503": {
"description": "ServiceUnavailableError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ServiceUnavailableErrorEncoded"
}
}
}
}
},
"description": "Start or join session execution without admitting a new input. Waits for the execution to settle.",
"summary": "Resume session"
}
},
"/api/experimental/session/{sessionID}/wait": {
"post": {
"tags": ["session"],
@@ -4508,6 +4701,21 @@
"pattern": "^frm_"
},
"required": true
},
{
"name": "message",
"in": "query",
"schema": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"required": false
}
],
"security": [],
@@ -4591,21 +4799,6 @@
"pattern": "^frm_"
},
"required": true
},
{
"name": "message",
"in": "query",
"schema": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"required": false
}
],
"security": [],
@@ -7197,6 +7390,125 @@
"summary": "List MCP resources"
}
},
"/api/credential": {
"get": {
"tags": ["credential"],
"operationId": "credential.list",
"parameters": [],
"security": [],
"responses": {
"200": {
"description": "Success",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"data": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Credential.Entry"
}
}
},
"required": ["data"],
"additionalProperties": false
}
}
}
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestErrorEncoded"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedErrorEncoded"
}
}
}
}
},
"description": "List every stored integration credential, including its secret value.",
"summary": "List credentials"
},
"post": {
"tags": ["credential"],
"operationId": "credential.create",
"parameters": [],
"security": [],
"responses": {
"200": {
"description": "Success",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"data": {
"$ref": "#/components/schemas/Credential.Entry"
}
},
"required": ["data"],
"additionalProperties": false
}
}
}
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestErrorEncoded"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedErrorEncoded"
}
}
}
},
"409": {
"description": "ConflictError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ConflictErrorEncoded"
}
}
}
}
},
"description": "Store an integration credential. It becomes the integration's active credential unless activate is false and the integration already has one. Fails with a conflict when the requested ID already exists.",
"summary": "Create credential",
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Credential.CreateInput"
}
}
},
"required": true
}
}
},
"/api/credential/{credentialID}": {
"patch": {
"tags": ["credential"],
@@ -13631,6 +13943,9 @@
"method": {
"type": "string",
"enum": ["key", "oauth"]
},
"status": {
"$ref": "#/components/schemas/Connection.Status"
}
},
"required": ["type", "id", "label", "method"],
@@ -13645,6 +13960,9 @@
},
"name": {
"type": "string"
},
"status": {
"$ref": "#/components/schemas/Connection.Status"
}
},
"required": ["type", "name"],
@@ -13660,6 +13978,128 @@
}
]
},
"Connection.Status": {
"type": "object",
"properties": {
"status": {
"anyOf": [
{
"type": "string",
"enum": ["needs_auth"]
}
]
},
"message": {
"type": "string"
},
"url": {
"type": "string"
}
},
"required": ["status", "message"],
"additionalProperties": false
},
"Credential.CreateInput": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"integrationID": {
"type": "string"
},
"label": {
"type": "string"
},
"value": {
"$ref": "#/components/schemas/Credential.Value"
},
"activate": {
"type": "boolean"
}
},
"required": ["integrationID", "value"],
"additionalProperties": false
},
"Credential.Entry": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"integrationID": {
"type": "string"
},
"label": {
"type": "string"
},
"active": {
"type": "boolean"
},
"value": {
"$ref": "#/components/schemas/Credential.Value"
}
},
"required": ["id", "integrationID", "label", "active", "value"],
"additionalProperties": false
},
"Credential.Key": {
"type": "object",
"properties": {
"type": {
"type": "string",
"enum": ["key"]
},
"key": {
"type": "string"
},
"metadata": {
"type": "object"
},
"configuration": {
"$ref": "#/components/schemas/Form.Answer_1"
}
},
"required": ["type", "key"],
"additionalProperties": false
},
"Credential.OAuth": {
"type": "object",
"properties": {
"type": {
"type": "string",
"enum": ["oauth"]
},
"methodID": {
"type": "string"
},
"refresh": {
"type": "string"
},
"access": {
"type": "string"
},
"expires": {
"type": "integer",
"minimum": 0
},
"metadata": {
"type": "object"
}
},
"required": ["type", "methodID", "refresh", "access", "expires"],
"additionalProperties": false
},
"Credential.Value": {
"anyOf": [
{
"$ref": "#/components/schemas/Credential.OAuth"
},
{
"$ref": "#/components/schemas/Credential.Key"
}
]
},
"FileDiff.Info": {
"type": "object",
"properties": {
@@ -13746,6 +14186,12 @@
"$ref": "#/components/schemas/Form.Value"
}
},
"Form.Answer_1": {
"type": "object",
"additionalProperties": {
"$ref": "#/components/schemas/Form.Value"
}
},
"Form.BooleanField": {
"type": "object",
"properties": {
@@ -15709,6 +16155,29 @@
"Money.USDPerMillionTokens": {
"type": "number"
},
"PairingCode": {
"type": "object",
"properties": {
"code": {
"type": "string"
},
"expires_in": {
"type": "integer"
}
},
"required": ["code", "expires_in"],
"additionalProperties": false
},
"PairingSession": {
"type": "object",
"properties": {
"token": {
"type": "string"
}
},
"required": ["token"],
"additionalProperties": false
},
"Permission.Effect": {
"type": "string",
"enum": ["allow", "deny", "ask"]
@@ -18234,6 +18703,16 @@
"type": "integer",
"minimum": 100,
"maximum": 599
},
"response": {
"type": "object",
"properties": {
"body": {
"type": "string"
}
},
"required": ["body"],
"additionalProperties": false
}
},
"required": ["type", "message"],
@@ -18653,6 +19132,9 @@
"exit": {
"type": "number"
},
"signal": {
"type": "string"
},
"metadata": {
"type": "object"
},
+498 -16
View File
@@ -48,6 +48,106 @@
"summary": "Get server info"
}
},
"/api/pair": {
"post": {
"tags": ["server"],
"operationId": "server.pair",
"parameters": [],
"security": [],
"responses": {
"200": {
"description": "PairingCode",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/PairingCode"
}
}
}
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestErrorEncoded"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedErrorEncoded"
}
}
}
}
},
"description": "Create a short-lived, single-use code for a /auth/connect/:code pairing link.",
"summary": "Create pairing code"
}
},
"/auth/connect/{code}": {
"get": {
"tags": ["server"],
"operationId": "server.connect",
"parameters": [
{
"name": "code",
"in": "path",
"schema": {
"type": "string"
},
"required": true
}
],
"security": [],
"responses": {
"200": {
"description": "PairingSession",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/PairingSession"
}
}
}
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestErrorEncoded"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"anyOf": [
{
"$ref": "#/components/schemas/UnauthorizedErrorEncoded"
},
{
"$ref": "#/components/schemas/UnauthorizedErrorEncoded"
}
]
}
}
}
}
},
"description": "Redeem a pairing code. Browsers receive a session cookie and a redirect to the web app; requests that accept JSON receive a session token to use as the password.",
"summary": "Redeem pairing code"
}
},
"/api/location": {
"get": {
"tags": ["location"],
@@ -886,9 +986,19 @@
}
}
}
},
"404": {
"description": "SessionNotFoundError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/SessionNotFoundErrorEncoded"
}
}
}
}
},
"description": "Create a session at the requested location.",
"description": "Create a session at the requested location. A parentID creates a linked child session at its parent's location.",
"summary": "Create session",
"requestBody": {
"content": {
@@ -907,6 +1017,17 @@
}
]
},
"parentID": {
"anyOf": [
{
"type": "string",
"pattern": "^ses"
},
{
"type": "null"
}
]
},
"title": {
"anyOf": [
{
@@ -2676,6 +2797,78 @@
}
}
},
"/api/session/{sessionID}/resume": {
"post": {
"tags": ["session"],
"operationId": "session.resume",
"parameters": [
{
"name": "sessionID",
"in": "path",
"schema": {
"type": "string",
"pattern": "^ses"
},
"required": true
}
],
"security": [],
"responses": {
"204": {
"description": "<No Content>"
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestErrorEncoded"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedErrorEncoded"
}
}
}
},
"404": {
"description": "SessionNotFoundError",
"content": {
"application/json": {
"schema": {
"anyOf": [
{
"$ref": "#/components/schemas/SessionNotFoundErrorEncoded"
},
{
"$ref": "#/components/schemas/SessionNotFoundErrorEncoded"
}
]
}
}
}
},
"503": {
"description": "ServiceUnavailableError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ServiceUnavailableErrorEncoded"
}
}
}
}
},
"description": "Start or join session execution without admitting a new input. Waits for the execution to settle.",
"summary": "Resume session"
}
},
"/api/experimental/session/{sessionID}/wait": {
"post": {
"tags": ["session"],
@@ -4508,6 +4701,21 @@
"pattern": "^frm_"
},
"required": true
},
{
"name": "message",
"in": "query",
"schema": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"required": false
}
],
"security": [],
@@ -4591,21 +4799,6 @@
"pattern": "^frm_"
},
"required": true
},
{
"name": "message",
"in": "query",
"schema": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"required": false
}
],
"security": [],
@@ -7197,6 +7390,125 @@
"summary": "List MCP resources"
}
},
"/api/credential": {
"get": {
"tags": ["credential"],
"operationId": "credential.list",
"parameters": [],
"security": [],
"responses": {
"200": {
"description": "Success",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"data": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Credential.Entry"
}
}
},
"required": ["data"],
"additionalProperties": false
}
}
}
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestErrorEncoded"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedErrorEncoded"
}
}
}
}
},
"description": "List every stored integration credential, including its secret value.",
"summary": "List credentials"
},
"post": {
"tags": ["credential"],
"operationId": "credential.create",
"parameters": [],
"security": [],
"responses": {
"200": {
"description": "Success",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"data": {
"$ref": "#/components/schemas/Credential.Entry"
}
},
"required": ["data"],
"additionalProperties": false
}
}
}
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestErrorEncoded"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedErrorEncoded"
}
}
}
},
"409": {
"description": "ConflictError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ConflictErrorEncoded"
}
}
}
}
},
"description": "Store an integration credential. It becomes the integration's active credential unless activate is false and the integration already has one. Fails with a conflict when the requested ID already exists.",
"summary": "Create credential",
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Credential.CreateInput"
}
}
},
"required": true
}
}
},
"/api/credential/{credentialID}": {
"patch": {
"tags": ["credential"],
@@ -13631,6 +13943,9 @@
"method": {
"type": "string",
"enum": ["key", "oauth"]
},
"status": {
"$ref": "#/components/schemas/Connection.Status"
}
},
"required": ["type", "id", "label", "method"],
@@ -13645,6 +13960,9 @@
},
"name": {
"type": "string"
},
"status": {
"$ref": "#/components/schemas/Connection.Status"
}
},
"required": ["type", "name"],
@@ -13660,6 +13978,128 @@
}
]
},
"Connection.Status": {
"type": "object",
"properties": {
"status": {
"anyOf": [
{
"type": "string",
"enum": ["needs_auth"]
}
]
},
"message": {
"type": "string"
},
"url": {
"type": "string"
}
},
"required": ["status", "message"],
"additionalProperties": false
},
"Credential.CreateInput": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"integrationID": {
"type": "string"
},
"label": {
"type": "string"
},
"value": {
"$ref": "#/components/schemas/Credential.Value"
},
"activate": {
"type": "boolean"
}
},
"required": ["integrationID", "value"],
"additionalProperties": false
},
"Credential.Entry": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"integrationID": {
"type": "string"
},
"label": {
"type": "string"
},
"active": {
"type": "boolean"
},
"value": {
"$ref": "#/components/schemas/Credential.Value"
}
},
"required": ["id", "integrationID", "label", "active", "value"],
"additionalProperties": false
},
"Credential.Key": {
"type": "object",
"properties": {
"type": {
"type": "string",
"enum": ["key"]
},
"key": {
"type": "string"
},
"metadata": {
"type": "object"
},
"configuration": {
"$ref": "#/components/schemas/Form.Answer_1"
}
},
"required": ["type", "key"],
"additionalProperties": false
},
"Credential.OAuth": {
"type": "object",
"properties": {
"type": {
"type": "string",
"enum": ["oauth"]
},
"methodID": {
"type": "string"
},
"refresh": {
"type": "string"
},
"access": {
"type": "string"
},
"expires": {
"type": "integer",
"minimum": 0
},
"metadata": {
"type": "object"
}
},
"required": ["type", "methodID", "refresh", "access", "expires"],
"additionalProperties": false
},
"Credential.Value": {
"anyOf": [
{
"$ref": "#/components/schemas/Credential.OAuth"
},
{
"$ref": "#/components/schemas/Credential.Key"
}
]
},
"FileDiff.Info": {
"type": "object",
"properties": {
@@ -13746,6 +14186,12 @@
"$ref": "#/components/schemas/Form.Value"
}
},
"Form.Answer_1": {
"type": "object",
"additionalProperties": {
"$ref": "#/components/schemas/Form.Value"
}
},
"Form.BooleanField": {
"type": "object",
"properties": {
@@ -15709,6 +16155,29 @@
"Money.USDPerMillionTokens": {
"type": "number"
},
"PairingCode": {
"type": "object",
"properties": {
"code": {
"type": "string"
},
"expires_in": {
"type": "integer"
}
},
"required": ["code", "expires_in"],
"additionalProperties": false
},
"PairingSession": {
"type": "object",
"properties": {
"token": {
"type": "string"
}
},
"required": ["token"],
"additionalProperties": false
},
"Permission.Effect": {
"type": "string",
"enum": ["allow", "deny", "ask"]
@@ -18234,6 +18703,16 @@
"type": "integer",
"minimum": 100,
"maximum": 599
},
"response": {
"type": "object",
"properties": {
"body": {
"type": "string"
}
},
"required": ["body"],
"additionalProperties": false
}
},
"required": ["type", "message"],
@@ -18653,6 +19132,9 @@
"exit": {
"type": "number"
},
"signal": {
"type": "string"
},
"metadata": {
"type": "object"
},
@@ -42,6 +42,17 @@ const info = yield* opencode.server.info()
const sessions = yield* opencode.sessions.list()
```
## Resume admitted work
To run a previously admitted prompt without adding a new message, use `sessions.resume`:
```ts
yield* opencode.sessions.prompt({ sessionID: session.id, text: "Review the current changes", resume: false })
yield* opencode.sessions.resume({ sessionID: session.id })
```
It starts execution when idle or joins an active execution, and resolves when that execution settles.
## Stream events
Streaming endpoints return Effect `Stream` values. Fork consumers in the host Scope when they should run in the
@@ -42,6 +42,18 @@ options, and `AsyncIterable` streams as `@opencode/client`. It exposes the
full generated client and adds the convenience aliases `sessions` and `events`
for the session and event groups.
## Resume admitted work
Admit a prompt without starting execution, then resume that session without sending another message:
```ts
await opencode.sessions.prompt({ sessionID: session.id, text: "Review the current changes", resume: false })
await opencode.sessions.resume({ sessionID: session.id })
```
`resume` starts execution when idle or joins an active execution. It resolves when that execution settles; unlike
`sessions.wait`, it can start work.
## Worktrees
Worktree operations require a `projectID`. Create and refresh load configuration and plugins from the project's saved