llm-cli-gateway 2.11.0 → 2.12.0

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 (36) hide show
  1. package/.agents/skills/async-job-orchestration/SKILL.md +288 -0
  2. package/.agents/skills/implement-review-fix/SKILL.md +154 -0
  3. package/.agents/skills/multi-llm-review/SKILL.md +174 -0
  4. package/.agents/skills/public-demo-session/SKILL.md +100 -0
  5. package/.agents/skills/secure-orchestration/SKILL.md +227 -0
  6. package/.agents/skills/session-workflow/SKILL.md +271 -0
  7. package/CHANGELOG.md +63 -1
  8. package/README.md +86 -27
  9. package/dist/acp/provider-registry.js +6 -6
  10. package/dist/api-provider.d.ts +7 -0
  11. package/dist/api-provider.js +7 -0
  12. package/dist/api-request.js +1 -0
  13. package/dist/async-job-manager.d.ts +11 -1
  14. package/dist/async-job-manager.js +44 -3
  15. package/dist/config.d.ts +2 -0
  16. package/dist/config.js +3 -0
  17. package/dist/index.d.ts +40 -4
  18. package/dist/index.js +611 -158
  19. package/dist/job-store.d.ts +48 -1
  20. package/dist/job-store.js +184 -0
  21. package/dist/provider-codegen.js +3 -0
  22. package/dist/provider-tool-capabilities.d.ts +4 -0
  23. package/dist/provider-tool-capabilities.js +16 -13
  24. package/dist/upstream-contracts.d.ts +1 -0
  25. package/dist/upstream-contracts.js +202 -45
  26. package/dist/validation-orchestrator.d.ts +5 -2
  27. package/dist/validation-orchestrator.js +71 -5
  28. package/dist/validation-receipt.d.ts +68 -0
  29. package/dist/validation-receipt.js +245 -0
  30. package/dist/validation-report.d.ts +4 -2
  31. package/dist/validation-report.js +18 -1
  32. package/dist/validation-tools.js +58 -9
  33. package/npm-shrinkwrap.json +5 -5
  34. package/package.json +10 -4
  35. package/dist/xai-api-provider.d.ts +0 -43
  36. package/dist/xai-api-provider.js +0 -191
package/CHANGELOG.md CHANGED
@@ -2,7 +2,69 @@
2
2
 
3
3
  All notable changes to the llm-cli-gateway project.
4
4
 
