mirror of
https://github.com/codewhale-hq/Codewhale.git
synced 2026-11-11 01:51:34 -10:00
feat(app-server-protocol): freeze the selected Codex v2 contract as a generated manifest (CW-7704 V2-01)
contract/codex-v2.json is now the one source for the Codex App Server v2
contract: every pinned rust-v0.166.0 stable method with its tier, status,
basis, upstream variant and params/result types, the dispatch rule per
tier, the error codes and the Codewhale extensions. src/codegen reads it
with the unmodified pinned schema member and generates src/methods.rs,
src/v2.rs (319 typed definitions: params, results, notification payloads,
server-request answers, the full ThreadItem/UserInput/Thread/Turn unions
and their item unions, plus typed ClientRequestParams, ClientResponse,
ServerRequestParams, ServerRequestResponse and ServerNotification
envelopes), METHODS.md, schema/json/codex-v2.schema.json and
schema/typescript/codex-v2.d.ts. This replaces the hand-kept methods.rs
macro and METHODS.md table, whose agreement tests/methods.rs only checked
after the fact.
Selection is unchanged: 44 A/B client requests, 1 client notification,
5 server requests, 33 server notifications. Tier C and undecided rows
(124) answer -32601 and are never emitted; the 29 undecided rows with an
A/B recommendation stay undecided pending the founder.
Shapes the earlier hand types dropped are now typed: ThreadItem's shared
startedAtMs/completedAtMs beside its tagged kind, and the url/fileId
alternatives inside image inputs and function-call output items.
Codewhale extensions: Thread.executor (ThreadExecutor) and
Thread.environment (ThreadPlacement, named apart from Codex
ThreadEnvironment) stay optional and credential-free; rpc re-exports them
under their old names. CAPABILITY_UNSUPPORTED (-32020) with
CapabilityRefusalData carries the executor/placement refusal codes.
Pinned: tests/pinned/codex/codex_app_server_protocol.schemas.json
(stable export member, sha256 9b6d5364...) and common.rs (git blob
2936c018, read only by the result-type check).
Checks (local, dev-cargo.sh, macOS): cargo test --locked -p
codewhale-app-server-protocol: lib 10, contract 7, wire 3 passed;
with --features schema also schema 2 passed. The contract test
round-trips 1064 schema-derived instances over 303 pinned definitions
and validates input and output with jsonschema; removing one Thread
field from src/v2.rs made it fail. clippy -D warnings (both feature
sets), cargo fmt --check, codewhale-codex-contract --check and
codewhale-app-server-schema --check clean; Cargo.lock unchanged.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
dde4b6542d
commit
acc0e30f65
17 files changed
+54134
-607
No files matched your search
@@ -22,6 +22,10 @@ schemars = { version = "1.2.1", optional = true, features = ["derive", "preserve
|
||||
[dev-dependencies]
|
||||
jsonschema.workspace = true
|
||||
|
||||
[[bin]]
|
||||
name = "codewhale-codex-contract"
|
||||
path = "src/bin/contract.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "codewhale-app-server-schema"
|
||||
path = "src/bin/export.rs"
|
||||
|
||||
@@ -1,252 +1,285 @@
|
||||
# Codex App Server method inventory
|
||||
|
||||
Every method in the pinned Codex `rust-v0.166.0` stable wire enums (commit `6ff1e1ca3b30fc8b914720681fcf5fe62e95f856`), with the tier Codewhale gives it and what is on main. The method sets are the unmodified export members under `tests/pinned/codex/`; `tests/methods.rs` fails when this file, those members and the typed enums in `src/methods.rs` disagree.
|
||||
Generated from `contract/codex-v2.json` (contract `codewhale-codex-app-server-v2` version 1) and the pinned Codex `rust-v0.166.0` export (commit `6ff1e1ca3b30fc8b914720681fcf5fe62e95f856`) by `cargo run -p codewhale-app-server-protocol --bin codewhale-codex-contract -- --write`. Edit the manifest, never this file; `tests/contract.rs` fails when they drift. The method sets are the unmodified export members under `tests/pinned/codex/`.
|
||||
|
||||
- **A**: required for 0.10.3 Codex parity.
|
||||
- **B**: service methods, required for 0.10.3.
|
||||
- **C**: out of scope, for the reason given. A Tier C client request answers `-32601` method not found; the server never sends a Tier C request or notification.
|
||||
- **undecided**: the 0.10.3 parity bar does not settle it yet. The last column carries a recommended tier for the founder to confirm.
|
||||
- **C**: out of scope, for the reason given.
|
||||
- **undecided**: the 0.10.3 parity bar does not settle it yet. The last column carries a recommended tier for the founder to confirm; until then it is handled exactly like Tier C.
|
||||
|
||||
Status: `implemented` means a Codex-shaped handler is on main (response goldens are not yet checked); `partial` means the Codex method name dispatches to the legacy shape; `missing` means no handler; `method-not-found` means the dispatcher answers `-32601`; `never-sent` means the server never emits it. The stable export is the scope: methods that exist only in the experimental export are not listed.
|
||||
Status: `implemented` means a Codex-shaped handler is on main (response goldens are not yet checked); `partial` means the Codex method name dispatches to the legacy shape; `missing` means no handler; `method-not-found` means the dispatcher answers `-32601`; `never-sent` means the server never emits it. The stable export is the scope: methods that exist only in the experimental export are not listed. A complete inventory or typed shape is not a handler: status, not tier, says what main dispatches.
|
||||
|
||||
## Dispatch rules
|
||||
|
||||
| Tier | Rule |
|
||||
| --- | --- |
|
||||
| A | selected for 0.10.3; the typed params/result in src/v2.rs are the wire contract |
|
||||
| B | selected for 0.10.3 (service methods); the typed params/result in src/v2.rs are the wire contract |
|
||||
| C | not selected; a client request answers methodNotFound (-32601); the server never sends a Tier C request or notification |
|
||||
| undecided | not selected until the founder decides; until then it is handled exactly like Tier C (methodNotFound, never sent), and the row keeps its recommendation |
|
||||
|
||||
## Client requests
|
||||
|
||||
| Method | Tier | Status | Basis | Recommended |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `initialize` | A | implemented | connection lifecycle (CW-7704; on main) | |
|
||||
| `thread/start` | A | implemented | thread lifecycle | |
|
||||
| `thread/resume` | A | partial | thread lifecycle | |
|
||||
| `thread/fork` | A | partial | thread lifecycle | |
|
||||
| `thread/archive` | A | partial | thread lifecycle | |
|
||||
| `thread/delete` | B | missing | S9 services: thread delete, revert, shellCommand, compact | |
|
||||
| `thread/unsubscribe` | A | missing | S5 subscriptions and paging | |
|
||||
| `thread/name/set` | A | implemented | thread lifecycle | |
|
||||
| `thread/goal/set` | A | partial | S8 goals | |
|
||||
| `thread/goal/get` | A | partial | S8 goals | |
|
||||
| `thread/goal/clear` | A | partial | S8 goals | |
|
||||
| `thread/metadata/update` | undecided | missing | thread metadata beyond name/goal | B |
|
||||
| `thread/attachment/add` | undecided | missing | Codex desktop attachments | C |
|
||||
| `thread/attachment/list` | undecided | missing | Codex desktop attachments | C |
|
||||
| `thread/attachmentOwner/list` | undecided | missing | Codex desktop attachments | C |
|
||||
| `thread/attachment/remove` | undecided | missing | Codex desktop attachments | C |
|
||||
| `thread/section/move` | undecided | missing | Codex desktop sidebar sections; Codewhale projects are CW-7878 | C |
|
||||
| `thread/unarchive` | A | partial | thread lifecycle | |
|
||||
| `thread/compact/start` | B | missing | S9 services: thread delete, revert, shellCommand, compact | |
|
||||
| `thread/shellCommand` | B | missing | S9 services: thread delete, revert, shellCommand, compact | |
|
||||
| `thread/approveGuardianDeniedAction` | undecided | missing | Codex Guardian auto-review override; Codewhale has its own Auto-Review posture | C |
|
||||
| `thread/revert` | B | missing | S9 services: thread delete, revert, shellCommand, compact | |
|
||||
| `thread/list` | A | implemented | thread lifecycle | |
|
||||
| `threadSection/list` | undecided | missing | Codex desktop sidebar sections; Codewhale projects are CW-7878 | C |
|
||||
| `threadSection/create` | undecided | missing | Codex desktop sidebar sections; Codewhale projects are CW-7878 | C |
|
||||
| `threadSection/update` | undecided | missing | Codex desktop sidebar sections; Codewhale projects are CW-7878 | C |
|
||||
| `threadSection/delete` | undecided | missing | Codex desktop sidebar sections; Codewhale projects are CW-7878 | C |
|
||||
| `thread/loaded/list` | A | implemented | thread lifecycle | |
|
||||
| `thread/read` | A | implemented | thread lifecycle | |
|
||||
| `thread/turns/list` | A | missing | S5 subscriptions and paging | |
|
||||
| `thread/items/list` | A | missing | S5 subscriptions and paging | |
|
||||
| `thread/inject_items` | undecided | missing | raw history injection | C |
|
||||
| `skills/list` | B | missing | S9 services: skills | |
|
||||
| `skills/extraRoots/set` | undecided | missing | skills configuration writes | B |
|
||||
| `hooks/list` | B | missing | S9 services: hooks | |
|
||||
| `marketplace/add` | undecided | missing | Codex plugin marketplace; Codewhale plugins are DSH (CW-7051) | C |
|
||||
| `marketplace/remove` | undecided | missing | Codex plugin marketplace; Codewhale plugins are DSH (CW-7051) | C |
|
||||
| `marketplace/upgrade` | undecided | missing | Codex plugin marketplace; Codewhale plugins are DSH (CW-7051) | C |
|
||||
| `plugin/list` | undecided | missing | Codex plugin marketplace; Codewhale plugins are DSH (CW-7051) | C |
|
||||
| `plugin/installed` | undecided | missing | Codex plugin marketplace; Codewhale plugins are DSH (CW-7051) | C |
|
||||
| `plugin/reconcile` | undecided | missing | Codex plugin marketplace; Codewhale plugins are DSH (CW-7051) | C |
|
||||
| `plugin/read` | undecided | missing | Codex plugin marketplace; Codewhale plugins are DSH (CW-7051) | C |
|
||||
| `plugin/skill/read` | undecided | missing | Codex plugin marketplace; Codewhale plugins are DSH (CW-7051) | C |
|
||||
| `plugin/share/save` | C | method-not-found | OpenAI-hosted plugin sharing; no Codewhale service | |
|
||||
| `plugin/share/updateTargets` | C | method-not-found | OpenAI-hosted plugin sharing; no Codewhale service | |
|
||||
| `plugin/share/list` | C | method-not-found | OpenAI-hosted plugin sharing; no Codewhale service | |
|
||||
| `plugin/share/checkout` | C | method-not-found | OpenAI-hosted plugin sharing; no Codewhale service | |
|
||||
| `plugin/share/delete` | C | method-not-found | OpenAI-hosted plugin sharing; no Codewhale service | |
|
||||
| `app/read` | C | method-not-found | ChatGPT Apps connectors; no Codewhale service | |
|
||||
| `app/list` | C | method-not-found | ChatGPT Apps connectors; no Codewhale service | |
|
||||
| `app/installed` | C | method-not-found | ChatGPT Apps connectors; no Codewhale service | |
|
||||
| `fs/readFile` | B | missing | S9 services: fs | |
|
||||
| `fs/writeFile` | B | missing | S9 services: fs | |
|
||||
| `fs/createDirectory` | B | missing | S9 services: fs | |
|
||||
| `fs/getMetadata` | B | missing | S9 services: fs | |
|
||||
| `fs/readDirectory` | B | missing | S9 services: fs | |
|
||||
| `fs/remove` | B | missing | S9 services: fs | |
|
||||
| `fs/copy` | B | missing | S9 services: fs | |
|
||||
| `fs/watch` | B | missing | S9 services: fs | |
|
||||
| `fs/unwatch` | B | missing | S9 services: fs | |
|
||||
| `skills/config/write` | undecided | missing | skills configuration writes | B |
|
||||
| `plugin/install` | undecided | missing | Codex plugin marketplace; Codewhale plugins are DSH (CW-7051) | C |
|
||||
| `plugin/uninstall` | undecided | missing | Codex plugin marketplace; Codewhale plugins are DSH (CW-7051) | C |
|
||||
| `turn/start` | A | missing | S4a full-tool turns | |
|
||||
| `turn/steer` | A | missing | S4a full-tool turns | |
|
||||
| `turn/interrupt` | A | missing | S4a full-tool turns | |
|
||||
| `review/start` | B | missing | S9 services: review | |
|
||||
| `model/list` | B | missing | S9 services: models | |
|
||||
| `account/gatewayOAuth/read` | C | method-not-found | OpenAI gateway OAuth; no Codewhale service | |
|
||||
| `account/gatewayOAuth/login` | C | method-not-found | OpenAI gateway OAuth; no Codewhale service | |
|
||||
| `account/gatewayOAuth/cancel` | C | method-not-found | OpenAI gateway OAuth; no Codewhale service | |
|
||||
| `modelProvider/capabilities/read` | undecided | missing | provider, feature and permission catalogs | B |
|
||||
| `experimentalFeature/list` | undecided | missing | Codex experimental feature flags | C |
|
||||
| `permissionProfile/list` | undecided | missing | provider, feature and permission catalogs | B |
|
||||
| `experimentalFeature/enablement/set` | undecided | missing | Codex experimental feature flags | C |
|
||||
| `mcpServer/oauth/login` | undecided | missing | MCP actions beyond status | B |
|
||||
| `config/mcpServer/reload` | undecided | missing | MCP actions beyond status | B |
|
||||
| `mcpServerStatus/list` | B | missing | S9 services: MCP status | |
|
||||
| `mcpServer/resource/read` | undecided | missing | MCP actions beyond status | B |
|
||||
| `mcpServer/tool/call` | undecided | missing | MCP actions beyond status | B |
|
||||
| `windowsSandbox/setupStart` | undecided | missing | Codex Windows sandbox setup; Codewhale has its own AppContainer sandbox | C |
|
||||
| `windowsSandbox/readiness` | undecided | missing | Codex Windows sandbox setup; Codewhale has its own AppContainer sandbox | C |
|
||||
| `account/login/start` | undecided | missing | OpenAI account sign-in and usage; Codewhale signs in through /auth | C |
|
||||
| `account/login/cancel` | undecided | missing | OpenAI account sign-in and usage; Codewhale signs in through /auth | C |
|
||||
| `account/logout` | undecided | missing | OpenAI account sign-in and usage; Codewhale signs in through /auth | C |
|
||||
| `account/rateLimits/read` | undecided | missing | OpenAI account sign-in and usage; Codewhale signs in through /auth | C |
|
||||
| `account/rateLimitResetCredit/consume` | C | method-not-found | OpenAI billing and workspace messaging; no Codewhale service | |
|
||||
| `account/usage/read` | undecided | missing | OpenAI account sign-in and usage; Codewhale signs in through /auth | C |
|
||||
| `account/workspaceMessages/read` | C | method-not-found | OpenAI billing and workspace messaging; no Codewhale service | |
|
||||
| `account/sendAddCreditsNudgeEmail` | C | method-not-found | OpenAI billing and workspace messaging; no Codewhale service | |
|
||||
| `feedback/upload` | C | method-not-found | uploads feedback to OpenAI; no Codewhale service | |
|
||||
| `feedback/save` | C | method-not-found | uploads feedback to OpenAI; no Codewhale service | |
|
||||
| `command/exec` | B | missing | S9 services: command/exec | |
|
||||
| `command/exec/write` | B | missing | S9 services: command/exec | |
|
||||
| `command/exec/terminate` | B | missing | S9 services: command/exec | |
|
||||
| `command/exec/resize` | B | missing | S9 services: command/exec | |
|
||||
| `config/read` | B | missing | S9 services: config | |
|
||||
| `externalAgentConfig/detect` | undecided | missing | import config from other agents; Codewhale external agents are CW-7879 | C |
|
||||
| `externalAgentConfig/import` | undecided | missing | import config from other agents; Codewhale external agents are CW-7879 | C |
|
||||
| `externalAgentConfig/import/recordHistory` | undecided | missing | import config from other agents; Codewhale external agents are CW-7879 | C |
|
||||
| `externalAgentConfig/import/readHistories` | undecided | missing | import config from other agents; Codewhale external agents are CW-7879 | C |
|
||||
| `config/value/write` | B | missing | S9 services: config | |
|
||||
| `config/batchWrite` | B | missing | S9 services: config | |
|
||||
| `configRequirements/read` | undecided | missing | managed config requirements | B |
|
||||
| `account/read` | undecided | missing | OpenAI account sign-in and usage; Codewhale signs in through /auth | C |
|
||||
| `getConversationSummary` | C | method-not-found | deprecated v1 API (upstream marks it DEPRECATED; TypeScript-only in the export) | |
|
||||
| `gitDiffToRemote` | C | method-not-found | deprecated v1 API (upstream marks it DEPRECATED; TypeScript-only in the export) | |
|
||||
| `getAuthStatus` | C | method-not-found | deprecated v1 API (upstream marks it DEPRECATED; TypeScript-only in the export) | |
|
||||
| `fuzzyFileSearch` | undecided | missing | file search | B |
|
||||
| Method | Tier | Status | Basis | Recommended | Params | Result |
|
||||
| --- | --- | --- | --- | --- | --- | --- |
|
||||
| `initialize` | A | implemented | connection lifecycle (CW-7704; on main) | | `InitializeParams` | `InitializeResponse` |
|
||||
| `thread/start` | A | implemented | thread lifecycle | | `ThreadStartParams` | `ThreadStartResponse` |
|
||||
| `thread/resume` | A | partial | thread lifecycle | | `ThreadResumeParams` | `ThreadResumeResponse` |
|
||||
| `thread/fork` | A | partial | thread lifecycle | | `ThreadForkParams` | `ThreadForkResponse` |
|
||||
| `thread/archive` | A | partial | thread lifecycle | | `ThreadArchiveParams` | `ThreadArchiveResponse` |
|
||||
| `thread/delete` | B | missing | S9 services: thread delete, revert, shellCommand, compact | | `ThreadDeleteParams` | `ThreadDeleteResponse` |
|
||||
| `thread/unsubscribe` | A | missing | S5 subscriptions and paging | | `ThreadUnsubscribeParams` | `ThreadUnsubscribeResponse` |
|
||||
| `thread/name/set` | A | implemented | thread lifecycle | | `ThreadSetNameParams` | `ThreadSetNameResponse` |
|
||||
| `thread/goal/set` | A | partial | S8 goals | | `ThreadGoalSetParams` | `ThreadGoalSetResponse` |
|
||||
| `thread/goal/get` | A | partial | S8 goals | | `ThreadGoalGetParams` | `ThreadGoalGetResponse` |
|
||||
| `thread/goal/clear` | A | partial | S8 goals | | `ThreadGoalClearParams` | `ThreadGoalClearResponse` |
|
||||
| `thread/metadata/update` | undecided | missing | thread metadata beyond name/goal | B | ThreadMetadataUpdateParams (untyped) | ThreadMetadataUpdateResponse (untyped) |
|
||||
| `thread/attachment/add` | undecided | missing | Codex desktop attachments | C | ThreadAttachmentAddParams (untyped) | ThreadAttachmentAddResponse (untyped) |
|
||||
| `thread/attachment/list` | undecided | missing | Codex desktop attachments | C | ThreadAttachmentListParams (untyped) | ThreadAttachmentListResponse (untyped) |
|
||||
| `thread/attachmentOwner/list` | undecided | missing | Codex desktop attachments | C | ThreadAttachmentOwnerListParams (untyped) | ThreadAttachmentOwnerListResponse (untyped) |
|
||||
| `thread/attachment/remove` | undecided | missing | Codex desktop attachments | C | ThreadAttachmentRemoveParams (untyped) | ThreadAttachmentRemoveResponse (untyped) |
|
||||
| `thread/section/move` | undecided | missing | Codex desktop sidebar sections; Codewhale projects are CW-7878 | C | ThreadSectionMoveParams (untyped) | ThreadSectionMoveResponse (untyped) |
|
||||
| `thread/unarchive` | A | partial | thread lifecycle | | `ThreadUnarchiveParams` | `ThreadUnarchiveResponse` |
|
||||
| `thread/compact/start` | B | missing | S9 services: thread delete, revert, shellCommand, compact | | `ThreadCompactStartParams` | `ThreadCompactStartResponse` |
|
||||
| `thread/shellCommand` | B | missing | S9 services: thread delete, revert, shellCommand, compact | | `ThreadShellCommandParams` | `ThreadShellCommandResponse` |
|
||||
| `thread/approveGuardianDeniedAction` | undecided | missing | Codex Guardian auto-review override; Codewhale has its own Auto-Review posture | C | ThreadApproveGuardianDeniedActionParams (untyped) | ThreadApproveGuardianDeniedActionResponse (untyped) |
|
||||
| `thread/revert` | B | missing | S9 services: thread delete, revert, shellCommand, compact | | `ThreadRevertParams` | `ThreadRevertResponse` |
|
||||
| `thread/list` | A | implemented | thread lifecycle | | `ThreadListParams` | `ThreadListResponse` |
|
||||
| `threadSection/list` | undecided | missing | Codex desktop sidebar sections; Codewhale projects are CW-7878 | C | ThreadSectionListParams (untyped) | ThreadSectionListResponse (untyped) |
|
||||
| `threadSection/create` | undecided | missing | Codex desktop sidebar sections; Codewhale projects are CW-7878 | C | ThreadSectionCreateParams (untyped) | ThreadSectionCreateResponse (untyped) |
|
||||
| `threadSection/update` | undecided | missing | Codex desktop sidebar sections; Codewhale projects are CW-7878 | C | ThreadSectionUpdateParams (untyped) | ThreadSectionUpdateResponse (untyped) |
|
||||
| `threadSection/delete` | undecided | missing | Codex desktop sidebar sections; Codewhale projects are CW-7878 | C | ThreadSectionDeleteParams (untyped) | ThreadSectionDeleteResponse (untyped) |
|
||||
| `thread/loaded/list` | A | implemented | thread lifecycle | | `ThreadLoadedListParams` | `ThreadLoadedListResponse` |
|
||||
| `thread/read` | A | implemented | thread lifecycle | | `ThreadReadParams` | `ThreadReadResponse` |
|
||||
| `thread/turns/list` | A | missing | S5 subscriptions and paging | | `ThreadTurnsListParams` | `ThreadTurnsListResponse` |
|
||||
| `thread/items/list` | A | missing | S5 subscriptions and paging | | `ThreadItemsListParams` | `ThreadItemsListResponse` |
|
||||
| `thread/inject_items` | undecided | missing | raw history injection | C | ThreadInjectItemsParams (untyped) | ThreadInjectItemsResponse (untyped) |
|
||||
| `skills/list` | B | missing | S9 services: skills | | `SkillsListParams` | `SkillsListResponse` |
|
||||
| `skills/extraRoots/set` | undecided | missing | skills configuration writes | B | SkillsExtraRootsSetParams (untyped) | SkillsExtraRootsSetResponse (untyped) |
|
||||
| `hooks/list` | B | missing | S9 services: hooks | | `HooksListParams` | `HooksListResponse` |
|
||||
| `marketplace/add` | undecided | missing | Codex plugin marketplace; Codewhale plugins are DSH (CW-7051) | C | MarketplaceAddParams (untyped) | MarketplaceAddResponse (untyped) |
|
||||
| `marketplace/remove` | undecided | missing | Codex plugin marketplace; Codewhale plugins are DSH (CW-7051) | C | MarketplaceRemoveParams (untyped) | MarketplaceRemoveResponse (untyped) |
|
||||
| `marketplace/upgrade` | undecided | missing | Codex plugin marketplace; Codewhale plugins are DSH (CW-7051) | C | MarketplaceUpgradeParams (untyped) | MarketplaceUpgradeResponse (untyped) |
|
||||
| `plugin/list` | undecided | missing | Codex plugin marketplace; Codewhale plugins are DSH (CW-7051) | C | PluginListParams (untyped) | PluginListResponse (untyped) |
|
||||
| `plugin/installed` | undecided | missing | Codex plugin marketplace; Codewhale plugins are DSH (CW-7051) | C | PluginInstalledParams (untyped) | PluginInstalledResponse (untyped) |
|
||||
| `plugin/reconcile` | undecided | missing | Codex plugin marketplace; Codewhale plugins are DSH (CW-7051) | C | PluginReconcileParams (untyped) | PluginReconcileResponse (untyped) |
|
||||
| `plugin/read` | undecided | missing | Codex plugin marketplace; Codewhale plugins are DSH (CW-7051) | C | PluginReadParams (untyped) | PluginReadResponse (untyped) |
|
||||
| `plugin/skill/read` | undecided | missing | Codex plugin marketplace; Codewhale plugins are DSH (CW-7051) | C | PluginSkillReadParams (untyped) | PluginSkillReadResponse (untyped) |
|
||||
| `plugin/share/save` | C | method-not-found | OpenAI-hosted plugin sharing; no Codewhale service | | PluginShareSaveParams (untyped) | PluginShareSaveResponse (untyped) |
|
||||
| `plugin/share/updateTargets` | C | method-not-found | OpenAI-hosted plugin sharing; no Codewhale service | | PluginShareUpdateTargetsParams (untyped) | PluginShareUpdateTargetsResponse (untyped) |
|
||||
| `plugin/share/list` | C | method-not-found | OpenAI-hosted plugin sharing; no Codewhale service | | PluginShareListParams (untyped) | PluginShareListResponse (untyped) |
|
||||
| `plugin/share/checkout` | C | method-not-found | OpenAI-hosted plugin sharing; no Codewhale service | | PluginShareCheckoutParams (untyped) | PluginShareCheckoutResponse (untyped) |
|
||||
| `plugin/share/delete` | C | method-not-found | OpenAI-hosted plugin sharing; no Codewhale service | | PluginShareDeleteParams (untyped) | PluginShareDeleteResponse (untyped) |
|
||||
| `app/read` | C | method-not-found | ChatGPT Apps connectors; no Codewhale service | | AppsReadParams (untyped) | AppsReadResponse (untyped) |
|
||||
| `app/list` | C | method-not-found | ChatGPT Apps connectors; no Codewhale service | | AppsListParams (untyped) | AppsListResponse (untyped) |
|
||||
| `app/installed` | C | method-not-found | ChatGPT Apps connectors; no Codewhale service | | AppsInstalledParams (untyped) | AppsInstalledResponse (untyped) |
|
||||
| `fs/readFile` | B | missing | S9 services: fs | | `FsReadFileParams` | `FsReadFileResponse` |
|
||||
| `fs/writeFile` | B | missing | S9 services: fs | | `FsWriteFileParams` | `FsWriteFileResponse` |
|
||||
| `fs/createDirectory` | B | missing | S9 services: fs | | `FsCreateDirectoryParams` | `FsCreateDirectoryResponse` |
|
||||
| `fs/getMetadata` | B | missing | S9 services: fs | | `FsGetMetadataParams` | `FsGetMetadataResponse` |
|
||||
| `fs/readDirectory` | B | missing | S9 services: fs | | `FsReadDirectoryParams` | `FsReadDirectoryResponse` |
|
||||
| `fs/remove` | B | missing | S9 services: fs | | `FsRemoveParams` | `FsRemoveResponse` |
|
||||
| `fs/copy` | B | missing | S9 services: fs | | `FsCopyParams` | `FsCopyResponse` |
|
||||
| `fs/watch` | B | missing | S9 services: fs | | `FsWatchParams` | `FsWatchResponse` |
|
||||
| `fs/unwatch` | B | missing | S9 services: fs | | `FsUnwatchParams` | `FsUnwatchResponse` |
|
||||
| `skills/config/write` | undecided | missing | skills configuration writes | B | SkillsConfigWriteParams (untyped) | SkillsConfigWriteResponse (untyped) |
|
||||
| `plugin/install` | undecided | missing | Codex plugin marketplace; Codewhale plugins are DSH (CW-7051) | C | PluginInstallParams (untyped) | PluginInstallResponse (untyped) |
|
||||
| `plugin/uninstall` | undecided | missing | Codex plugin marketplace; Codewhale plugins are DSH (CW-7051) | C | PluginUninstallParams (untyped) | PluginUninstallResponse (untyped) |
|
||||
| `turn/start` | A | missing | S4a full-tool turns | | `TurnStartParams` | `TurnStartResponse` |
|
||||
| `turn/steer` | A | missing | S4a full-tool turns | | `TurnSteerParams` | `TurnSteerResponse` |
|
||||
| `turn/interrupt` | A | missing | S4a full-tool turns | | `TurnInterruptParams` | `TurnInterruptResponse` |
|
||||
| `review/start` | B | missing | S9 services: review | | `ReviewStartParams` | `ReviewStartResponse` |
|
||||
| `model/list` | B | missing | S9 services: models | | `ModelListParams` | `ModelListResponse` |
|
||||
| `account/gatewayOAuth/read` | C | method-not-found | OpenAI gateway OAuth; no Codewhale service | | | |
|
||||
| `account/gatewayOAuth/login` | C | method-not-found | OpenAI gateway OAuth; no Codewhale service | | | |
|
||||
| `account/gatewayOAuth/cancel` | C | method-not-found | OpenAI gateway OAuth; no Codewhale service | | | |
|
||||
| `modelProvider/capabilities/read` | undecided | missing | provider, feature and permission catalogs | B | ModelProviderCapabilitiesReadParams (untyped) | ModelProviderCapabilitiesReadResponse (untyped) |
|
||||
| `experimentalFeature/list` | undecided | missing | Codex experimental feature flags | C | ExperimentalFeatureListParams (untyped) | ExperimentalFeatureListResponse (untyped) |
|
||||
| `permissionProfile/list` | undecided | missing | provider, feature and permission catalogs | B | PermissionProfileListParams (untyped) | PermissionProfileListResponse (untyped) |
|
||||
| `experimentalFeature/enablement/set` | undecided | missing | Codex experimental feature flags | C | ExperimentalFeatureEnablementSetParams (untyped) | ExperimentalFeatureEnablementSetResponse (untyped) |
|
||||
| `mcpServer/oauth/login` | undecided | missing | MCP actions beyond status | B | McpServerOauthLoginParams (untyped) | McpServerOauthLoginResponse (untyped) |
|
||||
| `config/mcpServer/reload` | undecided | missing | MCP actions beyond status | B | | |
|
||||
| `mcpServerStatus/list` | B | missing | S9 services: MCP status | | `ListMcpServerStatusParams` | `ListMcpServerStatusResponse` |
|
||||
| `mcpServer/resource/read` | undecided | missing | MCP actions beyond status | B | McpResourceReadParams (untyped) | McpResourceReadResponse (untyped) |
|
||||
| `mcpServer/tool/call` | undecided | missing | MCP actions beyond status | B | McpServerToolCallParams (untyped) | McpServerToolCallResponse (untyped) |
|
||||
| `windowsSandbox/setupStart` | undecided | missing | Codex Windows sandbox setup; Codewhale has its own AppContainer sandbox | C | WindowsSandboxSetupStartParams (untyped) | WindowsSandboxSetupStartResponse (untyped) |
|
||||
| `windowsSandbox/readiness` | undecided | missing | Codex Windows sandbox setup; Codewhale has its own AppContainer sandbox | C | | |
|
||||
| `account/login/start` | undecided | missing | OpenAI account sign-in and usage; Codewhale signs in through /auth | C | LoginAccountParams (untyped) | LoginAccountResponse (untyped) |
|
||||
| `account/login/cancel` | undecided | missing | OpenAI account sign-in and usage; Codewhale signs in through /auth | C | CancelLoginAccountParams (untyped) | CancelLoginAccountResponse (untyped) |
|
||||
| `account/logout` | undecided | missing | OpenAI account sign-in and usage; Codewhale signs in through /auth | C | | |
|
||||
| `account/rateLimits/read` | undecided | missing | OpenAI account sign-in and usage; Codewhale signs in through /auth | C | | |
|
||||
| `account/rateLimitResetCredit/consume` | C | method-not-found | OpenAI billing and workspace messaging; no Codewhale service | | ConsumeAccountRateLimitResetCreditParams (untyped) | ConsumeAccountRateLimitResetCreditResponse (untyped) |
|
||||
| `account/usage/read` | undecided | missing | OpenAI account sign-in and usage; Codewhale signs in through /auth | C | | |
|
||||
| `account/workspaceMessages/read` | C | method-not-found | OpenAI billing and workspace messaging; no Codewhale service | | | |
|
||||
| `account/sendAddCreditsNudgeEmail` | C | method-not-found | OpenAI billing and workspace messaging; no Codewhale service | | SendAddCreditsNudgeEmailParams (untyped) | SendAddCreditsNudgeEmailResponse (untyped) |
|
||||
| `feedback/upload` | C | method-not-found | uploads feedback to OpenAI; no Codewhale service | | FeedbackUploadParams (untyped) | FeedbackUploadResponse (untyped) |
|
||||
| `feedback/save` | C | method-not-found | uploads feedback to OpenAI; no Codewhale service | | FeedbackSaveParams (untyped) | FeedbackSaveResponse (untyped) |
|
||||
| `command/exec` | B | missing | S9 services: command/exec | | `CommandExecParams` | `CommandExecResponse` |
|
||||
| `command/exec/write` | B | missing | S9 services: command/exec | | `CommandExecWriteParams` | `CommandExecWriteResponse` |
|
||||
| `command/exec/terminate` | B | missing | S9 services: command/exec | | `CommandExecTerminateParams` | `CommandExecTerminateResponse` |
|
||||
| `command/exec/resize` | B | missing | S9 services: command/exec | | `CommandExecResizeParams` | `CommandExecResizeResponse` |
|
||||
| `config/read` | B | missing | S9 services: config | | `ConfigReadParams` | `ConfigReadResponse` |
|
||||
| `externalAgentConfig/detect` | undecided | missing | import config from other agents; Codewhale external agents are CW-7879 | C | ExternalAgentConfigDetectParams (untyped) | ExternalAgentConfigDetectResponse (untyped) |
|
||||
| `externalAgentConfig/import` | undecided | missing | import config from other agents; Codewhale external agents are CW-7879 | C | ExternalAgentConfigImportParams (untyped) | ExternalAgentConfigImportResponse (untyped) |
|
||||
| `externalAgentConfig/import/recordHistory` | undecided | missing | import config from other agents; Codewhale external agents are CW-7879 | C | ExternalAgentConfigImportHistoryRecordParams (untyped) | ExternalAgentConfigImportHistoryRecordResponse (untyped) |
|
||||
| `externalAgentConfig/import/readHistories` | undecided | missing | import config from other agents; Codewhale external agents are CW-7879 | C | | |
|
||||
| `config/value/write` | B | missing | S9 services: config | | `ConfigValueWriteParams` | `ConfigWriteResponse` |
|
||||
| `config/batchWrite` | B | missing | S9 services: config | | `ConfigBatchWriteParams` | `ConfigWriteResponse` |
|
||||
| `configRequirements/read` | undecided | missing | managed config requirements | B | | |
|
||||
| `account/read` | undecided | missing | OpenAI account sign-in and usage; Codewhale signs in through /auth | C | GetAccountParams (untyped) | GetAccountResponse (untyped) |
|
||||
| `getConversationSummary` | C | method-not-found | deprecated v1 API (upstream marks it DEPRECATED; TypeScript-only in the export) | | GetConversationSummaryParams (untyped) | GetConversationSummaryResponse (untyped) |
|
||||
| `gitDiffToRemote` | C | method-not-found | deprecated v1 API (upstream marks it DEPRECATED; TypeScript-only in the export) | | GitDiffToRemoteParams (untyped) | GitDiffToRemoteResponse (untyped) |
|
||||
| `getAuthStatus` | C | method-not-found | deprecated v1 API (upstream marks it DEPRECATED; TypeScript-only in the export) | | GetAuthStatusParams (untyped) | GetAuthStatusResponse (untyped) |
|
||||
| `fuzzyFileSearch` | undecided | missing | file search | B | FuzzyFileSearchParams (untyped) | FuzzyFileSearchResponse (untyped) |
|
||||
|
||||
## Client notifications
|
||||
|
||||
| Method | Tier | Status | Basis | Recommended |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `initialized` | A | implemented | connection lifecycle (CW-7704; on main) | |
|
||||
| Method | Tier | Status | Basis | Recommended | Params | Result |
|
||||
| --- | --- | --- | --- | --- | --- | --- |
|
||||
| `initialized` | A | implemented | connection lifecycle (CW-7704; on main) | | | |
|
||||
|
||||
## Server requests
|
||||
|
||||
| Method | Tier | Status | Basis | Recommended |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `item/commandExecution/requestApproval` | A | missing | S6 interactions (CW-7704: bidirectional approvals, input, client tools) | |
|
||||
| `item/fileChange/requestApproval` | A | missing | S6 interactions (CW-7704: bidirectional approvals, input, client tools) | |
|
||||
| `item/tool/requestUserInput` | A | missing | S6 interactions (CW-7704: bidirectional approvals, input, client tools) | |
|
||||
| `mcpServer/elicitation/request` | undecided | missing | MCP elicitation | A |
|
||||
| `item/permissions/requestApproval` | A | missing | S6 interactions (CW-7704: bidirectional approvals, input, client tools) | |
|
||||
| `item/tool/call` | A | missing | S6 interactions (CW-7704: bidirectional approvals, input, client tools) | |
|
||||
| `account/chatgptAuthTokens/refresh` | C | never-sent | ChatGPT externally managed auth tokens; no Codewhale service | |
|
||||
| `attestation/generate` | C | never-sent | OpenAI request attestation; no Codewhale service | |
|
||||
| `applyPatchApproval` | C | never-sent | deprecated v1 approvals for legacy turns; superseded by item/*/requestApproval | |
|
||||
| `execCommandApproval` | C | never-sent | deprecated v1 approvals for legacy turns; superseded by item/*/requestApproval | |
|
||||
| Method | Tier | Status | Basis | Recommended | Params | Result |
|
||||
| --- | --- | --- | --- | --- | --- | --- |
|
||||
| `item/commandExecution/requestApproval` | A | missing | S6 interactions (CW-7704: bidirectional approvals, input, client tools) | | `CommandExecutionRequestApprovalParams` | `CommandExecutionRequestApprovalResponse` |
|
||||
| `item/fileChange/requestApproval` | A | missing | S6 interactions (CW-7704: bidirectional approvals, input, client tools) | | `FileChangeRequestApprovalParams` | `FileChangeRequestApprovalResponse` |
|
||||
| `item/tool/requestUserInput` | A | missing | S6 interactions (CW-7704: bidirectional approvals, input, client tools) | | `ToolRequestUserInputParams` | `ToolRequestUserInputResponse` |
|
||||
| `mcpServer/elicitation/request` | undecided | missing | MCP elicitation | A | McpServerElicitationRequestParams (untyped) | McpServerElicitationRequestResponse (untyped) |
|
||||
| `item/permissions/requestApproval` | A | missing | S6 interactions (CW-7704: bidirectional approvals, input, client tools) | | `PermissionsRequestApprovalParams` | `PermissionsRequestApprovalResponse` |
|
||||
| `item/tool/call` | A | missing | S6 interactions (CW-7704: bidirectional approvals, input, client tools) | | `DynamicToolCallParams` | `DynamicToolCallResponse` |
|
||||
| `account/chatgptAuthTokens/refresh` | C | never-sent | ChatGPT externally managed auth tokens; no Codewhale service | | ChatgptAuthTokensRefreshParams (untyped) | ChatgptAuthTokensRefreshResponse (untyped) |
|
||||
| `attestation/generate` | C | never-sent | OpenAI request attestation; no Codewhale service | | AttestationGenerateParams (untyped) | AttestationGenerateResponse (untyped) |
|
||||
| `applyPatchApproval` | C | never-sent | deprecated v1 approvals for legacy turns; superseded by item/*/requestApproval | | ApplyPatchApprovalParams (untyped) | ApplyPatchApprovalResponse (untyped) |
|
||||
| `execCommandApproval` | C | never-sent | deprecated v1 approvals for legacy turns; superseded by item/*/requestApproval | | ExecCommandApprovalParams (untyped) | ExecCommandApprovalResponse (untyped) |
|
||||
|
||||
## Server notifications
|
||||
|
||||
| Method | Tier | Status | Basis | Recommended |
|
||||
| Method | Tier | Status | Basis | Recommended | Params | Result |
|
||||
| --- | --- | --- | --- | --- | --- | --- |
|
||||
| `error` | A | missing | S3 projection: errors | | `ErrorNotification` | |
|
||||
| `thread/started` | A | missing | S3 projection: thread lifecycle | | `ThreadStartedNotification` | |
|
||||
| `thread/status/changed` | A | missing | S3 projection: thread lifecycle | | `ThreadStatusChangedNotification` | |
|
||||
| `thread/readState/changed` | undecided | missing | thread state beyond the Tier A set | A | ThreadReadStateChangedNotification (untyped) | |
|
||||
| `thread/archived` | A | missing | S3 projection: thread lifecycle | | `ThreadArchivedNotification` | |
|
||||
| `thread/deleted` | B | missing | pairs with a Tier B service | | `ThreadDeletedNotification` | |
|
||||
| `thread/unarchived` | A | missing | S3 projection: thread lifecycle | | `ThreadUnarchivedNotification` | |
|
||||
| `thread/closed` | A | missing | S3 projection: thread lifecycle | | `ThreadClosedNotification` | |
|
||||
| `thread/reverted` | B | missing | pairs with a Tier B service | | `ThreadRevertedNotification` | |
|
||||
| `skills/changed` | B | missing | pairs with a Tier B service | | `SkillsChangedNotification` | |
|
||||
| `thread/name/updated` | A | missing | S3 projection: thread lifecycle | | `ThreadNameUpdatedNotification` | |
|
||||
| `thread/attachment/updated` | undecided | missing | Codex desktop attachments, predictions, projects and environments | C | ThreadAttachmentUpdatedNotification (untyped) | |
|
||||
| `thread/goal/updated` | A | missing | S3 projection with S8 goals | | `ThreadGoalUpdatedNotification` | |
|
||||
| `thread/prediction/updated` | undecided | missing | Codex desktop attachments, predictions, projects and environments | C | ThreadPredictionUpdatedNotification (untyped) | |
|
||||
| `thread/goal/cleared` | A | missing | S3 projection with S8 goals | | `ThreadGoalClearedNotification` | |
|
||||
| `thread/queue/changed` | undecided | missing | thread state beyond the Tier A set | A | ThreadQueueChangedNotification (untyped) | |
|
||||
| `project/changed` | undecided | missing | Codex desktop attachments, predictions, projects and environments | C | ProjectChangedNotification (untyped) | |
|
||||
| `thread/project/updated` | undecided | missing | Codex desktop attachments, predictions, projects and environments | C | ThreadProjectUpdatedNotification (untyped) | |
|
||||
| `thread/environment/connected` | undecided | missing | Codex desktop attachments, predictions, projects and environments | C | EnvironmentConnectionNotification (untyped) | |
|
||||
| `thread/environment/disconnected` | undecided | missing | Codex desktop attachments, predictions, projects and environments | C | EnvironmentConnectionNotification (untyped) | |
|
||||
| `thread/settings/updated` | undecided | missing | thread state beyond the Tier A set | A | ThreadSettingsUpdatedNotification (untyped) | |
|
||||
| `thread/tokenUsage/updated` | A | missing | S3 projection: thread lifecycle | | `ThreadTokenUsageUpdatedNotification` | |
|
||||
| `turn/started` | A | missing | S3 projection: turn/* | | `TurnStartedNotification` | |
|
||||
| `hook/started` | undecided | missing | hook runs; hooks/list is Tier B | B | HookStartedNotification (untyped) | |
|
||||
| `turn/completed` | A | missing | S3 projection: turn/* | | `TurnCompletedNotification` | |
|
||||
| `hook/completed` | undecided | missing | hook runs; hooks/list is Tier B | B | HookCompletedNotification (untyped) | |
|
||||
| `turn/diff/updated` | A | missing | S3 projection: turn/* | | `TurnDiffUpdatedNotification` | |
|
||||
| `turn/plan/updated` | A | missing | S3 projection: turn/* | | `TurnPlanUpdatedNotification` | |
|
||||
| `item/started` | A | missing | S3 projection: item/* | | `ItemStartedNotification` | |
|
||||
| `item/autoApprovalReview/started` | undecided | missing | Codex Guardian; Codewhale has its own Auto-Review posture | C | ItemGuardianApprovalReviewStartedNotification (untyped) | |
|
||||
| `item/autoApprovalReview/completed` | undecided | missing | Codex Guardian; Codewhale has its own Auto-Review posture | C | ItemGuardianApprovalReviewCompletedNotification (untyped) | |
|
||||
| `autoApprovalReview/strictReviewRequired` | undecided | missing | Codex Guardian; Codewhale has its own Auto-Review posture | C | StrictReviewRequiredNotification (untyped) | |
|
||||
| `item/completed` | A | missing | S3 projection: item/* | | `ItemCompletedNotification` | |
|
||||
| `rawResponseItem/completed` | C | never-sent | internal-only upstream (Codex Cloud, exact upstream usage); TypeScript-only in the export | | RawResponseItemCompletedNotification (untyped) | |
|
||||
| `rawResponse/completed` | C | never-sent | internal-only upstream (Codex Cloud, exact upstream usage); TypeScript-only in the export | | RawResponseCompletedNotification (untyped) | |
|
||||
| `item/agentMessage/delta` | A | missing | S3 projection: item/* | | `AgentMessageDeltaNotification` | |
|
||||
| `item/plan/delta` | A | missing | S3 projection: item/* | | `PlanDeltaNotification` | |
|
||||
| `command/exec/outputDelta` | B | missing | pairs with a Tier B service | | `CommandExecOutputDeltaNotification` | |
|
||||
| `process/outputDelta` | undecided | missing | background processes | B | ProcessOutputDeltaNotification (untyped) | |
|
||||
| `process/exited` | undecided | missing | background processes | B | ProcessExitedNotification (untyped) | |
|
||||
| `item/commandExecution/outputDelta` | A | missing | S3 projection: item/* | | `CommandExecutionOutputDeltaNotification` | |
|
||||
| `item/commandExecution/terminalInteraction` | A | missing | S3 projection: item/* | | `TerminalInteractionNotification` | |
|
||||
| `item/fileChange/outputDelta` | A | missing | S3 projection: item/* | | `FileChangeOutputDeltaNotification` | |
|
||||
| `item/fileChange/patchUpdated` | A | missing | S3 projection: item/* | | `FileChangePatchUpdatedNotification` | |
|
||||
| `serverRequest/resolved` | A | missing | S6 interactions (CW-7704) | | `ServerRequestResolvedNotification` | |
|
||||
| `item/mcpToolCall/progress` | A | missing | S3 projection: item/* | | `McpToolCallProgressNotification` | |
|
||||
| `mcpServer/oauthLogin/completed` | undecided | missing | MCP server events | B | McpServerOauthLoginCompletedNotification (untyped) | |
|
||||
| `mcpServer/startupStatus/updated` | undecided | missing | MCP server events | B | McpServerStatusUpdatedNotification (untyped) | |
|
||||
| `mcpServer/event/stream/notification` | undecided | missing | MCP server events | B | McpServerEventStreamNotification (untyped) | |
|
||||
| `account/updated` | undecided | missing | OpenAI account state | C | AccountUpdatedNotification (untyped) | |
|
||||
| `account/gatewayOAuth/changed` | C | never-sent | pairs with a Tier C method | | GatewayOAuthChangedNotification (untyped) | |
|
||||
| `account/rateLimits/updated` | undecided | missing | OpenAI account state | C | AccountRateLimitsUpdatedNotification (untyped) | |
|
||||
| `app/list/updated` | C | never-sent | pairs with a Tier C method | | AppListUpdatedNotification (untyped) | |
|
||||
| `remoteControl/status/changed` | undecided | missing | remote control status | C | RemoteControlStatusChangedNotification (untyped) | |
|
||||
| `externalAgentConfig/import/progress` | undecided | missing | pairs with externalAgentConfig/import | C | ExternalAgentConfigImportProgressNotification (untyped) | |
|
||||
| `externalAgentConfig/import/completed` | undecided | missing | pairs with externalAgentConfig/import | C | ExternalAgentConfigImportCompletedNotification (untyped) | |
|
||||
| `fs/changed` | B | missing | pairs with a Tier B service | | `FsChangedNotification` | |
|
||||
| `item/reasoning/summaryTextDelta` | A | missing | S3 projection: item/* | | `ReasoningSummaryTextDeltaNotification` | |
|
||||
| `item/reasoning/summaryPartAdded` | A | missing | S3 projection: item/* | | `ReasoningSummaryPartAddedNotification` | |
|
||||
| `item/reasoning/textDelta` | A | missing | S3 projection: item/* | | `ReasoningTextDeltaNotification` | |
|
||||
| `thread/compacted` | B | missing | pairs with a Tier B service | | `ContextCompactedNotification` | |
|
||||
| `model/rerouted` | undecided | missing | OpenAI model routing, verification, moderation and safety | C | ModelReroutedNotification (untyped) | |
|
||||
| `model/verification` | undecided | missing | OpenAI model routing, verification, moderation and safety | C | ModelVerificationNotification (untyped) | |
|
||||
| `modelProvider/authRecoveryStarted` | undecided | missing | provider auth recovery | B | AuthRecoveryNotification (untyped) | |
|
||||
| `modelProvider/authRecoveryCompleted` | undecided | missing | provider auth recovery | B | AuthRecoveryNotification (untyped) | |
|
||||
| `turn/moderationMetadata` | undecided | missing | OpenAI model routing, verification, moderation and safety | C | TurnModerationMetadataNotification (untyped) | |
|
||||
| `model/safetyBuffering/updated` | undecided | missing | OpenAI model routing, verification, moderation and safety | C | ModelSafetyBufferingUpdatedNotification (untyped) | |
|
||||
| `warning` | undecided | missing | generic warnings | A | WarningNotification (untyped) | |
|
||||
| `guardianWarning` | undecided | missing | Codex Guardian; Codewhale has its own Auto-Review posture | C | GuardianWarningNotification (untyped) | |
|
||||
| `deprecationNotice` | undecided | missing | generic warnings | A | DeprecationNoticeNotification (untyped) | |
|
||||
| `configWarning` | undecided | missing | generic warnings | A | ConfigWarningNotification (untyped) | |
|
||||
| `fuzzyFileSearch/sessionUpdated` | undecided | missing | pairs with fuzzyFileSearch | B | FuzzyFileSearchSessionUpdatedNotification (untyped) | |
|
||||
| `fuzzyFileSearch/sessionCompleted` | undecided | missing | pairs with fuzzyFileSearch | B | FuzzyFileSearchSessionCompletedNotification (untyped) | |
|
||||
| `thread/realtime/started` | undecided | missing | realtime audio sessions; no Codewhale equivalent | C | ThreadRealtimeStartedNotification (untyped) | |
|
||||
| `thread/realtime/itemAdded` | undecided | missing | realtime audio sessions; no Codewhale equivalent | C | ThreadRealtimeItemAddedNotification (untyped) | |
|
||||
| `thread/realtime/item/started` | undecided | missing | realtime audio sessions; no Codewhale equivalent | C | ThreadRealtimeItemStartedNotification (untyped) | |
|
||||
| `thread/realtime/item/transcript/delta` | undecided | missing | realtime audio sessions; no Codewhale equivalent | C | ThreadRealtimeItemTranscriptDeltaNotification (untyped) | |
|
||||
| `thread/realtime/item/completed` | undecided | missing | realtime audio sessions; no Codewhale equivalent | C | ThreadRealtimeItemCompletedNotification (untyped) | |
|
||||
| `thread/realtime/transcript/delta` | undecided | missing | realtime audio sessions; no Codewhale equivalent | C | ThreadRealtimeTranscriptDeltaNotification (untyped) | |
|
||||
| `thread/realtime/transcript/done` | undecided | missing | realtime audio sessions; no Codewhale equivalent | C | ThreadRealtimeTranscriptDoneNotification (untyped) | |
|
||||
| `thread/realtime/outputAudio/delta` | undecided | missing | realtime audio sessions; no Codewhale equivalent | C | ThreadRealtimeOutputAudioDeltaNotification (untyped) | |
|
||||
| `thread/realtime/sdp` | undecided | missing | realtime audio sessions; no Codewhale equivalent | C | ThreadRealtimeSdpNotification (untyped) | |
|
||||
| `thread/realtime/error` | undecided | missing | realtime audio sessions; no Codewhale equivalent | C | ThreadRealtimeErrorNotification (untyped) | |
|
||||
| `thread/realtime/closed` | undecided | missing | realtime audio sessions; no Codewhale equivalent | C | ThreadRealtimeClosedNotification (untyped) | |
|
||||
| `windows/worldWritableWarning` | undecided | missing | Codex Windows sandbox | C | WindowsWorldWritableWarningNotification (untyped) | |
|
||||
| `windowsSandbox/setupCompleted` | undecided | missing | Codex Windows sandbox | C | WindowsSandboxSetupCompletedNotification (untyped) | |
|
||||
| `account/login/completed` | undecided | missing | OpenAI account state | C | AccountLoginCompletedNotification (untyped) | |
|
||||
|
||||
## Totals
|
||||
|
||||
| Kind | A | B | C | undecided |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `error` | A | missing | S3 projection: errors | |
|
||||
| `thread/started` | A | missing | S3 projection: thread lifecycle | |
|
||||
| `thread/status/changed` | A | missing | S3 projection: thread lifecycle | |
|
||||
| `thread/readState/changed` | undecided | missing | thread state beyond the Tier A set | A |
|
||||
| `thread/archived` | A | missing | S3 projection: thread lifecycle | |
|
||||
| `thread/deleted` | B | missing | pairs with a Tier B service | |
|
||||
| `thread/unarchived` | A | missing | S3 projection: thread lifecycle | |
|
||||
| `thread/closed` | A | missing | S3 projection: thread lifecycle | |
|
||||
| `thread/reverted` | B | missing | pairs with a Tier B service | |
|
||||
| `skills/changed` | B | missing | pairs with a Tier B service | |
|
||||
| `thread/name/updated` | A | missing | S3 projection: thread lifecycle | |
|
||||
| `thread/attachment/updated` | undecided | missing | Codex desktop attachments, predictions, projects and environments | C |
|
||||
| `thread/goal/updated` | A | missing | S3 projection with S8 goals | |
|
||||
| `thread/prediction/updated` | undecided | missing | Codex desktop attachments, predictions, projects and environments | C |
|
||||
| `thread/goal/cleared` | A | missing | S3 projection with S8 goals | |
|
||||
| `thread/queue/changed` | undecided | missing | thread state beyond the Tier A set | A |
|
||||
| `project/changed` | undecided | missing | Codex desktop attachments, predictions, projects and environments | C |
|
||||
| `thread/project/updated` | undecided | missing | Codex desktop attachments, predictions, projects and environments | C |
|
||||
| `thread/environment/connected` | undecided | missing | Codex desktop attachments, predictions, projects and environments | C |
|
||||
| `thread/environment/disconnected` | undecided | missing | Codex desktop attachments, predictions, projects and environments | C |
|
||||
| `thread/settings/updated` | undecided | missing | thread state beyond the Tier A set | A |
|
||||
| `thread/tokenUsage/updated` | A | missing | S3 projection: thread lifecycle | |
|
||||
| `turn/started` | A | missing | S3 projection: turn/* | |
|
||||
| `hook/started` | undecided | missing | hook runs; hooks/list is Tier B | B |
|
||||
| `turn/completed` | A | missing | S3 projection: turn/* | |
|
||||
| `hook/completed` | undecided | missing | hook runs; hooks/list is Tier B | B |
|
||||
| `turn/diff/updated` | A | missing | S3 projection: turn/* | |
|
||||
| `turn/plan/updated` | A | missing | S3 projection: turn/* | |
|
||||
| `item/started` | A | missing | S3 projection: item/* | |
|
||||
| `item/autoApprovalReview/started` | undecided | missing | Codex Guardian; Codewhale has its own Auto-Review posture | C |
|
||||
| `item/autoApprovalReview/completed` | undecided | missing | Codex Guardian; Codewhale has its own Auto-Review posture | C |
|
||||
| `autoApprovalReview/strictReviewRequired` | undecided | missing | Codex Guardian; Codewhale has its own Auto-Review posture | C |
|
||||
| `item/completed` | A | missing | S3 projection: item/* | |
|
||||
| `rawResponseItem/completed` | C | never-sent | internal-only upstream (Codex Cloud, exact upstream usage); TypeScript-only in the export | |
|
||||
| `rawResponse/completed` | C | never-sent | internal-only upstream (Codex Cloud, exact upstream usage); TypeScript-only in the export | |
|
||||
| `item/agentMessage/delta` | A | missing | S3 projection: item/* | |
|
||||
| `item/plan/delta` | A | missing | S3 projection: item/* | |
|
||||
| `command/exec/outputDelta` | B | missing | pairs with a Tier B service | |
|
||||
| `process/outputDelta` | undecided | missing | background processes | B |
|
||||
| `process/exited` | undecided | missing | background processes | B |
|
||||
| `item/commandExecution/outputDelta` | A | missing | S3 projection: item/* | |
|
||||
| `item/commandExecution/terminalInteraction` | A | missing | S3 projection: item/* | |
|
||||
| `item/fileChange/outputDelta` | A | missing | S3 projection: item/* | |
|
||||
| `item/fileChange/patchUpdated` | A | missing | S3 projection: item/* | |
|
||||
| `serverRequest/resolved` | A | missing | S6 interactions (CW-7704) | |
|
||||
| `item/mcpToolCall/progress` | A | missing | S3 projection: item/* | |
|
||||
| `mcpServer/oauthLogin/completed` | undecided | missing | MCP server events | B |
|
||||
| `mcpServer/startupStatus/updated` | undecided | missing | MCP server events | B |
|
||||
| `mcpServer/event/stream/notification` | undecided | missing | MCP server events | B |
|
||||
| `account/updated` | undecided | missing | OpenAI account state | C |
|
||||
| `account/gatewayOAuth/changed` | C | never-sent | pairs with a Tier C method | |
|
||||
| `account/rateLimits/updated` | undecided | missing | OpenAI account state | C |
|
||||
| `app/list/updated` | C | never-sent | pairs with a Tier C method | |
|
||||
| `remoteControl/status/changed` | undecided | missing | remote control status | C |
|
||||
| `externalAgentConfig/import/progress` | undecided | missing | pairs with externalAgentConfig/import | C |
|
||||
| `externalAgentConfig/import/completed` | undecided | missing | pairs with externalAgentConfig/import | C |
|
||||
| `fs/changed` | B | missing | pairs with a Tier B service | |
|
||||
| `item/reasoning/summaryTextDelta` | A | missing | S3 projection: item/* | |
|
||||
| `item/reasoning/summaryPartAdded` | A | missing | S3 projection: item/* | |
|
||||
| `item/reasoning/textDelta` | A | missing | S3 projection: item/* | |
|
||||
| `thread/compacted` | B | missing | pairs with a Tier B service | |
|
||||
| `model/rerouted` | undecided | missing | OpenAI model routing, verification, moderation and safety | C |
|
||||
| `model/verification` | undecided | missing | OpenAI model routing, verification, moderation and safety | C |
|
||||
| `modelProvider/authRecoveryStarted` | undecided | missing | provider auth recovery | B |
|
||||
| `modelProvider/authRecoveryCompleted` | undecided | missing | provider auth recovery | B |
|
||||
| `turn/moderationMetadata` | undecided | missing | OpenAI model routing, verification, moderation and safety | C |
|
||||
| `model/safetyBuffering/updated` | undecided | missing | OpenAI model routing, verification, moderation and safety | C |
|
||||
| `warning` | undecided | missing | generic warnings | A |
|
||||
| `guardianWarning` | undecided | missing | Codex Guardian; Codewhale has its own Auto-Review posture | C |
|
||||
| `deprecationNotice` | undecided | missing | generic warnings | A |
|
||||
| `configWarning` | undecided | missing | generic warnings | A |
|
||||
| `fuzzyFileSearch/sessionUpdated` | undecided | missing | pairs with fuzzyFileSearch | B |
|
||||
| `fuzzyFileSearch/sessionCompleted` | undecided | missing | pairs with fuzzyFileSearch | B |
|
||||
| `thread/realtime/started` | undecided | missing | realtime audio sessions; no Codewhale equivalent | C |
|
||||
| `thread/realtime/itemAdded` | undecided | missing | realtime audio sessions; no Codewhale equivalent | C |
|
||||
| `thread/realtime/item/started` | undecided | missing | realtime audio sessions; no Codewhale equivalent | C |
|
||||
| `thread/realtime/item/transcript/delta` | undecided | missing | realtime audio sessions; no Codewhale equivalent | C |
|
||||
| `thread/realtime/item/completed` | undecided | missing | realtime audio sessions; no Codewhale equivalent | C |
|
||||
| `thread/realtime/transcript/delta` | undecided | missing | realtime audio sessions; no Codewhale equivalent | C |
|
||||
| `thread/realtime/transcript/done` | undecided | missing | realtime audio sessions; no Codewhale equivalent | C |
|
||||
| `thread/realtime/outputAudio/delta` | undecided | missing | realtime audio sessions; no Codewhale equivalent | C |
|
||||
| `thread/realtime/sdp` | undecided | missing | realtime audio sessions; no Codewhale equivalent | C |
|
||||
| `thread/realtime/error` | undecided | missing | realtime audio sessions; no Codewhale equivalent | C |
|
||||
| `thread/realtime/closed` | undecided | missing | realtime audio sessions; no Codewhale equivalent | C |
|
||||
| `windows/worldWritableWarning` | undecided | missing | Codex Windows sandbox | C |
|
||||
| `windowsSandbox/setupCompleted` | undecided | missing | Codex Windows sandbox | C |
|
||||
| `account/login/completed` | undecided | missing | OpenAI account state | C |
|
||||
| Client requests | 19 | 25 | 19 | 46 |
|
||||
| Client notifications | 1 | 0 | 0 | 0 |
|
||||
| Server requests | 5 | 0 | 4 | 1 |
|
||||
| Server notifications | 27 | 6 | 4 | 50 |
|
||||
|
||||
Typed definitions generated into `src/v2.rs`: 319.
|
||||
|
||||
## Errors
|
||||
|
||||
Codex envelopes carry no `jsonrpc` member. ACP keeps its own JSON-RPC 2.0 envelope and is not this contract.
|
||||
|
||||
| Name | Code | Meaning |
|
||||
| --- | --- | --- |
|
||||
| `PARSE_ERROR` | -32700 | the line or message is not JSON |
|
||||
| `INVALID_REQUEST` | -32600 | not a valid request envelope, or a request before initialize |
|
||||
| `METHOD_NOT_FOUND` | -32601 | Tier C or undecided method, or a name the pinned export does not have |
|
||||
| `INVALID_PARAMS` | -32602 | params do not decode into the selected method's typed params |
|
||||
| `INTERNAL_ERROR` | -32603 | the Engine failed while serving a valid request |
|
||||
| `OVERLOADED` | -32001 | upstream Codex transport overload code; the outgoing queue is full |
|
||||
| `THREAD_NOT_FOUND` | -32004 | the named thread does not exist (existing App Server code) |
|
||||
| `RUNTIME_UNAVAILABLE` | -32005 | the Engine could not be reached (existing App Server code) |
|
||||
| `CAPABILITY_UNSUPPORTED` | -32020 | the method exists and is selected, but this thread's executor or placement cannot serve it; `data` is `CapabilityRefusalData`; the request has no side effect and never falls back to local execution |
|
||||
|
||||
## Codewhale extensions
|
||||
|
||||
These ride on Codex shapes without changing any pinned field. The pinned
|
||||
`Thread` schema allows additional properties, so a Codex client ignores the
|
||||
fields. Codewhale notifications live under `codewhale/`, because Codex already
|
||||
uses `thread/environment/*` for its own environments.
|
||||
These ride on Codex shapes without changing any pinned field. The pinned `Thread` schema allows additional properties, so a Codex client ignores the fields. Codewhale notifications live under `codewhale/`, because Codex already uses `thread/environment/*` for its own environments. Extension shapes are declared under `extensions.definitions` in the manifest and generated with the Codex types; none carries a credential.
|
||||
|
||||
| Name | Kind | Tier | Status | Shape |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `Thread.executor` | field | A | defined, unpopulated | `rpc::ThreadExecutor` `{ kind, agentId, agentName? }`: a thread driven by an external agent over ACP; absent for a native thread. Never an environment value or a credential. |
|
||||
| `Thread.environment` | field | B | defined, unpopulated | `rpc::ThreadEnvironment` `{ id, providerKind }`: where the thread runs; absent means this computer. |
|
||||
| `Thread.executor` | field | A | defined, unpopulated | `ThreadExecutor` `{ kind, agentId, agentName? }`: a thread driven by an external agent over ACP; absent for a native thread. Never an environment value or a credential. |
|
||||
| `Thread.environment` | field | B | defined, unpopulated | `ThreadPlacement` `{ id, providerKind }` (named apart from Codex `ThreadEnvironment`, which is a different type): where the thread runs; absent means this computer. A thread placed in an environment this build cannot serve refuses with `CAPABILITY_UNSUPPORTED` (`environment_not_supported`) and never runs locally. |
|
||||
| `codewhale/environment/*` | server notifications | B | reserved, not implemented | environment state changes for placement |
|
||||
| `ThreadItem.placement` | field | B | reserved, not implemented | where a child or job ran |
|
||||
| `approval.decided.responder` | Runtime journal field | A | implemented | the client that answered (`desktop`, `phone`, `web`, `tui`, `acp`), as declared by that client |
|
||||
@@ -26,10 +26,25 @@ The command writes `schema/json/runtime.schema.json`,
|
||||
`--check` compares all three without writing. Normal runtime startup does not
|
||||
generate files or enable schema machinery.
|
||||
|
||||
`src/methods.rs` holds the typed Tier A and B Codex method enums. `METHODS.md` is the
|
||||
committed inventory of every pinned Codex method with its tier and status, and
|
||||
`tests/methods.rs` checks the enums, the inventory and the pinned export against
|
||||
each other.
|
||||
`contract/codex-v2.json` is the frozen, versioned Codex v2 contract manifest:
|
||||
every pinned stable Codex method with its tier (A, B, C or undecided), status,
|
||||
basis and, for selected methods, the upstream variant, params and result types;
|
||||
the dispatch rule for each tier; the error codes; and the Codewhale extensions.
|
||||
`src/codegen` reads it with the unmodified pinned export and writes
|
||||
`src/methods.rs`, `src/v2.rs`, `METHODS.md`,
|
||||
`schema/json/codex-v2.schema.json` and `schema/typescript/codex-v2.d.ts`:
|
||||
|
||||
```sh
|
||||
cargo run -p codewhale-app-server-protocol --bin codewhale-codex-contract -- --write
|
||||
```
|
||||
|
||||
`--check` compares without writing, and `tests/contract.rs` fails on any drift,
|
||||
on any disagreement between the manifest and the pinned method sets, and on
|
||||
any selected shape whose typed Rust value does not round-trip an instance the
|
||||
pinned schema accepts. `v2::ClientRequestParams`, `v2::ServerNotification`,
|
||||
`v2::ServerRequestParams`, `v2::ClientResponse` and `v2::ServerRequestResponse`
|
||||
are the typed envelopes; Codex envelopes carry no `jsonrpc` member. A typed
|
||||
shape is not a handler: `METHODS.md` status says what main dispatches.
|
||||
|
||||
The selected future Codex wire target is official `rust-v0.166.0`, commit
|
||||
`6ff1e1ca3b30fc8b914720681fcf5fe62e95f856`. Its four-variant envelope and initialize/initialized lifecycle serve JSONL
|
||||
|
||||
@@ -7,3 +7,5 @@ Changed files: these adaptations remove Codex-specific derive/export dependencie
|
||||
The exact pinned wire golden fixtures under tests/pinned are unmodified members of the local stable precomputed schema export. This is a staged owner/lifecycle adoption, not complete wire/parity acceptance.
|
||||
|
||||
The method sets in tests/pinned/codex/{ClientRequest,ClientNotification,ServerRequest,ServerNotification}.ts are unmodified TypeScript members of the same commit's stable precomputed export (`codex-rs/app-server-protocol/schema/precomputed/app-server-exports-stable.json.zst`, compressed SHA256 `c3802deddc224896967bccd29e353e93e62af768b44dbd5d583e35825fb3e02a`). Member SHA256: ClientRequest.ts `d52eab2b624ee8bab88a121f78a5465faaa6a2701bbd0d2979dc7e5937741b36`, ClientNotification.ts `96692c191ff9ac32c2688b46a0a04f23d15646937adc352f2adcc8211c6d17a0`, ServerRequest.ts `30880f1a8da876eec30600ba27560d05ad0db5c645e14564c9c79a2429274f06`, ServerNotification.ts `df9b5ab2f11010ac336fb7c5b6bb386a7c389cd5dbe742ac5c804fcc69bd40d9`. The TypeScript members are used because they carry the full wire enums: the export's JSON schemas omit three deprecated v1 client requests and two internal-only notifications. Variant names in src/methods.rs follow the upstream `common.rs` variants.
|
||||
|
||||
The selected v2 contract is generated from two further unmodified pins of the same commit. `tests/pinned/codex/codex_app_server_protocol.schemas.json` is the `json_schema/codex_app_server_protocol.schemas.json` member of the stable precomputed export (expanded export SHA256 `7ae41cc257d2a4d9f93d1e99f24560802439d2f2d050ec5685832025ced962a2`; member SHA256 `9b6d53647b00d68a04181b826599025a810cfa78acb3ec4d518c68b4120499c0`). `tests/pinned/codex/common.rs` is `codex-rs/app-server-protocol/src/protocol/common.rs` (Git blob `2936c01855e463275d8306df8583ec0303e5c854`, SHA256 `b7f0a90dcc822962e59890b252a43142a113cb1355f333d8c6b0f704b97f89f7`); it is read only by `tests/contract.rs` to check each selected request's result type and is not compiled. The Rust types in `src/v2.rs`, the TypeScript in `schema/typescript/codex-v2.d.ts` and the selected-closure JSON Schema in `schema/json/codex-v2.schema.json` are derived from the schema member by `src/codegen`; they are Apache-2.0 contract adaptations like the files above.
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,410 @@
|
||||
export type AbsolutePathBuf = string;
|
||||
export type AdditionalFileSystemPermissions = { "entries"?: Array<FileSystemSandboxEntry> | null; "globScanMaxDepth"?: number | null; "read"?: Array<LegacyAppPathString> | null; "write"?: Array<LegacyAppPathString> | null };
|
||||
export type AdditionalNetworkPermissions = { "enabled"?: boolean | null };
|
||||
export type AgentMessageDelivery = "async";
|
||||
export type AgentMessageDeltaNotification = { "delta": string; "itemId": string; "threadId": string; "turnId": string };
|
||||
export type AgentPath = string;
|
||||
export type AllowDenyRequirement = "allow" | "deny";
|
||||
export type AnalyticsConfig = { "enabled"?: boolean | null; [key: string]: unknown };
|
||||
export type ApprovalsReviewer = "user" | "auto_review" | "guardian_subagent";
|
||||
export type AskForApproval = "untrusted" | "on-request" | "never" | { "granular": { "mcp_elicitations": boolean; "request_permissions"?: boolean; "rules": boolean; "sandbox_approval": boolean; "skill_approval"?: boolean } };
|
||||
export type AsyncUserInputQuestion = { "options"?: Array<string> | null; "title": string };
|
||||
export type AutoCompactTokenLimitScope = "total" | "body_after_prefix";
|
||||
export type BrowserUseConfig = { "allow_history_access"?: boolean | null; "default_origin_policy"?: BrowserUseOriginPolicyConfig | null; "origins"?: { [key: string]: BrowserUseOriginPolicyConfig } | null };
|
||||
export type BrowserUseOriginPolicyConfig = { "access"?: AllowDenyRequirement | null; "downloads"?: AllowDenyRequirement | null; "full_cdp_access"?: AllowDenyRequirement | null; "uploads"?: AllowDenyRequirement | null };
|
||||
export type ByteRange = { "end": number; "start": number };
|
||||
export type CapabilityRefusal = { "code": CapabilityRefusalCode; "method"?: string | null; "threadId"?: string | null };
|
||||
export type CapabilityRefusalCode = "unknown_agent" | "not_installed" | "adapter_missing" | "below_min_version" | "needs_sign_in" | "no_acp" | "executor_not_supported" | "environment_not_supported" | "steer_not_supported" | "reopen_not_supported";
|
||||
export type CapabilityRefusalData = { "codewhaleRefusal": CapabilityRefusal };
|
||||
export type ClientInfo = { "name": string; "title"?: string | null; "version": string };
|
||||
export type CodexErrorInfo = CodexErrorInfoVariant0 | CodexErrorInfoVariant1 | CodexErrorInfoVariant2 | CodexErrorInfoVariant3 | CodexErrorInfoVariant4 | CodexErrorInfoVariant5 | unknown;
|
||||
export type CodexErrorInfoVariant0 = "contextWindowExceeded" | "sessionBudgetExceeded" | "usageLimitExceeded" | "rateLimitExceeded" | "flexUnavailable" | "serverOverloaded" | "modelNotFound" | "policyCapacityUnavailable" | "cyberPolicy" | "misalignmentPolicyViolation" | "tooManyDenials" | "internalServerError" | "unauthorized" | "badRequest" | "threadRollbackFailed" | "sandboxError" | "other";
|
||||
export type CodexErrorInfoVariant1 = { "httpConnectionFailed": CodexErrorInfoVariant1HttpConnectionFailed };
|
||||
export type CodexErrorInfoVariant1HttpConnectionFailed = { "httpStatusCode"?: number | null };
|
||||
export type CodexErrorInfoVariant2 = { "responseStreamConnectionFailed": CodexErrorInfoVariant2ResponseStreamConnectionFailed };
|
||||
export type CodexErrorInfoVariant2ResponseStreamConnectionFailed = { "httpStatusCode"?: number | null };
|
||||
export type CodexErrorInfoVariant3 = { "responseStreamDisconnected": CodexErrorInfoVariant3ResponseStreamDisconnected };
|
||||
export type CodexErrorInfoVariant3ResponseStreamDisconnected = { "httpStatusCode"?: number | null };
|
||||
export type CodexErrorInfoVariant4 = { "responseTooManyFailedAttempts": CodexErrorInfoVariant4ResponseTooManyFailedAttempts };
|
||||
export type CodexErrorInfoVariant4ResponseTooManyFailedAttempts = { "httpStatusCode"?: number | null };
|
||||
export type CodexErrorInfoVariant5 = { "activeTurnNotSteerable": CodexErrorInfoVariant5ActiveTurnNotSteerable };
|
||||
export type CodexErrorInfoVariant5ActiveTurnNotSteerable = { "turnKind": NonSteerableTurnKind };
|
||||
export type CollabAgentState = { "message"?: string | null; "status": CollabAgentStatus };
|
||||
export type CollabAgentStatus = "pendingInit" | "running" | "interrupted" | "completed" | "errored" | "shutdown" | "notFound";
|
||||
export type CollabAgentTool = "spawnAgent" | "sendInput" | "resumeAgent" | "wait" | "closeAgent" | "sendMessage" | "followupTask" | "interruptAgent" | "listAgents";
|
||||
export type CollabAgentToolCallStatus = "inProgress" | "completed" | "failed" | "interrupted";
|
||||
export type CollaborationMode = { "mode": ModeKind; "settings": Settings };
|
||||
export type CommandAction = { "command": string; "name": string; "path": LegacyAppPathString; "type": "read" } | { "command": string; "path"?: string | null; "type": "listFiles" } | { "command": string; "path"?: string | null; "query"?: string | null; "type": "search" } | { "command": string; "type": "unknown" };
|
||||
export type CommandExecOutputDeltaNotification = { "capReached": boolean; "deltaBase64": string; "processId": string; "stream": CommandExecOutputStream };
|
||||
export type CommandExecOutputStream = "stdout" | "stderr";
|
||||
export type CommandExecParams = { "command": Array<string>; "cwd"?: string | null; "disableOutputCap"?: boolean; "disableTimeout"?: boolean; "env"?: { [key: string]: string | null } | null; "outputBytesCap"?: number | null; "processId"?: string | null; "sandboxPolicy"?: SandboxPolicy | null; "size"?: CommandExecTerminalSize | null; "streamStdin"?: boolean; "streamStdoutStderr"?: boolean; "timeoutMs"?: number | null; "tty"?: boolean };
|
||||
export type CommandExecResizeParams = { "processId": string; "size": CommandExecTerminalSize };
|
||||
export type CommandExecResizeResponse = Record<string, never>;
|
||||
export type CommandExecResponse = { "exitCode": number; "stderr": string; "stdout": string };
|
||||
export type CommandExecTerminalSize = { "cols": number; "rows": number };
|
||||
export type CommandExecTerminateParams = { "processId": string };
|
||||
export type CommandExecTerminateResponse = Record<string, never>;
|
||||
export type CommandExecWriteParams = { "closeStdin"?: boolean; "deltaBase64"?: string | null; "processId": string };
|
||||
export type CommandExecWriteResponse = Record<string, never>;
|
||||
export type CommandExecutionApprovalDecision = "accept" | "acceptForSession" | { "acceptWithExecpolicyAmendment": { "execpolicy_amendment": Array<string> } } | { "applyNetworkPolicyAmendment": { "network_policy_amendment": NetworkPolicyAmendment } } | "decline" | "cancel";
|
||||
export type CommandExecutionApprovalKind = "command" | "writeStdin";
|
||||
export type CommandExecutionOutputDeltaNotification = { "delta": string; "itemId": string; "threadId": string; "turnId": string };
|
||||
export type CommandExecutionRequestApprovalParams = { "approvalId"?: string | null; "command"?: string | null; "commandActions"?: Array<CommandAction> | null; "cwd"?: LegacyAppPathString | null; "environmentId"?: string | null; "itemId": string; "kind"?: CommandExecutionApprovalKind; "networkApprovalContext"?: NetworkApprovalContext | null; "proposedExecpolicyAmendment"?: Array<string> | null; "proposedNetworkPolicyAmendments"?: Array<NetworkPolicyAmendment> | null; "reason"?: string | null; "startedAtMs": number; "threadId": string; "turnId": string };
|
||||
export type CommandExecutionRequestApprovalResponse = { "decision": CommandExecutionApprovalDecision };
|
||||
export type CommandExecutionSource = "agent" | "userShell" | "unifiedExecStartup" | "unifiedExecInteraction";
|
||||
export type CommandExecutionStatus = "inProgress" | "completed" | "failed" | "declined";
|
||||
export type ComputerUseConfig = { "default_app_access"?: AllowDenyRequirement | null; "macos"?: ComputerUseMacosConfig | null; "windows"?: ComputerUseWindowsConfig | null };
|
||||
export type ComputerUseMacosConfig = { "bundle_ids"?: { [key: string]: AllowDenyRequirement } | null };
|
||||
export type ComputerUseWindowsConfig = { "aumids"?: { [key: string]: AllowDenyRequirement } | null; "exes"?: Array<ComputerUseWindowsExeConfig> | null };
|
||||
export type ComputerUseWindowsExeConfig = { "access": AllowDenyRequirement; "binary_name"?: string | null; "product_name": string; "publisher_name": string };
|
||||
export type Config = { "analytics"?: AnalyticsConfig | null; "approval_policy"?: AskForApproval | null; "approvals_reviewer"?: ApprovalsReviewer | null; "browser_use"?: BrowserUseConfig | null; "compact_prompt"?: string | null; "computer_use"?: ComputerUseConfig | null; "desktop"?: { [key: string]: unknown } | null; "developer_instructions"?: string | null; "forced_chatgpt_workspace_id"?: ForcedChatgptWorkspaceIds | null; "forced_login_method"?: ForcedLoginMethod | null; "instructions"?: string | null; "model"?: string | null; "model_auto_compact_token_limit"?: number | null; "model_auto_compact_token_limit_scope"?: AutoCompactTokenLimitScope | null; "model_context_window"?: number | null; "model_provider"?: string | null; "model_reasoning_effort"?: ReasoningEffort | null; "model_reasoning_summary"?: ReasoningSummary | null; "model_verbosity"?: Verbosity | null; "review_model"?: string | null; "sandbox_mode"?: SandboxMode | null; "sandbox_workspace_write"?: SandboxWorkspaceWrite | null; "service_tier"?: string | null; "tools"?: ToolsV2 | null; "web_search"?: WebSearchMode | null; [key: string]: unknown };
|
||||
export type ConfigBatchWriteParams = { "edits": Array<ConfigEdit>; "expectedVersion"?: string | null; "filePath"?: string | null; "reloadUserConfig"?: boolean };
|
||||
export type ConfigEdit = { "keyPath": string; "mergeStrategy": MergeStrategy; "value": unknown };
|
||||
export type ConfigLayer = { "config": unknown; "disabledReason"?: string | null; "name": ConfigLayerSource; "version": string };
|
||||
export type ConfigLayerMetadata = { "name": ConfigLayerSource; "version": string };
|
||||
export type ConfigLayerSource = { "file": AbsolutePathBuf; "type": "packagedDefaults" } | { "domain": string; "key": string; "type": "mdm" } | { "file": AbsolutePathBuf; "type": "system" } | { "id": string; "name": string; "type": "enterpriseManaged" } | { "file": AbsolutePathBuf; "profile"?: string | null; "type": "user" } | { "dotCodexFolder": AbsolutePathBuf; "type": "project" } | { "type": "sessionFlags" } | { "file": AbsolutePathBuf; "type": "legacyManagedConfigTomlFromFile" } | { "type": "legacyManagedConfigTomlFromMdm" };
|
||||
export type ConfigReadParams = { "cwd"?: string | null; "includeLayers"?: boolean };
|
||||
export type ConfigReadResponse = { "config": Config; "layers"?: Array<ConfigLayer> | null; "origins": { [key: string]: ConfigLayerMetadata } };
|
||||
export type ConfigValueWriteParams = { "expectedVersion"?: string | null; "filePath"?: string | null; "keyPath": string; "mergeStrategy": MergeStrategy; "value": unknown };
|
||||
export type ConfigWriteResponse = { "filePath": AbsolutePathBuf; "overriddenMetadata"?: OverriddenMetadata | null; "status": WriteStatus; "version": string };
|
||||
export type ContextCompactedNotification = { "threadId": string; "turnId": string };
|
||||
export type CyberAccessProgram = "standard" | "daybreakBlue" | "daybreakRed";
|
||||
export type DynamicToolCallOutputContentItem = { "text": string; "type": "inputText" } | { "imageUrl": string; "type": "inputImage" } | { "audioUrl": string; "type": "inputAudio" };
|
||||
export type DynamicToolCallParams = { "arguments": unknown; "callId": string; "namespace"?: string | null; "threadId": string; "tool": string; "turnId": string };
|
||||
export type DynamicToolCallResponse = { "contentItems": Array<DynamicToolCallOutputContentItem>; "success": boolean };
|
||||
export type DynamicToolCallStatus = "inProgress" | "completed" | "failed";
|
||||
export type ErrorNotification = { "error": TurnError; "threadId": string; "turnId": string; "willRetry": boolean };
|
||||
export type FileChangeApprovalDecision = "accept" | "acceptForSession" | "decline" | "cancel";
|
||||
export type FileChangeOutputDeltaNotification = { "delta": string; "itemId": string; "threadId": string; "turnId": string };
|
||||
export type FileChangePatchUpdatedNotification = { "changes": Array<FileUpdateChange>; "itemId": string; "threadId": string; "turnId": string };
|
||||
export type FileChangeRequestApprovalParams = { "grantRoot"?: string | null; "itemId": string; "reason"?: string | null; "startedAtMs": number; "threadId": string; "turnId": string };
|
||||
export type FileChangeRequestApprovalResponse = { "decision": FileChangeApprovalDecision };
|
||||
export type FileSystemAccessMode = "read" | "write" | "deny";
|
||||
export type FileSystemPath = { "path": LegacyAppPathString; "type": "path" } | { "pattern": string; "type": "glob_pattern" } | { "value": FileSystemSpecialPath; "type": "special" };
|
||||
export type FileSystemSandboxEntry = { "access": FileSystemAccessMode; "path": FileSystemPath };
|
||||
export type FileSystemSpecialPath = { "kind": "root" } | { "kind": "minimal" } | { "subpath"?: LegacyAppPathString | null; "kind": "project_roots" } | { "kind": "tmpdir" } | { "kind": "slash_tmp" } | { "path": string; "subpath"?: LegacyAppPathString | null; "kind": "unknown" };
|
||||
export type FileUpdateChange = { "diff": string; "kind": PatchChangeKind; "path": string };
|
||||
export type ForcedChatgptWorkspaceIds = string | Array<string>;
|
||||
export type ForcedLoginMethod = "chatgpt" | "api";
|
||||
export type FsChangedNotification = { "changedPaths": Array<AbsolutePathBuf>; "watchId": string };
|
||||
export type FsCopyParams = { "destinationPath": AbsolutePathBuf; "recursive"?: boolean; "sourcePath": AbsolutePathBuf };
|
||||
export type FsCopyResponse = Record<string, never>;
|
||||
export type FsCreateDirectoryParams = { "path": AbsolutePathBuf; "recursive"?: boolean | null };
|
||||
export type FsCreateDirectoryResponse = Record<string, never>;
|
||||
export type FsGetMetadataParams = { "path": AbsolutePathBuf };
|
||||
export type FsGetMetadataResponse = { "createdAtMs": number; "isDirectory": boolean; "isFile": boolean; "isSymlink": boolean; "modifiedAtMs": number };
|
||||
export type FsReadDirectoryEntry = { "fileName": string; "isDirectory": boolean; "isFile": boolean };
|
||||
export type FsReadDirectoryParams = { "path": AbsolutePathBuf };
|
||||
export type FsReadDirectoryResponse = { "entries": Array<FsReadDirectoryEntry> };
|
||||
export type FsReadFileParams = { "path": AbsolutePathBuf };
|
||||
export type FsReadFileResponse = { "dataBase64": string };
|
||||
export type FsRemoveParams = { "force"?: boolean | null; "path": AbsolutePathBuf; "recursive"?: boolean | null };
|
||||
export type FsRemoveResponse = Record<string, never>;
|
||||
export type FsUnwatchParams = { "watchId": string };
|
||||
export type FsUnwatchResponse = Record<string, never>;
|
||||
export type FsWatchParams = { "path": AbsolutePathBuf; "watchId": string };
|
||||
export type FsWatchResponse = { "path": AbsolutePathBuf };
|
||||
export type FsWriteFileParams = { "dataBase64": string; "path": AbsolutePathBuf };
|
||||
export type FsWriteFileResponse = Record<string, never>;
|
||||
export type FunctionCallOutputBody = string | Array<FunctionCallOutputContentItem>;
|
||||
export type FunctionCallOutputContentItem = { "text": string; "type": "input_text" } | { "detail"?: ImageDetail | null; "type": "input_image" } & FunctionCallOutputContentItemInputImageKind | { "audio_url": string; "type": "input_audio" } | { "encrypted_content": string; "type": "encrypted_content" };
|
||||
export type FunctionCallOutputContentItemInputImageKind = { "image_url": string } | { "file_id": string };
|
||||
export type GitInfo = { "branch"?: string | null; "originUrl"?: string | null; "sha"?: string | null };
|
||||
export type GrantedPermissionProfile = { "fileSystem"?: AdditionalFileSystemPermissions | null; "network"?: AdditionalNetworkPermissions | null };
|
||||
export type HookErrorInfo = { "message": string; "path": string };
|
||||
export type HookEventName = "preModelRequest" | "postModelRequest" | "preToolUse" | "permissionRequest" | "postToolUse" | "preCompact" | "postCompact" | "sessionStart" | "sessionEnd" | "userPromptSubmit" | "subagentStart" | "subagentStop" | "stop" | "interrupt";
|
||||
export type HookMetadata = { "additionalContextLimit"?: number | null; "currentHash": string; "displayOrder": number; "enabled": boolean; "environmentId"?: string | null; "eventName": HookEventName; "isManaged": boolean; "key": string; "matcher"?: string | null; "pluginId"?: string | null; "source": HookSource; "sourcePath": AbsolutePathBuf; "statusMessage"?: string | null; "timeoutSec": number; "trustStatus": HookTrustStatus } & HookMetadataKind;
|
||||
export type HookMetadataKind = { "async"?: boolean; "command": string; "handlerType": "command" } | { "server": string; "tool": string; "handlerType": "mcpTool" } | { "handlerType": "prompt" } | { "handlerType": "agent" };
|
||||
export type HookPromptFragment = { "hookRunId": string; "text": string };
|
||||
export type HookSource = "system" | "user" | "project" | "mdm" | "sessionFlags" | "plugin" | "cloudRequirements" | "cloudManagedConfig" | "legacyManagedConfigFile" | "legacyManagedConfigMdm" | "unknown";
|
||||
export type HookTrustStatus = "managed" | "untrusted" | "trusted" | "modified";
|
||||
export type HooksListEntry = { "cwd": string; "errors": Array<HookErrorInfo>; "hooks": Array<HookMetadata>; "warnings": Array<string> };
|
||||
export type HooksListParams = { "cwds"?: Array<string>; "threadId"?: string | null };
|
||||
export type HooksListResponse = { "data": Array<HooksListEntry> };
|
||||
export type ImageDetail = "auto" | "low" | "high" | "original";
|
||||
export type ImageGenerationFailure = { "limitId": string; "resetsAt"?: number | null; "type": "usageLimitExceeded" };
|
||||
export type ImageGenerationInputImage = { "path": AbsolutePathBuf; "sha256": string };
|
||||
export type InitializeCapabilities = { "experimentalApi"?: boolean; "explicitGatewayOauth"?: boolean; "extensions"?: { [key: string]: unknown } | null; "mcpServerOpenaiFormElicitation"?: boolean; "optOutNotificationMethods"?: Array<string> | null; "requestAttestation"?: boolean };
|
||||
export type InitializeParams = { "capabilities"?: InitializeCapabilities | null; "clientInfo": ClientInfo };
|
||||
export type InitializeResponse = { "codexHome": AbsolutePathBuf; "platformFamily": string; "platformOs": string; "userAgent": string };
|
||||
export type InputModality = "text" | "image" | "audio";
|
||||
export type ItemCompletedNotification = { "completedAtMs": number; "item": ThreadItem; "threadId": string; "turnId": string };
|
||||
export type ItemStartedNotification = { "item": ThreadItem; "startedAtMs": number; "threadId": string; "turnId": string };
|
||||
export type LegacyAppPathString = string;
|
||||
export type ListMcpServerStatusParams = { "cursor"?: string | null; "detail"?: McpServerStatusDetail | null; "limit"?: number | null; "serverName"?: string | null; "threadId"?: string | null };
|
||||
export type ListMcpServerStatusResponse = { "data": Array<McpServerStatus>; "nextCursor"?: string | null };
|
||||
export type McpAppDisplayMode = "inline" | "fullscreen";
|
||||
export type McpAppUi = { "preferredModelDisplayMode": McpAppDisplayMode; "resourceUri": string };
|
||||
export type McpAuthStatus = "unknown" | "unsupported" | "notLoggedIn" | "bearerToken" | "oAuth";
|
||||
export type McpServerConnectionStatus = "notStarted" | "starting" | "connected" | "authenticationRequired" | "failed" | "cancelled" | "disabled";
|
||||
export type McpServerInfo = { "description"?: string | null; "icons"?: Array<unknown> | null; "name": string; "title"?: string | null; "version": string; "websiteUrl"?: string | null };
|
||||
export type McpServerStatus = { "authStatus": McpAuthStatus; "httpOrigin"?: string | null; "name": string; "pluginId"?: string | null; "resourceTemplates": Array<ResourceTemplate>; "resources": Array<Resource>; "runtimeStatus"?: McpServerConnectionStatus | null; "serverCapabilities"?: unknown; "serverInfo"?: McpServerInfo | null; "toolListMeta"?: unknown; "tools": { [key: string]: Tool }; "toolsError"?: string | null };
|
||||
export type McpServerStatusDetail = "full" | "toolsAndAuthOnly";
|
||||
export type McpToolCallAppContext = { "actionName"?: string | null; "appName"?: string | null; "connectorId": string; "linkId"?: string | null; "resourceUri"?: string | null };
|
||||
export type McpToolCallError = { "message": string };
|
||||
export type McpToolCallProgressNotification = { "itemId": string; "message": string; "threadId": string; "turnId": string };
|
||||
export type McpToolCallResult = { "_meta"?: unknown; "content": Array<unknown>; "structuredContent"?: unknown };
|
||||
export type McpToolCallStatus = "inProgress" | "completed" | "failed";
|
||||
export type MemoryCitation = { "entries": Array<MemoryCitationEntry>; "threadIds": Array<string> };
|
||||
export type MemoryCitationEntry = { "lineEnd": number; "lineStart": number; "note": string; "path": string };
|
||||
export type MergeStrategy = "replace" | "upsert";
|
||||
export type MessagePhase = "commentary" | "partial_answer" | "final_answer";
|
||||
export type MisalignmentErrorDetails = { "detailedExplanation"?: string | null; "errorType"?: string | null; "reviewTarget"?: string | null; "steer"?: MisalignmentSteer | null };
|
||||
export type MisalignmentSteer = { "message": string };
|
||||
export type ModeKind = "plan" | "default";
|
||||
export type Model = { "additionalSpeedTiers"?: Array<string>; "availabilityNux"?: ModelAvailabilityNux | null; "availableAccessPrograms"?: ModelAccessPrograms | null; "defaultReasoningEffort": ReasoningEffort; "defaultServiceTier"?: string | null; "description": string; "displayName": string; "hidden": boolean; "id": string; "inputModalities"?: Array<InputModality>; "isDefault": boolean; "model": string; "modelSpecialty"?: string | null; "multiAgentVersion"?: MultiAgentVersion | null; "serviceTiers"?: Array<ModelServiceTier>; "supportedReasoningEfforts": Array<ReasoningEffortOption>; "supportsPersonality"?: boolean; "upgrade"?: string | null; "upgradeInfo"?: ModelUpgradeInfo | null };
|
||||
export type ModelAccessPrograms = { "cyber": Array<CyberAccessProgram> };
|
||||
export type ModelAvailabilityNux = { "message": string };
|
||||
export type ModelListParams = { "cursor"?: string | null; "includeHidden"?: boolean | null; "limit"?: number | null };
|
||||
export type ModelListResponse = { "data": Array<Model>; "nextCursor"?: string | null };
|
||||
export type ModelServiceTier = { "description": string; "id": string; "name": string };
|
||||
export type ModelUpgradeInfo = { "migrationMarkdown"?: string | null; "model": string; "modelLink"?: string | null; "retirementAt"?: number | null; "upgradeCopy"?: string | null };
|
||||
export type MultiAgentVersion = "disabled" | "v1" | "v2";
|
||||
export type NetworkAccess = "restricted" | "enabled";
|
||||
export type NetworkApprovalContext = { "host": string; "protocol": NetworkApprovalProtocol };
|
||||
export type NetworkApprovalProtocol = "http" | "https" | "socks5Tcp" | "socks5Udp";
|
||||
export type NetworkPolicyAmendment = { "action": NetworkPolicyRuleAction; "host": string };
|
||||
export type NetworkPolicyRuleAction = "allow" | "deny";
|
||||
export type NonSteerableTurnKind = "review" | "compact";
|
||||
export type OverriddenMetadata = { "effectiveValue": unknown; "message": string; "overridingLayer": ConfigLayerMetadata };
|
||||
export type PatchApplyStatus = "inProgress" | "completed" | "failed" | "declined";
|
||||
export type PatchChangeKind = { "type": "add" } | { "type": "delete" } | { "move_path"?: string | null; "type": "update" };
|
||||
export type PermissionGrantScope = "turn" | "session";
|
||||
export type PermissionsRequestApprovalParams = { "cwd": LegacyAppPathString; "environmentId"?: string | null; "itemId": string; "permissions": RequestPermissionProfile; "reason"?: string | null; "startedAtMs": number; "threadId": string; "turnId": string };
|
||||
export type PermissionsRequestApprovalResponse = { "permissions": GrantedPermissionProfile; "scope"?: PermissionGrantScope; "strictAutoReview"?: boolean | null };
|
||||
export type Personality = "none" | "friendly" | "pragmatic";
|
||||
export type PlanDeltaNotification = { "delta": string; "itemId": string; "threadId": string; "turnId": string };
|
||||
export type ReasoningEffort = string;
|
||||
export type ReasoningEffortOption = { "description": string; "reasoningEffort": ReasoningEffort };
|
||||
export type ReasoningSummary = "auto" | "concise" | "detailed" | "none";
|
||||
export type ReasoningSummaryPartAddedNotification = { "itemId": string; "summaryIndex": number; "threadId": string; "turnId": string };
|
||||
export type ReasoningSummaryTextDeltaNotification = { "delta": string; "itemId": string; "summaryIndex": number; "threadId": string; "turnId": string };
|
||||
export type ReasoningTextDeltaNotification = { "contentIndex": number; "delta": string; "itemId": string; "threadId": string; "turnId": string };
|
||||
export type RequestId = string | number;
|
||||
export type RequestPermissionProfile = { "fileSystem"?: AdditionalFileSystemPermissions | null; "network"?: AdditionalNetworkPermissions | null };
|
||||
export type Resource = { "_meta"?: unknown; "annotations"?: unknown; "description"?: string | null; "icons"?: Array<unknown> | null; "mimeType"?: string | null; "name": string; "size"?: number | null; "title"?: string | null; "uri": string };
|
||||
export type ResourceTemplate = { "annotations"?: unknown; "description"?: string | null; "mimeType"?: string | null; "name": string; "title"?: string | null; "uriTemplate": string };
|
||||
export type ReviewDelivery = "inline";
|
||||
export type ReviewStartParams = { "delivery"?: ReviewDelivery | null; "target": ReviewTarget; "threadId": string };
|
||||
export type ReviewStartResponse = { "reviewThreadId": string; "turn": Turn };
|
||||
export type ReviewTarget = { "type": "uncommittedChanges" } | { "branch": string; "type": "baseBranch" } | { "sha": string; "title"?: string | null; "type": "commit" } | { "instructions": string; "type": "custom" };
|
||||
export type SandboxMode = "read-only" | "workspace-write" | "danger-full-access";
|
||||
export type SandboxPolicy = { "type": "dangerFullAccess" } | { "networkAccess"?: boolean; "type": "readOnly" } | { "networkAccess"?: NetworkAccess; "type": "externalSandbox" } | { "excludeSlashTmp"?: boolean; "excludeTmpdirEnvVar"?: boolean; "networkAccess"?: boolean; "writableRoots"?: Array<AbsolutePathBuf>; "type": "workspaceWrite" };
|
||||
export type SandboxWorkspaceWrite = { "exclude_slash_tmp"?: boolean; "exclude_tmpdir_env_var"?: boolean; "network_access"?: boolean; "writable_roots"?: Array<string> };
|
||||
export type ServerRequestResolvedNotification = { "requestId": RequestId; "threadId": string };
|
||||
export type SessionSource = "cli" | "vscode" | "exec" | "appServer" | "unknown" | { "custom": string } | { "subAgent": SubAgentSource };
|
||||
export type Settings = { "developer_instructions"?: string | null; "model": string; "reasoning_effort"?: ReasoningEffort | null };
|
||||
export type SkillDependencies = { "tools": Array<SkillToolDependency> };
|
||||
export type SkillErrorInfo = { "message": string; "path": string };
|
||||
export type SkillInterface = { "brandColor"?: string | null; "defaultPrompt"?: string | null; "displayName"?: string | null; "iconLarge"?: AbsolutePathBuf | null; "iconLargeUrl"?: string | null; "iconSmall"?: AbsolutePathBuf | null; "iconSmallUrl"?: string | null; "shortDescription"?: string | null };
|
||||
export type SkillMetadata = { "dependencies"?: SkillDependencies | null; "description": string; "enabled": boolean; "interface"?: SkillInterface | null; "name": string; "path": LegacyAppPathString; "pluginId"?: string | null; "scope": SkillScope; "shortDescription"?: string | null };
|
||||
export type SkillScope = "user" | "repo" | "system" | "admin";
|
||||
export type SkillToolDependency = { "command"?: string | null; "description"?: string | null; "transport"?: string | null; "type": string; "url"?: string | null; "value": string };
|
||||
export type SkillsChangedNotification = Record<string, never>;
|
||||
export type SkillsListEntry = { "cwd": string; "errors": Array<SkillErrorInfo>; "skills": Array<SkillMetadata> };
|
||||
export type SkillsListParams = { "cwds"?: Array<string>; "forceReload"?: boolean };
|
||||
export type SkillsListResponse = { "data": Array<SkillsListEntry> };
|
||||
export type SortDirection = "asc" | "desc";
|
||||
export type SubAgentActivityKind = "started" | "interacted" | "interrupted" | "completed";
|
||||
export type SubAgentSource = "review" | "compact" | "memory_consolidation" | { "thread_spawn": { "agent_nickname"?: string | null; "agent_path"?: AgentPath | null; "agent_role"?: string | null; "depth": number; "parent_thread_id": ThreadId } } | { "other": string };
|
||||
export type TerminalInteractionNotification = { "itemId": string; "processId": string; "stdin": string; "threadId": string; "turnId": string };
|
||||
export type TextElement = { "byteRange": ByteRange; "placeholder"?: string | null };
|
||||
export type Thread = { "agentNickname"?: string | null; "agentRole"?: string | null; "cliVersion": string; "createdAt": number; "cwd": AbsolutePathBuf; "ephemeral": boolean; "forkedFromId"?: string | null; "gitInfo"?: GitInfo | null; "historyMode"?: ThreadHistoryMode; "id": string; "model"?: string | null; "modelProvider": string; "name"?: string | null; "originator"?: string | null; "parentThreadId"?: string | null; "path"?: string | null; "preview": string; "projectId": string | null; "reasoningEffort"?: ReasoningEffort | null; "recencyAt"?: number | null; "section"?: ThreadSection | null; "sectionEnteredAt"?: number | null; "sessionId": string; "source": SessionSource; "status": ThreadStatus; "threadSource"?: ThreadSource | null; "turns": Array<Turn>; "updatedAt": number; "executor"?: ThreadExecutor | null; "environment"?: ThreadPlacement | null };
|
||||
export type ThreadActiveFlag = "waitingOnApproval" | "waitingOnUserInput";
|
||||
export type ThreadArchiveParams = { "threadId": string };
|
||||
export type ThreadArchiveResponse = Record<string, never>;
|
||||
export type ThreadArchivedNotification = { "threadId": string };
|
||||
export type ThreadClosedNotification = { "threadId": string };
|
||||
export type ThreadCompactStartParams = { "threadId": string };
|
||||
export type ThreadCompactStartResponse = Record<string, never>;
|
||||
export type ThreadDeleteParams = { "threadId": string };
|
||||
export type ThreadDeleteResponse = Record<string, never>;
|
||||
export type ThreadDeletedNotification = { "threadId": string };
|
||||
export type ThreadExecutor = { "kind": string; "agentId": string; "agentName"?: string | null };
|
||||
export type ThreadForkParams = { "approvalPolicy"?: AskForApproval | null; "approvalsReviewer"?: ApprovalsReviewer | null; "baseInstructions"?: string | null; "config"?: { [key: string]: unknown } | null; "cwd"?: string | null; "developerInstructions"?: string | null; "ephemeral"?: boolean; "excludeTurns"?: boolean; "lastTurnId"?: string | null; "model"?: string | null; "modelProvider"?: string | null; "sandbox"?: SandboxMode | null; "serviceTier"?: string | null; "threadId": string; "threadSource"?: ThreadSource | null };
|
||||
export type ThreadForkResponse = { "approvalPolicy": AskForApproval; "approvalsReviewer": ApprovalsReviewer; "cwd": AbsolutePathBuf; "disabledPluginIds"?: Array<string>; "instructionSources"?: Array<LegacyAppPathString>; "model": string; "modelProvider": string; "reasoningEffort"?: ReasoningEffort | null; "sandbox": SandboxPolicy; "serviceTier"?: string | null; "thread": Thread };
|
||||
export type ThreadGoal = { "createdAt": number; "objective": string; "status": ThreadGoalStatus; "threadId": string; "timeUsedSeconds": number; "tokenBudget"?: number | null; "tokensUsed": number; "updatedAt": number };
|
||||
export type ThreadGoalClearParams = { "origin"?: ThreadGoalMutationOrigin | null; "threadId": string };
|
||||
export type ThreadGoalClearResponse = { "cleared": boolean };
|
||||
export type ThreadGoalClearedNotification = { "threadId": string };
|
||||
export type ThreadGoalGetParams = { "threadId": string };
|
||||
export type ThreadGoalGetResponse = { "goal"?: ThreadGoal | null };
|
||||
export type ThreadGoalMutationOrigin = "user" | "automatic";
|
||||
export type ThreadGoalSetParams = { "objective"?: string | null; "origin"?: ThreadGoalMutationOrigin | null; "status"?: ThreadGoalStatus | null; "threadId": string; "tokenBudget"?: number | null };
|
||||
export type ThreadGoalSetResponse = { "goal": ThreadGoal };
|
||||
export type ThreadGoalStatus = "active" | "paused" | "blocked" | "usageLimited" | "budgetLimited" | "complete";
|
||||
export type ThreadGoalUpdatedNotification = { "goal": ThreadGoal; "threadId": string; "turnId"?: string | null };
|
||||
export type ThreadHistoryMode = "legacy" | "paginated";
|
||||
export type ThreadId = string;
|
||||
export type ThreadItem = { "completedAtMs"?: number | null; "startedAtMs"?: number | null } & ThreadItemKind;
|
||||
export type ThreadItemEntry = { "completedAtMs"?: number | null; "item": ThreadItem; "startedAtMs"?: number | null; "turnId": string };
|
||||
export type ThreadItemKind = { "clientId"?: string | null; "content": Array<UserInput>; "id": string; "type": "userMessage" } | { "fragments": Array<HookPromptFragment>; "id": string; "type": "hookPrompt" } | { "delivery"?: AgentMessageDelivery | null; "id": string; "memoryCitation"?: MemoryCitation | null; "phase"?: MessagePhase | null; "questions"?: Array<AsyncUserInputQuestion> | null; "text": string; "type": "agentMessage" } | { "id": string; "name": string; "namespace"?: string | null; "output": FunctionCallOutputBody; "type": "functionCallOutput" } | { "id": string; "text": string; "type": "plan" } | { "content"?: Array<string>; "id": string; "summary"?: Array<string>; "type": "reasoning" } | { "aggregatedOutput"?: string | null; "command": string; "commandActions": Array<CommandAction>; "cwd": LegacyAppPathString; "durationMs"?: number | null; "exitCode"?: number | null; "id": string; "pluginId"?: string | null; "processId"?: string | null; "scriptPath"?: string | null; "source"?: CommandExecutionSource; "status": CommandExecutionStatus; "type": "commandExecution" } | { "changes": Array<FileUpdateChange>; "id": string; "status": PatchApplyStatus; "type": "fileChange" } | { "appContext"?: McpToolCallAppContext | null; "arguments": unknown; "durationMs"?: number | null; "error"?: McpToolCallError | null; "id": string; "mcpAppResourceUri"?: string | null; "mcpAppUi"?: McpAppUi | null; "pluginId"?: string | null; "readOnlyHint"?: boolean | null; "result"?: McpToolCallResult | null; "server": string; "status": McpToolCallStatus; "tool": string; "type": "mcpToolCall" } | { "arguments": unknown; "contentItems"?: Array<DynamicToolCallOutputContentItem> | null; "durationMs"?: number | null; "id": string; "namespace"?: string | null; "status": DynamicToolCallStatus; "success"?: boolean | null; "tool": string; "type": "dynamicToolCall" } | { "agentsStates": { [key: string]: CollabAgentState }; "id": string; "model"?: string | null; "prompt"?: string | null; "reasoningEffort"?: ReasoningEffort | null; "receiverThreadIds": Array<string>; "senderThreadId": string; "status": CollabAgentToolCallStatus; "tool": CollabAgentTool; "type": "collabAgentToolCall" } | { "agentPath": string; "agentThreadId": string; "id": string; "kind": SubAgentActivityKind; "model"?: string | null; "reasoningEffort"?: ReasoningEffort | null; "type": "subAgentActivity" } | { "action"?: WebSearchAction | null; "id": string; "query": string; "results"?: Array<unknown> | null; "type": "webSearch" } | { "id": string; "path": LegacyAppPathString; "type": "imageView" } | { "durationMs": number; "id": string; "type": "sleep" } | { "failure"?: ImageGenerationFailure | null; "generationId"?: string | null; "id": string; "inputImages"?: Array<ImageGenerationInputImage>; "revisedPrompt"?: string | null; "savedPath"?: AbsolutePathBuf | null; "status": string; "transparentBackground"?: boolean | null; "type": "imageGeneration" } & ThreadItemKindImageGenerationKind | { "id": string; "review": string; "type": "enteredReviewMode" } | { "id": string; "review": string; "type": "exitedReviewMode" } | { "id": string; "type": "contextCompaction" };
|
||||
export type ThreadItemKindImageGenerationKind = { "fileId": string } | { "result": string };
|
||||
export type ThreadItemsListAnchor = { "itemId": string; "type": "item" };
|
||||
export type ThreadItemsListCursor = string | ThreadItemsListAnchor;
|
||||
export type ThreadItemsListParams = { "cursor"?: ThreadItemsListCursor | null; "limit"?: number | null; "sortDirection"?: SortDirection | null; "threadId": string; "turnId"?: string | null };
|
||||
export type ThreadItemsListResponse = { "backwardsCursor"?: string | null; "data": Array<ThreadItemEntry>; "nextCursor"?: string | null };
|
||||
export type ThreadListCwdFilter = string | Array<string>;
|
||||
export type ThreadListParams = { "archived"?: boolean | null; "cursor"?: string | null; "cwd"?: ThreadListCwdFilter | null; "excludedThreadIds"?: Array<string> | null; "limit"?: number | null; "modelProviders"?: Array<string> | null; "originators"?: Array<string> | null; "searchTerm"?: string | null; "sectionId"?: string | null; "sortDirection"?: SortDirection | null; "sortKey"?: ThreadSortKey | null; "sourceKinds"?: Array<ThreadSourceKind> | null; "useStateDbOnly"?: boolean };
|
||||
export type ThreadListResponse = { "backwardsCursor"?: string | null; "data": Array<Thread>; "nextCursor"?: string | null };
|
||||
export type ThreadLoadedListParams = { "cursor"?: string | null; "limit"?: number | null };
|
||||
export type ThreadLoadedListResponse = { "data": Array<string>; "nextCursor"?: string | null };
|
||||
export type ThreadNameUpdatedNotification = { "threadId": string; "threadName"?: string | null };
|
||||
export type ThreadPlacement = { "id": string; "providerKind": string };
|
||||
export type ThreadReadParams = { "includeTurns"?: boolean; "threadId": string };
|
||||
export type ThreadReadResponse = { "thread": Thread };
|
||||
export type ThreadResumeParams = { "approvalPolicy"?: AskForApproval | null; "approvalsReviewer"?: ApprovalsReviewer | null; "baseInstructions"?: string | null; "config"?: { [key: string]: unknown } | null; "cwd"?: string | null; "developerInstructions"?: string | null; "excludeTurns"?: boolean; "model"?: string | null; "modelProvider"?: string | null; "personality"?: Personality | null; "sandbox"?: SandboxMode | null; "serviceTier"?: string | null; "threadId": string };
|
||||
export type ThreadResumeResponse = { "approvalPolicy": AskForApproval; "approvalsReviewer": ApprovalsReviewer; "collaborationMode"?: CollaborationMode | null; "cwd": AbsolutePathBuf; "disabledPluginIds"?: Array<string>; "instructionSources"?: Array<LegacyAppPathString>; "itemsBackwardsCursor"?: string | null; "model": string; "modelProvider": string; "reasoningEffort"?: ReasoningEffort | null; "sandbox": SandboxPolicy; "serviceTier"?: string | null; "thread": Thread; "turnsBackwardsCursor"?: string | null };
|
||||
export type ThreadRevertParams = { "beforeTurnId": string; "threadId": string };
|
||||
export type ThreadRevertResponse = { "itemsBackwardsCursor"?: string | null; "thread": Thread; "turnsBackwardsCursor"?: string | null };
|
||||
export type ThreadRevertedNotification = { "threadId": string };
|
||||
export type ThreadSection = { "appearance"?: ThreadSectionAppearance | null; "id": string; "name": string };
|
||||
export type ThreadSectionAppearance = { "color"?: string | null; "icon"?: string | null };
|
||||
export type ThreadSetNameParams = { "name": string; "threadId": string };
|
||||
export type ThreadSetNameResponse = Record<string, never>;
|
||||
export type ThreadShellCommandParams = { "command": string; "threadId": string; "timeoutMs"?: number | null };
|
||||
export type ThreadShellCommandResponse = Record<string, never>;
|
||||
export type ThreadSortKey = "created_at" | "updated_at" | "recency_at" | "section_position";
|
||||
export type ThreadSource = string;
|
||||
export type ThreadSourceKind = "cli" | "vscode" | "exec" | "appServer" | "subAgent" | "subAgentReview" | "subAgentCompact" | "subAgentThreadSpawn" | "subAgentOther" | "unknown";
|
||||
export type ThreadStartParams = { "approvalPolicy"?: AskForApproval | null; "approvalsReviewer"?: ApprovalsReviewer | null; "baseInstructions"?: string | null; "config"?: { [key: string]: unknown } | null; "cwd"?: string | null; "developerInstructions"?: string | null; "ephemeral"?: boolean | null; "model"?: string | null; "modelProvider"?: string | null; "personality"?: Personality | null; "sandbox"?: SandboxMode | null; "serviceName"?: string | null; "serviceTier"?: string | null; "sessionStartSource"?: ThreadStartSource | null; "threadSource"?: ThreadSource | null };
|
||||
export type ThreadStartResponse = { "approvalPolicy": AskForApproval; "approvalsReviewer": ApprovalsReviewer; "cwd": AbsolutePathBuf; "disabledPluginIds"?: Array<string>; "instructionSources"?: Array<LegacyAppPathString>; "model": string; "modelProvider": string; "reasoningEffort"?: ReasoningEffort | null; "sandbox": SandboxPolicy; "serviceTier"?: string | null; "thread": Thread };
|
||||
export type ThreadStartSource = "startup" | "clear";
|
||||
export type ThreadStartedNotification = { "thread": Thread };
|
||||
export type ThreadStatus = { "type": "notLoaded" } | { "type": "idle" } | { "type": "systemError" } | { "activeFlags": Array<ThreadActiveFlag>; "type": "active" };
|
||||
export type ThreadStatusChangedNotification = { "status": ThreadStatus; "threadId": string };
|
||||
export type ThreadTokenUsage = { "last": TokenUsageBreakdown; "modelContextWindow"?: number | null; "total": TokenUsageBreakdown };
|
||||
export type ThreadTokenUsageUpdatedNotification = { "threadId": string; "tokenUsage": ThreadTokenUsage; "turnId": string };
|
||||
export type ThreadTurnsListParams = { "cursor"?: string | null; "itemsView"?: TurnItemsView | null; "limit"?: number | null; "sortDirection"?: SortDirection | null; "threadId": string };
|
||||
export type ThreadTurnsListResponse = { "backwardsCursor"?: string | null; "data": Array<Turn>; "nextCursor"?: string | null };
|
||||
export type ThreadUnarchiveParams = { "threadId": string };
|
||||
export type ThreadUnarchiveResponse = { "thread": Thread };
|
||||
export type ThreadUnarchivedNotification = { "threadId": string };
|
||||
export type ThreadUnsubscribeParams = { "threadId": string };
|
||||
export type ThreadUnsubscribeResponse = { "status": ThreadUnsubscribeStatus };
|
||||
export type ThreadUnsubscribeStatus = "notLoaded" | "notSubscribed" | "unsubscribed";
|
||||
export type TokenUsageBreakdown = { "cacheWriteInputTokens"?: number; "cachedInputTokens": number; "inputTokens": number; "outputTokens": number; "reasoningOutputTokens": number; "totalTokens": number };
|
||||
export type Tool = { "_meta"?: unknown; "annotations"?: unknown; "description"?: string | null; "icons"?: Array<unknown> | null; "inputSchema": unknown; "name": string; "outputSchema"?: unknown; "title"?: string | null };
|
||||
export type ToolRequestUserInputAnswer = { "answers": Array<string> };
|
||||
export type ToolRequestUserInputOption = { "description": string; "label": string };
|
||||
export type ToolRequestUserInputParams = { "autoResolutionMs"?: number | null; "isBlocking": boolean; "itemId": string; "questions": Array<ToolRequestUserInputQuestion>; "threadId": string; "turnId": string };
|
||||
export type ToolRequestUserInputQuestion = { "header": string; "id": string; "isOther"?: boolean; "isSecret"?: boolean; "options"?: Array<ToolRequestUserInputOption> | null; "question": string };
|
||||
export type ToolRequestUserInputResponse = { "answers": { [key: string]: ToolRequestUserInputAnswer } };
|
||||
export type ToolsV2 = { "web_search"?: WebSearchToolConfig | null };
|
||||
export type Turn = { "completedAt"?: number | null; "durationMs"?: number | null; "error"?: TurnError | null; "id": string; "items": Array<ThreadItem>; "itemsView"?: TurnItemsView; "rootTurnId"?: string | null; "startedAt"?: number | null; "status": TurnStatus };
|
||||
export type TurnCompletedNotification = { "threadId": string; "turn": Turn };
|
||||
export type TurnDiffUpdatedNotification = { "diff": string; "threadId": string; "turnId": string };
|
||||
export type TurnError = { "additionalDetails"?: string | null; "codexErrorInfo"?: CodexErrorInfo | null; "message": string; "misalignment"?: MisalignmentErrorDetails | null };
|
||||
export type TurnInterruptParams = { "threadId": string; "turnId": string };
|
||||
export type TurnInterruptResponse = Record<string, never>;
|
||||
export type TurnItemsView = "notLoaded" | "summary" | "full";
|
||||
export type TurnPlanStep = { "status": TurnPlanStepStatus; "step": string };
|
||||
export type TurnPlanStepStatus = "pending" | "inProgress" | "completed";
|
||||
export type TurnPlanUpdatedNotification = { "explanation"?: string | null; "plan": Array<TurnPlanStep>; "threadId": string; "turnId": string };
|
||||
export type TurnStartParams = { "approvalPolicy"?: AskForApproval | null; "approvalsReviewer"?: ApprovalsReviewer | null; "clientUserMessageId"?: string | null; "cwd"?: string | null; "disabledPluginIds"?: Array<string> | null; "effort"?: ReasoningEffort | null; "input": Array<UserInput>; "model"?: string | null; "outputSchema"?: unknown; "parentTurnId"?: string | null; "personality"?: Personality | null; "rootTurnId"?: string | null; "sandboxPolicy"?: SandboxPolicy | null; "serviceTier"?: string | null; "serviceTierForTurn"?: string | null; "summary"?: ReasoningSummary | null; "threadId": string; "toolOutput"?: TurnToolOutput | null; "turnTrigger"?: string | null };
|
||||
export type TurnStartResponse = { "turn": Turn };
|
||||
export type TurnStartedNotification = { "threadId": string; "turn": Turn };
|
||||
export type TurnStatus = "completed" | "interrupted" | "failed" | "inProgress";
|
||||
export type TurnSteerParams = { "clientUserMessageId"?: string | null; "expectedTurnId": string; "input": Array<UserInput>; "threadId": string };
|
||||
export type TurnSteerResponse = { "turnId": string };
|
||||
export type TurnToolOutput = { "name": string; "namespace"?: string | null; "output": FunctionCallOutputBody; "retain"?: boolean };
|
||||
export type UserInput = { "text": string; "text_elements"?: Array<TextElement>; "type": "text" } | { "detail"?: ImageDetail | null; "type": "image" } & UserInputImageKind | { "detail"?: ImageDetail | null; "path": string; "type": "localImage" } | { "url": string; "type": "audio" } | { "path": string; "type": "localAudio" } | { "name": string; "path": string; "type": "skill" } | { "name": string; "path": string; "type": "mention" };
|
||||
export type UserInputImageKind = { "url": string } | { "fileId": string };
|
||||
export type Verbosity = "low" | "medium" | "high";
|
||||
export type WebSearchAction = { "queries"?: Array<string> | null; "query"?: string | null; "type": "search" } | { "url"?: string | null; "type": "openPage" } | { "pattern"?: string | null; "url"?: string | null; "type": "findInPage" } | { "type": "other" };
|
||||
export type WebSearchContextSize = "low" | "medium" | "high";
|
||||
export type WebSearchLocation = { "city"?: string | null; "country"?: string | null; "region"?: string | null; "timezone"?: string | null };
|
||||
export type WebSearchMode = "disabled" | "cached" | "indexed" | "live";
|
||||
export type WebSearchToolConfig = { "allowed_domains"?: Array<string> | null; "context_size"?: WebSearchContextSize | null; "location"?: WebSearchLocation | null };
|
||||
export type WriteStatus = "ok" | "okOverridden";
|
||||
export type ClientRequestMap = {
|
||||
"initialize": { params: InitializeParams; result: InitializeResponse };
|
||||
"thread/start": { params: ThreadStartParams; result: ThreadStartResponse };
|
||||
"thread/resume": { params: ThreadResumeParams; result: ThreadResumeResponse };
|
||||
"thread/fork": { params: ThreadForkParams; result: ThreadForkResponse };
|
||||
"thread/archive": { params: ThreadArchiveParams; result: ThreadArchiveResponse };
|
||||
"thread/delete": { params: ThreadDeleteParams; result: ThreadDeleteResponse };
|
||||
"thread/unsubscribe": { params: ThreadUnsubscribeParams; result: ThreadUnsubscribeResponse };
|
||||
"thread/name/set": { params: ThreadSetNameParams; result: ThreadSetNameResponse };
|
||||
"thread/goal/set": { params: ThreadGoalSetParams; result: ThreadGoalSetResponse };
|
||||
"thread/goal/get": { params: ThreadGoalGetParams; result: ThreadGoalGetResponse };
|
||||
"thread/goal/clear": { params: ThreadGoalClearParams; result: ThreadGoalClearResponse };
|
||||
"thread/unarchive": { params: ThreadUnarchiveParams; result: ThreadUnarchiveResponse };
|
||||
"thread/compact/start": { params: ThreadCompactStartParams; result: ThreadCompactStartResponse };
|
||||
"thread/shellCommand": { params: ThreadShellCommandParams; result: ThreadShellCommandResponse };
|
||||
"thread/revert": { params: ThreadRevertParams; result: ThreadRevertResponse };
|
||||
"thread/list": { params: ThreadListParams; result: ThreadListResponse };
|
||||
"thread/loaded/list": { params: ThreadLoadedListParams; result: ThreadLoadedListResponse };
|
||||
"thread/read": { params: ThreadReadParams; result: ThreadReadResponse };
|
||||
"thread/turns/list": { params: ThreadTurnsListParams; result: ThreadTurnsListResponse };
|
||||
"thread/items/list": { params: ThreadItemsListParams; result: ThreadItemsListResponse };
|
||||
"skills/list": { params: SkillsListParams; result: SkillsListResponse };
|
||||
"hooks/list": { params: HooksListParams; result: HooksListResponse };
|
||||
"fs/readFile": { params: FsReadFileParams; result: FsReadFileResponse };
|
||||
"fs/writeFile": { params: FsWriteFileParams; result: FsWriteFileResponse };
|
||||
"fs/createDirectory": { params: FsCreateDirectoryParams; result: FsCreateDirectoryResponse };
|
||||
"fs/getMetadata": { params: FsGetMetadataParams; result: FsGetMetadataResponse };
|
||||
"fs/readDirectory": { params: FsReadDirectoryParams; result: FsReadDirectoryResponse };
|
||||
"fs/remove": { params: FsRemoveParams; result: FsRemoveResponse };
|
||||
"fs/copy": { params: FsCopyParams; result: FsCopyResponse };
|
||||
"fs/watch": { params: FsWatchParams; result: FsWatchResponse };
|
||||
"fs/unwatch": { params: FsUnwatchParams; result: FsUnwatchResponse };
|
||||
"turn/start": { params: TurnStartParams; result: TurnStartResponse };
|
||||
"turn/steer": { params: TurnSteerParams; result: TurnSteerResponse };
|
||||
"turn/interrupt": { params: TurnInterruptParams; result: TurnInterruptResponse };
|
||||
"review/start": { params: ReviewStartParams; result: ReviewStartResponse };
|
||||
"model/list": { params: ModelListParams; result: ModelListResponse };
|
||||
"mcpServerStatus/list": { params: ListMcpServerStatusParams; result: ListMcpServerStatusResponse };
|
||||
"command/exec": { params: CommandExecParams; result: CommandExecResponse };
|
||||
"command/exec/write": { params: CommandExecWriteParams; result: CommandExecWriteResponse };
|
||||
"command/exec/terminate": { params: CommandExecTerminateParams; result: CommandExecTerminateResponse };
|
||||
"command/exec/resize": { params: CommandExecResizeParams; result: CommandExecResizeResponse };
|
||||
"config/read": { params: ConfigReadParams; result: ConfigReadResponse };
|
||||
"config/value/write": { params: ConfigValueWriteParams; result: ConfigWriteResponse };
|
||||
"config/batchWrite": { params: ConfigBatchWriteParams; result: ConfigWriteResponse };
|
||||
};
|
||||
export type ClientNotificationMap = {
|
||||
"initialized": { params: undefined };
|
||||
};
|
||||
export type ServerRequestMap = {
|
||||
"item/commandExecution/requestApproval": { params: CommandExecutionRequestApprovalParams; result: CommandExecutionRequestApprovalResponse };
|
||||
"item/fileChange/requestApproval": { params: FileChangeRequestApprovalParams; result: FileChangeRequestApprovalResponse };
|
||||
"item/tool/requestUserInput": { params: ToolRequestUserInputParams; result: ToolRequestUserInputResponse };
|
||||
"item/permissions/requestApproval": { params: PermissionsRequestApprovalParams; result: PermissionsRequestApprovalResponse };
|
||||
"item/tool/call": { params: DynamicToolCallParams; result: DynamicToolCallResponse };
|
||||
};
|
||||
export type ServerNotificationMap = {
|
||||
"error": { params: ErrorNotification };
|
||||
"thread/started": { params: ThreadStartedNotification };
|
||||
"thread/status/changed": { params: ThreadStatusChangedNotification };
|
||||
"thread/archived": { params: ThreadArchivedNotification };
|
||||
"thread/deleted": { params: ThreadDeletedNotification };
|
||||
"thread/unarchived": { params: ThreadUnarchivedNotification };
|
||||
"thread/closed": { params: ThreadClosedNotification };
|
||||
"thread/reverted": { params: ThreadRevertedNotification };
|
||||
"skills/changed": { params: SkillsChangedNotification };
|
||||
"thread/name/updated": { params: ThreadNameUpdatedNotification };
|
||||
"thread/goal/updated": { params: ThreadGoalUpdatedNotification };
|
||||
"thread/goal/cleared": { params: ThreadGoalClearedNotification };
|
||||
"thread/tokenUsage/updated": { params: ThreadTokenUsageUpdatedNotification };
|
||||
"turn/started": { params: TurnStartedNotification };
|
||||
"turn/completed": { params: TurnCompletedNotification };
|
||||
"turn/diff/updated": { params: TurnDiffUpdatedNotification };
|
||||
"turn/plan/updated": { params: TurnPlanUpdatedNotification };
|
||||
"item/started": { params: ItemStartedNotification };
|
||||
"item/completed": { params: ItemCompletedNotification };
|
||||
"item/agentMessage/delta": { params: AgentMessageDeltaNotification };
|
||||
"item/plan/delta": { params: PlanDeltaNotification };
|
||||
"command/exec/outputDelta": { params: CommandExecOutputDeltaNotification };
|
||||
"item/commandExecution/outputDelta": { params: CommandExecutionOutputDeltaNotification };
|
||||
"item/commandExecution/terminalInteraction": { params: TerminalInteractionNotification };
|
||||
"item/fileChange/outputDelta": { params: FileChangeOutputDeltaNotification };
|
||||
"item/fileChange/patchUpdated": { params: FileChangePatchUpdatedNotification };
|
||||
"serverRequest/resolved": { params: ServerRequestResolvedNotification };
|
||||
"item/mcpToolCall/progress": { params: McpToolCallProgressNotification };
|
||||
"fs/changed": { params: FsChangedNotification };
|
||||
"item/reasoning/summaryTextDelta": { params: ReasoningSummaryTextDeltaNotification };
|
||||
"item/reasoning/summaryPartAdded": { params: ReasoningSummaryPartAddedNotification };
|
||||
"item/reasoning/textDelta": { params: ReasoningTextDeltaNotification };
|
||||
"thread/compacted": { params: ContextCompactedNotification };
|
||||
};
|
||||
@@ -0,0 +1,33 @@
|
||||
#[path = "../codegen/mod.rs"]
|
||||
mod codegen;
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let arguments: Vec<String> = std::env::args().skip(1).collect();
|
||||
if !matches!(arguments.as_slice(), [mode] if mode == "--write" || mode == "--check") {
|
||||
return Err("use --write or --check against the contract manifest outputs".into());
|
||||
}
|
||||
let write = arguments[0] == "--write";
|
||||
let package = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
||||
let manifest = std::fs::read_to_string(package.join(codegen::MANIFEST_PATH))?;
|
||||
let pinned = std::fs::read_to_string(package.join(codegen::PINNED_SCHEMA_PATH))?;
|
||||
let outputs = codegen::generate(&manifest, &pinned)?;
|
||||
let mut drift = Vec::new();
|
||||
for (path, text) in outputs.files {
|
||||
let target = package.join(path);
|
||||
if write {
|
||||
if let Some(parent) = target.parent() {
|
||||
std::fs::create_dir_all(parent)?;
|
||||
}
|
||||
std::fs::write(&target, text)?;
|
||||
} else if std::fs::read_to_string(&target).ok().as_deref() != Some(text.as_str()) {
|
||||
drift.push(path);
|
||||
}
|
||||
}
|
||||
if drift.is_empty() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(format!("generated contract drift: {}", drift.join(", ")).into())
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,7 +1,10 @@
|
||||
pub mod lifecycle;
|
||||
#[rustfmt::skip]
|
||||
pub mod methods;
|
||||
pub mod rpc;
|
||||
pub mod runtime;
|
||||
#[rustfmt::skip]
|
||||
pub mod v2;
|
||||
|
||||
#[cfg(feature = "schema")]
|
||||
pub mod schema;
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,6 +1,8 @@
|
||||
use serde::{Deserialize, Deserializer, Serialize};
|
||||
use serde_json::Value;
|
||||
|
||||
pub use crate::v2::{ThreadExecutor, ThreadPlacement as ThreadEnvironment};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum RequestId {
|
||||
@@ -149,24 +151,6 @@ pub struct ThreadListItem {
|
||||
pub environment: Option<ThreadEnvironment>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ThreadEnvironment {
|
||||
pub id: String,
|
||||
pub provider_kind: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ThreadExecutor {
|
||||
pub kind: String,
|
||||
pub agent_id: String,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub agent_name: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
|
||||
#[serde(tag = "type", rename_all = "camelCase")]
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,633 @@
|
||||
#[path = "../src/codegen/mod.rs"]
|
||||
mod codegen;
|
||||
|
||||
use codewhale_app_server_protocol::methods::{
|
||||
self, ClientNotificationMethod, ClientRequestMethod, Disposition, ServerNotificationMethod,
|
||||
ServerRequestMethod, Tier, error_code,
|
||||
};
|
||||
use codewhale_app_server_protocol::rpc::RpcCall;
|
||||
use codewhale_app_server_protocol::v2;
|
||||
use serde_json::{Map, Value, json};
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
|
||||
const MANIFEST: &str = include_str!("../contract/codex-v2.json");
|
||||
const PINNED: &str = include_str!("pinned/codex/codex_app_server_protocol.schemas.json");
|
||||
const COMMON: &str = include_str!("pinned/codex/common.rs");
|
||||
|
||||
fn manifest() -> Value {
|
||||
serde_json::from_str(MANIFEST).unwrap()
|
||||
}
|
||||
|
||||
fn pinned() -> Value {
|
||||
serde_json::from_str(PINNED).unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn generated_outputs_have_not_drifted_from_the_manifest_and_pinned_export() {
|
||||
let package = std::path::Path::new(env!("CARGO_MANIFEST_DIR"));
|
||||
let read = |path: &str| std::fs::read_to_string(package.join(path)).unwrap();
|
||||
assert_eq!(read(codegen::MANIFEST_PATH), MANIFEST);
|
||||
assert_eq!(read(codegen::PINNED_SCHEMA_PATH), PINNED);
|
||||
let outputs = codegen::generate(MANIFEST, PINNED).unwrap();
|
||||
let committed: BTreeMap<&str, &str> = BTreeMap::from([
|
||||
("src/methods.rs", include_str!("../src/methods.rs")),
|
||||
("src/v2.rs", include_str!("../src/v2.rs")),
|
||||
("METHODS.md", include_str!("../METHODS.md")),
|
||||
(
|
||||
"schema/json/codex-v2.schema.json",
|
||||
include_str!("../schema/json/codex-v2.schema.json"),
|
||||
),
|
||||
(
|
||||
"schema/typescript/codex-v2.d.ts",
|
||||
include_str!("../schema/typescript/codex-v2.d.ts"),
|
||||
),
|
||||
]);
|
||||
assert_eq!(outputs.files.len(), committed.len());
|
||||
for (path, text) in outputs.files {
|
||||
assert!(
|
||||
committed[path] == text,
|
||||
"{path} drifted; run cargo run -p codewhale-app-server-protocol --bin codewhale-codex-contract -- --write"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const KINDS: [(&str, &str, &str); 4] = [
|
||||
(
|
||||
"clientRequests",
|
||||
include_str!("pinned/codex/ClientRequest.ts"),
|
||||
"ClientRequest",
|
||||
),
|
||||
(
|
||||
"clientNotifications",
|
||||
include_str!("pinned/codex/ClientNotification.ts"),
|
||||
"ClientNotification",
|
||||
),
|
||||
(
|
||||
"serverRequests",
|
||||
include_str!("pinned/codex/ServerRequest.ts"),
|
||||
"ServerRequest",
|
||||
),
|
||||
(
|
||||
"serverNotifications",
|
||||
include_str!("pinned/codex/ServerNotification.ts"),
|
||||
"ServerNotification",
|
||||
),
|
||||
];
|
||||
|
||||
fn pinned_ts_methods(source: &str) -> Vec<&str> {
|
||||
source
|
||||
.split("\"method\": \"")
|
||||
.skip(1)
|
||||
.map(|rest| rest.split('"').next().unwrap())
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn typed(kind: &str) -> Vec<(&'static str, Tier)> {
|
||||
match kind {
|
||||
"clientRequests" => ClientRequestMethod::ALL
|
||||
.iter()
|
||||
.map(|method| (method.method(), method.tier()))
|
||||
.collect(),
|
||||
"clientNotifications" => ClientNotificationMethod::ALL
|
||||
.iter()
|
||||
.map(|method| (method.method(), method.tier()))
|
||||
.collect(),
|
||||
"serverRequests" => ServerRequestMethod::ALL
|
||||
.iter()
|
||||
.map(|method| (method.method(), method.tier()))
|
||||
.collect(),
|
||||
_ => ServerNotificationMethod::ALL
|
||||
.iter()
|
||||
.map(|method| (method.method(), method.tier()))
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
|
||||
fn disposition(kind: &str, method: &str) -> Option<Disposition> {
|
||||
match kind {
|
||||
"clientRequests" => methods::client_request_disposition(method),
|
||||
"clientNotifications" => methods::client_notification_disposition(method),
|
||||
"serverRequests" => methods::server_request_disposition(method),
|
||||
_ => methods::server_notification_disposition(method),
|
||||
}
|
||||
}
|
||||
|
||||
fn upstream_response(method: &str) -> String {
|
||||
let marker = format!("=> \"{method}\" {{");
|
||||
let start = COMMON
|
||||
.find(&marker)
|
||||
.unwrap_or_else(|| panic!("pinned common.rs has no {method}"));
|
||||
let rest = &COMMON[start..];
|
||||
let end = rest.find("\n },").unwrap();
|
||||
let body = &rest[..end];
|
||||
let response = body
|
||||
.split("response: ")
|
||||
.nth(1)
|
||||
.unwrap_or_else(|| panic!("{method} has no response in common.rs"));
|
||||
let ty = response.split(',').next().unwrap().trim();
|
||||
ty.rsplit("::").next().unwrap().to_owned()
|
||||
}
|
||||
|
||||
fn pinned_union_params(pinned: &Value, union: &str, method: &str) -> Option<String> {
|
||||
pinned["definitions"][union]["oneOf"]
|
||||
.as_array()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.find(|variant| variant["properties"]["method"]["enum"][0] == method)
|
||||
.unwrap_or_else(|| panic!("pinned {union} has no {method}"))["properties"]["params"]["$ref"]
|
||||
.as_str()
|
||||
.map(|reference| reference.rsplit('/').next().unwrap().to_owned())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn manifest_inventory_matches_the_pinned_method_sets_and_typed_enums() {
|
||||
let manifest = manifest();
|
||||
let pinned = pinned();
|
||||
let mut totals: BTreeMap<String, usize> = BTreeMap::new();
|
||||
for (kind, ts, union) in KINDS {
|
||||
let pinned_methods = pinned_ts_methods(ts);
|
||||
let pinned_set: BTreeSet<&str> = pinned_methods.iter().copied().collect();
|
||||
assert_eq!(pinned_methods.len(), pinned_set.len(), "{kind}: duplicate");
|
||||
let rows = manifest["methods"][kind].as_array().unwrap();
|
||||
let mut seen = BTreeSet::new();
|
||||
for row in rows {
|
||||
let method = row["method"].as_str().unwrap();
|
||||
let tier = row["tier"].as_str().unwrap();
|
||||
let status = row["status"].as_str().unwrap();
|
||||
assert!(seen.insert(method), "{kind}: {method} twice");
|
||||
assert!(pinned_set.contains(method), "{kind}: {method} not pinned");
|
||||
assert!(matches!(tier, "A" | "B" | "C" | "undecided"));
|
||||
assert!(matches!(
|
||||
status,
|
||||
"implemented" | "partial" | "missing" | "method-not-found" | "never-sent"
|
||||
));
|
||||
assert_eq!(
|
||||
tier == "C",
|
||||
matches!(status, "method-not-found" | "never-sent"),
|
||||
"{kind}: {method} status {status} does not fit tier {tier}"
|
||||
);
|
||||
assert_eq!(
|
||||
row.get("recommended").is_some(),
|
||||
tier == "undecided",
|
||||
"{kind}: {method} recommendation belongs to undecided rows only"
|
||||
);
|
||||
let expected = match tier {
|
||||
"A" => Disposition::Selected(Tier::A),
|
||||
"B" => Disposition::Selected(Tier::B),
|
||||
"C" => Disposition::C,
|
||||
_ => Disposition::Undecided,
|
||||
};
|
||||
assert_eq!(
|
||||
disposition(kind, method),
|
||||
Some(expected),
|
||||
"{kind}: {method}"
|
||||
);
|
||||
*totals.entry(format!("{kind}/{tier}")).or_default() += 1;
|
||||
if matches!(tier, "A" | "B") {
|
||||
let params = pinned_union_params(&pinned, union, method);
|
||||
assert_eq!(
|
||||
row["params"].as_str().map(str::to_owned),
|
||||
params,
|
||||
"{kind}: {method} params disagree with the pinned {union} union"
|
||||
);
|
||||
if matches!(kind, "clientRequests" | "serverRequests") {
|
||||
assert_eq!(
|
||||
row["response"].as_str().unwrap(),
|
||||
upstream_response(method),
|
||||
"{kind}: {method} result disagrees with pinned common.rs"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
assert_eq!(
|
||||
seen, pinned_set,
|
||||
"{kind}: manifest and pinned export differ"
|
||||
);
|
||||
let typed_set: BTreeSet<&str> = typed(kind).iter().map(|(method, _)| *method).collect();
|
||||
let selected: BTreeSet<&str> = rows
|
||||
.iter()
|
||||
.filter(|row| matches!(row["tier"].as_str(), Some("A" | "B")))
|
||||
.map(|row| row["method"].as_str().unwrap())
|
||||
.collect();
|
||||
assert_eq!(
|
||||
typed_set, selected,
|
||||
"{kind}: typed enum and selected rows differ"
|
||||
);
|
||||
}
|
||||
eprintln!("inventory totals: {totals:?}");
|
||||
assert_eq!(totals["clientRequests/A"] + totals["clientRequests/B"], 44);
|
||||
assert_eq!(totals["serverRequests/A"], 5);
|
||||
assert_eq!(
|
||||
totals["serverNotifications/A"] + totals["serverNotifications/B"],
|
||||
33
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn typed_methods_round_trip_their_wire_names() {
|
||||
for method in ClientRequestMethod::ALL {
|
||||
assert_eq!(
|
||||
ClientRequestMethod::from_method(method.method()),
|
||||
Some(*method)
|
||||
);
|
||||
let wire = serde_json::to_value(method).unwrap();
|
||||
assert_eq!(wire, method.method());
|
||||
assert_eq!(
|
||||
serde_json::from_value::<ClientRequestMethod>(wire).unwrap(),
|
||||
*method
|
||||
);
|
||||
}
|
||||
for method in ServerNotificationMethod::ALL {
|
||||
assert_eq!(
|
||||
ServerNotificationMethod::from_method(method.method()),
|
||||
Some(*method)
|
||||
);
|
||||
}
|
||||
for method in ServerRequestMethod::ALL {
|
||||
assert_eq!(
|
||||
ServerRequestMethod::from_method(method.method()),
|
||||
Some(*method)
|
||||
);
|
||||
}
|
||||
assert!(serde_json::from_value::<ClientRequestMethod>("feedback/upload".into()).is_err());
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
enum Mode {
|
||||
Full,
|
||||
Minimal,
|
||||
}
|
||||
|
||||
struct Instances {
|
||||
definitions: Value,
|
||||
}
|
||||
|
||||
const DEPTH: usize = 5;
|
||||
|
||||
impl Instances {
|
||||
fn new() -> Self {
|
||||
let mut definitions = pinned()["definitions"].clone();
|
||||
definitions["codewhale"] = manifest()["extensions"]["definitions"].clone();
|
||||
Instances { definitions }
|
||||
}
|
||||
|
||||
fn resolve(&self, reference: &str) -> &Value {
|
||||
let mut node = &self.definitions;
|
||||
for part in reference.trim_start_matches("#/definitions/").split('/') {
|
||||
node = &node[part];
|
||||
}
|
||||
assert!(!node.is_null(), "unresolved {reference}");
|
||||
node
|
||||
}
|
||||
|
||||
fn lookup(&self, name: &str) -> Option<(String, &Value)> {
|
||||
for namespace in ["v2/", "", "codewhale/"] {
|
||||
let reference = format!("#/definitions/{namespace}{name}");
|
||||
let mut node = &self.definitions;
|
||||
for part in reference.trim_start_matches("#/definitions/").split('/') {
|
||||
node = &node[part];
|
||||
}
|
||||
if !node.is_null() && !(namespace.is_empty() && name == "v2") {
|
||||
return Some((reference, node));
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn validator(&self, reference: &str) -> jsonschema::Validator {
|
||||
let schema = json!({
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"definitions": self.definitions,
|
||||
"allOf": [{"$ref": reference}],
|
||||
});
|
||||
jsonschema::validator_for(&schema).unwrap()
|
||||
}
|
||||
|
||||
fn members<'a>(&self, schema: &'a Value) -> Option<&'a Vec<Value>> {
|
||||
schema["oneOf"].as_array().or(schema["anyOf"].as_array())
|
||||
}
|
||||
|
||||
fn value(&self, schema: &Value, mode: Mode, depth: usize) -> Value {
|
||||
if schema == &Value::Bool(true) {
|
||||
return json!({"any": [1, "x"]});
|
||||
}
|
||||
if let Some(reference) = schema["$ref"].as_str() {
|
||||
return self.value(self.resolve(reference), mode, depth + 1);
|
||||
}
|
||||
if let Some(all) = schema["allOf"].as_array() {
|
||||
return self.value(&all[0], mode, depth);
|
||||
}
|
||||
if let Some(members) = self.members(schema) {
|
||||
let member = members
|
||||
.iter()
|
||||
.find(|member| member["type"] != "null")
|
||||
.unwrap();
|
||||
return self.member_value(schema, member, mode, depth);
|
||||
}
|
||||
if let Some(values) = schema["enum"].as_array() {
|
||||
return values[0].clone();
|
||||
}
|
||||
let ty = match &schema["type"] {
|
||||
Value::String(ty) => ty.clone(),
|
||||
Value::Array(list) => list
|
||||
.iter()
|
||||
.find(|ty| *ty != "null")
|
||||
.and_then(Value::as_str)
|
||||
.unwrap()
|
||||
.to_owned(),
|
||||
_ => return json!({"any": [1, "x"]}),
|
||||
};
|
||||
match ty.as_str() {
|
||||
"string" => json!("s"),
|
||||
"integer" => json!(1),
|
||||
"boolean" => json!(true),
|
||||
"array" => {
|
||||
if mode == Mode::Full && depth < DEPTH {
|
||||
json!([self.value(&schema["items"], mode, depth + 1)])
|
||||
} else {
|
||||
json!([])
|
||||
}
|
||||
}
|
||||
"object" => self.object(schema, mode, depth),
|
||||
other => panic!("unsupported type {other}"),
|
||||
}
|
||||
}
|
||||
|
||||
fn object(&self, schema: &Value, mode: Mode, depth: usize) -> Value {
|
||||
let mut out = Map::new();
|
||||
let required: BTreeSet<&str> = schema["required"]
|
||||
.as_array()
|
||||
.map(|names| names.iter().filter_map(Value::as_str).collect())
|
||||
.unwrap_or_default();
|
||||
let full = mode == Mode::Full && depth < DEPTH;
|
||||
if let Some(properties) = schema["properties"].as_object() {
|
||||
for (name, property) in properties {
|
||||
if required.contains(name.as_str()) || full {
|
||||
out.insert(name.clone(), self.value(property, mode, depth + 1));
|
||||
} else if let Some(default) =
|
||||
property.get("default").filter(|value| !value.is_null())
|
||||
{
|
||||
out.insert(name.clone(), default.clone());
|
||||
}
|
||||
}
|
||||
if full && schema["additionalProperties"] == true {
|
||||
out.insert("x-additional".into(), json!(1));
|
||||
}
|
||||
} else if full {
|
||||
match &schema["additionalProperties"] {
|
||||
Value::Object(_) => {
|
||||
out.insert(
|
||||
"k".into(),
|
||||
self.value(&schema["additionalProperties"], mode, depth + 1),
|
||||
);
|
||||
}
|
||||
Value::Bool(true) => {
|
||||
out.insert("k".into(), json!(1));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
Value::Object(out)
|
||||
}
|
||||
|
||||
fn member_value(&self, schema: &Value, member: &Value, mode: Mode, depth: usize) -> Value {
|
||||
let mut value = self.value(member, mode, depth);
|
||||
if schema.get("properties").is_some()
|
||||
&& let (Value::Object(base), Value::Object(extra)) =
|
||||
(self.object(schema, mode, depth), &mut value)
|
||||
{
|
||||
for (key, field) in base {
|
||||
extra.entry(key).or_insert(field);
|
||||
}
|
||||
}
|
||||
value
|
||||
}
|
||||
|
||||
fn cases(&self, schema: &Value) -> Vec<(String, Value)> {
|
||||
let mut cases = vec![
|
||||
("full".to_owned(), self.value(schema, Mode::Full, 0)),
|
||||
("minimal".to_owned(), self.value(schema, Mode::Minimal, 0)),
|
||||
];
|
||||
if let Some(members) = self.members(schema) {
|
||||
for (index, member) in members.iter().enumerate() {
|
||||
if member["type"] == "null" {
|
||||
continue;
|
||||
}
|
||||
if let Some(values) = member["enum"].as_array() {
|
||||
for value in values {
|
||||
cases.push((format!("member {index} {value}"), value.clone()));
|
||||
}
|
||||
} else {
|
||||
cases.push((
|
||||
format!("member {index} full"),
|
||||
self.member_value(schema, member, Mode::Full, 0),
|
||||
));
|
||||
cases.push((
|
||||
format!("member {index} minimal"),
|
||||
self.member_value(schema, member, Mode::Minimal, 0),
|
||||
));
|
||||
}
|
||||
}
|
||||
} else if let Some(values) = schema["enum"].as_array() {
|
||||
for value in values {
|
||||
cases.push((format!("enum {value}"), value.clone()));
|
||||
}
|
||||
}
|
||||
cases
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn every_selected_shape_round_trips_against_the_pinned_schema() {
|
||||
let instances = Instances::new();
|
||||
let mut checked = 0usize;
|
||||
let mut typed = 0usize;
|
||||
let mut inline = Vec::new();
|
||||
for name in v2::TYPE_NAMES {
|
||||
let Some((reference, schema)) = instances.lookup(name) else {
|
||||
inline.push(*name);
|
||||
continue;
|
||||
};
|
||||
typed += 1;
|
||||
let validator = instances.validator(&reference);
|
||||
for (case, value) in instances.cases(schema) {
|
||||
assert!(
|
||||
validator.is_valid(&value),
|
||||
"{name} {case}: generated instance is not valid upstream: {value}"
|
||||
);
|
||||
let again = v2::round_trip(name, value.clone())
|
||||
.unwrap()
|
||||
.unwrap_or_else(|error| {
|
||||
panic!("{name} {case}: typed decode failed: {error}: {value}")
|
||||
});
|
||||
assert!(
|
||||
validator.is_valid(&again),
|
||||
"{name} {case}: typed output is not valid upstream: {again}"
|
||||
);
|
||||
assert_eq!(
|
||||
again, value,
|
||||
"{name} {case}: typed round trip changed the value"
|
||||
);
|
||||
checked += 1;
|
||||
}
|
||||
}
|
||||
eprintln!(
|
||||
"typed definitions with a pinned schema: {typed}; inline helper types: {}; round-tripped instances: {checked}",
|
||||
inline.len()
|
||||
);
|
||||
assert!(typed > 250 && checked > typed * 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn typed_envelopes_match_the_pinned_unions_without_jsonrpc() {
|
||||
let instances = Instances::new();
|
||||
let client_request = instances.validator("#/definitions/ClientRequest");
|
||||
let server_request = instances.validator("#/definitions/ServerRequest");
|
||||
let server_notification = instances.validator("#/definitions/ServerNotification");
|
||||
let client_notification = instances.validator("#/definitions/ClientNotification");
|
||||
let mut counts = BTreeMap::new();
|
||||
for method in ClientRequestMethod::ALL {
|
||||
let (_, params_schema) = instances.lookup(method.params_type().unwrap()).unwrap();
|
||||
let (_, result_schema) = instances.lookup(method.response_type()).unwrap();
|
||||
for mode in [Mode::Full, Mode::Minimal] {
|
||||
let params = instances.value(params_schema, mode, 0);
|
||||
let envelope = json!({"id": 7, "method": method.method(), "params": params});
|
||||
assert!(client_request.is_valid(&envelope), "{method}: {envelope}");
|
||||
let call: RpcCall = serde_json::from_value(envelope).unwrap();
|
||||
let typed: v2::ClientRequestParams =
|
||||
serde_json::from_value(json!({"method": call.method, "params": call.params}))
|
||||
.unwrap_or_else(|error| panic!("{method}: {error}"));
|
||||
assert_eq!(typed.method(), *method);
|
||||
let encoded = serde_json::to_value(&typed).unwrap();
|
||||
assert!(encoded.get("jsonrpc").is_none());
|
||||
assert_eq!(encoded["params"], params);
|
||||
let result = instances.value(result_schema, mode, 0);
|
||||
let response = v2::ClientResponse::decode(*method, result.clone()).unwrap();
|
||||
assert_eq!(response.method(), *method);
|
||||
assert_eq!(serde_json::to_value(&response).unwrap(), result);
|
||||
}
|
||||
*counts.entry("client requests").or_insert(0) += 1;
|
||||
}
|
||||
for method in ServerRequestMethod::ALL {
|
||||
let (_, params_schema) = instances.lookup(method.params_type().unwrap()).unwrap();
|
||||
let (_, result_schema) = instances.lookup(method.response_type()).unwrap();
|
||||
for mode in [Mode::Full, Mode::Minimal] {
|
||||
let params = instances.value(params_schema, mode, 0);
|
||||
let typed: v2::ServerRequestParams =
|
||||
serde_json::from_value(json!({"method": method.method(), "params": params}))
|
||||
.unwrap();
|
||||
let mut encoded = serde_json::to_value(&typed).unwrap();
|
||||
encoded["id"] = json!("server-1");
|
||||
assert!(server_request.is_valid(&encoded), "{method}: {encoded}");
|
||||
let result = instances.value(result_schema, mode, 0);
|
||||
let answer = v2::ServerRequestResponse::decode(*method, result.clone()).unwrap();
|
||||
assert_eq!(answer.method(), *method);
|
||||
assert_eq!(serde_json::to_value(&answer).unwrap(), result);
|
||||
}
|
||||
*counts.entry("server requests").or_insert(0) += 1;
|
||||
}
|
||||
for method in ServerNotificationMethod::ALL {
|
||||
let (_, params_schema) = instances.lookup(method.params_type().unwrap()).unwrap();
|
||||
for mode in [Mode::Full, Mode::Minimal] {
|
||||
let params = instances.value(params_schema, mode, 0);
|
||||
let typed: v2::ServerNotification =
|
||||
serde_json::from_value(json!({"method": method.method(), "params": params}))
|
||||
.unwrap();
|
||||
assert_eq!(typed.method(), *method);
|
||||
let encoded = serde_json::to_value(&typed).unwrap();
|
||||
assert!(encoded.get("jsonrpc").is_none() && encoded.get("id").is_none());
|
||||
assert!(
|
||||
server_notification.is_valid(&encoded),
|
||||
"{method}: {encoded}"
|
||||
);
|
||||
}
|
||||
*counts.entry("server notifications").or_insert(0) += 1;
|
||||
}
|
||||
let initialized = serde_json::to_value(v2::ClientNotification::Initialized).unwrap();
|
||||
assert_eq!(initialized, json!({"method": "initialized"}));
|
||||
assert!(client_notification.is_valid(&initialized));
|
||||
eprintln!("typed envelopes checked: {counts:?}");
|
||||
assert_eq!(counts["client requests"], 44);
|
||||
assert_eq!(counts["server requests"], 5);
|
||||
assert_eq!(counts["server notifications"], 33);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tier_c_and_undecided_methods_are_method_not_found_and_never_typed() {
|
||||
let manifest = manifest();
|
||||
let mut refused = 0;
|
||||
for (kind, _, _) in KINDS {
|
||||
for row in manifest["methods"][kind].as_array().unwrap() {
|
||||
if matches!(row["tier"].as_str(), Some("A" | "B")) {
|
||||
continue;
|
||||
}
|
||||
let method = row["method"].as_str().unwrap();
|
||||
assert!(!disposition(kind, method).unwrap().selected());
|
||||
let envelope = json!({"method": method, "params": {}});
|
||||
match kind {
|
||||
"clientRequests" => {
|
||||
assert_eq!(
|
||||
methods::unselected_request_error(method),
|
||||
Some(error_code::METHOD_NOT_FOUND)
|
||||
);
|
||||
assert!(ClientRequestMethod::from_method(method).is_none());
|
||||
assert!(serde_json::from_value::<v2::ClientRequestParams>(envelope).is_err());
|
||||
}
|
||||
"serverRequests" => {
|
||||
assert!(ServerRequestMethod::from_method(method).is_none());
|
||||
assert!(serde_json::from_value::<v2::ServerRequestParams>(envelope).is_err());
|
||||
}
|
||||
"serverNotifications" => {
|
||||
assert!(ServerNotificationMethod::from_method(method).is_none());
|
||||
assert!(serde_json::from_value::<v2::ServerNotification>(envelope).is_err());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
refused += 1;
|
||||
}
|
||||
}
|
||||
assert_eq!(error_code::METHOD_NOT_FOUND, -32601);
|
||||
assert_eq!(methods::unselected_request_error("thread/start"), None);
|
||||
assert_eq!(
|
||||
methods::unselected_request_error("not/in/the/export"),
|
||||
Some(error_code::METHOD_NOT_FOUND)
|
||||
);
|
||||
eprintln!("unselected methods refused: {refused}");
|
||||
assert!(refused > 100);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn codewhale_extensions_stay_optional_and_valid_upstream() {
|
||||
let instances = Instances::new();
|
||||
let thread = instances.validator("#/definitions/v2/Thread");
|
||||
let (_, schema) = instances.lookup("Thread").unwrap();
|
||||
let mut value = instances.value(schema, Mode::Minimal, 0);
|
||||
let plain: v2::Thread = serde_json::from_value(value.clone()).unwrap();
|
||||
assert!(plain.executor.is_none() && plain.environment.is_none());
|
||||
assert_eq!(serde_json::to_value(&plain).unwrap(), value);
|
||||
value["executor"] = json!({"kind": "acp", "agentId": "claude", "agentName": "Claude"});
|
||||
value["environment"] = json!({"id": "env-1", "providerKind": "remote"});
|
||||
assert!(thread.is_valid(&value));
|
||||
let placed: v2::Thread = serde_json::from_value(value.clone()).unwrap();
|
||||
assert_eq!(placed.executor.as_ref().unwrap().agent_id, "claude");
|
||||
assert_eq!(serde_json::to_value(&placed).unwrap(), value);
|
||||
let refusal = instances.validator("#/definitions/codewhale/CapabilityRefusalData");
|
||||
let data = serde_json::to_value(v2::CapabilityRefusalData {
|
||||
codewhale_refusal: v2::CapabilityRefusal {
|
||||
code: v2::CapabilityRefusalCode::EnvironmentNotSupported,
|
||||
method: Some("turn/start".into()),
|
||||
thread_id: Some("thread-1".into()),
|
||||
},
|
||||
})
|
||||
.unwrap();
|
||||
assert!(refusal.is_valid(&data));
|
||||
assert_eq!(
|
||||
data["codewhaleRefusal"]["code"],
|
||||
"environment_not_supported"
|
||||
);
|
||||
assert_eq!(error_code::CAPABILITY_UNSUPPORTED, -32020);
|
||||
assert!(
|
||||
serde_json::from_value::<v2::CapabilityRefusal>(json!({"code": "no_acp", "token": "x"}))
|
||||
.is_err()
|
||||
);
|
||||
}
|
||||
@@ -1,219 +0,0 @@
|
||||
use codewhale_app_server_protocol::methods::{
|
||||
ClientNotificationMethod, ClientRequestMethod, ServerNotificationMethod, ServerRequestMethod,
|
||||
Tier,
|
||||
};
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
|
||||
const INVENTORY: &str = include_str!("../METHODS.md");
|
||||
|
||||
struct Kind {
|
||||
section: &'static str,
|
||||
pinned: &'static str,
|
||||
typed: Vec<(&'static str, Tier)>,
|
||||
}
|
||||
|
||||
fn kinds() -> [Kind; 4] {
|
||||
fn typed<T: Copy>(
|
||||
all: &[T],
|
||||
method: fn(T) -> &'static str,
|
||||
tier: fn(T) -> Tier,
|
||||
) -> Vec<(&'static str, Tier)> {
|
||||
all.iter()
|
||||
.map(|value| (method(*value), tier(*value)))
|
||||
.collect()
|
||||
}
|
||||
[
|
||||
Kind {
|
||||
section: "Client requests",
|
||||
pinned: include_str!("pinned/codex/ClientRequest.ts"),
|
||||
typed: typed(
|
||||
ClientRequestMethod::ALL,
|
||||
ClientRequestMethod::method,
|
||||
ClientRequestMethod::tier,
|
||||
),
|
||||
},
|
||||
Kind {
|
||||
section: "Client notifications",
|
||||
pinned: include_str!("pinned/codex/ClientNotification.ts"),
|
||||
typed: typed(
|
||||
ClientNotificationMethod::ALL,
|
||||
ClientNotificationMethod::method,
|
||||
ClientNotificationMethod::tier,
|
||||
),
|
||||
},
|
||||
Kind {
|
||||
section: "Server requests",
|
||||
pinned: include_str!("pinned/codex/ServerRequest.ts"),
|
||||
typed: typed(
|
||||
ServerRequestMethod::ALL,
|
||||
ServerRequestMethod::method,
|
||||
ServerRequestMethod::tier,
|
||||
),
|
||||
},
|
||||
Kind {
|
||||
section: "Server notifications",
|
||||
pinned: include_str!("pinned/codex/ServerNotification.ts"),
|
||||
typed: typed(
|
||||
ServerNotificationMethod::ALL,
|
||||
ServerNotificationMethod::method,
|
||||
ServerNotificationMethod::tier,
|
||||
),
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
fn pinned_methods(source: &str) -> Vec<&str> {
|
||||
source
|
||||
.split("\"method\": \"")
|
||||
.skip(1)
|
||||
.map(|rest| rest.split('"').next().expect("method literal"))
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn inventory_rows(section: &str) -> Vec<(String, String, String)> {
|
||||
let heading = format!("## {section}\n");
|
||||
let start = INVENTORY
|
||||
.find(&heading)
|
||||
.unwrap_or_else(|| panic!("inventory has no section {section}"))
|
||||
+ heading.len();
|
||||
INVENTORY[start..]
|
||||
.lines()
|
||||
.take_while(|line| !line.starts_with("## "))
|
||||
.filter(|line| line.starts_with("| `"))
|
||||
.map(|line| {
|
||||
let cells = line
|
||||
.trim_matches('|')
|
||||
.split('|')
|
||||
.map(str::trim)
|
||||
.collect::<Vec<_>>();
|
||||
assert_eq!(cells.len(), 5, "inventory row shape: {line}");
|
||||
(
|
||||
cells[0].trim_matches('`').to_owned(),
|
||||
cells[1].to_owned(),
|
||||
cells[2].to_owned(),
|
||||
)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn typed_methods_inventory_and_pinned_export_agree() {
|
||||
let mut totals = BTreeMap::new();
|
||||
for kind in kinds() {
|
||||
let pinned = pinned_methods(kind.pinned);
|
||||
let pinned_set = pinned.iter().copied().collect::<BTreeSet<_>>();
|
||||
assert_eq!(
|
||||
pinned.len(),
|
||||
pinned_set.len(),
|
||||
"{}: duplicate pinned method",
|
||||
kind.section
|
||||
);
|
||||
|
||||
let rows = inventory_rows(kind.section);
|
||||
let mut inventory = BTreeMap::new();
|
||||
for (method, tier, status) in &rows {
|
||||
assert!(
|
||||
matches!(tier.as_str(), "A" | "B" | "C" | "undecided"),
|
||||
"{}: {method} has tier {tier}",
|
||||
kind.section
|
||||
);
|
||||
assert!(
|
||||
matches!(
|
||||
status.as_str(),
|
||||
"implemented" | "partial" | "missing" | "method-not-found" | "never-sent"
|
||||
),
|
||||
"{}: {method} has status {status}",
|
||||
kind.section
|
||||
);
|
||||
assert_eq!(
|
||||
tier == "C",
|
||||
matches!(status.as_str(), "method-not-found" | "never-sent"),
|
||||
"{}: {method} status {status} does not fit tier {tier}",
|
||||
kind.section
|
||||
);
|
||||
assert!(
|
||||
inventory.insert(method.as_str(), tier.as_str()).is_none(),
|
||||
"{}: {method} listed twice",
|
||||
kind.section
|
||||
);
|
||||
*totals.entry(tier.clone()).or_insert(0usize) += 1;
|
||||
}
|
||||
let inventory_set = inventory.keys().copied().collect::<BTreeSet<_>>();
|
||||
assert_eq!(
|
||||
pinned_set.difference(&inventory_set).collect::<Vec<_>>(),
|
||||
Vec::<&&str>::new(),
|
||||
"{}: pinned export names methods the inventory lacks",
|
||||
kind.section
|
||||
);
|
||||
assert_eq!(
|
||||
inventory_set.difference(&pinned_set).collect::<Vec<_>>(),
|
||||
Vec::<&&str>::new(),
|
||||
"{}: inventory names methods the pinned export lacks",
|
||||
kind.section
|
||||
);
|
||||
|
||||
let mut typed = BTreeSet::new();
|
||||
for (method, tier) in &kind.typed {
|
||||
assert!(
|
||||
typed.insert(*method),
|
||||
"{}: {method} typed twice",
|
||||
kind.section
|
||||
);
|
||||
assert!(
|
||||
pinned_set.contains(method),
|
||||
"{}: typed {method} is not in the pinned export",
|
||||
kind.section
|
||||
);
|
||||
let expected = match tier {
|
||||
Tier::A => "A",
|
||||
Tier::B => "B",
|
||||
};
|
||||
assert_eq!(
|
||||
inventory.get(method).copied(),
|
||||
Some(expected),
|
||||
"{}: typed {method} disagrees with the inventory tier",
|
||||
kind.section
|
||||
);
|
||||
}
|
||||
for (method, tier) in &inventory {
|
||||
if matches!(*tier, "A" | "B") {
|
||||
assert!(
|
||||
typed.contains(method),
|
||||
"{}: Tier {tier} {method} has no typed variant",
|
||||
kind.section
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
assert!(totals.values().sum::<usize>() > 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn typed_methods_round_trip_their_wire_names() {
|
||||
for method in ClientRequestMethod::ALL {
|
||||
assert_eq!(
|
||||
ClientRequestMethod::from_method(method.method()),
|
||||
Some(*method)
|
||||
);
|
||||
let wire = serde_json::to_value(method).expect("serialize method");
|
||||
assert_eq!(wire, method.method());
|
||||
assert_eq!(
|
||||
serde_json::from_value::<ClientRequestMethod>(wire).expect("deserialize method"),
|
||||
*method
|
||||
);
|
||||
}
|
||||
for method in ServerNotificationMethod::ALL {
|
||||
assert_eq!(
|
||||
ServerNotificationMethod::from_method(method.method()),
|
||||
Some(*method)
|
||||
);
|
||||
}
|
||||
for method in ServerRequestMethod::ALL {
|
||||
assert_eq!(
|
||||
ServerRequestMethod::from_method(method.method()),
|
||||
Some(*method)
|
||||
);
|
||||
}
|
||||
assert_eq!(ClientRequestMethod::from_method("account/read"), None);
|
||||
assert!(serde_json::from_value::<ClientRequestMethod>("feedback/upload".into()).is_err());
|
||||
}
|
||||
+27737
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
Reference in new issue
Block a user