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.
- package/CHANGELOG.md +97 -0
- package/README.md +53 -26
- package/dist/acp/client.d.ts +29 -1
- package/dist/acp/client.js +78 -4
- package/dist/acp/errors.d.ts +9 -1
- package/dist/acp/errors.js +19 -0
- package/dist/acp/event-normalizer.d.ts +12 -0
- package/dist/acp/event-normalizer.js +16 -0
- package/dist/acp/flight-redaction.d.ts +3 -0
- package/dist/acp/flight-redaction.js +3 -0
- package/dist/acp/permission-bridge.js +11 -5
- package/dist/acp/process-manager.d.ts +2 -1
- package/dist/acp/process-manager.js +43 -4
- package/dist/acp/provider-registry.js +19 -11
- package/dist/acp/runtime.d.ts +8 -0
- package/dist/acp/runtime.js +47 -4
- package/dist/acp/types.d.ts +3083 -55
- package/dist/acp/types.js +242 -5
- package/dist/async-job-manager.d.ts +2 -0
- package/dist/async-job-manager.js +11 -0
- package/dist/codex-json-parser.d.ts +1 -0
- package/dist/codex-json-parser.js +6 -0
- package/dist/config.d.ts +8 -0
- package/dist/config.js +47 -0
- package/dist/flight-recorder.d.ts +2 -0
- package/dist/flight-recorder.js +20 -0
- package/dist/gemini-json-parser.d.ts +2 -0
- package/dist/gemini-json-parser.js +45 -8
- package/dist/grok-json-parser.d.ts +14 -0
- package/dist/grok-json-parser.js +156 -0
- package/dist/index.d.ts +47 -2
- package/dist/index.js +484 -119
- package/dist/job-store.d.ts +45 -7
- package/dist/job-store.js +138 -17
- package/dist/model-registry.d.ts +1 -0
- package/dist/model-registry.js +46 -0
- package/dist/oauth.js +2 -2
- package/dist/postgres-job-store-worker.d.ts +1 -0
- package/dist/postgres-job-store-worker.js +327 -0
- package/dist/pricing.d.ts +3 -2
- package/dist/provider-acp-capabilities.d.ts +52 -0
- package/dist/provider-acp-capabilities.js +101 -0
- package/dist/provider-admin-tools.d.ts +100 -0
- package/dist/provider-admin-tools.js +572 -0
- package/dist/provider-capability-cache.d.ts +46 -0
- package/dist/provider-capability-cache.js +248 -0
- package/dist/provider-capability-discovery.d.ts +85 -0
- package/dist/provider-capability-discovery.js +461 -0
- package/dist/provider-capability-resolver.d.ts +29 -0
- package/dist/provider-capability-resolver.js +92 -0
- package/dist/provider-definition-assertions.d.ts +9 -0
- package/dist/provider-definition-assertions.js +147 -0
- package/dist/provider-definitions.d.ts +127 -0
- package/dist/provider-definitions.js +758 -0
- package/dist/provider-help-parser.d.ts +34 -0
- package/dist/provider-help-parser.js +203 -0
- package/dist/provider-model-discovery.d.ts +30 -0
- package/dist/provider-model-discovery.js +229 -0
- package/dist/provider-output-metadata.d.ts +7 -0
- package/dist/provider-output-metadata.js +68 -0
- package/dist/provider-schema-builder.d.ts +22 -0
- package/dist/provider-schema-builder.js +55 -0
- package/dist/provider-surface-generator.d.ts +98 -0
- package/dist/provider-surface-generator.js +140 -0
- package/dist/provider-tool-capabilities.d.ts +3 -2
- package/dist/provider-tool-capabilities.js +77 -62
- package/dist/request-helpers.d.ts +37 -4
- package/dist/request-helpers.js +134 -0
- package/dist/resources.d.ts +6 -1
- package/dist/resources.js +64 -192
- package/dist/session-manager.js +18 -11
- package/dist/stream-json-parser.d.ts +1 -0
- package/dist/stream-json-parser.js +3 -0
- package/dist/upstream-contracts.d.ts +17 -1
- package/dist/upstream-contracts.js +209 -36
- package/npm-shrinkwrap.json +2 -2
- 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
|
-
|
|
12
|
+
**Cross-model review without rebuilding your agent stack.**
|
|
13
13
|
|
|
14
|
-
|
|
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
|
|
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
|
|
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 }
|
|
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
|
|
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
|
|
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 (
|
|
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;
|
|
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
|
-
- **
|
|
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
|
package/dist/acp/client.d.ts
CHANGED
|
@@ -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;
|
package/dist/acp/client.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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);
|
package/dist/acp/errors.d.ts
CHANGED
|
@@ -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?: {
|
package/dist/acp/errors.js
CHANGED
|
@@ -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
|
}
|