llm-cli-gateway 2.13.2 → 2.14.0-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/CHANGELOG.md +97 -0
  2. package/README.md +53 -26
  3. package/dist/acp/client.d.ts +29 -1
  4. package/dist/acp/client.js +78 -4
  5. package/dist/acp/errors.d.ts +9 -1
  6. package/dist/acp/errors.js +19 -0
  7. package/dist/acp/event-normalizer.d.ts +12 -0
  8. package/dist/acp/event-normalizer.js +16 -0
  9. package/dist/acp/flight-redaction.d.ts +3 -0
  10. package/dist/acp/flight-redaction.js +3 -0
  11. package/dist/acp/permission-bridge.js +11 -5
  12. package/dist/acp/process-manager.d.ts +2 -1
  13. package/dist/acp/process-manager.js +43 -4
  14. package/dist/acp/provider-registry.js +19 -11
  15. package/dist/acp/runtime.d.ts +8 -0
  16. package/dist/acp/runtime.js +47 -4
  17. package/dist/acp/types.d.ts +3083 -55
  18. package/dist/acp/types.js +242 -5
  19. package/dist/async-job-manager.d.ts +2 -0
  20. package/dist/async-job-manager.js +11 -0
  21. package/dist/codex-json-parser.d.ts +1 -0
  22. package/dist/codex-json-parser.js +6 -0
  23. package/dist/config.d.ts +8 -0
  24. package/dist/config.js +47 -0
  25. package/dist/flight-recorder.d.ts +2 -0
  26. package/dist/flight-recorder.js +20 -0
  27. package/dist/gemini-json-parser.d.ts +2 -0
  28. package/dist/gemini-json-parser.js +45 -8
  29. package/dist/grok-json-parser.d.ts +14 -0
  30. package/dist/grok-json-parser.js +156 -0
  31. package/dist/index.d.ts +47 -2
  32. package/dist/index.js +484 -119
  33. package/dist/job-store.d.ts +45 -7
  34. package/dist/job-store.js +138 -17
  35. package/dist/model-registry.d.ts +1 -0
  36. package/dist/model-registry.js +46 -0
  37. package/dist/oauth.js +2 -2
  38. package/dist/postgres-job-store-worker.d.ts +1 -0
  39. package/dist/postgres-job-store-worker.js +327 -0
  40. package/dist/pricing.d.ts +3 -2
  41. package/dist/provider-acp-capabilities.d.ts +52 -0
  42. package/dist/provider-acp-capabilities.js +101 -0
  43. package/dist/provider-admin-tools.d.ts +100 -0
  44. package/dist/provider-admin-tools.js +572 -0
  45. package/dist/provider-capability-cache.d.ts +46 -0
  46. package/dist/provider-capability-cache.js +248 -0
  47. package/dist/provider-capability-discovery.d.ts +85 -0
  48. package/dist/provider-capability-discovery.js +461 -0
  49. package/dist/provider-capability-resolver.d.ts +29 -0
  50. package/dist/provider-capability-resolver.js +92 -0
  51. package/dist/provider-definition-assertions.d.ts +9 -0
  52. package/dist/provider-definition-assertions.js +147 -0
  53. package/dist/provider-definitions.d.ts +127 -0
  54. package/dist/provider-definitions.js +758 -0
  55. package/dist/provider-help-parser.d.ts +34 -0
  56. package/dist/provider-help-parser.js +203 -0
  57. package/dist/provider-model-discovery.d.ts +30 -0
  58. package/dist/provider-model-discovery.js +229 -0
  59. package/dist/provider-output-metadata.d.ts +7 -0
  60. package/dist/provider-output-metadata.js +68 -0
  61. package/dist/provider-schema-builder.d.ts +22 -0
  62. package/dist/provider-schema-builder.js +55 -0
  63. package/dist/provider-surface-generator.d.ts +98 -0
  64. package/dist/provider-surface-generator.js +140 -0
  65. package/dist/provider-tool-capabilities.d.ts +3 -2
  66. package/dist/provider-tool-capabilities.js +77 -62
  67. package/dist/request-helpers.d.ts +37 -4
  68. package/dist/request-helpers.js +134 -0
  69. package/dist/resources.d.ts +6 -1
  70. package/dist/resources.js +64 -192
  71. package/dist/session-manager.js +18 -11
  72. package/dist/stream-json-parser.d.ts +1 -0
  73. package/dist/stream-json-parser.js +3 -0
  74. package/dist/upstream-contracts.d.ts +17 -1
  75. package/dist/upstream-contracts.js +209 -36
  76. package/npm-shrinkwrap.json +2 -2
  77. package/package.json +8 -3
package/CHANGELOG.md CHANGED
@@ -2,6 +2,103 @@
2
2
 
3
3
  All notable changes to the llm-cli-gateway project.
4
4
 
