llm-cli-gateway 1.1.0 → 1.5.4
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 +87 -0
- package/README.md +226 -9
- package/dist/approval-manager.d.ts +1 -1
- package/dist/async-job-manager.d.ts +75 -4
- package/dist/async-job-manager.js +303 -19
- package/dist/auth.d.ts +15 -0
- package/dist/auth.js +46 -0
- package/dist/cli-updater.d.ts +55 -0
- package/dist/cli-updater.js +248 -0
- package/dist/codex-json-parser.d.ts +34 -0
- package/dist/codex-json-parser.js +105 -0
- package/dist/doctor.d.ts +110 -0
- package/dist/doctor.js +280 -0
- package/dist/endpoint-exposure.d.ts +22 -0
- package/dist/endpoint-exposure.js +231 -0
- package/dist/executor.d.ts +2 -0
- package/dist/executor.js +2 -2
- package/dist/flight-recorder.d.ts +3 -1
- package/dist/flight-recorder.js +31 -2
- package/dist/gateway-server.d.ts +2 -0
- package/dist/gateway-server.js +1 -0
- package/dist/gemini-json-parser.d.ts +21 -0
- package/dist/gemini-json-parser.js +47 -0
- package/dist/health.d.ts +7 -0
- package/dist/health.js +22 -0
- package/dist/http-transport.d.ts +22 -0
- package/dist/http-transport.js +164 -0
- package/dist/index.d.ts +210 -2
- package/dist/index.js +2880 -1037
- package/dist/job-store.d.ts +84 -0
- package/dist/job-store.js +251 -0
- package/dist/logger.d.ts +9 -0
- package/dist/logger.js +14 -0
- package/dist/model-registry.d.ts +14 -0
- package/dist/model-registry.js +478 -134
- package/dist/provider-login-guidance.d.ts +21 -0
- package/dist/provider-login-guidance.js +98 -0
- package/dist/provider-status.d.ts +41 -0
- package/dist/provider-status.js +203 -0
- package/dist/request-helpers.d.ts +525 -4
- package/dist/request-helpers.js +653 -0
- package/dist/resources.js +88 -0
- package/dist/session-manager-pg.js +2 -0
- package/dist/session-manager.d.ts +1 -1
- package/dist/session-manager.js +3 -1
- package/dist/validation-normalizer.d.ts +23 -0
- package/dist/validation-normalizer.js +79 -0
- package/dist/validation-orchestrator.d.ts +47 -0
- package/dist/validation-orchestrator.js +145 -0
- package/dist/validation-prompts.d.ts +15 -0
- package/dist/validation-prompts.js +52 -0
- package/dist/validation-report.d.ts +57 -0
- package/dist/validation-report.js +129 -0
- package/dist/validation-tools.d.ts +7 -0
- package/dist/validation-tools.js +198 -0
- package/package.json +16 -6
- package/setup/status.schema.json +271 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,93 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to the llm-cli-gateway project.
|
|
4
4
|
|
|
5
|
+
## [1.5.4] - 2026-05-19
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- Disable the default shared SQLite flight recorder during Vitest runs so parallel test workers do not race on `~/.llm-cli-gateway/logs.db` in GitHub Actions.
|
|
10
|
+
- Keep the npm publish job under the public mirror's hosted-runner limit by installing without lifecycle scripts/audit, building once, verifying package contents, and leaving the full suite to CI.
|
|
11
|
+
|
|
12
|
+
## [1.5.3] - 2026-05-19
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
|
|
16
|
+
- Align npm and PyPI release versions at 1.5.3.
|
|
17
|
+
- Publish npm from the build already verified by CI instead of re-running `prepublishOnly` inside `npm publish`, which was causing the release publish step to be cancelled.
|
|
18
|
+
- Add a PyPI tag/version guard so future release jobs fail before upload when `integrations/llm-plugin/pyproject.toml` does not match the release tag.
|
|
19
|
+
|
|
20
|
+
## [1.5.2] - 2026-05-19
|
|
21
|
+
|
|
22
|
+
### Fixed
|
|
23
|
+
|
|
24
|
+
- **CI publish workflows fixed.** Both v1.5.0 and v1.5.1 npm + PyPI publish workflows failed; this release unblocks them:
|
|
25
|
+
- **`src/__tests__/session-manager.test.ts:437` — "should update lastUsedAt but not createdAt" was a broken test.** It used `setTimeout(...)` without awaiting it: the inner assertions never ran, AND the timer fired after `afterEach` removed the tmpdir, causing `FileSessionManager.updateSessionUsage` → `saveStorage` → `writeFileSync` to throw an unhandled `ENOENT`. Local vitest happened to exit 0 anyway; CI vitest correctly exits 1 on unhandled errors, so `npm test` failed every publish job. The test now `await`s the timer and snapshots `originalLastUsed` as a string (the original code compared against `session.lastUsedAt`, which is a live reference into the storage map and mutates when `updateSessionUsage` runs).
|
|
26
|
+
- **`.github/workflows/publish.yml` (PyPI) missing `contents: read`.** Declaring `permissions: { id-token: write }` shrinks `GITHUB_TOKEN` to only that scope, so `actions/checkout@v4` couldn't authenticate to fetch the release tag and failed with `fatal: could not read Username for 'https://github.com': terminal prompts disabled`. Permission now explicitly includes `contents: read`.
|
|
27
|
+
|
|
28
|
+
No package-code changes vs 1.5.0 (functional surface) or 1.5.1 (installer workflow). This patch is the test + workflow correctness fix that lets the npm + PyPI artifacts actually publish.
|
|
29
|
+
|
|
30
|
+
## [1.5.1] - 2026-05-19
|
|
31
|
+
|
|
32
|
+
### Changed
|
|
33
|
+
|
|
34
|
+
- **Desktop installer artifacts now built and uploaded automatically on release.** New `.github/workflows/release-installer.yml` triggers on `release: published`, cross-compiles the Go bootstrapper for 5 OS/arch targets (`darwin/{arm64,amd64}`, `linux/{amd64,arm64}`, `windows/amd64`), packages the Node gateway bundle (`llm-cli-gateway-bundle-<ver>.tar.gz`), generates `SHA256SUMS` + `release-manifest.json` with the repo-relative `RVWR_RELEASE_PUBLIC_BASE`, verifies checksums, and uploads everything as release assets via `gh release upload --clobber`. `workflow_dispatch` is supported so a missed run can be rebuilt for an existing tag. No package-code changes vs 1.5.0; this is purely the build/distribution pipeline that lets users install the desktop integration without git/npm/docker.
|
|
35
|
+
|
|
36
|
+
## [1.5.0] - 2026-05-19
|
|
37
|
+
|
|
38
|
+
Lands DAG layers 6-12 — the personal-MCP MVP terminal plus all of Phase 0-3 provider modernisation. Codex round-2 unconditional SHIP across U22-U27 (correlation `517700e1`). 523 tests passing (+184 from 1.4.0).
|
|
39
|
+
|
|
40
|
+
### Added
|
|
41
|
+
|
|
42
|
+
- **U19 / U20 — Early LLM-assisted setup validation + automated MVP test harness.** New `doctor.ts`, `http-transport.ts`, `validation-orchestrator.ts`, `validation-report.ts`, `validation-normalizer.ts`, `validation-prompts.ts`, `validation-tools.ts`, `endpoint-exposure.ts`, `auth.ts`, `provider-status.ts`, `provider-login-guidance.ts`, and `gateway-server.ts`. Prompt-pack tightenings driven by real LLM dogfooding (Gemini chat-only + Codex command-capable). 35 new tests across the four matching `__tests__/` files.
|
|
43
|
+
- **U13 / U16 — Release packaging + dogfood readiness.** `installer/build-release.sh` cross-compiles 5 OS/arch targets (linux/{amd64,arm64}, darwin/{amd64,arm64}, windows/amd64) + Node bundle + `SHA256SUMS` + `release-manifest.json`. New `cli_upgrade --uninstall` (idempotent, dry-run by default) and `cli_upgrade --check`. New `Dockerfile.personal` + `docker-compose.personal.yml` for the personal-MCP container path. New `installer/packaging/README.md`. New `package.json` scripts `release:build`, `release:checksums`, `release:docker`. Comprehensive `docs/personal-mcp/{DOGFOODING_RESULTS,RELEASE_READINESS,SINGLE_BINARY_INSTALLER,ENDPOINT_EXPOSURE,PRODUCT_CONTRACT,PROVIDER_SUPPORT_MATRIX,VALIDATION_REPORT_FORMAT}.md` + per-provider `connect-*.md` guides + `setup/assistants/*-install-prompt.md` install-prompt corpus.
|
|
44
|
+
- **U21 — Phase-0 parity fixes.** `SESSION_PROVIDER_VALUES` / `SESSION_PROVIDER_ENUM` now expose the full provider set (grok was previously absent from `session_create`/`session_list`/`session_clear_all` Zod enums despite the storage layer supporting it). `prepareGeminiRequest` emits `["-p", prompt, ...]` instead of a positional prompt, eliminating the dependency on Gemini's TTY/mode-detection heuristics. 6 new tests pin both fixes.
|
|
45
|
+
- **U22 — Mistral Vibe is the fifth supported provider.** New `mistral_request` and `mistral_request_async` MCP tools register alongside the four incumbents and route through the same async job manager, dedup store, flight recorder, approval manager, and validation orchestrator. Five Vibe-specific divergences are documented in `docs/personal-mcp/PROVIDER_MODERNISATION_AUDIT.md`:
|
|
46
|
+
- **No `--model` flag** — model selection is via the `VIBE_ACTIVE_MODEL` environment variable (default alias: `devstral-medium`); the executor and async job manager forward an `env` override.
|
|
47
|
+
- **Session-logging is opt-in** in `~/.vibe/config.toml` — `doctor --json` probes `[session_logging] enabled = true` (read-only) and surfaces an actionable `next_actions` entry when the toggle is missing.
|
|
48
|
+
- **`--agent` enum** replaces Grok's `--always-approve` (`default | plan | accept-edits | auto-approve | chat | explore | lean`); the gateway always emits `--agent` explicitly and defaults to `auto-approve` for programmatic callers.
|
|
49
|
+
- **`--enabled-tools` allow-list only** — `allowedTools` emits one `--enabled-tools <tool>` per entry; `disallowedTools` is accepted in the schema for caller parity but silently ignored at the CLI boundary (a logged warning records the no-op).
|
|
50
|
+
- **No self-update** — `cli_upgrade --cli mistral` detects pip / uv / brew via probes and dispatches to `pip install -U vibe-cli`, `uv tool upgrade vibe-cli`, or `brew upgrade mistral-vibe`. Unknown installations return an actionable error rather than running a non-existent `vibe update`.
|
|
51
|
+
|
|
52
|
+
Other surfaces extended: `SESSION_PROVIDER_VALUES` now includes `"mistral"`; `list_models`, `cli_versions`, `cli_upgrade`, `approval_list`, `session_create`, `session_list`, and `session_clear_all` accept the fifth provider; new MCP resources `sessions://mistral` and `models://mistral` are registered; `validate_with_models` / `consensus_check` / `red_team_review` can route to Mistral.
|
|
53
|
+
- **U23 — JSON output + token/cost parity across providers.** New `src/codex-json-parser.ts` parses the Codex `--json` JSONL event stream (`thread.started`, `turn.started`/`completed`/`failed`, `item.*`, `error`); lenient against partial streams and garbage preamble. New `src/gemini-json-parser.ts` parses `gemini -o json` output and maps `usageMetadata.{promptTokenCount, candidatesTokenCount, cachedContentTokenCount}`. `extractUsageAndCost` is now a thin per-provider dispatcher returning `{inputTokens, outputTokens, cacheReadTokens?, cacheCreationTokens?, costUsd?}` for every provider that supports JSON; Claude `cache_read_input_tokens` / `cache_creation_input_tokens` are now plumbed through instead of being discarded. `codex_request`, `codex_request_async`, `gemini_request`, and `gemini_request_async` now expose `outputFormat: enum("text","json")` — set to `"json"` and the gateway emits `--json` (Codex) or `-o json` (Gemini) and forwards parsed usage/cost into the flight recorder. Flight-recorder schema gains `cache_read_tokens` and `cache_creation_tokens` columns via idempotent migration (`PRAGMA table_info` → `ALTER TABLE ADD COLUMN`); existing `logs.db` files are upgraded in place. 15 new tests.
|
|
54
|
+
- **U24 — Permission/approval-mode parity across providers.** Claude `permissionMode` enum (`default | acceptEdits | plan | auto | dontAsk | bypassPermissions`) replaces the boolean `dangerouslySkipPermissions` (the boolean still works and now maps to `permissionMode: "bypassPermissions"`; setting both logs a warning, `permissionMode` wins). Gemini `approvalMode` gains `plan`. Codex splits `--full-auto` into `sandboxMode: enum("read-only","workspace-write","danger-full-access")` and `askForApproval: enum("untrusted","on-request","never")`, emitting `--sandbox <mode>` and `--ask-for-approval <mode>` independently; legacy `fullAuto: true` still works and expands to `--sandbox workspace-write --ask-for-approval never` by default, with `useLegacyFullAutoFlag: true` as an explicit escape hatch to emit `--full-auto` directly. Codex resume mode filters all three flags (`--full-auto`, `--sandbox`, `--ask-for-approval`) since `codex exec resume` inherits the session's policy. 26 new tests.
|
|
55
|
+
- **U25 — Claude high-impact features.** `claude_request` / `claude_request_async` schemas gain `agent?: string` (single sub-agent dispatch), `agents?: Record<string, object>` (multi-agent JSON, validated against `CLAUDE_AGENT_DEFINITION_SCHEMA` before emit), `forkSession?: boolean`, `systemPrompt?: string`, `appendSystemPrompt?: string` (mutually exclusive at the schema + tool-callback boundary), `maxBudgetUsd?: number`, `maxTurns?: number`, `effort?: enum("low","medium","high","xhigh","max")`, and `excludeDynamicSystemPromptSections?: boolean`. Each emits the documented `--<flag>` form. 25 new tests in `src/__tests__/claude-handler.test.ts`.
|
|
56
|
+
- **U26 — Codex high-impact features.** `codex_request` / `codex_request_async` gain `outputSchema?: string | object` (object form is materialised to an `0o600` temp file under `os.tmpdir()` and cleaned via the AsyncJobManager `onComplete` contract — see post-review fixes below), `search?: boolean`, `profile?: string`, `configOverrides?: Record<string,string>` (keys validated against `/^[a-zA-Z0-9._]+$/`, values reject `\r`/`\n` via Zod refinement; emitted as repeated `-c key=value`), `ephemeral?: boolean`, `images?: string[]` (each path existence-validated; missing paths fail fast), `ignoreUserConfig?: boolean`, `ignoreRules?: boolean`. New top-level tool `codex_fork_session` wraps `codex fork <UUID> <prompt>` and `codex fork --last <prompt>` (sessionId XOR forkLast via Zod refinement). Codex default model alias is now `gpt-5.5` (the prior `gpt-5.3-codex` alias still resolves). Codex resume filter list extended with `--add-dir`, `-C`, `--output-schema`, and `--search`. 28 new tests across `codex-handler.test.ts` and `codex-fork.test.ts`.
|
|
57
|
+
- **U27 — Gemini high-impact features.** `gemini_request` / `gemini_request_async` gain `sandbox?: boolean` (emits `-s`), `policyFiles?: string[]` and `adminPolicyFiles?: string[]` (each path existence-validated; missing paths fail fast), and `attachments?: string[]` (absolute paths only, validated and prepended to the prompt as `@<abs-path>` tokens before the `-p` pair — U21 ordering invariant preserved). For fresh sessions (`createNewSession: true` or no sessionId), the gateway now emits `--session-id <uuid-v4>` instead of `--resume`, mapping the gateway session 1:1 to Gemini's authoritative store; `gw-*` prefixed IDs are rejected via strict UUID-v4 regex. `doctor --json` probes `./GEMINI.md`, `~/.gemini/GEMINI.md`, and `~/.gemini/settings.json` (parses `mcpServers` and reconciles against the gateway's `--allowed-mcp-server-names` whitelist; surfaces `next_actions` for missing registrations). `provider-status.ts` `geminiAuthStatus()` recognises four auth methods: OAuth file, `GEMINI_API_KEY`, `GOOGLE_API_KEY`, and `GOOGLE_CLOUD_PROJECT` + `GOOGLE_GENAI_USE_VERTEXAI=true`. 41 new tests across `gemini-handler.test.ts`, `provider-status.test.ts`, and the extended `doctor.test.ts`.
|
|
58
|
+
|
|
59
|
+
### Fixed
|
|
60
|
+
|
|
61
|
+
Round-1 Codex review found 5 blockers across U22, U23, and U26; round-2 unconditional SHIP. Locked in by `src/__tests__/post-review-fixes.test.ts` (14 tests, no mocks).
|
|
62
|
+
|
|
63
|
+
- **U22 dedup key now reflects env vars.** `AsyncJobManager.buildRequestKey(cli, args, env)` hashes a `canonicaliseEnvForKey(env)` payload (sorted-keys JSON) via the existing `computeRequestKey(cli, args, extra)` API. Two Mistral requests with the same argv but different `VIBE_ACTIVE_MODEL` no longer collide on dedup. Empty/undefined env collapses to `""` so pre-U22 callers retain the same key shape and previously-stored entries remain hit-able.
|
|
64
|
+
- **U23 JSON parsers are now reachable.** The newly-added Codex JSONL parser and Gemini JSON parser were dead code because `codex_request` / `gemini_request` exposed no `outputFormat` parameter and the gateway never emitted `--json` / `-o json`. Both tool schemas (sync + async) now expose `outputFormat: enum("text","json")`. `prepareCodexRequest` emits `--json`; `prepareGeminiRequest` emits a contiguous `-o json` pair after the U21 `-p` prompt pair. The success paths for `codex_request` and `gemini_request` now run `extractUsageAndCost(cli, stdout, outputFormat)` and forward `inputTokens`, `outputTokens`, `cacheReadTokens`, `cacheCreationTokens`, and `costUsd` into the flight recorder.
|
|
65
|
+
- **U26 `outputSchema` temp-file lifecycle now correct on every exit path.** `AsyncJobRecord` gains `onComplete?: () => void` + `onCompleteFired?: boolean` guard. `fireOnComplete(job)` is wired into every site that calls `persistComplete(job)` (8 total: close handler, cancel, idle-timeout, output overflow, dead-process recovery, exited-flag mismatch, process-monitor expiry, persistence-recovery). The dedup path also fires the new request's `onComplete` immediately so a deduped request never leaves its own materialised temp file orphaned. `awaitJobOrDefer` now takes `onComplete` as a trailing arg and guarantees exactly-once consumption across direct-execution, deferred, and `startJobWithDedup`-throws branches. The sync `codex_request` finally no longer runs cleanup (would have deleted the temp file while the deferred CLI process was still reading it); the async `codex_request_async` no longer leaks the temp file on successful start.
|
|
66
|
+
|
|
67
|
+
### Changed
|
|
68
|
+
|
|
69
|
+
- Codex default model alias is now `gpt-5.5` (legacy `gpt-5.3-codex` alias preserved).
|
|
70
|
+
- Default `model-registry` fallback chain order updated for new aliases.
|
|
71
|
+
- Skills (`.agents/skills/*` and `skills/*`) extended from four-provider to five-provider lists, with Mistral notes on auto-approve default and session-logging requirement.
|
|
72
|
+
|
|
73
|
+
## [1.4.0] - 2026-05-16
|
|
74
|
+
|
|
75
|
+
### Added
|
|
76
|
+
|
|
77
|
+
- **Codex `exec resume` wired through the gateway** — `codex_request` and `codex_request_async` now accept `sessionId` (real Codex session UUID from `~/.codex/sessions/` or the `codex resume` picker) and `resumeLatest:true`, emitting `codex exec resume <UUID>` and `codex exec resume --last` respectively. Codex sessions are no longer bookkeeping-only at the gateway layer; multi-turn workflows carry real CLI continuity, matching Claude/Gemini/Grok. Gateway-generated `gw-*` IDs are rejected for Codex (as for Gemini/Grok). `--full-auto` is silently dropped on resume because `codex exec resume` does not accept it — the original session's approval policy is inherited.
|
|
78
|
+
- **Durable job results + automatic dedup** — Async jobs are now persisted to a `jobs` table in `~/.llm-cli-gateway/logs.db` on every state transition (start, output flush, completion). `llm_job_status` and `llm_job_result` fall back to the database when the job is no longer in memory, so callers can collect a result regardless of how long ago the work completed (default retention: **30 days**, configurable via `LLM_GATEWAY_JOB_RETENTION_DAYS`). Identical `*_request` / `*_request_async` calls within a dedup window (default **1 hour**, configurable via `LLM_GATEWAY_DEDUP_WINDOW_MS`) short-circuit onto the existing running or completed job instead of spawning a duplicate run — directly fixing the "agent re-issues and the whole job starts over" loop. Each tool now accepts `forceRefresh: true` to bypass dedup. Jobs that were running when the gateway last stopped are flipped to `orphaned` on startup so callers can still read their partial output.
|
|
79
|
+
- **Grok CLI provider (xAI Grok Build TUI)** — New `grok_request` and `grok_request_async` MCP tools mirror the existing Claude/Codex/Gemini surface (sync + async, session management via `--resume`/`--continue`, idle-timeout, approval policy, review-integrity, flight recorder, metrics). Auth assumes a prior `grok login` (OAuth) or `GROK_CODE_XAI_API_KEY`. Default model: `grok-build`. `GROK_DEFAULT_MODEL`, `GROK_MODELS`, and `GROK_MODEL_ALIASES` env vars are honored by the model registry. `cli_upgrade` treats Grok as self-updating (`grok update` / `grok update --version <target>`).
|
|
80
|
+
- **Source-aware model registry** — `list_models` now reports model source/confidence metadata, aliases, default model source, and non-fatal discovery warnings
|
|
81
|
+
- **Deterministic model configuration overrides** — Added `*_SETTINGS_PATH`, `GEMINI_HISTORY_ROOT`, `*_MODEL_ALIASES`, and `LLM_GATEWAY_MODEL_ALIASES` support for stable deployments and tests
|
|
82
|
+
- **CLI lifecycle tools** — Added `cli_versions` and `cli_upgrade` tools for inspecting and upgrading individual Claude, Codex, Gemini, and Grok CLI installations
|
|
83
|
+
- **`resolveCodexSessionArgs` helper** in `src/request-helpers.ts` with 7 new tests covering mode resolution and `gw-*` rejection (Codex uses an `exec resume` subcommand rather than a flag pair, so the helper returns a `mode` discriminant: `new` | `resume-by-id` | `resume-latest`)
|
|
84
|
+
|
|
85
|
+
### Changed
|
|
86
|
+
|
|
87
|
+
- **`better-sqlite3` bumped to `^12.9.0`** (from `^11.0.0`) — required engines now `node 20.x || 22.x || 23.x || 24.x || 25.x`
|
|
88
|
+
- **Gemini history discovery is no longer authoritative** — Models observed in local Gemini session files are merged as low-confidence entries and no longer replace the registry or set the default model
|
|
89
|
+
- **Codex default handling remains explicit** — If Codex has no configured default, `default`/`latest` resolve to no model flag so the Codex CLI can use its own built-in default
|
|
90
|
+
- **Gateway skills refreshed** — The `.agents/skills/` (async-job-orchestration, implement-review-fix, multi-llm-review, secure-orchestration, session-workflow) and `skills/` (multi-llm-orchestration, multi-llm-consensus, model-routing, design-review-cycle, agent-codex-gate, codex-review-gate, red-team-assessment) skill docs now cover Grok, durable job results, auto-dedup, and the new Codex resume capability. `.agents/skills/` entries bumped to metadata version 1.5.
|
|
91
|
+
|
|
5
92
|
## [1.1.0] - 2026-04-04
|
|
6
93
|
|
|
7
94
|
### Added
|
package/README.md
CHANGED
|
@@ -3,12 +3,60 @@
|
|
|
3
3
|
> *"Without consultation, plans are frustrated, but with many counselors they succeed."*
|
|
4
4
|
> — Proverbs 15:22 (LSB)
|
|
5
5
|
|
|
6
|
-
A Model Context Protocol (MCP) server providing unified access to Claude Code, Codex, and
|
|
6
|
+
A Model Context Protocol (MCP) server providing unified access to Claude Code, Codex, Gemini, and Grok CLIs with session management, retry logic, and async job orchestration.
|
|
7
|
+
|
|
8
|
+
## Personal MCP Appliance MVP
|
|
9
|
+
|
|
10
|
+
`llm-cli-gateway` is being packaged as a single-user personal MCP appliance for cross-LLM validation. The intended workflow is: connect one MCP endpoint, ask any client for cross-LLM validation.
|
|
11
|
+
|
|
12
|
+
The product contract is documented in [docs/personal-mcp/PRODUCT_CONTRACT.md](docs/personal-mcp/PRODUCT_CONTRACT.md). It defines the single-user scope, security posture, target support matrix, and provider-support verification gates. Public setup guides must not claim ChatGPT, Claude web, Claude Desktop, Codex, Gemini CLI, Gemini web, or Grok inbound support until the corresponding provider/client path has been verified.
|
|
13
|
+
|
|
14
|
+
This project does not provide hosted multi-tenant credential custody. Provider credentials stay on the user's machine or user-owned deployment volume.
|
|
15
|
+
|
|
16
|
+
MVP release readiness is tracked in [docs/personal-mcp/RELEASE_READINESS.md](docs/personal-mcp/RELEASE_READINESS.md). Dogfooding evidence (which target LLMs guided setup, what unsafe suggestions were captured, which findings are deferred to post-MVP work) is in [docs/personal-mcp/DOGFOODING_RESULTS.md](docs/personal-mcp/DOGFOODING_RESULTS.md).
|
|
17
|
+
|
|
18
|
+
Current personal-appliance artifacts include:
|
|
19
|
+
|
|
20
|
+
- Streamable HTTP startup: `LLM_GATEWAY_AUTH_TOKEN=<token> npm run start:http`
|
|
21
|
+
- Machine-readable diagnostics: `npm run doctor`
|
|
22
|
+
- Go bootstrapper scaffold: `installer/` with `setup`, `doctor --json`, `start`, `stop`, `status`, `repair`, `upgrade`, `uninstall`, `print-client-config`, and verified bundle download commands.
|
|
23
|
+
- Release packaging: `npm run release:build` produces cross-platform binaries plus a checksummed Node bundle under `installer/dist/`; see [installer/packaging/README.md](installer/packaging/README.md).
|
|
24
|
+
- Docker Compose fallback: [docker-compose.personal.yml](docker-compose.personal.yml) + [Dockerfile.personal](Dockerfile.personal) for users who already manage containers.
|
|
25
|
+
- Local setup UI artifact: [setup/ui/index.html](setup/ui/index.html)
|
|
26
|
+
- Provider setup snippets: [setup/providers/](setup/providers/)
|
|
27
|
+
- Cross-validation tools: `validate_with_models`, `second_opinion`, `compare_answers`, `red_team_review`, `consensus_check`, `ask_model`, `synthesize_validation`, `job_status`, and `job_result`.
|
|
28
|
+
|
|
29
|
+
### Install / Upgrade / Uninstall (single binary)
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
# After downloading the binary that matches your OS/arch from a release:
|
|
33
|
+
sha256sum --check SHA256SUMS # verify before run (or `shasum -a 256 --check` on macOS)
|
|
34
|
+
chmod +x llm-cli-gateway-<ver>-<os>-<arch>
|
|
35
|
+
./llm-cli-gateway-<ver>-<os>-<arch> setup
|
|
36
|
+
./llm-cli-gateway-<ver>-<os>-<arch> install-bundle # uses RVWR_GATEWAY_BUNDLE_URL/_SHA256
|
|
37
|
+
./llm-cli-gateway-<ver>-<os>-<arch> start
|
|
38
|
+
./llm-cli-gateway-<ver>-<os>-<arch> doctor
|
|
39
|
+
|
|
40
|
+
# Upgrade: replace the binary, set the new bundle env vars, run upgrade.
|
|
41
|
+
./llm-cli-gateway-<new>-<os>-<arch> upgrade
|
|
42
|
+
|
|
43
|
+
# Uninstall: dry-run first, then run with --yes.
|
|
44
|
+
./llm-cli-gateway-<ver>-<os>-<arch> uninstall
|
|
45
|
+
./llm-cli-gateway-<ver>-<os>-<arch> uninstall --yes
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Docker fallback:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
LLM_GATEWAY_AUTH_TOKEN=$(openssl rand -hex 32) \
|
|
52
|
+
docker compose -f docker-compose.personal.yml up -d
|
|
53
|
+
docker compose -f docker-compose.personal.yml run --rm doctor
|
|
54
|
+
```
|
|
7
55
|
|
|
8
56
|
## Features
|
|
9
57
|
|
|
10
58
|
### Core Capabilities
|
|
11
|
-
- **Multi-LLM Orchestration**: Unified interface for Claude Code, Codex, and
|
|
59
|
+
- **Multi-LLM Orchestration**: Unified interface for Claude Code, Codex, Gemini, and Grok CLIs
|
|
12
60
|
- **Session Management**: Track and resume conversations across all CLIs with persistent storage
|
|
13
61
|
- **Token Optimization**: Automatic 44% reduction on prompts, 37% on responses (opt-in)
|
|
14
62
|
- **Correlation ID Tracking**: Full request tracing across all LLM interactions
|
|
@@ -56,6 +104,43 @@ npm install -g @google/gemini-cli
|
|
|
56
104
|
# Or: https://github.com/google-gemini/gemini-cli
|
|
57
105
|
```
|
|
58
106
|
|
|
107
|
+
### Grok CLI (xAI)
|
|
108
|
+
```bash
|
|
109
|
+
npm install -g grok-build
|
|
110
|
+
grok login # OAuth flow, or set GROK_CODE_XAI_API_KEY
|
|
111
|
+
# Docs: https://docs.x.ai/build/cli
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Mistral Vibe CLI
|
|
115
|
+
```bash
|
|
116
|
+
# Pick one — the gateway's cli_upgrade auto-detects which one you used.
|
|
117
|
+
pip install vibe-cli
|
|
118
|
+
uv tool install vibe-cli
|
|
119
|
+
brew install mistral-vibe
|
|
120
|
+
|
|
121
|
+
vibe auth login
|
|
122
|
+
# Required for `mistral_request --resume` / `--continue` to persist sessions:
|
|
123
|
+
vibe config set session_logging.enabled true # or edit ~/.vibe/config.toml
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Vibe-specific notes:
|
|
127
|
+
|
|
128
|
+
- **Model selection is via the `VIBE_ACTIVE_MODEL` environment variable** —
|
|
129
|
+
Vibe has no `--model` flag. The gateway resolves the requested model alias
|
|
130
|
+
(default: `devstral-medium`) and injects it as `VIBE_ACTIVE_MODEL` when
|
|
131
|
+
spawning `vibe`.
|
|
132
|
+
- **`permissionMode` accepts** `default | plan | accept-edits | auto-approve |
|
|
133
|
+
chat | explore | lean` and emits `--agent <mode>`. The gateway's
|
|
134
|
+
programmatic-mode default is `auto-approve`; pick a stricter mode
|
|
135
|
+
explicitly if you need approval gates.
|
|
136
|
+
- **`allowedTools` is allow-list only** — the gateway emits one
|
|
137
|
+
`--enabled-tools <tool>` flag per entry. `disallowedTools` is accepted in
|
|
138
|
+
the schema for caller-side parity but is silently ignored at the CLI
|
|
139
|
+
boundary (a `logger.info` warning records the no-op).
|
|
140
|
+
- **No self-update**: `cli_upgrade --cli mistral` detects whether you used
|
|
141
|
+
pip / uv / brew and dispatches the matching upgrade command. Running
|
|
142
|
+
`vibe update` is not a thing.
|
|
143
|
+
|
|
59
144
|
## Installation
|
|
60
145
|
|
|
61
146
|
### As an MCP server (npm)
|
|
@@ -87,7 +172,7 @@ npm run build
|
|
|
87
172
|
|
|
88
173
|
### As an MCP Server
|
|
89
174
|
|
|
90
|
-
|
|
175
|
+
For clients that already support local stdio MCP servers, add a configuration like:
|
|
91
176
|
|
|
92
177
|
```json
|
|
93
178
|
{
|
|
@@ -100,8 +185,24 @@ Add to your MCP client configuration (e.g., Claude Desktop):
|
|
|
100
185
|
}
|
|
101
186
|
```
|
|
102
187
|
|
|
188
|
+
This generic stdio example is not provider-support verification for the Personal MCP Appliance MVP. Client-specific setup guides for ChatGPT, Claude web, Claude Desktop, Codex, Gemini CLI, Gemini web, and Grok remain gated by the provider-support matrix in [docs/personal-mcp/PRODUCT_CONTRACT.md](docs/personal-mcp/PRODUCT_CONTRACT.md).
|
|
189
|
+
|
|
103
190
|
### Available Tools
|
|
104
191
|
|
|
192
|
+
#### Cross-LLM Validation Tools
|
|
193
|
+
|
|
194
|
+
The personal-appliance surface exposes simplified validation tools for non-developer clients. These tools start provider CLI jobs through the durable async job manager and return normalized provider status plus raw job references.
|
|
195
|
+
|
|
196
|
+
- `validate_with_models`: ask two or more providers to independently validate a question.
|
|
197
|
+
- `second_opinion`: ask one provider to review an answer.
|
|
198
|
+
- `red_team_review`: challenge a plan, answer, or document for risks and failure modes.
|
|
199
|
+
- `consensus_check`: check whether providers agree with a claim.
|
|
200
|
+
- `ask_model`: ask one provider through the simplified surface.
|
|
201
|
+
- `synthesize_validation`: run an explicit judge model after provider results have been collected.
|
|
202
|
+
- `job_status` and `job_result`: poll and collect validation job outputs.
|
|
203
|
+
|
|
204
|
+
The validation report preserves per-provider disagreement. Optional judge synthesis is explicit about which provider produced the judge job.
|
|
205
|
+
|
|
105
206
|
#### LLM Request Tools
|
|
106
207
|
|
|
107
208
|
##### `claude_request`
|
|
@@ -205,8 +306,63 @@ Execute a Gemini CLI request with session support.
|
|
|
205
306
|
}
|
|
206
307
|
```
|
|
207
308
|
|
|
208
|
-
##### `
|
|
209
|
-
|
|
309
|
+
##### `grok_request`
|
|
310
|
+
Execute a Grok CLI (xAI) request with session support.
|
|
311
|
+
|
|
312
|
+
**Parameters:**
|
|
313
|
+
- `prompt` (string, required): The prompt to send (1-100,000 chars)
|
|
314
|
+
- `model` (string, optional): Model name or alias (e.g. `grok-build`, `latest`)
|
|
315
|
+
- `outputFormat` (string, optional): `"plain"` (default), `"json"`, or `"streaming-json"`
|
|
316
|
+
- `sessionId` (string, optional): Session ID to resume (`--resume <id>`)
|
|
317
|
+
- `resumeLatest` (boolean, optional): Resume the most recent session in the current cwd (`--continue`)
|
|
318
|
+
- `createNewSession` (boolean, optional): Always create a new session
|
|
319
|
+
- `alwaysApprove` (boolean, optional): Auto-approve all tool executions (`--always-approve`) in legacy mode
|
|
320
|
+
- `permissionMode` (string, optional): `default|acceptEdits|auto|dontAsk|bypassPermissions|plan`
|
|
321
|
+
- `effort` (string, optional): `low|medium|high|xhigh|max`
|
|
322
|
+
- `reasoningEffort` (string, optional): Reasoning effort for reasoning models
|
|
323
|
+
- `approvalStrategy` (string, optional): `"legacy"` (default) or `"mcp_managed"`
|
|
324
|
+
- `approvalPolicy` (string, optional): `"strict"`, `"balanced"`, or `"permissive"`
|
|
325
|
+
- `mcpServers` (string[], optional): MCP server names tracked for approvals (Grok manages its own MCP config via `grok mcp`)
|
|
326
|
+
- `allowedTools` (string[], optional): Allowed built-in tools (passed as `--tools` comma list)
|
|
327
|
+
- `disallowedTools` (string[], optional): Disallowed built-in tools (passed as `--disallowed-tools` comma list)
|
|
328
|
+
- `optimizePrompt` (boolean, optional): Optimize prompt for token efficiency, default: false
|
|
329
|
+
- `optimizeResponse` (boolean, optional): Optimize response for token efficiency, default: false
|
|
330
|
+
- `correlationId` (string, optional): Request trace ID (auto-generated if omitted)
|
|
331
|
+
|
|
332
|
+
**Example:**
|
|
333
|
+
```json
|
|
334
|
+
{
|
|
335
|
+
"prompt": "Summarize the latest commit message in 1 sentence",
|
|
336
|
+
"model": "grok-build",
|
|
337
|
+
"effort": "low"
|
|
338
|
+
}
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
#### Durable job results & automatic dedup
|
|
342
|
+
|
|
343
|
+
Every async job is persisted to a `jobs` table in `~/.llm-cli-gateway/logs.db` as it transitions through running → completed/failed/canceled. This makes the gateway a durable collection layer:
|
|
344
|
+
|
|
345
|
+
- **Re-issuing a request is safe.** Identical `*_request` / `*_request_async` calls within the dedup window (default 1 hour) short-circuit onto the existing running or completed job — the caller gets back the same job ID instead of starting a duplicate run. This directly fixes the "agent times out polling, re-issues, and the whole job starts over" failure mode.
|
|
346
|
+
- **`llm_job_status` and `llm_job_result` work across gateway restarts.** Job rows live for 30 days by default; callers can fetch results long after the in-memory cache has evicted them.
|
|
347
|
+
- **Jobs running at shutdown are marked `orphaned`** on the next gateway boot (the detached child can't be reattached to). Their captured partial output remains readable.
|
|
348
|
+
- **Pass `forceRefresh: true`** on any request tool to bypass dedup and force a fresh CLI run.
|
|
349
|
+
|
|
350
|
+
Environment variables:
|
|
351
|
+
- `LLM_GATEWAY_JOB_RETENTION_DAYS` — how long completed jobs stay queryable. Default `30`.
|
|
352
|
+
- `LLM_GATEWAY_DEDUP_WINDOW_MS` — how recent an existing job must be to dedup against. Default `3600000` (1 hour). Set `0` to disable dedup.
|
|
353
|
+
- `LLM_GATEWAY_JOBS_DB` — override the sqlite path. Defaults to the value of `LLM_GATEWAY_LOGS_DB`, then `~/.llm-cli-gateway/logs.db`. Set to `none` to disable durability entirely (in-memory only).
|
|
354
|
+
|
|
355
|
+
##### `mistral_request`
|
|
356
|
+
Run a Mistral Vibe agentic coding request. Like `grok_request` in shape, but with Vibe's specific surface:
|
|
357
|
+
|
|
358
|
+
- `model` (string, optional): Resolved alias (e.g. `devstral-medium`, `devstral-large`, `latest`). The resolved value is injected via the `VIBE_ACTIVE_MODEL` environment variable — Vibe has no `--model` flag.
|
|
359
|
+
- `permissionMode`: `default | plan | accept-edits | auto-approve | chat | explore | lean` — emitted as `--agent <mode>`. Defaults to `auto-approve` in programmatic mode.
|
|
360
|
+
- `allowedTools` (string[], optional): One `--enabled-tools <tool>` flag per entry (allow-list only).
|
|
361
|
+
- `disallowedTools` (string[], optional): Accepted for parity with the other providers; ignored at the CLI boundary with a logged warning.
|
|
362
|
+
- `sessionId` / `resumeLatest` / `createNewSession`: standard session controls. Continuity requires `[session_logging] enabled = true` in `~/.vibe/config.toml` — `doctor --json` surfaces an actionable next-action when the toggle is missing.
|
|
363
|
+
|
|
364
|
+
##### `claude_request_async` / `codex_request_async` / `gemini_request_async` / `grok_request_async` / `mistral_request_async`
|
|
365
|
+
Start a long-running Claude, Codex, Gemini, Grok, or Mistral request without waiting for completion in the same MCP call.
|
|
210
366
|
|
|
211
367
|
Use this flow when analysis/runtime can exceed client tool-call limits:
|
|
212
368
|
1. Start job with `*_request_async`
|
|
@@ -244,7 +400,7 @@ Approval records are persisted to `~/.llm-cli-gateway/approvals.jsonl`.
|
|
|
244
400
|
Create a new session for a specific CLI.
|
|
245
401
|
|
|
246
402
|
**Parameters:**
|
|
247
|
-
- `cli` (string, required): CLI to create session for ("claude", "codex", "gemini")
|
|
403
|
+
- `cli` (string, required): CLI to create session for ("claude", "codex", "gemini", "grok", "mistral")
|
|
248
404
|
- `description` (string, optional): Description for the session
|
|
249
405
|
- `setAsActive` (boolean, optional): Set as active session, default: true
|
|
250
406
|
|
|
@@ -261,7 +417,7 @@ Create a new session for a specific CLI.
|
|
|
261
417
|
List all sessions, optionally filtered by CLI.
|
|
262
418
|
|
|
263
419
|
**Parameters:**
|
|
264
|
-
- `cli` (string, optional): Filter by CLI ("claude", "codex", "gemini")
|
|
420
|
+
- `cli` (string, optional): Filter by CLI ("claude", "codex", "gemini", "grok", "mistral")
|
|
265
421
|
|
|
266
422
|
**Response includes:**
|
|
267
423
|
- Total session count
|
|
@@ -299,12 +455,74 @@ Clear all sessions, optionally for a specific CLI.
|
|
|
299
455
|
List available models for each CLI.
|
|
300
456
|
|
|
301
457
|
**Parameters:**
|
|
302
|
-
- `cli` (string, optional): Specific CLI to list models for ("claude", "codex", "gemini")
|
|
458
|
+
- `cli` (string, optional): Specific CLI to list models for ("claude", "codex", "gemini", "grok", "mistral")
|
|
303
459
|
|
|
304
460
|
**Response includes:**
|
|
305
461
|
- Model names and descriptions
|
|
306
462
|
- Best use cases for each model
|
|
307
463
|
- CLI-specific information
|
|
464
|
+
- `defaultModel` and `defaultModelSource` when a default is explicitly configured
|
|
465
|
+
- `modelMetadata` with source/confidence (`fallback`, `config`, `env`, `observed`)
|
|
466
|
+
- `aliases` and `warnings` when configured or when discovery degrades gracefully
|
|
467
|
+
|
|
468
|
+
The registry treats explicit configuration as authoritative. Bundled fallback models are low-confidence hints, and Gemini models observed in local session history are merged as low-confidence entries only; they do not become the default model.
|
|
469
|
+
|
|
470
|
+
Model registry environment overrides:
|
|
471
|
+
|
|
472
|
+
```bash
|
|
473
|
+
# Explicit defaults
|
|
474
|
+
CLAUDE_DEFAULT_MODEL=haiku
|
|
475
|
+
CODEX_DEFAULT_MODEL=<codex-model-id>
|
|
476
|
+
GEMINI_DEFAULT_MODEL=gemini-2.5-flash
|
|
477
|
+
|
|
478
|
+
# Additional models: comma/newline list, JSON array, or JSON object of model->description
|
|
479
|
+
GEMINI_MODELS='{"gemini-team-default":"Team-approved Gemini model"}'
|
|
480
|
+
|
|
481
|
+
# Aliases
|
|
482
|
+
GEMINI_MODEL_ALIASES='team=gemini-team-default'
|
|
483
|
+
LLM_GATEWAY_MODEL_ALIASES='codex.fast=gpt-5.3-codex-spark,gemini.fast=gemini-team-default'
|
|
484
|
+
|
|
485
|
+
# Deterministic config/discovery paths
|
|
486
|
+
CODEX_CONFIG_PATH=/path/to/config.toml
|
|
487
|
+
CLAUDE_SETTINGS_PATH=/path/to/settings.json
|
|
488
|
+
CLAUDE_SETTINGS_LOCAL_PATH=/path/to/settings.local.json
|
|
489
|
+
GEMINI_SETTINGS_PATH=/path/to/settings.json
|
|
490
|
+
GEMINI_HISTORY_ROOT=/path/to/.gemini/tmp
|
|
491
|
+
|
|
492
|
+
# Disable local model-history discovery
|
|
493
|
+
LLM_GATEWAY_DISABLE_MODEL_DISCOVERY=1
|
|
494
|
+
```
|
|
495
|
+
|
|
496
|
+
##### `cli_versions`
|
|
497
|
+
Report installed CLI versions.
|
|
498
|
+
|
|
499
|
+
**Parameters:**
|
|
500
|
+
- `cli` (string, optional): Specific CLI to inspect ("claude", "codex", "gemini", "grok", "mistral")
|
|
501
|
+
|
|
502
|
+
##### `cli_upgrade`
|
|
503
|
+
Plan or run an upgrade for one CLI.
|
|
504
|
+
|
|
505
|
+
**Parameters:**
|
|
506
|
+
- `cli` (string, required): CLI to upgrade ("claude", "codex", "gemini", "grok", "mistral")
|
|
507
|
+
- `target` (string, optional): Package tag/version/target, default: `latest`
|
|
508
|
+
- `dryRun` (boolean, optional): Return the upgrade plan without running it, default: `true`
|
|
509
|
+
- `timeoutMs` (number, optional): Upgrade timeout when `dryRun=false`
|
|
510
|
+
|
|
511
|
+
**Upgrade strategies:**
|
|
512
|
+
- Claude latest: `claude update`
|
|
513
|
+
- Claude explicit target: `claude install <target>`
|
|
514
|
+
- Codex latest: `codex update`
|
|
515
|
+
- Codex explicit target: `npm install -g @openai/codex@<target>`
|
|
516
|
+
- Gemini: `npm install -g @google/gemini-cli@<target>`
|
|
517
|
+
|
|
518
|
+
**Example dry run:**
|
|
519
|
+
```json
|
|
520
|
+
{
|
|
521
|
+
"cli": "gemini",
|
|
522
|
+
"target": "latest",
|
|
523
|
+
"dryRun": true
|
|
524
|
+
}
|
|
525
|
+
```
|
|
308
526
|
|
|
309
527
|
## Session Management
|
|
310
528
|
|
|
@@ -572,4 +790,3 @@ For issues and questions:
|
|
|
572
790
|
## Changelog
|
|
573
791
|
|
|
574
792
|
See [CHANGELOG.md](CHANGELOG.md) for detailed release history.
|
|
575
|
-
|
|
@@ -2,7 +2,7 @@ import type { Logger } from "./logger.js";
|
|
|
2
2
|
import type { ReviewIntegrityResult } from "./review-integrity.js";
|
|
3
3
|
export type ApprovalPolicy = "strict" | "balanced" | "permissive";
|
|
4
4
|
export type ApprovalStrategy = "legacy" | "mcp_managed";
|
|
5
|
-
export type ApprovalCli = "claude" | "codex" | "gemini";
|
|
5
|
+
export type ApprovalCli = "claude" | "codex" | "gemini" | "grok" | "mistral";
|
|
6
6
|
export type ApprovalStatus = "approved" | "denied";
|
|
7
7
|
export interface ApprovalRequest {
|
|
8
8
|
cli: ApprovalCli;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { Logger } from "./logger.js";
|
|
2
2
|
import { type JobHealth } from "./process-monitor.js";
|
|
3
|
-
|
|
4
|
-
export type
|
|
3
|
+
import { JobStore } from "./job-store.js";
|
|
4
|
+
export type LlmCli = "claude" | "codex" | "gemini" | "grok" | "mistral";
|
|
5
|
+
export type AsyncJobStatus = "running" | "completed" | "failed" | "canceled" | "orphaned";
|
|
5
6
|
export interface AsyncJobSnapshot {
|
|
6
7
|
id: string;
|
|
7
8
|
cli: LlmCli;
|
|
@@ -22,17 +23,87 @@ export interface AsyncJobResult extends AsyncJobSnapshot {
|
|
|
22
23
|
stdoutTruncated: boolean;
|
|
23
24
|
stderrTruncated: boolean;
|
|
24
25
|
}
|
|
26
|
+
export interface StartJobOptions {
|
|
27
|
+
cwd?: string;
|
|
28
|
+
idleTimeoutMs?: number;
|
|
29
|
+
outputFormat?: string;
|
|
30
|
+
/** Bypass dedup and force a fresh CLI run even if a recent matching job exists. */
|
|
31
|
+
forceRefresh?: boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Extra environment variables to inject when spawning the child CLI.
|
|
34
|
+
* Used by Mistral Vibe to pass `VIBE_ACTIVE_MODEL` (Vibe has no `--model` flag).
|
|
35
|
+
*
|
|
36
|
+
* IMPORTANT: env vars participate in the dedup key (canonicalised by sorted
|
|
37
|
+
* keys + JSON-stringified). Two requests that differ only in env (e.g. two
|
|
38
|
+
* Mistral requests with the same prompt but different VIBE_ACTIVE_MODEL)
|
|
39
|
+
* therefore do NOT collide on dedup.
|
|
40
|
+
*/
|
|
41
|
+
env?: Record<string, string>;
|
|
42
|
+
/**
|
|
43
|
+
* Optional hook fired exactly once when the job reaches a terminal state.
|
|
44
|
+
* Used by callers that own per-request resources (outputSchema temp files,
|
|
45
|
+
* etc.) that must persist for the lifetime of the spawned CLI process.
|
|
46
|
+
*/
|
|
47
|
+
onComplete?: () => void;
|
|
48
|
+
}
|
|
49
|
+
export interface StartJobOutcome {
|
|
50
|
+
snapshot: AsyncJobSnapshot;
|
|
51
|
+
/** Set to the existing job's id when the request was de-duplicated. */
|
|
52
|
+
deduped: boolean;
|
|
53
|
+
/** Set when deduped — the original job's correlation id, useful for logging. */
|
|
54
|
+
originalCorrelationId?: string;
|
|
55
|
+
}
|
|
25
56
|
export declare class AsyncJobManager {
|
|
26
57
|
private logger;
|
|
27
58
|
private onJobComplete?;
|
|
28
59
|
private jobs;
|
|
29
60
|
private evictionTimer;
|
|
30
61
|
private processMonitor;
|
|
31
|
-
|
|
62
|
+
private store;
|
|
63
|
+
constructor(logger?: Logger, onJobComplete?: ((cli: LlmCli, durationMs: number, success: boolean) => void) | undefined, store?: JobStore | null);
|
|
32
64
|
private emitMetrics;
|
|
33
65
|
private evictCompletedJobs;
|
|
34
|
-
|
|
66
|
+
/**
|
|
67
|
+
* Compute the dedup key for a job. Stable across re-issues of the same request,
|
|
68
|
+
* which is exactly what allows agents to safely retry without restarting the run.
|
|
69
|
+
*
|
|
70
|
+
* U22 fix: env vars participate in the key via a deterministic canonicalisation
|
|
71
|
+
* (sorted keys → JSON-stringified). This prevents two Mistral requests with the
|
|
72
|
+
* same argv but different `VIBE_ACTIVE_MODEL` from deduping onto each other.
|
|
73
|
+
*/
|
|
74
|
+
private buildRequestKey;
|
|
75
|
+
private fireOnComplete;
|
|
76
|
+
private safeStoreCall;
|
|
77
|
+
/**
|
|
78
|
+
* Flush in-memory stdout/stderr to the durable store if anything changed
|
|
79
|
+
* since the last flush. Throttled by OUTPUT_FLUSH_INTERVAL_MS to avoid
|
|
80
|
+
* pounding sqlite on every chunk of streaming output.
|
|
81
|
+
*/
|
|
82
|
+
private maybeFlushOutput;
|
|
83
|
+
private persistComplete;
|
|
84
|
+
/**
|
|
85
|
+
* Reconstitute an in-memory AsyncJobRecord from a durable row, so subsequent
|
|
86
|
+
* getJobSnapshot/getJobResult calls hit the in-memory cache.
|
|
87
|
+
* The reconstituted record has process=null — it represents historical data only.
|
|
88
|
+
*/
|
|
89
|
+
private hydrateFromStore;
|
|
90
|
+
/**
|
|
91
|
+
* Backwards-compatible entry point. Equivalent to startJobWithDedup({...}).snapshot.
|
|
92
|
+
* Existing callers keep working unchanged; forceRefresh is exposed as a trailing
|
|
93
|
+
* optional param for the dedup-aware path.
|
|
94
|
+
*/
|
|
95
|
+
startJob(cli: LlmCli, args: string[], correlationId: string, cwd?: string, idleTimeoutMs?: number, outputFormat?: string, forceRefresh?: boolean, env?: Record<string, string>, onComplete?: () => void): AsyncJobSnapshot;
|
|
96
|
+
/**
|
|
97
|
+
* Start a job, with optional dedup against recent identical requests.
|
|
98
|
+
* Returns `{ snapshot, deduped }` so callers can log/report the short-circuit.
|
|
99
|
+
*
|
|
100
|
+
* Dedup is keyed on (cli, args). If a job with the same key was started within
|
|
101
|
+
* the dedup window (default 1h) and is still running or completed, its snapshot
|
|
102
|
+
* is returned without spawning a new process. forceRefresh skips dedup entirely.
|
|
103
|
+
*/
|
|
104
|
+
startJobWithDedup(cli: LlmCli, args: string[], correlationId: string, opts?: StartJobOptions): StartJobOutcome;
|
|
35
105
|
getJobSnapshot(jobId: string): AsyncJobSnapshot | null;
|
|
106
|
+
getJobSnapshots(jobIds: string[]): Record<string, AsyncJobSnapshot | null>;
|
|
36
107
|
getJobResult(jobId: string, maxChars?: number): AsyncJobResult | null;
|
|
37
108
|
cancelJob(jobId: string): {
|
|
38
109
|
canceled: boolean;
|