5
- ## Unreleased
5
+ ## [2.12.0] - 2026-06-30: Validation receipts, API-provider parity, and a gateway usability pass
6
+
7
+ ### Added
8
+
9
+ - **Durable, owner-scoped receipts for cross-LLM validation runs.** A terminal validation run (every dispatched provider job terminal, and the judge if one was requested) mints exactly one immutable `validation_receipts` row enveloping the existing `validation-report.v1` structuredContent, with a deterministic `canonical_sha256` over the canonical serialization (recursively sorted object keys, preserved array order, UTF-8; the re-derived `humanReadable` is excluded). Minting is eager (the first time a run is observed terminal, via the `job_result` collection hook and `synthesize_validation`), so a receipt is captured before job rows are evicted; an explicit read is the fallback.
10
+ - **New `validation_receipt` tool.** Retrieves a receipt by `validationId` with own-or-not-found ownership: `minted` | `pending` | `expired_unminted` | `not_found`. `format: "json" | "markdown"` (markdown is a read-time rendering, never stored or hashed). `includeRawResponses` inlines full provider answer text as a read-time-only expansion pulled live per job under the same owner check (never persisted, never hashed).
11
+ - **New `validation-receipt://{validationId}` MCP resource** with the same own-or-not-found owner scoping.
12
+ - **Durable run identity (prerequisite).** `validation_runs` (plus a `validation_run_jobs` reverse index) persists each run's `validationId`, owner principal, and provider/judge job links at kickoff. The report and synthesis status enums gained a terminal `completed` value.
13
+ - The receipt tool, the resource, and the run/receipt tables exist ONLY under an implemented durable backend (today `sqlite`) with a store attached at runtime; under `memory` / `postgres` / `none` no run/receipt row is written and the tool/resource are not registered (the caller still receives a `validationId` at kickoff). Silent loss is impossible by construction.
14
+ - **Grok structured-output and session/worktree parity (Grok 0.2.73).** `grok_request` / `grok_request_async` gained `jsonSchema` (emits `--json-schema`, constrains output to a JSON Schema, implies json output), `forkSession` (emits `--fork-session`, forks a resumed session into a new ID), and `worktreeRef` (emits `--worktree-ref <REF>`; requires `nativeWorktree`, else rejected). Grok `--session-id` remains intentionally unwired (the gateway owns session-id lifecycle and cross-principal isolation).
15
+ - **Antigravity project selection (agy 1.0.13).** `gemini_request` / `gemini_request_async` gained `project` (emits `--project <ID>`) and `newProject` (emits `--new-project`); the two are mutually exclusive (rejected if both set).
16
+ - **API-provider unification: the generic `api_<name>_request` surface reaches CLI-tool parity (Slices 1-4b).** Telemetry parity (per-request usage/cost), schema parity (`promptParts`, `optimizePrompt`, `optimizeResponse`, `forceRefresh`, `sessionId`, `createNewSession`), capability-typed continuity with gateway sessions (`ApiContinuity` = `server-side-id` | `stateless-resend` | `none`), and server-side-id continuation on the generic handler. The legacy standalone `xai-api-provider` module was removed; the grok-api HTTP path now runs through the shared `api-provider` / `api-http` adapter (externally-observable response shape unchanged).
17
+ - **OpenRouter usage accounting.** New optional provider config key `usage_include` opts the OpenAI-compatible adapter into `usage: { include: true }` so token counts and end-to-end `usage.cost` (USD) are returned; off by default so strict OpenAI-compatible servers are unaffected.
18
+ - **Caller-facing skills now ship in the npm package.** The published gateway serves six caller skills (`async-job-orchestration`, `multi-llm-review`, `session-workflow`, `secure-orchestration`, `implement-review-fix`, `public-demo-session`); previously `.agents` was excluded from `package.json` `files`, so every install served zero skills. Operator/maintainer skills (the `provider-*` contract guides, `gateway-restart-surfaces`) stay out of the tarball, and the release tarball guard now scans shipped skills for host-internal leaks.
19
+
20
+ ### Changed
21
+
22
+ - **Upstream contracts refreshed to the installed provider versions.** Live `--probe-installed` probes were re-run across all six provider CLIs; ACP `targetVersion` pins bumped to claude 2.1.195, codex-cli 0.142.4, agy 1.0.13, grok 0.2.73, and devin 2026.8.18 (mistral vibe 2.17.1 unchanged), kept in sync across `upstream-contracts`, `provider-tool-capabilities`, and the ACP `provider-registry`. Acknowledged newly-advertised flags (claude `--bg`/`--background`); dropped stale acknowledgements (claude `--mcp-debug`, agy `-i`/`--version`); and acknowledged grok's `ssh` subcommand inheriting the global agent flag surface.
23
+ - **`agy update` help-probe drift silenced.** `agy update --help` uses Go's `flag` package (prints "Usage of update:" and exits 2). A new `helpProbeExitTolerant` subcommand-contract marker treats that legitimate non-zero help exit as clean rather than reporting it as drift.
24
+ - **Devin is now visible across discovery.** Devin was a fully registered request tool but omitted from the server instructions and rejected by the `list_models`, `cli_versions`, `cli_upgrade`, and `provider_tool_capabilities` enums; those now include devin (pure widening), and the stale "five providers" describe/description text was refreshed.
25
+ - **Request tool and parameter descriptions sharpened for usability.** Clarified Codex `sandboxMode` (omit = read-only), Gemini `approvalMode`/`outputFormat` (only default/yolo and text work on the agy headless path), `approvalStrategy`/`approvalPolicy` semantics, `idleTimeoutMs` (gateway 10-min idle kill; Claude stream-json only), `jsonSchema` (set `outputFormat:json`), session-resume rules (gw-* fresh-session ids are not resumable; use `resumeLatest`), the generic API params, and the mistral/devin permission defaults.
26
+
27
+ ### Reliability and error guidance
28
+
29
+ - **Result-health guards against silent success.** A Claude run that exits 0 but reports `is_error:true` (e.g. `error_max_turns`) now carries a `claude_result_error` warning and a `resultIsError` signal (the text is still returned); an exit-0 run with empty assistant output emits an `empty_output` warning. Claude `--output-format json` telemetry (usage/cost) is now parsed (it was previously dropped), and the real Codex session UUID is surfaced as `codexSessionId` so callers can resume/fork without filesystem access.
30
+ - **Actionable error remediation.** `createErrorResponse` now detects auth/login failures and appends per-CLI remediation (`errorCategory: auth_error`), categorizes the 50MB output overflow as `output_overflow`, adds async-retry guidance on wall-clock timeout (124) and an interactive/idle hint on idle timeout (125), enriches Codex failures from the JSONL stream when stderr is empty (with a resume-not-found hint), and adds a fallback hint when a non-zero exit captured no stderr.
31
+ - A whitespace-only prompt is now rejected at the prep boundary instead of being passed to the CLI.
32
+
33
+ ### Fixed
34
+
35
+ - **Cross-principal read hole on the validation collection tools.** `job_status` / `job_result` now apply the `principalCanAccess` owner check that the `llm_job_*` paths already enforce; a job owned by another principal is reported as not found.
36
+
37
+ ### Deps
38
+
39
+ - npm-minor-patch group: `smol-toml` 1.6.1 to 1.7.0 (runtime), `pg` 8.21.0 to 8.22.0, plus devDeps `@types/node` to 26.0.1, `@typescript-eslint/{eslint-plugin,parser}` 8.61.1 to 8.62.1, `eslint` 10.5.0 to 10.6.0, `prettier` 3.8.4 to 3.9.3. CI: `zizmor` 1.25.2 to 1.26.1, github-actions group bumps. Dependabot now ignores `body-parser` 2.3.0 (it transitively pulled the Socket-flagged `type-is` 2.1.0 / `content-type` 2.0.0 that the release audit blocks).
40
+
41
+ ### Not yet implemented (reserved)
42
+
43
+ - Cryptographic signing and hash chaining. The `prev_sha256` / `seq` / `signature` columns are reserved (NULL); the canonical byte definition they will build on is fixed now. Semantic enrichment (`key_points`, `evidence_cited`, `uncertainty_signals`, numeric per-model confidence) is deferred to `validation-receipt.v2` (no extraction source exists today).
44
+
45
+ ## [2.11.1] - 2026-06-22: Provider contract refresh and stable upstream scans
46
+
47
+ ### Changed
48
+
49
+ - **Upstream contracts refreshed for all providers.** Live `--probe-installed` probes were run across the installed provider CLIs. Synced contract surfaces and bumped ACP `targetVersion` pins:
50
+ - claude → 2.1.185 (added `--ax-screen-reader`, `--safe-mode` to acknowledged flags; `agents --all` to subcommand contract)
51
+ - codex → 0.141.0 (aligned `exec resume` / `exec review` / top-level `review` flag lists with current advertised surfaces)
52
+ - gemini (agy) → 1.0.10
53
+ - grok → 0.2.60 (474c2bbfc)
54
+ - mistral (vibe) → 2.17.1
55
+ - devin → 2026.7.23 (3bd47f77) (added new flags to `acknowledgedUpstreamFlags`)
56
+ - **Mistral upstream scan source stabilized.** The scanner now tracks GitHub's latest-release API for Mistral Vibe instead of the volatile HTML releases page, so `--live --fail-on-critical` no longer trips on release-page chrome/hash churn.
57
+ - **Provider skill docs updated.** All five `.agents/skills/provider-*` files bumped to metadata version 1.2. Descriptions now call out `/subcommand` behaviour changes; added current-version "Tested against" notes and usage examples for the cache levers (Grok compaction, Claude `cacheControl`).
58
+ - **Cache usage documentation expanded with concrete examples.** Added exact parameters, emitted CLI flags, and stream-json payload shapes for Grok `compactionMode`/`compactionDetail` and Claude `promptParts.cacheControl` (including the `assembleClaudeCacheBlocks` NDJSON structure with `cache_control: {type:"ephemeral", ttl:"1h"}`) to:
59
+ - `docs/personal-mcp/PROVIDER_CACHE_SURFACES.md`
60
+ - `README.md` (updated Cache-aware operation section and matrix)
61
+ - `.agents/skills/session-workflow/SKILL.md`
62
+ - Cross-LLM reviews (Claude, Codex, Mistral) on the implementation plan were incorporated: kept provider-specific levers (no new unifying `CacheDirective` abstraction); focused on documentation and discoverability.
63
+ - **Internal caching hygiene.** Promoted the ad-hoc capability cache in `provider-tool-capabilities.ts` to a documented reusable TTL structure (`CAPABILITY_CACHE`, helpers, test accessors) for easier future reuse.
64
+
65
+ ### Tests / CI
66
+
67
+ - `npm run upstream:contracts`, relevant cache/session/provider-tool tests, and build all green.
6
68
 