5
+ ## [2.14.0-rc.1] - 2026-07-03: full-featured provider integration + security-review hardening
6
+
7
+ Release candidate. Every non-Cursor provider (Claude, Codex, Gemini, Grok,
8
+ Mistral, Devin) becomes a first-class, provider-definition-driven integration,
9
+ and a security review of the change set is folded in. Cursor stays working via
10
+ the shared generation and is maintenance-only.
11
+
12
+ ### Added
13
+
14
+ - **Provider definitions as the single source of truth** (`src/provider-definitions.ts`):
15
+ one `ProviderDefinition` per CLI type drives request schemas, resources,
16
+ discovery, and capabilities, with compile-time exhaustiveness assertions and a
17
+ `scripts/provider-surfaces-check.mjs` ratchet wired into `npm run check`.
18
+ - **Runtime provider capability discovery with an on-disk cache**: an injectable
19
+ probe runner (no shell interpolation of caller input), a cache keyed on
20
+ discovery checksums with a shape-driven secret scrubber and Zod-validated
21
+ reads, and a TTL that bounds staleness so an upgraded or moved provider CLI is
22
+ re-discovered instead of served stale.
23
+ - **Registry-generated `models://` and `sessions://` resources** for every
24
+ provider (Devin and Cursor now first-class), plus **live model discovery**
25
+ wired into `models://<provider>` and `list_models` via a memoized resolver
26
+ with a fire-and-forget startup warm (reads never block on a spawn).
27
+ - **Complete CLI request-field coverage** across providers, guarded by
28
+ upstream-contract and coverage-closure tests; interactive/admin-only flags are
29
+ represented as typed capability facts rather than silently dropped.
30
+ - **Discovery-driven native ACP surface**: Grok, Mistral, and Devin route
31
+ natively over ACP when enabled (no masquerade), with capability-gated session
32
+ methods and state-mutating session ops gated behind
33
+ `acp.allow_mutating_session_ops`.
34
+ - **Provider admin tools**: `provider_admin_list` / `provider_admin_run`
35
+ (read-only) and `provider_admin_mutate` (gated). Availability is
36
+ discovery-driven; mutating ops require `[admin] allow_mutating_cli_admin_ops`
37
+ and, for remote callers, `LLM_GATEWAY_CLI_ADMIN=1` plus the `cli:admin` OAuth
38
+ scope, routed through the approval manager with a fail-closed pre-spawn audit.
39
+ - **Postgres job store** (`PostgresJobStore`): a durable async-job and
40
+ validation-run/receipt backend over the `pg` pool, driven through a dedicated
41
+ worker thread (`postgres-job-store-worker.ts`). Selectable via
42
+ `persistence.backend = "postgres"`; the ephemeral memory backend deliberately
43
+ does not implement the validation-run surface.
44
+ - **Search-engine discoverability for the docs site**: Google Search Console
45
+ and Bing IndexNow submission (`npm run search:*`), plus `sitemap.xml`,
46
+ `robots.txt`, and `llms.txt`, alongside a marketing-site refresh.
47
+
48
+ ### Changed
49
+
50
+ - **Unified provider output/event normalization**: a real Grok JSON parser, and
51
+ `stopReason`/error signals threaded across the CLI parsers and the ACP event
52
+ normalizer. The provider-minted session id and terminal stop reason are
53
+ preserved through the async job and flight recorder so a deferred job resumes
54
+ with the real provider session id.
55
+ - **`provider_tool_capabilities`** now reports ACP `runtimeEnabled` from the
56
+ resolved config (previously hardcoded), with entrypoint/version/mediation
57
+ derived from the registry.
58
+ - Docs: README provider-capability table, per-provider skills refreshed to the
59
+ installed CLI versions, ACP documented as sync-only for now, and the
60
+ `provider-acp://` resource registered on the MCP server.
61
+
62
+ ### Fixed
63
+
64
+ - Codex and Gemini clean exits that actually carried a failed terminal event
65
+ now surface a warning instead of being reported as a silent empty success;
66
+ the ACP transport likewise flags a refusal/cancelled/truncated turn.
67
+ - Codex surfaces the real `turn.failed` reason on a non-zero exit instead of a
68
+ partial agent message.
69
+ - json-mode output parsers tolerate a stray banner line around the JSON object
70
+ (telemetry is no longer all-or-nothing), and the Gemini stream replaces rather
71
+ than doubles a consolidated non-delta final message.
72
+ - Implausible `agy models` label lines are filtered instead of surfacing as
73
+ bogus models in `models://gemini`.
74
+
75
+ ### Security
76
+
77
+ - **Remote host-path confinement.** Remote HTTP/OAuth callers can no longer hand
78
+ a provider CLI an arbitrary host path to read, write, or load code from. The
79
+ advanced path/plugin fields are rejected for remote callers (local stdio is
80
+ unaffected): `systemPromptFile`/`appendSystemPromptFile`/`settings`/
81
+ `pluginDir`/`pluginUrl`/`debugFile` (Claude), string `outputSchema`/`images`/
82
+ `outputLastMessage` (Codex), `promptFile`/`config`/`agentConfig`/string
83
+ `exportSession` (Devin), and `promptFile`/`leaderSocket`/`rules`/`agent`
84
+ (Grok). `workingDir`/`addDir`/`includeDirs` remain workspace-confined.
85
+ - **Read-only provider-admin ops** now require the same remote `cli:admin` gate
86
+ as the mutating path, so a remote caller cannot enumerate or trigger
87
+ host-global provider-CLI spawns unprivileged.
88
+ - **ACP permission bridge** never turns a single approval into a standing
89
+ `allow_always` grant (it selects a one-time allow, else denies).
90
+ - **ACP subprocess reaping**: a graceful termination signal escalates to
91
+ SIGKILL after a grace window so a provider CLI that ignores SIGTERM is not
92
+ leaked as a zombie.
93
+ - Job results scrub the provider session id from returned streams for remote
94
+ callers; ACP error redaction covers Google/GitHub/Slack token shapes;
95
+ `session_info_update` is handled as a forward-compatible unknown ACP variant.
96
+ - CI/release: the Cloudflare Pages token is fetched from Azure Key Vault via
97
+ GitHub OIDC federation (no stored secret); the release tooling supports
98
+ prerelease cuts (a prerelease publishes under a side npm dist-tag, never
99
+ `latest`); typos and gitleaks allowlists cover a deliberate identifier and
100
+ secret-fixture test files.
101
+
5
102
  ## [2.13.2] - 2026-07-01: remote HTTP + OAuth connector UX and hardening
6
103
 
7
104
  ### Added
package/README.md CHANGED
@@ -9,9 +9,11 @@
9
9
  > _"Without consultation, plans are frustrated, but with many counselors they succeed."_
10
10
  > — Proverbs 15:22 (LSB)
11
11
 
12
- A Model Context Protocol (MCP) gateway for running Claude Code, Codex, Gemini, Grok, Mistral (Vibe), Devin, and Cursor Agent CLIs from one MCP endpoint, with durable async jobs, session continuity, cache-aware prompting, observability, and personal-appliance setup tooling.
12
+ **Cross-model review without rebuilding your agent stack.**
13
13
 
14
- **Why developers try it:** one local MCP endpoint for cross-LLM validation, multi-agent coding workflows, and repeatable assistant-led setup across registered provider CLIs.
14
+ `llm-cli-gateway` gives any MCP client one local-first gateway for Claude Code, Codex, Gemini/Antigravity, Grok Build, Mistral Vibe, Cognition Devin, Cursor Agent, and configured HTTP API providers, while preserving native CLI sessions, local credentials, durable async jobs, validation receipts, and review workflows.
15
+
16
+ **Why developers try it:** any connected client can ask other models for a second opinion, run implementation/review loops, collect durable job results, and route API-token LLMs without turning local coding agents into a generic chat proxy.
15
17
 
16
18
  **Current signals:** CI and security workflows pass on `main`, OpenSSF Scorecard is published, OpenSSF Best Practices is passing, releases use Sigstore signing, and the package is MIT licensed.
17
19
 
@@ -38,7 +40,7 @@ Or use directly with `npx` from an MCP client:
38
40
 
39
41
  `llm-cli-gateway` is a single-user MCP gateway for cross-LLM validation and multi-agent coding workflows. It is more than a thin CLI wrapper:
40
42
 
41
- - Runs registered provider CLIs through consistent sync and async MCP tools.
43
+ - Runs registered provider CLIs and configured HTTP API providers through consistent sync and async MCP tools.
42
44
  - Persists long-running jobs, supports restart-safe result collection, deduplication, cancellation, and sync-to-async deferral.
43
45
  - Tracks sessions, real CLI resume paths, structured response metadata, and cache telemetry.
44
46
  - Supports cache-aware `promptParts`, including explicit Claude `cache_control` when opted in.
@@ -51,7 +53,7 @@ Or use directly with `npx` from an MCP client:
51
53
 
