| ... | ... | @@ -7,6 +7,7 @@ Define a **breaking** canonical provider-runtime event model that can represent |
| 7 | 7 | - Claude Agent SDK types (`@anthropic-ai/claude-agent-sdk@0.2.62`) |
| 8 | 8 | - Codex App Server protocol (`schema/json/*`, including `ServerNotification`, `ServerRequest`, `EventMsg`, and v2 payload schemas) |
| 9 | 9 | - Codex TypeScript SDK thread events/items (`sdk/typescript/src/events.ts`, `items.ts`) |
| 10 | - Cursor ACP protocol (`agent acp` JSON-RPC 2.0 over stdio) |
| 10 | 11 | |
| 11 | 12 | This is a mapping/spec document only (no downstream compatibility constraints). |
| 12 | 13 | |
| ... | ... | @@ -24,7 +25,10 @@ type RuntimeEventRaw = { |
| 24 | 25 | | "codex.eventmsg" |
| 25 | 26 | | "claude.sdk.message" |
| 26 | 27 | | "claude.sdk.permission" |
| 27 | | | "codex.sdk.thread-event"; |
| 28 | | "codex.sdk.thread-event" |
| 29 | | "cursor.acp.notification" |
| 30 | | "cursor.acp.request" |
| 31 | | "cursor.acp.response"; |
| 28 | 32 | method?: string; |
| 29 | 33 | messageType?: string; |
| 30 | 34 | payload: unknown; |
| ... | ... | @@ -464,7 +468,47 @@ EventMsg is broader than server notification coverage. Important equivalents: |
| 464 | 468 | |
| 465 | 469 | --- |
| 466 | 470 | |
| 467 | | ## 5) Codex SDK Thread Events -> Canonical Mapping |
| 471 | ## 5) Cursor ACP -> Canonical Mapping |
| 472 | |
| 473 | ### 5.1 ACP session lifecycle (JSON-RPC methods) |
| 474 | |
| 475 | | Cursor ACP method | Canonical V2 | Notes | |
| 476 | | ------------------------------------ | ------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------- | |
| 477 | | `initialize` (response) | `session.configured` | preserve `protocolVersion`, `agentCapabilities`, `authMethods` | |
| 478 | | `authenticate` (response) | `auth.status` | success when empty result; errors map to `runtime.error(class=permission_error)` | |
| 479 | | `session/new` (response) | `session.started` + `thread.started` + `session.state.changed(ready)` | persist `sessionId` in `providerRefs.providerSessionId`; preserve `modes` in payload/raw | |
| 480 | | `session/load` (response) | `session.started` + `thread.started` + `session.state.changed(ready)` | resume path; note ACP requires `sessionId`, `cwd`, `mcpServers` in request | |
| 481 | | `session/prompt` (response) | `turn.completed` | map `stopReason` (`end_turn`, `cancelled`, etc.) | |
| 482 | | `session/cancel` (error `-32601`) | `runtime.warning` | currently unsupported in observed ACP version; adapter should fall back to process interrupt | |
| 483 | |
| 484 | ### 5.2 ACP notifications (`session/update`) |
| 485 | |
| 486 | | Cursor ACP `update.sessionUpdate` | Canonical V2 | Notes | |
| 487 | | --------------------------------- | ---------------------------------------- | ------------------------------------------------------------------------------------------- | |
| 488 | | `available_commands_update` | `session.configured` | preserve `availableCommands`; optional secondary `runtime.warning` if list changes mid-turn | |
| 489 | | `agent_thought_chunk` | `content.delta` (`streamKind=reasoning_text`) | map `content.text` | |
| 490 | | `agent_message_chunk` | `content.delta` (`streamKind=assistant_text`) | map `content.text` | |
| 491 | | `tool_call` | `item.started` | map `toolCallId` -> `itemId`; dedupe starts because ACP may emit multiple `tool_call` updates for the same id | |
| 492 | | `tool_call_update` (`in_progress`) | `item.updated` | include status + partial metadata | |
| 493 | | `tool_call_update` (`completed`) | `item.completed` | include `rawOutput` summary when present (terminal stdout/stderr, search totals, etc.) | |
| 494 | |
| 495 | ### 5.3 ACP server requests (permission) |
| 496 | |
| 497 | | Cursor ACP request method | Canonical V2 | Notes | |
| 498 | | ------------------------------- | ---------------------------- | ------------------------------------------------------------------------------------------------------------- | |
| 499 | | `session/request_permission` | `request.opened` | `requestType` usually `command_execution_approval` for `kind=execute`; preserve options (`allow/reject`) | |
| 500 | | permission response sent by T3 | `request.resolved` | map T3 decision -> ACP `optionId` (`allow-once`, `allow-always`, `reject-once`) | |
| 501 | |
| 502 | ### 5.4 ACP runtime/error mapping notes |
| 503 | |
| 504 | 1. Unknown session on `session/prompt` (`-32603`) -> `runtime.error(class=provider_error)` and terminate active turn as failed. |
| 505 | 2. Invalid params / method-not-found (`-32602`, `-32601`) -> `runtime.error(class=validation_error)` or `runtime.warning` depending recoverability. |
| 506 | 3. Concurrent `session/prompt` calls can cancel earlier prompt (`stopReason=cancelled`) -> emit `turn.completed(state=cancelled)` for the superseded turn. |
| 507 | 4. Keep all ACP envelopes in `raw` with `source` set to `cursor.acp.notification` or `cursor.acp.request`. |
| 508 | |
| 509 | --- |
| 510 | |
| 511 | ## 6) Codex SDK Thread Events -> Canonical Mapping |
| 468 | 512 | |
| 469 | 513 | (For parity with direct Codex SDK integrations and to cross-reference app-server behavior.) |
| 470 | 514 | |
| ... | ... | @@ -483,24 +527,24 @@ Codex SDK `ThreadItem.type` (`agent_message`, `reasoning`, `command_execution`, |
| 483 | 527 | |
| 484 | 528 | --- |
| 485 | 529 | |
| 486 | | ## 6) Cross-Provider Equivalence (Claude <-> Codex) |
| 487 | | |
| 488 | | | Concept | Codex source | Claude source | Canonical | |
| 489 | | | --------------------------- | -------------------------------------------- | ----------------------------------------------------------- | ------------------------------- | |
| 490 | | | Turn start | `turn/started` | first active response turn + `sendTurn` start | `turn.started` | |
| 491 | | | Turn completion | `turn/completed` | `result` | `turn.completed` | |
| 492 | | | Assistant text streaming | `item/agentMessage/delta` | `stream_event content_block_delta[text_delta]` | `content.delta(assistant_text)` | |
| 493 | | | Tool call start | `item/started` with tool item | `content_block_start tool_use/server_tool_use/mcp_tool_use` | `item.started` | |
| 494 | | | Tool call end | `item/completed` with tool item | `content_block_stop` | `item.completed` | |
| 495 | | | Approval required | server request `.../requestApproval` | `canUseTool` callback | `request.opened` | |
| 496 | | | Approval resolved | `serverRequest/resolved` + response decision | callback resolution decision | `request.resolved` | |
| 497 | | | Runtime error | `error` notification / failed turn | `result error_*` or assistant error | `runtime.error` | |
| 498 | | | Model reroute | `model/rerouted` | (none native, but could appear as result/meta) | `model.rerouted` | |
| 499 | | | Session capabilities/config | `session_configured`/init responses | `system:init` | `session.configured` | |
| 530 | ## 7) Cross-Provider Equivalence (Claude <-> Codex <-> Cursor ACP) |
| 531 | |
| 532 | | Concept | Codex source | Claude source | Cursor ACP source | Canonical | |
| 533 | | --------------------------- | -------------------------------------------- | ----------------------------------------------------------- | ------------------------------------------------- | ------------------------------- | |
| 534 | | Turn start | `turn/started` | first active response turn + `sendTurn` start | `session/prompt` dispatch lifecycle | `turn.started` | |
| 535 | | Turn completion | `turn/completed` | `result` | `session/prompt` response (`stopReason`) | `turn.completed` | |
| 536 | | Assistant text streaming | `item/agentMessage/delta` | `stream_event content_block_delta[text_delta]` | `session/update:agent_message_chunk` | `content.delta(assistant_text)` | |
| 537 | | Reasoning/thought streaming | `item/reasoning/textDelta` | reasoning deltas in `stream_event` | `session/update:agent_thought_chunk` | `content.delta(reasoning_text)` | |
| 538 | | Tool call start | `item/started` with tool item | `content_block_start tool_use/server_tool_use/mcp_tool_use` | `session/update:tool_call` | `item.started` | |
| 539 | | Tool call end | `item/completed` with tool item | `content_block_stop` | `session/update:tool_call_update(completed)` | `item.completed` | |
| 540 | | Approval required | server request `.../requestApproval` | `canUseTool` callback | `session/request_permission` | `request.opened` | |
| 541 | | Approval resolved | `serverRequest/resolved` + response decision | callback resolution decision | JSON-RPC response to permission request id | `request.resolved` | |
| 542 | | Runtime error | `error` notification / failed turn | `result error_*` or assistant error | JSON-RPC error response (`-3260x` etc.) | `runtime.error` | |
| 543 | | Session capabilities/config | `session_configured`/init responses | `system:init` | `initialize` / `available_commands_update` | `session.configured` | |
| 500 | 544 | |
| 501 | 545 | --- |
| 502 | 546 | |
| 503 | | ## 7) Implementation Guidance |
| 547 | ## 8) Implementation Guidance |
| 504 | 548 | |
| 505 | 549 | 1. Emit one canonical event per source message minimum; emit additional derived events when needed (for example `runtime.error` + `turn.completed`). |
| 506 | 550 | 2. Always attach `raw` payload so provider-specific detail is never lost. |
| ... | ... | @@ -513,7 +557,7 @@ Codex SDK `ThreadItem.type` (`agent_message`, `reasoning`, `command_execution`, |
| 513 | 557 | |
| 514 | 558 | --- |
| 515 | 559 | |
| 516 | | ## 8) Summary of Breaking Schema Direction |
| 560 | ## 9) Summary of Breaking Schema Direction |
| 517 | 561 | |
| 518 | 562 | - Move from a small, chat-centric runtime union to an **item/lifecycle/protocol-complete canonical runtime model**. |
| 519 | 563 | - Normalize Claude and Codex onto the same event families (`turn`, `item`, `content`, `request`, `task`, `hook`, `state`), keeping provider specifics in `payload` + `raw`. |