zelari-code 2.3.0 → 2.4.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.
@@ -595,8 +595,8 @@ async function runGrokOAuthFlow(options = {}) {
595
595
  );
596
596
  } catch {
597
597
  }
598
- const timeoutMs = options.timeoutMs ?? DEFAULT_OAUTH_TIMEOUT_MS;
599
- const effectiveTimeout = Math.min(timeoutMs, deviceAuth.expiresIn * 1e3);
598
+ const timeoutMs2 = options.timeoutMs ?? DEFAULT_OAUTH_TIMEOUT_MS;
599
+ const effectiveTimeout = Math.min(timeoutMs2, deviceAuth.expiresIn * 1e3);
600
600
  return pollForDeviceToken({
601
601
  clientId: clientId2,
602
602
  deviceCode: deviceAuth.deviceCode,
@@ -739,7 +739,7 @@ function generateOAuthState() {
739
739
  }
740
740
  async function waitForLoopbackCallback(options) {
741
741
  const host = options.host ?? "127.0.0.1";
742
- const timeoutMs = options.timeoutMs ?? 3e5;
742
+ const timeoutMs2 = options.timeoutMs ?? 3e5;
743
743
  const expectedPath = options.path.startsWith("/") ? options.path : `/${options.path}`;
744
744
  return new Promise((resolve3, reject) => {
745
745
  let settled = false;
@@ -759,12 +759,12 @@ async function waitForLoopbackCallback(options) {
759
759
  const ok = Boolean(result.code) && !result.error;
760
760
  res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
761
761
  res.end(oauthResultHtml(ok, result.errorDescription ?? result.error));
762
- finish(result);
762
+ finish2(result);
763
763
  });
764
764
  const timer = setTimeout(() => {
765
- finish(void 0, new Error(`Timed out waiting for OAuth callback on http://${host}:${options.port}`));
766
- }, timeoutMs);
767
- const finish = (value, err) => {
765
+ finish2(void 0, new Error(`Timed out waiting for OAuth callback on http://${host}:${options.port}`));
766
+ }, timeoutMs2);
767
+ const finish2 = (value, err) => {
768
768
  if (settled) return;
769
769
  settled = true;
770
770
  clearTimeout(timer);
@@ -773,7 +773,7 @@ async function waitForLoopbackCallback(options) {
773
773
  else resolve3(value ?? {});
774
774
  });
775
775
  };
776
- server.on("error", (err) => finish(void 0, err));
776
+ server.on("error", (err) => finish2(void 0, err));
777
777
  server.listen(options.port, host);
778
778
  });
779
779
  }
@@ -971,8 +971,8 @@ async function pollChatgptDeviceAuth(options) {
971
971
  const clientId2 = options.clientId || process.env.CHATGPT_OAUTH_CLIENT_ID || DEFAULT_CHATGPT_CLIENT_ID;
972
972
  const fetchImpl = options.fetchImpl ?? fetch;
973
973
  const sleep = options.sleepImpl ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
974
- const timeoutMs = options.timeoutMs ?? 3e5;
975
- const deadline = Date.now() + timeoutMs;
974
+ const timeoutMs2 = options.timeoutMs ?? 3e5;
975
+ const deadline = Date.now() + timeoutMs2;
976
976
  let interval = options.interval ?? 5;
977
977
  let authorizationCode;
978
978
  let codeVerifier;
@@ -19419,9 +19419,9 @@ function assertHttpUrl(raw) {
19419
19419
  }
19420
19420
  return url2;
19421
19421
  }