52
54
  The repo ships agent-ready workflow skills under [`.agents/skills`](.agents/skills) for async orchestration, session continuity, multi-LLM review, implement-review-fix loops, and secure approval-gated dispatch. Six caller-facing skills are bundled in the published npm package: `async-job-orchestration`, `multi-llm-review`, `session-workflow`, `secure-orchestration`, `implement-review-fix`, and `public-demo-session`. Machine-readable DAG-TOML plans live under [`docs/plans`](docs/plans) and [`setup/install-plan.dag.toml`](setup/install-plan.dag.toml) for workflows that need deterministic sequencing and verification gates.
53
55
 
54
- The next documentation focus is provider-specific skill and DAG-TOML pairs for each outbound CLI: Claude, Codex, Gemini, Grok, and Mistral Vibe. The implementation plan is tracked in [`docs/plans/provider-workflow-assets.dag.toml`](docs/plans/provider-workflow-assets.dag.toml), with each provider asset expected to cover install/login checks, session behavior, approval modes, cache/telemetry surfaces, failure modes, and a smoke-test gate.
56
+ The next documentation focus is provider-specific skill and DAG-TOML pairs for each outbound CLI and API-provider family: Claude, Codex, Gemini/Antigravity, Grok, Mistral Vibe, Devin, Cursor Agent, OpenAI-compatible endpoints, Anthropic Messages, and xAI Responses. The implementation plan is tracked in [`docs/plans/provider-workflow-assets.dag.toml`](docs/plans/provider-workflow-assets.dag.toml), with each provider asset expected to cover install/login checks or token-env checks, session behavior, approval modes, cache/telemetry surfaces, failure modes, and a smoke-test gate.
55
57
 
56
58
  ## Trust & Supply Chain
57
59
 
@@ -61,7 +63,7 @@ The next documentation focus is provider-specific skill and DAG-TOML pairs for e
61
63
  - CI runs build, lint, format, tests, package checks, and npm audit.
62
64
  - Security CI runs actionlint, zizmor, shellcheck, typos, osv-scanner, gitleaks, and lychee.
63
65
  - GitHub release installer artifacts are checksummed and signed with Sigstore keyless signing.
64
- - npm releases use provenance through OIDC trusted publishing.
66
+ - npm releases use a generated prod-only shrinkwrap and release security audit; the publish token is fetched at runtime from Azure Key Vault through GitHub OIDC to Entra.
65
67
  - The npm package intentionally ships a generated, prod-only `npm-shrinkwrap.json` so registry installs resolve the audited release tree. Release gates regenerate it from `package-lock.json`, compare for parity, and run a registry-fidelity consumer install before publishing.
66
68
  - Socket behavioural alerts are documented in [`socket.yml`](./socket.yml) and under "Security Considerations" below. `shellAccess` and `shrinkwrap` are reviewed package capabilities/configuration for this CLI appliance, not hidden install behaviour.
67
69
 
@@ -190,15 +192,15 @@ Every `*_request` and `*_request_async` tool except `devin_request` / `devin_req
190
192
 
191
193
  Per-CLI capability matrix (prefix discipline is automatic via `promptParts` for all providers except Devin and Cursor, which have no `promptParts` surface; explicit levers are provider-specific):
192
194
 
193
- | CLI | Prefix discipline | Explicit lever(s) |
194
- | ------- | ----------------- | ----------------- |
195
+ | CLI | Prefix discipline | Explicit lever(s) |
196
+ | ------- | ----------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
195
197
  | claude | yes | `promptParts.cacheControl` + `outputFormat: "stream-json"` (Anthropic `cache_control` breakpoints on stable blocks; `ttl="1h"` forced) |
196
- | codex | yes | none (OpenAI implicit) |
197
- | gemini | yes | none (implicit server-side) |
198
- | grok | yes | `compactionMode` / `compactionDetail` (context compaction: `summary|transcript|segments`; `segments` writes per-segment markdown) |
199
- | mistral | yes | none (implicit) |
200
- | devin | no | plain `prompt` only |
201
- | cursor | no | plain `prompt` only |
198
+ | codex | yes | none (OpenAI implicit) |
199
+ | gemini | yes | none (implicit server-side) |
200
+ | grok | yes | `compactionMode` / `compactionDetail` (context compaction: `summary | transcript | segments`; `segments` writes per-segment markdown) |
201
+ | mistral | yes | none (implicit) |
202
+ | devin | no | plain `prompt` only |
203
+ | cursor | no | plain `prompt` only |
202
204
 
203
205
  **Claude example (explicit cacheControl)**
204
206
 
@@ -208,10 +210,10 @@ claude_request({
208
210
  system: "You are a helpful code reviewer.",
209
211
  context: "<long stable file dump>",
210
212
  task: "Review the diff.",
211
- cacheControl: { system: true, context: true } // task is never marked
213
+ cacheControl: { system: true, context: true }, // task is never marked
212
214
  },
213
- outputFormat: "stream-json"
214
- })
215
+ outputFormat: "stream-json",
216
+ });
215
217
  ```
216
218
 
217
219
  Gateway emits the `stream-json` stdin path with `cache_control: {type:"ephemeral", ttl:"1h"}` on marked blocks only.
@@ -222,8 +224,8 @@ Gateway emits the `stream-json` stdin path with `cache_control: {type:"ephemeral
222
224
  grok_request({
223
225
  promptParts: { system: "...", context: "...", task: "..." },
224
226
  compactionMode: "segments",
225
- compactionDetail: "balanced"
226
- })
227
+ compactionDetail: "balanced",
228
+ });
227
229
  ```
228
230
 
229
231
  Emits `--compaction-mode segments --compaction-detail balanced`.
