zelari-code 2.12.0 → 2.13.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.
@@ -18235,15 +18235,66 @@ var init_toolTypes = __esm({
18235
18235
  }
18236
18236
  });
18237
18237
 
18238
+ // packages/core/dist/core/tools/builtin/newlines.js
18239
+ function detectNewline(text) {
18240
+ if (text.includes("\r\n"))
18241
+ return "\r\n";
18242
+ if (text.includes("\r"))
18243
+ return "\r";
18244
+ return "\n";
18245
+ }
18246
+ function toLF(text) {
18247
+ return text.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
18248
+ }
18249
+ function fromLF(text, nl) {
18250
+ if (nl === "\n")
18251
+ return text;
18252
+ return text.replace(/\n/g, nl);
18253
+ }
18254
+ function splitLinesLF(text) {
18255
+ return toLF(text).split("\n");
18256
+ }
18257
+ var init_newlines = __esm({
18258
+ "packages/core/dist/core/tools/builtin/newlines.js"() {
18259
+ "use strict";
18260
+ }
18261
+ });
18262
+
18238
18263
  // packages/core/dist/core/tools/builtin/filesystem.js
18239
18264
  import { promises as fs4 } from "node:fs";
18240
18265
  import path6 from "node:path";
18266
+ function replaceFileString(text, oldString, newString, replaceAll) {
18267
+ const exact = replaceOnceOrAll(text, oldString, newString, replaceAll);
18268
+ if (exact.occurrences > 0)
18269
+ return exact;
18270
+ const nl = detectNewline(text);
18271
+ const normalized = replaceOnceOrAll(toLF(text), toLF(oldString), toLF(newString), replaceAll);
18272
+ if (normalized.occurrences === 0)
18273
+ return normalized;
18274
+ return { occurrences: normalized.occurrences, newContent: fromLF(normalized.newContent, nl) };
18275
+ }
18276
+ function replaceOnceOrAll(text, oldString, newString, replaceAll) {
18277
+ if (replaceAll) {
18278
+ if (!text.includes(oldString))
18279
+ return { occurrences: 0, newContent: text };
18280
+ const parts = text.split(oldString);
18281
+ return { occurrences: parts.length - 1, newContent: parts.join(newString) };
18282
+ }
18283
+ const idx = text.indexOf(oldString);
18284
+ if (idx === -1)
18285
+ return { occurrences: 0, newContent: text };
18286
+ return {
18287
+ occurrences: 1,
18288
+ newContent: text.slice(0, idx) + newString + text.slice(idx + oldString.length)
18289
+ };
18290
+ }
18241
18291
  var ReadFileArgsSchema, readFileTool, WriteFileArgsSchema, writeFileTool, EditFileArgsSchema, editFileTool;
