llm-cli-gateway 1.11.0 → 1.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.
- package/CHANGELOG.md +114 -0
- package/dist/index.d.ts +21 -1
- package/dist/index.js +111 -8
- package/dist/request-helpers.d.ts +20 -0
- package/dist/request-helpers.js +13 -0
- package/dist/upstream-contracts.js +95 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,120 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to the llm-cli-gateway project.
|
|
4
4
|
|
|
5
|
+
## [1.12.0] - 2026-05-27 — Phase 4 slice ζ (working-dir + add-dir cross-provider)
|
|
6
|
+
|
|
7
|
+
Ships the seventh Phase 4 slice: working-directory and additional-directory
|
|
8
|
+
flags are now reachable across four CLIs in a single bundled PR. Three
|
|
9
|
+
commits land together (feature wiring, contract registration, test-veracity
|
|
10
|
+
regressions) plus this release commit.
|
|
11
|
+
|
|
12
|
+
### Added — working-dir + add-dir parity for four CLIs
|
|
13
|
+
|
|
14
|
+
- **Claude** — `claude_request` and `claude_request_async` accept a new
|
|
15
|
+
`addDir: string[]` field. Threaded through `prepareClaudeRequest` →
|
|
16
|
+
`prepareClaudeHighImpactFlags` (`src/request-helpers.ts:687`). Each
|
|
17
|
+
entry emits its own `--add-dir` instance per `claude --help` ("Additional
|
|
18
|
+
directories to allow tool access to"). Claude has no working-dir flag
|
|
19
|
+
(uses the process cwd).
|
|
20
|
+
- **Codex** — `codex_request` and `codex_request_async` accept new
|
|
21
|
+
`workingDir: string` (min 1) and `addDir: string[]` fields. Both flags
|
|
22
|
+
are already in `CODEX_RESUME_FILTERED_FLAGS` (the original session's cwd
|
|
23
|
+
and writable-dir policy are inherited on resume), so `prepareCodexRequest`
|
|
24
|
+
gates emission on `sessionPlan.mode === "new"` — resume argv stays clean
|
|
25
|
+
rather than emitting then stripping. Emits `-C <DIR>` (one) and
|
|
26
|
+
`--add-dir <DIR>` (one instance per entry).
|
|
27
|
+
- **Grok** — `grok_request` and `grok_request_async` accept a new
|
|
28
|
+
`workingDir: string` (min 1) field. `prepareGrokRequest` emits
|
|
29
|
+
`--cwd <DIR>`. Grok has no `--add-dir` analogue.
|
|
30
|
+
- **Vibe (Mistral)** — `mistral_request` and `mistral_request_async`
|
|
31
|
+
accept new `workingDir: string` (min 1) and `addDir: string[]` fields.
|
|
32
|
+
`prepareMistralRequest` (the `request-helpers.ts` helper) emits
|
|
33
|
+
`--workdir <DIR>` (one) and `--add-dir <DIR>` (one per entry; Vibe's
|
|
34
|
+
`--help` states the flag "Can be specified multiple times").
|
|
35
|
+
`buildMistralRetryPrep` threads both fields through to the stale-model
|
|
36
|
+
recovery argv per the slice-δ retry-path invariant.
|
|
37
|
+
- **Gemini** is not re-wired: `--include-directories` was wired in master
|
|
38
|
+
before this slice. A regression-guard test in REGRESSIONS Zε asserts
|
|
39
|
+
the existing wiring stays intact while adjacent contract entries
|
|
40
|
+
changed.
|
|
41
|
+
|
|
42
|
+
### Out of scope — worktree flags
|
|
43
|
+
|
|
44
|
+
Worktree flags (`-w/--worktree` on Claude, Gemini, Grok) create new git
|
|
45
|
+
worktree directories on disk with lifecycle implications and are
|
|
46
|
+
explicitly deferred to a later slice with explicit cleanup semantics.
|
|
47
|
+
|
|
48
|
+
### Contract surface
|
|
49
|
+
|
|
50
|
+
`UPSTREAM_CLI_CONTRACTS` updates:
|
|
51
|
+
|
|
52
|
+
- `claude.flags["--add-dir"]` (arity:"one"; repeated instances accepted)
|
|
53
|
+
- `codex.flags["-C"]` (the gateway only emits the short form; codex
|
|
54
|
+
0.134.0 accepts `--cd` as an alias but the contract registers exactly
|
|
55
|
+
what we emit — a future code path that emitted `--cd` would correctly
|
|
56
|
+
fail the contract check).
|
|
57
|
+
- `codex.flags["--add-dir"]`
|
|
58
|
+
- `grok.flags["--cwd"]`
|
|
59
|
+
- `mistral.flags["--workdir"]`
|
|
60
|
+
- `mistral.flags["--add-dir"]`
|
|
61
|
+
- `mcpParameters` arrays updated for all four CLIs.
|
|
62
|
+
- Six new passing conformance fixtures (`claude-add-dir`,
|
|
63
|
+
`codex-working-dir`, `codex-add-dir`, `grok-working-dir`,
|
|
64
|
+
`mistral-working-dir`, `mistral-add-dir`); each is mechanically
|
|
65
|
+
validated against `validateUpstreamCliArgs` in the REGRESSIONS Zε
|
|
66
|
+
suite, closing the gap class identified in slice ε round 1.
|
|
67
|
+
|
|
68
|
+
### Test-veracity audit
|
|
69
|
+
|
|
70
|
+
Per the standing protocol (`feedback_test_veracity_audit_protocol`),
|
|
71
|
+
this slice's tests were audited by all five LLM reviewers (Codex,
|
|
72
|
+
Gemini, Grok, Mistral, Claude) in async parallel with mandatory
|
|
73
|
+
mutation-probe execution against `docs/plans/test-veracity-audit-slice-zeta.spec.md`.
|
|
74
|
+
|
|
75
|
+
**Round 1 outcomes:**
|
|
76
|
+
|
|
77
|
+
- Codex: UNCONDITIONAL APPROVE — all 13 probes [as predicted], all 37
|
|
78
|
+
tests VERIFIED. Baseline (`npx vitest run` on the slice file: 37/37;
|
|
79
|
+
`npm test`: 54 files / 853 tests; build + format:check clean).
|
|
80
|
+
- Grok: UNCONDITIONAL APPROVE — all 13 probes [as predicted].
|
|
81
|
+
- Mistral: UNCONDITIONAL APPROVE — all 13 probes [as predicted].
|
|
82
|
+
- Claude: UNCONDITIONAL APPROVE — all 13 probes red as predicted; ran
|
|
83
|
+
in an isolated `/tmp/zeta-audit-claude` worktree because the four
|
|
84
|
+
parallel reviewers were concurrently mutating the live tree.
|
|
85
|
+
- Gemini: UNCONDITIONAL APPROVE — all 13 probes [as predicted].
|
|
86
|
+
|
|
87
|
+
First unanimous round-1 pass on a multi-CLI slice. The 37 new tests
|
|
88
|
+
(816 → 853 total) cover every new field/flag/fixture across REGRESSIONS
|
|
89
|
+
Zα/β/ε:
|
|
90
|
+
|
|
91
|
+
- **Zα** — Registered tool inputSchema for every new field on every
|
|
92
|
+
tool (sync + async), including `.min(1)` empty-string rejection on
|
|
93
|
+
`workingDir`.
|
|
94
|
+
- **Zβ** — `prepare*Request` end-to-end argv emission per CLI. The
|
|
95
|
+
Codex resume branch asserts NEITHER `-C` NOR `--add-dir` appears
|
|
96
|
+
in resume argv. `buildMistralRetryPrep` regression catches the
|
|
97
|
+
slice-δ retry-path bug class. Prepare → contract end-to-end
|
|
98
|
+
consistency covers all four CLIs.
|
|
99
|
+
- **Zε** — `UPSTREAM_CLI_CONTRACTS` introspection + mechanical
|
|
100
|
+
fixture validation in the same `it()` block (slice-ε round-1 gap
|
|
101
|
+
class). Includes a regression guard for the pre-existing Gemini
|
|
102
|
+
`--include-directories` wiring.
|
|
103
|
+
|
|
104
|
+
### Mechanical anchors (verify with `rg` before relying)
|
|
105
|
+
|
|
106
|
+
- `src/request-helpers.ts` — `ClaudeHighImpactFlagsInput.addDir`
|
|
107
|
+
(`:610`), `prepareClaudeHighImpactFlags` emission (`:686-690`).
|
|
108
|
+
`PrepareMistralRequestInput.workingDir`/`.addDir` (`:248-264`),
|
|
109
|
+
`prepareMistralRequest` emission (`:300-307`).
|
|
110
|
+
- `src/index.ts` — `prepareClaudeRequest` (`:1338`),
|
|
111
|
+
`prepareCodexRequest` new-session gate (`:1687-1700`),
|
|
112
|
+
`prepareGrokRequest` `--cwd` emission (`:2065-2067`),
|
|
113
|
+
`prepareMistralRequest` wrapper (`:2153-2168`),
|
|
114
|
+
`buildMistralRetryPrep` (`:2249-2289`).
|
|
115
|
+
- `src/upstream-contracts.ts` — flag registrations and conformance
|
|
116
|
+
fixtures for the four CLIs (`:146-149`, `:281-292`, `:438-441`,
|
|
117
|
+
`:524-533`, plus `mcpParameters` entries).
|
|
118
|
+
|
|
5
119
|
## [1.11.0] - 2026-05-27 — Phase 4 slice η (Claude `--fallback-model` + `--json-schema`)
|
|
6
120
|
|
|
7
121
|
Ships the sixth Phase 4 slice: Claude's reliability fallback and
|
package/dist/index.d.ts
CHANGED
|
@@ -157,6 +157,7 @@ export declare function prepareClaudeRequest(params: {
|
|
|
157
157
|
excludeDynamicSystemPromptSections?: boolean;
|
|
158
158
|
fallbackModel?: string;
|
|
159
159
|
jsonSchema?: string | Record<string, unknown>;
|
|
160
|
+
addDir?: string[];
|
|
160
161
|
}, runtime?: GatewayServerRuntime): CliRequestPrep | ExtendedToolResponse;
|
|
161
162
|
export interface CodexRequestPrep extends CliRequestPrep {
|
|
162
163
|
/**
|
|
@@ -199,6 +200,8 @@ export declare function prepareCodexRequest(params: {
|
|
|
199
200
|
images?: string[];
|
|
200
201
|
ignoreUserConfig?: boolean;
|
|
201
202
|
ignoreRules?: boolean;
|
|
203
|
+
workingDir?: string;
|
|
204
|
+
addDir?: string[];
|
|
202
205
|
}, runtime?: GatewayServerRuntime): CodexRequestPrep | ExtendedToolResponse;
|
|
203
206
|
export declare function prepareGeminiRequest(params: {
|
|
204
207
|
prompt?: string;
|
|
@@ -254,6 +257,11 @@ export declare function prepareGrokRequest(params: {
|
|
|
254
257
|
* iterations for cost / latency control. Mirrors Claude's wiring.
|
|
255
258
|
*/
|
|
256
259
|
maxTurns?: number;
|
|
260
|
+
/**
|
|
261
|
+
* Phase 4 slice ζ: emit `--cwd <DIR>` so headless callers can set Grok's
|
|
262
|
+
* working directory without depending on the gateway process's cwd.
|
|
263
|
+
*/
|
|
264
|
+
workingDir?: string;
|
|
257
265
|
}, runtime?: GatewayServerRuntime): CliRequestPrep | ExtendedToolResponse;
|
|
258
266
|
export declare function prepareMistralRequest(params: {
|
|
259
267
|
prompt?: string;
|
|
@@ -280,6 +288,10 @@ export declare function prepareMistralRequest(params: {
|
|
|
280
288
|
maxTurns?: number;
|
|
281
289
|
/** Phase 4 slice δ: Vibe `--max-price DOLLARS` cumulative-cost cap. */
|
|
282
290
|
maxPrice?: number;
|
|
291
|
+
/** Phase 4 slice ζ: Vibe `--workdir <DIR>` working-directory parity. */
|
|
292
|
+
workingDir?: string;
|
|
293
|
+
/** Phase 4 slice ζ: Vibe `--add-dir <DIR>` repeatable add-dir parity. */
|
|
294
|
+
addDir?: string[];
|
|
283
295
|
}, runtime?: GatewayServerRuntime): (CliRequestPrep & {
|
|
284
296
|
mistralEnv: Record<string, string>;
|
|
285
297
|
}) | ExtendedToolResponse;
|
|
@@ -292,7 +304,7 @@ export declare function prepareMistralRequest(params: {
|
|
|
292
304
|
* through here, or a fresh-workspace / budgeted run can degrade on
|
|
293
305
|
* the second attempt.
|
|
294
306
|
*/
|
|
295
|
-
export declare function buildMistralRetryPrep(params: Pick<MistralRequestParams, "outputFormat" | "permissionMode" | "effort" | "reasoningEffort" | "allowedTools" | "disallowedTools" | "approvalStrategy" | "trust" | "maxTurns" | "maxPrice"> & {
|
|
307
|
+
export declare function buildMistralRetryPrep(params: Pick<MistralRequestParams, "outputFormat" | "permissionMode" | "effort" | "reasoningEffort" | "allowedTools" | "disallowedTools" | "approvalStrategy" | "trust" | "maxTurns" | "maxPrice" | "workingDir" | "addDir"> & {
|
|
296
308
|
effectivePrompt: string;
|
|
297
309
|
}, recoveryModel: string): {
|
|
298
310
|
args: string[];
|
|
@@ -368,6 +380,8 @@ export interface GrokRequestParams {
|
|
|
368
380
|
forceRefresh?: boolean;
|
|
369
381
|
/** Phase 4 slice δ: cap agent-loop iterations via `--max-turns N`. */
|
|
370
382
|
maxTurns?: number;
|
|
383
|
+
/** Phase 4 slice ζ: emit `--cwd <DIR>` so the CLI uses the specified working directory. */
|
|
384
|
+
workingDir?: string;
|
|
371
385
|
}
|
|
372
386
|
export declare function handleGrokRequest(deps: HandlerDeps, params: GrokRequestParams): Promise<ExtendedToolResponse>;
|
|
373
387
|
export declare function handleGrokRequestAsync(deps: AsyncHandlerDeps, params: Omit<GrokRequestParams, "optimizeResponse">): Promise<ExtendedToolResponse>;
|
|
@@ -398,6 +412,10 @@ export interface MistralRequestParams {
|
|
|
398
412
|
maxTurns?: number;
|
|
399
413
|
/** Phase 4 slice δ: Vibe `--max-price DOLLARS` cumulative-cost cap. */
|
|
400
414
|
maxPrice?: number;
|
|
415
|
+
/** Phase 4 slice ζ: Vibe `--workdir <DIR>` working-directory parity. */
|
|
416
|
+
workingDir?: string;
|
|
417
|
+
/** Phase 4 slice ζ: Vibe `--add-dir <DIR>` repeatable add-dir parity. */
|
|
418
|
+
addDir?: string[];
|
|
401
419
|
}
|
|
402
420
|
export declare function handleMistralRequest(deps: HandlerDeps, params: MistralRequestParams): Promise<ExtendedToolResponse>;
|
|
403
421
|
export declare function handleMistralRequestAsync(deps: AsyncHandlerDeps, params: Omit<MistralRequestParams, "optimizeResponse">): Promise<ExtendedToolResponse>;
|
|
@@ -430,6 +448,8 @@ export declare function handleCodexRequestAsync(deps: AsyncHandlerDeps, params:
|
|
|
430
448
|
images?: string[];
|
|
431
449
|
ignoreUserConfig?: boolean;
|
|
432
450
|
ignoreRules?: boolean;
|
|
451
|
+
workingDir?: string;
|
|
452
|
+
addDir?: string[];
|
|
433
453
|
}): Promise<ExtendedToolResponse>;
|
|
434
454
|
export declare function createGatewayServer(deps?: GatewayServerDeps): McpServer;
|
|
435
455
|
export {};
|
package/dist/index.js
CHANGED
|
@@ -1007,6 +1007,7 @@ export function prepareClaudeRequest(params, runtime = resolveGatewayServerRunti
|
|
|
1007
1007
|
excludeDynamicSystemPromptSections: params.excludeDynamicSystemPromptSections,
|
|
1008
1008
|
fallbackModel: params.fallbackModel,
|
|
1009
1009
|
jsonSchema: params.jsonSchema,
|
|
1010
|
+
addDir: params.addDir,
|
|
1010
1011
|
}));
|
|
1011
1012
|
return {
|
|
1012
1013
|
corrId,
|
|
@@ -1126,6 +1127,19 @@ export function prepareCodexRequest(params, runtime = resolveGatewayServerRuntim
|
|
|
1126
1127
|
// and are emitted in both branches.
|
|
1127
1128
|
let highImpactCleanup;
|
|
1128
1129
|
if (sessionPlan.mode === "new") {
|
|
1130
|
+
// Phase 4 slice ζ: emit working-dir and add-dir on new sessions only.
|
|
1131
|
+
// Both flags are listed in CODEX_RESUME_FILTERED_FLAGS — resume inherits
|
|
1132
|
+
// the original session's cwd and writable-dir policy, so emitting them
|
|
1133
|
+
// on resume would be silently stripped (wasteful + misleading on argv
|
|
1134
|
+
// logs). Gating here mirrors `--search` / `--sandbox` / `--full-auto`.
|
|
1135
|
+
if (params.workingDir) {
|
|
1136
|
+
args.push("-C", params.workingDir);
|
|
1137
|
+
}
|
|
1138
|
+
if (params.addDir && params.addDir.length > 0) {
|
|
1139
|
+
for (const dir of params.addDir) {
|
|
1140
|
+
args.push("--add-dir", dir);
|
|
1141
|
+
}
|
|
1142
|
+
}
|
|
1129
1143
|
const high = prepareCodexHighImpactFlags({
|
|
1130
1144
|
outputSchema: params.outputSchema,
|
|
1131
1145
|
search: params.search,
|
|
@@ -1381,6 +1395,9 @@ export function prepareGrokRequest(params, runtime = resolveGatewayServerRuntime
|
|
|
1381
1395
|
if (params.maxTurns !== undefined) {
|
|
1382
1396
|
args.push("--max-turns", String(params.maxTurns));
|
|
1383
1397
|
}
|
|
1398
|
+
if (params.workingDir) {
|
|
1399
|
+
args.push("--cwd", params.workingDir);
|
|
1400
|
+
}
|
|
1384
1401
|
return {
|
|
1385
1402
|
corrId,
|
|
1386
1403
|
effectivePrompt,
|
|
@@ -1467,6 +1484,8 @@ export function prepareMistralRequest(params, runtime = resolveGatewayServerRunt
|
|
|
1467
1484
|
trust: params.trust,
|
|
1468
1485
|
maxTurns: params.maxTurns,
|
|
1469
1486
|
maxPrice: params.maxPrice,
|
|
1487
|
+
workingDir: params.workingDir,
|
|
1488
|
+
addDir: params.addDir,
|
|
1470
1489
|
});
|
|
1471
1490
|
if (prep.ignoredDisallowedTools) {
|
|
1472
1491
|
runtime.logger.info(`[${corrId}] Mistral does not support disallowedTools; ignoring (caller passed ${params.disallowedTools?.length ?? 0} entries)`);
|
|
@@ -1521,6 +1540,8 @@ export function buildMistralRetryPrep(params, recoveryModel) {
|
|
|
1521
1540
|
trust: params.trust,
|
|
1522
1541
|
maxTurns: params.maxTurns,
|
|
1523
1542
|
maxPrice: params.maxPrice,
|
|
1543
|
+
workingDir: params.workingDir,
|
|
1544
|
+
addDir: params.addDir,
|
|
1524
1545
|
});
|
|
1525
1546
|
}
|
|
1526
1547
|
function buildCliResponse(cli, stdout, optimizeResponse, corrId, sessionId, prep, durationMs, resumable, outputFormat, warnings) {
|
|
@@ -1862,6 +1883,7 @@ export async function handleGrokRequest(deps, params) {
|
|
|
1862
1883
|
optimizePrompt: params.optimizePrompt,
|
|
1863
1884
|
operation: "grok_request",
|
|
1864
1885
|
maxTurns: params.maxTurns,
|
|
1886
|
+
workingDir: params.workingDir,
|
|
1865
1887
|
}, runtime);
|
|
1866
1888
|
if (!("args" in prep))
|
|
1867
1889
|
return prep;
|
|
@@ -1983,6 +2005,7 @@ export async function handleGrokRequestAsync(deps, params) {
|
|
|
1983
2005
|
optimizePrompt: params.optimizePrompt,
|
|
1984
2006
|
operation: "grok_request_async",
|
|
1985
2007
|
maxTurns: params.maxTurns,
|
|
2008
|
+
workingDir: params.workingDir,
|
|
1986
2009
|
}, runtime);
|
|
1987
2010
|
if (!("args" in prep))
|
|
1988
2011
|
return prep;
|
|
@@ -2067,6 +2090,8 @@ export async function handleMistralRequest(deps, params) {
|
|
|
2067
2090
|
trust: params.trust,
|
|
2068
2091
|
maxTurns: params.maxTurns,
|
|
2069
2092
|
maxPrice: params.maxPrice,
|
|
2093
|
+
workingDir: params.workingDir,
|
|
2094
|
+
addDir: params.addDir,
|
|
2070
2095
|
}, runtime);
|
|
2071
2096
|
if (!("args" in prep))
|
|
2072
2097
|
return prep;
|
|
@@ -2202,6 +2227,8 @@ export async function handleMistralRequestAsync(deps, params) {
|
|
|
2202
2227
|
trust: params.trust,
|
|
2203
2228
|
maxTurns: params.maxTurns,
|
|
2204
2229
|
maxPrice: params.maxPrice,
|
|
2230
|
+
workingDir: params.workingDir,
|
|
2231
|
+
addDir: params.addDir,
|
|
2205
2232
|
}, runtime);
|
|
2206
2233
|
if (!("args" in prep))
|
|
2207
2234
|
return prep;
|
|
@@ -2290,6 +2317,8 @@ export async function handleCodexRequestAsync(deps, params) {
|
|
|
2290
2317
|
images: params.images,
|
|
2291
2318
|
ignoreUserConfig: params.ignoreUserConfig,
|
|
2292
2319
|
ignoreRules: params.ignoreRules,
|
|
2320
|
+
workingDir: params.workingDir,
|
|
2321
|
+
addDir: params.addDir,
|
|
2293
2322
|
}, runtime);
|
|
2294
2323
|
if (!("args" in prep))
|
|
2295
2324
|
return prep;
|
|
@@ -2493,6 +2522,11 @@ export function createGatewayServer(deps = {}) {
|
|
|
2493
2522
|
.union([z.string(), z.record(z.unknown())])
|
|
2494
2523
|
.optional()
|
|
2495
2524
|
.describe("Claude --json-schema: JSON Schema literal (NOT a path) constraining structured output. Object values are JSON.stringify-d; string values are passed verbatim. Use with outputFormat='json'."),
|
|
2525
|
+
// Phase 4 slice ζ — Claude additional-workspace-dirs parity
|
|
2526
|
+
addDir: z
|
|
2527
|
+
.array(z.string())
|
|
2528
|
+
.optional()
|
|
2529
|
+
.describe("Claude --add-dir: additional directories the CLI is allowed to read/write beyond the process cwd. Each entry is emitted as its own --add-dir instance."),
|
|
2496
2530
|
approvalStrategy: z
|
|
2497
2531
|
.enum(["legacy", "mcp_managed"])
|
|
2498
2532
|
.default("legacy")
|
|
@@ -2523,7 +2557,7 @@ export function createGatewayServer(deps = {}) {
|
|
|
2523
2557
|
.boolean()
|
|
2524
2558
|
.default(false)
|
|
2525
2559
|
.describe("Bypass dedup and force a fresh CLI run even if a recent identical request exists"),
|
|
2526
|
-
}, async ({ prompt, promptParts, model, outputFormat, sessionId, continueSession, createNewSession, allowedTools, disallowedTools, dangerouslySkipPermissions, permissionMode, agent, agents, forkSession, systemPrompt, appendSystemPrompt, maxBudgetUsd, maxTurns, effort, excludeDynamicSystemPromptSections, fallbackModel, jsonSchema, approvalStrategy, approvalPolicy, mcpServers, strictMcpConfig, correlationId, optimizePrompt, optimizeResponse, idleTimeoutMs, forceRefresh, }) => {
|
|
2560
|
+
}, async ({ prompt, promptParts, model, outputFormat, sessionId, continueSession, createNewSession, allowedTools, disallowedTools, dangerouslySkipPermissions, permissionMode, agent, agents, forkSession, systemPrompt, appendSystemPrompt, maxBudgetUsd, maxTurns, effort, excludeDynamicSystemPromptSections, fallbackModel, jsonSchema, addDir, approvalStrategy, approvalPolicy, mcpServers, strictMcpConfig, correlationId, optimizePrompt, optimizeResponse, idleTimeoutMs, forceRefresh, }) => {
|
|
2527
2561
|
const startTime = Date.now();
|
|
2528
2562
|
if (systemPrompt !== undefined && appendSystemPrompt !== undefined) {
|
|
2529
2563
|
return createErrorResponse("claude", 1, "", correlationId, new Error("systemPrompt and appendSystemPrompt are mutually exclusive; use one or the other (not both)."));
|
|
@@ -2555,6 +2589,7 @@ export function createGatewayServer(deps = {}) {
|
|
|
2555
2589
|
excludeDynamicSystemPromptSections,
|
|
2556
2590
|
fallbackModel,
|
|
2557
2591
|
jsonSchema,
|
|
2592
|
+
addDir,
|
|
2558
2593
|
}, runtime);
|
|
2559
2594
|
if (!("args" in prep))
|
|
2560
2595
|
return prep;
|
|
@@ -2809,7 +2844,17 @@ export function createGatewayServer(deps = {}) {
|
|
|
2809
2844
|
.boolean()
|
|
2810
2845
|
.optional()
|
|
2811
2846
|
.describe("Codex --ignore-rules: skip project rule files for this run."),
|
|
2812
|
-
|
|
2847
|
+
// Phase 4 slice ζ — Codex working-dir + add-dir parity (new sessions only).
|
|
2848
|
+
workingDir: z
|
|
2849
|
+
.string()
|
|
2850
|
+
.min(1)
|
|
2851
|
+
.optional()
|
|
2852
|
+
.describe("Codex -C/--cd <DIR>: working root for this session. Emitted on new sessions only; resume inherits the original session's cwd via CODEX_RESUME_FILTERED_FLAGS."),
|
|
2853
|
+
addDir: z
|
|
2854
|
+
.array(z.string())
|
|
2855
|
+
.optional()
|
|
2856
|
+
.describe("Codex --add-dir <DIR>: additional writable workspace directories. Emitted once per entry on new sessions only; resume inherits the original session's writable-dir policy."),
|
|
2857
|
+
}, async ({ prompt, promptParts, model, fullAuto, sandboxMode, askForApproval, useLegacyFullAutoFlag, dangerouslyBypassApprovalsAndSandbox, approvalStrategy, approvalPolicy, mcpServers, sessionId, resumeLatest, createNewSession, correlationId, optimizePrompt, optimizeResponse, idleTimeoutMs, forceRefresh, outputFormat, outputSchema, search, profile, configOverrides, ephemeral, images, ignoreUserConfig, ignoreRules, workingDir, addDir, }) => {
|
|
2813
2858
|
const startTime = Date.now();
|
|
2814
2859
|
const prep = prepareCodexRequest({
|
|
2815
2860
|
prompt,
|
|
@@ -2838,6 +2883,8 @@ export function createGatewayServer(deps = {}) {
|
|
|
2838
2883
|
images,
|
|
2839
2884
|
ignoreUserConfig,
|
|
2840
2885
|
ignoreRules,
|
|
2886
|
+
workingDir,
|
|
2887
|
+
addDir,
|
|
2841
2888
|
}, runtime);
|
|
2842
2889
|
if (!("args" in prep))
|
|
2843
2890
|
return prep;
|
|
@@ -3209,7 +3256,13 @@ export function createGatewayServer(deps = {}) {
|
|
|
3209
3256
|
.default(false)
|
|
3210
3257
|
.describe("Bypass dedup and force a fresh CLI run even if a recent identical request exists"),
|
|
3211
3258
|
maxTurns: MAX_TURNS_SCHEMA.optional().describe("Grok `--max-turns N`: cap on agent-loop iterations for cost / latency control (Phase 4 slice δ). Bounded to safe integers ≤ 10000."),
|
|
3212
|
-
|
|
3259
|
+
// Phase 4 slice ζ — Grok working-directory parity.
|
|
3260
|
+
workingDir: z
|
|
3261
|
+
.string()
|
|
3262
|
+
.min(1)
|
|
3263
|
+
.optional()
|
|
3264
|
+
.describe("Grok --cwd <DIR>: working directory for this invocation. Lets headless callers run Grok against a directory other than the gateway process's cwd."),
|
|
3265
|
+
}, async ({ prompt, promptParts, model, outputFormat, sessionId, resumeLatest, createNewSession, alwaysApprove, permissionMode, effort, reasoningEffort, approvalStrategy, approvalPolicy, mcpServers, allowedTools, disallowedTools, correlationId, optimizePrompt, optimizeResponse, idleTimeoutMs, forceRefresh, maxTurns, workingDir, }) => {
|
|
3213
3266
|
return handleGrokRequest({ sessionManager, logger, runtime }, {
|
|
3214
3267
|
prompt,
|
|
3215
3268
|
promptParts,
|
|
@@ -3233,6 +3286,7 @@ export function createGatewayServer(deps = {}) {
|
|
|
3233
3286
|
idleTimeoutMs,
|
|
3234
3287
|
forceRefresh,
|
|
3235
3288
|
maxTurns,
|
|
3289
|
+
workingDir,
|
|
3236
3290
|
});
|
|
3237
3291
|
});
|
|
3238
3292
|
//──────────────────────────────────────────────────────────────────────────────
|
|
@@ -3312,7 +3366,17 @@ export function createGatewayServer(deps = {}) {
|
|
|
3312
3366
|
.describe("Emit `--trust` so Vibe trusts the cwd for this invocation only (not persisted to trusted_folders.toml) and skips the interactive trust prompt (Phase 4 slice γ)."),
|
|
3313
3367
|
maxTurns: MAX_TURNS_SCHEMA.optional().describe("Vibe `--max-turns N`: cap the agent-loop iteration count (programmatic mode only, Phase 4 slice δ). Bounded to safe integers ≤ 10000."),
|
|
3314
3368
|
maxPrice: MAX_PRICE_SCHEMA.optional().describe("Vibe `--max-price DOLLARS`: interrupt the session when cumulative cost crosses this cap (programmatic mode only, Phase 4 slice δ). Bounded to finite values ≤ 10000 USD."),
|
|
3315
|
-
|
|
3369
|
+
// Phase 4 slice ζ — Vibe working-directory + additional-dirs parity.
|
|
3370
|
+
workingDir: z
|
|
3371
|
+
.string()
|
|
3372
|
+
.min(1)
|
|
3373
|
+
.optional()
|
|
3374
|
+
.describe("Vibe --workdir <DIR>: change to this directory before running. Single value (Vibe accepts one --workdir per invocation)."),
|
|
3375
|
+
addDir: z
|
|
3376
|
+
.array(z.string())
|
|
3377
|
+
.optional()
|
|
3378
|
+
.describe("Vibe --add-dir <DIR>: additional writable workspace directories. Each entry is emitted as its own --add-dir instance (Vibe states this flag may be specified multiple times)."),
|
|
3379
|
+
}, async ({ prompt, promptParts, model, outputFormat, sessionId, resumeLatest, createNewSession, permissionMode, effort, reasoningEffort, approvalStrategy, approvalPolicy, mcpServers, allowedTools, disallowedTools, correlationId, optimizePrompt, optimizeResponse, idleTimeoutMs, forceRefresh, trust, maxTurns, maxPrice, workingDir, addDir, }) => {
|
|
3316
3380
|
return handleMistralRequest({ sessionManager, logger, runtime }, {
|
|
3317
3381
|
prompt,
|
|
3318
3382
|
promptParts,
|
|
@@ -3337,6 +3401,8 @@ export function createGatewayServer(deps = {}) {
|
|
|
3337
3401
|
trust,
|
|
3338
3402
|
maxTurns,
|
|
3339
3403
|
maxPrice,
|
|
3404
|
+
workingDir,
|
|
3405
|
+
addDir,
|
|
3340
3406
|
});
|
|
3341
3407
|
});
|
|
3342
3408
|
//──────────────────────────────────────────────────────────────────────────────
|
|
@@ -3432,6 +3498,11 @@ export function createGatewayServer(deps = {}) {
|
|
|
3432
3498
|
.union([z.string(), z.record(z.unknown())])
|
|
3433
3499
|
.optional()
|
|
3434
3500
|
.describe("Claude --json-schema: JSON Schema literal (NOT a path) constraining structured output. Object values are JSON.stringify-d; string values are passed verbatim. Use with outputFormat='json'."),
|
|
3501
|
+
// Phase 4 slice ζ — Claude additional-workspace-dirs parity
|
|
3502
|
+
addDir: z
|
|
3503
|
+
.array(z.string())
|
|
3504
|
+
.optional()
|
|
3505
|
+
.describe("Claude --add-dir: additional directories the CLI is allowed to read/write beyond the process cwd. Each entry is emitted as its own --add-dir instance."),
|
|
3435
3506
|
approvalStrategy: z
|
|
3436
3507
|
.enum(["legacy", "mcp_managed"])
|
|
3437
3508
|
.default("legacy")
|
|
@@ -3461,7 +3532,7 @@ export function createGatewayServer(deps = {}) {
|
|
|
3461
3532
|
.boolean()
|
|
3462
3533
|
.default(false)
|
|
3463
3534
|
.describe("Bypass dedup and force a fresh CLI run even if a recent identical request exists"),
|
|
3464
|
-
}, async ({ prompt, promptParts, model, outputFormat, sessionId, continueSession, createNewSession, allowedTools, disallowedTools, dangerouslySkipPermissions, permissionMode, agent, agents, forkSession, systemPrompt, appendSystemPrompt, maxBudgetUsd, maxTurns, effort, excludeDynamicSystemPromptSections, fallbackModel, jsonSchema, approvalStrategy, approvalPolicy, mcpServers, strictMcpConfig, correlationId, optimizePrompt, idleTimeoutMs, forceRefresh, }) => {
|
|
3535
|
+
}, async ({ prompt, promptParts, model, outputFormat, sessionId, continueSession, createNewSession, allowedTools, disallowedTools, dangerouslySkipPermissions, permissionMode, agent, agents, forkSession, systemPrompt, appendSystemPrompt, maxBudgetUsd, maxTurns, effort, excludeDynamicSystemPromptSections, fallbackModel, jsonSchema, addDir, approvalStrategy, approvalPolicy, mcpServers, strictMcpConfig, correlationId, optimizePrompt, idleTimeoutMs, forceRefresh, }) => {
|
|
3465
3536
|
if (systemPrompt !== undefined && appendSystemPrompt !== undefined) {
|
|
3466
3537
|
return createErrorResponse("claude", 1, "", correlationId, new Error("systemPrompt and appendSystemPrompt are mutually exclusive; use one or the other (not both)."));
|
|
3467
3538
|
}
|
|
@@ -3492,6 +3563,7 @@ export function createGatewayServer(deps = {}) {
|
|
|
3492
3563
|
excludeDynamicSystemPromptSections,
|
|
3493
3564
|
fallbackModel,
|
|
3494
3565
|
jsonSchema,
|
|
3566
|
+
addDir,
|
|
3495
3567
|
}, runtime);
|
|
3496
3568
|
if (!("args" in prep))
|
|
3497
3569
|
return prep;
|
|
@@ -3646,7 +3718,17 @@ export function createGatewayServer(deps = {}) {
|
|
|
3646
3718
|
images: z.array(z.string()).optional().describe("Codex -i <path>: image attachments."),
|
|
3647
3719
|
ignoreUserConfig: z.boolean().optional().describe("Codex --ignore-user-config."),
|
|
3648
3720
|
ignoreRules: z.boolean().optional().describe("Codex --ignore-rules."),
|
|
3649
|
-
|
|
3721
|
+
// Phase 4 slice ζ — Codex working-dir + add-dir parity (new sessions only).
|
|
3722
|
+
workingDir: z
|
|
3723
|
+
.string()
|
|
3724
|
+
.min(1)
|
|
3725
|
+
.optional()
|
|
3726
|
+
.describe("Codex -C/--cd <DIR>: working root for this session. New sessions only; resume inherits the original session's cwd."),
|
|
3727
|
+
addDir: z
|
|
3728
|
+
.array(z.string())
|
|
3729
|
+
.optional()
|
|
3730
|
+
.describe("Codex --add-dir <DIR>: additional writable workspace directories (repeat per entry). New sessions only."),
|
|
3731
|
+
}, async ({ prompt, promptParts, model, fullAuto, sandboxMode, askForApproval, useLegacyFullAutoFlag, dangerouslyBypassApprovalsAndSandbox, approvalStrategy, approvalPolicy, mcpServers, sessionId, resumeLatest, createNewSession, correlationId, optimizePrompt, idleTimeoutMs, forceRefresh, outputFormat, outputSchema, search, profile, configOverrides, ephemeral, images, ignoreUserConfig, ignoreRules, workingDir, addDir, }) => {
|
|
3650
3732
|
return handleCodexRequestAsync({ sessionManager, asyncJobManager, logger, runtime }, {
|
|
3651
3733
|
prompt,
|
|
3652
3734
|
promptParts,
|
|
@@ -3675,6 +3757,8 @@ export function createGatewayServer(deps = {}) {
|
|
|
3675
3757
|
images,
|
|
3676
3758
|
ignoreUserConfig,
|
|
3677
3759
|
ignoreRules,
|
|
3760
|
+
workingDir,
|
|
3761
|
+
addDir,
|
|
3678
3762
|
});
|
|
3679
3763
|
});
|
|
3680
3764
|
server.tool("gemini_request_async", {
|
|
@@ -3841,7 +3925,13 @@ export function createGatewayServer(deps = {}) {
|
|
|
3841
3925
|
.default(false)
|
|
3842
3926
|
.describe("Bypass dedup and force a fresh CLI run even if a recent identical request exists"),
|
|
3843
3927
|
maxTurns: MAX_TURNS_SCHEMA.optional().describe("Grok `--max-turns N`: cap on agent-loop iterations for cost / latency control (Phase 4 slice δ). Bounded to safe integers ≤ 10000."),
|
|
3844
|
-
|
|
3928
|
+
// Phase 4 slice ζ — Grok working-directory parity.
|
|
3929
|
+
workingDir: z
|
|
3930
|
+
.string()
|
|
3931
|
+
.min(1)
|
|
3932
|
+
.optional()
|
|
3933
|
+
.describe("Grok --cwd <DIR>: working directory for this invocation. Lets headless callers run Grok against a directory other than the gateway process's cwd."),
|
|
3934
|
+
}, async ({ prompt, promptParts, model, outputFormat, sessionId, resumeLatest, createNewSession, alwaysApprove, permissionMode, effort, reasoningEffort, approvalStrategy, approvalPolicy, mcpServers, allowedTools, disallowedTools, correlationId, optimizePrompt, idleTimeoutMs, forceRefresh, maxTurns, workingDir, }) => {
|
|
3845
3935
|
return handleGrokRequestAsync({ sessionManager, asyncJobManager, logger, runtime }, {
|
|
3846
3936
|
prompt,
|
|
3847
3937
|
promptParts,
|
|
@@ -3864,6 +3954,7 @@ export function createGatewayServer(deps = {}) {
|
|
|
3864
3954
|
idleTimeoutMs,
|
|
3865
3955
|
forceRefresh,
|
|
3866
3956
|
maxTurns,
|
|
3957
|
+
workingDir,
|
|
3867
3958
|
});
|
|
3868
3959
|
});
|
|
3869
3960
|
server.tool("mistral_request_async", {
|
|
@@ -3939,7 +4030,17 @@ export function createGatewayServer(deps = {}) {
|
|
|
3939
4030
|
.describe("Emit `--trust` so Vibe trusts the cwd for this invocation only (not persisted to trusted_folders.toml) and skips the interactive trust prompt (Phase 4 slice γ)."),
|
|
3940
4031
|
maxTurns: MAX_TURNS_SCHEMA.optional().describe("Vibe `--max-turns N`: cap the agent-loop iteration count (programmatic mode only, Phase 4 slice δ). Bounded to safe integers ≤ 10000."),
|
|
3941
4032
|
maxPrice: MAX_PRICE_SCHEMA.optional().describe("Vibe `--max-price DOLLARS`: interrupt the session when cumulative cost crosses this cap (programmatic mode only, Phase 4 slice δ). Bounded to finite values ≤ 10000 USD."),
|
|
3942
|
-
|
|
4033
|
+
// Phase 4 slice ζ — Vibe working-directory + additional-dirs parity.
|
|
4034
|
+
workingDir: z
|
|
4035
|
+
.string()
|
|
4036
|
+
.min(1)
|
|
4037
|
+
.optional()
|
|
4038
|
+
.describe("Vibe --workdir <DIR>: change to this directory before running. Single value per invocation."),
|
|
4039
|
+
addDir: z
|
|
4040
|
+
.array(z.string())
|
|
4041
|
+
.optional()
|
|
4042
|
+
.describe("Vibe --add-dir <DIR>: additional writable workspace directories. Each entry is emitted as its own --add-dir instance."),
|
|
4043
|
+
}, async ({ prompt, promptParts, model, outputFormat, sessionId, resumeLatest, createNewSession, permissionMode, effort, reasoningEffort, approvalStrategy, approvalPolicy, mcpServers, allowedTools, disallowedTools, correlationId, optimizePrompt, idleTimeoutMs, forceRefresh, trust, maxTurns, maxPrice, workingDir, addDir, }) => {
|
|
3943
4044
|
return handleMistralRequestAsync({ sessionManager, asyncJobManager, logger, runtime }, {
|
|
3944
4045
|
prompt,
|
|
3945
4046
|
promptParts,
|
|
@@ -3963,6 +4064,8 @@ export function createGatewayServer(deps = {}) {
|
|
|
3963
4064
|
trust,
|
|
3964
4065
|
maxTurns,
|
|
3965
4066
|
maxPrice,
|
|
4067
|
+
workingDir,
|
|
4068
|
+
addDir,
|
|
3966
4069
|
});
|
|
3967
4070
|
});
|
|
3968
4071
|
server.tool("llm_job_status", {
|
|
@@ -125,6 +125,17 @@ export interface PrepareMistralRequestInput {
|
|
|
125
125
|
* only).
|
|
126
126
|
*/
|
|
127
127
|
maxPrice?: number;
|
|
128
|
+
/**
|
|
129
|
+
* Phase 4 slice ζ: emit `--workdir <DIR>` so Vibe changes into the named
|
|
130
|
+
* directory before running. Single value (Vibe accepts one --workdir).
|
|
131
|
+
*/
|
|
132
|
+
workingDir?: string;
|
|
133
|
+
/**
|
|
134
|
+
* Phase 4 slice ζ: emit `--add-dir <DIR>` per directory. Vibe's `--help`
|
|
135
|
+
* states the flag "Can be specified multiple times" — each entry is its
|
|
136
|
+
* own argv pair.
|
|
137
|
+
*/
|
|
138
|
+
addDir?: string[];
|
|
128
139
|
}
|
|
129
140
|
export interface PrepareMistralRequestResult {
|
|
130
141
|
args: string[];
|
|
@@ -364,6 +375,15 @@ export interface ClaudeHighImpactFlagsInput {
|
|
|
364
375
|
* `--output-schema`, which takes a path).
|
|
365
376
|
*/
|
|
366
377
|
jsonSchema?: string | Record<string, unknown>;
|
|
378
|
+
/**
|
|
379
|
+
* Phase 4 slice ζ — Claude `--add-dir <dirs...>`. Additional directories the
|
|
380
|
+
* Claude CLI is allowed to read/write beyond the process cwd. The CLI accepts
|
|
381
|
+
* a single variadic flag (space-separated values) per `claude --help`; we
|
|
382
|
+
* emit one `--add-dir` instance per directory so each path is its own argv
|
|
383
|
+
* token (survives any future tightening of the variadic parser without
|
|
384
|
+
* changing the call site).
|
|
385
|
+
*/
|
|
386
|
+
addDir?: string[];
|
|
367
387
|
}
|
|
368
388
|
/**
|
|
369
389
|
* Emit Claude high-impact feature flags (U25) as a flat argv segment.
|
package/dist/request-helpers.js
CHANGED
|
@@ -185,6 +185,14 @@ export function prepareMistralRequest(input) {
|
|
|
185
185
|
if (input.maxPrice !== undefined) {
|
|
186
186
|
args.push("--max-price", String(input.maxPrice));
|
|
187
187
|
}
|
|
188
|
+
if (input.workingDir) {
|
|
189
|
+
args.push("--workdir", input.workingDir);
|
|
190
|
+
}
|
|
191
|
+
if (input.addDir && input.addDir.length > 0) {
|
|
192
|
+
for (const dir of input.addDir) {
|
|
193
|
+
args.push("--add-dir", dir);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
188
196
|
const ignoredDisallowedTools = Boolean(input.disallowedTools && input.disallowedTools.length > 0);
|
|
189
197
|
return { args, env, ignoredDisallowedTools };
|
|
190
198
|
}
|
|
@@ -445,6 +453,11 @@ export function prepareClaudeHighImpactFlags(input) {
|
|
|
445
453
|
const schemaArg = typeof input.jsonSchema === "string" ? input.jsonSchema : JSON.stringify(input.jsonSchema);
|
|
446
454
|
args.push("--json-schema", schemaArg);
|
|
447
455
|
}
|
|
456
|
+
if (input.addDir && input.addDir.length > 0) {
|
|
457
|
+
for (const dir of input.addDir) {
|
|
458
|
+
args.push("--add-dir", dir);
|
|
459
|
+
}
|
|
460
|
+
}
|
|
448
461
|
return args;
|
|
449
462
|
}
|
|
450
463
|
//──────────────────────────────────────────────────────────────────────────────
|
|
@@ -39,6 +39,8 @@ export const UPSTREAM_CLI_CONTRACTS = {
|
|
|
39
39
|
"excludeDynamicSystemPromptSections",
|
|
40
40
|
"fallbackModel",
|
|
41
41
|
"jsonSchema",
|
|
42
|
+
// Phase 4 slice ζ
|
|
43
|
+
"addDir",
|
|
42
44
|
"approvalStrategy",
|
|
43
45
|
"mcpServers",
|
|
44
46
|
"strictMcpConfig",
|
|
@@ -88,6 +90,10 @@ export const UPSTREAM_CLI_CONTRACTS = {
|
|
|
88
90
|
arity: "one",
|
|
89
91
|
description: "JSON Schema literal constraining structured output",
|
|
90
92
|
},
|
|
93
|
+
"--add-dir": {
|
|
94
|
+
arity: "one",
|
|
95
|
+
description: "Additional workspace directory (Phase 4 slice ζ; repeat once per directory)",
|
|
96
|
+
},
|
|
91
97
|
"--continue": { arity: "none", description: "Continue active session" },
|
|
92
98
|
"--session-id": { arity: "one", description: "Session id" },
|
|
93
99
|
},
|
|
@@ -128,6 +134,14 @@ export const UPSTREAM_CLI_CONTRACTS = {
|
|
|
128
134
|
],
|
|
129
135
|
expect: "pass",
|
|
130
136
|
},
|
|
137
|
+
{
|
|
138
|
+
// Phase 4 slice ζ: --add-dir wired through prepareClaudeHighImpactFlags.
|
|
139
|
+
// Repeated once per directory; each instance has arity:"one".
|
|
140
|
+
id: "claude-add-dir",
|
|
141
|
+
description: "Phase 4 slice ζ: repeated --add-dir is accepted",
|
|
142
|
+
args: ["-p", "hello", "--add-dir", "/tmp/a", "--add-dir", "/tmp/b"],
|
|
143
|
+
expect: "pass",
|
|
144
|
+
},
|
|
131
145
|
],
|
|
132
146
|
},
|
|
133
147
|
codex: {
|
|
@@ -164,6 +178,9 @@ export const UPSTREAM_CLI_CONTRACTS = {
|
|
|
164
178
|
"images",
|
|
165
179
|
"ignoreUserConfig",
|
|
166
180
|
"ignoreRules",
|
|
181
|
+
// Phase 4 slice ζ
|
|
182
|
+
"workingDir",
|
|
183
|
+
"addDir",
|
|
167
184
|
],
|
|
168
185
|
resumeOnlyFlags: ["--last"],
|
|
169
186
|
// Phase 4 slice α (v1.8.0) verified that `codex exec resume` accepts
|
|
@@ -203,6 +220,18 @@ export const UPSTREAM_CLI_CONTRACTS = {
|
|
|
203
220
|
"-i": { arity: "one", description: "Image path" },
|
|
204
221
|
"--ignore-user-config": { arity: "none", description: "Ignore user config" },
|
|
205
222
|
"--ignore-rules": { arity: "none", description: "Ignore rule files" },
|
|
223
|
+
// The gateway only ever emits the short form `-C` (codex 0.134.0 accepts
|
|
224
|
+
// both `-C` and `--cd` as aliases). The contract registers exactly what
|
|
225
|
+
// we emit; if a future code path emits `--cd` instead, the contract
|
|
226
|
+
// check will fail loudly — which is the intended catch.
|
|
227
|
+
"-C": {
|
|
228
|
+
arity: "one",
|
|
229
|
+
description: "Working root for the session (Phase 4 slice ζ; new sessions only)",
|
|
230
|
+
},
|
|
231
|
+
"--add-dir": {
|
|
232
|
+
arity: "one",
|
|
233
|
+
description: "Additional writable workspace directory (Phase 4 slice ζ; repeat once per directory; new sessions only)",
|
|
234
|
+
},
|
|
206
235
|
},
|
|
207
236
|
env: {},
|
|
208
237
|
conformanceFixtures: [
|
|
@@ -239,6 +268,26 @@ export const UPSTREAM_CLI_CONTRACTS = {
|
|
|
239
268
|
args: ["exec", "resume", "--search", "session-id", "hello"],
|
|
240
269
|
expect: "fail",
|
|
241
270
|
},
|
|
271
|
+
{
|
|
272
|
+
id: "codex-working-dir",
|
|
273
|
+
description: "Phase 4 slice ζ: -C <DIR> accepted on a new session",
|
|
274
|
+
args: ["exec", "--skip-git-repo-check", "-C", "/tmp/work", "hello"],
|
|
275
|
+
expect: "pass",
|
|
276
|
+
},
|
|
277
|
+
{
|
|
278
|
+
id: "codex-add-dir",
|
|
279
|
+
description: "Phase 4 slice ζ: repeated --add-dir accepted on a new session",
|
|
280
|
+
args: [
|
|
281
|
+
"exec",
|
|
282
|
+
"--skip-git-repo-check",
|
|
283
|
+
"--add-dir",
|
|
284
|
+
"/tmp/a",
|
|
285
|
+
"--add-dir",
|
|
286
|
+
"/tmp/b",
|
|
287
|
+
"hello",
|
|
288
|
+
],
|
|
289
|
+
expect: "pass",
|
|
290
|
+
},
|
|
242
291
|
],
|
|
243
292
|
},
|
|
244
293
|
gemini: {
|
|
@@ -350,6 +399,8 @@ export const UPSTREAM_CLI_CONTRACTS = {
|
|
|
350
399
|
"disallowedTools",
|
|
351
400
|
// Phase 4 slice δ
|
|
352
401
|
"maxTurns",
|
|
402
|
+
// Phase 4 slice ζ
|
|
403
|
+
"workingDir",
|
|
353
404
|
],
|
|
354
405
|
flags: {
|
|
355
406
|
"-p": { arity: "one", description: "Prompt text" },
|
|
@@ -379,6 +430,10 @@ export const UPSTREAM_CLI_CONTRACTS = {
|
|
|
379
430
|
pattern: /^[1-9][0-9]*$/,
|
|
380
431
|
description: "Agent-loop iteration cap (Phase 4 slice δ)",
|
|
381
432
|
},
|
|
433
|
+
"--cwd": {
|
|
434
|
+
arity: "one",
|
|
435
|
+
description: "Working directory for the invocation (Phase 4 slice ζ)",
|
|
436
|
+
},
|
|
382
437
|
},
|
|
383
438
|
env: {},
|
|
384
439
|
conformanceFixtures: [
|
|
@@ -406,6 +461,12 @@ export const UPSTREAM_CLI_CONTRACTS = {
|
|
|
406
461
|
args: ["-p", "hello", "--max-turns", "0"],
|
|
407
462
|
expect: "fail",
|
|
408
463
|
},
|
|
464
|
+
{
|
|
465
|
+
id: "grok-working-dir",
|
|
466
|
+
description: "Phase 4 slice ζ: --cwd <DIR> is accepted",
|
|
467
|
+
args: ["-p", "hello", "--cwd", "/tmp/work"],
|
|
468
|
+
expect: "pass",
|
|
469
|
+
},
|
|
409
470
|
],
|
|
410
471
|
},
|
|
411
472
|
mistral: {
|
|
@@ -434,6 +495,9 @@ export const UPSTREAM_CLI_CONTRACTS = {
|
|
|
434
495
|
// Phase 4 slice δ
|
|
435
496
|
"maxTurns",
|
|
436
497
|
"maxPrice",
|
|
498
|
+
// Phase 4 slice ζ
|
|
499
|
+
"workingDir",
|
|
500
|
+
"addDir",
|
|
437
501
|
],
|
|
438
502
|
flags: {
|
|
439
503
|
"-p": { arity: "one", description: "Prompt text" },
|
|
@@ -468,6 +532,14 @@ export const UPSTREAM_CLI_CONTRACTS = {
|
|
|
468
532
|
pattern: /^(0|[1-9][0-9]*)(\.[0-9]+)?$/,
|
|
469
533
|
description: "Cumulative cost cap in USD (Phase 4 slice δ, programmatic mode only)",
|
|
470
534
|
},
|
|
535
|
+
"--workdir": {
|
|
536
|
+
arity: "one",
|
|
537
|
+
description: "Working directory for the invocation (Phase 4 slice ζ)",
|
|
538
|
+
},
|
|
539
|
+
"--add-dir": {
|
|
540
|
+
arity: "one",
|
|
541
|
+
description: "Additional writable workspace directory (Phase 4 slice ζ; repeat once per directory)",
|
|
542
|
+
},
|
|
471
543
|
},
|
|
472
544
|
env: {
|
|
473
545
|
VIBE_ACTIVE_MODEL: {
|
|
@@ -512,6 +584,29 @@ export const UPSTREAM_CLI_CONTRACTS = {
|
|
|
512
584
|
env: { VIBE_ACTIVE_MODEL: "mistral-medium-3.5" },
|
|
513
585
|
expect: "fail",
|
|
514
586
|
},
|
|
587
|
+
{
|
|
588
|
+
id: "mistral-working-dir",
|
|
589
|
+
description: "Phase 4 slice ζ: --workdir <DIR> is accepted",
|
|
590
|
+
args: ["-p", "hello", "--agent", "auto-approve", "--workdir", "/tmp/work"],
|
|
591
|
+
env: { VIBE_ACTIVE_MODEL: "mistral-medium-3.5" },
|
|
592
|
+
expect: "pass",
|
|
593
|
+
},
|
|
594
|
+
{
|
|
595
|
+
id: "mistral-add-dir",
|
|
596
|
+
description: "Phase 4 slice ζ: repeated --add-dir is accepted",
|
|
597
|
+
args: [
|
|
598
|
+
"-p",
|
|
599
|
+
"hello",
|
|
600
|
+
"--agent",
|
|
601
|
+
"auto-approve",
|
|
602
|
+
"--add-dir",
|
|
603
|
+
"/tmp/a",
|
|
604
|
+
"--add-dir",
|
|
605
|
+
"/tmp/b",
|
|
606
|
+
],
|
|
607
|
+
env: { VIBE_ACTIVE_MODEL: "mistral-medium-3.5" },
|
|
608
|
+
expect: "pass",
|
|
609
|
+
},
|
|
515
610
|
],
|
|
516
611
|
},
|
|
517
612
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "llm-cli-gateway",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.12.0",
|
|
4
4
|
"mcpName": "io.github.verivus-oss/llm-cli-gateway",
|
|
5
5
|
"description": "MCP server providing unified access to Claude Code, Codex, Gemini, Grok, and Mistral Vibe CLIs with session management, retry logic, async job orchestration, durable job results, and cross-LLM validation.",
|
|
6
6
|
"license": "MIT",
|