@@ -249,6 +251,26 @@ Opt-in flags (all default off) live under `[cache_awareness]` in `~/.llm-cli-gat
249
251
  - **Type Safety**: Strict TypeScript with comprehensive error handling
250
252
  - **Supply-chain hardening**: a dedicated `.github/workflows/security.yml` runs actionlint, zizmor, shellcheck, typos, osv-scanner, gitleaks, and lychee on every push and PR (see `SECURITY.md` for the threat model)
251
253
 
254
+ ## Provider capability surface
255
+
256
+ Every provider is reachable through the same request, session, job, and validation machinery, but the underlying CLIs differ in what they natively expose. The table records what actually shipped per provider; discover the live surface at runtime with `provider_tool_capabilities`, `list_models`, and the `provider-acp://<provider>` / `provider-tools://<provider>` resources.
257
+
258
+ | Provider | CLI request tools | Native ACP | Live model discovery | Admin surface |
259
+ | --- | --- | --- | --- | --- |
260
+ | Claude Code (`claude`) | `claude_request` / `_async` | None (CLI-first; no ACP entrypoint at claude 2.1.198) | model aliases, reasoning-effort levels, fallback model | read-only via `provider_admin_list` / `provider_admin_run` |
261
+ | OpenAI Codex (`codex`) | `codex_request` / `_async`, `codex_fork_session` | None (codex-cli 0.142.4 advertises mcp-server / app-server transports, not native ACP) | `codex debug models` | read-only via `provider_admin_list` / `provider_admin_run` |
262
+ | Gemini / Antigravity (`gemini`, `agy`) | `gemini_request` / `_async` | None (agy 1.0.14 exposes no ACP entrypoint; legacy Gemini CLI ACP evidence does not transfer) | `agy models` | read-only via `provider_admin_list` / `provider_admin_run` |
263
+ | xAI Grok (`grok`) | `grok_request` (sync `transport: "acp"`) / `_async` | Native via `grok agent stdio` | `grok models` + `~/.grok/config.toml` | read-only via `provider_admin_list` / `provider_admin_run` |
264
+ | Mistral Vibe (`mistral`) | `mistral_request` (sync `transport: "acp"`) / `_async` | Native via `vibe-acp` | Vibe config plus the `VIBE_ACTIVE_MODEL` active model and agent profiles | read-only via `provider_admin_list` / `provider_admin_run` |
265
+ | Cognition Devin (`devin`) | `devin_request` (sync `transport: "acp"`, `agentType: summarizer\|review`) / `_async` | Native via `devin acp` | `--model` / `DEVIN_MODEL` | read-only via `provider_admin_list` / `provider_admin_run` |
266
+ | Cursor Agent (`cursor`) | `cursor_request` (sync `transport: "acp"`) / `_async` | Native via `cursor-agent acp` (companion-owned) | model aliases | read-only via `provider_admin_list` / `provider_admin_run` |
267
+
268
+ - **Native ACP** is reported honestly. `grok`, `mistral`, `devin`, and `cursor` expose a native ACP entrypoint, so `provider-acp://<provider>` carries the negotiated `initialize` capability set and the derived session-method availability, and the sync `*_request` accepts `transport: "acp"` (fails closed unless `[acp]` and the provider's `runtime_enabled` gate are set). ACP routing is sync-only: the `*_request_async` variants always run the CLI transport and do not accept `transport: "acp"` (nor Devin's `agentType`); async ACP parity is a later phase. `claude`, `codex`, and `gemini` have no native ACP entrypoint at their target CLI versions; their `provider-acp://` records report `native: false` with no methods and no adapter-as-native masquerade, and they expose no `transport: "acp"` selector.
269
+ - **Resources** are generated from the provider registry for every CLI provider: `models://<provider>`, `sessions://<provider>`, `provider-acp://<provider>`, `provider-tools://<provider>`, and `provider-subcommands://<provider>`.
270
+ - **Model discovery** is live and account-aware: the discovery listed above reaches `models://<provider>` and `list_models`, degrading to static registry facts when a live probe is unavailable (a resource read never spawns a CLI).
271
+ - **Admin surfaces** are discovery-driven and output-redacted. `provider_admin_list` and `provider_admin_run` are read-only for every provider. State-mutating admin operations are exposed only through `provider_admin_mutate`, gated behind `[admin] allow_mutating_cli_admin_ops`, the remote `cli:admin` scope, an approval gate, and an audit record. Mutating ACP session operations are likewise gated behind `[acp] allow_mutating_session_ops`.
272
+ - **Validation** commands work across every provider: `validate_with_models`, `second_opinion`, `compare_answers`, `red_team_review`, `consensus_check`, `ask_model`, and `synthesize_validation`, with durable signed receipts via `validation_receipt` and the `validation-receipt://{validationId}` resource.
273
+
252
274
  ## Prerequisites
253
275
 
254
276
  **Node.js >= 24.4.0** is required (`engines.node` in `package.json`). The gateway uses Node's built-in `node:sqlite` module for persistence — there is no native binding to compile and no install scripts run. The 24.4 floor is where `allowBareNamedParameters` defaults to `true`, which the persistence layer relies on.
@@ -389,7 +411,7 @@ The personal-appliance surface exposes simplified validation tools for non-devel
389
411
  - `synthesize_validation`: run an explicit judge model after provider results have been collected.
390
412
  - `list_available_models`: list the models each provider CLI exposes through the simplified surface.
391
413
  - `job_status` and `job_result`: poll and collect validation job outputs.
392
- - `validation_receipt`: retrieve the immutable receipt of a terminal cross-LLM validation run by `validationId` (returns `minted | pending | expired_unminted | not_found`, own-or-not-found). `format: "markdown"` renders a human-readable report; `includeRawResponses` inlines provider answer text. Registered only under the durable persistence gate (`backend = "sqlite"` with an attached validation-run store).
414
+ - `validation_receipt`: retrieve the immutable receipt of a terminal cross-LLM validation run by `validationId` (returns `minted | pending | expired_unminted | not_found`, own-or-not-found). `format: "markdown"` renders a human-readable report; `includeRawResponses` inlines provider answer text. Registered only when the attached job store provides the durable validation-run store capability (`sqlite` and `postgres`).
393
415
 
394
416
  The same receipt is also exposed as the `validation-receipt://{validationId}` MCP resource (same durable gate and own-or-not-found owner scoping).
395
417
 
@@ -574,6 +596,7 @@ Execute a Grok CLI (xAI) request with session support.
574
596
 
575
597
  - `prompt` (string, optional*): The prompt to send (1-100,000 chars). *Exactly one of `prompt` or `promptParts` is required (mutually exclusive)
576
598
  - `model` (string, optional): Model name or alias (e.g. `grok-build`, `latest`)
599
+ - `transport` (string, optional): `"cli"` (default) runs the Grok CLI; `"acp"` routes through Grok's native `grok agent stdio` transport when `[acp].enabled` and the provider's `runtime_enabled` are set (fails closed otherwise). Sync-only: `grok_request_async` always runs the CLI transport and does not accept `transport`
577
600
  - `outputFormat` (string, optional): `"plain"` (default), `"json"`, or `"streaming-json"`
578
601
  - `sessionId` (string, optional): Session ID to resume (`--resume <id>`)
579
602
  - `resumeLatest` (boolean, optional): Resume the most recent session in the current cwd (`--continue`)