18242
18292
  var init_filesystem = __esm({
18243
18293
  "packages/core/dist/core/tools/builtin/filesystem.js"() {
18244
18294
  "use strict";
18245
18295
  init_zod();
18246
18296
  init_toolTypes();
18297
+ init_newlines();
18247
18298
  ReadFileArgsSchema = external_exports.object({
18248
18299
  path: external_exports.string().min(1),
18249
18300
  startLine: external_exports.number().int().nonnegative().optional().describe("0-based first line to include. Range is applied to the full file before maxBytes."),
@@ -18337,7 +18388,7 @@ var init_filesystem = __esm({
18337
18388
  });
18338
18389
  editFileTool = {
18339
18390
  name: "edit_file",
18340
- description: "Replace exact string match in a file. Idempotent: returns 0 occurrences if no match.",
18391
+ description: "Replace a string in a file. Matching ignores CRLF vs LF; the file keeps its original line endings. Returns an error if oldString is not found.",
18341
18392
  permissions: ["write"],
18342
18393
  sideEffect: "local",
18343
18394
  timeoutMs: 1e4,
@@ -18347,22 +18398,7 @@ var init_filesystem = __esm({
18347
18398
  const absPath = path6.isAbsolute(args.path) ? args.path : path6.join(ctx.cwd, args.path);
18348
18399
  const content = await fs4.readFile(absPath, { encoding: "utf-8", signal: ctx.signal });
18349
18400
  const text = typeof content === "string" ? content : content.toString("utf-8");
18350
- let occurrences = 0;
18351
- let newContent;
18352
- if (args.replaceAll) {
18353
- const parts = text.split(args.oldString);
18354
- occurrences = parts.length - 1;
18355
- newContent = parts.join(args.newString);
18356
- } else {
18357
- const idx = text.indexOf(args.oldString);
18358
- if (idx === -1) {
18359
- occurrences = 0;
18360
- newContent = text;
18361
- } else {
18362
- occurrences = 1;
18363
- newContent = text.slice(0, idx) + args.newString + text.slice(idx + args.oldString.length);
18364
- }
18365
- }
18401
+ const { occurrences, newContent } = replaceFileString(text, args.oldString, args.newString, args.replaceAll);
18366
18402
  if (occurrences === 0) {
18367
18403
  return typedErr(`edit_file: no match for oldString in ${args.path}. Use read_file to copy the exact text (whitespace included) and retry.`);
18368
18404
  }
@@ -19114,7 +19150,7 @@ function formatUnified(hunks, oldLabel, newLabel) {
19114
19150
  return lines.join("\n");
19115
19151
  }
19116
19152
  function parseUnified(raw) {
19117
- const lines = raw.split("\n");
19153
+ const lines = splitLinesLF(raw);
19118
19154
  if (lines.length < 2 || !lines[0].startsWith("--- ") || !lines[1].startsWith("+++ ")) {
19119
19155
  throw new Error("Invalid unified diff: missing --- / +++ headers");
19120
19156
  }
@@ -19170,6 +19206,72 @@ function parseUnified(raw) {
19170
19206
  function normalizeWhitespace(s) {
19171
19207
  return s.replace(/\s+/g, " ").trim();
19172
19208
  }
19209
+ function linesEqual(fileLine, opText, fuzzy) {
19210
+ if (fileLine === opText)
19211
+ return true;
19212
+ if (fuzzy && normalizeWhitespace(fileLine) === normalizeWhitespace(opText))
19213
+ return true;
19214
+ return false;
19215
+ }
19216
+ function hunkOldNeedle(hunk) {
19217
+ return hunk.ops.filter((op) => op.kind !== "+").map((op) => op.text);
19218
+ }
19219
+ function matchAt(lines, at, needle, fuzzy) {
19220
+ if (needle.length === 0)
19221
+ return at >= 0 && at <= lines.length;
19222
+ if (at < 0 || at + needle.length > lines.length)
19223
+ return false;
19224
+ for (let i = 0; i < needle.length; i++) {
19225
+ if (!linesEqual(lines[at + i] ?? "", needle[i], fuzzy))
19226
+ return false;
19227
+ }
19228
+ return true;
19229
+ }
19230
+ function locateHunk(lines, hunk, fileIdx, fuzzy) {
19231
+ const needle = hunkOldNeedle(hunk);
19232
+ const preferred = hunk.oldStart - 1;
19233
+ if (needle.length === 0) {
19234
+ const start = Math.max(fileIdx, preferred >= 0 ? preferred : fileIdx);
19235
+ return { start };
19236
+ }
19237
+ if (preferred >= fileIdx && matchAt(lines, preferred, needle, fuzzy)) {
19238
+ return { start: preferred };
19239
+ }
19240
+ const hits = [];
19241
+ for (let i = fileIdx; i <= lines.length - needle.length; i++) {
19242
+ if (matchAt(lines, i, needle, fuzzy))
19243
+ hits.push(i);
19244
+ }
19245
+ if (hits.length === 0) {
19246
+ if (preferred >= fileIdx) {
19247
+ let fileAt = preferred;
19248
+ for (const op of hunk.ops) {
19249
+ if (op.kind === "+")
19250
+ continue;
19251
+ const fileLine = lines[fileAt] ?? "";
19252
+ if (!linesEqual(fileLine, op.text, fuzzy)) {
19253
+ const label = op.kind === "-" ? "Delete mismatch" : "Context mismatch";
19254
+ return {
19255
+ error: `${label} at line ${fileAt + 1}: expected "${op.text.slice(0, 60)}", got "${fileLine.slice(0, 60)}"`
19256
+ };
19257
+ }
19258
+ fileAt++;
19259
+ }
19260
+ }
19261
+ return { error: `Hunk context not found (oldStart ${hunk.oldStart})` };
19262
+ }
19263
+ if (hits.length === 1)
19264
+ return { start: hits[0] };
19265
+ hits.sort((a, b) => Math.abs(a - preferred) - Math.abs(b - preferred) || a - b);
19266
+ const bestDist = Math.abs(hits[0] - preferred);
19267
+ const tied = hits.filter((h) => Math.abs(h - preferred) === bestDist);
19268
+ if (tied.length > 1) {
19269
+ return {
19270
+ error: `Ambiguous hunk at oldStart ${hunk.oldStart}: ${tied.length} equally close matches (refusing to guess)`
19271
+ };
19272
+ }
19273
+ return { start: hits[0] };
19274
+ }
19173
19275
  function applyAllHunks(originalLines, hunks, fuzzy) {
19174
19276
  const out = [];
19175
19277
  let fileIdx = 0;
@@ -19177,17 +19279,13 @@ function applyAllHunks(originalLines, hunks, fuzzy) {
19177
19279
  let hunksSkipped = 0;
19178
19280
  let lastReason;
19179
19281
  for (const hunk of hunks) {
19180
- const startIdx = hunk.oldStart - 1;
19181
- if (startIdx < 0) {
19282
+ const located = locateHunk(originalLines, hunk, fileIdx, fuzzy);
19283
+ if ("error" in located) {
19182
19284
  hunksSkipped++;
19183
- lastReason = `oldStart ${hunk.oldStart} out of range (must be >= 1)`;
19184
- continue;
19185
- }
19186
- if (startIdx > originalLines.length) {
19187
- hunksSkipped++;
19188
- lastReason = `oldStart ${hunk.oldStart} beyond file end (length ${originalLines.length})`;
19189
- continue;
19285
+ lastReason = located.error;
19286
+ break;
19190
19287
  }
19288
+ const startIdx = located.start;
19191
19289
  while (fileIdx < startIdx) {
19192
19290
  out.push(originalLines[fileIdx]);
19193
19291
  fileIdx++;
@@ -19200,7 +19298,7 @@ function applyAllHunks(originalLines, hunks, fuzzy) {
19200
19298
  const op = hunk.ops[hunkIdx];
19201
19299
  if (op.kind === " ") {
19202
19300
  const fileLine = originalLines[hunkFileIdx] ?? "";
19203
- if (fileLine === op.text || fuzzy && normalizeWhitespace(fileLine) === normalizeWhitespace(op.text)) {
19301
+ if (linesEqual(fileLine, op.text, fuzzy)) {
19204
19302
  hunkOut.push(fileLine);
19205
19303
  hunkFileIdx++;
19206
19304
  hunkIdx++;
@@ -19211,7 +19309,7 @@ function applyAllHunks(originalLines, hunks, fuzzy) {
19211
19309
  }
19212
19310
  } else if (op.kind === "-") {
19213
19311
  const fileLine = originalLines[hunkFileIdx] ?? "";
19214
- if (fileLine === op.text || fuzzy && normalizeWhitespace(fileLine) === normalizeWhitespace(op.text)) {
19312
+ if (linesEqual(fileLine, op.text, fuzzy)) {
19215
19313
  hunkFileIdx++;
19216
19314
  hunkIdx++;
19217
19315
  } else {
@@ -19244,6 +19342,7 @@ var init_diff = __esm({
19244
19342
  "use strict";
19245
19343
  init_zod();
19246
19344
  init_toolTypes();
19345
+ init_newlines();
19247
19346
  LCS_MAX_CELLS = 4e6;
19248
19347
  ShowDiffArgsSchema = external_exports.object({
19249
19348
  path: external_exports.string().min(1),
@@ -19268,8 +19367,8 @@ var init_diff = __esm({
19268
19367
  return typedErr(err instanceof Error ? err.message : String(err));
19269
19368
  }
19270
19369
  }
19271
- const a = current.split("\n");
19272
- const b = args.proposedContent.split("\n");
19370
+ const a = splitLinesLF(current);
19371
+ const b = splitLinesLF(args.proposedContent);
19273
19372
  const CONTEXT = args.contextLines;
19274
19373
  const rawOps = diffOps(a, b);
19275
19374
  if (rawOps === null) {
@@ -19355,7 +19454,7 @@ var init_diff = __esm({
19355
19454
  });
19356
19455
  applyDiffTool = {
19357
19456
  name: "apply_diff",
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.",
19457
+ description: "Apply a unified diff patch to a file. Parses ---/+++/@@ headers and applies each hunk sequentially. CRLF vs LF is ignored; the file keeps its original line endings. Hunks whose @@ line numbers drifted (common after an earlier insert) are relocated by matching context. With fuzzyMatch=true, also tolerates whitespace differences. With dryRun=true, returns the final content without writing. Atomic: if any hunk fails, no partial write occurs.",
19359
19458
  permissions: ["write"],
19360
19459
  sideEffect: "local",
19361
19460
  timeoutMs: 15e3,
@@ -19380,9 +19479,10 @@ var init_diff = __esm({
19380
19479
  if (parsed.hunks.length === 0) {
19381
19480
  return typedErr("No hunks found in diff");
19382
19481
  }
19383
- const originalLines = current.split("\n");
19482
+ const nl = detectNewline(current);
19483
+ const originalLines = splitLinesLF(current);
19384
19484
  const result = applyAllHunks(originalLines, parsed.hunks, args.fuzzyMatch);
19385
- const finalContent = result.lines.join("\n");
19485
+ const finalContent = fromLF(result.lines.join("\n"), nl);
19386
19486
  const ok = result.hunksSkipped === 0;
19387
19487
  if (ok && !args.dryRun) {
19388
19488
  await fs7.mkdir(path10.dirname(absPath), { recursive: true });
@@ -39312,7 +39412,7 @@ var init_taskTool = __esm({
39312
39412
  init_candidateRegistry();
39313
39413
  init_verifyReport();
39314
39414
  init_metrics2();
39315
- TASK_TOOL_TIMEOUT_MS = 9e5;
39415
+ TASK_TOOL_TIMEOUT_MS = 27e5;
39316
39416
  EXPLORE_PROMPT = [
39317
39417
  "You are a focused EXPLORE tentacle of Kraken (parent super-agent).",
39318
39418
  "READ-ONLY tools only (read, list, grep, fetch). No edits, no shell.",
@@ -45291,10 +45391,10 @@ import { readFileSync as readFileSync22 } from "node:fs";
45291
45391
  import { homedir as homedir10 } from "node:os";
45292
45392
  import path40 from "node:path";
45293
45393
  function emptyPolicySet() {
45294
- return { agents: /* @__PURE__ */ new Map(), warnings: [] };
45394
+ return { agents: /* @__PURE__ */ new Map(), warnings: [], precedence: policyPrecedenceFromEnv() };
45295
45395
  }
45296
- function agentRulesFor(set2, agent) {
45297
- return set2.agents.get(agent) ?? EMPTY_POLICY_RULE_SET;
45396
+ function agentLayersFor(set2, agent) {
45397
+ return set2.agents.get(agent) ?? EMPTY_POLICY_LAYERS;
45298
45398
  }
45299
45399
  function globToRegExp(pattern) {
45300
45400
  let src = "^";
@@ -45357,6 +45457,10 @@ function isPolicyEngineDisabled() {
45357
45457
  const v = process.env.ZELARI_POLICY?.trim().toLowerCase();
45358
45458
  return v === "0" || v === "false" || v === "no" || v === "off";
45359
45459
  }
45460
+ function policyPrecedenceFromEnv() {
45461
+ const v = process.env.ZELARI_POLICY_PRECEDENCE?.trim().toLowerCase();
45462
+ return v === "legacy" ? "legacy" : "restrict-only";
45463
+ }
45360
45464
  function isPlainObject2(v) {
45361
45465
  return typeof v === "object" && v !== null && !Array.isArray(v);
45362
45466
  }
@@ -45447,29 +45551,70 @@ function readPolicyFile(file2, warnings) {
45447
45551
  function loadPolicySet(root, opts = {}) {
45448
45552
  if (isPolicyEngineDisabled()) return emptyPolicySet();
45449
45553
  const warnings = [];
45554
+ const precedence = policyPrecedenceFromEnv();
45450
45555
  const project = readPolicyFile(path40.join(root, ".zelari", "policy.json"), warnings);
45451
45556
  const global = readPolicyFile(path40.join(opts.homeDir ?? homedir10(), ".zelari", "policy.json"), warnings);
45452
45557
  const agents = /* @__PURE__ */ new Map();
45453
- for (const [agent, g] of global) {
45454
- agents.set(agent, { shell: [...g.shell], edit: [...g.edit] });
45455
- }
45456
45558
  for (const [agent, p3] of project) {
45457
- const g = agents.get(agent) ?? EMPTY_POLICY_RULE_SET;
45458
- agents.set(agent, { shell: [...p3.shell, ...g.shell], edit: [...p3.edit, ...g.edit] });
45559
+ agents.set(agent, { project: p3, global: EMPTY_POLICY_RULE_SET });
45459
45560
  }
45460
- return { agents, warnings };
45561
+ for (const [agent, g] of global) {
45562
+ const l = agents.get(agent);
45563
+ agents.set(agent, l ? { ...l, global: g } : { project: EMPTY_POLICY_RULE_SET, global: g });
45564
+ }
45565
+ return { agents, warnings, precedence };
45461
45566
  }
45462
- var POLICY_AGENTS, KNOWN_AGENTS, EMPTY_POLICY_RULE_SET, EFFECT_RANK;
45567
+ var POLICY_AGENTS, KNOWN_AGENTS, EMPTY_POLICY_RULE_SET, EMPTY_POLICY_LAYERS, EFFECT_RANK;
45463
45568
  var init_policyEngine = __esm({
45464
45569
  "src/cli/safety/policyEngine.ts"() {
45465
45570
  "use strict";
45466
45571
  POLICY_AGENTS = ["lead", "explore", "general", "verify"];
45467
45572
  KNOWN_AGENTS = new Set(POLICY_AGENTS);
45468
45573
  EMPTY_POLICY_RULE_SET = { shell: [], edit: [] };
45574
+ EMPTY_POLICY_LAYERS = {
45575
+ global: EMPTY_POLICY_RULE_SET,
45576
+ project: EMPTY_POLICY_RULE_SET
45577
+ };
45469
45578
  EFFECT_RANK = { allow: 0, ask: 1, deny: 2 };
45470
45579
  }
45471
45580
  });
45472
45581
 
45582
+ // src/cli/safety/policyLayers.ts
45583
+ function intersectEffects(...effects) {
45584
+ let best;
45585
+ for (const effect of effects) {
45586
+ if (effect === void 0) continue;
45587
+ if (best === void 0 || EFFECT_RANK[effect] > EFFECT_RANK[best]) best = effect;
45588
+ }
45589
+ return best ?? "allow";
45590
+ }
45591
+ function matchAgentPolicyRuleLayered(layers, precedence, required2, args, root) {
45592
+ if (!layers) return null;
45593
+ if (precedence === "legacy") {
45594
+ return matchAgentPolicyRule(
45595
+ {
45596
+ shell: [...layers.project.shell, ...layers.global.shell],
45597
+ edit: [...layers.project.edit, ...layers.global.edit]
45598
+ },
45599
+ required2,
45600
+ args,
45601
+ root
45602
+ );
45603
+ }
45604
+ const g = matchAgentPolicyRule(layers.global, required2, args, root);
45605
+ const p3 = matchAgentPolicyRule(layers.project, required2, args, root);
45606
+ if (!g) return p3;
45607
+ if (!p3) return g;
45608
+ const win = intersectEffects(p3.effect, g.effect);
45609
+ return p3.effect === win ? p3 : g;
45610
+ }
45611
+ var init_policyLayers = __esm({
45612
+ "src/cli/safety/policyLayers.ts"() {
45613
+ "use strict";
45614
+ init_policyEngine();
45615
+ }
45616
+ });
45617
+
45473
45618
  // src/cli/toolResultCache.ts
45474
45619
  import { createHash as createHash13 } from "node:crypto";
45475
45620
  import { promises as fs19 } from "node:fs";
@@ -45810,11 +45955,19 @@ function createBuiltinToolRegistry(options = {}) {
45810
45955
  const allowMutators = !readOnly && !verifyMode && !gauntletParent;
45811
45956
  const allowBash = (allowMutators || verifyMode) && !gauntletParent;
45812
45957
  const permPolicy = options.permissionPolicy ?? defaultPermissionPolicy();
45813
- const agentPolicyRules = agentRulesFor(
45814
- loadPolicySet(root),
45958
+ const agentPolicySet = loadPolicySet(root);
45959
+ const agentPolicyLayers = agentLayersFor(
45960
+ agentPolicySet,
45815
45961
  options.policyAgent ?? "lead"
45816
45962
  );
45817
- const withPerm = (t) => wrapWithPermissions(t, permPolicy, options.onPermissionAsk, agentPolicyRules, root);
45963
+ const withPerm = (t) => wrapWithPermissions(
45964
+ t,
45965
+ permPolicy,
45966
+ options.onPermissionAsk,
45967
+ agentPolicyLayers,
45968
+ agentPolicySet.precedence,
45969
+ root
45970
+ );
45818
45971
  registry4.register(withPerm(safeReadFile));
45819
45972
  registry4.register(withPerm(safeGrepContent));
45820
45973
  registry4.register(withPerm(safeListFiles));
@@ -46081,7 +46234,7 @@ function createKrakenSubAgentContextFactory(opts) {
46081
46234
  };
46082
46235
  };
46083
46236
  }
46084
- function wrapWithPermissions(original, policy, onAsk, agentRules, root) {
46237
+ function wrapWithPermissions(original, policy, onAsk, agentLayers, precedence = "restrict-only", root) {
46085
46238
  const required2 = original.permissions ?? [];
46086
46239
  const decisionProbe = resolveToolPermission(original.name, required2, policy);
46087
46240
  if (decisionProbe.action === "allow" && !required2.includes("write") && !required2.includes("execute")) {
@@ -46090,8 +46243,9 @@ function wrapWithPermissions(original, policy, onAsk, agentRules, root) {
46090
46243
  ...original,
46091
46244
  execute: async (input, ctx) => {
46092
46245
  const decision = resolveToolPermission(original.name, required2, policy);
46093
- const rule = agentRules ? matchAgentPolicyRule(
46094
- agentRules,
46246
+ const rule = agentLayers ? matchAgentPolicyRuleLayered(
46247
+ agentLayers,
46248
+ precedence,
46095
46249
  required2,
46096
46250
  input ?? {},
46097
46251
  root ?? process.cwd()
@@ -46321,6 +46475,7 @@ var init_toolRegistry = __esm({
46321
46475
  init_toolPermissions();
46322
46476
  init_lifecycleHooks();
46323
46477
  init_policyEngine();
46478
+ init_policyLayers();
46324
46479
  init_toolResultCache();
46325
46480
  init_toolTypes();
46326
46481
  init_skills2();
@@ -69165,7 +69320,7 @@ var CsvFanoutArgsSchema = external_exports.object({
69165
69320
  scope_template: external_exports.array(external_exports.string()).optional(),
69166
69321
  /** Default: ZELARI_KRAKEN_MAX_PARALLEL. */
69167
69322
  max_concurrency: external_exports.number().int().positive().optional(),
69168
- /** Per-row timeout (ms). Default: 5 min for verify, 15 min for general. */
69323
+ /** Per-row timeout (ms). Default: 5 min for verify, 45 min for general. */
69169
69324
  max_runtime_seconds: external_exports.number().int().positive().optional()
69170
69325
  });
69171
69326
  async function readCsv(filePath) {
@@ -69256,7 +69411,7 @@ async function runCsvFanout(args, deps, opts) {
69256
69411
  }
69257
69412
  const concurrency = args.max_concurrency ?? resolveMaxConcurrency();
69258
69413
  opts.onLog?.(`csv fanout: ${rows.length} rows \xD7 ${args.agent_kind} @ concurrency=${concurrency}`);
69259
- const perRowMs = args.max_runtime_seconds !== void 0 ? args.max_runtime_seconds * 1e3 : args.agent_kind === "general" ? 9e5 : 3e5;
69414
+ const perRowMs = args.max_runtime_seconds !== void 0 ? args.max_runtime_seconds * 1e3 : args.agent_kind === "general" ? TASK_TOOL_TIMEOUT_MS : 3e5;
69260
69415
  const outputRecords = rows.map((r) => ({ ...r, status: "pending", result: "", error: "" }));
69261
69416
  const outHeaders = [...headers2, "status", "result", "error"];
69262
69417
  let writeChain2 = Promise.resolve();