llm-cli-gateway 2.11.1 → 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.
@@ -0,0 +1,288 @@
1
+ ---
2
+ name: async-job-orchestration
3
+ description: Manage long-running async LLM jobs. Use for tasks >2min, parallel jobs, or non-blocking execution. Covers cache-aware `promptParts`, `cache-state://` MCP resources, and the Claude `cache_ttl_expiring_soon` warning.
4
+ metadata:
5
+ author: verivus-oss
6
+ version: "1.6"
7
+ ---
8
+
9
+ # Async Job Orchestration
10
+
11
+ Async execution for Claude, Codex, Gemini, Grok, and Mistral. Non-blocking jobs with polling lifecycle. Job state is **durable** — results survive gateway restarts and polling timeouts (see [Durability & Dedup](#durability--dedup)).
12
+
13
+ > **Mistral Vibe**: the gateway always emits `--agent <mode>` and defaults to `auto-approve` for programmatic callers. Current Vibe defaults session logging on; `doctor --json` flags an explicit `[session_logging] enabled = false` before session-continuity use.
14
+
15
+ ## Dispatch Defaults
16
+
17
+ Apply these on every dispatch unless the caller has explicitly overridden a rule in the current turn:
18
+
19
+ 1. **Omit `model`** — let the gateway use its configured default per CLI. Nominating a model risks deprecated IDs (`o3`, `o3-pro`, `gpt-4o`, …) and capability mismatches.
20
+ 2. **`approvalStrategy:"mcp_managed"`** is the skill dispatch default (the gateway schema default is `"legacy"`). It gates the request before execution; Claude then runs with `bypassPermissions`, Gemini with `yolo`, and Codex still needs `fullAuto:true` for autonomous file/shell work. Prefer this over raw bypass flags.
21
+ 3. **No wallclock timeout; poll every 60 s** — see [Polling Strategy](#polling-strategy) below. Do **not** cancel jobs for taking too long; cancel only on explicit instruction or hard failure. `idleTimeoutMs` (no-output safeguard) is separate.
22
+ 4. **Iterate until unconditional APPROVED** (review dispatches only) — end every review prompt with "End with APPROVED or NOT APPROVED with findings." Loop: dispatch → poll → parse verdict → on `NOT APPROVED` or conditional approval, dispatch fixes + re-review → repeat. Escalate after 3 rounds. This rule does **not** apply to pure implementation or non-review analysis dispatches.
23
+
24
+ ## Auto-Async Deferral
25
+
26
+ Sync tools auto-defer when execution exceeds sync deadline. No manual sync/async choice needed.
27
+
28
+ ### Flow
29
+
30
+ 1. Call sync tool (`claude_request`, `codex_request`, `gemini_request`, `grok_request`, `mistral_request`)
31
+ 2. Gateway starts CLI as background job, polls internally
32
+ 3. Job completes within deadline (default **45s**) → result returned directly
33
+ 4. Deadline exceeded → **deferred response** with `jobId` for polling
34
+
35
+ ### Deferred Response
36
+
37
+ ```json
38
+ {"status":"deferred","jobId":"uuid","cli":"claude","correlationId":"...","message":"Execution exceeded sync deadline (45000ms). Poll with llm_job_status, fetch with llm_job_result.","sessionId":"...","pollWith":"llm_job_status","fetchWith":"llm_job_result","cancelWith":"llm_job_cancel"}
39
+ ```
40
+
41
+ ### Handling
42
+
43
+ 1. Parse response as JSON
44
+ 2. If `status==="deferred"` → extract `jobId`
45
+ 3. Poll `llm_job_status({jobId})` until `job.status` is terminal (`completed`, `failed`, `canceled`, or `orphaned`)
46
+ 4. Fetch `llm_job_result({jobId})`
47
+
48
+ Non-deferred responses: process as normal. Results are durable (default 30 days) — you can fetch them long after the deferred response was returned, even across gateway restarts.
49
+
50
+ ### Configuration
51
+
52
+ ```bash
53
+ SYNC_DEADLINE_MS=45000 # Default: 45s (under 60s MCP client cap)
54
+ SYNC_DEADLINE_MS=0 # Disable auto-deferral (pure sync)
55
+ SYNC_DEADLINE_MS=20000 # Shorter deadline
56
+ ```
57
+
58
+ ### When to Use Explicit Async
59
+
60
+ Use `*_request_async` when:
61
+ - Fire-and-forget (start job, work on other tasks, check later)
62
+ - Launching multiple parallel jobs (need all job IDs upfront)
63
+ - Avoiding any sync wait (even 45s deadline)
64
+
65
+ ## Core Tools
66
+
67
+ | Tool | Purpose |
68
+ |------|---------|
69
+ | `claude_request_async` | Start async Claude job |
70
+ | `codex_request_async` | Start async Codex job |
71
+ | `gemini_request_async` | Start async Gemini job |
72
+ | `grok_request_async` | Start async Grok (xAI) job |
73
+ | `mistral_request_async` | Start async Mistral Vibe job |
74
+ | `llm_job_status` | Poll job status (in-memory + durable store fallback) |
75
+ | `llm_job_result` | Retrieve job output (in-memory + durable store fallback) |
76
+ | `llm_job_cancel` | Cancel running job |
77
+ | `llm_process_health` | Inspect in-memory job/process health |
78
+
79
+ ## Single Job
80
+
81
+ ### Start
82
+
83
+ ```
84
+ claude_request_async({prompt:"Analyze src/ for type safety...",approvalStrategy:"mcp_managed",optimizePrompt:true})
85
+ ```
86
+
87
+ Response:
88
+ ```json
89
+ {"success":true,"job":{"id":"job-abc123","cli":"claude","status":"running","startedAt":"..."},"sessionId":"...","approval":null,"mcpServers":{"requested":["example-server"]}}
90
+ ```
91
+
92
+ - Gemini async responses also include `resumable:true|false`; only user-provided Gemini `sessionId` values are resumable
93
+ - Gateway-generated Gemini `gw-*` IDs are bookkeeping IDs and are rejected if passed back as `sessionId`
94
+
95
+ ### Poll
96
+
97
+ ```
98
+ llm_job_status({jobId:"job-abc123"})
99
+ ```
100
+
101
+ Statuses: `running` | `completed` | `failed` | `canceled`
102
+
103
+ ### Retrieve
104
+
105
+ ```
106
+ llm_job_result({jobId:"job-abc123",maxChars:200000})
107
+ ```
108
+
109
+ - `maxChars`: 1,000–2,000,000 (default 200,000). Returns tail (most recent) when truncated
110
+ - `stdoutTruncated`/`stderrTruncated` flags indicate truncation
111
+
112
+ ### Cancel
113
+
114
+ ```
115
+ llm_job_cancel({jobId:"job-abc123"})
116
+ ```
117
+
118
+ Sends SIGTERM, then SIGKILL after 5s.
119
+
120
+ ## Idle Timeout
121
+
122
+ Kills process if no stdout/stderr for configurable duration. Detects stuck processes.
123
+
124
+ | CLI | Default | Notes |
125
+ |-----|---------|-------|
126
+ | Claude | 600,000ms | **stream-json mode only.** text/json produce no output until done (would false-positive) |
127
+ | Codex | 600,000ms | Streams stderr progress — works all modes |
128
+ | Gemini | 600,000ms | Streams stdout — works all modes |
129
+ | Grok | 600,000ms | Streams stdout — works all modes |
130
+ | Mistral Vibe | 600,000ms | Streams stdout/stderr — works all modes |
131
+
132
+ Override: `idleTimeoutMs:int (30,000–3,600,000)`
133
+
134
+ When idle timeout fires: exit code **125** (non-transient, no retry).
135
+
136
+ ## Parallel Jobs
137
+
138
+ Start all, then collect:
139
+
140
+ ```
141
+ claude_request_async({prompt:"Review architecture... End with APPROVED or NOT APPROVED with findings.",approvalStrategy:"mcp_managed",correlationId:"review-arch"})
142
+ codex_request_async({prompt:"Check for bugs... End with APPROVED or NOT APPROVED with findings.",fullAuto:true,approvalStrategy:"mcp_managed",correlationId:"review-impl"})
143
+ gemini_request_async({prompt:"Security audit... End with APPROVED or NOT APPROVED with findings.",approvalStrategy:"mcp_managed",correlationId:"review-sec"})
144
+ grok_request_async({prompt:"Independent diversity review... End with APPROVED or NOT APPROVED with findings.",approvalStrategy:"mcp_managed",correlationId:"review-grok"})
145
+ mistral_request_async({prompt:"Independent Vibe review... End with APPROVED or NOT APPROVED with findings.",approvalStrategy:"mcp_managed",correlationId:"review-mistral"})
146
+ ```
147
+
148
+ Poll each with `llm_job_status` every 60s. Retrieve with `llm_job_result` when terminal.
149
+
150
+ ## Polling Strategy
151
+
152
+ - Poll `llm_job_status` **every 60 seconds** (not faster — wastes tokens/time)
153
+ - No wallclock timeout — good reviews take minutes to tens of minutes
154
+ - Do **not** cancel jobs for "taking too long"; cancel only on explicit user instruction or hard failure (process dead, non-transient error such as exit 125/126)
155
+ - `idleTimeoutMs` (no-output safeguard, default 10 min per CLI) remains active and will kill genuinely hung processes — this is separate from wallclock timeout and does not need tightening for normal reviews
156
+ - When using `ScheduleWakeup` or sleep loops, use 60 s cadence; the 5-minute prompt-cache window also favors intervals ≤ 270 s or ≥ 20 min — 60 s is safely inside cache
157
+
158
+ ### Wait-between-polls mechanism (orchestrator-specific)
159
+
160
+ Between `llm_job_status` calls, use a **non-blocking** wait. Standalone `sleep` commands are blocked in some orchestrators (e.g. the Claude Code harness rejects `Bash({command: "sleep 60"})` as a standalone call).
161
+
162
+ - **Claude Code harness** — use `Bash` with `run_in_background: true` for a one-shot wait:
163
+ ```
164
+ Bash({command: "sleep 60 && echo done", run_in_background: true})
165
+ // Returns a task ID. Harness emits a completion notification when the 60s elapses.
166
+ // On notification, call llm_job_status.
167
+ ```
168
+ The `Monitor` tool is for **streaming** progress (one event per stdout line) — not for one-shot waits. Do not chain multiple short sleeps to work around the standalone-sleep block; the harness detects and blocks that too.
169
+ - **`ScheduleWakeup`** (if available in your orchestrator): schedule a wakeup with `delaySeconds: 60` and a prompt that resumes the polling loop. The runtime fires you back on schedule.
170
+ - **Codex CLI / other orchestrators**: use the orchestrator's native non-blocking wait primitive (e.g. async/await on a timer). Avoid synchronous blocking sleeps that freeze the agent loop.
171
+
172
+ Treat the wait as "yield control for ~60 s, then poll once" — not "block the shell for 60 s."
173
+
174
+ ## Error Handling
175
+
176
+ | Status | Exit Code | Meaning | Action |
177
+ |--------|-----------|---------|--------|
178
+ | `completed` | 0 | Success | Retrieve result |
179
+ | `failed` | 124 | CLI timeout | Check stderr |
180
+ | `failed` | 125 | Idle timeout | Increase `idleTimeoutMs` or check CLI |
181
+ | `failed` | 126 | Output overflow (>50MB) | Reduce scope |
182
+ | `failed` | non-zero | CLI error | Check stderr |
183
+ | `failed` | null | Process error | Check `job.error` |
184
+ | `canceled` | any | Canceled | Result still retrievable |
185
+
186
+ Only `exitCode===0` → `completed`. All non-zero → `failed`. Results retrievable for ALL terminal states.
187
+
188
+ Exit codes 125/126 are non-transient — retry engine skips them. Adjust parameters instead.
189
+
190
+ ## Job Lifecycle
191
+
192
+ - **Durable**. Every state transition (start, throttled output flush, completion) is persisted to a `jobs` table in `~/.llm-cli-gateway/logs.db`. `llm_job_status` / `llm_job_result` transparently fall back to the durable store when the job is no longer in memory.
193
+ - **Default retention 30 days** — override with `LLM_GATEWAY_JOB_RETENTION_DAYS`. Override the sqlite path with `LLM_GATEWAY_JOBS_DB` (defaults to `LLM_GATEWAY_LOGS_DB`, then `~/.llm-cli-gateway/logs.db`). Set `LLM_GATEWAY_JOBS_DB=none` to disable durability (in-memory only — legacy behavior, not recommended).
194
+ - **Jobs still running when the gateway stopped** are flipped to `orphaned` on next boot (the detached child cannot be reattached). Their captured partial output remains readable via `llm_job_result`.
195
+ - 50 MB max output (stdout+stderr) — exceeding kills process (exit 126).
196
+ - In-memory cache eviction has 1-hour TTL after completion; this no longer means the result is gone — durable store backs every read.
197
+
198
+ ## Durability & Dedup
199
+
200
+ The gateway is a durable result collection layer. Two behaviors directly address the "agent polls, times out, re-issues, the whole CLI run starts over" failure mode:
201
+
202
+ ### Auto-dedup
203
+
204
+ Identical `*_request` / `*_request_async` calls within the dedup window (default **1 hour**, `LLM_GATEWAY_DEDUP_WINDOW_MS` in ms) short-circuit onto the existing running or completed job. You get back the same `jobId` instead of spawning a duplicate run.
205
+
206
+ - Set `LLM_GATEWAY_DEDUP_WINDOW_MS=0` to disable dedup gateway-wide.
207
+ - Pass `forceRefresh: true` on a single call to bypass dedup for that request:
208
+
209
+ ```
210
+ codex_request_async({prompt:"...",fullAuto:true,approvalStrategy:"mcp_managed",forceRefresh:true})
211
+ ```
212
+
213
+ Use `forceRefresh` when you genuinely need a fresh CLI run (e.g., file contents changed since the last dispatch, retry after manual fix). For normal "I crashed and restarted, let me re-issue" recovery, **omit `forceRefresh`** — dedup is exactly what you want.
214
+
215
+ ### Durable retrieval
216
+
217
+ - Polling timeouts no longer destroy results. If your wrapper agent gives up at 5 minutes, `llm_job_status({jobId})` and `llm_job_result({jobId})` still return the completed result hours/days later.
218
+ - Gateway restarts no longer destroy results. Persisted rows survive process restarts.
219
+ - An `orphaned` job is one that was `running` when the gateway last stopped — partial output is still readable; treat it as a non-recoverable terminal state for the active CLI invocation (re-dispatch with `forceRefresh:true` if you need fresh work).
220
+
221
+ ### Recovery pattern
222
+
223
+ ```
224
+ // 1. Wrapper agent died after dispatching — you have no jobId in memory.
225
+ // 2. Re-issue the identical *_request_async call. The gateway dedups onto
226
+ // the existing in-flight or completed job and returns its jobId.
227
+ result = codex_request_async({prompt:"<same prompt as before>",fullAuto:true,approvalStrategy:"mcp_managed",correlationId:"<same correlationId>"})
228
+ // result.job.id is the original job
229
+ // 3. Poll/fetch as normal — works whether the job is running, completed, or completed days ago.
230
+ ```
231
+
232
+ ## Cache-Aware Prompts (`promptParts`)
233
+
234
+ Every async request tool (`claude_request_async`, `codex_request_async`, `gemini_request_async`, `grok_request_async`, `mistral_request_async`) accepts a structured `promptParts` object instead of the flat `prompt` string. The two are **mutually exclusive** — supplying both returns `provide exactly one of \`prompt\` or \`promptParts\``; supplying neither returns `one of \`prompt\` or \`promptParts\` is required`.
235
+
236
+ ```
237
+ codex_request_async({
238
+ promptParts: {
239
+ system: "<long stable system instruction>",
240
+ tools: "<long stable tool description>",
241
+ context: "<long stable spec or file dump>",
242
+ task: "Implement X per the above."
243
+ },
244
+ fullAuto: true,
245
+ approvalStrategy: "mcp_managed",
246
+ correlationId: "impl-r1"
247
+ })
248
+ ```
249
+
250
+ The gateway concatenates in canonical order `system → tools → context → task` so parallel async dispatch to multiple CLIs sees byte-identical stable prefix bytes, and re-issues of the same async call (recovery, retry, dedup) keep the same stable-prefix hash. This raises implicit cache hit rate at each provider with no provider-API contortions.
251
+
252
+ For parallel async fan-out (Pattern: "fire N reviewers, collect when done"), the win is largest — every reviewer shares the prefix, and `cache-state://prefix/{hash}` lets you verify they actually hit cache.
253
+
254
+ ### Cache observability resources
255
+
256
+ Three MCP resources expose cache effectiveness from the flight recorder (tokens / hashes / aggregates only — no prompt or response text):
257
+
258
+ - `cache-state://global` — last-24h aggregate hit rate, total hits, estimated savings, per-CLI breakdown
259
+ - `cache-state://session/{sessionId}` — per-session aggregates incl. `ttlRemainingMs` for Claude
260
+ - `cache-state://prefix/{hash}` — per-stable-prefix-hash aggregates with CLI × model breakdown
261
+
262
+ `session_get({sessionId})` also projects a compact `cacheState` block when the session has prior requests (omitted entirely for fresh sessions).
263
+
264
+ ### TTL warning on Claude async jobs (opt-in)
265
+
266
+ With `[cache_awareness] warn_on_ttl_expiry = true` in `~/.llm-cli-gateway/config.toml`, both `claude_request` and `claude_request_async` responses include a structured warning when the resumed session's prior `lastRequestAt` is within 30 s of Anthropic's cache TTL (default 5 min; 1 h when `[cache_awareness] anthropic_ttl_seconds = 3600`):
267
+
268
+ ```json
269
+ { "warnings": [{ "code": "cache_ttl_expiring_soon", "ttlRemainingMs": 12000, "message": "Anthropic cache breakpoint for session <id> expires in 12000ms (< 30000ms). Subsequent requests may miss the cache." }] }
270
+ ```
271
+
272
+ For long-running async loops on a Claude session, treat the warning as a hint to dispatch the next turn promptly (or accept the upcoming cache miss). The warning is gated on the config flag and appears only for Claude sessions with prior cache writes.
273
+
274
+ ## Tips
275
+
276
+ - Use `correlationId` on every job for log tracing
277
+ - Async jobs do NOT support `optimizeResponse` — optimize after retrieval
278
+ - Sessions work with async — pass `sessionId` or `createNewSession`; Claude,
279
+ Codex, Gemini, Grok, and Mistral carry real provider continuity when their
280
+ provider-specific session rules are satisfied
281
+ - **Sync tools auto-defer at 45s** — check for `status:"deferred"` in sync responses, then poll every 60s
282
+ - `SYNC_DEADLINE_MS=0` disables auto-deferral
283
+ - For Gemini, check `resumable` — only `true` for user-provided `sessionId`
284
+ - Set higher `idleTimeoutMs` for tasks with long silent periods
285
+ - Review jobs: the verdict from the CLI is the gate — loop until unconditional APPROVED, do not settle early
286
+ - If jobs fail because a CLI is missing or stale, check `cli_versions` and run `cli_upgrade` as a dry run before any real upgrade. Grok self-updates via `grok update`; `cli_upgrade` routes that for you.
287
+ - **Don't burn re-runs after a polling timeout** — re-issue the same call; auto-dedup snaps you back onto the live job. Reserve `forceRefresh:true` for cases where the underlying inputs actually changed.
288
+ - **Durable results outlive in-memory caches** — `llm_job_result({jobId})` returns the same output 30 days later by default. Don't hold polling open just because you fear losing the result.
@@ -0,0 +1,154 @@
1
+ ---
2
+ name: implement-review-fix
3
+ description: Run implement-review-fix cycle using multiple LLMs (Claude, Codex, Gemini, Grok, Mistral). Use for features, bugs, or refactoring with multi-LLM collaboration. Mistral defaults to `--agent auto-approve`; current Vibe defaults session logging on, and doctor flags explicit `[session_logging] enabled = false` before session-continuity use.
4
+ metadata:
5
+ author: verivus-oss
6
+ version: "1.6"
7
+ ---
8
+
9
+ # Implement-Review-Fix Cycle
10
+
11
+ Structured workflow: multiple LLMs implement, review, iterate until quality met.
12
+
13
+ ## Cycle
14
+
15
+ ```
16
+ 1. Implement (Codex) → 2. Review (Claude+Gemini) → 3. Fix (Codex) → 4. Verify
17
+ └──────────────── loop until unconditional APPROVED ────────────────┘
18
+ ```
19
+
20
+ Single-level orchestration only — parent agent coordinates all steps. Child LLMs cannot call other LLMs through gateway.
21
+
22
+ ## Dispatch Defaults
23
+
24
+ Apply these on every dispatch unless the caller has explicitly overridden a rule in the current turn:
25
+
26
+ 1. **Omit `model`** — let the gateway use its configured default per CLI. Nominating a model risks deprecated IDs (`o3`, `o3-pro`, `gpt-4o`, …) and capability mismatches. Use `list_models` only if the caller has asked for a specific variant.
27
+ 2. **`approvalStrategy:"mcp_managed"`** is the skill dispatch default (the gateway schema default is `"legacy"`). It gates the request before execution; Claude then runs with `--permission-mode bypassPermissions`, Gemini with `--approval-mode yolo`, and Codex still needs `fullAuto:true` for autonomous file/shell work. Prefer this over raw bypass flags.
28
+ 3. **No wallclock timeout; poll every 60 s** — let sync auto-defer at 45 s or use `*_request_async`. Poll `llm_job_status` once every 60 seconds. Do **not** cancel jobs for taking too long; cancel only on explicit instruction or hard failure. `idleTimeoutMs` (no-output safeguard) is separate.
29
+ 4. **Iterate until unconditional APPROVED** (review dispatches only) — every review/re-review dispatch is a loop. End every review prompt with "End with APPROVED or NOT APPROVED with findings." On `NOT APPROVED` or conditional approval, consolidate findings, dispatch fixes (Codex + `fullAuto:true`), re-dispatch the review to the **same reviewer**. Repeat until unconditional APPROVED. Escalate after 3 rounds. This rule does **not** apply to pure implementation or non-review analysis dispatches. (The implementation step in Step 1 is not itself a review; the loop is driven by the reviewer verdict in Step 2 / Step 4.)
30
+
31
+ ## Session Continuity
32
+
33
+ | CLI | Effect | Mechanism |
34
+ |-----|--------|-----------|
35
+ | **Claude** | Real continuity | `--session-id` or `--continue` passed to CLI |
36
+ | **Codex** | Real continuity | `codex exec resume <UUID>` (`sessionId`) or `codex exec resume --last` (`resumeLatest:true`). `sessionId` must be a real Codex session UUID from `~/.codex/sessions/`; gateway-generated `gw-*` IDs are rejected |
37
+ | **Gemini** | Real continuity | `--resume` passed to CLI |
38
+ | **Grok** | Real continuity | `--resume` / `--continue` passed to CLI |
39
+
40
+ For Codex resumption: pass the UUID printed by `codex resume` / visible under `~/.codex/sessions/`, **or** pass `resumeLatest:true` to use the most recent session in cwd. Note: `--full-auto` is silently dropped on resume — Codex inherits the original session's approval policy.
41
+
42
+ ## Cache-Aware Prompts (`promptParts`)
43
+
44
+ The implement → review → fix loop is the canonical use case for the structured `promptParts` field: across rounds the system instructions, tool block, and file/spec context are byte-identical; only the `task` mutates. Switch from `prompt` to `promptParts` in every step so the gateway can hash the stable prefix once and providers can serve the long prefix from cache:
45
+
46
+ ```
47
+ codex_request({
48
+ promptParts: {
49
+ system: "<stable implementation brief>",
50
+ context: "<spec + relevant file paths or dump>",
51
+ task: "Implement [feature]. Requirements: …"
52
+ },
53
+ fullAuto: true,
54
+ approvalStrategy: "mcp_managed",
55
+ optimizePrompt: true
56
+ })
57
+ ```
58
+
59
+ `prompt` and `promptParts` are mutually exclusive — supplying both returns `provide exactly one of \`prompt\` or \`promptParts\``; supplying neither returns `one of \`prompt\` or \`promptParts\` is required`. The gateway assembles in canonical order `system → tools → context → task` so a re-review or fix call with the same `system` + `context` and a new `task` reuses the same hash.
60
+
61
+ After a round, `session_get({sessionId})` projects a compact `cacheState` block (hit rate, distinct prefix count, savings, `ttlRemainingMs` for Claude) — useful for confirming the loop is actually cache-warming as expected.
62
+
63
+ ## Step 1: Implement
64
+
65
+ ```
66
+ codex_request({prompt:"Implement [feature]. Requirements:\n- [req 1]\n- [req 2]\n\nWrite code in [path]. Include tests.",fullAuto:true,approvalStrategy:"mcp_managed",optimizePrompt:true})
67
+ ```
68
+
69
+ ## Step 2: Review
70
+
71
+ Send to reviewers in parallel. Reviewers **must have tool access** to read files and verify claims — never use `allowedTools:[]` or include tool-suppression language in review prompts. `mcp_managed` removes Claude/Gemini approval prompts; Codex also requires `fullAuto:true`.
72
+
73
+ Before adding provider-specific controls, check `provider_tool_capabilities` for
74
+ that provider. Do not copy Claude tool names into Grok, Gemini, Codex, or Vibe
75
+ requests; omit allowlists unless the capability record says the provider
76
+ supports the exact native tool names you intend to use.
77
+
78
+ **Claude — Quality:**
79
+ ```
80
+ claude_request({prompt:"Review changes in [path]. Read the files directly. Check:\n- Code quality/maintainability\n- Project conventions\n- Error handling\n- Documentation gaps\nList issues with severity and fixes. End with APPROVED or NOT APPROVED with findings.",allowedTools:["Read","Grep","Glob"],approvalStrategy:"mcp_managed",optimizePrompt:true,optimizeResponse:true})
81
+ ```
82
+
83
+ **Gemini — Bugs/Security:**
84
+ ```
85
+ gemini_request({prompt:"Analyze [path] for bugs, edge cases, security issues. Read the files directly. Check test coverage. Rate: critical/high/medium/low. End with APPROVED or NOT APPROVED with findings.",approvalStrategy:"mcp_managed",optimizePrompt:true,optimizeResponse:true})
86
+ ```
87
+
88
+ **Grok — Optional 4th Reviewer (Diversity / Consensus):**
89
+ ```
90
+ grok_request({prompt:"Independent review of [path]. Flag issues the other reviewers may have missed; contradict findings you disagree with. End with APPROVED or NOT APPROVED with findings.",approvalStrategy:"mcp_managed",optimizePrompt:true,optimizeResponse:true})
91
+ ```
92
+
93
+ Add Grok when consensus matters (high-stakes paths) or to break ties. Auth must already be set up (`grok login` or `GROK_CODE_XAI_API_KEY`).
94
+
95
+ Sync tools auto-defer at 45s — if response contains `status:"deferred"`, poll `jobId` via `llm_job_status` every 60s, fetch with `llm_job_result`. Results are durable (default 30 days) — if your polling wrapper times out, fetch by `jobId` later or re-issue the identical call (auto-dedup reattaches to the live job).
96
+
97
+ ## Step 3: Fix
98
+
99
+ Consolidate findings, send to Codex. Use `resumeLatest:true` or a real Codex
100
+ session UUID when you want Codex CLI continuity; otherwise re-state problem
101
+ context inline:
102
+
103
+ ```
104
+ codex_request({prompt:"Fix issues in [path]:\n\n1. [Critical] [desc]\n2. [High] [desc]\n3. [Medium] [desc]\n\nApply fixes and update tests.",fullAuto:true,approvalStrategy:"mcp_managed",optimizePrompt:true})
105
+ ```
106
+
107
+ ## Step 4: Verify (re-review)
108
+
109
+ Re-dispatch the **same reviewers** from Step 2 with fix context:
110
+
111
+ ```
112
+ claude_request({prompt:"Re-review [path] after fixes. Previous findings:\n1. [issue 1] — Fixed by: [what changed]\n2. [issue 2] — Fixed by: [what changed]\n\nConfirm each fix or flag remaining issues. End with APPROVED or NOT APPROVED with findings.",approvalStrategy:"mcp_managed",optimizePrompt:true,optimizeResponse:true})
113
+ ```
114
+
115
+ ## Iteration (mandatory)
116
+
117
+ - Any `NOT APPROVED` or conditional approval → back to Step 3 → Step 4
118
+ - "APPROVED with residual notes" counts as approved only if notes are purely informational
119
+ - Max 3 iterations before escalating to the user (but continue iterating until then)
120
+ - All reviewers must return unconditional APPROVED before the cycle ends
121
+
122
+ ## Long-Running Tasks
123
+
124
+ Sync tools auto-defer if execution exceeds 45s deadline. Response contains `status:"deferred"` with `jobId` — poll with `llm_job_status`, fetch with `llm_job_result`. No manual sync/async choice needed.
125
+
126
+ For explicit non-blocking (fire-and-forget, parallel jobs):
127
+
128
+ ```
129
+ codex_request_async({prompt:"...",fullAuto:true,approvalStrategy:"mcp_managed"})
130
+ ```
131
+
132
+ ### Parallel Async Reviews (up to 4 CLIs)
133
+
134
+ ```
135
+ codex_request_async({prompt:"Implement [feature]...",fullAuto:true,approvalStrategy:"mcp_managed",correlationId:"impl"})
136
+ // Poll every 60s until completed...
137
+
138
+ claude_request_async({prompt:"Review [path] for quality... End with APPROVED or NOT APPROVED with findings.",approvalStrategy:"mcp_managed",correlationId:"review-quality"})
139
+ codex_request_async({prompt:"Check [path] for logic bugs... End with APPROVED or NOT APPROVED with findings.",fullAuto:true,approvalStrategy:"mcp_managed",correlationId:"review-bugs"})
140
+ gemini_request_async({prompt:"Security audit [path]... End with APPROVED or NOT APPROVED with findings.",approvalStrategy:"mcp_managed",correlationId:"review-security"})
141
+ grok_request_async({prompt:"Independent review of [path]... End with APPROVED or NOT APPROVED with findings.",approvalStrategy:"mcp_managed",correlationId:"review-grok"})
142
+ // Poll every 60s, collect, synthesize, fix, re-review — until all APPROVED
143
+ ```
144
+
145
+ ## Tips
146
+
147
+ - Consolidate findings before sending fixes (avoid redundant work)
148
+ - Use `correlationId` to trace full cycle
149
+ - For security-sensitive code: raise to `approvalPolicy:"strict"` (on top of default `mcp_managed`)
150
+ - Keep implementation prompts specific — file paths, function names, acceptance criteria
151
+ - For Codex fixes: either pass `resumeLatest:true` (or the session UUID) to carry conversation context, **or** re-state problem context inline if running fresh
152
+ - Never pass Gemini `gw-*` session IDs — use your own Gemini IDs for resumable workflows
153
+ - Check for `status:"deferred"` in sync responses — poll `jobId` every 60s if present
154
+ - **Durable results**: deferred jobs persist for 30 days (`LLM_GATEWAY_JOB_RETENTION_DAYS`). If the cycle is interrupted, re-issue identical calls (auto-dedup snaps onto the live job) or fetch by `jobId` later. Use `forceRefresh:true` only when inputs have actually changed
@@ -0,0 +1,174 @@
1
+ ---
2
+ name: multi-llm-review
3
+ description: Parallel code reviews across Claude, Codex, Gemini, Grok, and Mistral. Use for quality analysis, bug finding, or security audit.
4
+ metadata:
5
+ author: verivus-oss
6
+ version: "1.6"
7
+ ---
8
+
9
+ # Multi-LLM Code Review
10
+
11
+ Parallel reviews using gateway MCP tools. Each LLM has different strengths — combine for comprehensive coverage.
12
+
13
+ ## LLM Strengths
14
+
15
+ | LLM | Best For | Sync | Async |
16
+ |-----|----------|------|-------|
17
+ | Claude | Architecture, design, quality, docs | `claude_request` | `claude_request_async` |
18
+ | Codex | Implementation correctness, logic bugs, tests | `codex_request` | `codex_request_async` |
19
+ | Gemini | Security, edge cases, multimodal context | `gemini_request` | `gemini_request_async` |
20
+ | Grok (xAI) | Independent fourth perspective for diversity / consensus tie-breaks | `grok_request` | `grok_request_async` |
21
+ | Mistral Vibe | Independent fifth perspective; uncorrelated with the OpenAI/Anthropic/Google/xAI family; defaults to `--agent auto-approve` | `mistral_request` | `mistral_request_async` |
22
+
23
+ ## Dispatch Defaults
24
+
25
+ Apply these on every dispatch unless the caller has explicitly overridden a rule in the current turn:
26
+
27
+ 1. **Omit `model`** — let the gateway use its configured default per CLI. Nominating a model risks deprecated IDs (`o3`, `o3-pro`, `gpt-4o`, …) and capability mismatches. Call `list_models` only when the caller has asked for a specific variant.
28
+ 2. **`approvalStrategy:"mcp_managed"`** is the skill dispatch default (the gateway schema default is `"legacy"`). It gates the request before execution; Claude then runs with `--permission-mode bypassPermissions`, Gemini with `--approval-mode yolo`, and Codex still needs `fullAuto:true` for autonomous file/shell work. Prefer this over raw bypass flags.
29
+ 3. **No wallclock timeout; poll every 60 s** — let sync auto-defer at 45 s or use `*_request_async`. Poll `llm_job_status` once every 60 seconds. Do **not** cancel jobs for taking too long; cancel only on explicit instruction or hard failure. `idleTimeoutMs` (no-output safeguard) is separate.
30
+ 4. **Iterate until unconditional APPROVED** (review dispatches only) — every review prompt must end with "End with APPROVED or NOT APPROVED with findings." On `NOT APPROVED` or conditional approval, consolidate findings, dispatch fixes (Codex + `fullAuto:true`), re-dispatch the review to the same reviewer. Repeat until unconditional APPROVED. Escalate after 3 rounds without convergence. This rule does **not** apply to pure implementation or non-review analysis dispatches.
31
+
32
+ ## Workflow
33
+
34
+ ### 1. Discover Models (optional)
35
+
36
+ Only when the caller has asked for a specific variant:
37
+
38
+ ```
39
+ list_models()
40
+ ```
41
+
42
+ Otherwise omit `model` and proceed.
43
+
44
+ ### 1a. Discover Provider Capabilities
45
+
46
+ Before applying provider-specific controls such as tool allowlists, MCP server
47
+ fields, session resume, media skills, or output formats, ask the gateway for the
48
+ provider surface:
49
+
50
+ ```
51
+ provider_tool_capabilities({cli:"claude"})
52
+ provider_tool_capabilities({cli:"codex"})
53
+ provider_tool_capabilities({cli:"gemini"})
54
+ provider_tool_capabilities({cli:"grok"})
55
+ provider_tool_capabilities({cli:"mistral"})
56
+ ```
57
+
58
+ Use the reported `unsupportedInputs` and `controls` instead of assuming all
59
+ CLIs share Claude's tool names or MCP semantics. The same data is available as
60
+ `provider-tools://{provider}` resources.
61
+
62
+ ### 2. Send Parallel Reviews
63
+
64
+ Sync tools auto-defer at 45s — if response contains `status:"deferred"`, poll `jobId` via `llm_job_status` every 60s, fetch with `llm_job_result`.
65
+
66
+ **Tip — share the stable prefix across reviewers:** when the same long brief / file dump is sent to every reviewer, switch from `prompt` to the structured `promptParts` field. The gateway concatenates in canonical order `system → tools → context → task`, so every reviewer sees byte-identical stable prefix bytes, raising implicit cache hit rate at each provider. `prompt` and `promptParts` are mutually exclusive — the runtime returns `provide exactly one of \`prompt\` or \`promptParts\`` if both are supplied. After the round, read `cache-state://prefix/{hash}` (tokens/hashes only, no prompt text) to confirm reviewers actually shared the prefix.
67
+
68
+ **Claude — Quality & Architecture:**
69
+ ```
70
+ claude_request({prompt:"Review changes in {path} for architecture, design patterns, maintainability, documentation gaps. Read the files directly. Specific line numbers and fixes. End with APPROVED or NOT APPROVED with findings.",approvalStrategy:"mcp_managed",optimizePrompt:true,optimizeResponse:true})
71
+ ```
72
+
73
+ **Codex — Logic & Correctness:**
74
+ ```
75
+ codex_request({prompt:"Analyze {path} for logic bugs, off-by-one, missing error handling, race conditions, test gaps. Read the files directly. Severity: critical/high/medium/low. End with APPROVED or NOT APPROVED with findings.",fullAuto:true,approvalStrategy:"mcp_managed",optimizePrompt:true,optimizeResponse:true})
76
+ ```
77
+
78
+ **Gemini — Security & Edge Cases:**
79
+ ```
80
+ gemini_request({prompt:"Security audit {path}: injection, auth bypasses, data leaks, OWASP Top 10, crash-causing edge cases. Read the files directly. End with APPROVED or NOT APPROVED with findings.",approvalStrategy:"mcp_managed",optimizePrompt:true,optimizeResponse:true})
81
+ ```
82
+
83
+ **Grok — Independent Diversity (optional 4th reviewer):**
84
+ ```
85
+ grok_request({prompt:"Independent review of {path}. Read the files directly. Flag issues the other reviewers may have missed, contradict findings you disagree with, and call out blind spots. End with APPROVED or NOT APPROVED with findings.",approvalStrategy:"mcp_managed",optimizePrompt:true,optimizeResponse:true})
86
+ ```
87
+
88
+ Add Grok when consensus matters (high-stakes changes, security-critical paths) or to break ties between the other three. Auth must already be set up (`grok login` OAuth or `GROK_CODE_XAI_API_KEY`).
89
+
90
+ ### 3. Synthesize
91
+
92
+ 1. **Deduplicate** — Remove findings from multiple LLMs
93
+ 2. **Prioritize** — critical > high > medium > low
94
+ 3. **Cross-validate** — Unique findings from one LLM → verify
95
+ 4. **Categorize** — Security, Correctness, Performance, Maintainability
96
+
97
+ ### 4. Consolidated Report
98
+
99
+ ```markdown
100
+ ## Code Review Summary
101
+ ### Critical (must fix)
102
+ - [Issue] — found by [LLM], severity: critical
103
+ ### High Priority
104
+ - ...
105
+ ### Medium Priority
106
+ - ...
107
+ ### Suggestions
108
+ - ...
109
+ ### Positive Observations
110
+ - ...
111
+ ```
112
+
113
+ ## Large Codebases
114
+
115
+ Use async for parallel execution:
116
+
117
+ ```
118
+ claude_request_async({prompt:"Review all TS files in src/ for architecture/quality... End with APPROVED or NOT APPROVED with findings.",approvalStrategy:"mcp_managed",optimizePrompt:true,correlationId:"review-quality"})
119
+ codex_request_async({prompt:"Check all TS files in src/ for logic bugs/test gaps... End with APPROVED or NOT APPROVED with findings.",fullAuto:true,approvalStrategy:"mcp_managed",optimizePrompt:true,correlationId:"review-bugs"})
120
+ gemini_request_async({prompt:"Security audit all TS files in src/... End with APPROVED or NOT APPROVED with findings.",approvalStrategy:"mcp_managed",correlationId:"review-security"})
121
+ grok_request_async({prompt:"Independent diversity review of all TS files in src/... End with APPROVED or NOT APPROVED with findings.",approvalStrategy:"mcp_managed",correlationId:"review-grok"})
122
+ mistral_request_async({prompt:"Independent Vibe review of all TS files in src/... End with APPROVED or NOT APPROVED with findings.",approvalStrategy:"mcp_managed",correlationId:"review-mistral"})
123
+ ```
124
+
125
+ Poll with `llm_job_status` every 60s, retrieve with `llm_job_result` when terminal. Jobs are durable — if your polling wrapper times out, re-issue the same call (the gateway auto-dedups onto the live job) or fetch by `jobId` later (default 30-day retention).
126
+
127
+ For iterative Gemini reviews, pass `sessionId` for resumability:
128
+
129
+ ```
130
+ gemini_request_async({prompt:"Deep security audit of src/... End with APPROVED or NOT APPROVED with findings.",sessionId:"gemini-security-review",approvalStrategy:"mcp_managed"})
131
+ // Response: resumable:true
132
+ ```
133
+
134
+ ## Anti-Patterns
135
+
136
+ These patterns undermine review quality and trigger review integrity warnings:
137
+
138
+ - **Don't inline code in `<code>` blocks** — provide file paths and let reviewers read files directly
139
+ - **Don't suppress tools** — never include "do not run tools" or "respond only based on code provided" in review prompts
140
+ - **Don't use `allowedTools:[]`** for reviews — reviewers need tool access to
141
+ verify claims
142
+ - **Don't copy tool names between providers** — Claude's `Read` / `Grep` /
143
+ `Glob` names are not Grok, Codex, Gemini, or Vibe allowlist names. Check
144
+ `provider_tool_capabilities` first, or omit provider tool allowlists.
145
+ - **Do provide file paths** — `"Review changes in src/auth.ts"` instead of dumping file contents
146
+
147
+ ## Iteration Loop (mandatory)
148
+
149
+ Reviews are not one-shot. The caller runs this loop:
150
+
151
+ 1. Dispatch reviewer(s) with the verdict clause in the prompt
152
+ 2. Poll every 60s if deferred; fetch result
153
+ 3. Parse verdict from each reviewer — APPROVED / NOT APPROVED / conditional
154
+ 4. **Any NOT APPROVED or conditional** → consolidate findings → dispatch fixes (Codex + `fullAuto:true`) → re-dispatch same review → goto 2
155
+ 5. **All APPROVED (unconditional)** → done
156
+ 6. After 3 rounds without convergence, escalate to the user
157
+
158
+ "APPROVED with residual notes" counts as approved only if notes are purely informational.
159
+
160
+ ## Tips
161
+
162
+ - Always use `optimizePrompt:true` and `optimizeResponse:true`
163
+ - Use sessions for iterative reviews (review → fix → re-review). Claude,
164
+ Codex, Gemini, Grok, and Mistral carry real provider continuity when their
165
+ provider-specific session rules are satisfied
166
+ - For security-sensitive: `approvalPolicy:"strict"` (in addition to default `mcp_managed`)
167
+ - Include file paths and line numbers for actionable feedback
168
+ - If CLI unavailable, skip gracefully and note gap
169
+ - Use all five async variants for true parallel reviews when you want Grok's
170
+ independent perspective and Mistral Vibe's fifth review
171
+ - Pass `sessionId` to `gemini_request_async` / `grok_request_async` for resumable follow-up
172
+ - Check for `status:"deferred"` in sync responses — poll `jobId` every 60s if present
173
+ - Gateway `mcpServers` default to the host's configured Claude MCP set; pass explicit server names only when the review needs those capabilities
174
+ - **Re-issuing after a polling timeout is safe** — auto-dedup (default 1 h window, `LLM_GATEWAY_DEDUP_WINDOW_MS`) reattaches the new call to the existing job. Use `forceRefresh:true` only when inputs genuinely changed