@@ -647,7 +670,7 @@ The job-store backend is configured by `~/.llm-cli-gateway/config.toml` (overrid
647
670
  [persistence]
648
671
  backend = "sqlite" # "sqlite" | "memory" | "postgres" | "none"
649
672
  path = "~/.llm-cli-gateway/logs.db" # for sqlite
650
- # dsn = "postgresql://user:pw@host/db" # for postgres (interface only — impl not yet shipped)
673
+ # dsn = "postgresql://user:pw@host/db" # for postgres
651
674
  retentionDays = 30
652
675
  dedupWindowMs = 3600000
653
676
  acknowledgeEphemeral = false # required to enable async tools with memory backend
@@ -656,8 +679,8 @@ acknowledgeEphemeral = false # required to enable async tools wit
656
679
  Backends:
657
680
 
658
681
  - **`sqlite`** (default) — durable, file-backed. Safe for single-instance deployments.
682
+ - **`postgres`** — durable PostgreSQL-backed async job, dedup, orphan recovery, HTTP job, and validation receipt storage. Use this for multi-instance or service deployments. Requires the optional peer dependency `pg` to be installed alongside the gateway.
659
683
  - **`memory`** — in-process Map. Lost on gateway exit. Requires `acknowledgeEphemeral = true` to be loaded. Suitable for tests and ephemeral CI gateways.
660
- - **`postgres`** — interface only, implementation not yet shipped. Selecting this backend throws at startup.
661
684
  - **`none`** — no store. **`*_request_async`, `llm_job_status`, `llm_job_result`, and `llm_job_cancel` are NOT registered on the gateway.** This is a structural invariant: agents that try to call async tools against a gateway with `backend = "none"` get a clean "tool not found" at connect time instead of silent in-memory loss after the 1-hour TTL. Use `llm_process_health` to inspect the resolved persistence state programmatically.
662
685
 
663
686
  Legacy environment variables (deprecated; emit a warning at startup):
@@ -922,6 +945,7 @@ consumes = ["OUT:mcp-reconnected"]
922
945
  Run a Mistral Vibe agentic coding request. Like `grok_request` in shape, but with Vibe's specific surface:
923
946
 
924
947
  - `model` (string, optional): Vibe model alias (for example `mistral-medium-3.5` or `latest`). The resolved value is injected via the `VIBE_ACTIVE_MODEL` environment variable; omit it to let the gateway discover Vibe config and avoid stale hardcoded defaults.
948
+ - `transport` (string, optional): `"cli"` (default) runs the Vibe CLI; `"acp"` routes through Vibe's native `vibe-acp` transport when `[acp].enabled` and the provider's `runtime_enabled` are set (fails closed otherwise). Sync-only: `mistral_request_async` always runs the CLI transport and does not accept `transport`
925
949
  - `permissionMode`: the Vibe `--agent` name — builtins `default | plan | accept-edits | auto-approve`, or any install-gated/custom agent. Emitted as `--agent <name>`. Defaults to `auto-approve` in programmatic mode.
926
950
  - `allowedTools` (string[], optional): One `--enabled-tools <tool>` flag per entry (allow-list only).
927
951
  - `disallowedTools` (string[], optional): Accepted for parity with the other providers; ignored at the CLI boundary with a logged warning.
@@ -951,7 +975,8 @@ Run a Cognition Devin CLI request synchronously (headless print mode, `devin -p`
951
975
 
952
976
  - `prompt` (string, optional*): Prompt text for Devin CLI (1-100,000 chars). Required in practice; `promptFile` is additive
953
977
  - `model` (string, optional): Model name or alias (e.g. `opus`, `latest`)
954
- - `transport` (string, optional): `"cli"` (default) runs the Devin CLI; `"acp"` routes through `devin acp` when `[acp].enabled` and the provider's `runtime_enabled` are set (fails closed otherwise)
978
+ - `transport` (string, optional): `"cli"` (default) runs the Devin CLI; `"acp"` routes through Devin's native `devin acp` transport when `[acp].enabled` and the provider's `runtime_enabled` are set (fails closed otherwise). Sync-only: `devin_request_async` always runs the CLI transport and accepts neither `transport` nor `agentType`
979
+ - `agentType` (string, optional): ACP agent variant for `transport: "acp"` (`devin acp --agent-type`): `"summarizer"` (no tools, text summary) or `"review"` (read-only plus shell code-review); ignored for the CLI transport
955
980
  - `permissionMode` (string, optional): Devin CLI permission mode (`--permission-mode`): `auto` (auto-approves read-only tools), `smart` (also auto-runs actions a fast model judges safe), `dangerous` (auto-approves all). Omit to use Devin's headless default
956
981
  - `promptFile` (string, optional): Load the initial prompt from a file (`--prompt-file`)
957
982
  - `sessionId` (string, optional): Devin session ID to resume (`--resume <id>`). The `gw-*` id minted for a brand-new session is not resumable via `sessionId`; continue with `resumeLatest: true`
@@ -972,7 +997,7 @@ Run a Cursor Agent CLI request synchronously. Defaults to headless print mode (`
972
997
  - `model` (string, optional): Model name or alias (for example `gpt-5`, `sonnet-4-thinking`, or `latest`)
973
998
  - `mode` (string, optional): Cursor mode, `"plan"` or `"ask"` (`--mode`)
974
999
  - `outputFormat` (string, optional): `"text"` (default), `"json"`, or `"stream-json"`
975
- - `transport` (string, optional): `"cli"` (default) or `"acp"`; ACP fails closed unless enabled in gateway config and rejects unsupported Cursor CLI-only controls instead of dropping them
1000
+ - `transport` (string, optional): `"cli"` (default) or `"acp"`; ACP fails closed unless enabled in gateway config and rejects unsupported Cursor CLI-only controls instead of dropping them. Sync-only: `cursor_request_async` always runs the CLI transport and does not accept `transport`
976
1001
  - `force` (boolean, optional): Emit `--force` for non-interactive operation
977
1002
  - `autoReview` (boolean, optional): Emit `--auto-review`
978
1003
  - `sandbox` (string, optional): `"enabled"` or `"disabled"` (`--sandbox`)
@@ -1317,7 +1342,7 @@ The API key value is never emitted on any of these surfaces (only the env var na
1317
1342
 
1318
1343
  ### Security note
1319
1344
 
1320
- The resolved API key is excluded from `payloadJson`, the dedup key, logs, and the flight recorder. However, the **request prompt is persisted in plaintext** in the async job store (the SQLite file at `[persistence].path`, default `~/.llm-cli-gateway/logs.db`) and is not covered by secret redaction. This mirrors the CLI tools, whose prompt is persisted in `argsJson` whenever it is passed as a command argument (a few CLI paths instead stream the prompt over stdin and so do not persist it). Treat the job store as sensitive at rest. See [Security Considerations](#security-considerations).
1345
+ The resolved API key is excluded from `payloadJson`, the dedup key, logs, and the flight recorder. However, the **request prompt is persisted in plaintext** in the async job store (SQLite at `[persistence].path`, default `~/.llm-cli-gateway/logs.db`, or Postgres rows under `backend = "postgres"`) and is not covered by secret redaction. This mirrors the CLI tools, whose prompt is persisted in `argsJson` whenever it is passed as a command argument (a few CLI paths instead stream the prompt over stdin and so do not persist it). Treat the job store as sensitive at rest. See [Security Considerations](#security-considerations).
1321
1346
 
1322
1347
  ## Session Management
1323
1348
 
@@ -1384,9 +1409,11 @@ await callTool("session_delete", {
1384
1409
  - **Gemini (Antigravity)** → `default` / prompted, i.e. **no** `--dangerously-skip-permissions` (the `agy` CLI has no accept-edits middle rung, so the safe default is prompted execution; without the opt-in, Gemini cannot auto-approve mutating tools under `mcp_managed`)
1385
1410
 
1386
1411
  Set to `1`/`true` to let the operator opt back in: this permits bypass requests through the approval gate **and** restores each provider's full auto-approve mode under `mcp_managed` (Claude `bypassPermissions`, Grok `--always-approve`, Mistral `--agent auto-approve`, Gemini `--dangerously-skip-permissions`). Sandboxed auto modes (e.g. codex `--sandbox workspace-write`) are unaffected.
1412
+
1387
1413
  ```bash
1388
1414
  LLM_GATEWAY_APPROVAL_ALLOW_BYPASS=1 node dist/index.js
1389
1415
  ```
1416
+
1390
1417
  - `LLM_GATEWAY_TRUSTED_PRINCIPAL_HEADER`: Name of an HTTP header carrying the authenticated user identity asserted by a **trusted front door** (any identity-aware reverse proxy / IdP). When set, the gateway adopts that header value as the request's ownership principal — but **only** for requests authenticated with the gateway's own static bearer token (i.e. the trusted upstream proxy), never from an arbitrary remote client. Off by default; IdP-agnostic. Lets a proxy-fronted multi-user deployment carry per-user identity into the gateway.
1391
1418
  ```bash
1392
1419
  LLM_GATEWAY_TRUSTED_PRINCIPAL_HEADER=x-gateway-principal node dist/index.js
@@ -1575,11 +1602,11 @@ The gateway supports concurrent requests across different CLIs. Each request spa
1575
1602
 
1576
1603
  - **Input Validation**: All prompts are validated (min 1 char, max 100k chars)
1577
1604
  - **API-provider keys**: For `[providers.<name>]` HTTP providers, the gateway reads the key from the named environment variable at request time only. The resolved key is excluded from the persisted `payloadJson`, the dedup key, logs, and the flight recorder, and is never surfaced on the discovery/diagnostic surfaces (which report only the env var name and a presence boolean). `base_url` userinfo is redacted on the diagnostic surfaces. See [API providers (HTTP)](#api-providers-http).
1578
- - **Prompt persistence at rest**: Async job rows store the request **prompt in plaintext** (HTTP `payloadJson`, and CLI `argsJson` whenever the prompt is passed as a command argument rather than streamed over stdin); this is not covered by secret redaction. The SQLite job-store file (default `~/.llm-cli-gateway/logs.db`, configurable via `[persistence].path`) is `chmod`ed to `0o600` on non-Windows hosts; treat it as sensitive and scope/rotate it like any prompt log. Set `[persistence].backend = "none"` to disable the async job store entirely (the `*_request_async` / `llm_job_*` tools are then not registered).
1605
+ - **Prompt persistence at rest**: Async job rows store the request **prompt in plaintext** (HTTP `payloadJson`, and CLI `argsJson` whenever the prompt is passed as a command argument rather than streamed over stdin); this is not covered by secret redaction. The SQLite job-store file (default `~/.llm-cli-gateway/logs.db`, configurable via `[persistence].path`) is `chmod`ed to `0o600` on non-Windows hosts; the Postgres backend stores the same fields in database rows. Treat either backend as sensitive and scope/rotate it like any prompt log. Set `[persistence].backend = "none"` to disable the async job store entirely (the `*_request_async` / `llm_job_*` tools are then not registered).
1579
1606
  - **Command Execution**: Uses `spawn` with separate arguments (not shell execution)
1580
1607
  - **No Eval**: No dynamic code evaluation in our source (see "Socket alerts" below for the transitive `ajv` codegen case)
1581
1608
  - **Sandboxing**: Consider running in containers for production use
1582
- - **Provenance**: Releases are published with [npm provenance](https://docs.npmjs.com/generating-provenance-statements) via OIDC trusted publishing from GitHub Actions
1609
+ - **npm publish control**: npm releases are gated by the generated prod-only shrinkwrap, release security audit, packed-consumer checks, and a publish token fetched at runtime from Azure Key Vault through GitHub OIDC to Entra
1583
1610
  - **Release signing**: GitHub release installer artifacts are signed with Sigstore keyless signing; verify `SHA256SUMS.sigstore.json` before trusting the checksum file
1584
1611
 
1585
1612
  ### Socket alerts — context for reviewers
@@ -1,6 +1,6 @@
1
1
  import { AcpError } from "./errors.js";
2
2
  import type { JsonRpcStdioTransport, JsonRpcId } from "./json-rpc-stdio.js";
3
- import { type ContentBlock, type InitializeResponse, type ReadTextFileRequest, type ReadTextFileResponse, type RequestPermissionRequest, type RequestPermissionResponse, type SessionLoadResponse, type SessionNewResponse, type SessionPromptResponse, type SessionUpdateNotification, type WriteTextFileRequest, type WriteTextFileResponse } from "./types.js";
3
+ import { type CloseSessionResponse, type ContentBlock, type DeleteSessionResponse, type InitializeResponse, type ListSessionsResponse, type ReadTextFileRequest, type ReadTextFileResponse, type RequestPermissionRequest, type RequestPermissionResponse, type SessionLoadResponse, type SessionNewResponse, type SessionPromptResponse, type SessionResumeResponse, type SessionUpdateNotification, type SetSessionConfigOptionResponse, type SetSessionModeResponse, type WriteTextFileRequest, type WriteTextFileResponse } from "./types.js";
4
4
  import type { Logger } from "../logger.js";
5
5
  import type { CliType } from "../session-manager.js";
6
6
  export declare const DEFAULT_ACP_PROTOCOL_VERSION = 1;
@@ -25,6 +25,7 @@ export interface AcpClientOptions {
25
25
  readonly logger?: Logger;
26
26
  readonly protocolVersion?: number;
27
27
  readonly timeouts?: AcpClientTimeouts;
28
+ readonly allowMutatingSessionOps?: boolean;
28
29
  }
29
30
  export interface AcpClientTimeouts {
30
31
  readonly initializeMs?: number;
@@ -48,6 +49,18 @@ export interface PromptParams {
48
49
  readonly sessionId: string;
49
50
  readonly prompt: ReadonlyArray<ContentBlock>;
50
51
  }
52
+ export interface ResumeSessionParams extends NewSessionParams {
53
+ readonly sessionId: string;
54
+ }
55
+ export interface SetSessionModeParams {
56
+ readonly sessionId: string;
57
+ readonly modeId: string;
58
+ }
59
+ export interface SetSessionConfigOptionParams {
60
+ readonly sessionId: string;
61
+ readonly configId: string;
62
+ readonly value: string;
63
+ }
51
64
  export declare class AcpClient {
52
65
  private readonly transport;
53
66
  private readonly provider;
@@ -56,16 +69,31 @@ export declare class AcpClient {
56
69
  private readonly logger;
57
70
  private readonly protocolVersion;
58
71
  private readonly timeouts;
72
+ private readonly allowMutatingSessionOps;
59
73
  private initialized;
60
74
  private initializeResult;
75
+ private methodAvailability;
76
+ private static readonly MUTATING_METHODS;
61
77
  constructor(options: AcpClientOptions);
62
78
  get isInitialized(): boolean;
63
79
  get agentInfo(): InitializeResponse | null;
80
+ get availableMethods(): ReadonlySet<string>;
81
+ supportsMethod(method: string): boolean;
64
82
  initialize(options?: InitializeOptions): Promise<InitializeResponse>;
65
83
  newSession(params: NewSessionParams): Promise<SessionNewResponse>;
66
84
  loadSession(params: LoadSessionParams): Promise<SessionLoadResponse>;
67
85
  prompt(params: PromptParams): Promise<SessionPromptResponse>;
68
86
  cancel(sessionId: string): void;
87
+ resumeSession(params: ResumeSessionParams): Promise<SessionResumeResponse>;
88
+ listSessions(params?: {
89
+ cursor?: string;
90
+ }): Promise<ListSessionsResponse>;
91
+ closeSession(sessionId: string): Promise<CloseSessionResponse>;
92
+ deleteSession(sessionId: string): Promise<DeleteSessionResponse>;
93
+ setSessionMode(params: SetSessionModeParams): Promise<SetSessionModeResponse>;
94
+ setSessionConfigOption(params: SetSessionConfigOptionParams): Promise<SetSessionConfigOptionResponse>;
95
+ private augmentSessionMethods;
96
+ private assertMethodAvailable;
69
97
  private send;
70
98
  private normalizeError;
71
99
  private assertInitialized;
@@ -1,5 +1,5 @@
1
- import { AcpProtocolError, isAcpError } from "./errors.js";
2
- import { parseInitializeResponse, parseReadTextFileRequest, parseRequestPermissionRequest, parseSessionLoadResponse, parseSessionNewResponse, parseSessionPromptResponse, parseSessionUpdateNotification, parseWriteTextFileRequest, } from "./types.js";
1
+ import { AcpMethodUnsupportedError, AcpMutatingDisabledError, AcpProtocolError, isAcpError, } from "./errors.js";
2
+ import { deriveAcpMethodAvailability, parseCloseSessionResponse, parseDeleteSessionResponse, parseInitializeResponse, parseListSessionsResponse, parseReadTextFileRequest, parseRequestPermissionRequest, parseSessionLoadResponse, parseSessionNewResponse, parseSessionPromptResponse, parseSessionResumeResponse, parseSessionUpdateNotification, parseSetSessionConfigOptionResponse, parseSetSessionModeResponse, parseWriteTextFileRequest, sessionResponseMethods, } from "./types.js";
3
3
  import { noopLogger } from "../logger.js";
4
4
  export const DEFAULT_ACP_PROTOCOL_VERSION = 1;
5
5
  export class AcpClient {
@@ -10,8 +10,15 @@ export class AcpClient {
10
10
  logger;
11
11
  protocolVersion;
12
12
  timeouts;
13
+ allowMutatingSessionOps;
13
14
  initialized = false;
14
15
  initializeResult = null;
16
+ methodAvailability = new Set();
17
+ static MUTATING_METHODS = new Set([
18
+ "session/delete",
19
+ "session/set_mode",
20
+ "session/set_config_option",
21
+ ]);
15
22
  constructor(options) {
16
23
  this.transport = options.transport;
17
24
  this.provider = options.provider;
@@ -20,6 +27,7 @@ export class AcpClient {
20
27
  this.logger = options.logger ?? noopLogger;
21
28
  this.protocolVersion = options.protocolVersion ?? DEFAULT_ACP_PROTOCOL_VERSION;
22
29
  this.timeouts = options.timeouts ?? {};
30
+ this.allowMutatingSessionOps = options.allowMutatingSessionOps ?? false;
23
31
  }
24
32
  get isInitialized() {
25
33
  return this.initialized;
@@ -27,6 +35,12 @@ export class AcpClient {
27
35
  get agentInfo() {
28
36
  return this.initializeResult;
29
37
  }
38
+ get availableMethods() {
39
+ return new Set(this.methodAvailability);
40
+ }
41
+ supportsMethod(method) {
42
+ return this.methodAvailability.has(method);
43
+ }
30
44
  async initialize(options = {}) {
31
45
  if (this.initialized && this.initializeResult) {
32
46
  return this.initializeResult;
@@ -45,12 +59,15 @@ export class AcpClient {
45
59
  const result = parseInitializeResponse(raw, this.provider);
46
60
  this.initialized = true;
47
61
  this.initializeResult = result;
62
+ this.methodAvailability = new Set(deriveAcpMethodAvailability(result));
48
63
  return result;
49
64
  }
50
65
  async newSession(params) {
51
66
  this.assertInitialized("session/new");
52
67
  const raw = await this.send("session/new", { cwd: params.cwd, mcpServers: params.mcpServers ?? [] }, this.timeouts.sessionNewMs);
53
- return parseSessionNewResponse(raw, this.provider);
68
+ const response = parseSessionNewResponse(raw, this.provider);
69
+ this.augmentSessionMethods(response);
70
+ return response;
54
71
  }
55
72
  async loadSession(params) {
56
73
  this.assertInitialized("session/load");
@@ -59,7 +76,9 @@ export class AcpClient {
59
76
  cwd: params.cwd,
60
77
  mcpServers: params.mcpServers ?? [],
61
78
  }, this.timeouts.sessionLoadMs);
62
- return parseSessionLoadResponse(raw, this.provider);
79
+ const response = parseSessionLoadResponse(raw, this.provider);
80
+ this.augmentSessionMethods(response);
81
+ return response;
63
82
  }
64
83
  async prompt(params) {
65
84
  this.assertInitialized("session/prompt");
@@ -69,6 +88,61 @@ export class AcpClient {
69
88
  cancel(sessionId) {
70
89
  this.transport.notify("session/cancel", { sessionId });
71
90
  }
91
+ async resumeSession(params) {
92
+ this.assertMethodAvailable("session/resume");
93
+ const raw = await this.send("session/resume", { sessionId: params.sessionId, cwd: params.cwd, mcpServers: params.mcpServers ?? [] }, this.timeouts.sessionLoadMs);
94
+ const response = parseSessionResumeResponse(raw, this.provider);
95
+ this.augmentSessionMethods(response);
96
+ return response;
97
+ }
98
+ async listSessions(params = {}) {
99
+ this.assertMethodAvailable("session/list");
100
+ const raw = await this.send("session/list", { cursor: params.cursor });
101
+ return parseListSessionsResponse(raw, this.provider);
102
+ }
103
+ async closeSession(sessionId) {
104
+ this.assertMethodAvailable("session/close");
105
+ const raw = await this.send("session/close", { sessionId });
106
+ return parseCloseSessionResponse(raw, this.provider);
107
+ }
108
+ async deleteSession(sessionId) {
109
+ this.assertMethodAvailable("session/delete");
110
+ const raw = await this.send("session/delete", { sessionId });
111
+ return parseDeleteSessionResponse(raw, this.provider);
112
+ }
113
+ async setSessionMode(params) {
114
+ this.assertMethodAvailable("session/set_mode");
115
+ const raw = await this.send("session/set_mode", {
116
+ sessionId: params.sessionId,
117
+ modeId: params.modeId,
118
+ });
119
+ return parseSetSessionModeResponse(raw, this.provider);
120
+ }
121
+ async setSessionConfigOption(params) {
122
+ this.assertMethodAvailable("session/set_config_option");
123
+ const raw = await this.send("session/set_config_option", {
124
+ sessionId: params.sessionId,
125
+ configId: params.configId,
126
+ value: params.value,
127
+ });
128
+ return parseSetSessionConfigOptionResponse(raw, this.provider);
129
+ }
130
+ augmentSessionMethods(response) {
131
+ for (const method of sessionResponseMethods(response)) {
132
+ this.methodAvailability.add(method);
133
+ }
134
+ }
135
+ assertMethodAvailable(method) {
136
+ this.assertInitialized(method);
137
+ if (!this.methodAvailability.has(method)) {
138
+ throw new AcpMethodUnsupportedError(this.provider, method, {
139
+ reason: "capability_not_advertised",
140
+ });
141
+ }
142
+ if (AcpClient.MUTATING_METHODS.has(method) && !this.allowMutatingSessionOps) {
143
+ throw new AcpMutatingDisabledError(this.provider, method, { reason: "config_gate_off" });
144
+ }
145
+ }
72
146
  async send(method, params, timeoutMs) {
73
147
  try {
74
148
  return await this.transport.request(method, params, timeoutMs);
@@ -1,5 +1,5 @@
1
1
  import type { CliType } from "../session-manager.js";
2
- export type AcpErrorKind = "acp_disabled" | "provider_acp_disabled" | "provider_acp_unsupported" | "provider_runtime_disabled" | "provider_unavailable" | "protocol" | "timeout" | "permission_denied" | "process_exit";
2
+ export type AcpErrorKind = "acp_disabled" | "provider_acp_disabled" | "provider_acp_unsupported" | "provider_runtime_disabled" | "provider_unavailable" | "method_unsupported" | "mutating_disabled" | "protocol" | "timeout" | "permission_denied" | "process_exit";
3
3
  export declare function redactAcpMessage(input: string): string;
4
4
  export declare function redactAcpDebug(value: unknown): unknown;
5
5
  export declare function redactAcpCause(cause: unknown): unknown;
@@ -32,6 +32,14 @@ export declare class ProviderRuntimeDisabledError extends AcpError {
32
32
  export declare class ProviderUnavailableError extends AcpError {
33
33
  constructor(provider: CliType, reason: string, debug?: AcpErrorDebug);
34
34
  }
35
+ export declare class AcpMethodUnsupportedError extends AcpError {
36
+ readonly method: string;
37
+ constructor(provider: CliType, method: string, debug?: AcpErrorDebug);
38
+ }
39
+ export declare class AcpMutatingDisabledError extends AcpError {
40
+ readonly method: string;
41
+ constructor(provider: CliType, method: string, debug?: AcpErrorDebug);
42
+ }
35
43
  export declare class AcpProtocolError extends AcpError {
36
44
  readonly code?: number;
37
45
  constructor(userMessage: string, options?: {
@@ -2,6 +2,9 @@ export function redactAcpMessage(input) {
2
2
  let out = redactJsonLikeBodies(input);
3
3
  out = out.replace(/\b(bearer|token|api[_-]?key|secret)\b\s*[:=]?\s*\S+/gi, "$1 <redacted>");
4
4
  out = out.replace(/\b(sk|xai|gsk|key)-[A-Za-z0-9_-]{8,}\b/gi, "<redacted-token>");
5
+ out = out.replace(/\bAIza[A-Za-z0-9_-]{10,}/g, "<redacted-token>");
6
+ out = out.replace(/\bgh[pousr]_[A-Za-z0-9]{20,}\b/g, "<redacted-token>");
7
+ out = out.replace(/\bxox[baprs]-[A-Za-z0-9-]{10,}\b/gi, "<redacted-token>");
5
8
  out = out.replace(/(^|[^A-Za-z0-9._~\\/-])[A-Za-z]:\\[^\s"')\]}>]*/g, "$1<redacted-path>");
6
9
  out = out.replace(/(^|[^A-Za-z0-9._~\\/-])\\\\[^\s"')\]}>]+/g, "$1<redacted-path>");
7
10
  out = out.replace(/(^|[^A-Za-z0-9._~/-])~\/[^\s"')\]}>]+/g, "$1<redacted-path>");
@@ -150,6 +153,22 @@ export class ProviderUnavailableError extends AcpError {
150
153
  this.name = "ProviderUnavailableError";
151
154
  }
152
155
  }
156
+ export class AcpMethodUnsupportedError extends AcpError {
157
+ method;
158
+ constructor(provider, method, debug) {
159
+ super("method_unsupported", `ACP method ${method} is not advertised by provider ${provider}'s capability set; the agent did not declare support for it.`, { provider, debug: { method, ...(debug ?? {}) } });
160
+ this.name = "AcpMethodUnsupportedError";
161
+ this.method = method;
162
+ }
163
+ }
164
+ export class AcpMutatingDisabledError extends AcpError {
165
+ method;
166
+ constructor(provider, method, debug) {
167
+ super("mutating_disabled", `ACP mutating session op ${method} is disabled. Set allow_mutating_session_ops=true under [acp] to permit state-mutating ACP admin ops.`, { provider, debug: { method, ...(debug ?? {}) } });
168
+ this.name = "AcpMutatingDisabledError";
169
+ this.method = method;
170
+ }
171
+ }
153
172
  export class AcpProtocolError extends AcpError {
154
173
  code;
155
174
  constructor(userMessage, options) {
@@ -29,6 +29,12 @@ export type AcpProgressEvent = {
29
29
  readonly kind: "usage";
30
30
  readonly size: number;
31
31
  readonly used: number;
32
+ } | {
33
+ readonly kind: "session_complete";
34
+ readonly stopReason: string;
35
+ } | {
36
+ readonly kind: "error";
37
+ readonly message: string;
32
38
  } | {
33
39
  readonly kind: "other";
34
40
  readonly sessionUpdate: string;
@@ -37,6 +43,12 @@ export declare function summarizeContentBlock(block: ContentBlock): string;
37
43
  export declare function normalizeSessionUpdate(notification: SessionUpdateNotification): AcpProgressEvent;
38
44
  export declare class AcpEventNormalizer {
39
45
  private text;
46
+ private stopReasonValue;
47
+ private errorValue;
40
48
  handle(notification: SessionUpdateNotification): AcpProgressEvent;
49
+ completeWith(stopReason: string): AcpProgressEvent;
50
+ error(message: string): AcpProgressEvent;
41
51
  get finalText(): string;
52
+ get stopReason(): string | undefined;
53
+ get errorMessage(): string | undefined;
42
54
  }