7
69
  ## [2.11.0] - 2026-06-18: API providers, Devin, ACP runtime, and a strip-at-publish internal-MCP boundary
8
70
 
package/README.md CHANGED
@@ -9,9 +9,9 @@
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, and Mistral (Vibe) CLIs from one MCP endpoint, with durable async jobs, session continuity, cache-aware prompting, observability, and personal-appliance setup tooling.
12
+ A Model Context Protocol (MCP) gateway for running Claude Code, Codex, Gemini, Grok, Mistral (Vibe), and Devin CLIs from one MCP endpoint, with durable async jobs, session continuity, cache-aware prompting, observability, and personal-appliance setup tooling.
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 five provider CLIs.
14
+ **Why developers try it:** one local MCP endpoint for cross-LLM validation, multi-agent coding workflows, and repeatable assistant-led setup across six provider CLIs.
15
15
 
16
16
  **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
17
 
@@ -38,7 +38,7 @@ Or use directly with `npx` from an MCP client:
38
38
 
39
39
  `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
40
 
41
- - Runs five provider CLIs through consistent sync and async MCP tools.
41
+ - Runs six provider CLIs through consistent sync and async MCP tools.
42
42
  - Persists long-running jobs, supports restart-safe result collection, deduplication, cancellation, and sync-to-async deferral.
43
43
  - Tracks sessions, real CLI resume paths, structured response metadata, and cache telemetry.
44
44
  - Supports cache-aware `promptParts`, including explicit Claude `cache_control` when opted in.
@@ -49,7 +49,7 @@ Or use directly with `npx` from an MCP client:
49
49
 
50
50
  ## Workflow Assets
51
51
 
52
- 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. 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.
52
+ 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
53
 
54
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.
55
55
 
@@ -84,7 +84,7 @@ Current personal-appliance artifacts include:
84
84
  - Docker Compose fallback: [docker/personal.compose.yml](docker/personal.compose.yml) + [docker/Dockerfile.personal](docker/Dockerfile.personal) for users who already manage containers.
85
85
  - Local setup UI artifact: [setup/ui/index.html](setup/ui/index.html)
86
86
  - Provider setup snippets: [setup/providers/](setup/providers/)
87
- - Cross-validation tools: `validate_with_models`, `second_opinion`, `compare_answers`, `red_team_review`, `consensus_check`, `ask_model`, `synthesize_validation`, `job_status`, and `job_result`.
87
+ - Cross-validation tools: `validate_with_models`, `second_opinion`, `compare_answers`, `red_team_review`, `consensus_check`, `ask_model`, `synthesize_validation`, `job_status`, `job_result`, and `validation_receipt` (plus the `validation-receipt://{validationId}` resource).
88
88
 
89
89
  ### Install / Upgrade / Uninstall (single binary)
90
90
 
@@ -157,7 +157,7 @@ docker compose -f docker/personal.compose.yml run --rm doctor
157
157
 
158
158
  ### Core Capabilities
159
159
 
160
- - **Multi-LLM Orchestration**: Unified interface for Claude Code, Codex, Gemini, Grok, and Mistral (Vibe) CLIs
160
+ - **Multi-LLM Orchestration**: Unified interface for Claude Code, Codex, Gemini, Grok, Mistral (Vibe), and Devin CLIs
161
161
  - **Session Management**: Track and resume conversations across all CLIs with persistent storage
162
162
  - **Gateway-owned worktrees**: Run any sync or async provider request inside a managed git worktree, with per-session reuse and cleanup
