llm-cli-gateway 1.14.0 → 1.15.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.
@@ -207,15 +207,21 @@ export class AsyncJobManager {
207
207
  * (sorted keys → JSON-stringified). This prevents two Mistral requests with the
208
208
  * same argv but different `VIBE_ACTIVE_MODEL` from deduping onto each other.
209
209
  */
210
- buildRequestKey(cli, args, env, stdin) {
210
+ buildRequestKey(cli, args, env, stdin, cwd) {
211
211
  // Slice κ: stdin participates in the dedup key. Two Claude requests
212
212
  // with identical argv but different cache_control content blocks
213
213
  // would otherwise collide on dedup and the second caller would get
214
214
  // the wrong response. The legacy "no stdin" code path passes
215
215
  // stdin=undefined, which serialises to the same empty marker the
216
216
  // previous version emitted — non-κ dedup is unchanged.
217
+ // Slice λ: cwd participates similarly. Two requests with identical
218
+ // argv but different worktrees would otherwise collide on dedup and
219
+ // the second caller would receive a response executed in the wrong
220
+ // worktree. cwd=undefined preserves the pre-λ key shape — non-λ
221
+ // dedup is unchanged.
217
222
  const extraEnv = canonicaliseEnvForKey(env);
218
- const extra = stdin === undefined ? extraEnv : `${extraEnv}|stdin:${stdin}`;
223
+ const withStdin = stdin === undefined ? extraEnv : `${extraEnv}|stdin:${stdin}`;
224
+ const extra = cwd === undefined ? withStdin : `${withStdin}|cwd:${cwd}`;
219
225
  return computeRequestKey(cli, args, extra);
220
226
  }
221
227
  fireOnComplete(job) {
@@ -449,7 +455,7 @@ export class AsyncJobManager {
449
455
  */
450
456
  startJobWithDedup(cli, args, correlationId, opts = {}) {
451
457
  const { cwd, idleTimeoutMs, outputFormat, forceRefresh, env: extraEnv, stdin, onComplete, flightRecorderEntry, extractUsage, writeFlightStart, } = opts;
452
- const requestKey = this.buildRequestKey(cli, args, extraEnv, stdin);
458
+ const requestKey = this.buildRequestKey(cli, args, extraEnv, stdin, cwd);
453
459
  if (!forceRefresh && this.store) {
454
460
  try {
455
461
  const existing = this.store.findByRequestKey(requestKey);
package/dist/index.d.ts CHANGED
@@ -67,6 +67,36 @@ type GatewayLogger = typeof logger;
67
67
  */
68
68
  export declare const MAX_TURNS_SCHEMA: z.ZodNumber;
69
69
  export declare const MAX_PRICE_SCHEMA: z.ZodNumber;
70
+ /**
71
+ * Slice λ: shared worktree directive for all 10 `*_request` / `*_request_async`
72
+ * tools. `true` creates a fresh worktree under `<repoRoot>/.worktrees/<uuid>`
73
+ * branched from HEAD. `{ name?, ref? }` lets the caller supply a sanitized
74
+ * name and/or git ref (default ref: HEAD).
75
+ *
76
+ * Lifecycle is gateway-owned: the gateway pre-creates the worktree via
77
+ * `git worktree add`, then spawns the child CLI with `cwd: <worktree-path>`.
78
+ * No `-w` / `--worktree` flag is ever emitted to the underlying CLI. When
79
+ * the request carries a sessionId and the session already has a worktree,
80
+ * that worktree is reused. On session_delete or TTL eviction the gateway
81
+ * runs `git worktree remove --force`.
82
+ *
83
+ * Tool response: when a worktree was used, the successful response stdout
84
+ * is prefixed with `[gateway] worktree=<absolute-path>\n` so callers can
85
+ * parse/use the path without a schema change (slice λ §1.d).
86
+ *
87
+ * NOTE: callers should `.gitignore` the `.worktrees/` directory in their
88
+ * repo (the gateway does NOT auto-gitignore — see slice λ spec Q4).
89
+ */
90
+ export declare const WORKTREE_SCHEMA: z.ZodUnion<[z.ZodBoolean, z.ZodObject<{
91
+ name: z.ZodOptional<z.ZodString>;
92
+ ref: z.ZodOptional<z.ZodString>;
93
+ }, "strict", z.ZodTypeAny, {
94
+ name?: string | undefined;
95
+ ref?: string | undefined;
96
+ }, {
97
+ name?: string | undefined;
98
+ ref?: string | undefined;
99
+ }>]>;
70
100
  export declare const SESSION_PROVIDER_VALUES: readonly ["claude", "codex", "gemini", "grok", "mistral"];
71
101
  export declare const SESSION_PROVIDER_ENUM: z.ZodEnum<["claude", "codex", "gemini", "grok", "mistral"]>;
72
102
  export type SessionProvider = (typeof SESSION_PROVIDER_VALUES)[number];
@@ -97,6 +127,57 @@ export interface GatewayServerRuntime {
97
127
  export declare function resolveGatewayServerRuntime(deps?: GatewayServerDeps, options?: {
98
128
  isolateState?: boolean;
99
129
  }): GatewayServerRuntime;
130
+ /**
131
+ * Slice λ: shape returned by `resolveWorktreeForRequest`. `cwd` is what
132
+ * the spawn helpers (`executeCli`, `startJobWithDedup`) consume;
133
+ * `worktreePath` is what the tool handler embeds in the response prefix
134
+ * so callers can discover the path.
135
+ */
136
+ export interface ResolvedWorktree {
137
+ cwd?: string;
138
+ worktreePath?: string;
139
+ }
140
+ /**
141
+ * Slice λ: resolve a request's worktree directive into a spawn cwd.
142
+ *
143
+ * - `worktreeOpt` is the Zod-validated input value (boolean |
144
+ * `{ name?, ref? }` | undefined).
145
+ * - When the request has a session AND the session already has a
146
+ * `metadata.worktreePath`, that path is reused (resume semantics).
147
+ * The reused path is returned without touching git; if the directory
148
+ * was externally removed between requests, the next CLI invocation
149
+ * will surface the error naturally.
150
+ * - When no reusable worktree exists, `createWorktree` runs; on success
151
+ * the new path is written to `session.metadata` (only when a session
152
+ * exists — request-scoped worktrees do NOT persist).
153
+ * - Returns `{}` when `worktreeOpt` is undefined/false (preserves
154
+ * pre-λ behaviour at non-worktree call sites).
155
+ * - Errors propagate as `WorktreeError`/`Error`; the caller wraps them
156
+ * in a `createErrorResponse` envelope. Do NOT swallow.
157
+ *
158
+ * Spec: docs/plans/slice-lambda.spec.md §"Implementation surface to
159
+ * verify" §5.
160
+ */
161
+ export declare function resolveWorktreeForRequest(worktreeOpt: boolean | {
162
+ name?: string;
163
+ ref?: string;
164
+ } | undefined, sessionId: string | undefined, runtime: GatewayServerRuntime): Promise<ResolvedWorktree>;
165
+ /**
166
+ * Slice λ §1.d: response-envelope shape decision for `worktreePath`.
167
+ *
168
+ * We surface the worktree path inline as a stdout prefix
169
+ * (`[gateway] worktree=<absolute-path>\n`) rather than as a
170
+ * structuredContent field or JSON wrapper. Rationale:
171
+ * - zero schema change across all 10 tools and their downstream parsers
172
+ * - matches how other slice features (session warnings, cache_state
173
+ * aggregates) surface side-channel metadata today
174
+ * - callers that want the path can split on the first newline; callers
175
+ * that don't care see a single ignorable header line
176
+ *
177
+ * Use `formatWorktreePrefix(resolution.worktreePath)` once per tool, at
178
+ * the moment a successful response is constructed.
179
+ */
180
+ export declare function formatWorktreePrefix(worktreePath?: string): string;
100
181
  export declare function extractUsageAndCost(cli: "claude" | "codex" | "gemini" | "grok" | "mistral", output: string, outputFormat?: string,
101
182
  /**
102
183
  * Optional context for off-stdout telemetry sources. Today only Mistral
@@ -384,6 +465,11 @@ export interface GeminiRequestParams {
384
465
  attachments?: string[];
385
466
  /** Phase 4 slice γ: emit `--skip-trust` for fresh-workspace headless runs. */
386
467
  skipTrust?: boolean;
468
+ /** Slice λ: run this request inside a gateway-owned git worktree. */
469
+ worktree?: boolean | {
470
+ name?: string;
471
+ ref?: string;
472
+ };
387
473
  }
388
474
  export interface HandlerDeps {
389
475
  sessionManager: ISessionManager;
@@ -436,6 +522,11 @@ export interface GrokRequestParams {
436
522
  allow?: string[];
437
523
  /** Phase 4 slice θ: Grok `--deny <RULE>` (repeatable; one entry per --deny instance). */
438
524
  deny?: string[];
525
+ /** Slice λ: run this request inside a gateway-owned git worktree. */
526
+ worktree?: boolean | {
527
+ name?: string;
528
+ ref?: string;
529
+ };
439
530
  }
440
531
  export declare function handleGrokRequest(deps: HandlerDeps, params: GrokRequestParams): Promise<ExtendedToolResponse>;
441
532
  export declare function handleGrokRequestAsync(deps: AsyncHandlerDeps, params: Omit<GrokRequestParams, "optimizeResponse">): Promise<ExtendedToolResponse>;
@@ -470,6 +561,11 @@ export interface MistralRequestParams {
470
561
  workingDir?: string;
471
562
  /** Phase 4 slice ζ: Vibe `--add-dir <DIR>` repeatable add-dir parity. */
472
563
  addDir?: string[];
564
+ /** Slice λ: run this request inside a gateway-owned git worktree. */
565
+ worktree?: boolean | {
566
+ name?: string;
567
+ ref?: string;
568
+ };
473
569
  }
474
570
  export declare function handleMistralRequest(deps: HandlerDeps, params: MistralRequestParams): Promise<ExtendedToolResponse>;
475
571
  export declare function handleMistralRequestAsync(deps: AsyncHandlerDeps, params: Omit<MistralRequestParams, "optimizeResponse">): Promise<ExtendedToolResponse>;
@@ -504,6 +600,11 @@ export declare function handleCodexRequestAsync(deps: AsyncHandlerDeps, params:
504
600
  ignoreRules?: boolean;
505
601
  workingDir?: string;
506
602
  addDir?: string[];
603
+ /** Slice λ: run this request inside a gateway-owned git worktree. */
604
+ worktree?: boolean | {
605
+ name?: string;
606
+ ref?: string;
607
+ };
507
608
  }): Promise<ExtendedToolResponse>;
508
609
  export declare function createGatewayServer(deps?: GatewayServerDeps): McpServer;
509
610
  export {};