llm-cli-gateway 1.4.0 → 1.5.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/CHANGELOG.md +135 -1
  2. package/README.md +358 -15
  3. package/dist/approval-manager.d.ts +1 -1
  4. package/dist/async-job-manager.d.ts +32 -2
  5. package/dist/async-job-manager.js +101 -16
  6. package/dist/auth.d.ts +15 -0
  7. package/dist/auth.js +46 -0
  8. package/dist/cli-updater.d.ts +19 -2
  9. package/dist/cli-updater.js +110 -7
  10. package/dist/codex-json-parser.d.ts +34 -0
  11. package/dist/codex-json-parser.js +105 -0
  12. package/dist/config.d.ts +30 -0
  13. package/dist/config.js +167 -0
  14. package/dist/doctor.d.ts +110 -0
  15. package/dist/doctor.js +280 -0
  16. package/dist/endpoint-exposure.d.ts +22 -0
  17. package/dist/endpoint-exposure.js +231 -0
  18. package/dist/entrypoint-url.d.ts +1 -0
  19. package/dist/entrypoint-url.js +5 -0
  20. package/dist/executor.d.ts +9 -1
  21. package/dist/executor.js +52 -17
  22. package/dist/flight-recorder.d.ts +3 -1
  23. package/dist/flight-recorder.js +31 -2
  24. package/dist/gateway-server.d.ts +2 -0
  25. package/dist/gateway-server.js +1 -0
  26. package/dist/gemini-json-parser.d.ts +21 -0
  27. package/dist/gemini-json-parser.js +47 -0
  28. package/dist/health.d.ts +7 -0
  29. package/dist/health.js +22 -0
  30. package/dist/http-transport.d.ts +22 -0
  31. package/dist/http-transport.js +164 -0
  32. package/dist/index.d.ts +186 -2
  33. package/dist/index.js +2761 -1454
  34. package/dist/job-store.d.ts +118 -2
  35. package/dist/job-store.js +176 -5
  36. package/dist/logger.d.ts +9 -0
  37. package/dist/logger.js +14 -0
  38. package/dist/model-registry.js +40 -6
  39. package/dist/provider-login-guidance.d.ts +21 -0
  40. package/dist/provider-login-guidance.js +98 -0
  41. package/dist/provider-status.d.ts +41 -0
  42. package/dist/provider-status.js +203 -0
  43. package/dist/request-helpers.d.ts +484 -4
  44. package/dist/request-helpers.js +613 -0
  45. package/dist/resources.js +44 -0
  46. package/dist/session-manager-pg.js +1 -0
  47. package/dist/session-manager.d.ts +1 -1
  48. package/dist/session-manager.js +2 -1
  49. package/dist/upstream-contracts.d.ts +62 -0
  50. package/dist/upstream-contracts.js +620 -0
  51. package/dist/validation-normalizer.d.ts +23 -0
  52. package/dist/validation-normalizer.js +79 -0
  53. package/dist/validation-orchestrator.d.ts +47 -0
  54. package/dist/validation-orchestrator.js +145 -0
  55. package/dist/validation-prompts.d.ts +15 -0
  56. package/dist/validation-prompts.js +52 -0
  57. package/dist/validation-report.d.ts +57 -0
  58. package/dist/validation-report.js +129 -0
  59. package/dist/validation-tools.d.ts +7 -0
  60. package/dist/validation-tools.js +198 -0
  61. package/package.json +25 -10
  62. package/setup/status.schema.json +271 -0