163
163
  - **Token Optimization**: Automatic 44% reduction on prompts, 37% on responses (opt-in)
@@ -169,11 +169,11 @@ docker compose -f docker/personal.compose.yml run --rm doctor
169
169
  - **SQLite Flight Recorder**: Every request/response logged to `~/.llm-cli-gateway/logs.db` with correlation IDs, token usage, duration, retry counts, and circuit breaker state. Browse with [Datasette](https://datasette.io/): `datasette ~/.llm-cli-gateway/logs.db`
170
170
  - **Structured Metadata**: Tool responses include machine-readable `structuredContent` (model, cli, correlationId, sessionId, durationMs, token counts)
171
171
  - **Cache observability resources**: `cache-state://global`, `cache-state://session/{id}`, and `cache-state://prefix/{hash}` MCP resources return aggregate cache hit/miss/savings — tokens and hashes only, no prompt text. `session_get` includes a `cacheState` block when the session has prior requests.
172
- - **Provider capability inventory**: `provider_tool_capabilities` and `provider-tools://catalog` expose the gateway request fields, supported/degraded provider controls, local skill/tool discovery, and safe config-surface hints for Claude Code, Codex CLI, Gemini/Antigravity, Grok CLI/API, and Mistral Vibe. `doctor --json` includes a compact `provider_capabilities` summary for setup assistants.
172
+ - **Provider capability inventory**: `provider_tool_capabilities` and `provider-tools://catalog` expose the gateway request fields, supported/degraded provider controls, local skill/tool discovery, and safe config-surface hints for Claude Code, Codex CLI, Gemini/Antigravity, Grok CLI/API, Mistral Vibe, and Cognition Devin. `doctor --json` includes a compact `provider_capabilities` summary for setup assistants.
173
173
 
174
174
  ### Cache-aware operation
175
175
 
176
- Every `*_request` and `*_request_async` tool accepts an optional `promptParts` field that structures the prompt for better cache hit rates. The gateway concatenates the parts in canonical order (`system → tools → context → task`) so that the stable prefix bytes precede the volatile task tail unchanged across calls, letting each provider's automatic prompt-caching land on the same content hash each time.
176
+ Every `*_request` and `*_request_async` tool except `devin_request` / `devin_request_async` accepts an optional `promptParts` field that structures the prompt for better cache hit rates (the Devin headless path takes a plain `prompt` only). The gateway concatenates the parts in canonical order (`system → tools → context → task`) so that the stable prefix bytes precede the volatile task tail unchanged across calls, letting each provider's automatic prompt-caching land on the same content hash each time.
177
177
 
178
178
  ```json
179
179
  {
@@ -188,17 +188,47 @@ Every `*_request` and `*_request_async` tool accepts an optional `promptParts` f
188
188
 
189
189
  `prompt` and `promptParts` are mutually exclusive — pass exactly one.
190
190
 
191
- Per-CLI capability matrix:
191
+ Per-CLI capability matrix (prefix discipline is automatic via `promptParts` for all providers except Devin, which has no `promptParts` surface; explicit levers are provider-specific):
192
+
193
+ | CLI | Prefix discipline | Explicit lever(s) |
194
+ | ------- | ----------------- | ----------------- |
195
+ | 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
+
201
+ **Claude example (explicit cacheControl)**
202
+
203
+ ```ts
204
+ claude_request({
205
+ promptParts: {
206
+ system: "You are a helpful code reviewer.",
207
+ context: "<long stable file dump>",
208
+ task: "Review the diff.",
209
+ cacheControl: { system: true, context: true } // task is never marked
210
+ },
211
+ outputFormat: "stream-json"
212
+ })
213
+ ```
214
+
215
+ Gateway emits the `stream-json` stdin path with `cache_control: {type:"ephemeral", ttl:"1h"}` on marked blocks only.
216
+
217
+ **Grok example (compaction)**
218
+
219
+ ```ts
220
+ grok_request({
221
+ promptParts: { system: "...", context: "...", task: "..." },
222
+ compactionMode: "segments",
223
+ compactionDetail: "balanced"
224
+ })
225
+ ```
226
+
227
+ Emits `--compaction-mode segments --compaction-detail balanced`.
192
228
 
193
- | CLI | Prefix discipline (auto via `promptParts`) | Explicit `cache_control` emission |
194
- | ------- | ------------------------------------------ | ---------------------------------------------------------------------------- |
195
- | claude | yes | yes, opt-in via `promptParts.cacheControl` and `outputFormat: "stream-json"` |
196
- | codex | yes | n/a (OpenAI implicit cache, no CLI lever) |
197
- | gemini | yes | n/a (implicit prefix cache server-side) |
198
- | grok | yes | n/a (no surfaced cache lever) |
199
- | mistral | yes | n/a (no surfaced cache lever) |
229
+ See `docs/personal-mcp/PROVIDER_CACHE_SURFACES.md` for full surfaces, telemetry differences (e.g. Grok `-p` vs ACP), exact stream-json payload shapes, and cross-LLM review notes.
200
230
 
201
- Opt-in flags (all default off) live under `[cache_awareness]` in `~/.llm-cli-gateway/config.toml`. See `docs/personal-mcp/PROVIDER_CACHE_SURFACES.md` for the per-model minimum cacheable token thresholds and field-name divergences.
231
+ Opt-in flags (all default off) live under `[cache_awareness]` in `~/.llm-cli-gateway/config.toml`.
202
232
 
203
233
  ### Reliability & Performance
204
234
 
@@ -210,7 +240,7 @@ Opt-in flags (all default off) live under `[cache_awareness]` in `~/.llm-cli-gat
210
240
 
211
241
  ### Security & Quality
212
242
 
213
- - **Comprehensive Testing**: 1,000+ tests covering unit, integration, and regression scenarios with real CLI execution
243
+ - **Comprehensive Testing**: 1,700+ tests covering unit, integration, and regression scenarios with real CLI execution
214
244
  - **Input Validation**: Zod schemas prevent injection attacks
215
245
  - **No Secret Leakage**: Generic session descriptions only (file permissions 0o600)
216
246
  - **No ReDoS**: Bounded regex patterns prevent catastrophic backtracking
@@ -357,6 +387,9 @@ The personal-appliance surface exposes simplified validation tools for non-devel
357
387
  - `synthesize_validation`: run an explicit judge model after provider results have been collected.
358
388
  - `list_available_models`: list the models each provider CLI exposes through the simplified surface.
359
389
  - `job_status` and `job_result`: poll and collect validation job outputs.
390
+ - `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).
391
+
392
+ The same receipt is also exposed as the `validation-receipt://{validationId}` MCP resource (same durable gate and own-or-not-found owner scoping).
360
393
 
361
394
  The validation report preserves per-provider disagreement. Optional judge synthesis is explicit about which provider produced the judge job.
362
395
 
@@ -501,6 +534,8 @@ Execute a Google Antigravity CLI (`agy`) request with session support.
501
534
  - `approvalStrategy` (string, optional): `"legacy"` (default) or `"mcp_managed"`
502
535
  - `approvalPolicy` (string, optional): `"strict"`, `"balanced"`, or `"permissive"`
503
536
  - `includeDirs` (string[], optional): Additional workspace directories (passed as `--add-dir`)
537
+ - `project` (string, optional): Select the Antigravity project for this session (`--project <ID>`); mutually exclusive with `newProject`
538
+ - `newProject` (boolean, optional): Create a new Antigravity project for this session (`--new-project`); mutually exclusive with `project`
504
539
  - `sandbox` (boolean, optional): Run Antigravity in sandbox mode (`--sandbox`)
505
540
  - `outputFormat` (string, optional): `text` only. Antigravity print mode emits text; `json` and `stream-json` are rejected.
506
541
  - `mcpServers`, `allowedTools`, `policyFiles`, `adminPolicyFiles`, `attachments` (string[], optional) and `skipTrust` (boolean, optional): **Unsupported by Antigravity CLI** — non-empty values (or `skipTrust: true`) are rejected with an explanatory error. Retained in the schema for caller parity.
@@ -572,6 +607,9 @@ Execute a Grok CLI (xAI) request with session support.
572
607
  - `restoreCode` (boolean, optional): Check out the original session commit when resuming
573
608
  - `leaderSocket` (string, optional): Custom leader socket path (`--leader-socket`, Grok 0.2.32+; default `~/.grok/leader.sock`) — targets an isolated leader process, e.g. a local/branch Grok build
574
609
  - `nativeWorktree` (boolean|string, optional): Grok's own `--worktree` flag (`true` → bare, string → named); distinct from the gateway `worktree` option
610
+ - `worktreeRef` (string, optional): Branch/tag/commit to base the native worktree on (`--worktree-ref`); requires `nativeWorktree`
611
+ - `forkSession` (boolean, optional): Fork the resumed session into a new branch instead of appending to it
612
+ - `jsonSchema` (string|object, optional): JSON Schema (string or object) constraining structured output (`--json-schema`)
575
613
  - `worktree` (boolean|object, optional): Run inside a gateway-owned git worktree (slice λ)
576
614
  - `promptParts` (object, optional): Cache-aware structured prompt `{ system?, tools?, context?, task }`; mutually exclusive with `prompt`
577
615
  - `optimizePrompt` (boolean, optional): Optimize prompt for token efficiency, default: false
@@ -848,9 +886,28 @@ Run a Mistral Vibe agentic coding request. Like `grok_request` in shape, but wit
848
886
  - `idleTimeoutMs` (integer, optional): Kill a stuck process after output inactivity; 30,000 to 3,600,000 ms
849
887
  - `forceRefresh` (boolean, optional): Bypass dedup and force a fresh CLI run, default: false
850
888
 
851
- ##### `claude_request_async` / `codex_request_async` / `gemini_request_async` / `grok_request_async` / `mistral_request_async`
889
+ ##### `devin_request`
890
+
891
+ Run a Cognition Devin CLI request synchronously (headless print mode, `devin -p`). Auto-defers to a pollable job past the sync deadline when async jobs are enabled.
892
+
893
+ **Parameters:**
894
+
895
+ - `prompt` (string, optional*): Prompt text for Devin CLI (1-100,000 chars). Required in practice; `promptFile` is additive
896
+ - `model` (string, optional): Model name or alias (e.g. `opus`, `latest`)
897
+ - `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)
898
+ - `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
899
+ - `promptFile` (string, optional): Load the initial prompt from a file (`--prompt-file`)
900
+ - `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`
901
+ - `resumeLatest` (boolean, optional): Resume the most recent Devin session in cwd (`--continue`)
902
+ - `createNewSession` (boolean, optional): Force a new session
903
+ - `optimizePrompt` / `optimizeResponse` (boolean, optional): Token-efficiency optimisation, default: false
904
+ - `correlationId` (string, optional): Request trace ID (auto-generated if omitted)
905
+ - `idleTimeoutMs` (integer, optional): Kill a stuck process after output inactivity; 30,000 to 3,600,000 ms
906
+ - `forceRefresh` (boolean, optional): Bypass dedup and force a fresh CLI run, default: false
907
+
908
+ ##### `claude_request_async` / `codex_request_async` / `gemini_request_async` / `grok_request_async` / `mistral_request_async` / `devin_request_async`
852
909
 
853
- Start a long-running Claude, Codex, Gemini, Grok, or Mistral request without waiting for completion in the same MCP call.
910
+ Start a long-running Claude, Codex, Gemini, Grok, Mistral, or Devin request without waiting for completion in the same MCP call.
854
911
 
855
912
  Use this flow when analysis/runtime can exceed client tool-call limits:
856
913
 
@@ -909,7 +966,7 @@ Return the gateway's declared provider CLI contracts, optionally probing the ins
909
966
 
910
967
  **Parameters:**
911
968
 
912
- - `cli` (string, optional): Filter (`claude|codex|gemini|grok|mistral`)
969
+ - `cli` (string, optional): Filter (`claude|codex|gemini|grok|mistral|devin`)
913
970
  - `probeInstalled` (boolean, optional, default `false`): Run local `--help` probes and compare advertised flags against the declared contract — strongly recommended after any provider CLI upgrade. The probe reports `missingFlags`, `extraFlags`, `acknowledgedExtraFlags` (known upstream-only flags filtered from `extraFlags`), `discoveredFlags`, and stale-marker `warnings`.
914
971
 
915
972
  #### Session Management Tools
@@ -920,7 +977,7 @@ Create a new session for a specific CLI.
920
977
 
921
978
  **Parameters:**
922
979
 
923
- - `cli` (string, required): CLI to create session for ("claude", "codex", "gemini", "grok", "mistral")
980
+ - `cli` (string, required): CLI to create session for ("claude", "codex", "gemini", "grok", "mistral", "devin")
924
981
  - `description` (string, optional): Description for the session
925
982
  - `setAsActive` (boolean, optional): Set as active session, default: true
926
983
 
@@ -940,7 +997,7 @@ List all sessions, optionally filtered by CLI.
940
997
 
941
998
  **Parameters:**
942
999
 
943
- - `cli` (string, optional): Filter by CLI ("claude", "codex", "gemini", "grok", "mistral")
1000
+ - `cli` (string, optional): Filter by CLI ("claude", "codex", "gemini", "grok", "mistral", "devin")
944
1001
 
945
1002
  **Response includes:**
946
1003
 
@@ -989,7 +1046,7 @@ List available models for each CLI.
989
1046
 
990
1047
  **Parameters:**
991
1048
 
992
- - `cli` (string, optional): Specific CLI to list models for ("claude", "codex", "gemini", "grok", "mistral")
1049
+ - `cli` (string, optional): Specific CLI to list models for ("claude", "codex", "gemini", "grok", "mistral", "devin")
993
1050
 
994
1051
  **Response includes:**
995
1052
 
@@ -1037,7 +1094,7 @@ inputs.
1037
1094
 
1038
1095
  **Parameters:**
1039
1096
 
1040
- - `cli` (string, optional): Provider filter (`"claude"`, `"codex"`, `"gemini"`, `"grok"`, `"grok_api"`, or `"mistral"`)
1097
+ - `cli` (string, optional): Provider filter (`"claude"`, `"codex"`, `"gemini"`, `"grok"`, `"mistral"`, `"devin"`, or `"grok_api"`)
1041
1098
  - `includeSkills` (boolean, default `true`): Include bounded local skill discovery
1042
1099
  - `includeProviderTools` (boolean, default `true`): Include provider-native tools extracted from discovered skills
1043
1100
  - `includeUnsupported` (boolean, default `true`): Include explicit unsupported/degraded input records
@@ -1057,6 +1114,7 @@ Equivalent MCP resources:
1057
1114
  - `provider-tools://grok`
1058
1115
  - `provider-tools://grok_api`
1059
1116
  - `provider-tools://mistral`
1117
+ - `provider-tools://devin`
1060
1118
 
1061
1119
  `doctor --json` also emits a compact `provider_capabilities` block with the
1062
1120
  same schema version, per-provider request tool names, supported feature names,
@@ -1070,7 +1128,7 @@ Report installed CLI versions.
1070
1128
 
1071
1129
  **Parameters:**
1072
1130
 
1073
- - `cli` (string, optional): Specific CLI to inspect ("claude", "codex", "gemini", "grok", "mistral")
1131
+ - `cli` (string, optional): Specific CLI to inspect ("claude", "codex", "gemini", "grok", "mistral", "devin")
1074
1132
 
1075
1133
  ##### `cli_upgrade`
1076
1134
 
@@ -1078,7 +1136,7 @@ Plan or run an upgrade for one CLI.
1078
1136
 
1079
1137
  **Parameters:**
1080
1138
 
1081
- - `cli` (string, required): CLI to upgrade ("claude", "codex", "gemini", "grok", "mistral")
1139
+ - `cli` (string, required): CLI to upgrade ("claude", "codex", "gemini", "grok", "mistral", "devin")
1082
1140
  - `target` (string, optional): Package tag/version/target, default: `latest`
1083
1141
  - `dryRun` (boolean, optional): Return the upgrade plan without running it, default: `true`
1084
1142
  - `timeoutMs` (number, optional): Upgrade timeout when `dryRun=false`
@@ -1093,6 +1151,7 @@ Plan or run an upgrade for one CLI.
1093
1151
  - Grok latest: `grok update`
1094
1152
  - Grok explicit target: `grok update --version <target>`
1095
1153
  - Mistral (Vibe): dispatches to the detected installer (`pip`/`uv`/`brew`); errors with guidance when none is detected (Vibe ships no self-update command)
1154
+ - Devin latest: `devin update` (self-update; explicit version targets are unsupported)
1096
1155
 
1097
1156
  **Example dry run:**
1098
1157
 
@@ -4,7 +4,7 @@ const ACP_PROVIDER_REGISTRY = Object.freeze({
4
4
  displayName: "Mistral Vibe",
5
5
  status: "native_smoke_passed",
6
6
  supportKind: "native",
7
- targetVersion: "vibe 2.14.1",
7
+ targetVersion: "vibe 2.17.1",
8
8
  entrypoint: Object.freeze({ command: "vibe-acp", args: Object.freeze([]) }),
9
9
  runtimeEnabledDefault: false,
10
10
  shipRuntimePilot: true,
@@ -17,7 +17,7 @@ const ACP_PROVIDER_REGISTRY = Object.freeze({
17
17
  displayName: "xAI Grok CLI",
18
18
  status: "native_smoke_passed",
19
19
  supportKind: "native",
20
- targetVersion: "grok 0.2.50 (cadf94855)",
20
+ targetVersion: "grok 0.2.73 (9ff14c43bb)",
21
21
  entrypoint: Object.freeze({ command: "grok", args: Object.freeze(["agent", "stdio"]) }),
22
22
  runtimeEnabledDefault: false,
23
23
  shipRuntimePilot: true,
@@ -30,7 +30,7 @@ const ACP_PROVIDER_REGISTRY = Object.freeze({
30
30
  displayName: "OpenAI Codex CLI",
31
31
  status: "adapter_mediated_deferred",
32
32
  supportKind: "adapter_mediated",
33
- targetVersion: "codex-cli 0.139.0",
33
+ targetVersion: "codex-cli 0.142.4",
34
34
  entrypoint: null,
35
35
  runtimeEnabledDefault: false,
36
36
  shipRuntimePilot: false,
@@ -43,7 +43,7 @@ const ACP_PROVIDER_REGISTRY = Object.freeze({
43
43
  displayName: "Anthropic Claude Code",
44
44
  status: "adapter_mediated_deferred",
45
45
  supportKind: "adapter_mediated",
46
- targetVersion: "claude 2.1.175",
46
+ targetVersion: "claude 2.1.195",
47
47
  entrypoint: null,
48
48
  runtimeEnabledDefault: false,
49
49
  shipRuntimePilot: false,
@@ -56,7 +56,7 @@ const ACP_PROVIDER_REGISTRY = Object.freeze({
56
56
  displayName: "Google Antigravity",
57
57
  status: "absent_watchlist",
58
58
  supportKind: "none",
59
- targetVersion: "agy 1.0.7",
59
+ targetVersion: "agy 1.0.13",
60
60
  entrypoint: null,
61
61
  runtimeEnabledDefault: false,
62
62
  shipRuntimePilot: false,
@@ -69,7 +69,7 @@ const ACP_PROVIDER_REGISTRY = Object.freeze({
69
69
  displayName: "Cognition Devin CLI",
70
70
  status: "native_smoke_passed",
71
71
  supportKind: "native",
72
- targetVersion: "devin 2026.5.26-8 (1a388fa9)",
72
+ targetVersion: "devin 2026.8.18 (16737566)",
73
73
  entrypoint: Object.freeze({ command: "devin", args: Object.freeze(["acp"]) }),
74
74
  runtimeEnabledDefault: false,
75
75
  shipRuntimePilot: true,
@@ -2,6 +2,7 @@ import type { URL } from "node:url";
2
2
  import type { Logger } from "./logger.js";
3
3
  import { ApiHttpError } from "./api-http.js";
4
4
  export type ApiProviderKind = "openai-compatible" | "anthropic" | "xai-responses";
5
+ export type ApiContinuity = "server-side-id" | "stateless-resend" | "none";
5
6
  export interface ApiChatMessage {
6
7
  role: "system" | "user" | "assistant";
7
8
  content: string;
@@ -17,6 +18,7 @@ export interface ApiRequest {
17
18
  reasoningEffort?: "none" | "low" | "medium" | "high";
18
19
  timeoutMs?: number;
19
20
  previousResponseId?: string;
21
+ usageInclude?: boolean;
20
22
  }
21
23
  export interface ApiUsage {
22
24
  inputTokens?: number;
@@ -32,10 +34,12 @@ export interface ApiResult {
32
34
  raw: unknown;
33
35
  httpStatus: number;
34
36
  responseId?: string | null;
37
+ status?: string | null;
35
38
  }
36
39
  export interface ApiProvider {
37
40
  readonly name: string;
38
41
  readonly kind: ApiProviderKind;
42
+ readonly continuity: ApiContinuity;
39
43
  endpointUrl(baseUrl: string): URL;
40
44
  buildBody(req: ApiRequest): Record<string, unknown>;
41
45
  parseResult(httpStatus: number, body: string): ApiResult;
@@ -45,6 +49,7 @@ export interface ApiProvider {
45
49
  export declare class OpenAiCompatibleProvider implements ApiProvider {
46
50
  readonly name: string;
47
51
  readonly kind: "openai-compatible";
52
+ readonly continuity: "stateless-resend";
48
53
  constructor(name: string);
49
54
  endpointUrl(baseUrl: string): URL;
50
55
  buildBody(req: ApiRequest): Record<string, unknown>;
@@ -57,6 +62,7 @@ export declare class AnthropicProvider implements ApiProvider {
57
62
  readonly name: string;
58
63
  private readonly anthropicVersion;
59
64
  readonly kind: "anthropic";
65
+ readonly continuity: "stateless-resend";
60
66
  constructor(name: string, anthropicVersion?: string);
61
67
  endpointUrl(baseUrl: string): URL;
62
68
  buildBody(req: ApiRequest): Record<string, unknown>;
@@ -67,6 +73,7 @@ export declare class AnthropicProvider implements ApiProvider {
67
73
  export declare class XaiResponsesProvider implements ApiProvider {
68
74
  readonly name: string;
69
75
  readonly kind: "xai-responses";
76
+ readonly continuity: "server-side-id";
70
77
  constructor(name: string);
71
78
  endpointUrl(baseUrl: string): URL;
72
79
  buildBody(req: ApiRequest): Record<string, unknown>;
@@ -15,6 +15,7 @@ function firstNumber(...candidates) {
15
15
  export class OpenAiCompatibleProvider {
16
16
  name;
17
17
  kind = "openai-compatible";
18
+ continuity = "stateless-resend";
18
19
  constructor(name) {
19
20
  this.name = name;
20
21
  }
@@ -32,6 +33,8 @@ export class OpenAiCompatibleProvider {
32
33
  body.temperature = req.temperature;
33
34
  if (req.topP !== undefined)
34
35
  body.top_p = req.topP;
36
+ if (req.usageInclude)
37
+ body.usage = { include: true };
35
38
  return body;
36
39
  }
37
40
  parseResult(httpStatus, body) {
@@ -46,6 +49,7 @@ export class OpenAiCompatibleProvider {
46
49
  inputTokens: firstNumber(usage.prompt_tokens, usage.input_tokens),
47
50
  outputTokens: firstNumber(usage.completion_tokens, usage.output_tokens),
48
51
  cacheReadTokens: firstNumber(usage?.prompt_tokens_details?.cached_tokens),
52
+ costUsd: firstNumber(usage.cost),
49
53
  raw: usage,
50
54
  },
51
55
  raw: parsed,
@@ -65,6 +69,7 @@ export class AnthropicProvider {
65
69
  name;
66
70
  anthropicVersion;
67
71
  kind = "anthropic";
72
+ continuity = "stateless-resend";
68
73
  constructor(name, anthropicVersion = DEFAULT_ANTHROPIC_VERSION) {
69
74
  this.name = name;
70
75
  this.anthropicVersion = anthropicVersion;
@@ -152,6 +157,7 @@ function extractXaiResponseText(parsed) {
152
157
  export class XaiResponsesProvider {
153
158
  name;
154
159
  kind = "xai-responses";
160
+ continuity = "server-side-id";
155
161
  constructor(name) {
156
162
  this.name = name;
157
163
  }
@@ -186,6 +192,7 @@ export class XaiResponsesProvider {
186
192
  const usage = parsed?.usage ?? {};
187
193
  return {
188
194
  responseId: typeof parsed?.id === "string" ? parsed.id : null,
195
+ status: typeof parsed?.status === "string" ? parsed.status : null,
189
196
  model: typeof parsed?.model === "string" ? parsed.model : "unknown",
190
197
  text: extractXaiResponseText(parsed),
191
198
  usage: {
@@ -47,5 +47,6 @@ export function prepareApiRequest(runtime, params) {
47
47
  reasoningEffort: params.reasoningEffort,
48
48
  timeoutMs: params.timeoutMs,
49
49
  previousResponseId: params.previousResponseId,
50
+ usageInclude: runtime.usageInclude,
50
51
  };
51
52
  }
@@ -2,7 +2,10 @@ import type { Logger } from "./logger.js";
2
2
  import { type JobHealth } from "./process-monitor.js";
3
3
  import { JobStore } from "./job-store.js";
4
4
  import { type FlightRecorderLike } from "./flight-recorder.js";
5
- import { type ApiProvider, type ApiRequest } from "./api-provider.js";
5
+ import type { ValidationRunStore } from "./job-store.js";
6
+ import { type ApiProvider, type ApiRequest, type ApiUsage } from "./api-provider.js";
7
+ export declare function extractApiHttpStatus(error: unknown): number | null;
8
+ export declare function extractApiErrorBody(error: unknown): string | undefined;
6
9
  export type LlmCli = "claude" | "codex" | "gemini" | "grok" | "mistral" | "devin";
7
10
  export type JobProvider = LlmCli | (string & {});
8
11
  export type AsyncJobStatus = "running" | "completed" | "failed" | "canceled" | "orphaned";
@@ -41,6 +44,11 @@ export interface AsyncJobResult extends AsyncJobSnapshot {
41
44
  stderr: string;
42
45
  stdoutTruncated: boolean;
43
46
  stderrTruncated: boolean;
47
+ apiUsage?: ApiUsage;
48
+ httpStatus?: number | null;
49
+ responseId?: string | null;
50
+ model?: string;
51
+ errorBody?: string;
44
52
  }
45
53
  export interface StartJobOptions {
46
54
  cwd?: string;
@@ -72,6 +80,7 @@ export declare class AsyncJobManager {
72
80
  private buildOrphanFlightResult;
73
81
  checkStalledJobs(now?: number): void;
74
82
  hasStore(): boolean;
83
+ getValidationRunStore(): ValidationRunStore | null;
75
84
  private emitMetrics;
76
85
  private evictCompletedJobs;
77
86
  private buildRequestKey;
@@ -91,6 +100,7 @@ export declare class AsyncJobManager {
91
100
  private fireOnComplete;
92
101
  private writeFlightComplete;
93
102
  private safeExtractUsage;
103
+ private httpUsage;
94
104
  armFlightCompleteForDeferral(jobId: string): void;
95
105
  private safeStoreCall;
96
106
  private maybeFlushOutput;