zelari-code 2.34.0 → 2.34.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/dist/cli/councilDispatcher.js +1 -0
  2. package/dist/cli/councilDispatcher.js.map +1 -1
  3. package/dist/cli/headless/liveTurnAbort.js +67 -0
  4. package/dist/cli/headless/liveTurnAbort.js.map +1 -0
  5. package/dist/cli/headless/runOneTurn.js +5 -1
  6. package/dist/cli/headless/runOneTurn.js.map +1 -1
  7. package/dist/cli/headless.js.map +1 -1
  8. package/dist/cli/main.bundled.js +819 -401
  9. package/dist/cli/main.bundled.js.map +4 -4
  10. package/dist/cli/runHeadless.js +55 -7
  11. package/dist/cli/runHeadless.js.map +1 -1
  12. package/dist/cli/serve/askUserBridge.js +59 -0
  13. package/dist/cli/serve/askUserBridge.js.map +1 -0
  14. package/dist/cli/serve/harnessServer.js +14 -2
  15. package/dist/cli/serve/harnessServer.js.map +1 -1
  16. package/dist/cli/serve/permissionBridge.js +64 -8
  17. package/dist/cli/serve/permissionBridge.js.map +1 -1
  18. package/dist/cli/serve/sessionControl.js +5 -3
  19. package/dist/cli/serve/sessionControl.js.map +1 -1
  20. package/dist/cli/toolRegistry.js +32 -17
  21. package/dist/cli/toolRegistry.js.map +1 -1
  22. package/dist/cli/tools/krakenModel.js +18 -0
  23. package/dist/cli/tools/krakenModel.js.map +1 -1
  24. package/dist/cli/tools/taskTool.js +59 -9
  25. package/dist/cli/tools/taskTool.js.map +1 -1
  26. package/dist/cli/utils/doctor.js +24 -3
  27. package/dist/cli/utils/doctor.js.map +1 -1
  28. package/dist/cli/utils/fixPath.js +18 -14
  29. package/dist/cli/utils/fixPath.js.map +1 -1
  30. package/dist/cli/utils/streamScrub.js +5 -3
  31. package/dist/cli/utils/streamScrub.js.map +1 -1
  32. package/dist/cli/zelariMission.js +14 -0
  33. package/dist/cli/zelariMission.js.map +1 -1
  34. package/package.json +2 -2
@@ -28372,6 +28372,136 @@ var init_textLoopDetect = __esm({
28372
28372
  }
28373
28373
  });
28374
28374
 
28375
+ // packages/core/dist/agents/council/outputCleaning.js
28376
+ function extractBalancedJsonObject(s) {
28377
+ const start = s.indexOf("{");
28378
+ if (start < 0)
28379
+ return null;
28380
+ let depth = 0;
28381
+ let inString = false;
28382
+ let escape = false;
28383
+ for (let i = start; i < s.length; i++) {
28384
+ const ch = s[i];
28385
+ if (inString) {
28386
+ if (escape) {
28387
+ escape = false;
28388
+ continue;
28389
+ }
28390
+ if (ch === "\\") {
28391
+ escape = true;
28392
+ continue;
28393
+ }
28394
+ if (ch === '"')
28395
+ inString = false;
28396
+ continue;
28397
+ }
28398
+ if (ch === '"') {
28399
+ inString = true;
28400
+ continue;
28401
+ }
28402
+ if (ch === "{")
28403
+ depth++;
28404
+ else if (ch === "}") {
28405
+ depth--;
28406
+ if (depth === 0)
28407
+ return s.slice(start, i + 1);
28408
+ }
28409
+ }
28410
+ return null;
28411
+ }
28412
+ function parseClarificationRequest(text) {
28413
+ const start = text.indexOf(QUESTION_MARKER);
28414
+ if (start < 0)
28415
+ return null;
28416
+ const rest = text.slice(start + QUESTION_MARKER.length);
28417
+ const end = rest.indexOf(QUESTION_END_MARKER);
28418
+ const block = end >= 0 ? rest.slice(0, end) : rest;
28419
+ const cleaned = block.replace(/```json\n?/g, "").replace(/```\n?/g, "").trim();
28420
+ const jsonText = extractBalancedJsonObject(cleaned) ?? (() => {
28421
+ const objStart = cleaned.indexOf("{");
28422
+ const objEnd = cleaned.lastIndexOf("}");
28423
+ return objStart >= 0 && objEnd > objStart ? cleaned.slice(objStart, objEnd + 1) : cleaned;
28424
+ })();
28425
+ try {
28426
+ const parsed = JSON.parse(jsonText);
28427
+ if (typeof parsed.question !== "string" || !parsed.question.trim())
28428
+ return null;
28429
+ return {
28430
+ question: parsed.question.trim(),
28431
+ choices: Array.isArray(parsed.choices) ? parsed.choices.filter((c) => typeof c === "string" && c.trim().length > 0).map((c) => c.trim()) : void 0,
28432
+ context: typeof parsed.context === "string" ? parsed.context.trim() : void 0
28433
+ };
28434
+ } catch {
28435
+ return null;
28436
+ }
28437
+ }
28438
+ function hasInteractiveClarification(text) {
28439
+ const c = parseClarificationRequest(text);
28440
+ return !!(c && c.choices && c.choices.length >= 2);
28441
+ }
28442
+ function stripQuestionBlocks(text) {
28443
+ let out = "";
28444
+ let rest = text;
28445
+ while (true) {
28446
+ const start = rest.indexOf(QUESTION_MARKER);
28447
+ if (start < 0) {
28448
+ out += rest;
28449
+ break;
28450
+ }
28451
+ out += rest.slice(0, start);
28452
+ const afterMarker = rest.slice(start + QUESTION_MARKER.length);
28453
+ const trimmed = afterMarker.replace(/^\s+/, "");
28454
+ if (!trimmed.startsWith("{")) {
28455
+ out += QUESTION_MARKER;
28456
+ rest = afterMarker;
28457
+ continue;
28458
+ }
28459
+ const endIdx = afterMarker.indexOf(QUESTION_END_MARKER);
28460
+ if (endIdx >= 0) {
28461
+ rest = afterMarker.slice(endIdx + QUESTION_END_MARKER.length);
28462
+ continue;
28463
+ }
28464
+ const json3 = extractBalancedJsonObject(trimmed);
28465
+ if (json3) {
28466
+ const jsonAt = afterMarker.indexOf(json3);
28467
+ rest = afterMarker.slice(jsonAt + json3.length);
28468
+ continue;
28469
+ }
28470
+ break;
28471
+ }
28472
+ return out.replace(/\n{3,}/g, "\n\n").trim();
28473
+ }
28474
+ function parseThinking(text) {
28475
+ const complete = text.match(/<think(?:ing)?>([\s\S]*?)<\/think(?:ing)?>/i);
28476
+ if (complete)
28477
+ return complete[1].trim();
28478
+ const open2 = text.match(/<think(?:ing)?>([\s\S]*)$/i);
28479
+ return open2 ? open2[1].trim() : "";
28480
+ }
28481
+ function cleanAgentContent(text, opts = {}) {
28482
+ const stripQuestion = opts.stripQuestion !== false;
28483
+ const stripThink = opts.stripThink !== false;
28484
+ let out = text;
28485
+ if (stripThink) {
28486
+ out = out.replace(/<think(?:ing)?>[\s\S]*?<\/think(?:ing)?>/gi, "").replace(/<think(?:ing)?>[\s\S]*$/gi, "").replace(/<\/think(?:ing)?>/gi, "");
28487
+ }
28488
+ out = out.replace(/<minimax:tool_call>[\s\S]*?<\/minimax:tool_call>/gi, "").replace(/<\/?minimax:tool_call>/gi, "").replace(/<tool_call>[\s\S]*?<\/tool_call>/gi, "").replace(/<\/?tool_call>/gi, "").replace(/<function_call>[\s\S]*?<\/function_call>/gi, "").replace(/<\/?function_call>/gi, "").replace(/<invoke\b[^>]*>[\s\S]*?<\/invoke>/gi, "").replace(/<\/invoke>/gi, "").replace(/<parameter\b[^>]*>[\s\S]*?<\/parameter>/gi, "").replace(/<\/parameter>/gi, "").replace(/\]\s*<\]\s*minimax\s*\[>\s*\[?<invoke\b[^>]*>[\s\S]*?<\/invoke>/gi, "").replace(/<minimax:tool_call>[\s\S]*$/gi, "").replace(/<tool_call>[\s\S]*$/gi, "").replace(/<function_call>[\s\S]*$/gi, "").replace(/<invoke\b[^>]*>[\s\S]*$/gi, "").replace(/\]\s*<\]\s*minimax\s*\[>[\s\S]*$/gi, "").replace(/^\s*\]\s*<\]\s*minimax\s*\[>.*$/gim, "").replace(/^\s*<\/?(?:tool_call|function_call|invoke|parameter|minimax:tool_call)\b[^>]*>\s*$/gim, "");
28489
+ if (stripQuestion) {
28490
+ out = stripQuestionBlocks(out);
28491
+ }
28492
+ out = out.replace(/\n{3,}/g, "\n\n").trim();
28493
+ return scrubProprietaryLeak(out);
28494
+ }
28495
+ var QUESTION_MARKER, QUESTION_END_MARKER;
28496
+ var init_outputCleaning = __esm({
28497
+ "packages/core/dist/agents/council/outputCleaning.js"() {
28498
+ "use strict";
28499
+ init_secrecyPolicy();
28500
+ QUESTION_MARKER = "---QUESTION---";
28501
+ QUESTION_END_MARKER = "---END---";
28502
+ }
28503
+ });
28504
+
28375
28505
  // packages/core/dist/core/AgentHarness.js