@@ -1,6 +1,6 @@
1
1
  export interface FlightLogStart {
2
2
  correlationId: string;
3
- cli: "claude" | "codex" | "gemini" | "grok";
3
+ cli: "claude" | "codex" | "gemini" | "grok" | "mistral";
4
4
  model: string;
5
5
  prompt: string;
6
6
  system?: string;
@@ -11,6 +11,8 @@ export interface FlightLogResult {
11
11
  response: string;
12
12
  inputTokens?: number;
13
13
  outputTokens?: number;
14
+ cacheReadTokens?: number;
15
+ cacheCreationTokens?: number;
14
16
  durationMs: number;
15
17
  retryCount: number;
16
18
  circuitBreakerState: string;
@@ -3,6 +3,21 @@ import os from "os";
3
3
  import path from "path";
4
4
  import { createRequire } from "module";
5
5
  const MAX_THINKING_BYTES = 1_000_000;
6
+ /**
7
+ * Idempotent migration: add `cache_read_tokens` / `cache_creation_tokens`
8
+ * columns to the `requests` table if a pre-U23 logs.db is opened. Existing
9
+ * rows keep NULL for the new columns; that is intentional.
10
+ */
11
+ function ensureRequestsCacheColumns(db) {
12
+ const rows = db.prepare("PRAGMA table_info(requests)").all?.() ?? [];
13
+ const names = new Set(rows.map((row) => (row && typeof row.name === "string" ? row.name : "")));
14
+ if (!names.has("cache_read_tokens")) {
15
+ db.exec("ALTER TABLE requests ADD COLUMN cache_read_tokens INTEGER");
16
+ }
17
+ if (!names.has("cache_creation_tokens")) {
18
+ db.exec("ALTER TABLE requests ADD COLUMN cache_creation_tokens INTEGER");
19
+ }
20
+ }
6
21
  export function resolveFlightRecorderDbPath() {
7
22
  const configured = process.env.LLM_GATEWAY_LOGS_DB;
8
23
  if (configured !== undefined) {
@@ -80,7 +95,9 @@ export class FlightRecorder {
80
95
  duration_ms INTEGER,
81
96
  datetime_utc TEXT NOT NULL,
82
97
  input_tokens INTEGER,
83
- output_tokens INTEGER
98
+ output_tokens INTEGER,
99
+ cache_read_tokens INTEGER,
100
+ cache_creation_tokens INTEGER
84
101
  );
85
102
 
86
103
  CREATE TABLE IF NOT EXISTS gateway_metadata (
@@ -106,6 +123,14 @@ export class FlightRecorder {
106
123
  this.db
107
124
  .prepare("INSERT OR IGNORE INTO _migrations(version, applied_at) VALUES(1, ?)")
108
125
  .run(new Date().toISOString());
126
+ // Migration v2: cache_read_tokens / cache_creation_tokens columns on
127
+ // pre-U23 logs.db files. ALTER TABLE ADD COLUMN is idempotent only via
128
+ // a prior PRAGMA table_info() check; better-sqlite3 has no native
129
+ // "IF NOT EXISTS" for ADD COLUMN.
130
+ ensureRequestsCacheColumns(this.db);
131
+ this.db
132
+ .prepare("INSERT OR IGNORE INTO _migrations(version, applied_at) VALUES(2, ?)")
133
+ .run(new Date().toISOString());
109
134
  if (process.platform !== "win32") {
110
135
  try {
111
136
  chmodSync(dbPath, 0o600);
@@ -142,7 +167,9 @@ export class FlightRecorder {
142
167
  SET response = @response,
143
168
  duration_ms = @duration_ms,
144
169
  input_tokens = @input_tokens,
145
- output_tokens = @output_tokens
170
+ output_tokens = @output_tokens,
171
+ cache_read_tokens = @cache_read_tokens,
172
+ cache_creation_tokens = @cache_creation_tokens
146
173
  WHERE id = @id
147
174
  `);
148
175
  const updateMetadata = this.db.prepare(`
@@ -168,6 +195,8 @@ export class FlightRecorder {
168
195
  duration_ms: result.durationMs,
169
196
  input_tokens: result.inputTokens ?? null,
170
197
  output_tokens: result.outputTokens ?? null,
198
+ cache_read_tokens: result.cacheReadTokens ?? null,
199
+ cache_creation_tokens: result.cacheCreationTokens ?? null,
171
200
  });
172
201
  updateMetadata.run({
173
202
  id: correlationId,
@@ -0,0 +1,2 @@
1
+ export type { GatewayServerDeps } from "./index.js";
2
+ export { createGatewayServer } from "./index.js";
@@ -0,0 +1 @@
1
+ export { createGatewayServer } from "./index.js";
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Parser for Gemini CLI `-o json` output.
3
+ *
4
+ * Gemini emits a single JSON object with:
5
+ * - `response`: string final model output
6
+ * - `usageMetadata`: { promptTokenCount, candidatesTokenCount,
7
+ * cachedContentTokenCount?, totalTokenCount }
8
+ *
9
+ * Returns null when stdout is not parseable as JSON. Returns an object with
10
+ * only `response` when usageMetadata is missing.
11
+ */
12
+ export interface GeminiUsage {
13
+ input_tokens: number;
14
+ output_tokens: number;
15
+ cache_read_tokens?: number;
16
+ }
17
+ export interface GeminiJsonParseResult {
18
+ usage?: GeminiUsage;
19
+ response?: string;
20
+ }
21
+ export declare function parseGeminiJson(stdout: string): GeminiJsonParseResult | null;
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Parser for Gemini CLI `-o json` output.
3
+ *
4
+ * Gemini emits a single JSON object with:
5
+ * - `response`: string final model output
6
+ * - `usageMetadata`: { promptTokenCount, candidatesTokenCount,
7
+ * cachedContentTokenCount?, totalTokenCount }
8
+ *
9
+ * Returns null when stdout is not parseable as JSON. Returns an object with
10
+ * only `response` when usageMetadata is missing.
11
+ */
12
+ export function parseGeminiJson(stdout) {
13
+ const trimmed = stdout.trim();
14
+ if (!trimmed) {
15
+ return null;
16
+ }
17
+ let parsed;
18
+ try {
19
+ parsed = JSON.parse(trimmed);
20
+ }
21
+ catch {
22
+ return null;
23
+ }
24
+ if (!parsed || typeof parsed !== "object") {
25
+ return null;
26
+ }
27
+ const result = {};
28
+ if (typeof parsed.response === "string") {
29
+ result.response = parsed.response;
30
+ }
31
+ const meta = parsed.usageMetadata;
32
+ if (meta && typeof meta === "object") {
33
+ const input = typeof meta.promptTokenCount === "number" ? meta.promptTokenCount : undefined;
34
+ const output = typeof meta.candidatesTokenCount === "number" ? meta.candidatesTokenCount : undefined;
35
+ if (input !== undefined || output !== undefined) {
36
+ const usage = {
37
+ input_tokens: input ?? 0,
38
+ output_tokens: output ?? 0,
39
+ };
40
+ if (typeof meta.cachedContentTokenCount === "number") {
41
+ usage.cache_read_tokens = meta.cachedContentTokenCount;
42
+ }
43
+ result.usage = usage;
44
+ }
45
+ }
46
+ return result;
47
+ }
package/dist/health.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { DatabaseConnection } from "./db.js";
2
+ import { type ProviderRuntimeStatus } from "./provider-status.js";
2
3
  export interface HealthStatus {
3
4
  status: "healthy" | "degraded" | "unhealthy";
4
5
  postgres: {
@@ -11,6 +12,11 @@ export interface HealthStatus {
11
12
  };
12
13
  timestamp: string;
13
14
  }
15
+ export interface ProviderRuntimeHealth {
16
+ status: "healthy" | "degraded" | "unhealthy";
17
+ providers: Record<string, Pick<ProviderRuntimeStatus, "installed" | "version" | "loginStatus" | "loginCheck">>;
18
+ timestamp: string;
19
+ }
14
20
  /**
15
21
  * Check health status of PostgreSQL and Redis
16
22
  * - Both up → healthy
@@ -18,3 +24,4 @@ export interface HealthStatus {
18
24
  * - PostgreSQL down → unhealthy (critical failure)
19
25
  */
20
26
  export declare function checkHealth(db: DatabaseConnection): Promise<HealthStatus>;
27
+ export declare function checkProviderRuntimeHealth(): ProviderRuntimeHealth;
package/dist/health.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { listProviderRuntimeStatuses } from "./provider-status.js";
1
2
  /**
2
3
  * Check health status of PostgreSQL and Redis
3
4
  * - Both up → healthy
@@ -30,3 +31,24 @@ export async function checkHealth(db) {
30
31
  }
31
32
  return health;
32
33
  }
34
+ export function checkProviderRuntimeHealth() {
35
+ const providers = listProviderRuntimeStatuses();
36
+ const projected = Object.fromEntries(Object.entries(providers).map(([name, provider]) => [
37
+ name,
38
+ {
39
+ installed: provider.installed,
40
+ version: provider.version,
41
+ loginStatus: provider.loginStatus,
42
+ loginCheck: provider.loginCheck,
43
+ },
44
+ ]));
45
+ const statuses = Object.values(providers);
46
+ const installedCount = statuses.filter(provider => provider.installed).length;
47
+ const authenticatedCount = statuses.filter(provider => provider.loginStatus === "authenticated").length;
48
+ const status = installedCount === 0 ? "unhealthy" : authenticatedCount === 0 ? "degraded" : "healthy";
49
+ return {
50
+ status,
51
+ providers: projected,
52
+ timestamp: new Date().toISOString(),
53
+ };
54
+ }
@@ -0,0 +1,22 @@
1
+ import { type Server } from "node:http";
2
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
+ import type { GatewayServerDeps } from "./index.js";
4
+ export interface HttpTransportOptions {
5
+ host?: string;
6
+ port?: number;
7
+ path?: string;
8
+ deps?: GatewayServerDeps;
9
+ createGatewayServer: (deps?: GatewayServerDeps) => McpServer;
10
+ logger?: {
11
+ info: (...args: any[]) => void;
12
+ error: (...args: any[]) => void;
13
+ debug: (...args: any[]) => void;
14
+ };
15
+ }
16
+ export interface HttpGatewayHandle {
17
+ server: Server;
18
+ url: string;
19
+ close: () => Promise<void>;
20
+ sessionCount: () => number;
21
+ }
22
+ export declare function startHttpGateway(options: HttpTransportOptions): Promise<HttpGatewayHandle>;
@@ -0,0 +1,164 @@
1
+ import { createServer } from "node:http";
2
+ import { randomUUID } from "node:crypto";
3
+ import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
4
+ import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
5
+ import { authorizeBearerRequest, getRequiredBearerToken, writeAuthFailure } from "./auth.js";
6
+ const noopLogger = {
7
+ info: (..._args) => { },
8
+ error: (..._args) => { },
9
+ debug: (..._args) => { },
10
+ };
11
+ function readBody(req) {
12
+ return new Promise((resolve, reject) => {
13
+ const chunks = [];
14
+ req.on("data", chunk => chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)));
15
+ req.on("error", reject);
16
+ req.on("end", () => {
17
+ if (chunks.length === 0) {
18
+ resolve(undefined);
19
+ return;
20
+ }
21
+ const raw = Buffer.concat(chunks).toString("utf8");
22
+ try {
23
+ resolve(JSON.parse(raw));
24
+ }
25
+ catch (error) {
26
+ reject(error);
27
+ }
28
+ });
29
+ });
30
+ }
31
+ function methodNotAllowed(res) {
32
+ res.writeHead(405, { allow: "GET, POST, DELETE", "content-type": "application/json" });
33
+ res.end(JSON.stringify({ error: "Method not allowed" }));
34
+ }
35
+ function jsonError(res, status, message) {
36
+ res.writeHead(status, { "content-type": "application/json" });
37
+ res.end(JSON.stringify({ error: message }));
38
+ }
39
+ export async function startHttpGateway(options) {
40
+ const host = options.host ?? process.env.LLM_GATEWAY_HTTP_HOST ?? "127.0.0.1";
41
+ const port = options.port ?? Number(process.env.LLM_GATEWAY_HTTP_PORT ?? 3333);
42
+ const path = options.path ?? process.env.LLM_GATEWAY_HTTP_PATH ?? "/mcp";
43
+ const logger = options.logger ?? noopLogger;
44
+ const sessions = new Map();
45
+ const token = getRequiredBearerToken();
46
+ async function closeSession(sessionId) {
47
+ const entry = sessions.get(sessionId);
48
+ if (!entry)
49
+ return;
50
+ sessions.delete(sessionId);
51
+ await entry.transport
52
+ .close()
53
+ .catch(error => logger.error("HTTP transport close failed", error));
54
+ await entry.server.close().catch(error => logger.error("HTTP MCP server close failed", error));
55
+ }
56
+ async function createSession() {
57
+ const gatewayServer = options.createGatewayServer(options.deps);
58
+ const transport = new StreamableHTTPServerTransport({
59
+ sessionIdGenerator: () => randomUUID(),
60
+ onsessioninitialized: sessionId => {
61
+ sessions.set(sessionId, { server: gatewayServer, transport });
62
+ },
63
+ });
64
+ transport.onclose = () => {
65
+ if (transport.sessionId) {
66
+ sessions.delete(transport.sessionId);
67
+ }
68
+ };
69
+ transport.onerror = error => logger.error("HTTP MCP transport error", error);
70
+ await gatewayServer.connect(transport);
71
+ return { server: gatewayServer, transport };
72
+ }
73
+ const httpServer = createServer(async (req, res) => {
74
+ try {
75
+ const url = new URL(req.url || "/", `http://${req.headers.host || `${host}:${port}`}`);
76
+ if (url.pathname === "/healthz") {
77
+ res.writeHead(200, { "content-type": "application/json" });
78
+ res.end(JSON.stringify({ ok: true, sessions: sessions.size }));
79
+ return;
80
+ }
81
+ if (url.pathname !== path) {
82
+ jsonError(res, 404, "Not found");
83
+ return;
84
+ }
85
+ const auth = authorizeBearerRequest(req, token);
86
+ if (!auth.ok) {
87
+ writeAuthFailure(res, auth);
88
+ return;
89
+ }
90
+ if (req.method !== "GET" && req.method !== "POST" && req.method !== "DELETE") {
91
+ methodNotAllowed(res);
92
+ return;
93
+ }
94
+ const sessionId = req.headers["mcp-session-id"];
95
+ const normalizedSessionId = Array.isArray(sessionId) ? sessionId[0] : sessionId;
96
+ if (req.method === "DELETE") {
97
+ if (!normalizedSessionId) {
98
+ jsonError(res, 400, "Missing mcp-session-id");
99
+ return;
100
+ }
101
+ await closeSession(normalizedSessionId);
102
+ res.writeHead(204);
103
+ res.end();
104
+ return;
105
+ }
106
+ if (normalizedSessionId) {
107
+ const entry = sessions.get(normalizedSessionId);
108
+ if (!entry) {
109
+ jsonError(res, 404, "Unknown MCP session");
110
+ return;
111
+ }
112
+ const body = req.method === "POST" ? await readBody(req) : undefined;
113
+ await entry.transport.handleRequest(req, res, body);
114
+ return;
115
+ }
116
+ if (req.method !== "POST") {
117
+ if (req.method === "GET") {
118
+ methodNotAllowed(res);
119
+ return;
120
+ }
121
+ jsonError(res, 400, "Missing mcp-session-id");
122
+ return;
123
+ }
124
+ const body = await readBody(req);
125
+ if (!isInitializeRequest(body)) {
126
+ jsonError(res, 400, "First request must be initialize");
127
+ return;
128
+ }
129
+ const entry = await createSession();
130
+ await entry.transport.handleRequest(req, res, body);
131
+ }
132
+ catch (error) {
133
+ logger.error("HTTP transport request failed", error);
134
+ if (!res.headersSent) {
135
+ jsonError(res, 500, "Internal server error");
136
+ }
137
+ else {
138
+ res.end();
139
+ }
140
+ }
141
+ });
142
+ await new Promise((resolve, reject) => {
143
+ httpServer.once("error", reject);
144
+ httpServer.listen(port, host, () => {
145
+ httpServer.off("error", reject);
146
+ resolve();
147
+ });
148
+ });
149
+ const address = httpServer.address();
150
+ const actualPort = typeof address === "object" && address ? address.port : port;
151
+ const url = `http://${host}:${actualPort}${path}`;
152
+ logger.info(`HTTP MCP transport listening at ${url}`);
153
+ return {
154
+ server: httpServer,
155
+ url,
156
+ close: async () => {
157
+ await Promise.all([...sessions.keys()].map(closeSession));
158
+ await new Promise((resolve, reject) => {
159
+ httpServer.close(error => (error ? reject(error) : resolve()));
160
+ });
161
+ },
162
+ sessionCount: () => sessions.size,
163
+ };
164
+ }
package/dist/index.d.ts CHANGED
@@ -1,9 +1,17 @@
1
1
  #!/usr/bin/env node
2
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
+ import { z } from "zod";
2
4
  import { ISessionManager } from "./session-manager.js";
5
+ import { ResourceProvider } from "./resources.js";
6
+ import { PerformanceMetrics } from "./metrics.js";
7
+ import { type PersistenceConfig } from "./config.js";
8
+ import { DatabaseConnection } from "./db.js";
3
9
  import { AsyncJobManager } from "./async-job-manager.js";
4
- import { ApprovalRecord } from "./approval-manager.js";
10
+ import { ApprovalManager, ApprovalRecord } from "./approval-manager.js";
5
11
  import { ReviewIntegrityResult } from "./review-integrity.js";
6
- import { ClaudeMcpServerName } from "./claude-mcp-config.js";
12
+ import { ClaudeMcpConfigResult, ClaudeMcpServerName } from "./claude-mcp-config.js";
13
+ import { type MistralAgentMode, type ClaudePermissionMode, type CodexSandboxMode, type CodexAskForApproval, type ClaudeEffortLevel } from "./request-helpers.js";
14
+ import { FlightRecorderLike } from "./flight-recorder.js";
7
15
  type ExtendedToolResponse = {
8
16
  content: {
9
17
  type: "text";
@@ -21,6 +29,137 @@ type ExtendedToolResponse = {
21
29
  };
22
30
  reviewIntegrity?: ReviewIntegrityResult;
23
31
  };
32
+ declare const logger: {
33
+ info: (message: string, ...args: any[]) => void;
34
+ warn: (message: string, ...args: any[]) => void;
35
+ error: (message: string, ...args: any[]) => void;
36
+ debug: (message: string, ...args: any[]) => void;
37
+ };
38
+ type GatewayLogger = typeof logger;
39
+ export declare const SESSION_PROVIDER_VALUES: readonly ["claude", "codex", "gemini", "grok", "mistral"];
40
+ export declare const SESSION_PROVIDER_ENUM: z.ZodEnum<["claude", "codex", "gemini", "grok", "mistral"]>;
41
+ export type SessionProvider = (typeof SESSION_PROVIDER_VALUES)[number];
42
+ export interface GatewayServerDeps {
43
+ sessionManager?: ISessionManager;
44
+ resourceProvider?: ResourceProvider;
45
+ db?: DatabaseConnection | null;
46
+ performanceMetrics?: PerformanceMetrics;
47
+ asyncJobManager?: AsyncJobManager;
48
+ approvalManager?: ApprovalManager;
49
+ flightRecorder?: FlightRecorderLike;
50
+ logger?: GatewayLogger;
51
+ persistence?: PersistenceConfig;
52
+ }
53
+ interface GatewayServerRuntime {
54
+ sessionManager: ISessionManager;
55
+ resourceProvider: ResourceProvider;
56
+ db: DatabaseConnection | null;
57
+ performanceMetrics: PerformanceMetrics;
58
+ asyncJobManager: AsyncJobManager;
59
+ approvalManager: ApprovalManager;
60
+ flightRecorder: FlightRecorderLike;
61
+ logger: GatewayLogger;
62
+ persistence: PersistenceConfig;
63
+ }
64
+ interface CliRequestPrep {
65
+ corrId: string;
66
+ effectivePrompt: string;
67
+ resolvedModel: string | undefined;
68
+ requestedMcpServers: ClaudeMcpServerName[];
69
+ mcpConfig?: ClaudeMcpConfigResult;
70
+ approvalDecision: ApprovalRecord | null;
71
+ reviewIntegrity?: ReviewIntegrityResult;
72
+ args: string[];
73
+ }
74
+ export declare function prepareClaudeRequest(params: {
75
+ prompt: string;
76
+ model?: string;
77
+ outputFormat: "text" | "json" | "stream-json";
78
+ allowedTools?: string[];
79
+ disallowedTools?: string[];
80
+ dangerouslySkipPermissions: boolean;
81
+ permissionMode?: ClaudePermissionMode;
82
+ approvalStrategy: "legacy" | "mcp_managed";
83
+ approvalPolicy?: string;
84
+ mcpServers?: ClaudeMcpServerName[];
85
+ strictMcpConfig: boolean;
86
+ correlationId?: string;
87
+ optimizePrompt: boolean;
88
+ operation: string;
89
+ agent?: string;
90
+ agents?: Record<string, unknown>;
91
+ forkSession?: boolean;
92
+ systemPrompt?: string;
93
+ appendSystemPrompt?: string;
94
+ maxBudgetUsd?: number;
95
+ maxTurns?: number;
96
+ effort?: ClaudeEffortLevel;
97
+ excludeDynamicSystemPromptSections?: boolean;
98
+ }, runtime?: GatewayServerRuntime): CliRequestPrep | ExtendedToolResponse;
99
+ export interface CodexRequestPrep extends CliRequestPrep {
100
+ /**
101
+ * U26: Cleanup hook for any `outputSchema` temp file written during prep.
102
+ * Callers MUST invoke this in a `finally` block (regardless of whether the
103
+ * spawn succeeded, failed, or never ran) to avoid leaking the 0o600 temp
104
+ * file into `os.tmpdir()`.
105
+ */
106
+ cleanup?: () => void;
107
+ }
108
+ export declare function prepareCodexRequest(params: {
109
+ prompt: string;
110
+ model?: string;
111
+ fullAuto: boolean;
112
+ sandboxMode?: CodexSandboxMode;
113
+ askForApproval?: CodexAskForApproval;
114
+ useLegacyFullAutoFlag?: boolean;
115
+ dangerouslyBypassApprovalsAndSandbox: boolean;
116
+ approvalStrategy: "legacy" | "mcp_managed";
117
+ approvalPolicy?: string;
118
+ mcpServers?: ClaudeMcpServerName[];
119
+ sessionId?: string;
120
+ resumeLatest?: boolean;
121
+ createNewSession?: boolean;
122
+ correlationId?: string;
123
+ optimizePrompt: boolean;
124
+ operation: string;
125
+ /**
126
+ * U23: output format. When set to "json", emits `--json` so Codex streams
127
+ * the JSONL event format that `parseCodexJsonStream` (and downstream
128
+ * `extractUsageAndCost`) can consume. Defaults to "text".
129
+ */
130
+ outputFormat?: "text" | "json";
131
+ outputSchema?: string | Record<string, unknown>;
132
+ search?: boolean;
133
+ profile?: string;
134
+ configOverrides?: Record<string, string>;
135
+ ephemeral?: boolean;
136
+ images?: string[];
137
+ ignoreUserConfig?: boolean;
138
+ ignoreRules?: boolean;
139
+ }, runtime?: GatewayServerRuntime): CodexRequestPrep | ExtendedToolResponse;
140
+ export declare function prepareGeminiRequest(params: {
141
+ prompt: string;
142
+ model?: string;
143
+ approvalMode?: string;
144
+ approvalStrategy: "legacy" | "mcp_managed";
145
+ approvalPolicy?: string;
146
+ allowedTools?: string[];
147
+ includeDirs?: string[];
148
+ mcpServers?: ClaudeMcpServerName[];
149
+ correlationId?: string;
150
+ optimizePrompt: boolean;
151
+ operation: string;
152
+ /**
153
+ * U23: output format. When set to "json", emits `-o json` so Gemini emits
154
+ * the JSON object containing usageMetadata that `parseGeminiJson` (and
155
+ * downstream `extractUsageAndCost`) can consume. Defaults to "text".
156
+ */
157
+ outputFormat?: "text" | "json";
158
+ sandbox?: boolean;
159
+ policyFiles?: string[];
160
+ adminPolicyFiles?: string[];
161
+ attachments?: string[];
162
+ }, runtime?: GatewayServerRuntime): CliRequestPrep | ExtendedToolResponse;
24
163
  export interface GeminiRequestParams {
25
164
  prompt: string;
26
165
  model?: string;
@@ -38,14 +177,22 @@ export interface GeminiRequestParams {
38
177
  optimizeResponse?: boolean;
39
178
  idleTimeoutMs?: number;
40
179
  forceRefresh?: boolean;
180
+ /** U23: "json" emits `-o json` so token usage is parsed and reported. */
181
+ outputFormat?: "text" | "json";
182
+ sandbox?: boolean;
183
+ policyFiles?: string[];
184
+ adminPolicyFiles?: string[];
185
+ attachments?: string[];
41
186
  }
42
187
  export interface HandlerDeps {
43
188
  sessionManager: ISessionManager;
44
189
  logger: {
45
190
  info: (...args: any[]) => void;
191
+ warn?: (...args: any[]) => void;
46
192
  error: (...args: any[]) => void;
47
193
  debug: (...args: any[]) => void;
48
194
  };
195
+ runtime?: GatewayServerRuntime;
49
196
  }
50
197
  export interface AsyncHandlerDeps extends HandlerDeps {
51
198
  asyncJobManager: AsyncJobManager;
@@ -76,10 +223,36 @@ export interface GrokRequestParams {
76
223
  }
77
224
  export declare function handleGrokRequest(deps: HandlerDeps, params: GrokRequestParams): Promise<ExtendedToolResponse>;
78
225
  export declare function handleGrokRequestAsync(deps: AsyncHandlerDeps, params: Omit<GrokRequestParams, "optimizeResponse">): Promise<ExtendedToolResponse>;
226
+ export interface MistralRequestParams {
227
+ prompt: string;
228
+ model?: string;
229
+ outputFormat?: string;
230
+ sessionId?: string;
231
+ resumeLatest: boolean;
232
+ createNewSession: boolean;
233
+ permissionMode?: MistralAgentMode;
234
+ effort?: string;
235
+ reasoningEffort?: string;
236
+ approvalStrategy: "legacy" | "mcp_managed";
237
+ approvalPolicy?: string;
238
+ mcpServers?: ClaudeMcpServerName[];
239
+ allowedTools?: string[];
240
+ disallowedTools?: string[];
241
+ correlationId?: string;
242
+ optimizePrompt: boolean;
243
+ optimizeResponse?: boolean;
244
+ idleTimeoutMs?: number;
245
+ forceRefresh?: boolean;
246
+ }
247
+ export declare function handleMistralRequest(deps: HandlerDeps, params: MistralRequestParams): Promise<ExtendedToolResponse>;
248
+ export declare function handleMistralRequestAsync(deps: AsyncHandlerDeps, params: Omit<MistralRequestParams, "optimizeResponse">): Promise<ExtendedToolResponse>;
79
249
  export declare function handleCodexRequestAsync(deps: AsyncHandlerDeps, params: {
80
250
  prompt: string;
81
251
  model?: string;
82
252
  fullAuto: boolean;
253
+ sandboxMode?: CodexSandboxMode;
254
+ askForApproval?: CodexAskForApproval;
255
+ useLegacyFullAutoFlag?: boolean;
83
256
  dangerouslyBypassApprovalsAndSandbox: boolean;
84
257
  approvalStrategy: "legacy" | "mcp_managed";
85
258
  approvalPolicy?: string;
@@ -91,5 +264,16 @@ export declare function handleCodexRequestAsync(deps: AsyncHandlerDeps, params:
91
264
  optimizePrompt: boolean;
92
265
  idleTimeoutMs?: number;
93
266
  forceRefresh?: boolean;
267
+ /** U23: when "json", emits Codex `--json` so the parser is reachable. */
268
+ outputFormat?: "text" | "json";
269
+ outputSchema?: string | Record<string, unknown>;
270
+ search?: boolean;
271
+ profile?: string;
272
+ configOverrides?: Record<string, string>;
273
+ ephemeral?: boolean;
274
+ images?: string[];
275
+ ignoreUserConfig?: boolean;
276
+ ignoreRules?: boolean;
94
277
  }): Promise<ExtendedToolResponse>;
278
+ export declare function createGatewayServer(deps?: GatewayServerDeps): McpServer;
95
279
  export {};