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.
@@ -73,7 +73,46 @@ export interface OrphanedJobSnapshot {
73
73
  transport: JobTransport;
74
74
  httpStatus: number | null;
75
75
  }
76
- export declare class SqliteJobStore implements JobStore {
76
+ export interface ValidationRunLink {
77
+ provider: string;
78
+ jobId: string;
79
+ correlationId: string;
80
+ }
81
+ export interface ValidationRunRecord {
82
+ validationId: string;
83
+ ownerPrincipal: string;
84
+ intent: string;
85
+ createdAt: string;
86
+ requestJson: string;
87
+ providerLinks: ValidationRunLink[];
88
+ judgeLink: ValidationRunLink | null;
89
+ status: "running" | "finalized";
90
+ }
91
+ export interface ValidationReceiptRecord {
92
+ validationId: string;
93
+ ownerPrincipal: string;
94
+ mintedAt: string;
95
+ schemaVersion: string;
96
+ reportJson: string;
97
+ canonicalSha256: string;
98
+ prevSha256: string | null;
99
+ seq: number | null;
100
+ signature: string | null;
101
+ models: string[];
102
+ hasMaterialDisagreement: boolean;
103
+ confidence: string;
104
+ }
105
+ export interface ValidationRunStore {
106
+ recordValidationRun(run: ValidationRunRecord): void;
107
+ getValidationRun(validationId: string): ValidationRunRecord | null;
108
+ setValidationJudgeLink(validationId: string, judgeLink: ValidationRunLink): void;
109
+ setValidationRunStatus(validationId: string, status: ValidationRunRecord["status"]): void;
110
+ getValidationRunIdByJobId(jobId: string): string | null;
111
+ recordValidationReceipt(receipt: ValidationReceiptRecord): void;
112
+ getValidationReceipt(validationId: string): ValidationReceiptRecord | null;
113
+ }
114
+ export declare function isValidationRunStore(store: unknown): store is ValidationRunStore;
115
+ export declare class SqliteJobStore implements JobStore, ValidationRunStore {
77
116
  private logger;
78
117
  private db;
79
118
  private retentionMs;
@@ -122,6 +161,14 @@ export declare class SqliteJobStore implements JobStore {
122
161
  orphaned: Array<OrphanedJobSnapshot>;
123
162
  };
124
163
  evictExpired(): number;
164
+ recordValidationRun(run: ValidationRunRecord): void;
165
+ private linkRunJob;
166
+ getValidationRunIdByJobId(jobId: string): string | null;
167
+ getValidationRun(validationId: string): ValidationRunRecord | null;
168
+ setValidationJudgeLink(validationId: string, judgeLink: ValidationRunLink): void;
169
+ setValidationRunStatus(validationId: string, status: ValidationRunRecord["status"]): void;
170
+ recordValidationReceipt(receipt: ValidationReceiptRecord): void;
171
+ getValidationReceipt(validationId: string): ValidationReceiptRecord | null;
125
172
  close(): void;
126
173
  }
127
174
  export declare const JobStoreClass: typeof SqliteJobStore;
package/dist/job-store.js CHANGED
@@ -83,6 +83,13 @@ function ensureJobsTransportColumns(db) {
83
83
  db.exec("ALTER TABLE jobs ADD COLUMN payload_json TEXT");
84
84
  }
85
85
  }