28376
28506
  function gateAdvice(hardLimit) {
28377
28507
  return hardLimit ? "Finalize now with the evidence already collected; no further tool calls will run this turn." : "Prioritize verification/repair actions (test, typecheck, build, read failures) or finalize honestly.";
@@ -28668,6 +28798,7 @@ var init_AgentHarness = __esm({
28668
28798
  init_requestSnapshot();
28669
28799
  init_textLoopDetect();
28670
28800
  init_ObserverBus();
28801
+ init_outputCleaning();
28671
28802
  init_textLoopDetect();
28672
28803
  TOOL_CALL_TRUNCATED_RECOVERY_MARKER = "[harness] Previous tool call was truncated";
28673
28804
  TOOL_CALL_TRUNCATED_RECOVERY_USER = `${TOOL_CALL_TRUNCATED_RECOVERY_MARKER} by the provider before completion (finish_reason=tool_calls but no complete tool_call arrived). Retry with a shorter payload or split the work into smaller tool calls.`;
@@ -29562,7 +29693,7 @@ ${cached2}`
29562
29693
  }
29563
29694
  pendingNativeTools.length = 0;
29564
29695
  }
29565
- const clarificationPause = /---QUESTION---/.test(turnText) && /"choices"\s*:\s*\[/.test(turnText);
29696
+ const clarificationPause = hasInteractiveClarification(turnText);
29566
29697
  if (clarificationPause) {
29567
29698
  finishRef.value = "stop";
29568
29699
  finishRef.clarificationRequested = true;
@@ -32128,101 +32259,36 @@ var init_types7 = __esm({
32128
32259
  }
32129
32260
  });
32130
32261
 
32131
- // packages/core/dist/agents/council/outputCleaning.js
32132
- function extractBalancedJsonObject(s) {
32133
- const start = s.indexOf("{");
32134
- if (start < 0)
32135
- return null;
32136
- let depth = 0;
32137
- let inString = false;
32138
- let escape = false;
32139
- for (let i = start; i < s.length; i++) {
32140
- const ch = s[i];
32141
- if (inString) {
32142
- if (escape) {
32143
- escape = false;
32144
- continue;
32145
- }
32146
- if (ch === "\\") {
32147
- escape = true;
32148
- continue;
32149
- }
32150
- if (ch === '"')
32151
- inString = false;
32152
- continue;
32153
- }
32154
- if (ch === '"') {
32155
- inString = true;
32156
- continue;
32157
- }
32158
- if (ch === "{")
32159
- depth++;
32160
- else if (ch === "}") {
32161
- depth--;
32162
- if (depth === 0)
32163
- return s.slice(start, i + 1);
32164
- }
32165
- }
32166
- return null;
32262
+ // packages/core/dist/agents/council/cancel.js
32263
+ function isCouncilCancelled(signal) {
32264
+ return signal?.aborted === true;
32167
32265
  }
32168
- function parseClarificationRequest(text) {
32169
- const start = text.indexOf(QUESTION_MARKER);
32170
- if (start < 0)
32171
- return null;
32172
- const rest = text.slice(start + QUESTION_MARKER.length);
32173
- const end = rest.indexOf(QUESTION_END_MARKER);
32174
- const block = end >= 0 ? rest.slice(0, end) : rest;
32175
- const cleaned = block.replace(/```json\n?/g, "").replace(/```\n?/g, "").trim();
32176
- const jsonText = extractBalancedJsonObject(cleaned) ?? (() => {
32177
- const objStart = cleaned.indexOf("{");
32178
- const objEnd = cleaned.lastIndexOf("}");
32179
- return objStart >= 0 && objEnd > objStart ? cleaned.slice(objStart, objEnd + 1) : cleaned;
32180
- })();
32181
- try {
32182
- const parsed = JSON.parse(jsonText);
32183
- if (typeof parsed.question !== "string" || !parsed.question.trim())
32184
- return null;
32185
- return {
32186
- question: parsed.question.trim(),
32187
- choices: Array.isArray(parsed.choices) ? parsed.choices.filter((c) => typeof c === "string" && c.trim().length > 0).map((c) => c.trim()) : void 0,
32188
- context: typeof parsed.context === "string" ? parsed.context.trim() : void 0
32189
- };
32190
- } catch {
32191
- return null;
32266
+ function bindHarnessAbort(harness, signal) {
32267
+ if (!signal)
32268
+ return () => void 0;
32269
+ if (signal.aborted) {
32270
+ harness.cancel();
32271
+ return () => void 0;
32192
32272
  }
32273
+ const onAbort = () => {
32274
+ harness.cancel();
32275
+ };
32276
+ signal.addEventListener("abort", onAbort);
32277
+ return () => {
32278
+ signal.removeEventListener("abort", onAbort);
32279
+ };
32193
32280
  }
32194
- function hasInteractiveClarification(text) {
32195
- const c = parseClarificationRequest(text);
32196
- return !!(c && c.choices && c.choices.length >= 2);
32197
- }
32198
- function parseThinking(text) {
32199
- const complete = text.match(/<think(?:ing)?>([\s\S]*?)<\/think(?:ing)?>/i);
32200
- if (complete)
32201
- return complete[1].trim();
32202
- const open2 = text.match(/<think(?:ing)?>([\s\S]*)$/i);
32203
- return open2 ? open2[1].trim() : "";
32204
- }
32205
- function cleanAgentContent(text, opts = {}) {
32206
- const stripQuestion = opts.stripQuestion !== false;
32207
- const stripThink = opts.stripThink !== false;
32208
- let out = text;
32209
- if (stripThink) {
32210
- out = out.replace(/<think(?:ing)?>[\s\S]*?<\/think(?:ing)?>/gi, "").replace(/<think(?:ing)?>[\s\S]*$/gi, "").replace(/<\/think(?:ing)?>/gi, "");
32211
- }
32212
- out = out.replace(/<minimax:tool_call>[\s\S]*?<\/minimax:tool_call>/gi, "").replace(/<\/?minimax:tool_call>/gi, "").replace(/<tool_call>[\s\S]*?<\/tool_call>/gi, "").replace(/<\/?tool_call>/gi, "").replace(/<function_call>[\s\S]*?<\/function_call>/gi, "").replace(/<\/?function_call>/gi, "").replace(/<invoke\b[^>]*>[\s\S]*?<\/invoke>/gi, "").replace(/<\/invoke>/gi, "").replace(/<parameter\b[^>]*>[\s\S]*?<\/parameter>/gi, "").replace(/<\/parameter>/gi, "").replace(/\]\s*<\]\s*minimax\s*\[>\s*\[?<invoke\b[^>]*>[\s\S]*?<\/invoke>/gi, "").replace(/<minimax:tool_call>[\s\S]*$/gi, "").replace(/<tool_call>[\s\S]*$/gi, "").replace(/<function_call>[\s\S]*$/gi, "").replace(/<invoke\b[^>]*>[\s\S]*$/gi, "").replace(/\]\s*<\]\s*minimax\s*\[>[\s\S]*$/gi, "").replace(/^\s*\]\s*<\]\s*minimax\s*\[>.*$/gim, "").replace(/^\s*<\/?(?:tool_call|function_call|invoke|parameter|minimax:tool_call)\b[^>]*>\s*$/gim, "");
32213
- if (stripQuestion) {
32214
- out = out.replace(/---QUESTION---[\s\S]*?---END---/g, "").replace(/---QUESTION---[\s\S]*$/g, "");
32281
+ async function* runHarnessWithAbort(harness, signal) {
32282
+ const unbind = bindHarnessAbort(harness, signal);
32283
+ try {
32284
+ yield* harness.run();
32285
+ } finally {
32286
+ unbind();
32215
32287
  }
32216
- out = out.replace(/\n{3,}/g, "\n\n").trim();
32217
- return scrubProprietaryLeak(out);
32218
32288
  }
32219
- var QUESTION_MARKER, QUESTION_END_MARKER;
32220
- var init_outputCleaning = __esm({
32221
- "packages/core/dist/agents/council/outputCleaning.js"() {
32289
+ var init_cancel = __esm({
32290
+ "packages/core/dist/agents/council/cancel.js"() {
32222
32291
  "use strict";
32223
- init_secrecyPolicy();
32224
- QUESTION_MARKER = "---QUESTION---";
32225
- QUESTION_END_MARKER = "---END---";
32226
32292
  }
32227
32293
  });
32228
32294
 
@@ -32531,6 +32597,8 @@ function buildRetryPrompt(missingToolNames) {
32531
32597
  return `You did not emit the required workspace tools: ${names}. Call ${names} NOW with concrete arguments. No prose. No search.`;
32532
32598
  }
32533
32599
  async function* runRetryTurnForMember(args) {
32600
+ if (isCouncilCancelled(args.signal))
32601
+ return [];
32534
32602
  const executableMissing = args.executableTools ? args.missingToolNames.filter((n) => args.executableTools.has(n)) : args.missingToolNames;
32535
32603
  if (executableMissing.length === 0) {
32536
32604
  return [];
@@ -32566,7 +32634,7 @@ async function* runRetryTurnForMember(args) {
32566
32634
  providerStream: (params) => args.providerStream(params)
32567
32635
  });
32568
32636
  const retryEmitted = [];
32569
- for await (const event of retryHarness.run()) {
32637
+ for await (const event of runHarnessWithAbort(retryHarness, args.signal)) {
32570
32638
  if (event.type === "tool_execution_start") {
32571
32639
  retryEmitted.push(event.toolName);
32572
32640
  }
@@ -32575,6 +32643,8 @@ async function* runRetryTurnForMember(args) {
32575
32643
  return retryEmitted;
32576
32644
  }
32577
32645
  async function* applyRetryIfMissing(args) {
32646
+ if (isCouncilCancelled(args.config.signal))
32647
+ return;
32578
32648
  if (args.check.ok)
32579
32649
  return;
32580
32650
  const missingToolNames = args.check.missing.map((m) => m.split(" ")[0]);
@@ -32607,7 +32677,8 @@ async function* applyRetryIfMissing(args) {
32607
32677
  toolRegistry: args.config.tools,
32608
32678
  providerStream: args.config.providerStream,
32609
32679
  runMode: args.config.runMode,
32610
- languageModule: args.languageModule
32680
+ languageModule: args.languageModule,
32681
+ signal: args.config.signal
32611
32682
  });
32612
32683
  for await (const event of retryGenerator) {
32613
32684
  if (event.type === "tool_execution_start") {
@@ -32626,6 +32697,7 @@ var init_retryTurn = __esm({
32626
32697
  "packages/core/dist/agents/council/retryTurn.js"() {
32627
32698
  "use strict";
32628
32699
  init_AgentHarness();
32700
+ init_cancel();
32629
32701
  init_toolSchemas();
32630
32702
  init_memberMessages();
32631
32703
  init_toolEmission();
@@ -32635,6 +32707,8 @@ var init_retryTurn = __esm({
32635
32707
 
32636
32708
  // packages/core/dist/agents/council/chairmanDelivery.js
32637
32709
  async function* applyCompletionRetry(args) {
32710
+ if (isCouncilCancelled(args.config.signal))
32711
+ return;
32638
32712
  const check2 = checkImplementationCompletion(args.emittedToolNames);
32639
32713
  if (check2.ok)
32640
32714
  return;
@@ -32664,7 +32738,8 @@ async function* applyCompletionRetry(args) {
32664
32738
  providerStream: args.config.providerStream,
32665
32739
  runMode: args.config.runMode,
32666
32740
  retryPrompt: buildImplementationVerifyRetryPrompt(retryTool),
32667
- languageModule: args.languageModule
32741
+ languageModule: args.languageModule,
32742
+ signal: args.config.signal
32668
32743
  });
32669
32744
  for await (const event of retryGenerator) {
32670
32745
  if (event.type === "tool_execution_start") {
@@ -32697,6 +32772,8 @@ ${blocks}
32697
32772
  Rules: animate ONLY transform and opacity. Replace any box-shadow / background / background-position / filter / color / border-color / width / height / grid-template-rows used in @keyframes or transitions with transform/opacity equivalents (e.g. render a glow via a pseudo-element that scales and fades). For every classList.add('x') in the script, add a matching '.x' CSS rule. Use read_file to see the exact lines, then edit_file. When the listed items are fixed, stop \u2014 no summary.`;
32698
32773
  }
32699
32774
  async function* applyImplementationWriteRetry(args) {
32775
+ if (isCouncilCancelled(args.config.signal))
32776
+ return;
32700
32777
  if (args.check.ok)
32701
32778
  return;
32702
32779
  if (!shouldRetryMember(["write_file"], 0))
@@ -32723,7 +32800,8 @@ async function* applyImplementationWriteRetry(args) {
32723
32800
  providerStream: args.config.providerStream,
32724
32801
  runMode: "implementation",
32725
32802
  retryPrompt: buildImplementationWriteRetryPrompt(args.userMessage),
32726
- languageModule: args.languageModule
32803
+ languageModule: args.languageModule,
32804
+ signal: args.config.signal
32727
32805
  });
32728
32806
  for await (const event of retryGenerator) {
32729
32807
  if (event.type === "tool_execution_start")
@@ -32751,6 +32829,8 @@ async function* runChairmanDeliveryLoop(args) {
32751
32829
  const zelariRoot = `${args.projectRoot}/.zelari`;
32752
32830
  let attempt = 0;
32753
32831
  while (attempt < maxAttempts) {
32832
+ if (isCouncilCancelled(args.config.signal))
32833
+ return false;
32754
32834
  const report = runImplementationVerification({
32755
32835
  projectRoot: args.projectRoot,
32756
32836
  zelariRoot
@@ -32793,7 +32873,8 @@ async function* runChairmanDeliveryLoop(args) {
32793
32873
  providerStream: args.config.providerStream,
32794
32874
  runMode: "implementation",
32795
32875
  retryPrompt: buildDeliveryFixPrompt(blocking, args.userMessage),
32796
- languageModule: args.languageModule
32876
+ languageModule: args.languageModule,
32877
+ signal: args.config.signal
32797
32878
  });
32798
32879
  for await (const event of fixGenerator) {
32799
32880
  if (event.type === "tool_execution_start")
@@ -32826,6 +32907,7 @@ var init_chairmanDelivery = __esm({
32826
32907
  init_runChecks();
32827
32908
  init_inlineJsAutofix();
32828
32909
  init_retryTurn();
32910
+ init_cancel();
32829
32911
  MAX_DELIVERY_ATTEMPTS = 2;
32830
32912
  }
32831
32913
  });
@@ -32887,6 +32969,8 @@ async function* runChairmanFixLoop(args) {
32887
32969
  let current = Array.from(args.violations.values());
32888
32970
  let attempt = 0;
32889
32971
  while (current.length > 0 && attempt < maxAttempts) {
32972
+ if (isCouncilCancelled(args.config.signal))
32973
+ return;
32890
32974
  attempt++;
32891
32975
  try {
32892
32976
  const fixGenerator = runRetryTurnForMember({
@@ -32907,7 +32991,8 @@ async function* runChairmanFixLoop(args) {
32907
32991
  providerStream: args.config.providerStream,
32908
32992
  runMode: "implementation",
32909
32993
  retryPrompt: buildMotionFixPrompt(current),
32910
- languageModule: args.languageModule
32994
+ languageModule: args.languageModule,
32995
+ signal: args.config.signal
32911
32996
  });
32912
32997
  for await (const event of fixGenerator) {
32913
32998
  if (event.type === "tool_execution_start")
@@ -32935,6 +33020,7 @@ var init_chairmanFixLoop = __esm({
32935
33020
  init_microGate();
32936
33021
  init_chairmanDelivery();
32937
33022
  init_retryTurn();
33023
+ init_cancel();
32938
33024
  }
32939
33025
  });
32940
33026
 
@@ -32976,6 +33062,17 @@ async function* runCouncilPure(userMessage, config2, callbacks = {}) {
32976
33062
  model: config2.model,
32977
33063
  provider: config2.provider ?? "minimax"
32978
33064
  };
33065
+ if (isCouncilCancelled(config2.signal)) {
33066
+ yield {
33067
+ type: "agent_end",
33068
+ id: crypto.randomUUID(),
33069
+ ts: Date.now(),
33070
+ sessionId: sessionId2,
33071
+ reason: "cancelled",
33072
+ durationMs: 0
33073
+ };
33074
+ return;
33075
+ }
32979
33076
  const emitMemberCost = (input) => {
32980
33077
  const usage = input.usage;
32981
33078
  const prompt = usage?.promptTokens ?? 0;
@@ -33005,6 +33102,8 @@ async function* runCouncilPure(userMessage, config2, callbacks = {}) {
33005
33102
  const oracle = agents.find((a) => a.id === "minos") ?? (config2.skipSpecialists ? getAgent("minos") : void 0);
33006
33103
  const chairman = agents.find((a) => a.id === "lucifer") ?? (config2.skipSpecialists ? getAgent("lucifer") : void 0);
33007
33104
  for (const agent of specialists) {
33105
+ if (isCouncilCancelled(config2.signal))
33106
+ break;
33008
33107
  if (completedIds.has(agent.id))
33009
33108
  continue;
33010
33109
  callbacks.onAgentStart?.(agent);
@@ -33050,7 +33149,7 @@ async function* runCouncilPure(userMessage, config2, callbacks = {}) {
33050
33149
  const emittedToolNames = [];
33051
33150
  const memberStart = Date.now();
33052
33151
  try {
33053
- for await (const event of harness.run()) {
33152
+ for await (const event of runHarnessWithAbort(harness, config2.signal)) {
33054
33153
  yield event;
33055
33154
  if (event.type === "tool_execution_start") {
33056
33155
  toolCalls += 1;
@@ -33072,7 +33171,7 @@ async function* runCouncilPure(userMessage, config2, callbacks = {}) {
33072
33171
  fullText = `Error: ${err instanceof Error ? err.message : "Unknown"}`;
33073
33172
  errored = true;
33074
33173
  }
33075
- if (isDesignPhase && !errored && !NON_RETRY_AGENTS.has(agent.id)) {
33174
+ if (isDesignPhase && !errored && !NON_RETRY_AGENTS.has(agent.id) && !isCouncilCancelled(config2.signal)) {
33076
33175
  const specialistCheck = enforceDesignPhaseToolEmissions(agent.id, emittedToolNames);
33077
33176
  yield* applyRetryIfMissing({
33078
33177
  agent,
@@ -33155,7 +33254,7 @@ async function* runCouncilPure(userMessage, config2, callbacks = {}) {
33155
33254
  }
33156
33255
  }
33157
33256
  }
33158
- if (oracle && !completedIds.has(oracle.id)) {
33257
+ if (oracle && !completedIds.has(oracle.id) && !isCouncilCancelled(config2.signal)) {
33159
33258
  callbacks.onAgentStart?.(oracle);
33160
33259
  const override = config2.agentModels?.[oracle.id];
33161
33260
  const effectiveProvider = override?.providerId ?? config2.provider ?? "minimax";
@@ -33204,7 +33303,7 @@ async function* runCouncilPure(userMessage, config2, callbacks = {}) {
33204
33303
  const emittedToolNames = [];
33205
33304
  const memberStart = Date.now();
33206
33305
  try {
33207
- for await (const event of harness.run()) {
33306
+ for await (const event of runHarnessWithAbort(harness, config2.signal)) {
33208
33307
  yield event;
33209
33308
  if (event.type === "tool_execution_start") {
33210
33309
  toolCalls += 1;
@@ -33226,7 +33325,7 @@ async function* runCouncilPure(userMessage, config2, callbacks = {}) {
33226
33325
  fullText = `Review error: ${err instanceof Error ? err.message : "Unknown"}`;
33227
33326
  errored = true;
33228
33327
  }
33229
- if (isDesignPhase && !errored) {
33328
+ if (isDesignPhase && !errored && !isCouncilCancelled(config2.signal)) {
33230
33329
  const oracleCheck = enforceDesignPhaseToolEmissions(oracle.id, emittedToolNames);
33231
33330
  yield* applyRetryIfMissing({
33232
33331
  agent: oracle,
@@ -33286,7 +33385,7 @@ async function* runCouncilPure(userMessage, config2, callbacks = {}) {
33286
33385
  });
33287
33386
  agentOutputs.push({ name: oracle.name, role: oracle.role, content: cleaned });
33288
33387
  }
33289
- if (chairman && !completedIds.has(chairman.id)) {
33388
+ if (chairman && !completedIds.has(chairman.id) && !isCouncilCancelled(config2.signal)) {
33290
33389
  callbacks.onSynthesisStart?.();
33291
33390
  callbacks.onAgentStart?.(chairman);
33292
33391
  const override = config2.agentModels?.[chairman.id];
@@ -33333,7 +33432,7 @@ async function* runCouncilPure(userMessage, config2, callbacks = {}) {
33333
33432
  let chairmanProjectRoot = parseProjectRootFromWorkspaceContext(config2.workspaceContext ?? "");
33334
33433
  const memberStart = Date.now();
33335
33434
  try {
33336
- for await (const event of chairmanHarness.run()) {
33435
+ for await (const event of runHarnessWithAbort(chairmanHarness, config2.signal)) {
33337
33436
  yield event;
33338
33437
  if (event.type === "tool_execution_start") {
33339
33438
  toolCalls += 1;
@@ -33397,7 +33496,7 @@ async function* runCouncilPure(userMessage, config2, callbacks = {}) {
33397
33496
  errored = true;
33398
33497
  lastErrorMessage = err instanceof Error ? err.message : String(err);
33399
33498
  }
33400
- if (isDesignPhase && !errored) {
33499
+ if (isDesignPhase && !errored && !isCouncilCancelled(config2.signal)) {
33401
33500
  const chairmanCheck = enforceDesignPhaseToolEmissions(chairman.id, emittedToolNames);
33402
33501
  yield* applyRetryIfMissing({
33403
33502
  agent: chairman,
@@ -33418,7 +33517,7 @@ async function* runCouncilPure(userMessage, config2, callbacks = {}) {
33418
33517
  });
33419
33518
  } else if (isDesignPhase) {
33420
33519
  enforceDesignPhaseToolEmissions(chairman.id, emittedToolNames);
33421
- } else if (!errored) {
33520
+ } else if (!errored && !isCouncilCancelled(config2.signal)) {
33422
33521
  if (chairmanProjectRoot) {
33423
33522
  const zelariRoot = `${chairmanProjectRoot}/.zelari`;
33424
33523
  const spec = loadNfrSpec(zelariRoot) ?? DEFAULT_NFR_SPEC;
@@ -33547,7 +33646,7 @@ async function* runCouncilPure(userMessage, config2, callbacks = {}) {
33547
33646
  id: crypto.randomUUID(),
33548
33647
  ts: Date.now(),
33549
33648
  sessionId: sessionId2,
33550
- reason: "completed",
33649
+ reason: isCouncilCancelled(config2.signal) ? "cancelled" : "completed",
33551
33650
  durationMs: 0
33552
33651
  };
33553
33652
  }
@@ -33566,6 +33665,7 @@ var init_councilApi = __esm({
33566
33665
  init_runChecks();
33567
33666
  init_implementationDelivery();
33568
33667
  init_types7();
33668
+ init_cancel();
33569
33669
  init_types7();
33570
33670
  init_outputCleaning();
33571
33671
  init_outputCleaning();
@@ -34572,6 +34672,7 @@ __export(council_exports, {
34572
34672
  shouldRetryMember: () => shouldRetryMember,
34573
34673
  slugify: () => slugify2,
34574
34674
  stripClarificationProtocol: () => stripClarificationProtocol,
34675
+ stripQuestionBlocks: () => stripQuestionBlocks,
34575
34676
  swapMembers: () => swapMembers,
34576
34677
  systemMessagesFromSplit: () => systemMessagesFromSplit,
34577
34678
  taskMatchesNfrKeywords: () => taskMatchesNfrKeywords,
@@ -38114,7 +38215,7 @@ var CORE_VERSION;
38114
38215
  var init_version = __esm({
38115
38216
  "packages/core/dist/version.js"() {
38116
38217
  "use strict";
38117
- CORE_VERSION = "2.34.0";
38218
+ CORE_VERSION = "2.34.1";
38118
38219
  }
38119
38220
  });
38120
38221
 
@@ -38626,6 +38727,7 @@ __export(dist_exports, {
38626
38727
  strictBuildGate: () => strictBuildGate,
38627
38728
  stripAnsi: () => stripAnsi,
38628
38729
  stripClarificationProtocol: () => stripClarificationProtocol,
38730
+ stripQuestionBlocks: () => stripQuestionBlocks,
38629
38731
  swapMembers: () => swapMembers,
38630
38732
  systemMessagesFromSplit: () => systemMessagesFromSplit,
38631
38733
  taskMatchesNfrKeywords: () => taskMatchesNfrKeywords,
@@ -44116,6 +44218,7 @@ __export(krakenModel_exports, {
44116
44218
  inferModelFamily: () => inferModelFamily,
44117
44219
  isCheapModelId: () => isCheapModelId,
44118
44220
  isKrakenAutoModelEnabled: () => isKrakenAutoModelEnabled,
44221
+ isUnknownModelError: () => isUnknownModelError,
44119
44222
  parseQualifiedModelRef: () => parseQualifiedModelRef,
44120
44223
  pickCheapModel: () => pickCheapModel,
44121
44224
  pickDifferentFamily: () => pickDifferentFamily,
@@ -44227,6 +44330,12 @@ function resolveKrakenSubModel(agent, parentModel, env = process.env, opts = {})
44227
44330
  }
44228
44331
  return parentModel;
44229
44332
  }
44333
+ function isUnknownModelError(message) {
44334
+ if (!message) return false;
44335
+ const m = message.toLowerCase();
44336
+ if (!/model/.test(m)) return false;
44337
+ return /http\s*404/.test(m) || /not-found/.test(m) || /not_found/.test(m) || /does not exist/.test(m) || /unknown model/.test(m) || /model_not_found/.test(m);
44338
+ }
44230
44339
  function resolveKrakenPlannerModel(parentModel, env = process.env) {
44231
44340
  const specific = env.ZELARI_KRAKEN_PLANNER_MODEL?.trim();
44232
44341
  if (specific) return specific;
@@ -46026,6 +46135,12 @@ ${failed.error ?? "unknown error"}`,
46026
46135
  // src/cli/tools/taskTool.ts
46027
46136
  import { existsSync as existsSync26 } from "node:fs";
46028
46137
  import { randomUUID as randomUUID4 } from "node:crypto";
46138
+ function permissionsForTaskAgent(agent) {
46139
+ const kind2 = agent ?? "explore";
46140
+ if (kind2 === "general") return ["read", "write", "execute", "network"];
46141
+ if (kind2 === "verify") return ["read", "execute", "network"];
46142
+ return ["read"];
46143
+ }
46029
46144
  function resetTaskSpawnCount() {
46030
46145
  const g = globalThis;
46031
46146
  g.__zelariTaskSpawnCount = 0;
@@ -46507,17 +46622,45 @@ ${taskUserContent}`,
46507
46622
  };
46508
46623
  }
46509
46624
  const startedTools = /* @__PURE__ */ new Map();
46510
- const { result, error: error51, aborted: aborted2, usage, toolTrace } = await runSubAgent(harness, {
46625
+ const onHarnessEvent = (ev) => {
46626
+ if (ev.type === "tool_execution_start") {
46627
+ startedTools.set(ev.toolCallId, ev.toolName);
46628
+ emitActivity({ type: "agent_tool", agentId: liveId, toolCallId: ev.toolCallId, tool: ev.toolName, status: "started", ...ev.args ? { summary: toolCommandHint(ev.args) } : {}, ts: Date.now() });
46629
+ } else if (ev.type === "tool_execution_end") {
46630
+ emitActivity({ type: "agent_tool", agentId: liveId, toolCallId: ev.toolCallId, tool: startedTools.get(ev.toolCallId) ?? "unknown", status: ev.isError ? "failed" : "completed", durationMs: ev.durationMs, ts: Date.now() });
46631
+ }
46632
+ };
46633
+ let { result, error: error51, aborted: aborted2, usage, toolTrace } = await runSubAgent(harness, {
46511
46634
  ...opts.signal ? { signal: opts.signal } : {},
46512
- onEvent: (ev) => {
46513
- if (ev.type === "tool_execution_start") {
46514
- startedTools.set(ev.toolCallId, ev.toolName);
46515
- emitActivity({ type: "agent_tool", agentId: liveId, toolCallId: ev.toolCallId, tool: ev.toolName, status: "started", ...ev.args ? { summary: toolCommandHint(ev.args) } : {}, ts: Date.now() });
46516
- } else if (ev.type === "tool_execution_end") {
46517
- emitActivity({ type: "agent_tool", agentId: liveId, toolCallId: ev.toolCallId, tool: startedTools.get(ev.toolCallId) ?? "unknown", status: ev.isError ? "failed" : "completed", durationMs: ev.durationMs, ts: Date.now() });
46635
+ onEvent: onHarnessEvent
46636
+ });
46637
+ if (!aborted2 && !result && sub.fallback && sub.fallback.model !== sub.model) {
46638
+ const { isUnknownModelError: isUnknownModelError2 } = await Promise.resolve().then(() => (init_krakenModel(), krakenModel_exports));
46639
+ if (isUnknownModelError2(error51)) {
46640
+ emitPhase(`model ${sub.model} unavailable \u2014 retrying with ${sub.fallback.model}`);
46641
+ const retryConfig = {
46642
+ ...config2,
46643
+ model: sub.fallback.model,
46644
+ provider: sub.fallback.provider,
46645
+ providerStream: sub.fallback.providerStream
46646
+ };
46647
+ try {
46648
+ harness = deps.harnessFactory ? deps.harnessFactory(retryConfig) : new (await Promise.resolve().then(() => (init_harness(), harness_exports))).AgentHarness(retryConfig);
46649
+ const retry = await runSubAgent(harness, {
46650
+ ...opts.signal ? { signal: opts.signal } : {},
46651
+ onEvent: onHarnessEvent
46652
+ });
46653
+ result = retry.result;
46654
+ error51 = retry.error;
46655
+ aborted2 = retry.aborted;
46656
+ usage = retry.usage;
46657
+ toolTrace = retry.toolTrace;
46658
+ sub = { ...sub, model: sub.fallback.model, provider: sub.fallback.provider };
46659
+ } catch (err) {
46660
+ error51 = err instanceof Error ? err.message : String(err);
46518
46661
  }
46519
46662
  }
46520
- });
46663
+ }
46521
46664
  const durationMs = Date.now() - started;
46522
46665
  if (aborted2) {
46523
46666
  if (worktree && !shouldKeepWorktree()) await cleanupKrakenWorktree(worktree);
@@ -54082,6 +54225,7 @@ function createBuiltinToolRegistry(options = {}) {
54082
54225
  // registry's own policy (permPolicy above) — they can never
54083
54226
  // exceed it.
54084
54227
  parentPolicy: permPolicy,
54228
+ ...options.onPermissionAsk ? { onPermissionAsk: options.onPermissionAsk } : {},
54085
54229
  ...options.subAgentProvider ? { provider: options.subAgentProvider } : {},
54086
54230
  ...options.subAgentModel ? { model: options.subAgentModel } : {}
54087
54231
  }),
@@ -54181,12 +54325,13 @@ function taskAgentToProfile(agent) {
54181
54325
  return "explore";
54182
54326
  }
54183
54327
  function createKrakenSubAgentContextFactory(opts) {
54184
- const { root, audit, sessionId: sessionId2, provider: providerOverride, model: modelOverride, parentPolicy } = opts;
54328
+ const { root, audit, sessionId: sessionId2, provider: providerOverride, model: modelOverride, parentPolicy, onPermissionAsk } = opts;
54185
54329
  return async ({ agent, cwd: subCwd }) => {
54186
54330
  const cfg = providerOverride ? await providerConfigFor(providerOverride) : await providerFromEnv();
54187
54331
  if (!cfg) return null;
54188
54332
  const { resolveKrakenSubModel: resolveKrakenSubModel2, parseQualifiedModelRef: parseQualifiedModelRef2 } = await Promise.resolve().then(() => (init_krakenModel(), krakenModel_exports));
54189
- const resolvedModel = resolveKrakenSubModel2(agent, modelOverride || cfg.model);
54333
+ const parentModel = modelOverride || cfg.model;
54334
+ const resolvedModel = resolveKrakenSubModel2(agent, parentModel);
54190
54335
  let effCfg = cfg;
54191
54336
  let model = resolvedModel;
54192
54337
  const ref = parseQualifiedModelRef2(resolvedModel);
@@ -54215,6 +54360,7 @@ function createKrakenSubAgentContextFactory(opts) {
54215
54360
  diagnostics: false,
54216
54361
  lspProvider: null,
54217
54362
  permissionPolicy: effectiveSubPolicy,
54363
+ ...onPermissionAsk ? { onPermissionAsk } : {},
54218
54364
  // P0.5: the tentacle's agent identity drives per-agent policy rules.
54219
54365
  policyAgent: agent
54220
54366
  });
@@ -54222,6 +54368,13 @@ function createKrakenSubAgentContextFactory(opts) {
54222
54368
  providerStream: buildProviderStream(subCfg),
54223
54369
  model,
54224
54370
  provider: subCfg.providerId,
54371
+ ...model !== parentModel ? {
54372
+ fallback: {
54373
+ model: parentModel,
54374
+ provider: cfg.providerId,
54375
+ providerStream: buildProviderStream({ ...cfg, model: parentModel })
54376
+ }
54377
+ } : {},
54225
54378
  registry: subRegistry,
54226
54379
  tools: subRegistry.toOpenAITools().map((t) => ({
54227
54380
  name: t.function.name,
@@ -54241,11 +54394,14 @@ function wrapWithPermissions(original, policy, onAsk, agentLayers, precedence =
54241
54394
  return {
54242
54395
  ...original,
54243
54396
  execute: async (input, ctx) => {
54244
- const decision = resolveToolPermission(original.name, required2, policy);
54397
+ const requiredNow = original.name === "task" ? permissionsForTaskAgent(
54398
+ input?.agent
54399
+ ) : required2;
54400
+ const decision = resolveToolPermission(original.name, requiredNow, policy);
54245
54401
  const rule = agentLayers ? matchAgentPolicyRuleLayered(
54246
54402
  agentLayers,
54247
54403
  precedence,
54248
- required2,
54404
+ requiredNow,
54249
54405
  input ?? {},
54250
54406
  root ?? process.cwd()
54251
54407
  ) : null;
@@ -54257,25 +54413,25 @@ function wrapWithPermissions(original, policy, onAsk, agentLayers, precedence =
54257
54413
  root ?? process.cwd()
54258
54414
  ) : void 0;
54259
54415
  const contractRule = matchContractCapabilityRule(
54260
- required2,
54416
+ requiredNow,
54261
54417
  input ?? {},
54262
54418
  root ?? process.cwd()
54263
54419
  );
54264
54420
  let action = intersectEffects(mergeRuleEffect(decision.action, rule), claims?.effect, contractRule?.effect);
54265
54421
  let actionReason = decision.reason;
54266
- if (action !== "deny" && (required2.includes("write") || required2.includes("execute"))) {
54422
+ if (action !== "deny" && (requiredNow.includes("write") || requiredNow.includes("execute"))) {
54267
54423
  const provHit = provenanceMatchIn(JSON.stringify(input ?? {}));
54268
- if (provHit && provenanceAppliesTo(provHit.source, required2)) {
54424
+ if (provHit && provenanceAppliesTo(provHit.source, requiredNow)) {
54269
54425
  const provNote = `[provenance] args embed non-user ${provHit.source} content (via ${provHit.tool})`;
54270
54426
  if (action === "allow") {
54271
54427
  action = "ask";
54272
- actionReason = `${provNote} \u2014 confirm before ${required2.join("+")}`;
54428
+ actionReason = `${provNote} \u2014 confirm before ${requiredNow.join("+")}`;
54273
54429
  } else {
54274
54430
  actionReason = `${decision.reason} \xB7 ${provNote}`;
54275
54431
  }
54276
54432
  }
54277
54433
  }
54278
- if (action === "allow" && required2.includes("execute") && activePermissionPreset() !== "yolo" && !isSessionGranted(original.name, required2)) {
54434
+ if (action === "allow" && requiredNow.includes("execute") && activePermissionPreset() !== "yolo" && !isSessionGranted(original.name, requiredNow)) {
54279
54435
  const destructiveHit = destructiveCommandHit(input ?? {});
54280
54436
  if (destructiveHit) {
54281
54437
  action = "ask";
@@ -54318,7 +54474,7 @@ function wrapWithPermissions(original, policy, onAsk, agentLayers, precedence =
54318
54474
  }
54319
54475
  }
54320
54476
  const outcome = await original.execute(input, ctx);
54321
- recordResultForProvenance(original.name, required2, outcome);
54477
+ recordResultForProvenance(original.name, requiredNow, outcome);
54322
54478
  return outcome;
54323
54479
  }
54324
54480
  };
@@ -55654,6 +55810,25 @@ var init_spineTelemetry = __esm({
55654
55810
  }
55655
55811
  });
55656
55812
 
55813
+ // src/cli/hooks/askUserTimeout.ts
55814
+ function askUserTimeoutMs() {
55815
+ const raw = process.env.ZELARI_ASK_USER_TIMEOUT_MS?.trim();
55816
+ if (!raw) return 3e5;
55817
+ const n = Number.parseInt(raw, 10);
55818
+ if (!Number.isFinite(n) || n < 0) return 3e5;
55819
+ return n;
55820
+ }
55821
+ function armPickerTimeout(onFire, ms) {
55822
+ if (ms <= 0) return () => void 0;
55823
+ const id3 = setTimeout(onFire, ms);
55824
+ return () => clearTimeout(id3);
55825
+ }
55826
+ var init_askUserTimeout = __esm({
55827
+ "src/cli/hooks/askUserTimeout.ts"() {
55828
+ "use strict";
55829
+ }
55830
+ });
55831
+
55657
55832
  // src/cli/state/fileStateStore.ts
55658
55833
  import { createHash as createHash17, randomUUID as randomUUID5 } from "node:crypto";
55659
55834
  import { promises as fs26 } from "node:fs";
@@ -61040,7 +61215,8 @@ async function* dispatchCouncil(userMessage, options) {
61040
61215
  maxToolLoopIterations: options.maxToolLoopIterations,
61041
61216
  maxToolLoopHardCap: options.maxToolLoopHardCap,
61042
61217
  skipSpecialists: options.skipSpecialists,
61043
- feedbackStore: options.feedbackStore
61218
+ feedbackStore: options.feedbackStore,
61219
+ signal: options.signal
61044
61220
  };
61045
61221
  if (!options.disableWorkspaceTools) {
61046
61222
  const { setWorkspaceStubs: setWorkspaceStubs2 } = await Promise.resolve().then(() => (init_skills2(), skills_exports));
@@ -62798,6 +62974,13 @@ async function runZelariMission(userMessage, brief, deps) {
62798
62974
  let forcePivot = false;
62799
62975
  const missionStartMs = now().getTime();
62800
62976
  while (true) {
62977
+ if (deps.signal?.aborted) {
62978
+ state3.status = "cancelled";
62979
+ state3.updatedAt = now().toISOString();
62980
+ await persist();
62981
+ deps.emit("[zelari] missione cancellata.");
62982
+ return state3;
62983
+ }
62801
62984
  const runMode = pendingDesign ? "design-phase" : "implementation";
62802
62985
  if (runMode === "implementation") {
62803
62986
  deps.onMissionPhase?.("build", `impl-${implStep + 1}`);
@@ -62854,6 +63037,13 @@ async function runZelariMission(userMessage, brief, deps) {
62854
63037
  );
62855
63038
  return state3;
62856
63039
  }
63040
+ if (deps.signal?.aborted) {
63041
+ state3.status = "cancelled";
63042
+ state3.updatedAt = now().toISOString();
63043
+ await persist();
63044
+ deps.emit("[zelari] missione cancellata.");
63045
+ return state3;
63046
+ }
62857
63047
  if (typeof result.costUsd === "number") cumulativeCostUsd += result.costUsd;
62858
63048
  if (typeof result.costTokens === "number") cumulativeTokens += result.costTokens;
62859
63049
  await deps.memory.add(
@@ -65972,11 +66162,12 @@ var init_facts = __esm({
65972
66162
  });
65973
66163
 
65974
66164
  // src/cli/utils/streamScrub.ts
65975
- function createStreamScrubber2() {
66165
+ function createStreamScrubber2(opts = {}) {
66166
+ const stripQuestion = opts.stripQuestion !== false;
65976
66167
  let rawBuf = "";
65977
66168
  let emittedLen = 0;
65978
66169
  const snapshot = () => {
65979
- const cleaned = cleanAgentContent(rawBuf);
66170
+ const cleaned = cleanAgentContent(rawBuf, { stripQuestion });
65980
66171
  if (cleaned.length <= emittedLen) return "";
65981
66172
  const delta = cleaned.slice(emittedLen);
65982
66173
  emittedLen = cleaned.length;
@@ -66214,215 +66405,39 @@ var init_harnessStateEmit = __esm({
66214
66405
  }
66215
66406
  });
66216
66407
 
66217
- // src/cli/headless/policyGate.ts
66218
- import { isAbsolute as isAbsolute5, resolve as resolve7 } from "node:path";
66219
- import { randomUUID as randomUUID9 } from "node:crypto";
66220
- function checkStrictPolicyLoad(root, opts) {
66221
- if (isPolicyEngineDisabled()) return { blocked: false, warnings: [] };
66222
- try {
66223
- const set2 = loadPolicySet(root, opts);
66224
- return { blocked: false, warnings: set2.warnings };
66225
- } catch (err) {
66226
- if (!(err instanceof PolicyLoadError)) throw err;
66227
- const file2 = isAbsolute5(err.file) ? err.file : resolve7(root, err.file);
66228
- return {
66229
- blocked: true,
66230
- warnings: [],
66231
- block: {
66232
- reason: POLICY_LOAD_BLOCK_REASON,
66233
- exitCode: POLICY_LOAD_EXIT_CODE,
66234
- code: err.code,
66235
- file: file2,
66236
- ...err.message ? { detail: err.message } : {}
66237
- }
66238
- };
66239
- }
66240
- }
66241
- function reportPolicyLoadBlocked(block, output) {
66242
- const where = `${block.file}${block.detail ? ` \u2014 ${block.detail}` : ""}`;
66243
- process.stderr.write(`[zelari-code --headless] ${block.reason}: ${where}
66244
- `);
66245
- if (output === "json") {
66246
- emitEvent({
66247
- type: "error",
66248
- severity: "fatal",
66249
- message: `${block.reason}: ${where}`,
66250
- code: block.reason
66251
- });
66252
- }
66253
- }
66254
- async function recordPolicyLoadBlockedOnSpine(block, opts = {}) {
66255
- try {
66256
- const { openHeadlessSpine: openHeadlessSpine2 } = await Promise.resolve().then(() => (init_headlessSpine(), headlessSpine_exports));
66257
- const sessionId2 = opts.resumeSessionId ?? randomUUID9();
66258
- const spine = await openHeadlessSpine2({
66259
- sessionId: sessionId2,
66260
- ...opts.mode ? { mode: opts.mode } : {},
66261
- ...opts.profile ? { profile: opts.profile } : {},
66262
- workspace: opts.workspace ?? process.cwd()
66263
- });
66264
- if (opts.mode === "zelari") {
66265
- spine.missionPhase("dispatch", block.reason);
66266
- }
66267
- spine.note(block.reason, {
66268
- code: block.code,
66269
- file: block.file,
66270
- exitCode: block.exitCode,
66271
- ...block.detail ? { detail: block.detail } : {}
66272
- });
66273
- await spine.close("error");
66274
- } catch {
66275
- }
66276
- }
66277
- var init_policyGate = __esm({
66278
- "src/cli/headless/policyGate.ts"() {
66279
- "use strict";
66280
- init_policyEngine();
66281
- init_policyLoadMode();
66282
- init_headless();
66283
- }
66284
- });
66285
-
66286
- // src/cli/kraken/verifierResolution.ts
66287
- function verifierOverrideToModelSelection(override) {
66288
- if (override && typeof override.provider === "string" && typeof override.model === "string" && override.provider.trim().length > 0 && override.model.trim().length > 0) {
66289
- return {
66290
- mode: "fixed",
66291
- provider: override.provider.trim(),
66292
- model: override.model.trim()
66293
- };
66294
- }
66295
- return { mode: "inherit" };
66296
- }
66297
- function loadVerifierModelSelection() {
66298
- return verifierOverrideToModelSelection(getKrakenVerifierOverride());
66299
- }
66300
- var init_verifierResolution = __esm({
66301
- "src/cli/kraken/verifierResolution.ts"() {
66302
- "use strict";
66303
- init_providerConfig();
66304
- }
66305
- });
66306
-
66307
- // src/cli/kraken/verifierLifecycle.ts
66308
- function verifierReviewEnabled(selection = loadVerifierModelSelection(), env = process.env) {
66309
- const v = env.ZELARI_VERIFIER_REVIEW?.toLowerCase();
66310
- if (v === "0" || v === "false" || v === "off") return false;
66311
- if (v === "1" || v === "true" || v === "on") return true;
66312
- return selection.mode === "fixed";
66408
+ // src/cli/serve/sessionControl.ts
66409
+ import { AsyncLocalStorage } from "node:async_hooks";
66410
+ function runWithSession(sessionId2, fn) {
66411
+ return dispatchContext.run({ sessionId: sessionId2, token: {} }, fn);
66313
66412
  }
66314
- function makeVerifierCallModel(loadStream, identity, timeoutMs2 = 12e4) {
66315
- return async ({ system, user }) => {
66316
- const stream = await loadStream(identity.provider, identity.model);
66317
- if (!stream) {
66318
- throw new Error(`no provider config for verifier "${identity.provider}"`);
66413
+ function registerLiveTurnControl(control) {
66414
+ const store6 = dispatchContext.getStore();
66415
+ if (!store6) return void 0;
66416
+ const registered = { ...control, token: store6.token };
66417
+ liveTurns.set(store6.sessionId, registered);
66418
+ return () => {
66419
+ if (liveTurns.get(store6.sessionId) === registered) {
66420
+ liveTurns.delete(store6.sessionId);
66319
66421
  }
66320
- const { text } = await collectProviderText(stream, {
66321
- messages: [
66322
- { role: "system", content: system },
66323
- { role: "user", content: user }
66324
- ],
66325
- model: identity.model,
66326
- provider: identity.provider,
66327
- tools: [],
66328
- signal: AbortSignal.timeout(timeoutMs2)
66329
- });
66330
- return { text, provider: identity.provider, model: identity.model };
66331
66422
  };
66332
66423
  }
66333
- function isTestEvidenceCriterion(criterionId2) {
66334
- const id3 = criterionId2.toLowerCase();
66335
- return ["test", "typecheck", "build", "lint"].some((k) => id3.includes(k));
66336
- }
66337
- function extractTestOutputExcerpt(results, maxChars = 4e3) {
66338
- const lines = [];
66339
- for (const r of results) {
66340
- if (!isTestEvidenceCriterion(r.criterionId)) continue;
66341
- lines.push([r.criterionId, r.status, r.detail].filter(Boolean).join(" \u2014 "));
66342
- }
66343
- if (lines.length === 0) return "";
66344
- return lines.join("\n").slice(0, maxChars);
66345
- }
66346
- async function buildBlindReviewInput(evaluation, deps) {
66347
- const results = evaluation.results ?? [];
66348
- const passed = results.filter((r) => r.status === "pass").length;
66349
- const verdict = evaluation.evaluation?.verdict ?? "UNKNOWN";
66350
- const summary = `Kraken BUILD turn \u2014 deterministic evidence: ${passed}/${results.length} criteria pass, completion verdict ${verdict}.`;
66351
- const task = deps.task?.trim();
66352
- const testOutputExcerpt = extractTestOutputExcerpt(results);
66353
- let diffSummary;
66354
- try {
66355
- const res = await (deps.getDiff ?? getWorkingDiff)({
66356
- cwd: deps.cwd ?? process.cwd(),
66357
- maxChars: 8e3,
66358
- staged: true
66359
- });
66360
- if (res && !res.empty && res.diff) diffSummary = res.diff;
66361
- } catch {
66362
- }
66363
- return {
66364
- ...task ? { task } : {},
66365
- summary,
66366
- ...diffSummary !== void 0 ? { diffSummary } : {},
66367
- ...testOutputExcerpt ? { testOutputExcerpt } : {},
66368
- results
66369
- };
66424
+ function getLiveTurnControl(sessionId2) {
66425
+ return liveTurns.get(sessionId2);
66370
66426
  }
66371
- async function runAdvisoryVerifierReview(evaluation, deps = {}) {
66372
- if (!evaluation.evaluation || !evaluation.results) return null;
66373
- const env = deps.env ?? process.env;
66374
- const selection = deps.selection ?? loadVerifierModelSelection();
66375
- if (!verifierReviewEnabled(selection, env)) return null;
66376
- const risk = deps.risk ?? activeRisk(env);
66377
- if (risk === "low") return null;
66378
- const route = resolveVerifierRouting(
66379
- selection.mode === "fixed" ? { provider: selection.provider, model: selection.model } : null,
66380
- risk,
66381
- {
66382
- selectionMode: selection.mode === "fixed" ? "fixed" : "inherit",
66383
- session: deps.session ?? null,
66384
- familyCandidates: deps.familyCandidates,
66385
- env
66386
- }
66387
- );
66388
- const reviewers = route.reviewers;
66389
- let callModel = deps.callModel;
66390
- if (reviewers.length === 0 || !callModel && !deps.loadStream) return null;
66391
- const blind = await buildBlindReviewInput(evaluation, deps);
66392
- const reviews = [];
66393
- for (const reviewer of reviewers) {
66394
- const call = callModel ?? makeVerifierCallModel(deps.loadStream, reviewer.identity, deps.timeoutMs);
66395
- const service = new VerifierService({
66396
- callModel: call,
66397
- config: {
66398
- enabled: true,
66399
- model: selection,
66400
- progressScoring: false,
66401
- bon: { enabled: false, n: 3 }
66402
- },
66403
- emit: deps.emit,
66404
- env
66405
- });
66406
- reviews.push(await service.reviewCompletion({ ...blind, session: deps.session }));
66407
- }
66408
- const review = reviews.length > 1 ? mergeVerifierVerdicts(reviews) : reviews[0];
66409
- evaluation.review = review;
66410
- if (reviews.length > 1 && risk === "critical") {
66411
- evaluation.reviewDivergence = divergenceFromReviews(
66412
- reviews,
66413
- reviewers.map((r) => ({ family: r.family, role: r.role }))
66414
- );
66427
+ function clearSessionTurnControl(sessionId2) {
66428
+ const store6 = dispatchContext.getStore();
66429
+ if (!store6 || store6.sessionId !== sessionId2) return;
66430
+ const registered = liveTurns.get(sessionId2);
66431
+ if (registered && registered.token === store6.token) {
66432
+ liveTurns.delete(sessionId2);
66415
66433
  }
66416
- return review;
66417
66434
  }
66418
- var init_verifierLifecycle = __esm({
66419
- "src/cli/kraken/verifierLifecycle.ts"() {
66435
+ var dispatchContext, liveTurns;
66436
+ var init_sessionControl = __esm({
66437
+ "src/cli/serve/sessionControl.ts"() {
66420
66438
  "use strict";
66421
- init_verification();
66422
- init_krakenSelectTool();
66423
- init_gitOps();
66424
- init_verifierResolution();
66425
- init_verifierRouting();
66439
+ dispatchContext = new AsyncLocalStorage();
66440
+ liveTurns = /* @__PURE__ */ new Map();
66426
66441
  }
66427
66442
  });
66428
66443
 
@@ -66652,39 +66667,277 @@ var init_controlBridge = __esm({
66652
66667
  }
66653
66668
  });
66654
66669
 
66655
- // src/cli/serve/sessionControl.ts
66656
- import { AsyncLocalStorage } from "node:async_hooks";
66657
- function runWithSession(sessionId2, fn) {
66658
- return dispatchContext.run({ sessionId: sessionId2, token: {} }, fn);
66670
+ // src/cli/headless/liveTurnAbort.ts
66671
+ function attachHeadlessLiveCancel(opts) {
66672
+ const abort = new AbortController();
66673
+ const controlQueue = new RuntimeControlQueue();
66674
+ const cancel = () => {
66675
+ if (!abort.signal.aborted) abort.abort();
66676
+ return true;
66677
+ };
66678
+ const controlPlane = opts?.output === "json" && process.stdin.isTTY !== true && process.env.ZELARI_SERVE_HARNESS !== "1" ? (() => {
66679
+ emitEvent(protocolInfoEvent());
66680
+ return attachControlPlane({
66681
+ input: process.stdin,
66682
+ queue: controlQueue,
66683
+ emit: emitEvent,
66684
+ onCancel: () => {
66685
+ cancel();
66686
+ }
66687
+ });
66688
+ })() : void 0;
66689
+ const unregister = process.env.ZELARI_SERVE_HARNESS === "1" ? registerLiveTurnControl({
66690
+ queue: controlQueue,
66691
+ cancel
66692
+ }) : void 0;
66693
+ if (unregister) {
66694
+ const appliedBoundary = {
66695
+ steer: "turn-end",
66696
+ follow_up: "run-end",
66697
+ cancel: "cancel"
66698
+ };
66699
+ controlQueue.onDrained = (events) => {
66700
+ for (const event of events) {
66701
+ emitEvent(
66702
+ controlAppliedEvent(
66703
+ event.id,
66704
+ event.type,
66705
+ appliedBoundary[event.type] ?? "unknown"
66706
+ )
66707
+ );
66708
+ }
66709
+ };
66710
+ }
66711
+ return {
66712
+ signal: abort.signal,
66713
+ cancel,
66714
+ dispose() {
66715
+ controlPlane?.finalize();
66716
+ controlPlane?.dispose();
66717
+ unregister?.();
66718
+ }
66719
+ };
66659
66720
  }
66660
- function registerLiveTurnControl(control) {
66661
- const store6 = dispatchContext.getStore();
66662
- if (!store6) return void 0;
66663
- const registered = { ...control, token: store6.token };
66664
- liveTurns.set(store6.sessionId, registered);
66665
- return () => {
66666
- if (liveTurns.get(store6.sessionId) === registered) {
66667
- liveTurns.delete(store6.sessionId);
66721
+ var init_liveTurnAbort = __esm({
66722
+ "src/cli/headless/liveTurnAbort.ts"() {
66723
+ "use strict";
66724
+ init_runtime();
66725
+ init_sessionControl();
66726
+ init_controlBridge();
66727
+ init_protocol2();
66728
+ init_headless();
66729
+ }
66730
+ });
66731
+
66732
+ // src/cli/headless/policyGate.ts
66733
+ import { isAbsolute as isAbsolute5, resolve as resolve7 } from "node:path";
66734
+ import { randomUUID as randomUUID9 } from "node:crypto";
66735
+ function checkStrictPolicyLoad(root, opts) {
66736
+ if (isPolicyEngineDisabled()) return { blocked: false, warnings: [] };
66737
+ try {
66738
+ const set2 = loadPolicySet(root, opts);
66739
+ return { blocked: false, warnings: set2.warnings };
66740
+ } catch (err) {
66741
+ if (!(err instanceof PolicyLoadError)) throw err;
66742
+ const file2 = isAbsolute5(err.file) ? err.file : resolve7(root, err.file);
66743
+ return {
66744
+ blocked: true,
66745
+ warnings: [],
66746
+ block: {
66747
+ reason: POLICY_LOAD_BLOCK_REASON,
66748
+ exitCode: POLICY_LOAD_EXIT_CODE,
66749
+ code: err.code,
66750
+ file: file2,
66751
+ ...err.message ? { detail: err.message } : {}
66752
+ }
66753
+ };
66754
+ }
66755
+ }
66756
+ function reportPolicyLoadBlocked(block, output) {
66757
+ const where = `${block.file}${block.detail ? ` \u2014 ${block.detail}` : ""}`;
66758
+ process.stderr.write(`[zelari-code --headless] ${block.reason}: ${where}
66759
+ `);
66760
+ if (output === "json") {
66761
+ emitEvent({
66762
+ type: "error",
66763
+ severity: "fatal",
66764
+ message: `${block.reason}: ${where}`,
66765
+ code: block.reason
66766
+ });
66767
+ }
66768
+ }
66769
+ async function recordPolicyLoadBlockedOnSpine(block, opts = {}) {
66770
+ try {
66771
+ const { openHeadlessSpine: openHeadlessSpine2 } = await Promise.resolve().then(() => (init_headlessSpine(), headlessSpine_exports));
66772
+ const sessionId2 = opts.resumeSessionId ?? randomUUID9();
66773
+ const spine = await openHeadlessSpine2({
66774
+ sessionId: sessionId2,
66775
+ ...opts.mode ? { mode: opts.mode } : {},
66776
+ ...opts.profile ? { profile: opts.profile } : {},
66777
+ workspace: opts.workspace ?? process.cwd()
66778
+ });
66779
+ if (opts.mode === "zelari") {
66780
+ spine.missionPhase("dispatch", block.reason);
66668
66781
  }
66782
+ spine.note(block.reason, {
66783
+ code: block.code,
66784
+ file: block.file,
66785
+ exitCode: block.exitCode,
66786
+ ...block.detail ? { detail: block.detail } : {}
66787
+ });
66788
+ await spine.close("error");
66789
+ } catch {
66790
+ }
66791
+ }
66792
+ var init_policyGate = __esm({
66793
+ "src/cli/headless/policyGate.ts"() {
66794
+ "use strict";
66795
+ init_policyEngine();
66796
+ init_policyLoadMode();
66797
+ init_headless();
66798
+ }
66799
+ });
66800
+
66801
+ // src/cli/kraken/verifierResolution.ts
66802
+ function verifierOverrideToModelSelection(override) {
66803
+ if (override && typeof override.provider === "string" && typeof override.model === "string" && override.provider.trim().length > 0 && override.model.trim().length > 0) {
66804
+ return {
66805
+ mode: "fixed",
66806
+ provider: override.provider.trim(),
66807
+ model: override.model.trim()
66808
+ };
66809
+ }
66810
+ return { mode: "inherit" };
66811
+ }
66812
+ function loadVerifierModelSelection() {
66813
+ return verifierOverrideToModelSelection(getKrakenVerifierOverride());
66814
+ }
66815
+ var init_verifierResolution = __esm({
66816
+ "src/cli/kraken/verifierResolution.ts"() {
66817
+ "use strict";
66818
+ init_providerConfig();
66819
+ }
66820
+ });
66821
+
66822
+ // src/cli/kraken/verifierLifecycle.ts
66823
+ function verifierReviewEnabled(selection = loadVerifierModelSelection(), env = process.env) {
66824
+ const v = env.ZELARI_VERIFIER_REVIEW?.toLowerCase();
66825
+ if (v === "0" || v === "false" || v === "off") return false;
66826
+ if (v === "1" || v === "true" || v === "on") return true;
66827
+ return selection.mode === "fixed";
66828
+ }
66829
+ function makeVerifierCallModel(loadStream, identity, timeoutMs2 = 12e4) {
66830
+ return async ({ system, user }) => {
66831
+ const stream = await loadStream(identity.provider, identity.model);
66832
+ if (!stream) {
66833
+ throw new Error(`no provider config for verifier "${identity.provider}"`);
66834
+ }
66835
+ const { text } = await collectProviderText(stream, {
66836
+ messages: [
66837
+ { role: "system", content: system },
66838
+ { role: "user", content: user }
66839
+ ],
66840
+ model: identity.model,
66841
+ provider: identity.provider,
66842
+ tools: [],
66843
+ signal: AbortSignal.timeout(timeoutMs2)
66844
+ });
66845
+ return { text, provider: identity.provider, model: identity.model };
66669
66846
  };
66670
66847
  }
66671
- function getLiveTurnControl(sessionId2) {
66672
- return liveTurns.get(sessionId2);
66848
+ function isTestEvidenceCriterion(criterionId2) {
66849
+ const id3 = criterionId2.toLowerCase();
66850
+ return ["test", "typecheck", "build", "lint"].some((k) => id3.includes(k));
66673
66851
  }
66674
- function clearSessionTurnControl(sessionId2) {
66675
- const store6 = dispatchContext.getStore();
66676
- if (!store6 || store6.sessionId !== sessionId2) return;
66677
- const registered = liveTurns.get(sessionId2);
66678
- if (registered && registered.token === store6.token) {
66679
- liveTurns.delete(sessionId2);
66852
+ function extractTestOutputExcerpt(results, maxChars = 4e3) {
66853
+ const lines = [];
66854
+ for (const r of results) {
66855
+ if (!isTestEvidenceCriterion(r.criterionId)) continue;
66856
+ lines.push([r.criterionId, r.status, r.detail].filter(Boolean).join(" \u2014 "));
66857
+ }
66858
+ if (lines.length === 0) return "";
66859
+ return lines.join("\n").slice(0, maxChars);
66860
+ }
66861
+ async function buildBlindReviewInput(evaluation, deps) {
66862
+ const results = evaluation.results ?? [];
66863
+ const passed = results.filter((r) => r.status === "pass").length;
66864
+ const verdict = evaluation.evaluation?.verdict ?? "UNKNOWN";
66865
+ const summary = `Kraken BUILD turn \u2014 deterministic evidence: ${passed}/${results.length} criteria pass, completion verdict ${verdict}.`;
66866
+ const task = deps.task?.trim();
66867
+ const testOutputExcerpt = extractTestOutputExcerpt(results);
66868
+ let diffSummary;
66869
+ try {
66870
+ const res = await (deps.getDiff ?? getWorkingDiff)({
66871
+ cwd: deps.cwd ?? process.cwd(),
66872
+ maxChars: 8e3,
66873
+ staged: true
66874
+ });
66875
+ if (res && !res.empty && res.diff) diffSummary = res.diff;
66876
+ } catch {
66680
66877
  }
66878
+ return {
66879
+ ...task ? { task } : {},
66880
+ summary,
66881
+ ...diffSummary !== void 0 ? { diffSummary } : {},
66882
+ ...testOutputExcerpt ? { testOutputExcerpt } : {},
66883
+ results
66884
+ };
66681
66885
  }
66682
- var dispatchContext, liveTurns;
66683
- var init_sessionControl = __esm({
66684
- "src/cli/serve/sessionControl.ts"() {
66886
+ async function runAdvisoryVerifierReview(evaluation, deps = {}) {
66887
+ if (!evaluation.evaluation || !evaluation.results) return null;
66888
+ const env = deps.env ?? process.env;
66889
+ const selection = deps.selection ?? loadVerifierModelSelection();
66890
+ if (!verifierReviewEnabled(selection, env)) return null;
66891
+ const risk = deps.risk ?? activeRisk(env);
66892
+ if (risk === "low") return null;
66893
+ const route = resolveVerifierRouting(
66894
+ selection.mode === "fixed" ? { provider: selection.provider, model: selection.model } : null,
66895
+ risk,
66896
+ {
66897
+ selectionMode: selection.mode === "fixed" ? "fixed" : "inherit",
66898
+ session: deps.session ?? null,
66899
+ familyCandidates: deps.familyCandidates,
66900
+ env
66901
+ }
66902
+ );
66903
+ const reviewers = route.reviewers;
66904
+ let callModel = deps.callModel;
66905
+ if (reviewers.length === 0 || !callModel && !deps.loadStream) return null;
66906
+ const blind = await buildBlindReviewInput(evaluation, deps);
66907
+ const reviews = [];
66908
+ for (const reviewer of reviewers) {
66909
+ const call = callModel ?? makeVerifierCallModel(deps.loadStream, reviewer.identity, deps.timeoutMs);
66910
+ const service = new VerifierService({
66911
+ callModel: call,
66912
+ config: {
66913
+ enabled: true,
66914
+ model: selection,
66915
+ progressScoring: false,
66916
+ bon: { enabled: false, n: 3 }
66917
+ },
66918
+ emit: deps.emit,
66919
+ env
66920
+ });
66921
+ reviews.push(await service.reviewCompletion({ ...blind, session: deps.session }));
66922
+ }
66923
+ const review = reviews.length > 1 ? mergeVerifierVerdicts(reviews) : reviews[0];
66924
+ evaluation.review = review;
66925
+ if (reviews.length > 1 && risk === "critical") {
66926
+ evaluation.reviewDivergence = divergenceFromReviews(
66927
+ reviews,
66928
+ reviewers.map((r) => ({ family: r.family, role: r.role }))
66929
+ );
66930
+ }
66931
+ return review;
66932
+ }
66933
+ var init_verifierLifecycle = __esm({
66934
+ "src/cli/kraken/verifierLifecycle.ts"() {
66685
66935
  "use strict";
66686
- dispatchContext = new AsyncLocalStorage();
66687
- liveTurns = /* @__PURE__ */ new Map();
66936
+ init_verification();
66937
+ init_krakenSelectTool();
66938
+ init_gitOps();
66939
+ init_verifierResolution();
66940
+ init_verifierRouting();
66688
66941
  }
66689
66942
  });
66690
66943
 
@@ -67046,6 +67299,7 @@ async function runOneTurn(opts, provider, model, providerStream, extras) {
67046
67299
  // an interactive approval (permission.request over NDJSON) instead of
67047
67300
  // the fail-closed typedErr. Absent handler ⇒ unchanged fail-closed.
67048
67301
  ...opts.onPermissionAsk ? { onPermissionAsk: opts.onPermissionAsk } : {},
67302
+ ...opts.onAskUser ? { onAskUser: opts.onAskUser } : {},
67049
67303
  permissionPolicy: defaultPermissionPolicy2(),
67050
67304
  ...nativeMemory ? { memoryService: nativeMemory } : {},
67051
67305
  memoryAutoWrite,
@@ -67272,7 +67526,7 @@ async function runOneTurn(opts, provider, model, providerStream, extras) {
67272
67526
  let finalReason = "completed";
67273
67527
  let exitCode = 0;
67274
67528
  const textBuffer = [];
67275
- const scrub = createStreamScrubber2();
67529
+ const scrub = createStreamScrubber2({ stripQuestion: opts.output !== "json" });
67276
67530
  try {
67277
67531
  for await (const event of harness.run()) {
67278
67532
  progressRuntime.observe(event);
@@ -68814,6 +69068,7 @@ async function runHeadlessKrakenGraph(opts, provider, model) {
68814
69068
  // ask without a UI fails closed), so this intersection actually
68815
69069
  // bites: tentacles can never exceed the preset.
68816
69070
  parentPolicy: defaultPermissionPolicy2(),
69071
+ ...opts.onPermissionAsk ? { onPermissionAsk: opts.onPermissionAsk } : {},
68817
69072
  // Anchor every tentacle to the SAME provider/model this run
68818
69073
  // resolved (Desktop's selector, or --provider/--model), instead
68819
69074
  // of the persisted provider.json default the factory falls back
@@ -68930,6 +69185,8 @@ async function buildCouncilToolRegistry(planMode, opts, memoryService, memoryAut
68930
69185
  planMode,
68931
69186
  ...extras?.lspProvider ? { lspProvider: extras.lspProvider } : {},
68932
69187
  permissionPolicy: defaultPermissionPolicy2(),
69188
+ ...opts?.onPermissionAsk ? { onPermissionAsk: opts.onPermissionAsk } : {},
69189
+ ...opts?.onAskUser ? { onAskUser: opts.onAskUser } : {},
68933
69190
  ...memoryService ? { memoryService } : {},
68934
69191
  memoryAutoWrite
68935
69192
  });
@@ -68949,6 +69206,14 @@ async function buildCouncilToolRegistry(planMode, opts, memoryService, memoryAut
68949
69206
  return { toolRegistry, workspaceCtx: realCtx };
68950
69207
  }
68951
69208
  async function runHeadlessCouncil(opts, provider, model, providerStream, extras) {
69209
+ const live = attachHeadlessLiveCancel({ output: opts.output });
69210
+ try {
69211
+ return await runHeadlessCouncilBody(opts, provider, model, providerStream, extras, live.signal);
69212
+ } finally {
69213
+ live.dispose();
69214
+ }
69215
+ }
69216
+ async function runHeadlessCouncilBody(opts, provider, model, providerStream, extras, signal) {
68952
69217
  const { dispatchCouncil: dispatchCouncil2 } = await Promise.resolve().then(() => (init_councilDispatcher(), councilDispatcher_exports));
68953
69218
  const sessionId2 = crypto.randomUUID();
68954
69219
  const cwd = resolveHeadlessCwd(opts);
@@ -69024,7 +69289,7 @@ async function runHeadlessCouncil(opts, provider, model, providerStream, extras)
69024
69289
  const effectiveTask = buildCouncilTaskWithHistory(opts.task, historySeed);
69025
69290
  if (opts.task) spine.userMessage(effectiveTask);
69026
69291
  let exitCode = 0;
69027
- const scrub = createStreamScrubber2();
69292
+ const scrub = createStreamScrubber2({ stripQuestion: opts.output !== "json" });
69028
69293
  let lastAssistantText = "";
69029
69294
  let currentAssistantText = "";
69030
69295
  try {
@@ -69059,6 +69324,7 @@ async function runHeadlessCouncil(opts, provider, model, providerStream, extras)
69059
69324
  tools: toolRegistry,
69060
69325
  feedbackStore,
69061
69326
  runMode: councilRunMode,
69327
+ signal,
69062
69328
  // t23: an auto-SELECTED council runs the LITE tier (3 members) unless
69063
69329
  // ZELARI_COUNCIL_TIER / ZELARI_COUNCIL_SIZE explicitly opt into full.
69064
69330
  ...opts.orchestrationDecision?.strategy === "council" && process.env["ZELARI_COUNCIL_TIER"] === void 0 && process.env["ZELARI_COUNCIL_SIZE"] === void 0 ? { councilSize: COUNCIL_TIER_SIZES.lite } : {},
@@ -69117,7 +69383,9 @@ async function runHeadlessCouncil(opts, provider, model, providerStream, extras)
69117
69383
  return 2;
69118
69384
  }
69119
69385
  try {
69120
- await spine.close(exitCode === 0 ? "completed" : "error");
69386
+ await spine.close(
69387
+ signal.aborted ? "stopped" : exitCode === 0 ? "completed" : "error"
69388
+ );
69121
69389
  } catch {
69122
69390
  }
69123
69391
  try {
@@ -69129,7 +69397,7 @@ async function runHeadlessCouncil(opts, provider, model, providerStream, extras)
69129
69397
  at: (/* @__PURE__ */ new Date()).toISOString(),
69130
69398
  mode: "shadow",
69131
69399
  taskClass: classifyTask2({ prompt: effectiveTask }).taskClass,
69132
- verdict: exitCode === 0 ? "PASS" : exitCode === 3 ? "FAIL" : "UNKNOWN"
69400
+ verdict: signal.aborted ? "UNKNOWN" : exitCode === 0 ? "PASS" : exitCode === 3 ? "FAIL" : "UNKNOWN"
69133
69401
  });
69134
69402
  }
69135
69403
  } catch {
@@ -69148,7 +69416,7 @@ async function runHeadlessCouncil(opts, provider, model, providerStream, extras)
69148
69416
  } catch {
69149
69417
  }
69150
69418
  }
69151
- if (nativeMemory && memoryAutoWrite && lastAssistantText) {
69419
+ if (nativeMemory && memoryAutoWrite && lastAssistantText && !signal.aborted) {
69152
69420
  try {
69153
69421
  await nativeMemory.remember({
69154
69422
  kind: councilRunMode === "design-phase" ? "decision" : "outcome",
@@ -69175,6 +69443,14 @@ async function runHeadlessCouncil(opts, provider, model, providerStream, extras)
69175
69443
  return exitCode;
69176
69444
  }
69177
69445
  async function runHeadlessZelari(opts, provider, model, providerStream, extras) {
69446
+ const live = attachHeadlessLiveCancel({ output: opts.output });
69447
+ try {
69448
+ return await runHeadlessZelariBody(opts, provider, model, providerStream, extras, live.signal);
69449
+ } finally {
69450
+ live.dispose();
69451
+ }
69452
+ }
69453
+ async function runHeadlessZelariBody(opts, provider, model, providerStream, extras, signal) {
69178
69454
  const projectRoot = resolveHeadlessCwd(opts);
69179
69455
  const sessionId2 = opts.resumeSessionId ?? crypto.randomUUID();
69180
69456
  const spine = await openHeadlessSpine({
@@ -69285,6 +69561,7 @@ ${JSON.stringify({ deliverable: brief.deliverableThisMission, mvp: brief.sliceMv
69285
69561
  memory,
69286
69562
  emit,
69287
69563
  buildViaAgent,
69564
+ signal,
69288
69565
  onMissionPhase: (phase2, note) => spine.missionPhase(phase2, note),
69289
69566
  onMissionProgress: (advice, iteration) => spine.missionProgress({
69290
69567
  recommendation: advice.recommendation,
@@ -69310,7 +69587,7 @@ ${ragContext}` : slicePrompt;
69310
69587
  let writeCount = 0;
69311
69588
  let chairmanErrored = false;
69312
69589
  let membersCompleted = 0;
69313
- const scrub = createStreamScrubber2();
69590
+ const scrub = createStreamScrubber2({ stripQuestion: opts.output !== "json" });
69314
69591
  const { composeProjectContext: composeProjectContext3 } = await Promise.resolve().then(() => (init_composeContext(), composeContext_exports));
69315
69592
  const { loadDurableContext: loadDurableContext3 } = await Promise.resolve().then(() => (init_loadDurableContext(), loadDurableContext_exports));
69316
69593
  const memOnly = ragContext?.trim() ? ragContext : void 0;
@@ -69333,6 +69610,7 @@ ${ragContext}` : slicePrompt;
69333
69610
  tools: toolRegistry,
69334
69611
  feedbackStore,
69335
69612
  runMode: effectiveRunMode,
69613
+ signal,
69336
69614
  maxToolCallsChairman: chairmanBudget,
69337
69615
  ...implementerRetry ? { skipSpecialists: true } : {},
69338
69616
  workspaceContext: composed2.workspaceContext,
@@ -69380,11 +69658,20 @@ ${ragContext}` : slicePrompt;
69380
69658
  }
69381
69659
  let completionOk = false;
69382
69660
  let degraded = false;
69661
+ if (signal.aborted) {
69662
+ return {
69663
+ completionOk: false,
69664
+ ran: membersCompleted > 0 || synthesisText.length > 0,
69665
+ synthesisText: synthesisText || void 0,
69666
+ writeCount,
69667
+ degraded: true
69668
+ };
69669
+ }
69383
69670
  try {
69384
69671
  const { detectDegradedRun: detectDegradedRun3 } = await Promise.resolve().then(() => (init_council(), council_exports));
69385
69672
  const d = detectDegradedRun3({
69386
69673
  chairmanErrored,
69387
- councilAborted: false,
69674
+ councilAborted: signal.aborted,
69388
69675
  luciferWriteCount: writeCount,
69389
69676
  synthesisText,
69390
69677
  runMode: effectiveRunMode
@@ -69451,7 +69738,9 @@ ${ragContext}` : slicePrompt;
69451
69738
  const { registry: agentRegistry } = createBuiltinToolRegistry2({
69452
69739
  root: projectRoot,
69453
69740
  planMode: false,
69454
- permissionPolicy: defaultPermissionPolicy2()
69741
+ permissionPolicy: defaultPermissionPolicy2(),
69742
+ ...opts.onPermissionAsk ? { onPermissionAsk: opts.onPermissionAsk } : {},
69743
+ ...opts.onAskUser ? { onAskUser: opts.onAskUser } : {}
69455
69744
  });
69456
69745
  await registerHeadlessMcp(agentRegistry, opts);
69457
69746
  const durableState = await loadDurableContext2(projectRoot);
@@ -69518,7 +69807,10 @@ ${ragContext}` : slicePrompt;
69518
69807
  }
69519
69808
  });
69520
69809
  if (state3.status === "error") exitCode = exitCode || 3;
69521
- else if (state3.status === "success") {
69810
+ else if (state3.status === "cancelled") {
69811
+ exitCode = 0;
69812
+ spine.missionPhase("verification", "mission-cancelled");
69813
+ } else if (state3.status === "success") {
69522
69814
  const missionGate = await evaluateStrictBuildGate("build", {
69523
69815
  emit: (input) => spine.appendEvent(input),
69524
69816
  surface: "mission",
@@ -69556,7 +69848,8 @@ ${ragContext}` : slicePrompt;
69556
69848
  }
69557
69849
  await memory.close().catch(() => void 0);
69558
69850
  try {
69559
- if (exitCode === 0) await spine.close("completed");
69851
+ if (signal.aborted) await spine.close("stopped");
69852
+ else if (exitCode === 0) await spine.close("completed");
69560
69853
  else await spine.close(exitCode === 2 ? "error" : "stopped");
69561
69854
  } catch {
69562
69855
  }
@@ -69603,6 +69896,7 @@ var init_runHeadless = __esm({
69603
69896
  init_metrics3();
69604
69897
  init_headlessSpine();
69605
69898
  init_harnessStateEmit();
69899
+ init_liveTurnAbort();
69606
69900
  init_policyGate();
69607
69901
  init_policyLoadMode();
69608
69902
  init_runOneTurn();
@@ -71190,38 +71484,61 @@ function applyTurnPermissionPreset(input) {
71190
71484
  process.env[PRESET_ENV] = value;
71191
71485
  return true;
71192
71486
  }
71487
+ function isPermissionDecision(value) {
71488
+ return typeof value === "string" && PERMISSION_DECISIONS.includes(value);
71489
+ }
71193
71490
  function createServePermissionBridge(write, timeoutMs2 = 12e4) {
71194
71491
  const pending = /* @__PURE__ */ new Map();
71195
71492
  let seq = 0;
71196
- const settle = (requestId, decision) => {
71493
+ const settle = (requestId, decision, timedOut = false) => {
71197
71494
  const entry = pending.get(requestId);
71198
71495
  if (!entry) return false;
71199
71496
  pending.delete(requestId);
71200
71497
  clearTimeout(entry.timer);
71498
+ write(
71499
+ JSON.stringify({
71500
+ type: "permission.settled",
71501
+ requestId,
71502
+ decision,
71503
+ ...timedOut ? { timedOut: true } : {}
71504
+ })
71505
+ );
71201
71506
  entry.resolve(decision);
71202
71507
  return true;
71203
71508
  };
71204
71509
  return {
71205
71510
  onPermissionAsk(payload) {
71206
71511
  const requestId = `perm-${Date.now()}-${++seq}`;
71512
+ const categories = payload.categories && payload.categories.length > 0 ? payload.categories : payload.category ? payload.category.split(",").map((c) => c.trim()).filter(Boolean) : [];
71207
71513
  return new Promise((resolve9) => {
71208
71514
  const timer = setTimeout(() => {
71209
- settle(requestId, "deny");
71515
+ settle(requestId, "deny", true);
71210
71516
  }, timeoutMs2);
71211
- pending.set(requestId, { resolve: resolve9, timer });
71517
+ pending.set(requestId, { resolve: resolve9, timer, payload });
71212
71518
  write(
71213
71519
  JSON.stringify({
71214
71520
  type: "permission.request",
71215
71521
  requestId,
71216
71522
  tool: payload.tool,
71217
71523
  category: payload.category,
71524
+ categories,
71218
71525
  ...payload.inputPreview !== void 0 ? { inputPreview: payload.inputPreview } : {},
71219
71526
  ...payload.reason !== void 0 ? { reason: payload.reason } : {}
71220
71527
  })
71221
71528
  );
71222
71529
  });
71223
71530
  },
71224
- respond: settle,
71531
+ respond: (requestId, decision) => settle(requestId, decision, false),
71532
+ releaseGranted() {
71533
+ let n = 0;
71534
+ for (const [id3, entry] of [...pending]) {
71535
+ const cats = entry.payload.categories && entry.payload.categories.length > 0 ? entry.payload.categories : entry.payload.category ? entry.payload.category.split(",").map((c) => c.trim()).filter(Boolean) : [];
71536
+ if (isSessionGranted(entry.payload.tool, cats)) {
71537
+ if (settle(id3, "allow", false)) n += 1;
71538
+ }
71539
+ }
71540
+ return n;
71541
+ },
71225
71542
  pendingCount: () => pending.size
71226
71543
  };
71227
71544
  }
@@ -71233,8 +71550,11 @@ function servePermissionRespond(bridge, params) {
71233
71550
  if (typeof requestId !== "string" || requestId.length === 0) {
71234
71551
  return { accepted: false, reason: "permission.respond requires a non-empty string requestId" };
71235
71552
  }
71236
- if (decision !== "allow" && decision !== "deny") {
71237
- return { accepted: false, reason: "permission.respond decision must be 'allow' or 'deny'" };
71553
+ if (!isPermissionDecision(decision)) {
71554
+ return {
71555
+ accepted: false,
71556
+ reason: "permission.respond decision must be 'allow' | 'deny' | 'always-tool' | 'always-category'"
71557
+ };
71238
71558
  }
71239
71559
  return { accepted: bridge.respond(requestId, decision) };
71240
71560
  }
@@ -71244,18 +71564,104 @@ function asRegistryAskHandler(bridge) {
71244
71564
  const decision = await bridge.onPermissionAsk({
71245
71565
  tool: req.toolName,
71246
71566
  category: req.categories.join(",") || "other",
71567
+ categories: req.categories,
71247
71568
  reason,
71248
71569
  ...req.claims && req.claims.length > 0 ? { inputPreview: req.claims.map((c) => c.summary).join(" \xB7 ") } : {}
71249
71570
  });
71250
- return decision === "allow";
71571
+ if (decision === "deny") return false;
71572
+ if (decision === "always-tool") {
71573
+ grantSessionTool(req.toolName);
71574
+ bridge.releaseGranted();
71575
+ } else if (decision === "always-category") {
71576
+ for (const cat of req.categories) {
71577
+ grantSessionCategory(cat);
71578
+ }
71579
+ grantSessionTool(req.toolName);
71580
+ bridge.releaseGranted();
71581
+ }
71582
+ return true;
71251
71583
  };
71252
71584
  }
71253
- var SERVE_PERMISSION_PRESETS, PRESET_ENV;
71585
+ var SERVE_PERMISSION_PRESETS, PRESET_ENV, PERMISSION_DECISIONS;
71254
71586
  var init_permissionBridge = __esm({
71255
71587
  "src/cli/serve/permissionBridge.ts"() {
71256
71588
  "use strict";
71589
+ init_toolPermissions();
71257
71590
  SERVE_PERMISSION_PRESETS = ["standard", "strict", "yolo"];
71258
71591
  PRESET_ENV = "ZELARI_PERMISSION_PRESET";
71592
+ PERMISSION_DECISIONS = [
71593
+ "allow",
71594
+ "deny",
71595
+ "always-tool",
71596
+ "always-category"
71597
+ ];
71598
+ }
71599
+ });
71600
+
71601
+ // src/cli/serve/askUserBridge.ts
71602
+ function createServeAskUserBridge(write, timeoutMs2 = askUserTimeoutMs()) {
71603
+ const pending = /* @__PURE__ */ new Map();
71604
+ let seq = 0;
71605
+ const settle = (requestId, answer, timedOut = false) => {
71606
+ const entry = pending.get(requestId);
71607
+ if (!entry) return false;
71608
+ pending.delete(requestId);
71609
+ clearTimeout(entry.timer);
71610
+ write(
71611
+ JSON.stringify({
71612
+ type: "ask_user.settled",
71613
+ requestId,
71614
+ answer,
71615
+ ...timedOut ? { timedOut: true } : {}
71616
+ })
71617
+ );
71618
+ entry.resolve(answer);
71619
+ return true;
71620
+ };
71621
+ return {
71622
+ onAskUser(req) {
71623
+ const question = req.question.trim();
71624
+ const choices = req.choices.map((c) => c.trim()).filter(Boolean);
71625
+ if (choices.length < 2) return Promise.resolve(null);
71626
+ const requestId = `ask-${Date.now()}-${++seq}`;
71627
+ return new Promise((resolve9) => {
71628
+ const timer = setTimeout(() => {
71629
+ settle(requestId, null, true);
71630
+ }, timeoutMs2);
71631
+ pending.set(requestId, { resolve: resolve9, timer });
71632
+ write(
71633
+ JSON.stringify({
71634
+ type: "ask_user.request",
71635
+ requestId,
71636
+ question,
71637
+ choices,
71638
+ ...req.context ? { context: req.context } : {}
71639
+ })
71640
+ );
71641
+ });
71642
+ },
71643
+ respond: (requestId, answer) => settle(requestId, answer, false),
71644
+ pendingCount: () => pending.size
71645
+ };
71646
+ }
71647
+ function serveAskUserRespond(bridge, params) {
71648
+ if (!params || typeof params !== "object") {
71649
+ return { accepted: false, reason: "ask_user.respond requires an object params" };
71650
+ }
71651
+ const { requestId, answer } = params;
71652
+ if (typeof requestId !== "string" || requestId.length === 0) {
71653
+ return { accepted: false, reason: "ask_user.respond requires a non-empty string requestId" };
71654
+ }
71655
+ if (answer !== null && typeof answer !== "string") {
71656
+ return { accepted: false, reason: "ask_user.respond answer must be a string or null" };
71657
+ }
71658
+ const text = typeof answer === "string" ? answer.trim() : null;
71659
+ return { accepted: bridge.respond(requestId, text && text.length > 0 ? text : null) };
71660
+ }
71661
+ var init_askUserBridge = __esm({
71662
+ "src/cli/serve/askUserBridge.ts"() {
71663
+ "use strict";
71664
+ init_askUserTimeout();
71259
71665
  }
71260
71666
  });
71261
71667
 
@@ -71372,7 +71778,7 @@ function resolveTurnLspProvider(services) {
71372
71778
  const candidate = services?.lspManager;
71373
71779
  return candidate instanceof LspManager ? candidate : void 0;
71374
71780
  }
71375
- function createCliRunTurn(onPermissionAsk) {
71781
+ function createCliRunTurn(onPermissionAsk, onAskUser) {
71376
71782
  let streamPromise = null;
71377
71783
  const ensureStream = () => {
71378
71784
  if (!streamPromise) {
@@ -71399,6 +71805,7 @@ function createCliRunTurn(onPermissionAsk) {
71399
71805
  const { provider, model, stream } = await ensureStream();
71400
71806
  const opts = bindHarnessTurnOptions(input, deps.session.workspaceRoot);
71401
71807
  if (onPermissionAsk) opts.onPermissionAsk = onPermissionAsk;
71808
+ if (onAskUser) opts.onAskUser = onAskUser;
71402
71809
  applyTurnPermissionPreset(input);
71403
71810
  const lspProvider = resolveTurnLspProvider(deps.services);
71404
71811
  const exitCode = await dispatchHeadlessTurn(
@@ -71434,6 +71841,7 @@ function startHarnessServer(options = {}) {
71434
71841
  write(JSON.stringify(envelope));
71435
71842
  };
71436
71843
  const permissionBridge = createServePermissionBridge(write);
71844
+ const askUserBridge = createServeAskUserBridge(write);
71437
71845
  const dispatch = async (req) => {
71438
71846
  if (typeof req.method !== "string") {
71439
71847
  return { id: req.id ?? null, ok: false, error: { code: "bad_request", message: "missing method" } };
@@ -71447,11 +71855,21 @@ function startHarnessServer(options = {}) {
71447
71855
  result: servePermissionRespond(permissionBridge, params)
71448
71856
  };
71449
71857
  }
71858
+ case "ask_user.respond": {
71859
+ return {
71860
+ id: req.id ?? null,
71861
+ ok: true,
71862
+ result: serveAskUserRespond(askUserBridge, params)
71863
+ };
71864
+ }
71450
71865
  case "session.create": {
71451
71866
  const root = typeof params.workspaceRoot === "string" ? params.workspaceRoot : process.cwd();
71452
71867
  const session = server.createSession({
71453
71868
  workspaceRoot: root,
71454
- runTurn: options.runTurn ?? createCliRunTurn(asRegistryAskHandler(permissionBridge))
71869
+ runTurn: options.runTurn ?? createCliRunTurn(
71870
+ asRegistryAskHandler(permissionBridge),
71871
+ askUserBridge.onAskUser
71872
+ )
71455
71873
  });
71456
71874
  return { id: req.id ?? null, ok: true, result: { sessionId: session.id, workspaceRoot: session.workspaceRoot } };
71457
71875
  }
@@ -71605,6 +72023,7 @@ var init_harnessServer = __esm({
71605
72023
  init_policyGate();
71606
72024
  init_policyLoadMode();
71607
72025
  init_permissionBridge();
72026
+ init_askUserBridge();
71608
72027
  init_spineLockSweep();
71609
72028
  }
71610
72029
  });
@@ -72647,7 +73066,12 @@ function readPackageJson4() {
72647
73066
  }
72648
73067
  }
72649
73068
  function getGlobalPrefix() {
72650
- return (process.env.npm_config_prefix || process.env.NPM_CONFIG_PREFIX || "").trim() || tryExec("npm prefix -g");
73069
+ const fromNpm = tryExec("npm prefix -g");
73070
+ if (fromNpm) return fromNpm;
73071
+ return (process.env.npm_config_prefix || process.env.NPM_CONFIG_PREFIX || "").trim();
73072
+ }
73073
+ function isSourceCheckout() {
73074
+ return existsSync62(path95.join(packageRoot, "src", "cli", "main.ts")) && existsSync62(path95.join(packageRoot, "apps", "desktop", "package.json"));
72651
73075
  }
72652
73076
  function checkShim(pkgName) {
72653
73077
  const prefix = getGlobalPrefix();
@@ -72658,6 +73082,14 @@ function checkShim(pkgName) {
72658
73082
  const shimName = isWin ? "zelari-code.cmd" : "zelari-code";
72659
73083
  const shimPath = path95.join(prefix, shimName);
72660
73084
  if (!existsSync62(shimPath)) {
73085
+ const localBin = path95.join(packageRoot, "bin", "zelari-code.js");
73086
+ if (isSourceCheckout() && existsSync62(localBin)) {
73087
+ return WARN(
73088
+ `global shim not found at ${shimPath}
73089
+ source checkout \u2014 using ${localBin}
73090
+ optional: npm install -g ${pkgName}@latest --force`
73091
+ );
73092
+ }
72661
73093
  return FAIL(
72662
73094
  `shim not found at ${shimPath}
72663
73095
  fix: npm install -g ${pkgName}@latest --force`
@@ -73052,16 +73484,15 @@ __export(fixPath_exports, {
73052
73484
  });
73053
73485
  import { spawnSync as spawnSync3 } from "node:child_process";
73054
73486
  function getGlobalPrefix2() {
73055
- return (process.env.npm_config_prefix || process.env.NPM_CONFIG_PREFIX || "").trim() || (() => {
73056
- try {
73057
- return spawnSync3("npm", ["prefix", "-g"], {
73058
- encoding: "utf8",
73059
- stdio: ["ignore", "pipe", "ignore"]
73060
- }).stdout?.trim() ?? "";
73061
- } catch {
73062
- return "";
73063
- }
73064
- })();
73487
+ try {
73488
+ const fromNpm = spawnSync3("npm", ["prefix", "-g"], {
73489
+ encoding: "utf8",
73490
+ stdio: ["ignore", "pipe", "ignore"]
73491
+ }).stdout?.trim() ?? "";
73492
+ if (fromNpm) return fromNpm;
73493
+ } catch {
73494
+ }
73495
+ return (process.env.npm_config_prefix || process.env.NPM_CONFIG_PREFIX || "").trim();
73065
73496
  }
73066
73497
  function powershell(script) {
73067
73498
  try {
@@ -76700,22 +77131,7 @@ init_spineTelemetry();
76700
77131
 
76701
77132
  // src/cli/hooks/permissionPicker.ts
76702
77133
  init_toolPermissions();
76703
-
76704
- // src/cli/hooks/askUserTimeout.ts
76705
- function askUserTimeoutMs() {
76706
- const raw = process.env.ZELARI_ASK_USER_TIMEOUT_MS?.trim();
76707
- if (!raw) return 3e5;
76708
- const n = Number.parseInt(raw, 10);
76709
- if (!Number.isFinite(n) || n < 0) return 3e5;
76710
- return n;
76711
- }
76712
- function armPickerTimeout(onFire, ms) {
76713
- if (ms <= 0) return () => void 0;
76714
- const id3 = setTimeout(onFire, ms);
76715
- return () => clearTimeout(id3);
76716
- }
76717
-
76718
- // src/cli/hooks/permissionPicker.ts
77134
+ init_askUserTimeout();
76719
77135
  function createPermissionAskHandler(opts) {
76720
77136
  const { setPicker: setPicker2, appendSystem: appendSystem2 } = opts;
76721
77137
  return (req) => new Promise((resolve9) => {
@@ -76808,6 +77224,7 @@ ${detail}${note}${claimsBlock}
76808
77224
  }
76809
77225
 
76810
77226
  // src/cli/hooks/useChatTurn.ts
77227
+ init_askUserTimeout();
76811
77228
  init_toolPermissions();
76812
77229
  init_skills2();
76813
77230
  init_fileStateStore();
@@ -78737,6 +79154,7 @@ init_permissionBroker();
78737
79154
  init_brokerHandlers();
78738
79155
  import { useEffect as useEffect5, useRef as useRef5 } from "react";
78739
79156
  init_messageHelpers();
79157
+ init_askUserTimeout();
78740
79158
  function usePermissionBroker(opts) {
78741
79159
  const { setPicker: setPicker2, setMessages } = opts;
78742
79160
  const handleRef = useRef5(null);