diff --git a/package-lock.json b/package-lock.json index c1707d8e..967fc2e8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "2.2.2", "license": "Apache-2.0", "dependencies": { - "@agentclientprotocol/sdk": "~1.5.0", + "@agentclientprotocol/sdk": "~1.8.0", "@openai/codex": "^0.160.1", "cross-spawn": "^7.0.6", "diff": "^9.0.0", @@ -32,9 +32,9 @@ } }, "node_modules/@agentclientprotocol/sdk": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@agentclientprotocol/sdk/-/sdk-1.5.0.tgz", - "integrity": "sha512-524jwbB2iYWA+kWWyv9fhKbhU89dH/lu9u5EXwVNmfYzopV8BujCDxByBDZhxRUkB7RWIJzISCnwivdDk+bdVg==", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@agentclientprotocol/sdk/-/sdk-1.8.0.tgz", + "integrity": "sha512-fbP7qHZsFyTJVT0/RPrcWJ8An26qLXHEuGCHHEKDyxD1xpXV+Ejyh2LAE6vBAFePkpVjZeeohnbgvuEhZ7wLMg==", "license": "Apache-2.0", "peerDependencies": { "zod": "^3.25.0 || ^4.0.0" diff --git a/package.json b/package.json index 33c478e4..c09649fa 100644 --- a/package.json +++ b/package.json @@ -67,7 +67,7 @@ "vitest": "^5.0.1" }, "dependencies": { - "@agentclientprotocol/sdk": "~1.5.0", + "@agentclientprotocol/sdk": "~1.8.0", "@openai/codex": "^0.160.1", "cross-spawn": "^7.0.6", "diff": "^9.0.0", diff --git a/src/ACPSessionConnection.ts b/src/ACPSessionConnection.ts index ce4b24af..e37e38c0 100644 --- a/src/ACPSessionConnection.ts +++ b/src/ACPSessionConnection.ts @@ -84,10 +84,13 @@ export class AcpV2Connection { * is no v1 shape for them, so this bypasses `toV2SessionUpdates` entirely. */ async startReplayMessage(sessionId: string, kind: ReplayMessageKind, messageId: string): Promise { - await this.client.notify(acpV2.methods.client.session.update, { - sessionId, - update: {sessionUpdate: kind, messageId, content: []}, - }); + // One literal per kind: TypeScript does not match a union-typed tag against the v2 union. + const message = {messageId, content: []}; + const update: acpV2.SessionUpdate = + kind === "user_message" ? {sessionUpdate: kind, ...message} + : kind === "agent_message" ? {sessionUpdate: kind, ...message} + : {sessionUpdate: kind, ...message}; + await this.client.notify(acpV2.methods.client.session.update, {sessionId, update}); } /** `updateState`, with send failures caught and logged instead of propagated. */ diff --git a/src/AcpV2Prompt.ts b/src/AcpV2Prompt.ts index 72c94736..7f6249da 100644 --- a/src/AcpV2Prompt.ts +++ b/src/AcpV2Prompt.ts @@ -69,12 +69,14 @@ export function toV1PromptRequest(params: acpV2.PromptRequest): acp.PromptReques * negotiated it). */ export function toV2IdleState(response: acp.PromptResponse): acpV2.StateUpdate { + // Every v1 stop reason is a v2 one. The v2 type is a union per stop reason, which TypeScript + // does not match against a union-typed `stopReason`, hence the cast. return { state: "idle", stopReason: response.stopReason, ...(response.usage != null ? {usage: response.usage} : {}), ...(response._meta != null ? {_meta: response._meta} : {}), - }; + } as acpV2.StateUpdate; } /** diff --git a/src/AcpV2SessionUpdate.ts b/src/AcpV2SessionUpdate.ts index e01cdf13..f96de970 100644 --- a/src/AcpV2SessionUpdate.ts +++ b/src/AcpV2SessionUpdate.ts @@ -74,9 +74,9 @@ export function toV2SessionUpdate(update: AcpSessionUpdate): acpV2.SessionUpdate case "available_commands_update": return { ...update, - availableCommands: update.availableCommands.map((command) => ({ + availableCommands: update.availableCommands.map(({input, ...command}) => ({ ...command, - ...(command.input != null ? {input: {...command.input, type: "text" as const}} : {}), + ...(input !== undefined ? {input: input && {...input, type: "text" as const}} : {}), })), }; // v2 removed the modes API. codex-acp never emits this (mode changes go out as @@ -114,9 +114,9 @@ export function toV2SessionUpdate(update: AcpSessionUpdate): acpV2.SessionUpdate ...(content !== undefined ? {content: content && content.map(toV2ToolCallContent)} : {}), }; } - // Subagent RFD (spec PR #1992, unmerged) uses a single upsert-style update. Sent under a - // `_`-prefixed tag because the RFD's `subagent_update` isn't in the v2 unstable schema yet; - // rename to `subagent_update` once it lands there. The AIR `nativeSubagentSessions` gate + // The subagent RFD uses a single upsert-style update. Sent under a `_`-prefixed tag because + // this payload predates the `subagent_update` now in the v2 unstable schema and does not + // have its fields yet; rename once it does. The AIR `nativeSubagentSessions` gate // already ran upstream (whatever created this update), so no gate check is needed here. case "subagent_spawned": { const {subagentSessionId, name, task, _meta} = update; @@ -145,6 +145,9 @@ export function toV2SessionUpdate(update: AcpSessionUpdate): acpV2.SessionUpdate case "async_task_state_update": return {...update, sessionUpdate: "_async_task_state_update"}; // Declared as a type but never emitted; no v2 rendering to give it yet. + case "subagent_update": + case "session_message": + case "session_message_chunk": case "async_task_progress": throw acp.RequestError.internalError( undefined, diff --git a/src/CodexAcpServer.ts b/src/CodexAcpServer.ts index f6839c70..db960eda 100644 --- a/src/CodexAcpServer.ts +++ b/src/CodexAcpServer.ts @@ -1461,10 +1461,10 @@ export class CodexAcpServer { if (sessionState.codexReportedRunningTurnId === event.params.turn.id) { sessionState.codexReportedRunningTurnId = null; } - await this.reportUnownedTurnState(sessionState.sessionId, { - state: "idle", - stopReason: stopReasonForUnownedTurn(event.params.turn.status), - }); + await this.reportUnownedTurnState( + sessionState.sessionId, + toV2IdleState({stopReason: stopReasonForUnownedTurn(event.params.turn.status)}), + ); } /** diff --git a/src/CodexAuthMethod.ts b/src/CodexAuthMethod.ts index c7abc955..662c51d4 100644 --- a/src/CodexAuthMethod.ts +++ b/src/CodexAuthMethod.ts @@ -1,5 +1,5 @@ -import type {AuthenticateRequest, AuthMethod, ClientCapabilities} from "@agentclientprotocol/sdk"; +import type {AuthenticateRequest, AuthMethodAgent, ClientCapabilities} from "@agentclientprotocol/sdk"; import type * as acpV2 from "@agentclientprotocol/sdk/experimental/v2"; import {clientSupportsUrlElicitation} from "./ElicitationCapabilities"; @@ -15,7 +15,7 @@ export interface ApiKeyAuthRequest extends AuthenticateRequest { } | null; } -const ApiKeyAuthMethod: AuthMethod = { +const ApiKeyAuthMethod: AuthMethodAgent = { id: "api-key", name: "API Key", description: "Use an API key to authenticate", @@ -26,7 +26,7 @@ const ApiKeyAuthMethod: AuthMethod = { } } -const ChatGptAuthMethod: AuthMethod = { +const ChatGptAuthMethod: AuthMethodAgent = { id: "chat-gpt", name: "ChatGPT", description: "Use ChatGPT to authenticate" @@ -36,7 +36,7 @@ export interface ChatGPTAuthRequest extends AuthenticateRequest { methodId: "chat-gpt"; } -const ChatGptDeviceCodeAuthMethod: AuthMethod = { +const ChatGptDeviceCodeAuthMethod: AuthMethodAgent = { id: "chat-gpt-device-code", name: "ChatGPT (device code)", description: "Sign in to ChatGPT by opening a verification page and entering a one-time code" @@ -69,8 +69,8 @@ export interface GatewayAuthRequest extends AuthenticateRequest { }; } -export function getCodexAuthMethods(clientCapabilities?: ClientCapabilities | null, env: NodeJS.ProcessEnv = process.env): AuthMethod[] { - const authMethods: AuthMethod[] = [ApiKeyAuthMethod]; +export function getCodexAuthMethods(clientCapabilities?: ClientCapabilities | null, env: NodeJS.ProcessEnv = process.env): AuthMethodAgent[] { + const authMethods: AuthMethodAgent[] = [ApiKeyAuthMethod]; if (!env["NO_BROWSER"]) { authMethods.push(ChatGptAuthMethod); } diff --git a/src/__tests__/CodexACPAgent/data/available-commands-v2-rendered.json b/src/__tests__/CodexACPAgent/data/available-commands-v2-rendered.json index 02d360ce..c3caf2bd 100644 --- a/src/__tests__/CodexACPAgent/data/available-commands-v2-rendered.json +++ b/src/__tests__/CodexACPAgent/data/available-commands-v2-rendered.json @@ -4,12 +4,12 @@ { "name": "review", "description": "Review changes", + "_meta": { + "kind": "hint" + }, "input": { "hint": "focus", "type": "text" - }, - "_meta": { - "kind": "hint" } }, {