86
+ export function isValidationRunStore(store) {
87
+ return (typeof store === "object" &&
88
+ store !== null &&
89
+ typeof store.recordValidationRun === "function" &&
90
+ typeof store.getValidationRun === "function" &&
91
+ typeof store.recordValidationReceipt === "function");
92
+ }
86
93
  export class SqliteJobStore {
87
94
  logger;
88
95
  db;
@@ -128,6 +135,42 @@ export class SqliteJobStore {
128
135
  CREATE INDEX IF NOT EXISTS idx_jobs_status ON jobs(status);
129
136
  CREATE INDEX IF NOT EXISTS idx_jobs_expires_at ON jobs(expires_at);
130
137
  CREATE INDEX IF NOT EXISTS idx_jobs_request_key_finished ON jobs(request_key, finished_at);
138
+ `);
139
+ this.db.exec(`
140
+ CREATE TABLE IF NOT EXISTS validation_runs (
141
+ validation_id TEXT PRIMARY KEY,
142
+ owner_principal TEXT NOT NULL,
143
+ intent TEXT NOT NULL,
144
+ created_at TEXT NOT NULL,
145
+ request_json TEXT NOT NULL,
146
+ provider_links TEXT NOT NULL,
147
+ judge_link TEXT,
148
+ status TEXT NOT NULL
149
+ );
150
+ CREATE INDEX IF NOT EXISTS idx_validation_runs_owner ON validation_runs(owner_principal);
151
+ `);
152
+ this.db.exec(`
153
+ CREATE TABLE IF NOT EXISTS validation_run_jobs (
154
+ job_id TEXT PRIMARY KEY,
155
+ validation_id TEXT NOT NULL,
156
+ role TEXT NOT NULL
157
+ );
158
+ CREATE INDEX IF NOT EXISTS idx_validation_run_jobs_run ON validation_run_jobs(validation_id);
159
+ CREATE TABLE IF NOT EXISTS validation_receipts (
160
+ validation_id TEXT PRIMARY KEY,
161
+ owner_principal TEXT NOT NULL,
162
+ minted_at TEXT NOT NULL,
163
+ schema_version TEXT NOT NULL,
164
+ report_json TEXT NOT NULL,
165
+ canonical_sha256 TEXT NOT NULL,
166
+ prev_sha256 TEXT,
167
+ seq INTEGER,
168
+ signature TEXT,
169
+ models TEXT NOT NULL,
170
+ has_material_disagreement INTEGER NOT NULL,
171
+ confidence TEXT NOT NULL
172
+ );
173
+ CREATE INDEX IF NOT EXISTS idx_validation_receipts_owner ON validation_receipts(owner_principal);
131
174
  `);
132
175
  ensureJobsOwnerColumn(this.db);
133
176
  ensureJobsTransportColumns(this.db);
@@ -262,6 +305,86 @@ export class SqliteJobStore {
262
305
  const result = this.deleteExpiredStmt.run(now);
263
306
  return Number(result.changes);
264
307
  }
308
+ recordValidationRun(run) {
309
+ this.db
310
+ .prepare(`INSERT OR IGNORE INTO validation_runs
311
+ (validation_id, owner_principal, intent, created_at, request_json,
312
+ provider_links, judge_link, status)
313
+ VALUES (@validation_id, @owner_principal, @intent, @created_at, @request_json,
314
+ @provider_links, @judge_link, @status)`)
315
+ .run({
316
+ validation_id: run.validationId,
317
+ owner_principal: run.ownerPrincipal,
318
+ intent: run.intent,
319
+ created_at: run.createdAt,
320
+ request_json: run.requestJson,
321
+ provider_links: JSON.stringify(run.providerLinks),
322
+ judge_link: run.judgeLink ? JSON.stringify(run.judgeLink) : null,
323
+ status: run.status,
324
+ });
325
+ for (const link of run.providerLinks) {
326
+ this.linkRunJob(run.validationId, link.jobId, "provider");
327
+ }
328
+ }
329
+ linkRunJob(validationId, jobId, role) {
330
+ this.db
331
+ .prepare(`INSERT OR IGNORE INTO validation_run_jobs (job_id, validation_id, role)
332
+ VALUES (?, ?, ?)`)
333
+ .run(jobId, validationId, role);
334
+ }
335
+ getValidationRunIdByJobId(jobId) {
336
+ const row = this.db
337
+ .prepare(`SELECT validation_id FROM validation_run_jobs WHERE job_id = ?`)
338
+ .get(jobId);
339
+ return row?.validation_id ?? null;
340
+ }
341
+ getValidationRun(validationId) {
342
+ const row = this.db
343
+ .prepare(`SELECT * FROM validation_runs WHERE validation_id = ?`)
344
+ .get(validationId);
345
+ return row ? rowToValidationRunRecord(row) : null;
346
+ }
347
+ setValidationJudgeLink(validationId, judgeLink) {
348
+ this.db
349
+ .prepare(`UPDATE validation_runs SET judge_link = ? WHERE validation_id = ?`)
350
+ .run(JSON.stringify(judgeLink), validationId);
351
+ this.linkRunJob(validationId, judgeLink.jobId, "judge");
352
+ }
353
+ setValidationRunStatus(validationId, status) {
354
+ this.db
355
+ .prepare(`UPDATE validation_runs SET status = ? WHERE validation_id = ?`)
356
+ .run(status, validationId);
357
+ }
358
+ recordValidationReceipt(receipt) {
359
+ this.db
360
+ .prepare(`INSERT OR IGNORE INTO validation_receipts
361
+ (validation_id, owner_principal, minted_at, schema_version, report_json,
362
+ canonical_sha256, prev_sha256, seq, signature, models,
363
+ has_material_disagreement, confidence)
364
+ VALUES (@validation_id, @owner_principal, @minted_at, @schema_version, @report_json,
365
+ @canonical_sha256, @prev_sha256, @seq, @signature, @models,
366
+ @has_material_disagreement, @confidence)`)
367
+ .run({
368
+ validation_id: receipt.validationId,
369
+ owner_principal: receipt.ownerPrincipal,
370
+ minted_at: receipt.mintedAt,
371
+ schema_version: receipt.schemaVersion,
372
+ report_json: receipt.reportJson,
373
+ canonical_sha256: receipt.canonicalSha256,
374
+ prev_sha256: receipt.prevSha256,
375
+ seq: receipt.seq,
376
+ signature: receipt.signature,
377
+ models: JSON.stringify(receipt.models),
378
+ has_material_disagreement: receipt.hasMaterialDisagreement ? 1 : 0,
379
+ confidence: receipt.confidence,
380
+ });
381
+ }
382
+ getValidationReceipt(validationId) {
383
+ const row = this.db
384
+ .prepare(`SELECT * FROM validation_receipts WHERE validation_id = ?`)
385
+ .get(validationId);
386
+ return row ? rowToValidationReceiptRecord(row) : null;
387
+ }
265
388
  close() {
266
389
  try {
267
390
  this.db.close();
@@ -271,6 +394,67 @@ export class SqliteJobStore {
271
394
  }
272
395
  }
273
396
  }
397
+ function rowToValidationRunRecord(row) {
398
+ return {
399
+ validationId: row.validation_id,
400
+ ownerPrincipal: row.owner_principal,
401
+ intent: row.intent,
402
+ createdAt: row.created_at,
403
+ requestJson: row.request_json,
404
+ providerLinks: parseLinks(row.provider_links) ?? [],
405
+ judgeLink: parseLink(row.judge_link),
406
+ status: row.status,
407
+ };
408
+ }
409
+ function rowToValidationReceiptRecord(row) {
410
+ return {
411
+ validationId: row.validation_id,
412
+ ownerPrincipal: row.owner_principal,
413
+ mintedAt: row.minted_at,
414
+ schemaVersion: row.schema_version,
415
+ reportJson: row.report_json,
416
+ canonicalSha256: row.canonical_sha256,
417
+ prevSha256: row.prev_sha256 ?? null,
418
+ seq: row.seq ?? null,
419
+ signature: row.signature ?? null,
420
+ models: parseStringArray(row.models),
421
+ hasMaterialDisagreement: Boolean(row.has_material_disagreement),
422
+ confidence: row.confidence,
423
+ };
424
+ }
425
+ function parseLinks(value) {
426
+ if (typeof value !== "string" || value.length === 0)
427
+ return null;
428
+ try {
429
+ const parsed = JSON.parse(value);
430
+ return Array.isArray(parsed) ? parsed : null;
431
+ }
432
+ catch {
433
+ return null;
434
+ }
435
+ }
436
+ function parseStringArray(value) {
437
+ if (typeof value !== "string" || value.length === 0)
438
+ return [];
439
+ try {
440
+ const parsed = JSON.parse(value);
441
+ return Array.isArray(parsed) ? parsed : [];
442
+ }
443
+ catch {
444
+ return [];
445
+ }
446
+ }
447
+ function parseLink(value) {
448
+ if (typeof value !== "string" || value.length === 0)
449
+ return null;
450
+ try {
451
+ const parsed = JSON.parse(value);
452
+ return parsed && typeof parsed === "object" ? parsed : null;
453
+ }
454
+ catch {
455
+ return null;
456
+ }
457
+ }
274
458
  export const JobStoreClass = SqliteJobStore;
275
459
  export class MemoryJobStore {
276
460
  rows = new Map();
@@ -329,6 +329,9 @@ export const UNGENERATED_GROK_FLAGS = [
329
329
  "--agents",
330
330
  "--prompt-json",
331
331
  "--worktree",
332
+ "--worktree-ref",
333
+ "--fork-session",
334
+ "--json-schema",
332
335
  "-p",
333
336
  "--resume",
334
337
  "--continue",
@@ -49,7 +49,7 @@ export const ACP_CONTRACT = {
49
49
  },
50
50
  gemini: {
51
51
  classification: "absent_watchlist",
52
- summary: "Google Antigravity agy 1.0.10 has no ACP surface; watchlist item only.",
52
+ summary: "Google Antigravity agy 1.0.13 has no ACP surface; watchlist item only.",
53
53
  },
54
54
  grok_api: {
55
55
  classification: "absent_watchlist",
@@ -80,7 +80,7 @@ const ACP_CAPABILITIES = {
80
80
  grok: {
81
81
  status: "native_smoke_passed",
82
82
  mediation: "native",
83
- targetVersion: "grok 0.2.60 (474c2bbfc)",
83
+ targetVersion: "grok 0.2.73 (9ff14c43bb)",
84
84
  entrypoint: { command: "grok", args: ["agent", "stdio"] },
85
85
  runtimeEnabled: false,
86
86
  smokeSupported: true,
@@ -95,7 +95,7 @@ const ACP_CAPABILITIES = {
95
95
  codex: {
96
96
  status: "adapter_mediated_deferred",
97
97
  mediation: "adapter_mediated",
98
- targetVersion: "codex-cli 0.141.0",
98
+ targetVersion: "codex-cli 0.142.4",
99
99
  entrypoint: null,
100
100
  runtimeEnabled: false,
101
101
  smokeSupported: false,
@@ -109,7 +109,7 @@ const ACP_CAPABILITIES = {
109
109
  claude: {
110
110
  status: "adapter_mediated_deferred",
111
111
  mediation: "adapter_mediated",
112
- targetVersion: "claude 2.1.185",
112
+ targetVersion: "claude 2.1.195",
113
113
  entrypoint: null,
114
114
  runtimeEnabled: false,
115
115
  smokeSupported: false,
@@ -123,13 +123,13 @@ const ACP_CAPABILITIES = {
123
123
  gemini: {
124
124
  status: "absent_watchlist",
125
125
  mediation: "none",
126
- targetVersion: "agy 1.0.10",
126
+ targetVersion: "agy 1.0.13",
127
127
  entrypoint: null,
128
128
  runtimeEnabled: false,
129
129
  smokeSupported: false,
130
130
  smokeStatus: "unsupported",
131
131
  caveats: [
132
- "Antigravity agy 1.0.10 has no ACP flag or subcommand.",
132
+ "Antigravity agy 1.0.13 has no ACP flag or subcommand.",
133
133
  "Legacy Gemini CLI ACP evidence does not transfer to agy; kept on the upstream drift watchlist.",
134
134
  ],
135
135
  docs: ACP_DOCS_REFERENCE,
@@ -148,7 +148,7 @@ const ACP_CAPABILITIES = {
148
148
  devin: {
149
149
  status: "native_smoke_passed",
150
150
  mediation: "native",
151
- targetVersion: "devin 2026.7.23 (3bd47f77)",
151
+ targetVersion: "devin 2026.8.18 (16737566)",
152
152
  entrypoint: { command: "devin", args: ["acp"] },
153
153
  runtimeEnabled: false,
154
154
  smokeSupported: true,
@@ -62,6 +62,7 @@ export interface CliSubcommandContract {
62
62
  tokenCost: CliSubcommandTokenCost;
63
63
  summary: string;
64
64
  conformanceFixtures: readonly CliContractFixture[];
65
+ helpProbeExitTolerant?: boolean;
65
66
  }
66
67
  export interface ContractViolation {
67
68
  cli: CliType;
@@ -19,9 +19,9 @@ export const ACP_ENTRYPOINT_CONTRACTS = {
19
19
  status: "native",
20
20
  executable: "grok",
21
21
  entrypointArgs: ["agent", "stdio"],
22
- targetVersion: "grok 0.2.60 (474c2bbfc)",
22
+ targetVersion: "grok 0.2.73 (9ff14c43bb)",
23
23
  probeArgs: [["agent", "stdio", "--help"]],
24
- evidence: "Native ACP via `grok agent stdio`; initialize + session/new smoke passed with isolated leader socket. Second runtime pilot. Bumped for 0.2.60.",
24
+ evidence: "Native ACP via `grok agent stdio`; initialize + session/new smoke passed with isolated leader socket. Second runtime pilot. Entrypoint re-probed clean at 0.2.73.",
25
25
  docsRef: "docs/plans/first-class-acp-gateway-extension.dag.toml#provider_matrix.grok",
26
26
  },
27
27
  codex: {
@@ -30,10 +30,10 @@ export const ACP_ENTRYPOINT_CONTRACTS = {
30
30
  status: "adapter_mediated_deferred",
31
31
  executable: "codex",
32
32
  entrypointArgs: [],
33
- targetVersion: "codex-cli 0.141.0",
33
+ targetVersion: "codex-cli 0.142.4",
34
34
  probeArgs: [],
35
35
  adapterCandidates: ["zed-industries/codex-acp", "agentclientprotocol/codex-acp"],
36
- evidence: "No native ACP entrypoint at codex-cli 0.141.0. Adapter evidence tracked as documentation only; not native gateway ACP support.",
36
+ evidence: "No native ACP entrypoint at codex-cli 0.142.4. Adapter evidence tracked as documentation only; not native gateway ACP support.",
37
37
  docsRef: "docs/plans/first-class-acp-gateway-extension.dag.toml#provider_matrix.codex",
38
38
  },
39
39
  claude: {
@@ -42,10 +42,10 @@ export const ACP_ENTRYPOINT_CONTRACTS = {
42
42
  status: "adapter_mediated_deferred",
43
43
  executable: "claude",
44
44
  entrypointArgs: [],
45
- targetVersion: "claude 2.1.185",
45
+ targetVersion: "claude 2.1.195",
46
46
  probeArgs: [],
47
47
  adapterCandidates: ["Claude Agent SDK ACP adapter"],
48
- evidence: "No native Claude Code CLI ACP entrypoint at claude 2.1.185. Adapter ownership/permission bridging unresolved; deferred.",
48
+ evidence: "No native Claude Code CLI ACP entrypoint at claude 2.1.195. Adapter ownership/permission bridging unresolved; deferred.",
49
49
  docsRef: "docs/plans/first-class-acp-gateway-extension.dag.toml#provider_matrix.claude",
50
50
  },
51
51
  gemini: {
@@ -54,9 +54,9 @@ export const ACP_ENTRYPOINT_CONTRACTS = {
54
54
  status: "absent_watchlist",
55
55
  executable: "agy",
56
56
  entrypointArgs: [],
57
- targetVersion: "agy 1.0.10",
57
+ targetVersion: "agy 1.0.13",
58
58
  probeArgs: [],
59
- evidence: "agy 1.0.10 has no ACP flag or subcommand. Legacy Gemini CLI ACP evidence does not transfer. Watchlist item.",
59
+ evidence: "agy 1.0.13 has no ACP flag or subcommand. Legacy Gemini CLI ACP evidence does not transfer. Watchlist item.",
60
60
  docsRef: "docs/plans/first-class-acp-gateway-extension.dag.toml#provider_matrix.gemini",
61
61
  },
62
62
  devin: {
@@ -65,7 +65,7 @@ export const ACP_ENTRYPOINT_CONTRACTS = {
65
65
  status: "native",
66
66
  executable: "devin",
67
67
  entrypointArgs: ["acp"],
68
- targetVersion: "devin 2026.7.23 (3bd47f77)",
68
+ targetVersion: "devin 2026.8.18 (16737566)",
69
69
  probeArgs: [["--version"]],
70
70
  evidence: 'Native ACP entrypoint `devin acp` (stdio JSON-RPC). Slice D1 manual initialize + session/new smoke passed (protocolVersion 1, agent "Affogato", session created). Third native runtime pilot; routing stays config-gated.',
71
71
  docsRef: "docs/plans/first-class-acp-gateway-extension.dag.toml#provider_matrix.devin",
@@ -107,6 +107,7 @@ function subcommand(commandPath, summary, risk, flags = [], options = {}) {
107
107
  summary,
108
108
  conformanceFixtures: options.fixtures ?? [],
109
109
  acknowledgedUpstreamFlags: options.acknowledgedUpstreamFlags ?? [],
110
+ ...(options.helpProbeExitTolerant ? { helpProbeExitTolerant: true } : {}),
110
111
  };
111
112
  }
112
113
  function acknowledgeSubcommandFlags(subcommands, flags) {
@@ -307,8 +308,10 @@ export const UPSTREAM_CLI_CONTRACTS = {
307
308
  "--allow-dangerously-skip-permissions",
308
309
  "--allowed",
309
310
  "--ax-screen-reader",
311
+ "--background",
310
312
  "--bare",
311
313
  "--betas",
314
+ "--bg",
312
315
  "--brief",
313
316
  "--chrome",
314
317
  "--dangerously-skip-permissions",
@@ -320,7 +323,6 @@ export const UPSTREAM_CLI_CONTRACTS = {
320
323
  "--from-pr",
321
324
  "--ide",
322
325
  "--include-hook-events",
323
- "--mcp-debug",
324
326
  "--name",
325
327
  "--no-chrome",
326
328
  "--plugin-dir",
@@ -419,6 +421,12 @@ export const UPSTREAM_CLI_CONTRACTS = {
419
421
  ],
420
422
  expect: "pass",
421
423
  },
424
+ {
425
+ id: "claude-background-acknowledged-not-emitted",
426
+ description: "Claude 2.1.195 advertises --bg/--background (background agent), but the gateway acknowledges them without emitting; caller argv is rejected",
427
+ args: ["-p", "hello", "--background"],
428
+ expect: "fail",
429
+ },
422
430
  ],
423
431
  },
424
432
  codex: {
@@ -899,7 +907,7 @@ export const UPSTREAM_CLI_CONTRACTS = {
899
907
  plugins: subcommand(["plugins"], "Alias for Antigravity plugin management.", "writes_local_config", [], {
900
908
  aliases: ["plugin"],
901
909
  }),
902
- update: subcommand(["update"], "Update Antigravity CLI to the current release.", "writes_local_config", [], { tokenCost: "small" }),
910
+ update: subcommand(["update"], "Update Antigravity CLI to the current release.", "writes_local_config", [], { tokenCost: "small", helpProbeExitTolerant: true }),
903
911
  },
904
912
  maxPositionals: 1,
905
913
  mcpTools: ["gemini_request", "gemini_request_async"],
@@ -921,6 +929,8 @@ export const UPSTREAM_CLI_CONTRACTS = {
921
929
  "attachments",
922
930
  "skipTrust",
923
931
  "yolo",
932
+ "project",
933
+ "newProject",
924
934
  ],
925
935
  flags: {
926
936
  "--print": { arity: "none", description: "Run a single prompt non-interactively" },
@@ -936,14 +946,13 @@ export const UPSTREAM_CLI_CONTRACTS = {
936
946
  "--conversation": { arity: "one", description: "Resume a previous conversation by ID" },
937
947
  "--continue": { arity: "none", description: "Continue the most recent conversation" },
938
948
  "-c": { arity: "none", description: "Short alias for --continue" },
949
+ "--project": { arity: "one", description: "Antigravity project ID for this session" },
950
+ "--new-project": {
951
+ arity: "none",
952
+ description: "Create a new Antigravity project for this session",
953
+ },
939
954
  },
940
- acknowledgedUpstreamFlags: [
941
- "--prompt-interactive",
942
- "-i",
943
- "--print-timeout",
944
- "--log-file",
945
- "--version",
946
- ],
955
+ acknowledgedUpstreamFlags: ["--log-file", "--print-timeout", "--prompt-interactive"],
947
956
  env: {},
948
957
  conformanceFixtures: [
949
958
  {
@@ -982,6 +991,18 @@ export const UPSTREAM_CLI_CONTRACTS = {
982
991
  args: ["--print", "hello", "-o", "json"],
983
992
  expect: "fail",
984
993
  },
994
+ {
995
+ id: "gemini-project-wired",
996
+ description: "Antigravity 1.0.13: --project <ID> is wired",
997
+ args: ["--print", "hello", "--project", "proj-123"],
998
+ expect: "pass",
999
+ },
1000
+ {
1001
+ id: "gemini-new-project-wired",
1002
+ description: "Antigravity 1.0.13: --new-project is wired",
1003
+ args: ["--print", "hello", "--new-project"],
1004
+ expect: "pass",
1005
+ },
985
1006
  ],
986
1007
  },
987
1008
  grok: {
@@ -1073,7 +1094,49 @@ export const UPSTREAM_CLI_CONTRACTS = {
1073
1094
  tier: "inspect",
1074
1095
  }),
1075
1096
  setup: subcommand(["setup"], "Configure Grok CLI local setup.", "writes_local_config", ["--leader-socket"], { exposure: "not_exposed" }),
1076
- ssh: subcommand(["ssh"], "Manage Grok SSH integration.", "network", ["--leader-socket"]),
1097
+ ssh: subcommand(["ssh"], "Manage Grok SSH integration.", "network", ["--leader-socket"], {
1098
+ acknowledgedUpstreamFlags: [
1099
+ "--agent",
1100
+ "--agents",
1101
+ "--allow",
1102
+ "--always-approve",
1103
+ "--best-of-n",
1104
+ "--check",
1105
+ "--continue",
1106
+ "--cwd",
1107
+ "--deny",
1108
+ "--disable-web-search",
1109
+ "--disallowed-tools",
1110
+ "--effort",
1111
+ "--experimental-memory",
1112
+ "--fork-session",
1113
+ "--json-schema",
1114
+ "--max-turns",
1115
+ "--model",
1116
+ "--no-alt-screen",
1117
+ "--no-memory",
1118
+ "--no-plan",
1119
+ "--no-subagents",
1120
+ "--oauth",
1121
+ "--output-format",
1122
+ "--permission-mode",
1123
+ "--prompt-file",
1124
+ "--prompt-json",
1125
+ "--reasoning-effort",
1126
+ "--restore-code",
1127
+ "--resume",
1128
+ "--rules",
1129
+ "--sandbox",
1130
+ "--session-id",
1131
+ "--single",
1132
+ "--system-prompt-override",
1133
+ "--tools",
1134
+ "--verbatim",
1135
+ "--version",
1136
+ "--worktree",
1137
+ "--worktree-ref",
1138
+ ],
1139
+ }),
1077
1140
  trace: subcommand(["trace"], "Inspect Grok trace data.", "read_only", ["--json", "--leader-socket", "--local", "--output"], { tier: "diagnostic" }),
1078
1141
  update: subcommand(["update"], "Update the Grok CLI binary.", "updates_binary", [
1079
1142
  "--alpha",
@@ -1088,7 +1151,7 @@ export const UPSTREAM_CLI_CONTRACTS = {
1088
1151
  worktree: subcommand(["worktree"], "Manage Grok worktree sessions.", "writes_local_config", ["--leader-socket"]),
1089
1152
  }, GROK_DEBUG_HELP_FLAGS),
1090
1153
  maxPositionals: 0,
1091
- acknowledgedUpstreamFlags: GROK_DEBUG_HELP_FLAGS,
1154
+ acknowledgedUpstreamFlags: [...GROK_DEBUG_HELP_FLAGS, "--session-id"],
1092
1155
  mcpTools: ["grok_request", "grok_request_async"],
1093
1156
  mcpParameters: [
1094
1157
  "prompt",
@@ -1133,6 +1196,9 @@ export const UPSTREAM_CLI_CONTRACTS = {
1133
1196
  "restoreCode",
1134
1197
  "leaderSocket",
1135
1198
  "nativeWorktree",
1199
+ "worktreeRef",
1200
+ "forkSession",
1201
+ "jsonSchema",
1136
1202
  ],
1137
1203
  flags: {
1138
1204
  "-p": { arity: "one", description: "Prompt text" },
@@ -1229,6 +1295,18 @@ export const UPSTREAM_CLI_CONTRACTS = {
1229
1295
  arity: "optional",
1230
1296
  description: "Start the session in a new git worktree, optionally named",
1231
1297
  },
1298
+ "--worktree-ref": {
1299
+ arity: "one",
1300
+ description: "Git ref to base the worktree on (requires --worktree)",
1301
+ },
1302
+ "--fork-session": {
1303
+ arity: "none",
1304
+ description: "Fork the resumed session into a new session ID",
1305
+ },
1306
+ "--json-schema": {
1307
+ arity: "one",
1308
+ description: "JSON Schema literal constraining structured output (implies json output)",
1309
+ },
1232
1310
  "--compaction-mode": {
1233
1311
  arity: "one",
1234
1312
  values: ["summary", "transcript", "segments"],
@@ -1397,6 +1475,35 @@ export const UPSTREAM_CLI_CONTRACTS = {
1397
1475
  args: ["-p", "hello", "--agent", "reviewer", "--leader-socket", "/tmp/leader.sock"],
1398
1476
  expect: "pass",
1399
1477
  },
1478
+ {
1479
+ id: "grok-json-schema-wired",
1480
+ description: "Grok 0.2.73: --json-schema <SCHEMA> is wired (structured output, implies json)",
1481
+ args: [
1482
+ "-p",
1483
+ "hello",
1484
+ "--json-schema",
1485
+ '{"type":"object","properties":{"name":{"type":"string"}}}',
1486
+ ],
1487
+ expect: "pass",
1488
+ },
1489
+ {
1490
+ id: "grok-fork-session-wired",
1491
+ description: "Grok 0.2.73: --fork-session is wired (fork resumed session into a new ID)",
1492
+ args: ["-p", "hello", "--resume", "sess-1", "--fork-session"],
1493
+ expect: "pass",
1494
+ },
1495
+ {
1496
+ id: "grok-worktree-ref-wired",
1497
+ description: "Grok 0.2.73: --worktree-ref <REF> is wired (with --worktree)",
1498
+ args: ["-p", "hello", "--worktree", "--worktree-ref", "main"],
1499
+ expect: "pass",
1500
+ },
1501
+ {
1502
+ id: "grok-session-id-acknowledged-not-emitted",
1503
+ description: "Grok 0.2.73 advertises --session-id, but the gateway owns session-id lifecycle and does not emit it; caller argv is rejected",
1504
+ args: ["-p", "hello", "--session-id", "11111111-1111-1111-1111-111111111111"],
1505
+ expect: "fail",
1506
+ },
1400
1507
  ],
1401
1508
  },
1402
1509
  mistral: {
@@ -2274,7 +2381,7 @@ function probeInstalledCliSubcommands(cli, timeoutMs) {
2274
2381
  break;
2275
2382
  }
2276
2383
  outputs.push(`${result.stdout ?? ""}\n${result.stderr ?? ""}`);
2277
- if (result.status !== 0) {
2384
+ if (result.status !== 0 && !sub.helpProbeExitTolerant) {
2278
2385
  warnings.push(`${contract.executable} ${args.join(" ")} exited with status ${result.status}`);
2279
2386
  }
2280
2387
  }
@@ -3,11 +3,13 @@ import { type ProviderRuntimeStatus } from "./provider-status.js";
3
3
  import type { ApiProviderRuntime } from "./config.js";
4
4
  import { type NormalizedValidationResult, type ValidationProvider } from "./validation-normalizer.js";
5
5
  import { type ValidationReport } from "./validation-report.js";
6
+ import type { ValidationRunStore } from "./job-store.js";
6
7
  import { type ValidationIntent } from "./validation-prompts.js";
7
8
  export interface ValidationOrchestratorDeps {
8
9
  asyncJobManager: AsyncJobManager;
9
10
  getProviderRuntimeStatus?: (provider: ValidationProvider) => ProviderRuntimeStatus;
10
11
  apiProviders?: ApiProviderRuntime[];
12
+ validationRunStore?: ValidationRunStore | null;
11
13
  }
12
14
  export interface StartValidationInput {
13
15
  intent: ValidationIntent;
@@ -21,7 +23,7 @@ export interface StartValidationInput {
21
23
  export interface ValidationRunReport {
22
24
  success: boolean;
23
25
  validationId: string;
24
- status: "running" | "partial" | "not_started";
26
+ status: "running" | "partial" | "not_started" | "completed";
25
27
  startedAt: string;
26
28
  intent: ValidationIntent;
27
29
  originalRequest: {
@@ -32,7 +34,7 @@ export interface ValidationRunReport {
32
34
  modelList: ValidationProvider[];
33
35
  results: NormalizedValidationResult[];
34
36
  synthesis: {
35
- status: "not_requested" | "waiting_for_provider_results" | "running" | "skipped";
37
+ status: "not_requested" | "waiting_for_provider_results" | "running" | "skipped" | "completed";
36
38
  judgeModel: ValidationProvider | null;
37
39
  rawJobReference: NormalizedValidationResult["rawJobReference"];
38
40
  note: string;
@@ -45,5 +47,6 @@ export declare function startJudgeSynthesis(deps: ValidationOrchestratorDeps, in
45
47
  question: string;
46
48
  providerResults: NormalizedValidationResult[];
47
49
  judgeProvider: ValidationProvider;
50
+ validationId?: string;
48
51
  }): ValidationRunReport["synthesis"];
49
52
  export declare function collectValidationJobResult(deps: ValidationOrchestratorDeps, provider: ValidationProvider, jobId: string, model: string | null, maxChars?: number): NormalizedValidationResult | null;