19422
- async function fetchWithTimeout(url2, init, outerSignal, timeoutMs) {
19422
+ async function fetchWithTimeout(url2, init, outerSignal, timeoutMs2) {
19423
19423
  const ctrl = new AbortController();
19424
- const timer = setTimeout(() => ctrl.abort(new Error(`timeout after ${timeoutMs}ms`)), timeoutMs);
19424
+ const timer = setTimeout(() => ctrl.abort(new Error(`timeout after ${timeoutMs2}ms`)), timeoutMs2);
19425
19425
  const onOuterAbort = () => ctrl.abort(outerSignal?.reason);
19426
19426
  outerSignal?.addEventListener("abort", onOuterAbort, { once: true });
19427
19427
  try {
@@ -23327,8 +23327,8 @@ var init_lifecycleHookRunner = __esm({
23327
23327
  /** Execute a single hook, swallowing every failure into an allow. */
23328
23328
  async runHookSafely(hook, payload) {
23329
23329
  try {
23330
- const timeoutMs = hook.timeoutMs ?? this.defaultTimeoutMs;
23331
- const raw = hook.url ? await this.postHttp(hook.url, payload, timeoutMs) : await this.execCommand(hook.command ?? "", payload, timeoutMs, hook.cwd);
23330
+ const timeoutMs2 = hook.timeoutMs ?? this.defaultTimeoutMs;
23331
+ const raw = hook.url ? await this.postHttp(hook.url, payload, timeoutMs2) : await this.execCommand(hook.command ?? "", payload, timeoutMs2, hook.cwd);
23332
23332
  const decision = parseJson(raw);
23333
23333
  if (!decision) {
23334
23334
  this.logger(`hook "${hook.name}" returned invalid JSON (fail-open): ${raw.slice(0, 200)}`);
@@ -23345,7 +23345,7 @@ var init_lifecycleHookRunner = __esm({
23345
23345
  }
23346
23346
  }
23347
23347
  /** Spawn `command`, write JSON payload to stdin, read stdout until close. */
23348
- execCommand(command, payload, timeoutMs, cwd) {
23348
+ execCommand(command, payload, timeoutMs2, cwd) {
23349
23349
  return new Promise((resolve3, reject) => {
23350
23350
  const child = spawn2(command, {
23351
23351
  shell: true,
@@ -23357,8 +23357,8 @@ var init_lifecycleHookRunner = __esm({
23357
23357
  let stderr = "";
23358
23358
  const timer = setTimeout(() => {
23359
23359
  child.kill("SIGKILL");
23360
- reject(new Error(`timed out after ${timeoutMs}ms`));
23361
- }, timeoutMs);
23360
+ reject(new Error(`timed out after ${timeoutMs2}ms`));
23361
+ }, timeoutMs2);
23362
23362
  child.stdout?.on("data", (d) => {
23363
23363
  stdout += d.toString();
23364
23364
  });
@@ -23384,9 +23384,9 @@ var init_lifecycleHookRunner = __esm({
23384
23384
  });
23385
23385
  }
23386
23386
  /** POST JSON payload; resolve with the response body text. */
23387
- async postHttp(url2, payload, timeoutMs) {
23387
+ async postHttp(url2, payload, timeoutMs2) {
23388
23388
  const ctrl = new AbortController();
23389
- const timer = setTimeout(() => ctrl.abort(), timeoutMs);
23389
+ const timer = setTimeout(() => ctrl.abort(), timeoutMs2);
23390
23390
  try {
23391
23391
  const res = await fetch(url2, {
23392
23392
  method: "POST",
@@ -27962,7 +27962,7 @@ function deepFreeze(value) {
27962
27962
  return Object.freeze(value);
27963
27963
  }
27964
27964
  async function runInSandbox(opts) {
27965
- const { bundleCode, sdk, timeoutMs } = opts;
27965
+ const { bundleCode, sdk, timeoutMs: timeoutMs2 } = opts;
27966
27966
  if (!opts.skipFootgunScan) {
27967
27967
  const footguns = scanForFootguns(bundleCode);
27968
27968
  if (footguns.length > 0) {
@@ -27982,7 +27982,7 @@ async function runInSandbox(opts) {
27982
27982
  });
27983
27983
  const start = Date.now();
27984
27984
  const vmOpts = {
27985
- timeout: Math.max(1, timeoutMs),
27985
+ timeout: Math.max(1, timeoutMs2),
27986
27986
  displayErrors: true,
27987
27987
  breakOnSigint: true
27988
27988
  };
@@ -27992,7 +27992,7 @@ async function runInSandbox(opts) {
27992
27992
  } catch (err) {
27993
27993
  const msg = err && typeof err === "object" && "message" in err ? String(err.message ?? "") : String(err);
27994
27994
  if (/timed out/i.test(msg)) {
27995
- throw new PlanError("budget_exceeded", `script exceeded ${timeoutMs}ms budget`);
27995
+ throw new PlanError("budget_exceeded", `script exceeded ${timeoutMs2}ms budget`);
27996
27996
  }
27997
27997
  if (opts.signal?.aborted) {
27998
27998
  throw new PlanError("cancelled", "script aborted by host");
@@ -29237,7 +29237,7 @@ var init_nodeProviders = __esm({
29237
29237
  async exec(command, options = {}) {
29238
29238
  const started = Date.now();
29239
29239
  const cwd = this.workspace.resolve(options.cwd ?? ".");
29240
- const timeoutMs = options.timeoutMs ?? this.defaults.timeoutMs ?? DEFAULT_TIMEOUT_MS2;
29240
+ const timeoutMs2 = options.timeoutMs ?? this.defaults.timeoutMs ?? DEFAULT_TIMEOUT_MS2;
29241
29241
  const maxChars = options.maxOutputChars ?? DEFAULT_MAX_OUTPUT_CHARS;
29242
29242
  const isWindows = process.platform === "win32";
29243
29243
  const file2 = isWindows ? "cmd.exe" : "/bin/sh";
@@ -29260,7 +29260,7 @@ var init_nodeProviders = __esm({
29260
29260
  const timer = setTimeout(() => {
29261
29261
  timedOut = true;
29262
29262
  killTree(child);
29263
- }, timeoutMs);
29263
+ }, timeoutMs2);
29264
29264
  child.stdout?.on("data", (d) => {
29265
29265
  stdout = append(stdout, d.toString("utf-8"));
29266
29266
  });
@@ -30198,9 +30198,9 @@ var init_sessionEvidence = __esm({
30198
30198
 
30199
30199
  // packages/core/dist/verification/criteriaPack.v1.js
30200
30200
  function codingCriteriaPack(options = {}) {
30201
- const timeoutMs = options.commandTimeoutMs ?? 6e5;
30201
+ const timeoutMs2 = options.commandTimeoutMs ?? 6e5;
30202
30202
  const withDefault = (o, d) => o === void 0 ? d : o;
30203
- const command = (command2) => command2 ? { kind: "command", command: command2, timeoutMs } : void 0;
30203
+ const command = (command2) => command2 ? { kind: "command", command: command2, timeoutMs: timeoutMs2 } : void 0;
30204
30204
  const criteria = [
30205
30205
  {
30206
30206
  id: "correctness.error-signals",
@@ -33281,7 +33281,7 @@ var init_registry2 = __esm({
33281
33281
  if (!parsed.success) {
33282
33282
  return typedErr(`Invalid input: ${parsed.error.message}`);
33283
33283
  }
33284
- const timeoutMs = options.timeoutMs ?? tool.timeoutMs ?? 3e4;
33284
+ const timeoutMs2 = options.timeoutMs ?? tool.timeoutMs ?? 3e4;
33285
33285
  const parentSignal = options.signal;
33286
33286
  const controller = new AbortController();
33287
33287
  let timer;
@@ -33331,8 +33331,8 @@ var init_registry2 = __esm({
33331
33331
  timer = setTimeout(() => {
33332
33332
  if (!controller.signal.aborted)
33333
33333
  controller.abort();
33334
- reject(new Error(`Tool "${name}" timed out after ${timeoutMs}ms`));
33335
- }, timeoutMs);
33334
+ reject(new Error(`Tool "${name}" timed out after ${timeoutMs2}ms`));
33335
+ }, timeoutMs2);
33336
33336
  })
33337
33337
  ]);
33338
33338
  if (result.ok) {
@@ -33678,11 +33678,11 @@ async function runDiagnosticsForFile(file2, options = {}) {
33678
33678
  const provider = providerForFile(file2, options.providers);
33679
33679
  if (!provider) return [];
33680
33680
  const cwd = options.cwd ?? process.cwd();
33681
- const timeoutMs = options.timeoutMs ?? 5e3;
33681
+ const timeoutMs2 = options.timeoutMs ?? 5e3;
33682
33682
  const runner = options.runner ?? defaultRunner;
33683
33683
  try {
33684
33684
  const bin = options.runner ? provider.bin : resolveBin(provider.bin, cwd);
33685
- const result = await runner(bin, provider.args(file2), { cwd, timeoutMs });
33685
+ const result = await runner(bin, provider.args(file2), { cwd, timeoutMs: timeoutMs2 });
33686
33686
  return provider.parse(result.stdout, file2);
33687
33687
  } catch {
33688
33688
  return [];
@@ -35184,7 +35184,7 @@ function renderVerdict(verdict, candidateCount) {
35184
35184
  return lines.join("\n");
35185
35185
  }
35186
35186
  function createKrakenSelectTool(deps) {
35187
- const timeoutMs = deps.timeoutMs ?? 12e4;
35187
+ const timeoutMs2 = deps.timeoutMs ?? 12e4;
35188
35188
  return {
35189
35189
  name: "kraken_select",
35190
35190
  description: "Compare the candidate research reports spawned this turn (task purpose=candidate) and select the hypothesis best supported by OBSERVED EVIDENCE. A dedicated verifier call (default: the current model) judges grounded vs unsupported claims; degraded observations are never treated as proof of absence. Returns the selected candidate, a rationale, and requiredChecks the implementation must pass. Call it ONCE per turn, after candidate tentacles finished and BEFORE implementing. If it reports needs_more_evidence, either spawn ONE more differentiated candidate or proceed with your own judgment.",
@@ -35241,7 +35241,7 @@ function createKrakenSelectTool(deps) {
35241
35241
  model: id.model,
35242
35242
  provider: id.provider,
35243
35243
  tools: [],
35244
- signal: AbortSignal.timeout(timeoutMs)
35244
+ signal: AbortSignal.timeout(timeoutMs2)
35245
35245
  });
35246
35246
  judgingUsage = usage;
35247
35247
  if (raw.trim().length === 0) throw new Error("empty verifier response");
@@ -38792,7 +38792,7 @@ exec node "$(dirname "$0")/askpass.cjs"
38792
38792
  }
38793
38793
  return sh;
38794
38794
  }
38795
- function runSsh(target, remoteCommand, timeoutMs = 6e4) {
38795
+ function runSsh(target, remoteCommand, timeoutMs2 = 6e4) {
38796
38796
  return new Promise((resolve3) => {
38797
38797
  if (target.auth === "password" && !getSshPassword(target.id)) {
38798
38798
  resolve3({
@@ -38832,7 +38832,7 @@ function runSsh(target, remoteCommand, timeoutMs = 6e4) {
38832
38832
  stdout,
38833
38833
  stderr: stderr + "\n[ssh] timeout"
38834
38834
  });
38835
- }, timeoutMs);
38835
+ }, timeoutMs2);
38836
38836
  child.on("error", (err) => {
38837
38837
  clearTimeout(timer);
38838
38838
  resolve3({
@@ -39061,7 +39061,7 @@ async function readChecks(cwd) {
39061
39061
  return [];
39062
39062
  }
39063
39063
  }
39064
- function runShell(command, cwd, timeoutMs, signal) {
39064
+ function runShell(command, cwd, timeoutMs2, signal) {
39065
39065
  return new Promise((resolve3) => {
39066
39066
  const isWin = process.platform === "win32";
39067
39067
  const child = spawn10(isWin ? "cmd.exe" : "/bin/sh", isWin ? ["/c", command] : ["-c", command], {
@@ -39073,7 +39073,7 @@ function runShell(command, cwd, timeoutMs, signal) {
39073
39073
  let stdout = "";
39074
39074
  let stderr = "";
39075
39075
  let settled = false;
39076
- const finish = (exitCode) => {
39076
+ const finish2 = (exitCode) => {
39077
39077
  if (settled) return;
39078
39078
  settled = true;
39079
39079
  resolve3({ exitCode, stdout, stderr });
@@ -39083,8 +39083,8 @@ function runShell(command, cwd, timeoutMs, signal) {
39083
39083
  child.kill("SIGTERM");
39084
39084
  } catch {
39085
39085
  }
39086
- finish(124);
39087
- }, timeoutMs);
39086
+ finish2(124);
39087
+ }, timeoutMs2);
39088
39088
  if (signal) {
39089
39089
  if (signal.aborted) {
39090
39090
  try {
@@ -39092,7 +39092,7 @@ function runShell(command, cwd, timeoutMs, signal) {
39092
39092
  } catch {
39093
39093
  }
39094
39094
  clearTimeout(timer);
39095
- finish(130);
39095
+ finish2(130);
39096
39096
  return;
39097
39097
  }
39098
39098
  signal.addEventListener(
@@ -39103,7 +39103,7 @@ function runShell(command, cwd, timeoutMs, signal) {
39103
39103
  } catch {
39104
39104
  }
39105
39105
  clearTimeout(timer);
39106
- finish(130);
39106
+ finish2(130);
39107
39107
  },
39108
39108
  { once: true }
39109
39109
  );
@@ -39118,11 +39118,11 @@ function runShell(command, cwd, timeoutMs, signal) {
39118
39118
  });
39119
39119
  child.on("error", () => {
39120
39120
  clearTimeout(timer);
39121
- finish(1);
39121
+ finish2(1);
39122
39122
  });
39123
39123
  child.on("close", (code) => {
39124
39124
  clearTimeout(timer);
39125
- finish(code ?? 1);
39125
+ finish2(code ?? 1);
39126
39126
  });
39127
39127
  });
39128
39128
  }
@@ -39144,9 +39144,9 @@ async function runBacktest(cwd, signal) {
39144
39144
  const results = [];
39145
39145
  for (const c of checks) {
39146
39146
  const expectExit = c.expectExit ?? 0;
39147
- const timeoutMs = c.timeoutMs ?? 12e4;
39147
+ const timeoutMs2 = c.timeoutMs ?? 12e4;
39148
39148
  const start = Date.now();
39149
- const { exitCode, stdout, stderr } = await runShell(c.command, cwd, timeoutMs, signal);
39149
+ const { exitCode, stdout, stderr } = await runShell(c.command, cwd, timeoutMs2, signal);
39150
39150
  const combined = `${stdout}${stderr}`;
39151
39151
  const preview = combined.slice(0, 400);
39152
39152
  let ok = exitCode === expectExit;
@@ -39858,8 +39858,9 @@ function createBuiltinToolRegistry(options = {}) {
39858
39858
  if (hooks) registry4.setLifecycleHooks(hooks);
39859
39859
  const readOnly = options.readOnly === true || options.planMode === true || profile === "explore";
39860
39860
  const verifyMode = profile === "verify";
39861
- const allowMutators = !readOnly && !verifyMode;
39862
- const allowBash = allowMutators || verifyMode;
39861
+ const gauntletParent = options.gauntletParent === true;
39862
+ const allowMutators = !readOnly && !verifyMode && !gauntletParent;
39863
+ const allowBash = (allowMutators || verifyMode) && !gauntletParent;
39863
39864
  const permPolicy = options.permissionPolicy ?? defaultPermissionPolicy();
39864
39865
  const withPerm = (t) => wrapWithPermissions(t, permPolicy, options.onPermissionAsk);
39865
39866
  registry4.register(withPerm(safeReadFile));
@@ -39892,21 +39893,21 @@ function createBuiltinToolRegistry(options = {}) {
39892
39893
  if (allowBash) {
39893
39894
  registry4.register(withPerm(safeBash));
39894
39895
  }
39895
- const askUserTool = options.readOnly === true || profile === "explore" || profile === "verify" ? null : createAskUserTool(options.onAskUser);
39896
+ const askUserTool = options.readOnly === true || profile === "explore" || profile === "verify" || gauntletParent ? null : createAskUserTool(options.onAskUser);
39896
39897
  if (askUserTool) {
39897
39898
  registry4.register(withPerm(askUserTool));
39898
39899
  }
39899
- const enableSkill = options.enableSkill !== false && options.readOnly !== true && profile !== "explore" && profile !== "verify";
39900
+ const enableSkill = options.enableSkill !== false && options.readOnly !== true && !gauntletParent && profile !== "explore" && profile !== "verify";
39900
39901
  const skillTool = enableSkill ? withPerm(createSkillTool({ cwd: root })) : null;
39901
39902
  if (skillTool) {
39902
39903
  registry4.register(skillTool);
39903
39904
  }
39904
- const enableTodos = options.enableTodos !== false && options.readOnly !== true && profile === "full";
39905
+ const enableTodos = options.enableTodos !== false && options.readOnly !== true && !gauntletParent && profile === "full";
39905
39906
  const todoWrite = enableTodos ? withPerm(createTodoWriteTool()) : null;
39906
39907
  const todoRead = enableTodos ? withPerm(createTodoReadTool()) : null;
39907
39908
  if (todoWrite) registry4.register(todoWrite);
39908
39909
  if (todoRead) registry4.register(todoRead);
39909
- const enablePlanTasks = options.enablePlanTasks !== false && options.readOnly !== true && (profile === "full" || options.planMode === true) && profile !== "explore" && profile !== "verify" && profile !== "general";
39910
+ const enablePlanTasks = options.enablePlanTasks !== false && options.readOnly !== true && !gauntletParent && (profile === "full" || options.planMode === true) && profile !== "explore" && profile !== "verify" && profile !== "general";
39910
39911
  const planTaskToolsWrapped = (enablePlanTasks ? createPlanTaskTools({ projectRoot: root, onTaskEvent: options.onTaskEvent }) : []).map((t) => withPerm(t));
39911
39912
  for (const t of planTaskToolsWrapped) {
39912
39913
  registry4.register(t);
@@ -39957,7 +39958,7 @@ function createBuiltinToolRegistry(options = {}) {
39957
39958
  permissions: semanticTool.permissions ?? []
39958
39959
  });
39959
39960
  }
39960
- if (!readOnly && process.env.ZELARI_BROWSER !== "0") {
39961
+ if (!readOnly && !gauntletParent && process.env.ZELARI_BROWSER !== "0") {
39961
39962
  const browserTool = createBrowserTool();
39962
39963
  registry4.register(browserTool);
39963
39964
  tools.push({
@@ -39966,7 +39967,7 @@ function createBuiltinToolRegistry(options = {}) {
39966
39967
  permissions: browserTool.permissions ?? []
39967
39968
  });
39968
39969
  }
39969
- if (!readOnly && process.env.ZELARI_SSH !== "0") {
39970
+ if (!readOnly && !gauntletParent && process.env.ZELARI_SSH !== "0") {
39970
39971
  for (const t of createSshTools()) {
39971
39972
  registry4.register(t);
39972
39973
  tools.push({
@@ -39976,7 +39977,7 @@ function createBuiltinToolRegistry(options = {}) {
39976
39977
  });
39977
39978
  }
39978
39979
  }
39979
- if (!readOnly) {
39980
+ if (!readOnly && !gauntletParent) {
39980
39981
  for (const t of createWorldModelTools()) {
39981
39982
  const safe = wrapWithAudit(t, audit, sessionId2);
39982
39983
  registry4.register(safe);
@@ -40202,10 +40203,10 @@ function wrapWithDiagnostics(original, root, runner) {
40202
40203
  const filePath = value && typeof value === "object" && typeof value.path === "string" ? value.path : void 0;
40203
40204
  if (!filePath) return result;
40204
40205
  try {
40205
- const timeoutMs = Number(process.env.ZELARI_DIAGNOSTICS_TIMEOUT_MS) || 5e3;
40206
+ const timeoutMs2 = Number(process.env.ZELARI_DIAGNOSTICS_TIMEOUT_MS) || 5e3;
40206
40207
  const diags = await runDiagnosticsForFile(filePath, {
40207
40208
  cwd: root,
40208
- timeoutMs,
40209
+ timeoutMs: timeoutMs2,
40209
40210
  ...runner ? { runner } : {}
40210
40211
  });
40211
40212
  const formatted = formatDiagnostics(diags, { relativeTo: root });
@@ -40470,6 +40471,7 @@ function parseHeadlessFlags(argv) {
40470
40471
  let krakenGraph;
40471
40472
  let planOnly = process.env.ZELARI_KRAKEN_PLAN_ONLY === "1" || process.env.ZELARI_KRAKEN_PLAN_ONLY === "true";
40472
40473
  let runPlan = process.env.ZELARI_KRAKEN_RUN_PLAN;
40474
+ let gauntlet = process.env.ZELARI_GAUNTLET === "1" || process.env.ZELARI_GAUNTLET === "true";
40473
40475
  for (let i = 0; i < argv.length; i++) {
40474
40476
  const arg = argv[i];
40475
40477
  if (arg === "--headless") continue;
@@ -40640,6 +40642,10 @@ function parseHeadlessFlags(argv) {
40640
40642
  } else if (arg === "--run-plan") {
40641
40643
  runPlan = argv[i + 1];
40642
40644
  i++;
40645
+ } else if (arg === "--gauntlet") {
40646
+ gauntlet = true;
40647
+ } else if (arg === "--no-gauntlet") {
40648
+ gauntlet = false;
40643
40649
  }
40644
40650
  }
40645
40651
  if (councilFlag && !modeExplicit) {
@@ -40674,7 +40680,8 @@ function parseHeadlessFlags(argv) {
40674
40680
  ...strictDone ? { strictDone: true } : {},
40675
40681
  ...krakenGraph ? { krakenGraph } : {},
40676
40682
  ...planOnly ? { planOnly: true } : {},
40677
- ...runPlan ? { runPlan } : {}
40683
+ ...runPlan ? { runPlan } : {},
40684
+ ...gauntlet ? { gauntlet: true } : {}
40678
40685
  }
40679
40686
  };
40680
40687
  }
@@ -41985,10 +41992,10 @@ __export(claudeProvider_exports, {
41985
41992
  createLocalCliProvider: () => createLocalCliProvider
41986
41993
  });
41987
41994
  import { spawn as spawn11 } from "node:child_process";
41988
- function waitForExit(child, timeoutMs = 2e3) {
41995
+ function waitForExit(child, timeoutMs2 = 2e3) {
41989
41996
  return new Promise((resolve3) => {
41990
41997
  if (child.exitCode != null) return resolve3(child.exitCode);
41991
- const timer = setTimeout(() => resolve3(child.exitCode ?? null), timeoutMs);
41998
+ const timer = setTimeout(() => resolve3(child.exitCode ?? null), timeoutMs2);
41992
41999
  timer.unref?.();
41993
42000
  child.once("exit", (code) => {
41994
42001
  clearTimeout(timer);
@@ -43559,10 +43566,10 @@ function distTagForVersion(version2) {
43559
43566
  function registryUrlForTag(tag = distTagForVersion(getCurrentVersion())) {
43560
43567
  return `https://registry.npmjs.org/zelari-code/${tag}`;
43561
43568
  }
43562
- async function fetchLatestVersion(fetcher = fetch, registryUrl = REGISTRY_URL, timeoutMs = 5e3) {
43569
+ async function fetchLatestVersion(fetcher = fetch, registryUrl = REGISTRY_URL, timeoutMs2 = 5e3) {
43563
43570
  try {
43564
43571
  const controller = new AbortController();
43565
- const timer = setTimeout(() => controller.abort(), timeoutMs);
43572
+ const timer = setTimeout(() => controller.abort(), timeoutMs2);
43566
43573
  const response = await fetcher(registryUrl, { signal: controller.signal });
43567
43574
  clearTimeout(timer);
43568
43575
  if (!response.ok) {
@@ -43732,11 +43739,11 @@ var init_mcpClient = __esm({
43732
43739
  * Call a tool. Returns the concatenated text content; non-text content
43733
43740
  * items are summarized by type. Throws when the server flags isError.
43734
43741
  */
43735
- async callTool(name, args, timeoutMs = DEFAULT_REQUEST_TIMEOUT_MS) {
43742
+ async callTool(name, args, timeoutMs2 = DEFAULT_REQUEST_TIMEOUT_MS) {
43736
43743
  const res = await this.request(
43737
43744
  "tools/call",
43738
43745
  { name, arguments: args },
43739
- timeoutMs
43746
+ timeoutMs2
43740
43747
  );
43741
43748
  const text = (res.content ?? []).map(
43742
43749
  (c) => c.type === "text" && typeof c.text === "string" ? c.text : `[${c.type ?? "unknown"} content]`
@@ -43753,7 +43760,7 @@ var init_mcpClient = __esm({
43753
43760
  this.child = null;
43754
43761
  }
43755
43762
  // ── JSON-RPC plumbing ────────────────────────────────────────────────
43756
- request(method, params, timeoutMs = DEFAULT_REQUEST_TIMEOUT_MS) {
43763
+ request(method, params, timeoutMs2 = DEFAULT_REQUEST_TIMEOUT_MS) {
43757
43764
  const child = this.child;
43758
43765
  if (!child)
43759
43766
  return Promise.reject(new Error(`[mcp:${this.serverName}] not started`));
@@ -43764,10 +43771,10 @@ var init_mcpClient = __esm({
43764
43771
  this.pending.delete(id);
43765
43772
  reject(
43766
43773
  new Error(
43767
- `[mcp:${this.serverName}] ${method} timed out after ${timeoutMs}ms`
43774
+ `[mcp:${this.serverName}] ${method} timed out after ${timeoutMs2}ms`
43768
43775
  )
43769
43776
  );
43770
- }, timeoutMs);
43777
+ }, timeoutMs2);
43771
43778
  this.pending.set(id, { resolve: resolve3, reject, timer });
43772
43779
  child.stdin.write(payload + "\n", (err) => {
43773
43780
  if (err) {
@@ -44841,7 +44848,7 @@ function pickSmokeScript(scripts) {
44841
44848
  }
44842
44849
  return null;
44843
44850
  }
44844
- async function runProjectSmoke(projectRoot, timeoutMs = DEFAULT_TIMEOUT_MS3) {
44851
+ async function runProjectSmoke(projectRoot, timeoutMs2 = DEFAULT_TIMEOUT_MS3) {
44845
44852
  if (process.env["ZELARI_SMOKE"] === "0") {
44846
44853
  return { ran: false, reason: "ZELARI_SMOKE=0 (disabled)" };
44847
44854
  }
@@ -44874,7 +44881,7 @@ async function runProjectSmoke(projectRoot, timeoutMs = DEFAULT_TIMEOUT_MS3) {
44874
44881
  let stdout = "";
44875
44882
  let stderr = "";
44876
44883
  let settled = false;
44877
- const finish = (result) => {
44884
+ const finish2 = (result) => {
44878
44885
  if (settled) return;
44879
44886
  settled = true;
44880
44887
  clearTimeout(timer);
@@ -44882,15 +44889,15 @@ async function runProjectSmoke(projectRoot, timeoutMs = DEFAULT_TIMEOUT_MS3) {
44882
44889
  };
44883
44890
  const timer = setTimeout(() => {
44884
44891
  child.kill("SIGTERM");
44885
- finish({
44892
+ finish2({
44886
44893
  ran: true,
44887
44894
  ok: false,
44888
44895
  script,
44889
44896
  exitCode: -1,
44890
44897
  output: stdout + stderr,
44891
- reason: `smoke timeout after ${timeoutMs}ms`
44898
+ reason: `smoke timeout after ${timeoutMs2}ms`
44892
44899
  });
44893
- }, timeoutMs);
44900
+ }, timeoutMs2);
44894
44901
  child.stdout?.on("data", (chunk) => {
44895
44902
  stdout += chunk.toString("utf8");
44896
44903
  });
@@ -44898,7 +44905,7 @@ async function runProjectSmoke(projectRoot, timeoutMs = DEFAULT_TIMEOUT_MS3) {
44898
44905
  stderr += chunk.toString("utf8");
44899
44906
  });
44900
44907
  child.on("error", (err) => {
44901
- finish({
44908
+ finish2({
44902
44909
  ran: true,
44903
44910
  ok: false,
44904
44911
  script,
@@ -44910,7 +44917,7 @@ async function runProjectSmoke(projectRoot, timeoutMs = DEFAULT_TIMEOUT_MS3) {
44910
44917
  child.on("close", (code) => {
44911
44918
  const exitCode = code ?? -1;
44912
44919
  const ok = exitCode === 0;
44913
- finish({
44920
+ finish2({
44914
44921
  ran: true,
44915
44922
  ok,
44916
44923
  script,
@@ -46519,7 +46526,7 @@ function startPermissionBroker(socketPath, handlers, opts) {
46519
46526
  const closeAll = server.closeAllConnections;
46520
46527
  if (typeof closeAll === "function") closeAll.call(server);
46521
46528
  let done = false;
46522
- const finish = () => {
46529
+ const finish2 = () => {
46523
46530
  if (done) return;
46524
46531
  done = true;
46525
46532
  if (process.platform !== "win32") {
@@ -46528,8 +46535,8 @@ function startPermissionBroker(socketPath, handlers, opts) {
46528
46535
  res();
46529
46536
  }
46530
46537
  };
46531
- server.close(() => finish());
46532
- setTimeout(finish, 1e3).unref?.();
46538
+ server.close(() => finish2());
46539
+ setTimeout(finish2, 1e3).unref?.();
46533
46540
  })
46534
46541
  });
46535
46542
  });
@@ -46966,13 +46973,13 @@ async function createDefaultLlmClient(opts) {
46966
46973
  const llm = await resolveLlm(opts);
46967
46974
  return {
46968
46975
  async complete({ system, user }) {
46969
- const timeoutMs = resolvePlannerTimeoutMs();
46976
+ const timeoutMs2 = resolvePlannerTimeoutMs();
46970
46977
  const controller = new AbortController();
46971
46978
  let timedOut = false;
46972
- const t = timeoutMs > 0 ? setTimeout(() => {
46979
+ const t = timeoutMs2 > 0 ? setTimeout(() => {
46973
46980
  timedOut = true;
46974
46981
  controller.abort();
46975
- }, timeoutMs) : void 0;
46982
+ }, timeoutMs2) : void 0;
46976
46983
  try {
46977
46984
  const url2 = `${llm.baseUrl.replace(/\/$/, "")}/chat/completions`;
46978
46985
  let res;
@@ -46998,7 +47005,7 @@ async function createDefaultLlmClient(opts) {
46998
47005
  } catch (err) {
46999
47006
  if (timedOut) {
47000
47007
  throw new PlannerTransportError(
47001
- `Planner request timed out after ${Math.round(timeoutMs / 1e3)}s (no response). Raise ZELARI_KRAKEN_PLANNER_TIMEOUT_MS, or set ZELARI_KRAKEN_PLANNER_MODEL to a faster non-reasoning model.`
47008
+ `Planner request timed out after ${Math.round(timeoutMs2 / 1e3)}s (no response). Raise ZELARI_KRAKEN_PLANNER_TIMEOUT_MS, or set ZELARI_KRAKEN_PLANNER_MODEL to a faster non-reasoning model.`
47002
47009
  );
47003
47010
  }
47004
47011
  throw new PlannerTransportError(
@@ -49381,10 +49388,10 @@ async function validateApiKey(providerId, apiKey, options = {}) {
49381
49388
  return { ok: true, skipped: true, reason: "no_base_url" };
49382
49389
  }
49383
49390
  const probeUrl = options.probeUrl ?? `${spec.baseUrl.replace(/\/+$/, "")}/models`;
49384
- const timeoutMs = options.timeoutMs ?? 5e3;
49391
+ const timeoutMs2 = options.timeoutMs ?? 5e3;
49385
49392
  const fetchImpl = options.fetchImpl ?? fetch;
49386
49393
  const controller = new AbortController();
49387
- const timer = setTimeout(() => controller.abort(), timeoutMs);
49394
+ const timer = setTimeout(() => controller.abort(), timeoutMs2);
49388
49395
  try {
49389
49396
  const response = await fetchImpl(probeUrl, {
49390
49397
  method: "GET",
@@ -49413,7 +49420,7 @@ async function validateApiKey(providerId, apiKey, options = {}) {
49413
49420
  return {
49414
49421
  ok: false,
49415
49422
  reason: isAbort ? "network" : "network",
49416
- detail: isAbort ? `timeout after ${timeoutMs}ms` : msg,
49423
+ detail: isAbort ? `timeout after ${timeoutMs2}ms` : msg,
49417
49424
  durationMs
49418
49425
  };
49419
49426
  } finally {
@@ -50182,6 +50189,864 @@ var init_atMentions = __esm({
50182
50189
  }
50183
50190
  });
50184
50191
 
50192
+ // src/cli/gauntlet/policy.ts
50193
+ var policy_exports = {};
50194
+ __export(policy_exports, {
50195
+ DEFAULT_MAX_PARALLEL: () => DEFAULT_MAX_PARALLEL2,
50196
+ DEFAULT_MAX_PIECES: () => DEFAULT_MAX_PIECES,
50197
+ DEFAULT_MAX_ROUNDS: () => DEFAULT_MAX_ROUNDS,
50198
+ DEFAULT_WALL_MS: () => DEFAULT_WALL_MS,
50199
+ GAUNTLET_PARENT_BLOCKED_TOOLS: () => GAUNTLET_PARENT_BLOCKED_TOOLS,
50200
+ isGauntletFlagOn: () => isGauntletFlagOn,
50201
+ resolveGauntletCaps: () => resolveGauntletCaps,
50202
+ shouldRunGauntletHostLoop: () => shouldRunGauntletHostLoop
50203
+ });
50204
+ function envInt(raw, fallback, min, max) {
50205
+ if (raw === void 0 || raw === "") return fallback;
50206
+ const n = Number.parseInt(raw, 10);
50207
+ if (!Number.isFinite(n)) return fallback;
50208
+ return Math.min(max, Math.max(min, n));
50209
+ }
50210
+ function isGauntletFlagOn(explicit, env = process.env) {
50211
+ if (explicit === true) return true;
50212
+ if (explicit === false) return false;
50213
+ const raw = env.ZELARI_GAUNTLET;
50214
+ if (!raw) return false;
50215
+ return raw === "1" || raw.toLowerCase() === "true";
50216
+ }
50217
+ function resolveGauntletCaps(env = process.env) {
50218
+ const wallRaw = env.ZELARI_GAUNTLET_WALL_MS;
50219
+ let wallClockMs = DEFAULT_WALL_MS;
50220
+ if (wallRaw !== void 0 && wallRaw !== "") {
50221
+ const n = Number.parseInt(wallRaw, 10);
50222
+ if (Number.isFinite(n) && n >= 0) wallClockMs = n;
50223
+ }
50224
+ return {
50225
+ maxPieces: envInt(env.ZELARI_GAUNTLET_MAX_PIECES, DEFAULT_MAX_PIECES, 1, 16),
50226
+ maxRounds: envInt(env.ZELARI_GAUNTLET_MAX_ROUNDS, DEFAULT_MAX_ROUNDS, 1, 8),
50227
+ maxParallel: envInt(env.ZELARI_GAUNTLET_MAX_PARALLEL, DEFAULT_MAX_PARALLEL2, 1, 4),
50228
+ wallClockMs
50229
+ };
50230
+ }
50231
+ function shouldRunGauntletHostLoop(opts) {
50232
+ if (!isGauntletFlagOn(opts.gauntlet)) return false;
50233
+ if (opts.krakenGraph) return false;
50234
+ if (opts.useCouncil || opts.mode === "council" || opts.mode === "zelari") return false;
50235
+ const phase2 = opts.phase ?? "build";
50236
+ return phase2 !== "plan";
50237
+ }
50238
+ var DEFAULT_MAX_PIECES, DEFAULT_MAX_ROUNDS, DEFAULT_MAX_PARALLEL2, DEFAULT_WALL_MS, GAUNTLET_PARENT_BLOCKED_TOOLS;
50239
+ var init_policy = __esm({
50240
+ "src/cli/gauntlet/policy.ts"() {
50241
+ "use strict";
50242
+ DEFAULT_MAX_PIECES = 6;
50243
+ DEFAULT_MAX_ROUNDS = 3;
50244
+ DEFAULT_MAX_PARALLEL2 = 2;
50245
+ DEFAULT_WALL_MS = 45 * 60 * 1e3;
50246
+ GAUNTLET_PARENT_BLOCKED_TOOLS = [
50247
+ "write_file",
50248
+ "edit_file",
50249
+ "apply_diff",
50250
+ "bash"
50251
+ ];
50252
+ }
50253
+ });
50254
+
50255
+ // src/cli/gauntlet/complete.ts
50256
+ function timeoutMs(env = process.env) {
50257
+ const raw = env.ZELARI_GAUNTLET_DECOMPOSE_TIMEOUT_MS;
50258
+ if (raw === void 0 || raw === "") return DEFAULT_TIMEOUT_MS4;
50259
+ const n = Number.parseInt(raw, 10);
50260
+ return Number.isFinite(n) && n >= 0 ? n : DEFAULT_TIMEOUT_MS4;
50261
+ }
50262
+ async function gauntletComplete(args, opts) {
50263
+ const provider = opts.provider;
50264
+ const meta3 = await resolveApiKeyWithMeta(provider);
50265
+ if (!meta3?.apiKey) {
50266
+ throw new Error(`No API key for provider '${provider}'`);
50267
+ }
50268
+ const baseUrl = resolveBaseUrl(provider);
50269
+ if (!baseUrl) {
50270
+ throw new Error(`No base URL for provider '${provider}'`);
50271
+ }
50272
+ const model = process.env.ZELARI_KRAKEN_PLANNER_MODEL?.trim() || opts.model || getModelForProvider(provider) || getProviderConfig().modelByProvider[provider] || "";
50273
+ if (!model) throw new Error(`No model for provider '${provider}'`);
50274
+ const ms = timeoutMs();
50275
+ const controller = new AbortController();
50276
+ const onParentAbort = () => controller.abort();
50277
+ opts.signal?.addEventListener("abort", onParentAbort, { once: true });
50278
+ let timedOut = false;
50279
+ const timer = ms > 0 ? setTimeout(() => {
50280
+ timedOut = true;
50281
+ controller.abort();
50282
+ }, ms) : void 0;
50283
+ try {
50284
+ const url2 = `${baseUrl.replace(/\/$/, "")}/chat/completions`;
50285
+ const res = await fetch(url2, {
50286
+ method: "POST",
50287
+ signal: controller.signal,
50288
+ headers: {
50289
+ "content-type": "application/json",
50290
+ authorization: `Bearer ${meta3.apiKey}`
50291
+ },
50292
+ body: JSON.stringify({
50293
+ model,
50294
+ temperature: 0.2,
50295
+ max_tokens: DEFAULT_MAX_TOKENS,
50296
+ stream: false,
50297
+ messages: [
50298
+ { role: "system", content: args.system },
50299
+ { role: "user", content: args.user }
50300
+ ]
50301
+ })
50302
+ });
50303
+ if (!res.ok) {
50304
+ const errBody = await res.text().catch(() => "");
50305
+ throw new Error(`decompose HTTP ${res.status}${errBody ? `: ${errBody.slice(0, 160)}` : ""}`);
50306
+ }
50307
+ const json2 = await res.json();
50308
+ const msg = json2.choices?.[0]?.message;
50309
+ const text = msg?.content?.trim() || msg?.reasoning_content?.trim() || "";
50310
+ if (!text) throw new Error("decompose: empty model response");
50311
+ return text;
50312
+ } catch (err) {
50313
+ if (timedOut) {
50314
+ throw new Error(
50315
+ `decompose timed out after ${Math.round(ms / 1e3)}s \u2014 using a single piece. Raise ZELARI_GAUNTLET_DECOMPOSE_TIMEOUT_MS or set ZELARI_KRAKEN_PLANNER_MODEL to a fast model.`
50316
+ );
50317
+ }
50318
+ throw err instanceof Error ? err : new Error(String(err));
50319
+ } finally {
50320
+ if (timer) clearTimeout(timer);
50321
+ opts.signal?.removeEventListener("abort", onParentAbort);
50322
+ }
50323
+ }
50324
+ var DEFAULT_TIMEOUT_MS4, DEFAULT_MAX_TOKENS;
50325
+ var init_complete = __esm({
50326
+ "src/cli/gauntlet/complete.ts"() {
50327
+ "use strict";
50328
+ init_providerConfig();
50329
+ init_keyStore();
50330
+ init_openai_compatible();
50331
+ DEFAULT_TIMEOUT_MS4 = 6e4;
50332
+ DEFAULT_MAX_TOKENS = 2048;
50333
+ }
50334
+ });
50335
+
50336
+ // src/cli/gauntlet/decompose.ts
50337
+ function fallbackPieces(goal) {
50338
+ const prompt = goal.trim() || "Complete the user Goal.";
50339
+ return [
50340
+ {
50341
+ id: "g1",
50342
+ label: labelFromGoal(prompt),
50343
+ prompt,
50344
+ acceptance: []
50345
+ }
50346
+ ];
50347
+ }
50348
+ function labelFromGoal(goal) {
50349
+ const line = goal.trim().split(/\r?\n/, 1)[0] ?? "Goal";
50350
+ return line.slice(0, MAX_LABEL) || "Goal";
50351
+ }
50352
+ function parsePiecesJson(raw, maxPieces) {
50353
+ if (!raw || typeof raw !== "object") return null;
50354
+ const rec = raw;
50355
+ const list = rec.pieces ?? rec.nodes;
50356
+ if (!Array.isArray(list) || list.length === 0) return null;
50357
+ const out = [];
50358
+ for (let i = 0; i < list.length && out.length < maxPieces; i++) {
50359
+ const item = list[i];
50360
+ if (!item || typeof item !== "object") continue;
50361
+ const row = item;
50362
+ const prompt = typeof row.prompt === "string" ? row.prompt.trim() : typeof row.goal === "string" ? row.goal.trim() : "";
50363
+ if (!prompt) continue;
50364
+ const id = typeof row.id === "string" && row.id.trim() ? row.id.trim().slice(0, 32) : `g${out.length + 1}`;
50365
+ const label = typeof row.label === "string" && row.label.trim() ? row.label.trim().slice(0, MAX_LABEL) : labelFromGoal(prompt);
50366
+ const acceptance = Array.isArray(row.acceptance) ? row.acceptance.filter((a) => typeof a === "string" && a.trim().length > 0).map((a) => a.trim()).slice(0, MAX_ACCEPTANCE) : [];
50367
+ const scope = Array.isArray(row.scope) ? row.scope.filter((s) => typeof s === "string" && s.trim().length > 0).map((s) => s.trim()).slice(0, 16) : void 0;
50368
+ const bar = Array.isArray(row.bar) ? row.bar.filter((s) => typeof s === "string" && s.trim().length > 0).map((s) => s.trim()).slice(0, 4) : void 0;
50369
+ out.push({
50370
+ id: uniqueId2(id, out),
50371
+ label,
50372
+ prompt: prompt.slice(0, MAX_PROMPT),
50373
+ acceptance,
50374
+ ...scope && scope.length > 0 ? { scope } : {},
50375
+ ...bar && bar.length > 0 ? { bar } : {}
50376
+ });
50377
+ }
50378
+ return out.length > 0 ? out : null;
50379
+ }
50380
+ function formatHistoryNote(messages, maxMessages = 4, each = 400) {
50381
+ const slice = messages.filter((m) => m.role === "user" || m.role === "assistant").slice(-maxMessages);
50382
+ if (slice.length === 0) return "";
50383
+ return slice.map((m) => {
50384
+ const body = m.content.replace(/\s+/g, " ").trim().slice(0, each);
50385
+ return `${m.role}: ${body}`;
50386
+ }).join("\n");
50387
+ }
50388
+ function buildDecomposeUser(opts) {
50389
+ const parts = [
50390
+ `Max pieces: ${opts.maxPieces}`,
50391
+ "",
50392
+ "## Goal",
50393
+ opts.goal.trim() || "(empty)"
50394
+ ];
50395
+ if (opts.historyNote?.trim()) {
50396
+ parts.push("", "## Prior turns (context only \u2014 the Goal above is authoritative)", opts.historyNote.trim());
50397
+ }
50398
+ if (opts.workspace?.trim()) {
50399
+ parts.push("", "## Workspace", opts.workspace.trim());
50400
+ }
50401
+ return parts.join("\n");
50402
+ }
50403
+ function piecesFromModelText(text, maxPieces) {
50404
+ try {
50405
+ const json2 = extractJsonObject(text, { requireKey: "pieces" });
50406
+ return parsePiecesJson(json2, maxPieces);
50407
+ } catch {
50408
+ return null;
50409
+ }
50410
+ }
50411
+ async function decomposeGoal(opts) {
50412
+ const fallback = fallbackPieces(opts.goal);
50413
+ if (!opts.complete) return { pieces: fallback, source: "fallback" };
50414
+ try {
50415
+ const text = await opts.complete({
50416
+ system: GAUNTLET_DECOMPOSE_SYSTEM,
50417
+ user: buildDecomposeUser(opts)
50418
+ });
50419
+ const parsed = piecesFromModelText(text, opts.maxPieces);
50420
+ if (parsed && parsed.length > 0) return { pieces: parsed, source: "llm" };
50421
+ return { pieces: fallback, source: "fallback", error: "decompose JSON unusable" };
50422
+ } catch (err) {
50423
+ return {
50424
+ pieces: fallback,
50425
+ source: "fallback",
50426
+ error: err instanceof Error ? err.message : String(err)
50427
+ };
50428
+ }
50429
+ }
50430
+ function uniqueId2(id, existing) {
50431
+ if (!existing.some((p3) => p3.id === id)) return id;
50432
+ let n = 2;
50433
+ while (existing.some((p3) => p3.id === `${id}-${n}`)) n += 1;
50434
+ return `${id}-${n}`;
50435
+ }
50436
+ var MAX_LABEL, MAX_PROMPT, MAX_ACCEPTANCE, GAUNTLET_DECOMPOSE_SYSTEM;
50437
+ var init_decompose = __esm({
50438
+ "src/cli/gauntlet/decompose.ts"() {
50439
+ "use strict";
50440
+ init_planner();
50441
+ MAX_LABEL = 80;
50442
+ MAX_PROMPT = 8e3;
50443
+ MAX_ACCEPTANCE = 8;
50444
+ GAUNTLET_DECOMPOSE_SYSTEM = [
50445
+ "Decompose the user Goal into independently shippable Gauntlet pieces.",
50446
+ "Return ONLY JSON (no markdown, no prose):",
50447
+ '{ "pieces": [ { "id": string, "label": string, "prompt": string, "scope"?: string[], "acceptance"?: string[], "bar"?: string[] } ] }',
50448
+ "Rules:",
50449
+ "- 1 to N pieces (N is given in the user message). Prefer fewer. One piece is valid when the goal is atomic.",
50450
+ '- "prompt" is self-contained: the builder will not see this conversation.',
50451
+ '- "scope" is a path allowlist so disjoint pieces can run in parallel. Omit if unsure (forces sequential).',
50452
+ '- "acceptance" is checkable (file, command, observable). Never subjective ("elegant").',
50453
+ '- "bar" (optional) is on-disk reference path(s) the critic compares against blindly (gold file, screenshot, test).',
50454
+ "- Use paths from the workspace listing. Do not invent a tree.",
50455
+ "- Decompose the USER GOAL literally. Do not enlarge it into a different project."
50456
+ ].join("\n");
50457
+ }
50458
+ });
50459
+
50460
+ // src/cli/gauntlet/events.ts
50461
+ function gauntletProgressEvent(sessionId2, progress) {
50462
+ return {
50463
+ type: "gauntlet_progress",
50464
+ sessionId: sessionId2,
50465
+ ts: Date.now(),
50466
+ progress
50467
+ };
50468
+ }
50469
+ function formatGauntletSummary(results, opts) {
50470
+ if (results.length === 0) {
50471
+ if (opts?.timedOut) return "Gauntlet stopped: wall clock. No pieces finished.";
50472
+ if (opts?.cancelled) return "Gauntlet cancelled. No pieces finished.";
50473
+ return "Gauntlet: no pieces ran.";
50474
+ }
50475
+ const head = opts?.timedOut ? "Gauntlet stopped: wall clock." : opts?.cancelled ? "Gauntlet cancelled." : "Gauntlet finished.";
50476
+ const lines = [head, ""];
50477
+ for (const r of results) {
50478
+ const gap = r.gap ? ` \u2014 ${r.gap}` : "";
50479
+ const win = r.winner ? ` [A/B ${r.winner}]` : "";
50480
+ lines.push(`- ${r.label} (${r.id}): ${r.verdict} after ${r.rounds} round(s)${win}${gap}`);
50481
+ }
50482
+ return lines.join("\n");
50483
+ }
50484
+ var init_events3 = __esm({
50485
+ "src/cli/gauntlet/events.ts"() {
50486
+ "use strict";
50487
+ }
50488
+ });
50489
+
50490
+ // src/cli/gauntlet/blind.ts
50491
+ function assignBlindLabels(items, rand = Math.random) {
50492
+ if (rand() < 0.5) {
50493
+ return { A: items[0], B: items[1], firstLabel: "A" };
50494
+ }
50495
+ return { A: items[1], B: items[0], firstLabel: "B" };
50496
+ }
50497
+ function qualityBarSection(bars, rand = Math.random) {
50498
+ if (!bars || bars.length === 0) return "";
50499
+ if (bars.length === 1) {
50500
+ return [
50501
+ "## Quality bar (blind)",
50502
+ "Inspect this reference on disk. PASS only if the work in Scope meets or beats it.",
50503
+ "Do not assume the new files are better \u2014 compare concretely.",
50504
+ `- ${bars[0]}`
50505
+ ].join("\n");
50506
+ }
50507
+ const pair = assignBlindLabels([bars[0], bars[1]], rand);
50508
+ return [
50509
+ "## Blind A/B",
50510
+ "Two unlabeled artifacts on disk. Compare them against the piece acceptance.",
50511
+ 'Do not assume which is newer or which you are "supposed" to prefer.',
50512
+ `A: ${pair.A}`,
50513
+ `B: ${pair.B}`,
50514
+ "Also emit: WINNER: A | B | TIE"
50515
+ ].join("\n");
50516
+ }
50517
+ function parseBlindWinner(text) {
50518
+ const m = WINNER_RE.exec(text);
50519
+ if (!m) return void 0;
50520
+ const v = m[1].toUpperCase();
50521
+ if (v === "A" || v === "B" || v === "TIE") return v;
50522
+ return void 0;
50523
+ }
50524
+ var WINNER_RE;
50525
+ var init_blind = __esm({
50526
+ "src/cli/gauntlet/blind.ts"() {
50527
+ "use strict";
50528
+ WINNER_RE = /\bWINNER:\s*(A|B|TIE)\b/i;
50529
+ }
50530
+ });
50531
+
50532
+ // src/cli/gauntlet/verdict.ts
50533
+ function parseGauntletVerdict(text, opts = {}) {
50534
+ const evidence = (opts.toolTraceCount ?? 0) > 0;
50535
+ if (opts.builderFailed) {
50536
+ return {
50537
+ kind: "GAP",
50538
+ gap: (opts.builderError ?? "builder failed").trim() || "builder failed",
50539
+ evidence
50540
+ };
50541
+ }
50542
+ const raw = (text ?? "").trim();
50543
+ if (!raw) {
50544
+ return { kind: "BLOCKED", gap: "critic produced no output", evidence };
50545
+ }
50546
+ const explicit = VERDICT_RE.exec(raw);
50547
+ let kind = explicit ? explicit[1].toUpperCase() : void 0;
50548
+ const reports = [...raw.matchAll(REPORT_STATUS_RE)].map((m) => m[1].toLowerCase());
50549
+ const anyFail = reports.includes("fail");
50550
+ const anyUnknown = reports.includes("unknown");
50551
+ const anyPass = reports.includes("pass");
50552
+ if (!kind) {
50553
+ if (anyFail) kind = "GAP";
50554
+ else if (anyPass && !anyUnknown) kind = "PASS";
50555
+ else if (anyUnknown && !anyPass) kind = "BLOCKED";
50556
+ else kind = "BLOCKED";
50557
+ }
50558
+ const gapMatch = GAP_RE.exec(raw);
50559
+ let gap = gapMatch ? gapMatch[1].trim() : void 0;
50560
+ if (kind === "GAP" && !gap) {
50561
+ gap = anyFail ? "a verify-report check failed" : "critic named a gap without a GAP: line";
50562
+ }
50563
+ if (kind === "PASS" && !evidence) {
50564
+ return {
50565
+ kind: "GAP",
50566
+ gap: "unknown \u2260 pass: critic declared PASS without tool evidence",
50567
+ evidence: false
50568
+ };
50569
+ }
50570
+ if (kind === "PASS" && anyFail) {
50571
+ return {
50572
+ kind: "GAP",
50573
+ gap: gap ?? "verify-report contains a fail",
50574
+ evidence
50575
+ };
50576
+ }
50577
+ return { kind, ...gap ? { gap } : {}, evidence };
50578
+ }
50579
+ var VERDICT_RE, GAP_RE, REPORT_STATUS_RE;
50580
+ var init_verdict2 = __esm({
50581
+ "src/cli/gauntlet/verdict.ts"() {
50582
+ "use strict";
50583
+ VERDICT_RE = /\bVERDICT:\s*(PASS|GAP|BLOCKED)\b/i;
50584
+ GAP_RE = /\bGAP:\s*(.+)/i;
50585
+ REPORT_STATUS_RE = /^status:\s*(pass|fail|unknown)\b/gim;
50586
+ }
50587
+ });
50588
+
50589
+ // src/cli/gauntlet/prompts.ts
50590
+ function builderUserPrompt(piece, gap, briefing) {
50591
+ const parts = [
50592
+ `You are the BUILDER for one Gauntlet piece. Implement it on disk.`,
50593
+ `Do not spawn sub-agents. Stay inside Scope if provided.`
50594
+ ];
50595
+ if (briefing?.trim()) {
50596
+ parts.push("", "## Workspace briefing (do not treat as already-done work)", briefing.trim());
50597
+ }
50598
+ parts.push("", `## Piece: ${piece.label}`, piece.prompt.trim());
50599
+ if (piece.scope && piece.scope.length > 0) {
50600
+ parts.push("", "## Scope", ...piece.scope.map((s) => `- ${s}`));
50601
+ }
50602
+ if (piece.acceptance.length > 0) {
50603
+ parts.push("", "## Acceptance", ...piece.acceptance.map((a) => `- ${a}`));
50604
+ }
50605
+ if (gap && gap.trim()) {
50606
+ parts.push(
50607
+ "",
50608
+ "## Previous critic GAP (fix ONLY this)",
50609
+ gap.trim(),
50610
+ "Do not widen scope. Re-check the acceptance after the fix."
50611
+ );
50612
+ }
50613
+ parts.push("", "Return: files touched, what changed, residual risks.");
50614
+ return parts.join("\n");
50615
+ }
50616
+ function criticUserPrompt(piece, round, rand) {
50617
+ const parts = [
50618
+ `Round ${round}. Inspect the current tree against this piece.`,
50619
+ "Do not edit files. Do not take the builder's word \u2014 open files / run checks.",
50620
+ "",
50621
+ `## Piece: ${piece.label}`,
50622
+ piece.prompt.trim()
50623
+ ];
50624
+ if (piece.scope && piece.scope.length > 0) {
50625
+ parts.push("", "## Scope", ...piece.scope.map((s) => `- ${s}`));
50626
+ }
50627
+ if (piece.acceptance.length > 0) {
50628
+ parts.push("", "## Acceptance (check each)", ...piece.acceptance.map((a) => `- ${a}`));
50629
+ } else {
50630
+ parts.push(
50631
+ "",
50632
+ "## Acceptance",
50633
+ "- The piece prompt is satisfied by files on disk.",
50634
+ "- Relevant typecheck/tests pass when the project has them."
50635
+ );
50636
+ }
50637
+ const bar = qualityBarSection(piece.bar, rand);
50638
+ if (bar) parts.push("", bar);
50639
+ return parts.join("\n");
50640
+ }
50641
+ var GAUNTLET_CRITIC_SYSTEM;
50642
+ var init_prompts = __esm({
50643
+ "src/cli/gauntlet/prompts.ts"() {
50644
+ "use strict";
50645
+ init_blind();
50646
+ GAUNTLET_CRITIC_SYSTEM = [
50647
+ "You are a ruthless VERIFY critic in a Gauntlet Loop.",
50648
+ "Inspect the REAL files and commands on disk. Do not trust a builder self-report.",
50649
+ "You have no builder transcript \u2014 only the Goal, Scope, Acceptance, and the tree.",
50650
+ "OBSERVATION INTEGRITY: unknown \u2260 pass. EMPTY/degraded tools are not evidence.",
50651
+ "Name at most ONE biggest remaining gap. Do not write a laundry list.",
50652
+ "You may read files and run test/build commands via bash. Prefer targeted checks.",
50653
+ "",
50654
+ "End with BOTH:",
50655
+ "1. One <verify-report> block per acceptance criterion:",
50656
+ "<verify-report>",
50657
+ "check: <criterion text as given>",
50658
+ "status: pass | fail | unknown",
50659
+ "note: <one line of evidence (command or file + outcome)>",
50660
+ "</verify-report>",
50661
+ "2. A trailer on its own lines:",
50662
+ "VERDICT: PASS | GAP | BLOCKED",
50663
+ "GAP: <single biggest remaining gap; omit on PASS>",
50664
+ "3. If a Blind A/B section is present, also emit WINNER: A | B | TIE"
50665
+ ].join("\n");
50666
+ }
50667
+ });
50668
+
50669
+ // src/cli/gauntlet/schedule.ts
50670
+ function piecesCanRunInParallel(a, b) {
50671
+ return disjointScopeSets(a.scope, b.scope);
50672
+ }
50673
+ function scheduleWaves(pieces, maxParallel) {
50674
+ const cap3 = Math.max(1, maxParallel);
50675
+ const remaining = [...pieces];
50676
+ const waves = [];
50677
+ while (remaining.length > 0) {
50678
+ const wave = [];
50679
+ const leftover = [];
50680
+ for (const p3 of remaining) {
50681
+ if (wave.length >= cap3) {
50682
+ leftover.push(p3);
50683
+ continue;
50684
+ }
50685
+ if (wave.length === 0 || wave.every((w) => piecesCanRunInParallel(w, p3))) {
50686
+ wave.push(p3);
50687
+ } else {
50688
+ leftover.push(p3);
50689
+ }
50690
+ }
50691
+ waves.push(wave);
50692
+ remaining.splice(0, remaining.length, ...leftover);
50693
+ }
50694
+ return waves;
50695
+ }
50696
+ var init_schedule = __esm({
50697
+ "src/cli/gauntlet/schedule.ts"() {
50698
+ "use strict";
50699
+ init_dist();
50700
+ }
50701
+ });
50702
+
50703
+ // src/cli/gauntlet/loop.ts
50704
+ async function runGauntletLoop(args) {
50705
+ const { caps, deps } = args;
50706
+ const pieces = args.pieces.slice(0, caps.maxPieces);
50707
+ const started = (deps.now ?? Date.now)();
50708
+ const results = [];
50709
+ const emitProgress = (partial2) => {
50710
+ const progress = {
50711
+ ...partial2,
50712
+ elapsedMs: (deps.now ?? Date.now)() - started
50713
+ };
50714
+ deps.emit(gauntletProgressEvent(deps.sessionId, progress));
50715
+ deps.note?.("gauntlet.progress", progress);
50716
+ };
50717
+ const wall = caps.wallClockMs === void 0 ? DEFAULT_WALL_MS : caps.wallClockMs;
50718
+ const expired = () => wall > 0 && (deps.now ?? Date.now)() - started >= wall;
50719
+ const aborted2 = () => Boolean(deps.signal?.aborted);
50720
+ const stop = () => aborted2() || expired();
50721
+ const indexOf = (piece) => pieces.findIndex((p3) => p3.id === piece.id);
50722
+ const runOnePiece = async (piece) => {
50723
+ let last = {
50724
+ kind: "BLOCKED",
50725
+ gap: "no round ran",
50726
+ evidence: false
50727
+ };
50728
+ let gap;
50729
+ let winner;
50730
+ let rounds = 0;
50731
+ const i = Math.max(0, indexOf(piece));
50732
+ for (let round = 1; round <= caps.maxRounds; round++) {
50733
+ if (stop()) break;
50734
+ rounds = round;
50735
+ const phase2 = gap ? "repairing" : "building";
50736
+ emitProgress({
50737
+ phase: phase2,
50738
+ pieceId: piece.id,
50739
+ pieceLabel: piece.label,
50740
+ pieceIndex: i,
50741
+ pieceCount: pieces.length,
50742
+ round,
50743
+ maxRounds: caps.maxRounds
50744
+ });
50745
+ const built = await deps.runBuilder({
50746
+ piece,
50747
+ prompt: builderUserPrompt(piece, gap, deps.briefing),
50748
+ round
50749
+ });
50750
+ if (stop()) break;
50751
+ emitProgress({
50752
+ phase: "critiquing",
50753
+ pieceId: piece.id,
50754
+ pieceLabel: piece.label,
50755
+ pieceIndex: i,
50756
+ pieceCount: pieces.length,
50757
+ round,
50758
+ maxRounds: caps.maxRounds
50759
+ });
50760
+ const criticized = await deps.runCritic({
50761
+ piece,
50762
+ prompt: criticUserPrompt(piece, round),
50763
+ systemPrompt: GAUNTLET_CRITIC_SYSTEM,
50764
+ round
50765
+ });
50766
+ last = parseGauntletVerdict(criticized.result, {
50767
+ toolTraceCount: criticized.toolTraceCount ?? 0,
50768
+ builderFailed: !built.ok,
50769
+ builderError: built.error
50770
+ });
50771
+ winner = parseBlindWinner(criticized.result) ?? winner;
50772
+ emitProgress({
50773
+ phase: last.kind === "PASS" ? "settled" : last.kind === "BLOCKED" ? "blocked" : "repairing",
50774
+ pieceId: piece.id,
50775
+ pieceLabel: piece.label,
50776
+ pieceIndex: i,
50777
+ pieceCount: pieces.length,
50778
+ round,
50779
+ maxRounds: caps.maxRounds,
50780
+ verdict: last.kind,
50781
+ gap: last.gap,
50782
+ winner
50783
+ });
50784
+ if (last.kind === "PASS" || last.kind === "BLOCKED") break;
50785
+ gap = last.gap;
50786
+ }
50787
+ return {
50788
+ id: piece.id,
50789
+ label: piece.label,
50790
+ verdict: last,
50791
+ rounds,
50792
+ ...winner ? { winner } : {}
50793
+ };
50794
+ };
50795
+ for (const wave of scheduleWaves(pieces, caps.maxParallel)) {
50796
+ if (stop()) return finish(results, { cancelled: aborted2(), timedOut: expired() });
50797
+ if (wave.length === 1) {
50798
+ results.push(await runOnePiece(wave[0]));
50799
+ } else {
50800
+ const waveResults = await Promise.all(wave.map((p3) => runOnePiece(p3)));
50801
+ results.push(...waveResults);
50802
+ }
50803
+ if (stop()) return finish(results, { cancelled: aborted2(), timedOut: expired() });
50804
+ }
50805
+ return finish(results, { cancelled: aborted2(), timedOut: expired() });
50806
+ }
50807
+ function finish(results, flags) {
50808
+ const cancelled = flags.timedOut ? false : flags.cancelled;
50809
+ const timedOut = flags.timedOut;
50810
+ const settled = !cancelled && !timedOut && results.length > 0 && results.every((r) => r.verdict.kind === "PASS");
50811
+ return {
50812
+ settled,
50813
+ cancelled,
50814
+ timedOut,
50815
+ pieces: results,
50816
+ summary: formatGauntletSummary(
50817
+ results.map((r) => ({
50818
+ id: r.id,
50819
+ label: r.label,
50820
+ verdict: r.verdict.kind,
50821
+ rounds: r.rounds,
50822
+ gap: r.verdict.gap,
50823
+ winner: r.winner
50824
+ })),
50825
+ { timedOut, cancelled }
50826
+ )
50827
+ };
50828
+ }
50829
+ var init_loop = __esm({
50830
+ "src/cli/gauntlet/loop.ts"() {
50831
+ "use strict";
50832
+ init_blind();
50833
+ init_policy();
50834
+ init_verdict2();
50835
+ init_prompts();
50836
+ init_schedule();
50837
+ init_events3();
50838
+ }
50839
+ });
50840
+
50841
+ // src/cli/gauntlet/run.ts
50842
+ var run_exports = {};
50843
+ __export(run_exports, {
50844
+ runHeadlessGauntlet: () => runHeadlessGauntlet
50845
+ });
50846
+ async function runHeadlessGauntlet(opts, provider, model) {
50847
+ const sessionId2 = opts.resumeSessionId ?? crypto.randomUUID();
50848
+ const spine = await openHeadlessSpine({
50849
+ sessionId: sessionId2,
50850
+ mode: opts.mode,
50851
+ profile: opts.profile,
50852
+ workspace: process.cwd()
50853
+ });
50854
+ emitEvent(sessionStartedEvent(spine));
50855
+ const seeded = await seedHeadlessModelHistory(spine, opts.history);
50856
+ if (opts.task) spine.userMessage(opts.task);
50857
+ spine.note("gauntlet.start", { goal: opts.task.slice(0, 200) });
50858
+ const abort = new AbortController();
50859
+ const onSigint = () => {
50860
+ emitEvent({ type: "log", message: "[gauntlet] SIGINT \u2014 cancelling" });
50861
+ abort.abort();
50862
+ };
50863
+ process.once("SIGINT", onSigint);
50864
+ const wallMs = resolveGauntletCaps().wallClockMs ?? 0;
50865
+ const wallTimer = wallMs > 0 ? setTimeout(() => {
50866
+ emitEvent({ type: "log", message: "[gauntlet] wall clock \u2014 aborting tentacles" });
50867
+ abort.abort();
50868
+ }, wallMs) : void 0;
50869
+ const cwd = process.cwd();
50870
+ const audit = new AuditLogger();
50871
+ const createSubAgentContext = createKrakenSubAgentContextFactory({
50872
+ root: cwd,
50873
+ audit,
50874
+ sessionId: sessionId2,
50875
+ provider,
50876
+ model
50877
+ });
50878
+ const deps = {
50879
+ createSubAgentContext,
50880
+ allowWorktree: false,
50881
+ harnessFactory: (config2) => {
50882
+ const harness = new AgentHarness(config2);
50883
+ return {
50884
+ async *run() {
50885
+ for await (const ev of harness.run()) {
50886
+ if (ev.type === "thinking_delta" || ev.type === "tool_execution_start" || ev.type === "tool_execution_end") {
50887
+ emitEvent(ev);
50888
+ spine.observe(ev);
50889
+ }
50890
+ yield ev;
50891
+ }
50892
+ },
50893
+ cancel: () => harness.cancel()
50894
+ };
50895
+ }
50896
+ };
50897
+ const emit = (event) => {
50898
+ if (opts.output === "json") emitEvent(event);
50899
+ else if (typeof event.message === "string") {
50900
+ process.stderr.write(`${event.message}
50901
+ `);
50902
+ }
50903
+ };
50904
+ emitEvent({ type: "agent_start", model, provider, role: "gauntlet" });
50905
+ emit({ type: "log", message: "[gauntlet] host loop \u2014 builder/critic rounds, parent cannot write" });
50906
+ const caps = resolveGauntletCaps();
50907
+ const workspace = buildWorkspaceSummary(cwd, { maxChars: 2500 });
50908
+ const historyNote = formatHistoryNote(seeded.history);
50909
+ emitEvent(
50910
+ gauntletProgressEvent(sessionId2, {
50911
+ phase: "decomposing",
50912
+ pieceId: "",
50913
+ pieceLabel: "Goal",
50914
+ pieceIndex: 0,
50915
+ pieceCount: 1,
50916
+ round: 0,
50917
+ maxRounds: caps.maxRounds,
50918
+ elapsedMs: 0
50919
+ })
50920
+ );
50921
+ try {
50922
+ const decomposed = await decomposeGoal({
50923
+ goal: opts.task,
50924
+ maxPieces: caps.maxPieces,
50925
+ workspace,
50926
+ historyNote,
50927
+ complete: (req) => gauntletComplete(req, { provider, model, signal: abort.signal })
50928
+ });
50929
+ spine.note("gauntlet.decompose", {
50930
+ source: decomposed.source,
50931
+ count: decomposed.pieces.length,
50932
+ ...decomposed.error ? { error: decomposed.error.slice(0, 240) } : {}
50933
+ });
50934
+ emit({
50935
+ type: "log",
50936
+ message: `[gauntlet] ${decomposed.pieces.length} piece(s) from ${decomposed.source}${decomposed.error ? ` (${decomposed.error.slice(0, 80)})` : ""}`
50937
+ });
50938
+ const result = await runGauntletLoop({
50939
+ pieces: decomposed.pieces,
50940
+ caps,
50941
+ deps: {
50942
+ sessionId: sessionId2,
50943
+ signal: abort.signal,
50944
+ emit,
50945
+ briefing: workspace.slice(0, 1200),
50946
+ note: (text, data) => spine.note(text, data),
50947
+ runBuilder: async ({ piece, prompt, round }) => {
50948
+ emit({
50949
+ type: "log",
50950
+ message: `[gauntlet] ${piece.id} round ${round}/${caps.maxRounds} builder`
50951
+ });
50952
+ const tent = await runTentacle({
50953
+ deps,
50954
+ agent: "general",
50955
+ thoroughness: "medium",
50956
+ parentCwd: cwd,
50957
+ sessionId: sessionId2,
50958
+ signal: abort.signal,
50959
+ args: {
50960
+ description: `gauntlet-builder ${piece.id} r${round}`,
50961
+ prompt,
50962
+ ...piece.scope ? { scope: piece.scope } : {},
50963
+ ...piece.acceptance.length > 0 ? { acceptance: piece.acceptance } : {}
50964
+ }
50965
+ });
50966
+ if (!tent.ok) return { ok: false, result: "", error: tent.error };
50967
+ return {
50968
+ ok: true,
50969
+ result: tent.result,
50970
+ toolTraceCount: tent.toolTrace?.length ?? 0
50971
+ };
50972
+ },
50973
+ runCritic: async ({ piece, prompt, systemPrompt, round }) => {
50974
+ emit({
50975
+ type: "log",
50976
+ message: `[gauntlet] ${piece.id} round ${round}/${caps.maxRounds} critic`
50977
+ });
50978
+ const tent = await runTentacle({
50979
+ deps,
50980
+ agent: "verify",
50981
+ thoroughness: "medium",
50982
+ parentCwd: cwd,
50983
+ sessionId: sessionId2,
50984
+ signal: abort.signal,
50985
+ systemPromptOverride: systemPrompt,
50986
+ args: {
50987
+ description: `gauntlet-critic ${piece.id} r${round}`,
50988
+ prompt,
50989
+ ...piece.scope ? { scope: piece.scope } : {},
50990
+ ...piece.acceptance.length > 0 ? { acceptance: piece.acceptance } : {}
50991
+ }
50992
+ });
50993
+ if (!tent.ok) {
50994
+ return { ok: false, result: tent.error, error: tent.error, toolTraceCount: 0 };
50995
+ }
50996
+ return {
50997
+ ok: true,
50998
+ result: tent.result,
50999
+ toolTraceCount: tent.toolTrace?.length ?? 0
51000
+ };
51001
+ }
51002
+ }
51003
+ });
51004
+ emitEvent({ type: "message_start", role: "assistant" });
51005
+ emitEvent({ type: "message_delta", delta: result.summary });
51006
+ emitEvent({ type: "message_end", totalLength: result.summary.length });
51007
+ emitEvent({
51008
+ type: "agent_end",
51009
+ reason: result.cancelled || result.timedOut ? "cancelled" : result.settled ? "completed" : "error"
51010
+ });
51011
+ if (result.timedOut) {
51012
+ emit({ type: "log", message: `[gauntlet] wall clock (${Math.round(wallMs / 6e4)}m)` });
51013
+ await spine.interrupt("gauntlet-wall-clock");
51014
+ return 1;
51015
+ }
51016
+ if (result.cancelled) {
51017
+ await spine.interrupt("gauntlet-cancelled");
51018
+ return 1;
51019
+ }
51020
+ await spine.close(result.settled ? "gauntlet-pass" : "gauntlet-incomplete");
51021
+ return result.settled ? 0 : 3;
51022
+ } catch (err) {
51023
+ const msg = err instanceof Error ? err.message : String(err);
51024
+ emitEvent({ type: "error", severity: "fatal", message: msg, code: "gauntlet" });
51025
+ await spine.interrupt(`gauntlet-error: ${msg}`).catch(() => void 0);
51026
+ return 2;
51027
+ } finally {
51028
+ if (wallTimer) clearTimeout(wallTimer);
51029
+ process.removeListener("SIGINT", onSigint);
51030
+ }
51031
+ }
51032
+ var init_run = __esm({
51033
+ "src/cli/gauntlet/run.ts"() {
51034
+ "use strict";
51035
+ init_harness();
51036
+ init_headless();
51037
+ init_headlessSpine();
51038
+ init_auditLogger();
51039
+ init_toolRegistry();
51040
+ init_taskTool();
51041
+ init_complete();
51042
+ init_decompose();
51043
+ init_events3();
51044
+ init_loop();
51045
+ init_policy();
51046
+ init_workspaceSummary();
51047
+ }
51048
+ });
51049
+
50185
51050
  // src/cli/triggerLock.ts
50186
51051
  var triggerLock_exports = {};
50187
51052
  __export(triggerLock_exports, {
@@ -50956,7 +51821,7 @@ function startPermissionMcpServer(opts) {
50956
51821
  }
50957
51822
  return { error: { code: -32601, message: `method not found: ${method}` } };
50958
51823
  }
50959
- async function callApprove(args, timeoutMs) {
51824
+ async function callApprove(args, timeoutMs2) {
50960
51825
  const toolName = typeof args.tool_name === "string" ? args.tool_name : "";
50961
51826
  if (!toolName) {
50962
51827
  return { error: { code: -32602, message: "approve requires a string tool_name" } };
@@ -50975,7 +51840,7 @@ function startPermissionMcpServer(opts) {
50975
51840
  toolUseId: typeof args.tool_use_id === "string" ? args.tool_use_id : void 0,
50976
51841
  suggestions
50977
51842
  },
50978
- { requestTimeoutMs: timeoutMs }
51843
+ { requestTimeoutMs: timeoutMs2 }
50979
51844
  );
50980
51845
  const result = res.behavior === "allow" ? {
50981
51846
  behavior: "allow",
@@ -50995,7 +51860,7 @@ function startPermissionMcpServer(opts) {
50995
51860
  );
50996
51861
  }
50997
51862
  }
50998
- async function callAskUser(args, timeoutMs) {
51863
+ async function callAskUser(args, timeoutMs2) {
50999
51864
  const question = typeof args.question === "string" ? args.question : "";
51000
51865
  if (!question) {
51001
51866
  return { error: { code: -32602, message: "ask_user requires a string question" } };
@@ -51013,7 +51878,7 @@ function startPermissionMcpServer(opts) {
51013
51878
  choices,
51014
51879
  context
51015
51880
  },
51016
- { requestTimeoutMs: timeoutMs }
51881
+ { requestTimeoutMs: timeoutMs2 }
51017
51882
  );
51018
51883
  const text = res.behavior === "allow" && typeof res.answer === "string" ? res.answer : res.message ?? "No answer was given \u2014 use your best judgment.";
51019
51884
  return textResult(text);
@@ -55861,8 +56726,8 @@ async function readPackageScripts(cwd = process.cwd()) {
55861
56726
  return {};
55862
56727
  }
55863
56728
  }
55864
- function buildNativeCriteria(commands, timeoutMs) {
55865
- const pack = codingCriteriaPack({ ...commands, commandTimeoutMs: timeoutMs });
56729
+ function buildNativeCriteria(commands, timeoutMs2) {
56730
+ const pack = codingCriteriaPack({ ...commands, commandTimeoutMs: timeoutMs2 });
55866
56731
  return pack.criteria.filter(
55867
56732
  (c) => !(c.required && (!c.check || c.check.kind === "none"))
55868
56733
  );
@@ -56164,7 +57029,7 @@ ${detail}
56164
57029
  let settled = false;
56165
57030
  const askTimeoutMs = askUserTimeoutMs();
56166
57031
  let cancelAskTimeout = () => void 0;
56167
- const finish = (ok, note) => {
57032
+ const finish2 = (ok, note) => {
56168
57033
  if (settled) return;
56169
57034
  settled = true;
56170
57035
  cancelAskTimeout();
@@ -56173,7 +57038,7 @@ ${detail}
56173
57038
  resolve3(ok);
56174
57039
  };
56175
57040
  cancelAskTimeout = armPickerTimeout(
56176
- () => finish(
57041
+ () => finish2(
56177
57042
  false,
56178
57043
  `[permission] ask for "${req.toolName}" timed out after ${Math.round(
56179
57044
  askTimeoutMs / 1e3
@@ -56202,12 +57067,12 @@ ${detail}
56202
57067
  onAnswer: (value) => {
56203
57068
  const v = value.trim().toLowerCase();
56204
57069
  if (v === "deny" || v.startsWith("deny")) {
56205
- finish(false);
57070
+ finish2(false);
56206
57071
  return;
56207
57072
  }
56208
57073
  if (v === "always-tool" || v.includes("always") && v.includes("tool")) {
56209
57074
  grantSessionTool(req.toolName);
56210
- finish(
57075
+ finish2(
56211
57076
  true,
56212
57077
  `[permission] Granted "${req.toolName}" for this session (tool).`
56213
57078
  );
@@ -56216,19 +57081,19 @@ ${detail}
56216
57081
  if (v === "always-cat" || v.includes("always") && !v.includes("tool")) {
56217
57082
  for (const c of cats) grantSessionCategory(c);
56218
57083
  grantSessionTool(req.toolName);
56219
- finish(
57084
+ finish2(
56220
57085
  true,
56221
57086
  `[permission] Granted ${catLabel} (+ ${req.toolName}) for this session.`
56222
57087
  );
56223
57088
  return;
56224
57089
  }
56225
57090
  if (v === "allow" || v.startsWith("allow") || v === "yes" || v === "y" || v === "1") {
56226
- finish(true);
57091
+ finish2(true);
56227
57092
  return;
56228
57093
  }
56229
- finish(false);
57094
+ finish2(false);
56230
57095
  },
56231
- onCancel: () => finish(false)
57096
+ onCancel: () => finish2(false)
56232
57097
  });
56233
57098
  });
56234
57099
  }
@@ -56547,7 +57412,7 @@ _(${req.context})_` : "") + "\n\u2192 scegli dalla lista (il turno continua dopo
56547
57412
  let settled = false;
56548
57413
  const askTimeoutMs = askUserTimeoutMs();
56549
57414
  let cancelAskTimeout = () => void 0;
56550
- const finish = (value) => {
57415
+ const finish2 = (value) => {
56551
57416
  if (settled) return;
56552
57417
  settled = true;
56553
57418
  cancelAskTimeout();
@@ -56563,7 +57428,7 @@ _(${req.context})_` : "") + "\n\u2192 scegli dalla lista (il turno continua dopo
56563
57428
  )}s \u2014 proseguo con assunzione documentata (ZELARI_ASK_USER_TIMEOUT_MS).`,
56564
57429
  Date.now()
56565
57430
  );
56566
- finish(null);
57431
+ finish2(null);
56567
57432
  },
56568
57433
  askTimeoutMs
56569
57434
  );
@@ -56571,8 +57436,8 @@ _(${req.context})_` : "") + "\n\u2192 scegli dalla lista (il turno continua dopo
56571
57436
  kind: "clarification",
56572
57437
  title: req.question,
56573
57438
  items: choices.map((c) => ({ value: c, label: c })),
56574
- onAnswer: (value) => finish(value),
56575
- onCancel: () => finish(null)
57439
+ onAnswer: (value) => finish2(value),
57440
+ onCancel: () => finish2(null)
56576
57441
  });
56577
57442
  }) : void 0;
56578
57443
  const onPermissionAsk = setPicker2 ? createPermissionAskHandler({
@@ -57361,7 +58226,7 @@ _(${req.context})_` : "") + "\n\u2192 scegli dalla lista (il membro riprende dop
57361
58226
  let settled = false;
57362
58227
  const askTimeoutMs = askUserTimeoutMs();
57363
58228
  let cancelAskTimeout = () => void 0;
57364
- const finish = (value) => {
58229
+ const finish2 = (value) => {
57365
58230
  if (settled) return;
57366
58231
  settled = true;
57367
58232
  cancelAskTimeout();
@@ -57377,7 +58242,7 @@ _(${req.context})_` : "") + "\n\u2192 scegli dalla lista (il membro riprende dop
57377
58242
  )}s \u2014 il membro prosegue con assunzione documentata (ZELARI_ASK_USER_TIMEOUT_MS).`,
57378
58243
  Date.now()
57379
58244
  );
57380
- finish(null);
58245
+ finish2(null);
57381
58246
  },
57382
58247
  askTimeoutMs
57383
58248
  );
@@ -57385,8 +58250,8 @@ _(${req.context})_` : "") + "\n\u2192 scegli dalla lista (il membro riprende dop
57385
58250
  kind: "clarification",
57386
58251
  title: req.question,
57387
58252
  items: choices.map((c) => ({ value: c, label: c })),
57388
- onAnswer: (value) => finish(value),
57389
- onCancel: () => finish(null)
58253
+ onAnswer: (value) => finish2(value),
58254
+ onCancel: () => finish2(null)
57390
58255
  });
57391
58256
  }) : void 0;
57392
58257
  let phaseRunMode = overrides.runMode ?? (workPhase === "plan" ? "design-phase" : "implementation");
@@ -57530,7 +58395,7 @@ _(${req.context})_` : "") + "\n\u2192 scegli dalla lista (il membro riprende dop
57530
58395
  Date.now()
57531
58396
  );
57532
58397
  let settled = false;
57533
- const finish = (value) => {
58398
+ const finish2 = (value) => {
57534
58399
  if (settled) return;
57535
58400
  settled = true;
57536
58401
  setPicker2(null);
@@ -57540,8 +58405,8 @@ _(${req.context})_` : "") + "\n\u2192 scegli dalla lista (il membro riprende dop
57540
58405
  kind: "clarification",
57541
58406
  title: req.question,
57542
58407
  items: choices.map((c) => ({ value: c, label: c })),
57543
- onAnswer: (value) => finish(value),
57544
- onCancel: () => finish(null)
58408
+ onAnswer: (value) => finish2(value),
58409
+ onCancel: () => finish2(null)
57545
58410
  });
57546
58411
  }) : void 0
57547
58412
  })) {
@@ -58146,7 +59011,7 @@ function usePermissionBroker(opts) {
58146
59011
  }
58147
59012
  let settled = false;
58148
59013
  let cancelWaitTimeout;
58149
- const finish = (value) => {
59014
+ const finish2 = (value) => {
58150
59015
  if (settled) return;
58151
59016
  settled = true;
58152
59017
  cancelWaitTimeout?.();
@@ -58162,7 +59027,7 @@ function usePermissionBroker(opts) {
58162
59027
  )}s \u2014 richiesta annullata.`,
58163
59028
  Date.now()
58164
59029
  );
58165
- finish(null);
59030
+ finish2(null);
58166
59031
  }, waitTimeoutMs);
58167
59032
  appendSystem(
58168
59033
  setMessages,
@@ -58174,8 +59039,8 @@ function usePermissionBroker(opts) {
58174
59039
  kind: "clarification",
58175
59040
  title: req.question,
58176
59041
  items: choices.map((c) => ({ value: c, label: c })),
58177
- onAnswer: (value) => finish(value),
58178
- onCancel: () => finish(null)
59042
+ onAnswer: (value) => finish2(value),
59043
+ onCancel: () => finish2(null)
58179
59044
  });
58180
59045
  });
58181
59046
  void startPermissionBroker(socketPath, {
@@ -62280,7 +63145,7 @@ function verifierReviewEnabled(selection = loadVerifierModelSelection(), env = p
62280
63145
  if (v === "1" || v === "true" || v === "on") return true;
62281
63146
  return selection.mode === "fixed";
62282
63147
  }
62283
- function makeVerifierCallModel(loadStream, identity, timeoutMs = 12e4) {
63148
+ function makeVerifierCallModel(loadStream, identity, timeoutMs2 = 12e4) {
62284
63149
  return async ({ system, user }) => {
62285
63150
  const stream = await loadStream(identity.provider, identity.model);
62286
63151
  if (!stream) {
@@ -62294,7 +63159,7 @@ function makeVerifierCallModel(loadStream, identity, timeoutMs = 12e4) {
62294
63159
  model: identity.model,
62295
63160
  provider: identity.provider,
62296
63161
  tools: [],
62297
- signal: AbortSignal.timeout(timeoutMs)
63162
+ signal: AbortSignal.timeout(timeoutMs2)
62298
63163
  });
62299
63164
  return { text, provider: identity.provider, model: identity.model };
62300
63165
  };
@@ -62451,6 +63316,18 @@ ${err.stack}` : "";
62451
63316
  if (opts.krakenGraph) {
62452
63317
  return runHeadlessKrakenGraph(opts, provider, model);
62453
63318
  }
63319
+ const { shouldRunGauntletHostLoop: shouldRunGauntletHostLoop2 } = await Promise.resolve().then(() => (init_policy(), policy_exports));
63320
+ if (shouldRunGauntletHostLoop2(opts)) {
63321
+ const { runHeadlessGauntlet: runHeadlessGauntlet2 } = await Promise.resolve().then(() => (init_run(), run_exports));
63322
+ return runHeadlessGauntlet2(opts, provider, model);
63323
+ }
63324
+ if (opts.gauntlet) {
63325
+ const why = opts.krakenGraph ? "kraken-graph owns dispatch" : (opts.phase ?? "build") === "plan" ? "PLAN is already write-stripped; host loop is BUILD-only" : "mode is not kraken-build";
63326
+ const line = `[gauntlet] flag set but host loop skipped (${why})`;
63327
+ if (opts.output === "json") emitEvent({ type: "log", message: line });
63328
+ else process.stderr.write(`[zelari-code --headless] ${line}
63329
+ `);
63330
+ }
62454
63331
  if (mode === "zelari") {
62455
63332
  return runHeadlessZelari(opts, provider, model, providerStream);
62456
63333
  }
@@ -62667,6 +63544,7 @@ async function runHeadlessSingle(opts, provider, model, providerStream) {
62667
63544
  resetKrakenTurnMetrics();
62668
63545
  const { registry: toolRegistry } = createBuiltinToolRegistry({
62669
63546
  planMode: planModeFromOpts(opts),
63547
+ gauntletParent: Boolean(opts.gauntlet) && !planModeFromOpts(opts),
62670
63548
  // Fase 1 (ADR-0020): anchor tentacles to the provider/model THIS run
62671
63549
  // resolved (--provider/--model opts or Desktop's selector), mirroring
62672
63550
  // what the kraken-graph path already does for its executor.