zelari-code 2.1.2 → 2.3.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.
@@ -18246,14 +18246,15 @@ var init_filesystem = __esm({
18246
18246
  init_toolTypes();
18247
18247
  ReadFileArgsSchema = external_exports.object({
18248
18248
  path: external_exports.string().min(1),
18249
- startLine: external_exports.number().int().nonnegative().optional(),
18250
- endLine: external_exports.number().int().positive().optional(),
18249
+ startLine: external_exports.number().int().nonnegative().optional().describe("0-based first line to include. Range is applied to the full file before maxBytes."),
18250
+ endLine: external_exports.number().int().positive().optional().describe("Exclusive 0-based end line. Omit to read through EOF (still capped by maxBytes)."),
18251
18251
  maxBytes: external_exports.number().int().positive().max(1e7).default(1e6)
18252
18252
  });
18253
18253
  readFileTool = {
18254
18254
  name: "read_file",
18255
- description: "Read a file with optional line range. Returns content + metadata. Use before edit_file.",
18255
+ description: "Read a file with optional 0-based line range (endLine exclusive). maxBytes caps the selected range, not a prefix of the file. Returns content + metadata. Use before edit_file.",
18256
18256
  permissions: ["read"],
18257
+ sideEffect: "none",
18257
18258
  timeoutMs: 5e3,
18258
18259
  inputSchema: ReadFileArgsSchema,
18259
18260
  execute: async (args, ctx) => {
@@ -18262,24 +18263,31 @@ var init_filesystem = __esm({
18262
18263
  const buf = await fs4.readFile(absPath, { encoding: "utf-8", signal: ctx.signal });
18263
18264
  const content = typeof buf === "string" ? buf : buf.toString("utf-8");
18264
18265
  const allLines = content.split("\n");
18265
- const truncated = content.length > args.maxBytes ? content.slice(0, args.maxBytes) : content;
18266
- const lines = truncated === content ? allLines : truncated.split("\n");
18267
18266
  const totalLines = allLines.length;
18268
18267
  const start = args.startLine ?? 0;
18269
- const end = Math.min(args.endLine ?? lines.length, lines.length);
18270
- const wasTruncated = truncated !== content;
18268
+ const endExclusive = Math.min(args.endLine ?? totalLines, totalLines);
18271
18269
  const empty = content.length === 0;
18272
- const status = empty ? "empty" : wasTruncated ? "partial" : "complete";
18270
+ const rangeEmpty = !empty && (start >= totalLines || endExclusive <= start);
18271
+ const selected = rangeEmpty ? [] : allLines.slice(start, endExclusive);
18272
+ let text = selected.join("\n");
18273
+ const wasTruncated = text.length > args.maxBytes;
18274
+ if (wasTruncated)
18275
+ text = text.slice(0, args.maxBytes);
18276
+ const returnedLines = text.length === 0 ? 0 : text.split("\n").length;
18277
+ const readEnd = returnedLines === 0 ? Math.max(0, start - 1) : start + returnedLines - 1;
18278
+ const status = empty || rangeEmpty ? "empty" : wasTruncated ? "partial" : "complete";
18273
18279
  const warnings = [];
18274
18280
  if (empty)
18275
18281
  warnings.push("EMPTY_FILE");
18282
+ if (rangeEmpty)
18283
+ warnings.push("LINE_RANGE_EMPTY");
18276
18284
  if (wasTruncated)
18277
18285
  warnings.push("MAX_BYTES_TRUNCATED");
18278
18286
  return typedOk({
18279
18287
  path: absPath,
18280
- content: lines.slice(start, end).join("\n"),
18288
+ content: text,
18281
18289
  totalLines,
18282
- readLines: { start, end: end - 1 },
18290
+ readLines: { start, end: readEnd },
18283
18291
  sizeBytes: content.length
18284
18292
  }, {
18285
18293
  status,
@@ -18305,6 +18313,7 @@ var init_filesystem = __esm({
18305
18313
  name: "write_file",
18306
18314
  description: "Write or create a file. Use createDirs=true to auto-create parent directories.",
18307
18315
  permissions: ["write"],
18316
+ sideEffect: "local",
18308
18317
  timeoutMs: 1e4,
18309
18318
  inputSchema: WriteFileArgsSchema,
18310
18319
  execute: async (args, ctx) => {
@@ -18330,6 +18339,7 @@ var init_filesystem = __esm({
18330
18339
  name: "edit_file",
18331
18340
  description: "Replace exact string match in a file. Idempotent: returns 0 occurrences if no match.",
18332
18341
  permissions: ["write"],
18342
+ sideEffect: "local",
18333
18343
  timeoutMs: 1e4,
18334
18344
  inputSchema: EditFileArgsSchema,
18335
18345
  execute: async (args, ctx) => {
@@ -18561,6 +18571,7 @@ var init_shell = __esm({
18561
18571
  name: "bash",
18562
18572
  description: "Run a shell command. On Windows uses Git Bash when available (POSIX semantics: ls, $VAR, && work); falls back to cmd.exe otherwise. Streams stdout/stderr. Respects timeout and cancellation. Returns exit code. stdin is CLOSED (non-interactive): any command that prompts for input will fail or be cancelled \u2014 always pass non-interactive flags (--yes, -y, --template), and if a scaffolder insists on prompting (e.g. create-vite in a non-empty directory), create the files manually with write_file instead.",
18563
18573
  permissions: ["execute"],
18574
+ sideEffect: "local",
18564
18575
  timeoutMs: 6e4,
18565
18576
  inputSchema: BashArgsSchema,
18566
18577
  execute: async (args, ctx) => {
@@ -18870,6 +18881,7 @@ var init_search = __esm({
18870
18881
  name: "grep_content",
18871
18882
  description: 'Regex search for content in a file OR recursively in a directory. When path is a directory, include/exclude globs filter which files are searched (default: all files, excluding node_modules/dist/.git/etc.). Glob semantics: "*" matches ONE path segment only ("*.ts" does NOT match "sub/file.ts"); use "**" for recursive matching ("**/*.ts" matches at any depth). include/exclude accept a single glob string (e.g. "*.ts") OR an array of globs. Returns matches with line numbers and context, plus filesWalked/filesInTree counts and a warning when the include globs matched suspiciously few files.',
18872
18883
  permissions: ["read"],
18884
+ sideEffect: "none",
18873
18885
  timeoutMs: 3e4,
18874
18886
  inputSchema: GrepContentArgsSchema,
18875
18887
  execute: async (args, ctx) => {
@@ -18987,6 +18999,7 @@ var init_listFiles = __esm({
18987
18999
  name: "list_files",
18988
19000
  description: "List files and directories in the given path (defaults to the working directory). Returns names with types (file/directory). Use this to discover the project structure before reading specific files. Supports a maxDepth for recursive listing and excludes common dependency/build directories by default.",
18989
19001
  permissions: ["read"],
19002
+ sideEffect: "none",
18990
19003
  timeoutMs: 15e3,
18991
19004
  inputSchema: ListFilesArgsSchema,
18992
19005
  execute: async (args, ctx) => {
@@ -19241,6 +19254,7 @@ var init_diff = __esm({
19241
19254
  name: "show_diff",
19242
19255
  description: "Show unified diff between current file content and proposed content. Read-only \u2014 does NOT modify the file. Use this to preview edits before applying with edit_file or apply_diff.",
19243
19256
  permissions: ["read"],
19257
+ sideEffect: "none",
19244
19258
  timeoutMs: 15e3,
19245
19259
  inputSchema: ShowDiffArgsSchema,
19246
19260
  execute: async (args, ctx) => {
@@ -19343,6 +19357,7 @@ var init_diff = __esm({
19343
19357
  name: "apply_diff",
19344
19358
  description: "Apply a unified diff patch to a file. Parses ---/+++/@@ headers and applies each hunk sequentially. With fuzzyMatch=true, tolerates whitespace differences. With dryRun=true, returns the final content without writing. Atomic: if any hunk fails, no partial write occurs.",
19345
19359
  permissions: ["write"],
19360
+ sideEffect: "local",
19346
19361
  timeoutMs: 15e3,
19347
19362
  inputSchema: ApplyDiffArgsSchema,
19348
19363
  execute: async (args, ctx) => {
@@ -21471,6 +21486,42 @@ var init_types = __esm({
21471
21486
  }
21472
21487
  });
21473
21488
 
21489
+ // packages/core/dist/core/tools/concurrency.js
21490
+ function taskAgent(args) {
21491
+ if (!args || typeof args !== "object")
21492
+ return "explore";
21493
+ const agent = args.agent;
21494
+ return typeof agent === "string" && agent.trim() ? agent : "explore";
21495
+ }
21496
+ function classifyToolConcurrency(input) {
21497
+ const env = input.env ?? process.env;
21498
+ if (env.ZELARI_PARALLEL_TOOLS === "0")
21499
+ return "exclusive";
21500
+ const name = input.toolName;
21501
+ if (name === "task") {
21502
+ return taskAgent(input.args) === "general" ? "exclusive" : "parallel-safe";
21503
+ }
21504
+ if (input.registered === false) {
21505
+ if (name.startsWith("mcp_")) {
21506
+ const lower = name.toLowerCase();
21507
+ if (lower.includes("write") || lower.includes("edit") || lower.includes("delete")) {
21508
+ return "exclusive";
21509
+ }
21510
+ return "parallel-safe";
21511
+ }
21512
+ return "exclusive";
21513
+ }
21514
+ const perms = input.permissions ?? [];
21515
+ if (perms.includes("write") || perms.includes("execute"))
21516
+ return "exclusive";
21517
+ return "parallel-safe";
21518
+ }
21519
+ var init_concurrency = __esm({
21520
+ "packages/core/dist/core/tools/concurrency.js"() {
21521
+ "use strict";
21522
+ }
21523
+ });
21524
+
21474
21525
  // packages/core/dist/core/contextGrowth.js
21475
21526
  function emptyContextGrowthStats() {
21476
21527
  return {
@@ -22022,6 +22073,7 @@ var init_AgentHarness = __esm({
22022
22073
  "packages/core/dist/core/AgentHarness.js"() {
22023
22074
  "use strict";
22024
22075
  init_events();
22076
+ init_concurrency();
22025
22077
  init_toolTypes();
22026
22078
  init_contextGrowth();
22027
22079
  init_requestSnapshot();
@@ -22106,30 +22158,19 @@ var init_AgentHarness = __esm({
22106
22158
  };
22107
22159
  }
22108
22160
  /**
22109
- * v1.8.0: true when a tool may run concurrently with other parallel-safe
22110
- * tools. Write/execute tools stay serial (preserve file/order safety).
22111
- * `task` is parallel-safe (read-only sub-agents). Opt out: ZELARI_PARALLEL_TOOLS=0.
22161
+ * v1.8.0 / 2.x F: true when a tool may run concurrently with other
22162
+ * parallel-safe tools. Write/execute stay serial. `task agent=general`
22163
+ * is exclusive (it writes); explore/verify tentacles stay parallel-safe.
22164
+ * Opt out: ZELARI_PARALLEL_TOOLS=0.
22112
22165
  */
22113
- isParallelSafeTool(toolName) {
22114
- if (process.env.ZELARI_PARALLEL_TOOLS === "0")
22115
- return false;
22116
- if (toolName === "task")
22117
- return true;
22166
+ isParallelSafeTool(toolName, args) {
22118
22167
  const def = this.config.toolRegistry?.get(toolName);
22119
- if (!def) {
22120
- if (toolName.startsWith("mcp_")) {
22121
- const lower = toolName.toLowerCase();
22122
- if (lower.includes("write") || lower.includes("edit") || lower.includes("delete")) {
22123
- return false;
22124
- }
22125
- return true;
22126
- }
22127
- return false;
22128
- }
22129
- const perms = def.permissions ?? [];
22130
- if (perms.includes("write") || perms.includes("execute"))
22131
- return false;
22132
- return true;
22168
+ return classifyToolConcurrency({
22169
+ toolName,
22170
+ args,
22171
+ permissions: def?.permissions,
22172
+ registered: Boolean(def) || toolName === "task"
22173
+ }) === "parallel-safe";
22133
22174
  }
22134
22175
  /**
22135
22176
  * Execute buffered native tool calls: consecutive parallel-safe tools run
@@ -22234,13 +22275,13 @@ ${shared2.content}`,
22234
22275
  let i = 0;
22235
22276
  while (i < pending.length) {
22236
22277
  const p3 = pending[i];
22237
- if (p3.skipped || p3.cached !== void 0 || !this.isParallelSafeTool(p3.toolName)) {
22278
+ if (p3.skipped || p3.cached !== void 0 || !this.isParallelSafeTool(p3.toolName, p3.args)) {
22238
22279
  out[i] = toOut(p3, await invokeOne(p3));
22239
22280
  i += 1;
22240
22281
  continue;
22241
22282
  }
22242
22283
  let j = i;
22243
- while (j < pending.length && !pending[j].skipped && pending[j].cached === void 0 && this.isParallelSafeTool(pending[j].toolName)) {
22284
+ while (j < pending.length && !pending[j].skipped && pending[j].cached === void 0 && this.isParallelSafeTool(pending[j].toolName, pending[j].args)) {
22244
22285
  j += 1;
22245
22286
  }
22246
22287
  for (let off = i; off < j; off += maxParallel) {
@@ -28263,6 +28304,8 @@ var init_types8 = __esm({
28263
28304
  "assistant.message",
28264
28305
  "tool.call",
28265
28306
  "tool.result",
28307
+ // 2.x B (crash-safe recovery): dangling call classified. State-only.
28308
+ "tool.interrupted",
28266
28309
  "context.injected",
28267
28310
  "session.compacted",
28268
28311
  "task.created",
@@ -28515,6 +28558,82 @@ var init_writer = __esm({
28515
28558
  }
28516
28559
  });
28517
28560
 
28561
+ // packages/core/dist/session/recovery.js
28562
+ function sideEffectForTool(tool, declared) {
28563
+ if (declared)
28564
+ return declared;
28565
+ if (EXTERNAL.has(tool))
28566
+ return "external";
28567
+ if (READ_ONLY.has(tool))
28568
+ return "none";
28569
+ return "local";
28570
+ }
28571
+ function retrySafetyForSideEffect(sideEffect) {
28572
+ return sideEffect === "none" ? "safe" : "inspect-first";
28573
+ }
28574
+ function toolNameOf(call) {
28575
+ return typeof call.data.tool === "string" && call.data.tool.trim() ? call.data.tool : "unknown";
28576
+ }
28577
+ function callIdOf(call) {
28578
+ return typeof call.data.callId === "string" ? call.data.callId : `seq:${call.seq}`;
28579
+ }
28580
+ function classifyInterruptedTools(events, sideEffects = /* @__PURE__ */ new Map()) {
28581
+ const alreadyClassified = new Set(events.filter((e) => e.kind === "tool.interrupted").map((e) => typeof e.data.callId === "string" ? e.data.callId : void 0).filter((id) => Boolean(id)));
28582
+ const out = [];
28583
+ for (const pair of pairToolCalls(events)) {
28584
+ if (pair.result)
28585
+ continue;
28586
+ const callId = callIdOf(pair.call);
28587
+ if (alreadyClassified.has(callId))
28588
+ continue;
28589
+ const tool = toolNameOf(pair.call);
28590
+ const sideEffect = sideEffectForTool(tool, sideEffects.get(tool));
28591
+ const retrySafety = retrySafetyForSideEffect(sideEffect);
28592
+ out.push({
28593
+ toolCallSeq: pair.call.seq,
28594
+ callId,
28595
+ tool,
28596
+ state: retrySafety === "safe" ? "not-started" : "started-outcome-unknown",
28597
+ retrySafety,
28598
+ sideEffect
28599
+ });
28600
+ }
28601
+ return out;
28602
+ }
28603
+ function interruptedEventData(item) {
28604
+ return {
28605
+ toolCallSeq: item.toolCallSeq,
28606
+ callId: item.callId,
28607
+ tool: item.tool,
28608
+ state: item.state,
28609
+ retrySafety: item.retrySafety,
28610
+ sideEffect: item.sideEffect
28611
+ };
28612
+ }
28613
+ var READ_ONLY, EXTERNAL;
28614
+ var init_recovery = __esm({
28615
+ "packages/core/dist/session/recovery.js"() {
28616
+ "use strict";
28617
+ init_modelSurface();
28618
+ READ_ONLY = /* @__PURE__ */ new Set([
28619
+ "read_file",
28620
+ "list_files",
28621
+ "grep_content",
28622
+ "show_diff",
28623
+ "web_search",
28624
+ "fetch_url",
28625
+ "lsp_definition",
28626
+ "lsp_references",
28627
+ "lsp_hover",
28628
+ "lsp_symbols",
28629
+ "ast_outline",
28630
+ "ast_find_symbol",
28631
+ "semantic_search"
28632
+ ]);
28633
+ EXTERNAL = /* @__PURE__ */ new Set(["fetch_url", "web_search"]);
28634
+ }
28635
+ });
28636
+
28518
28637
  // packages/core/dist/session/replay.js
28519
28638
  import { promises as fs10 } from "node:fs";
28520
28639
  async function readSessionLog(filePath) {
@@ -28599,7 +28718,8 @@ function buildProjection(events, issues = []) {
28599
28718
  missionPhases: [],
28600
28719
  missionAdvice: [],
28601
28720
  replans: 0,
28602
- issues
28721
+ issues,
28722
+ interruptedTools: classifyInterruptedTools(events)
28603
28723
  };
28604
28724
  for (const e of events) {
28605
28725
  switch (e.kind) {
@@ -28650,6 +28770,7 @@ var init_replay = __esm({
28650
28770
  "use strict";
28651
28771
  init_types8();
28652
28772
  init_modelSurface();
28773
+ init_recovery();
28653
28774
  }
28654
28775
  });
28655
28776
 
@@ -28789,12 +28910,20 @@ async function forkSession(store6, parentSessionId, options = {}) {
28789
28910
  }
28790
28911
  async function resumeSession(store6, sessionId2) {
28791
28912
  const opened = await store6.open(sessionId2);
28913
+ const classified = [];
28914
+ for (const item of classifyInterruptedTools(opened.report.events)) {
28915
+ classified.push(await opened.writer.append({
28916
+ kind: "tool.interrupted",
28917
+ actor: ACTOR_SYSTEM,
28918
+ data: interruptedEventData(item)
28919
+ }));
28920
+ }
28792
28921
  const resumed = await opened.writer.append({
28793
28922
  kind: "session.resumed",
28794
28923
  actor: ACTOR_SYSTEM,
28795
28924
  data: { fromSeq: opened.projection.lastSeq }
28796
28925
  });
28797
- const projection = buildProjection([...opened.report.events, resumed]);
28926
+ const projection = buildProjection([...opened.report.events, ...classified, resumed]);
28798
28927
  return { writer: opened.writer, projection };
28799
28928
  }
28800
28929
  async function lineageOf(store6, sessionId2) {
@@ -28822,6 +28951,7 @@ var init_lineage = __esm({
28822
28951
  "use strict";
28823
28952
  init_types8();
28824
28953
  init_replay();
28954
+ init_recovery();
28825
28955
  }
28826
28956
  });
28827
28957
 
@@ -28862,6 +28992,104 @@ var init_exportSession = __esm({
28862
28992
  }
28863
28993
  });
28864
28994
 
28995
+ // packages/core/dist/session/invariants.js
28996
+ function asSeq(value) {
28997
+ return typeof value === "number" && Number.isInteger(value) && value > 0 ? value : void 0;
28998
+ }
28999
+ function collectEvidenceSeqs(events) {
29000
+ const out = [];
29001
+ const walk2 = (ownerSeq, value) => {
29002
+ if (Array.isArray(value)) {
29003
+ for (const item of value)
29004
+ walk2(ownerSeq, item);
29005
+ return;
29006
+ }
29007
+ if (!value || typeof value !== "object")
29008
+ return;
29009
+ const rec = value;
29010
+ const seq = asSeq(rec.seq);
29011
+ if (seq !== void 0 && ("digest" in rec || "tier" in rec || "ref" in rec)) {
29012
+ out.push({ ownerSeq, refSeq: seq });
29013
+ }
29014
+ for (const v of Object.values(rec))
29015
+ walk2(ownerSeq, v);
29016
+ };
29017
+ for (const e of events)
29018
+ walk2(e.seq, e.data);
29019
+ return out;
29020
+ }
29021
+ function validateSessionTrace(events, mode = "minimal") {
29022
+ const violations = [];
29023
+ let expected = 1;
29024
+ for (const e of events) {
29025
+ if (e.seq !== expected) {
29026
+ violations.push({
29027
+ code: "SEQ_NOT_MONOTONIC",
29028
+ seq: e.seq,
29029
+ message: `expected seq ${expected}, got ${e.seq}`
29030
+ });
29031
+ }
29032
+ expected = e.seq + 1;
29033
+ }
29034
+ const pairs = pairToolCalls(events);
29035
+ const resultCounts = /* @__PURE__ */ new Map();
29036
+ for (const e of events) {
29037
+ if (e.kind !== "tool.result")
29038
+ continue;
29039
+ const callId = typeof e.data.callId === "string" ? e.data.callId : `seq:${e.seq}`;
29040
+ resultCounts.set(callId, (resultCounts.get(callId) ?? 0) + 1);
29041
+ }
29042
+ for (const [callId, n] of resultCounts) {
29043
+ if (n > 1) {
29044
+ violations.push({
29045
+ code: "DUPLICATE_TOOL_RESULT",
29046
+ message: `callId ${callId} has ${n} tool.result events`
29047
+ });
29048
+ }
29049
+ }
29050
+ const interruptedCallIds = new Set(events.filter((e) => e.kind === "tool.interrupted").map((e) => typeof e.data.callId === "string" ? e.data.callId : void 0).filter((id) => Boolean(id)));
29051
+ for (const pair of pairs) {
29052
+ if (pair.result)
29053
+ continue;
29054
+ const callId = typeof pair.call.data.callId === "string" ? pair.call.data.callId : `seq:${pair.call.seq}`;
29055
+ if (interruptedCallIds.has(callId))
29056
+ continue;
29057
+ if (mode === "strict") {
29058
+ violations.push({
29059
+ code: "DANGLING_TOOL_CALL",
29060
+ seq: pair.call.seq,
29061
+ message: `tool.call ${callId} has no tool.result and no tool.interrupted`
29062
+ });
29063
+ }
29064
+ }
29065
+ const knownSeq = new Set(events.map((e) => e.seq));
29066
+ for (const ref of collectEvidenceSeqs(events)) {
29067
+ if (!knownSeq.has(ref.refSeq)) {
29068
+ violations.push({
29069
+ code: "EVIDENCE_SEQ_MISSING",
29070
+ seq: ref.ownerSeq,
29071
+ message: `EvidenceRef.seq ${ref.refSeq} is not an event in this trace`
29072
+ });
29073
+ }
29074
+ }
29075
+ const firstVerification = events.find((e) => e.kind === "verification.run");
29076
+ const firstEnded = events.find((e) => e.kind === "session.ended");
29077
+ if (firstVerification && firstEnded && firstEnded.seq < firstVerification.seq) {
29078
+ violations.push({
29079
+ code: "COMPLETION_BEFORE_VERIFICATION",
29080
+ seq: firstEnded.seq,
29081
+ message: `session.ended seq ${firstEnded.seq} precedes verification.run seq ${firstVerification.seq}`
29082
+ });
29083
+ }
29084
+ return violations;
29085
+ }
29086
+ var init_invariants = __esm({
29087
+ "packages/core/dist/session/invariants.js"() {
29088
+ "use strict";
29089
+ init_modelSurface();
29090
+ }
29091
+ });
29092
+
28865
29093
  // packages/core/dist/session/index.js
28866
29094
  var init_session = __esm({
28867
29095
  "packages/core/dist/session/index.js"() {
@@ -28874,6 +29102,8 @@ var init_session = __esm({
28874
29102
  init_store();
28875
29103
  init_lineage();
28876
29104
  init_exportSession();
29105
+ init_invariants();
29106
+ init_recovery();
28877
29107
  }
28878
29108
  });
28879
29109
 
@@ -29486,6 +29716,72 @@ var init_types9 = __esm({
29486
29716
  }
29487
29717
  });
29488
29718
 
29719
+ // packages/core/dist/verification/scopeDiscipline.js
29720
+ function normalize(p3) {
29721
+ return p3.replace(/\\/g, "/").replace(/^\.\//, "");
29722
+ }
29723
+ function isGeneratedPath(file2) {
29724
+ const n = normalize(file2);
29725
+ return GENERATED_NAME.test(n) || GENERATED_DIR.test(n);
29726
+ }
29727
+ function parseNameOnlyDiff(stdout) {
29728
+ return stdout.split(/\r?\n/).map((l) => l.trim()).filter(Boolean).map(normalize);
29729
+ }
29730
+ function analyzeScope(input) {
29731
+ const changed = input.changedFiles.map(normalize);
29732
+ if (changed.length === 0) {
29733
+ return {
29734
+ status: "unknown",
29735
+ expected: [],
29736
+ unexpected: [],
29737
+ generated: [],
29738
+ reasons: ["no changed files observed"]
29739
+ };
29740
+ }
29741
+ const expectedSet = new Set((input.expectedFiles ?? []).map(normalize));
29742
+ const generatedExtra = new Set((input.generatedFiles ?? []).map(normalize));
29743
+ const generated = [];
29744
+ const unexpected = [];
29745
+ const expectedHit = [];
29746
+ for (const file2 of changed) {
29747
+ if (generatedExtra.has(file2) || isGeneratedPath(file2)) {
29748
+ generated.push(file2);
29749
+ continue;
29750
+ }
29751
+ if (expectedSet.size === 0) {
29752
+ expectedHit.push(file2);
29753
+ continue;
29754
+ }
29755
+ if (expectedSet.has(file2))
29756
+ expectedHit.push(file2);
29757
+ else
29758
+ unexpected.push(file2);
29759
+ }
29760
+ const reasons = [];
29761
+ if (unexpected.length > 0) {
29762
+ reasons.push(`${unexpected.length} file(s) outside expected scope: ${unexpected.join(", ")}`);
29763
+ }
29764
+ if (generated.length > 0) {
29765
+ reasons.push(`${generated.length} generated/lockfile path(s) ignored for the concern`);
29766
+ }
29767
+ let status = "pass";
29768
+ if (expectedSet.size === 0) {
29769
+ status = "unknown";
29770
+ reasons.push("no expected-files allowlist \u2014 cannot judge scope");
29771
+ } else if (unexpected.length > 0) {
29772
+ status = "concern";
29773
+ }
29774
+ return { status, expected: expectedHit, unexpected, generated, reasons };
29775
+ }
29776
+ var GENERATED_NAME, GENERATED_DIR;
29777
+ var init_scopeDiscipline = __esm({
29778
+ "packages/core/dist/verification/scopeDiscipline.js"() {
29779
+ "use strict";
29780
+ GENERATED_NAME = /(?:^|\/)(?:package-lock\.json|pnpm-lock\.yaml|yarn\.lock|bun\.lockb|Cargo\.lock|go\.sum|poetry\.lock|composer\.lock)$/i;
29781
+ GENERATED_DIR = /(?:^|\/)(?:dist|build|out|coverage|\.next|node_modules)\//;
29782
+ }
29783
+ });
29784
+
29489
29785
  // packages/core/dist/verification/engine.js
29490
29786
  import { createHash as createHash4 } from "node:crypto";
29491
29787
  function defaultSha256(input) {
@@ -29498,6 +29794,7 @@ var VerificationEngine;
29498
29794
  var init_engine = __esm({
29499
29795
  "packages/core/dist/verification/engine.js"() {
29500
29796
  "use strict";
29797
+ init_scopeDiscipline();
29501
29798
  VerificationEngine = class {
29502
29799
  services;
29503
29800
  options;
@@ -29509,7 +29806,7 @@ var init_engine = __esm({
29509
29806
  async evaluate(criteria, context = {}) {
29510
29807
  const results = [];
29511
29808
  for (const criterion of criteria) {
29512
- results.push(await this.evaluateOne(criterion));
29809
+ results.push(await this.evaluateOne(criterion, context.scope));
29513
29810
  }
29514
29811
  if (this.options.emit) {
29515
29812
  try {
@@ -29559,7 +29856,7 @@ var init_engine = __esm({
29559
29856
  return void 0;
29560
29857
  }
29561
29858
  }
29562
- async evaluateOne(criterion) {
29859
+ async evaluateOne(criterion, scope) {
29563
29860
  const started = this.options.now?.() ?? Date.now();
29564
29861
  const base = {
29565
29862
  criterionId: criterion.id,
@@ -29571,6 +29868,15 @@ var init_engine = __esm({
29571
29868
  ...patch,
29572
29869
  durationMs: Math.max(0, (this.options.now?.() ?? Date.now()) - started)
29573
29870
  });
29871
+ if (criterion.id === "quality.scope-discipline" && scope) {
29872
+ const analysis = analyzeScope(scope);
29873
+ const status = analysis.status === "pass" ? "pass" : "unknown";
29874
+ return done({
29875
+ status,
29876
+ evidence: [],
29877
+ detail: analysis.reasons.join("; ") || analysis.status
29878
+ });
29879
+ }
29574
29880
  const check2 = criterion.check;
29575
29881
  if (!check2 || check2.kind === "none") {
29576
29882
  return done({
@@ -30184,6 +30490,7 @@ var init_verification2 = __esm({
30184
30490
  init_criteriaPack_v1();
30185
30491
  init_metrics();
30186
30492
  init_verifier();
30493
+ init_scopeDiscipline();
30187
30494
  }
30188
30495
  });
30189
30496
 
@@ -30448,6 +30755,7 @@ __export(dist_exports, {
30448
30755
  WorkspacePathEscapeError: () => WorkspacePathEscapeError,
30449
30756
  WorktreeWorkspace: () => WorktreeWorkspace,
30450
30757
  ZELARI_CODING_PACK_ID: () => ZELARI_CODING_PACK_ID,
30758
+ analyzeScope: () => analyzeScope,
30451
30759
  applyCompletionRetry: () => applyCompletionRetry,
30452
30760
  applyDeterministicAutofix: () => applyDeterministicAutofix,
30453
30761
  applyImplementationWriteRetry: () => applyImplementationWriteRetry,
@@ -30478,6 +30786,7 @@ __export(dist_exports, {
30478
30786
  checkImplementationDelivery: () => checkImplementationDelivery,
30479
30787
  checkMemberToolEmissionSets: () => checkMemberToolEmissionSets,
30480
30788
  checkMemberToolEmissions: () => checkMemberToolEmissions,
30789
+ classifyInterruptedTools: () => classifyInterruptedTools,
30481
30790
  classifyMission: () => classifyMission,
30482
30791
  classifyTaskScope: () => classifyTaskScope,
30483
30792
  cleanAgentContent: () => cleanAgentContent,
@@ -30545,6 +30854,7 @@ __export(dist_exports, {
30545
30854
  hasInteractiveClarification: () => hasInteractiveClarification,
30546
30855
  hashToolCall: () => hashToolCall,
30547
30856
  hookMatches: () => hookMatches,
30857
+ interruptedEventData: () => interruptedEventData,
30548
30858
  isAnswerLeak: () => isAnswerLeak,
30549
30859
  isBrainAgentEndEvent: () => isBrainAgentEndEvent,
30550
30860
  isBrainAgentStartEvent: () => isBrainAgentStartEvent,
@@ -30568,6 +30878,7 @@ __export(dist_exports, {
30568
30878
  isConverged: () => isConverged,
30569
30879
  isEventBackedEvidence: () => isEventBackedEvidence,
30570
30880
  isExperimentalEnabled: () => isExperimentalEnabled,
30881
+ isGeneratedPath: () => isGeneratedPath,
30571
30882
  isModelSurfaceEvent: () => isModelSurfaceEvent,
30572
30883
  isReviewerKind: () => isReviewerKind,
30573
30884
  isSettled: () => isSettled,
@@ -30594,6 +30905,7 @@ __export(dist_exports, {
30594
30905
  parseClarificationRequest: () => parseClarificationRequest,
30595
30906
  parseEvidenceTier: () => parseEvidenceTier,
30596
30907
  parseMinimaxStyleToolCalls: () => parseMinimaxStyleToolCalls,
30908
+ parseNameOnlyDiff: () => parseNameOnlyDiff,
30597
30909
  parsePersonaVerdict: () => parsePersonaVerdict,
30598
30910
  parseProjectRootFromWorkspaceContext: () => parseProjectRootFromWorkspaceContext,
30599
30911
  parseTextToolCalls: () => parseTextToolCalls,
@@ -30631,6 +30943,7 @@ __export(dist_exports, {
30631
30943
  resolveVerifyRetryTool: () => resolveVerifyRetryTool,
30632
30944
  restrictImplementationWrites: () => restrictImplementationWrites,
30633
30945
  resumeSession: () => resumeSession,
30946
+ retrySafetyForSideEffect: () => retrySafetyForSideEffect,
30634
30947
  runChairmanDeliveryLoop: () => runChairmanDeliveryLoop,
30635
30948
  runChairmanFixLoop: () => runChairmanFixLoop,
30636
30949
  runChairmanMicroGate: () => runChairmanMicroGate,
@@ -30647,6 +30960,7 @@ __export(dist_exports, {
30647
30960
  setWorkspaceStubs: () => setWorkspaceStubs,
30648
30961
  sha256Hex: () => sha256Hex,
30649
30962
  shouldRetryMember: () => shouldRetryMember,
30963
+ sideEffectForTool: () => sideEffectForTool,
30650
30964
  slugify: () => slugify2,
30651
30965
  snapshotToCompletionEvaluation: () => snapshotToCompletionEvaluation,
30652
30966
  specificityFromAssumptions: () => specificityFromAssumptions,
@@ -30666,6 +30980,7 @@ __export(dist_exports, {
30666
30980
  utf8Bytes: () => utf8Bytes,
30667
30981
  validateCodingSkillRequires: () => validateCodingSkillRequires,
30668
30982
  validateGraph: () => validateGraph,
30983
+ validateSessionTrace: () => validateSessionTrace,
30669
30984
  verificationCostRatio: () => verificationCostRatio,
30670
30985
  verifiedSolveRate: () => verifiedSolveRate,
30671
30986
  verifyCitations: () => verifyCitations,
@@ -32967,9 +33282,25 @@ var init_registry2 = __esm({
32967
33282
  return typedErr(`Invalid input: ${parsed.error.message}`);
32968
33283
  }
32969
33284
  const timeoutMs = options.timeoutMs ?? tool.timeoutMs ?? 3e4;
32970
- const signal = options.signal;
33285
+ const parentSignal = options.signal;
33286
+ const controller = new AbortController();
33287
+ let timer;
33288
+ let rejectRace;
33289
+ const onParentAbort = () => {
33290
+ if (timer !== void 0)
33291
+ clearTimeout(timer);
33292
+ if (!controller.signal.aborted)
33293
+ controller.abort();
33294
+ rejectRace?.(new Error(`Tool "${name}" aborted`));
33295
+ };
33296
+ if (parentSignal) {
33297
+ if (parentSignal.aborted)
33298
+ onParentAbort();
33299
+ else
33300
+ parentSignal.addEventListener("abort", onParentAbort);
33301
+ }
32971
33302
  const ctx = {
32972
- signal: signal ?? new AbortController().signal,
33303
+ signal: controller.signal,
32973
33304
  cwd: options.cwd ?? process.cwd(),
32974
33305
  audit: () => {
32975
33306
  },
@@ -32982,6 +33313,7 @@ var init_registry2 = __esm({
32982
33313
  cwd: ctx.cwd
32983
33314
  });
32984
33315
  if (!pre.ok) {
33316
+ parentSignal?.removeEventListener("abort", onParentAbort);
32985
33317
  return typedErr(`[hook:${pre.hookName ?? "unknown"}] ${pre.reason ?? "denied"}`);
32986
33318
  }
32987
33319
  } catch (hookErr) {
@@ -32991,11 +33323,16 @@ var init_registry2 = __esm({
32991
33323
  const result = await Promise.race([
32992
33324
  tool.execute(parsed.data, ctx),
32993
33325
  new Promise((_, reject) => {
32994
- const timer = setTimeout(() => reject(new Error(`Tool "${name}" timed out after ${timeoutMs}ms`)), timeoutMs);
32995
- signal?.addEventListener("abort", () => {
32996
- clearTimeout(timer);
32997
- reject(new Error(`Tool "${name}" aborted`));
32998
- });
33326
+ rejectRace = reject;
33327
+ if (parentSignal?.aborted) {
33328
+ onParentAbort();
33329
+ return;
33330
+ }
33331
+ timer = setTimeout(() => {
33332
+ if (!controller.signal.aborted)
33333
+ controller.abort();
33334
+ reject(new Error(`Tool "${name}" timed out after ${timeoutMs}ms`));
33335
+ }, timeoutMs);
32999
33336
  })
33000
33337
  ]);
33001
33338
  if (result.ok) {
@@ -33030,6 +33367,11 @@ var init_registry2 = __esm({
33030
33367
  } catch (err) {
33031
33368
  const error51 = err instanceof Error ? err.message : String(err);
33032
33369
  return typedErr(error51);
33370
+ } finally {
33371
+ rejectRace = void 0;
33372
+ if (timer !== void 0)
33373
+ clearTimeout(timer);
33374
+ parentSignal?.removeEventListener("abort", onParentAbort);
33033
33375
  }
33034
33376
  }
33035
33377
  /** Return all tool definitions in OpenAI function-calling format (memoized). */
@@ -33829,7 +34171,7 @@ var init_candidateRegistry = __esm({
33829
34171
  });
33830
34172
 
33831
34173
  // src/cli/kraken/verifyReport.ts
33832
- function normalize(text) {
34174
+ function normalize2(text) {
33833
34175
  return text.toLowerCase().replace(/\s+/g, " ").trim();
33834
34176
  }
33835
34177
  function extractVerifyReportBlocks(raw) {
@@ -33858,11 +34200,11 @@ function extractVerifyReportBlocks(raw) {
33858
34200
  function parseVerifyReport(raw, requiredChecks) {
33859
34201
  const byCriterion = /* @__PURE__ */ new Map();
33860
34202
  for (const block of extractVerifyReportBlocks(raw)) {
33861
- byCriterion.set(normalize(block.check), block);
34203
+ byCriterion.set(normalize2(block.check), block);
33862
34204
  }
33863
34205
  const keys = [...byCriterion.keys()];
33864
34206
  return requiredChecks.map((check2) => {
33865
- const norm = normalize(check2);
34207
+ const norm = normalize2(check2);
33866
34208
  let block = byCriterion.get(norm) ?? null;
33867
34209
  if (!block) {
33868
34210
  for (const key of keys) {
@@ -33901,18 +34243,18 @@ var init_verifyReport = __esm({
33901
34243
  });
33902
34244
 
33903
34245
  // src/cli/kraken/completionGate.ts
33904
- function normalize2(text) {
34246
+ function normalize3(text) {
33905
34247
  return text.toLowerCase().replace(/\s+/g, " ").trim();
33906
34248
  }
33907
34249
  function classifyKrakenChecks(requiredChecks, results) {
33908
34250
  const byCriterion = /* @__PURE__ */ new Map();
33909
34251
  for (const result of results ?? []) {
33910
- byCriterion.set(normalize2(result.check), result);
34252
+ byCriterion.set(normalize3(result.check), result);
33911
34253
  }
33912
34254
  const keys = [...byCriterion.keys()];
33913
34255
  const out = { passed: [], failed: [], unknown: [] };
33914
34256
  for (const check2 of requiredChecks) {
33915
- const norm = normalize2(check2);
34257
+ const norm = normalize3(check2);
33916
34258
  let result = byCriterion.get(norm) ?? null;
33917
34259
  if (!result) {
33918
34260
  for (const key of keys) {
@@ -34137,69 +34479,79 @@ async function runSubAgent(harness, opts = {}) {
34137
34479
  let usage;
34138
34480
  const pendingTools = /* @__PURE__ */ new Map();
34139
34481
  const toolTrace = [];
34140
- if (signal?.aborted) return { result: "", aborted: true };
34141
- for await (const ev of harness.run()) {
34142
- if (signal?.aborted) {
34143
- return {
34144
- result: (lastCompleted || current).trim(),
34145
- ...error51 ? { error: error51 } : {},
34146
- aborted: true,
34147
- ...toolTrace.length > 0 ? { toolTrace } : {}
34148
- };
34149
- }
34150
- if (ev.type === "tool_execution_start") {
34151
- pendingTools.set(ev.toolCallId, { tool: ev.toolName, command: toolCommandHint(ev.args) });
34152
- } else if (ev.type === "tool_execution_end") {
34153
- const started = pendingTools.get(ev.toolCallId);
34154
- pendingTools.delete(ev.toolCallId);
34155
- toolTrace.push({
34156
- tool: started?.tool ?? "unknown",
34157
- callId: ev.toolCallId,
34158
- ok: !ev.isError,
34159
- ...started?.command ? { command: started.command } : {},
34160
- output: String(ev.result ?? "").slice(0, TOOL_TRACE_OUTPUT_MAX),
34161
- durationMs: ev.durationMs,
34162
- endedAt: Date.now()
34163
- });
34164
- if (toolTrace.length > TOOL_TRACE_RING) {
34165
- toolTrace.splice(0, toolTrace.length - TOOL_TRACE_RING);
34482
+ const onAbort = () => harness.cancel?.();
34483
+ if (signal?.aborted) {
34484
+ onAbort();
34485
+ return { result: "", aborted: true };
34486
+ }
34487
+ signal?.addEventListener("abort", onAbort, { once: true });
34488
+ try {
34489
+ for await (const ev of harness.run()) {
34490
+ if (signal?.aborted) {
34491
+ onAbort();
34492
+ return {
34493
+ result: (lastCompleted || current).trim(),
34494
+ ...error51 ? { error: error51 } : {},
34495
+ aborted: true,
34496
+ ...toolTrace.length > 0 ? { toolTrace } : {}
34497
+ };
34166
34498
  }
34167
- }
34168
- switch (ev.type) {
34169
- case "message_start":
34170
- current = "";
34171
- break;
34172
- case "message_delta":
34173
- current += ev.delta;
34174
- break;
34175
- case "message_end":
34176
- if (current.trim()) lastCompleted = current;
34177
- if (ev.usage) {
34178
- usage = usage ? {
34179
- promptTokens: usage.promptTokens + ev.usage.promptTokens,
34180
- completionTokens: usage.completionTokens + ev.usage.completionTokens,
34181
- totalTokens: usage.totalTokens + ev.usage.totalTokens,
34182
- ...(usage.cachedPromptTokens ?? 0) + (ev.usage.cachedPromptTokens ?? 0) > 0 ? {
34183
- cachedPromptTokens: (usage.cachedPromptTokens ?? 0) + (ev.usage.cachedPromptTokens ?? 0)
34184
- } : {}
34185
- } : ev.usage;
34499
+ if (ev.type === "tool_execution_start") {
34500
+ pendingTools.set(ev.toolCallId, { tool: ev.toolName, command: toolCommandHint(ev.args) });
34501
+ } else if (ev.type === "tool_execution_end") {
34502
+ const started = pendingTools.get(ev.toolCallId);
34503
+ pendingTools.delete(ev.toolCallId);
34504
+ toolTrace.push({
34505
+ tool: started?.tool ?? "unknown",
34506
+ callId: ev.toolCallId,
34507
+ ok: !ev.isError,
34508
+ ...started?.command ? { command: started.command } : {},
34509
+ output: String(ev.result ?? "").slice(0, TOOL_TRACE_OUTPUT_MAX),
34510
+ durationMs: ev.durationMs,
34511
+ endedAt: Date.now()
34512
+ });
34513
+ if (toolTrace.length > TOOL_TRACE_RING) {
34514
+ toolTrace.splice(0, toolTrace.length - TOOL_TRACE_RING);
34186
34515
  }
34187
- current = "";
34188
- break;
34189
- case "error":
34190
- error51 = ev.message;
34191
- break;
34192
- default:
34193
- break;
34516
+ }
34517
+ switch (ev.type) {
34518
+ case "message_start":
34519
+ current = "";
34520
+ break;
34521
+ case "message_delta":
34522
+ current += ev.delta;
34523
+ break;
34524
+ case "message_end":
34525
+ if (current.trim()) lastCompleted = current;
34526
+ if (ev.usage) {
34527
+ usage = usage ? {
34528
+ promptTokens: usage.promptTokens + ev.usage.promptTokens,
34529
+ completionTokens: usage.completionTokens + ev.usage.completionTokens,
34530
+ totalTokens: usage.totalTokens + ev.usage.totalTokens,
34531
+ ...(usage.cachedPromptTokens ?? 0) + (ev.usage.cachedPromptTokens ?? 0) > 0 ? {
34532
+ cachedPromptTokens: (usage.cachedPromptTokens ?? 0) + (ev.usage.cachedPromptTokens ?? 0)
34533
+ } : {}
34534
+ } : ev.usage;
34535
+ }
34536
+ current = "";
34537
+ break;
34538
+ case "error":
34539
+ error51 = ev.message;
34540
+ break;
34541
+ default:
34542
+ break;
34543
+ }
34194
34544
  }
34545
+ const result = (lastCompleted || current).trim();
34546
+ return {
34547
+ result,
34548
+ ...error51 ? { error: error51 } : {},
34549
+ ...usage ? { usage } : {},
34550
+ ...toolTrace.length > 0 ? { toolTrace } : {}
34551
+ };
34552
+ } finally {
34553
+ signal?.removeEventListener("abort", onAbort);
34195
34554
  }
34196
- const result = (lastCompleted || current).trim();
34197
- return {
34198
- result,
34199
- ...error51 ? { error: error51 } : {},
34200
- ...usage ? { usage } : {},
34201
- ...toolTrace.length > 0 ? { toolTrace } : {}
34202
- };
34203
34555
  }
34204
34556
  async function runTentacle(opts) {
34205
34557
  const { deps, args, agent, thoroughness, parentCwd, sessionId: sessionId2 } = opts;
@@ -34429,7 +34781,7 @@ function createTaskTool(deps, policy = {}) {
34429
34781
  description: "Delegate a focused sub-task to an isolated sub-agent with its own context; returns only a concise conclusion (keeps parent context lean).\n- agent=explore (default): read-only research/search\n- agent=general: can edit files for one bounded unit of work\n- agent=verify: read + bash to run tests/checks\nProvide a fully self-contained `prompt` (sub-agent cannot see this conversation). Optional scope[] + acceptance[] contracts. After general, follow up with verify." + (restricted ? `
34430
34782
  RESTRICTED in this mode: only agent=${allowedAgents.join("|")} is allowed.` : ""),
34431
34783
  permissions: ["read", "network", "write", "execute"],
34432
- timeoutMs: 3e5,
34784
+ timeoutMs: TASK_TOOL_TIMEOUT_MS,
34433
34785
  inputSchema: inputSchema2,
34434
34786
  execute: async (args, ctx) => {
34435
34787
  const agent = args.agent ?? "explore";
@@ -34532,7 +34884,7 @@ ${res.result}${res.footer}`,
34532
34884
  }
34533
34885
  };
34534
34886
  }
34535
- var EXPLORE_PROMPT, GENERAL_PROMPT, VERIFY_PROMPT, TaskArgsSchema, TaskPurposeSchema, TaskArgsWithPurposeSchema, TOOL_TRACE_RING, TOOL_TRACE_OUTPUT_MAX;
34887
+ var TASK_TOOL_TIMEOUT_MS, EXPLORE_PROMPT, GENERAL_PROMPT, VERIFY_PROMPT, TaskArgsSchema, TaskPurposeSchema, TaskArgsWithPurposeSchema, TOOL_TRACE_RING, TOOL_TRACE_OUTPUT_MAX;
34536
34888
  var init_taskTool = __esm({
34537
34889
  "src/cli/tools/taskTool.ts"() {
34538
34890
  "use strict";
@@ -34544,6 +34896,7 @@ var init_taskTool = __esm({
34544
34896
  init_candidateRegistry();
34545
34897
  init_verifyReport();
34546
34898
  init_metrics3();
34899
+ TASK_TOOL_TIMEOUT_MS = 9e5;
34547
34900
  EXPLORE_PROMPT = [
34548
34901
  "You are a focused EXPLORE tentacle of Kraken (parent super-agent).",
34549
34902
  "READ-ONLY tools only (read, list, grep, fetch). No edits, no shell.",
@@ -39077,7 +39430,7 @@ import path38 from "node:path";
39077
39430
  function trustStorePath() {
39078
39431
  return _overrideStorePath ?? path38.join(homedir8(), ".zelari-code", "trust.json");
39079
39432
  }
39080
- function normalize3(p3) {
39433
+ function normalize4(p3) {
39081
39434
  const resolved = path38.resolve(p3);
39082
39435
  return process.platform === "win32" ? resolved.toLowerCase() : resolved;
39083
39436
  }
@@ -39114,14 +39467,14 @@ function isFolderTrusted(folderPath) {
39114
39467
  const env = envTrustedFolder();
39115
39468
  if (env === "all") return true;
39116
39469
  if (env === "none") return false;
39117
- if (env) return normalize3(env) === normalize3(folderPath);
39118
- const target = normalize3(folderPath);
39119
- return readStore3().folders.some((f) => normalize3(f.path) === target);
39470
+ if (env) return normalize4(env) === normalize4(folderPath);
39471
+ const target = normalize4(folderPath);
39472
+ return readStore3().folders.some((f) => normalize4(f.path) === target);
39120
39473
  }
39121
39474
  function trustFolder(folderPath) {
39122
39475
  const store6 = readStore3();
39123
39476
  const normalized = path38.resolve(folderPath);
39124
- if (!store6.folders.some((f) => normalize3(f.path) === normalize3(normalized))) {
39477
+ if (!store6.folders.some((f) => normalize4(f.path) === normalize4(normalized))) {
39125
39478
  store6.folders.push({ path: normalized, trustedAt: (/* @__PURE__ */ new Date()).toISOString() });
39126
39479
  writeStore3(store6);
39127
39480
  }
@@ -39129,9 +39482,9 @@ function trustFolder(folderPath) {
39129
39482
  }
39130
39483
  function untrustFolder(folderPath) {
39131
39484
  const store6 = readStore3();
39132
- const target = normalize3(folderPath);
39485
+ const target = normalize4(folderPath);
39133
39486
  const before = store6.folders.length;
39134
- store6.folders = store6.folders.filter((f) => normalize3(f.path) !== target);
39487
+ store6.folders = store6.folders.filter((f) => normalize4(f.path) !== target);
39135
39488
  if (store6.folders.length === before) return { ok: true, removed: false };
39136
39489
  writeStore3(store6);
39137
39490
  return { ok: true, removed: true };
@@ -47538,7 +47891,7 @@ var init_executor = __esm({
47538
47891
  DEFAULT_MAX_REVIEW_ROUNDS = 1;
47539
47892
  DEFAULT_GRAPH_TIMEOUT_MS = 0;
47540
47893
  DEFAULT_NODE_TIMEOUT_MS = 3e5;
47541
- DEFAULT_WRITER_NODE_TIMEOUT_MS = 9e5;
47894
+ DEFAULT_WRITER_NODE_TIMEOUT_MS = TASK_TOOL_TIMEOUT_MS;
47542
47895
  DEFAULT_CANCEL_GRACE_MS = 3e4;
47543
47896
  MAX_UPSTREAM_CHARS_PER_DEP = 2800;
47544
47897
  MAX_UPSTREAM_CHARS_TOTAL = 8e3;
@@ -55543,11 +55896,11 @@ function criterionId(check2, index) {
55543
55896
  const slug = check2.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48);
55544
55897
  return `check-${index + 1}-${slug || "criterion"}`;
55545
55898
  }
55546
- function normalize4(text) {
55899
+ function normalize5(text) {
55547
55900
  return text.toLowerCase().replace(/\s+/g, " ").trim();
55548
55901
  }
55549
55902
  function matchResult(check2, byNormalized) {
55550
- const norm = normalize4(check2);
55903
+ const norm = normalize5(check2);
55551
55904
  const direct = byNormalized.get(norm);
55552
55905
  if (direct) return direct;
55553
55906
  for (const key of byNormalized.keys()) {
@@ -55559,7 +55912,7 @@ function matchResult(check2, byNormalized) {
55559
55912
  }
55560
55913
  function krakenResultsToContract(requiredChecks, results, now = Date.now()) {
55561
55914
  const byNormalized = /* @__PURE__ */ new Map();
55562
- for (const r of results ?? []) byNormalized.set(normalize4(r.check), r);
55915
+ for (const r of results ?? []) byNormalized.set(normalize5(r.check), r);
55563
55916
  const criteria = [];
55564
55917
  const verifications = [];
55565
55918
  requiredChecks.forEach((check2, i) => {
@@ -55595,16 +55948,16 @@ function sha256Hex2(input) {
55595
55948
  return createHash10("sha256").update(input).digest("hex");
55596
55949
  }
55597
55950
  function matchNoteToToolTrace(note, trace) {
55598
- const n = normalize4(note);
55951
+ const n = normalize5(note);
55599
55952
  if (!n) return null;
55600
55953
  for (let i = trace.length - 1; i >= 0; i--) {
55601
55954
  const t = trace[i];
55602
- const cmd = t.command ? normalize4(t.command) : "";
55955
+ const cmd = t.command ? normalize5(t.command) : "";
55603
55956
  if (cmd.length >= 4 && (n.includes(cmd) || cmd.includes(n))) return t;
55604
55957
  }
55605
55958
  for (let i = trace.length - 1; i >= 0; i--) {
55606
55959
  const t = trace[i];
55607
- const out = normalize4(t.output);
55960
+ const out = normalize5(t.output);
55608
55961
  if (!out) continue;
55609
55962
  if (n.length >= 8 && out.includes(n)) return t;
55610
55963
  const fragments = n.match(/\S*\d[\d.,/%]*\S*/g) ?? [];