@gobing-ai/spur 0.3.77 → 0.3.80

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 (32) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/config/templates/AGENTS.md +26 -23
  3. package/config/templates/docs/00_ADR.md +13 -23
  4. package/config/templates/docs/01_PRD.md +5 -2
  5. package/config/templates/docs/02_ROADMAP.md +9 -13
  6. package/config/templates/docs/03_ARCHITECTURE.md +2 -2
  7. package/config/templates/docs/04_DESIGN.md +12 -31
  8. package/config/templates/docs/05_FEATURES.md +6 -18
  9. package/config/templates/docs/99_PROJECT_CONSTITUTION.md +162 -394
  10. package/package.json +9 -9
  11. package/plugins/sp/README.md +12 -7
  12. package/plugins/sp/agents/super-reviewer.md +43 -8
  13. package/plugins/sp/commands/dev-refineall.md +1 -0
  14. package/plugins/sp/commands/dev-run.md +1 -0
  15. package/plugins/sp/commands/dev-runall.md +1 -0
  16. package/plugins/sp/commands/dev-verifyall.md +1 -0
  17. package/plugins/sp/plugin.json +1 -1
  18. package/plugins/sp/scripts/batch-preflight.mjs +173 -2
  19. package/plugins/sp/scripts/batch-preflight.ts +257 -2
  20. package/plugins/sp/scripts/verify-answer-lint.ts +32 -9
  21. package/plugins/sp/skills/conflict-finding/SKILL.md +6 -0
  22. package/plugins/sp/skills/daily-summary/SKILL.md +1 -1
  23. package/plugins/sp/skills/doc-evolve/SKILL.md +26 -40
  24. package/plugins/sp/skills/doc-evolve/references/operations.md +17 -30
  25. package/plugins/sp/skills/spur-cli/references/tasks/verbs.md +17 -1
  26. package/plugins/sp/skills/spur-cli/references/tasks.md +31 -1
  27. package/plugins/sp/skills/spur-dev/references/ac-style-guide.md +14 -0
  28. package/plugins/sp/skills/spur-dev/references/cross-cutting.md +36 -3
  29. package/plugins/sp/skills/spur-dev/references/done-housekeeping.md +12 -0
  30. package/plugins/sp/skills/spur-dev/references/execution-batch.md +17 -0
  31. package/plugins/sp/skills/spur-dev/references/inline-pipeline-driver.md +148 -17
  32. package/spur.js +1389 -615
package/spur.js CHANGED
@@ -45,9 +45,10 @@ var __export = (target, all) => {
45
45
  });
46
46
  };
47
47
  var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
48
+ var __promiseAll = (args) => Promise.all(args);
48
49
  var __require = import.meta.require;
49
50
 
50
- // ../../node_modules/.bun/@gobing-ai+ts-utils@0.4.57/node_modules/@gobing-ai/ts-utils/dist/errors.js
51
+ // ../../node_modules/.bun/@gobing-ai+ts-utils@0.4.62/node_modules/@gobing-ai/ts-utils/dist/errors.js
51
52
  var ErrorCode, AppError, ValidationError;
52
53
  var init_errors = __esm(() => {
53
54
  ErrorCode = {
@@ -72,7 +73,7 @@ var init_errors = __esm(() => {
72
73
  };
73
74
  });
74
75
 
75
- // ../../node_modules/.bun/@gobing-ai+ts-utils@0.4.57/node_modules/@gobing-ai/ts-utils/dist/api-response.js
76
+ // ../../node_modules/.bun/@gobing-ai+ts-utils@0.4.62/node_modules/@gobing-ai/ts-utils/dist/api-response.js
76
77
  var API_ERROR_CODES, ERROR_CODE_TO_HTTP, CLIENT_SAFE_CODES;
77
78
  var init_api_response = __esm(() => {
78
79
  init_errors();
@@ -95,13 +96,13 @@ var init_api_response = __esm(() => {
95
96
  CLIENT_SAFE_CODES = new Set([ErrorCode.NotFound, ErrorCode.Validation, ErrorCode.Conflict]);
96
97
  });
97
98
 
98
- // ../../node_modules/.bun/@gobing-ai+ts-utils@0.4.57/node_modules/@gobing-ai/ts-utils/dist/cursor.js
99
+ // ../../node_modules/.bun/@gobing-ai+ts-utils@0.4.62/node_modules/@gobing-ai/ts-utils/dist/cursor.js
99
100
  var init_cursor = () => {};
100
101
 
101
- // ../../node_modules/.bun/@gobing-ai+ts-utils@0.4.57/node_modules/@gobing-ai/ts-utils/dist/event-severity.js
102
+ // ../../node_modules/.bun/@gobing-ai+ts-utils@0.4.62/node_modules/@gobing-ai/ts-utils/dist/event-severity.js
102
103
  var init_event_severity = () => {};
103
104
 
104
- // ../../node_modules/.bun/@gobing-ai+ts-utils@0.4.57/node_modules/@gobing-ai/ts-utils/dist/object.js
105
+ // ../../node_modules/.bun/@gobing-ai+ts-utils@0.4.62/node_modules/@gobing-ai/ts-utils/dist/object.js
105
106
  function isPlainObject(value) {
106
107
  return typeof value === "object" && value !== null && !Array.isArray(value);
107
108
  }
@@ -119,7 +120,7 @@ function deepMerge(target, source) {
119
120
  return result;
120
121
  }
121
122
 
122
- // ../../node_modules/.bun/@gobing-ai+ts-utils@0.4.57/node_modules/@gobing-ai/ts-utils/dist/output.js
123
+ // ../../node_modules/.bun/@gobing-ai+ts-utils@0.4.62/node_modules/@gobing-ai/ts-utils/dist/output.js
123
124
  function processStream(name) {
124
125
  const proc = globalThis.process;
125
126
  const stream = proc?.[name];
@@ -140,7 +141,7 @@ function echoError(message, target = defaultStderrTarget ?? processStream("stder
140
141
  }
141
142
  var defaultStdoutTarget, defaultStderrTarget;
142
143
 
143
- // ../../node_modules/.bun/@gobing-ai+ts-utils@0.4.57/node_modules/@gobing-ai/ts-utils/dist/index.js
144
+ // ../../node_modules/.bun/@gobing-ai+ts-utils@0.4.62/node_modules/@gobing-ai/ts-utils/dist/index.js
144
145
  var init_dist = __esm(() => {
145
146
  init_api_response();
146
147
  init_cursor();
@@ -21763,7 +21764,7 @@ var init_zod = __esm(() => {
21763
21764
  init_external();
21764
21765
  });
21765
21766
 
21766
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.57+91f4e61e91341aed/node_modules/@gobing-ai/ts-runtime/dist/config.js
21767
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.62+7984b2f1c2109363/node_modules/@gobing-ai/ts-runtime/dist/config.js
21767
21768
  function getProcessEnv() {
21768
21769
  return process.env;
21769
21770
  }
@@ -21841,7 +21842,7 @@ var init_config = __esm(() => {
21841
21842
  };
21842
21843
  });
21843
21844
 
21844
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.57+91f4e61e91341aed/node_modules/@gobing-ai/ts-runtime/dist/file-system-node.js
21845
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.62+7984b2f1c2109363/node_modules/@gobing-ai/ts-runtime/dist/file-system-node.js
21845
21846
  import { appendFileSync, cpSync, createReadStream, createWriteStream, existsSync, mkdirSync, readdirSync, readFileSync, realpathSync, renameSync, rmSync, statSync, writeFileSync } from "fs";
21846
21847
  import { dirname, resolve as resolvePath } from "path";
21847
21848
  function createNodeFileSystem(root, paths) {
@@ -21928,7 +21929,7 @@ function findProjectRoot(startDir) {
21928
21929
  }
21929
21930
  var init_file_system_node = () => {};
21930
21931
 
21931
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.57+91f4e61e91341aed/node_modules/@gobing-ai/ts-runtime/dist/db-errors.js
21932
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.62+7984b2f1c2109363/node_modules/@gobing-ai/ts-runtime/dist/db-errors.js
21932
21933
  var DbModuleNotInstalledError;
21933
21934
  var init_db_errors = __esm(() => {
21934
21935
  DbModuleNotInstalledError = class DbModuleNotInstalledError extends Error {
@@ -29139,12 +29140,12 @@ var init_execa = __esm(() => {
29139
29140
  } = getIpcExport());
29140
29141
  });
29141
29142
 
29142
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.57+91f4e61e91341aed/node_modules/@gobing-ai/ts-runtime/dist/process-executor.js
29143
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.62+7984b2f1c2109363/node_modules/@gobing-ai/ts-runtime/dist/process-executor.js
29143
29144
  import { isatty } from "tty";
29144
29145
  function processEventSeverity(reason, exitCode) {
29145
29146
  if (reason === "error")
29146
29147
  return "error";
29147
- if (reason === "timeout")
29148
+ if (reason === "timeout" || reason === "cancelled")
29148
29149
  return "warning";
29149
29150
  if (exitCode !== null && exitCode !== 0)
29150
29151
  return "warning";
@@ -29171,15 +29172,19 @@ class NodeProcessExecutor {
29171
29172
  }
29172
29173
  async runUntraced(options) {
29173
29174
  const args = options.args ?? [];
29175
+ const deadline = resolveDeadline(options.timeout, this.config.defaultTimeout);
29176
+ const killGraceMs = resolveKillGraceMs(options.killGraceMs);
29177
+ const groupOwned = process.platform !== "win32" && (typeof deadline === "number" || options.signal !== undefined);
29174
29178
  const execaOptions = buildExecaOptions({
29175
29179
  cwd: options.cwd ?? this.config.paths?.cwd,
29176
29180
  env: options.env,
29177
- timeout: options.timeout ?? this.config.defaultTimeout,
29181
+ timeout: typeof deadline === "number" ? deadline : undefined,
29178
29182
  maxOutput: options.maxOutput ?? this.config.defaultMaxOutput,
29179
29183
  rejectOnError: options.rejectOnError ?? false,
29180
29184
  outputPolicy: this.config.output,
29181
29185
  forceBuffered: options.forceBuffered ?? false,
29182
- signal: options.signal
29186
+ signal: options.signal,
29187
+ groupOwned
29183
29188
  });
29184
29189
  const startedAt = Date.now();
29185
29190
  const startedIso = new Date(startedAt).toISOString();
@@ -29200,8 +29205,17 @@ class NodeProcessExecutor {
29200
29205
  severity: "info",
29201
29206
  ...options.label !== undefined ? { label: options.label } : {}
29202
29207
  });
29208
+ let ownership;
29203
29209
  try {
29204
29210
  const subprocess = execa(options.command, args, execaOptions);
29211
+ if (groupOwned && subprocess.pid !== undefined) {
29212
+ ownership = ownProcessGroupLifecycle({
29213
+ pid: subprocess.pid,
29214
+ deadlineMs: typeof deadline === "number" ? deadline : undefined,
29215
+ signal: options.signal,
29216
+ graceMs: killGraceMs
29217
+ });
29218
+ }
29205
29219
  if (subprocess.pid !== undefined) {
29206
29220
  this.config.registry?.update(registryId, { pid: subprocess.pid });
29207
29221
  if (options.onSpawn !== undefined) {
@@ -29210,10 +29224,11 @@ class NodeProcessExecutor {
29210
29224
  } catch {}
29211
29225
  }
29212
29226
  }
29213
- const stopGroupCancellation = observeProcessGroupCancellation(subprocess, options.signal);
29227
+ const stopOwnedTermination = () => ownership?.stop();
29214
29228
  observeOutput(subprocess.stdout, "stdout", options.onOutput);
29215
29229
  observeOutput(subprocess.stderr, "stderr", options.onOutput);
29216
- const result = await subprocess.finally(stopGroupCancellation);
29230
+ const result = await subprocess.finally(stopOwnedTermination);
29231
+ const ownedOutcome = ownership !== undefined ? await ownership.finish() : undefined;
29217
29232
  const processResult = {
29218
29233
  command: options.command,
29219
29234
  args,
@@ -29221,13 +29236,15 @@ class NodeProcessExecutor {
29221
29236
  stdout: asString(result.stdout),
29222
29237
  stderr: asString(result.stderr),
29223
29238
  ...result.signalDescription !== undefined ? { signal: result.signalDescription } : {},
29224
- durationMs: result.durationMs
29239
+ durationMs: result.durationMs,
29240
+ outcome: ownedOutcome ?? (result.signalDescription !== undefined ? "signal" : "exit")
29225
29241
  };
29226
29242
  this.completeRegistry(registryId, processResult.exitCode);
29227
29243
  this.emitExitedFromResult(options, processResult, result);
29228
29244
  return processResult;
29229
29245
  } catch (error51) {
29230
29246
  const failed = error51;
29247
+ const ownedOutcome = ownership !== undefined ? await ownership.finish() : undefined;
29231
29248
  const processResult = {
29232
29249
  command: options.command,
29233
29250
  args,
@@ -29235,7 +29252,8 @@ class NodeProcessExecutor {
29235
29252
  stdout: asString(failed.stdout),
29236
29253
  stderr: asString(failed.stderr),
29237
29254
  ...failed.signalDescription !== undefined ? { signal: failed.signalDescription } : failed.signal !== undefined ? { signal: failed.signal } : {},
29238
- durationMs: failed.durationMs ?? Date.now() - startedAt
29255
+ durationMs: failed.durationMs ?? Date.now() - startedAt,
29256
+ outcome: ownedOutcome ?? classifyFailedCompletion(failed)
29239
29257
  };
29240
29258
  this.completeRegistry(registryId, processResult.exitCode);
29241
29259
  this.emitExitedFromResult(options, processResult, error51, error51);
@@ -29335,7 +29353,7 @@ class NodeProcessExecutor {
29335
29353
  });
29336
29354
  }
29337
29355
  emitExitedFromResult(options, result, completion, error51) {
29338
- const reason = isTimedOut(completion) ? "timeout" : result.signal !== undefined ? "signal" : error51 ? "error" : "exit";
29356
+ const reason = result.outcome ?? (isTimedOut(completion) ? "timeout" : result.signal !== undefined ? "signal" : error51 ? "error" : "exit");
29339
29357
  this.emitProcessEvent("process.exited", {
29340
29358
  command: result.command,
29341
29359
  args: result.args,
@@ -29442,30 +29460,110 @@ function buildExecaOptions(opts) {
29442
29460
  ...mode === "pipe" ? { stdout: "pipe", stderr: "pipe" } : streamToTerminal ? { stdout: ["inherit", "pipe"], stderr: ["inherit", "pipe"] } : { all: true },
29443
29461
  ...opts.cwd !== undefined ? { cwd: opts.cwd } : {},
29444
29462
  ...opts.env !== undefined ? { env: opts.env } : {},
29445
- ...opts.timeout !== undefined ? { timeout: opts.timeout } : {},
29463
+ ...opts.timeout !== undefined && !opts.groupOwned ? { timeout: opts.timeout } : {},
29446
29464
  ...opts.maxOutput !== undefined ? { maxBuffer: opts.maxOutput } : {},
29447
- ...opts.signal !== undefined ? {
29448
- cancelSignal: opts.signal,
29449
- ...process.platform !== "win32" ? { detached: true } : {}
29450
- } : {}
29465
+ ...opts.groupOwned ? { detached: true } : {},
29466
+ ...opts.signal !== undefined && !opts.groupOwned ? { cancelSignal: opts.signal } : {}
29451
29467
  };
29452
29468
  }
29453
- function observeProcessGroupCancellation(subprocess, signal) {
29454
- const pid = subprocess.pid;
29455
- if (signal === undefined || process.platform === "win32" || pid === undefined)
29456
- return () => {};
29457
- const abort = () => {
29458
- try {
29459
- process.kill(-pid, "SIGTERM");
29460
- } catch {
29461
- subprocess.kill("SIGTERM");
29469
+ function resolveDeadline(optionTimeout, defaultTimeout) {
29470
+ if (optionTimeout === null)
29471
+ return null;
29472
+ const candidate = optionTimeout === undefined ? defaultTimeout : optionTimeout;
29473
+ if (candidate === undefined || candidate === null)
29474
+ return candidate;
29475
+ if (!Number.isInteger(candidate) || candidate <= 0 || candidate > MAX_TIMEOUT_MS) {
29476
+ throw new TypeError(`Process timeout must be a positive integer within the native timer range (1-${MAX_TIMEOUT_MS} ms), got ${String(candidate)}`);
29477
+ }
29478
+ return candidate;
29479
+ }
29480
+ function resolveKillGraceMs(optionGraceMs) {
29481
+ if (optionGraceMs === undefined)
29482
+ return DEFAULT_KILL_GRACE_MS;
29483
+ if (!Number.isInteger(optionGraceMs) || optionGraceMs < 0 || optionGraceMs > MAX_TIMEOUT_MS) {
29484
+ throw new TypeError(`Process killGraceMs must be a non-negative integer within the native timer range (0-${MAX_TIMEOUT_MS} ms), got ${String(optionGraceMs)}`);
29485
+ }
29486
+ return optionGraceMs;
29487
+ }
29488
+ function groupExists(pid) {
29489
+ try {
29490
+ process.kill(-pid, 0);
29491
+ return true;
29492
+ } catch (error51) {
29493
+ return error51.code === "EPERM";
29494
+ }
29495
+ }
29496
+ function signalGroup(pid, signal) {
29497
+ try {
29498
+ process.kill(-pid, signal);
29499
+ return true;
29500
+ } catch {
29501
+ return false;
29502
+ }
29503
+ }
29504
+ async function waitForGroupExit(pid, budgetMs) {
29505
+ const giveUpAt = Date.now() + budgetMs;
29506
+ while (groupExists(pid)) {
29507
+ if (Date.now() >= giveUpAt)
29508
+ return false;
29509
+ await new Promise((resolve) => setTimeout(resolve, GROUP_POLL_INTERVAL_MS));
29510
+ }
29511
+ return true;
29512
+ }
29513
+ async function reapProcessGroup(pid, graceMs) {
29514
+ if (!signalGroup(pid, "SIGTERM"))
29515
+ return false;
29516
+ if (await waitForGroupExit(pid, graceMs))
29517
+ return true;
29518
+ signalGroup(pid, "SIGKILL");
29519
+ await waitForGroupExit(pid, GROUP_KILL_PATIENCE_MS);
29520
+ return true;
29521
+ }
29522
+ function ownProcessGroupLifecycle(ownership) {
29523
+ const { pid, graceMs } = ownership;
29524
+ let trigger;
29525
+ let termination;
29526
+ let timer;
29527
+ const initiate = (reason) => {
29528
+ if (trigger !== undefined)
29529
+ return;
29530
+ trigger = reason;
29531
+ termination = reapProcessGroup(pid, graceMs);
29532
+ };
29533
+ if (ownership.deadlineMs !== undefined) {
29534
+ timer = setTimeout(() => initiate("timeout"), ownership.deadlineMs);
29535
+ }
29536
+ const onAbort = () => initiate("cancelled");
29537
+ if (ownership.signal !== undefined) {
29538
+ if (ownership.signal.aborted)
29539
+ onAbort();
29540
+ else
29541
+ ownership.signal.addEventListener("abort", onAbort, { once: true });
29542
+ }
29543
+ return {
29544
+ finish: async () => {
29545
+ if (trigger === undefined) {
29546
+ await reapProcessGroup(pid, graceMs);
29547
+ return;
29548
+ }
29549
+ return await termination ? trigger : undefined;
29550
+ },
29551
+ stop: () => {
29552
+ if (timer !== undefined)
29553
+ clearTimeout(timer);
29554
+ ownership.signal?.removeEventListener("abort", onAbort);
29462
29555
  }
29463
29556
  };
29464
- if (signal.aborted)
29465
- abort();
29466
- else
29467
- signal.addEventListener("abort", abort, { once: true });
29468
- return () => signal.removeEventListener("abort", abort);
29557
+ }
29558
+ function classifyFailedCompletion(error51) {
29559
+ if (error51.timedOut === true)
29560
+ return "timeout";
29561
+ if (error51.isCanceled === true)
29562
+ return "cancelled";
29563
+ if (error51.isTerminated === true || error51.signalDescription !== undefined || error51.signal !== undefined) {
29564
+ return "signal";
29565
+ }
29566
+ return "error";
29469
29567
  }
29470
29568
  function observeOutput(stream, name, observer) {
29471
29569
  if (!stream || !observer)
@@ -29498,12 +29596,13 @@ function isTimedOut(error51) {
29498
29596
  function errorMessage(error51) {
29499
29597
  return error51 instanceof Error ? error51.message : String(error51);
29500
29598
  }
29599
+ var MAX_TIMEOUT_MS = 2147483647, DEFAULT_KILL_GRACE_MS = 5000, GROUP_POLL_INTERVAL_MS = 25, GROUP_KILL_PATIENCE_MS = 2000;
29501
29600
  var init_process_executor = __esm(() => {
29502
29601
  init_execa();
29503
29602
  init_config();
29504
29603
  });
29505
29604
 
29506
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.57+91f4e61e91341aed/node_modules/@gobing-ai/ts-runtime/dist/runtime-node-bun.js
29605
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.62+7984b2f1c2109363/node_modules/@gobing-ai/ts-runtime/dist/runtime-node-bun.js
29507
29606
  function getNodeFileSystem() {
29508
29607
  if (!_nodeFileSystem)
29509
29608
  _nodeFileSystem = createNodeFileSystem();
@@ -29563,7 +29662,7 @@ var init_runtime_node_bun = __esm(() => {
29563
29662
  };
29564
29663
  });
29565
29664
 
29566
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.57+91f4e61e91341aed/node_modules/@gobing-ai/ts-runtime/dist/context.js
29665
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.62+7984b2f1c2109363/node_modules/@gobing-ai/ts-runtime/dist/context.js
29567
29666
  class RuntimeContext {
29568
29667
  scope;
29569
29668
  runtimeName;
@@ -29633,7 +29732,7 @@ var init_context = __esm(() => {
29633
29732
  init_runtime_node_bun();
29634
29733
  });
29635
29734
 
29636
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.57+91f4e61e91341aed/node_modules/@gobing-ai/ts-runtime/dist/path.js
29735
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.62+7984b2f1c2109363/node_modules/@gobing-ai/ts-runtime/dist/path.js
29637
29736
  function normalizeSeparators(path6) {
29638
29737
  return path6.replaceAll("\\", "/");
29639
29738
  }
@@ -29694,17 +29793,17 @@ var init_path = __esm(() => {
29694
29793
  SEP = globalThis.process?.platform === "win32" ? "\\" : "/";
29695
29794
  });
29696
29795
 
29697
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.57+91f4e61e91341aed/node_modules/@gobing-ai/ts-runtime/dist/schema-validation.js
29796
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.62+7984b2f1c2109363/node_modules/@gobing-ai/ts-runtime/dist/schema-validation.js
29698
29797
  var REMOTE_SCHEMA_MAX_BYTES;
29699
29798
  var init_schema_validation = __esm(() => {
29700
29799
  init_dist2();
29701
29800
  REMOTE_SCHEMA_MAX_BYTES = 5 * 1024 * 1024;
29702
29801
  });
29703
29802
 
29704
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.57+91f4e61e91341aed/node_modules/@gobing-ai/ts-runtime/dist/types.js
29803
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.62+7984b2f1c2109363/node_modules/@gobing-ai/ts-runtime/dist/types.js
29705
29804
  var init_types = () => {};
29706
29805
 
29707
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.57+91f4e61e91341aed/node_modules/@gobing-ai/ts-runtime/dist/index.js
29806
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.62+7984b2f1c2109363/node_modules/@gobing-ai/ts-runtime/dist/index.js
29708
29807
  var init_dist3 = __esm(() => {
29709
29808
  init_config();
29710
29809
  init_context();
@@ -29713,7 +29812,7 @@ var init_dist3 = __esm(() => {
29713
29812
  init_types();
29714
29813
  });
29715
29814
 
29716
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.57+91f4e61e91341aed/node_modules/@gobing-ai/ts-runtime/dist/bun-sqlite.js
29815
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.62+7984b2f1c2109363/node_modules/@gobing-ai/ts-runtime/dist/bun-sqlite.js
29717
29816
  import { Database } from "bun:sqlite";
29718
29817
  var init_bun_sqlite = () => {};
29719
29818
 
@@ -34143,7 +34242,7 @@ var init_bun_sqlite2 = __esm(() => {
34143
34242
  init_session2();
34144
34243
  });
34145
34244
 
34146
- // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.57+2c070f091b619667/node_modules/@gobing-ai/ts-db/dist/schema/inbox-messages.js
34245
+ // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.62+39fe09901e9c1f39/node_modules/@gobing-ai/ts-db/dist/schema/inbox-messages.js
34147
34246
  var inboxMessages;
34148
34247
  var init_inbox_messages = __esm(() => {
34149
34248
  init_sqlite_core();
@@ -34162,7 +34261,7 @@ var init_inbox_messages = __esm(() => {
34162
34261
  }, (table2) => [index("idx_inbox_messages_to_status").on(table2.toId, table2.status)]);
34163
34262
  });
34164
34263
 
34165
- // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.57+2c070f091b619667/node_modules/@gobing-ai/ts-db/dist/schema/common.js
34264
+ // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.62+39fe09901e9c1f39/node_modules/@gobing-ai/ts-db/dist/schema/common.js
34166
34265
  function nowTimestamp() {
34167
34266
  return Date.now();
34168
34267
  }
@@ -34191,7 +34290,7 @@ var init_common3 = __esm(() => {
34191
34290
  appendOnlyColumns = buildAppendOnlyColumns();
34192
34291
  });
34193
34292
 
34194
- // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.57+2c070f091b619667/node_modules/@gobing-ai/ts-db/dist/schema/queue-jobs.js
34293
+ // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.62+39fe09901e9c1f39/node_modules/@gobing-ai/ts-db/dist/schema/queue-jobs.js
34195
34294
  var queueJobs;
34196
34295
  var init_queue_jobs = __esm(() => {
34197
34296
  init_sqlite_core();
@@ -34207,11 +34306,15 @@ var init_queue_jobs = __esm(() => {
34207
34306
  nextRetryAt: integer2("next_retry_at"),
34208
34307
  lastError: text("last_error"),
34209
34308
  processingAt: integer2("processing_at"),
34210
- expiresAt: integer2("expires_at")
34309
+ expiresAt: integer2("expires_at"),
34310
+ timeoutMs: integer2("timeout_ms"),
34311
+ timeoutUnlimited: integer2("timeout_unlimited").notNull().default(0),
34312
+ attemptToken: text("attempt_token"),
34313
+ leaseExpiresAt: integer2("lease_expires_at")
34211
34314
  }, (table2) => [index("queue_jobs_ready_idx").on(table2.status, table2.nextRetryAt, table2.createdAt)]);
34212
34315
  });
34213
34316
 
34214
- // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.57+2c070f091b619667/node_modules/@gobing-ai/ts-db/dist/schema/runtime.js
34317
+ // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.62+39fe09901e9c1f39/node_modules/@gobing-ai/ts-db/dist/schema/runtime.js
34215
34318
  var exports_runtime = {};
34216
34319
  __export(exports_runtime, {
34217
34320
  queueJobs: () => queueJobs,
@@ -34222,7 +34325,7 @@ var init_runtime = __esm(() => {
34222
34325
  init_queue_jobs();
34223
34326
  });
34224
34327
 
34225
- // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.57+2c070f091b619667/node_modules/@gobing-ai/ts-db/dist/adapters/bun-sqlite.js
34328
+ // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.62+39fe09901e9c1f39/node_modules/@gobing-ai/ts-db/dist/adapters/bun-sqlite.js
34226
34329
  var exports_bun_sqlite = {};
34227
34330
  __export(exports_bun_sqlite, {
34228
34331
  BunSqliteAdapter: () => BunSqliteAdapter
@@ -34269,7 +34372,7 @@ class BunSqliteAdapter {
34269
34372
  }
34270
34373
  async queryFirst(sql3, ...params) {
34271
34374
  const stmt = this.getStatement(sql3);
34272
- return stmt.get(...params);
34375
+ return stmt.get(...params) ?? undefined;
34273
34376
  }
34274
34377
  async queryAll(sql3, ...params) {
34275
34378
  const stmt = this.getStatement(sql3);
@@ -34533,7 +34636,7 @@ var init_d1 = __esm(() => {
34533
34636
  init_session3();
34534
34637
  });
34535
34638
 
34536
- // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.57+2c070f091b619667/node_modules/@gobing-ai/ts-db/dist/adapters/d1.js
34639
+ // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.62+39fe09901e9c1f39/node_modules/@gobing-ai/ts-db/dist/adapters/d1.js
34537
34640
  var exports_d1 = {};
34538
34641
  __export(exports_d1, {
34539
34642
  D1Adapter: () => D1Adapter
@@ -34597,7 +34700,7 @@ var init_d12 = __esm(() => {
34597
34700
  init_runtime();
34598
34701
  });
34599
34702
 
34600
- // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.57+2c070f091b619667/node_modules/@gobing-ai/ts-db/dist/adapter.js
34703
+ // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.62+39fe09901e9c1f39/node_modules/@gobing-ai/ts-db/dist/adapter.js
34601
34704
  async function createDbAdapter(config3) {
34602
34705
  switch (config3.driver) {
34603
34706
  case "bun-sqlite": {
@@ -34631,7 +34734,7 @@ var init_drizzle_orm = __esm(() => {
34631
34734
  init_view_common();
34632
34735
  });
34633
34736
 
34634
- // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.57+2c070f091b619667/node_modules/@gobing-ai/ts-db/dist/query-spec.js
34737
+ // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.62+39fe09901e9c1f39/node_modules/@gobing-ai/ts-db/dist/query-spec.js
34635
34738
  function compilePredicate(predicate) {
34636
34739
  if ("and" in predicate) {
34637
34740
  const parts = predicate.and.map(compilePredicate).filter((p) => p !== undefined);
@@ -34669,7 +34772,7 @@ var init_query_spec = __esm(() => {
34669
34772
  };
34670
34773
  });
34671
34774
 
34672
- // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.57+2c070f091b619667/node_modules/@gobing-ai/ts-db/dist/base-dao.js
34775
+ // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.62+39fe09901e9c1f39/node_modules/@gobing-ai/ts-db/dist/base-dao.js
34673
34776
  function asSelectQuery(query) {
34674
34777
  return query;
34675
34778
  }
@@ -34711,7 +34814,7 @@ var init_base_dao = __esm(() => {
34711
34814
  init_query_spec();
34712
34815
  });
34713
34816
 
34714
- // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.57+2c070f091b619667/node_modules/@gobing-ai/ts-db/dist/embedded-migrations.js
34817
+ // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.62+39fe09901e9c1f39/node_modules/@gobing-ai/ts-db/dist/embedded-migrations.js
34715
34818
  var embeddedMigrations;
34716
34819
  var init_embedded_migrations = __esm(() => {
34717
34820
  embeddedMigrations = [
@@ -34739,11 +34842,16 @@ var init_embedded_migrations = __esm(() => {
34739
34842
  tag: "0004_inbox_messages_to_status_idx",
34740
34843
  sql: "CREATE INDEX `idx_inbox_messages_to_status` ON `inbox_messages` (`to_id`,`status`);",
34741
34844
  hash: "3c00f1bce4f569e442f3142df31ff478d6c9b3d460824d46575f50a324bfae61"
34845
+ },
34846
+ {
34847
+ tag: "0005_queue_attempt_ownership",
34848
+ sql: "ALTER TABLE `queue_jobs` ADD `timeout_ms` integer;\nALTER TABLE `queue_jobs` ADD `timeout_unlimited` integer DEFAULT 0 NOT NULL;\nALTER TABLE `queue_jobs` ADD `attempt_token` text;\nALTER TABLE `queue_jobs` ADD `lease_expires_at` integer;",
34849
+ hash: "f6a4eaf11eee838f424515e57eca3a46539260bb2fb5b0b112870ca5ed38d040"
34742
34850
  }
34743
34851
  ];
34744
34852
  });
34745
34853
 
34746
- // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.57+2c070f091b619667/node_modules/@gobing-ai/ts-db/dist/entity-dao.js
34854
+ // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.62+39fe09901e9c1f39/node_modules/@gobing-ai/ts-db/dist/entity-dao.js
34747
34855
  var EntityDao;
34748
34856
  var init_entity_dao = __esm(() => {
34749
34857
  init_drizzle_orm();
@@ -34905,7 +35013,7 @@ var init_entity_dao = __esm(() => {
34905
35013
  };
34906
35014
  });
34907
35015
 
34908
- // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.57+2c070f091b619667/node_modules/@gobing-ai/ts-db/dist/inbox-message-dao.js
35016
+ // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.62+39fe09901e9c1f39/node_modules/@gobing-ai/ts-db/dist/inbox-message-dao.js
34909
35017
  function isPromiseLike(value) {
34910
35018
  return value !== null && (typeof value === "object" || typeof value === "function") && typeof value.then === "function";
34911
35019
  }
@@ -35083,7 +35191,7 @@ var init_migrator2 = __esm(() => {
35083
35191
  init_migrator();
35084
35192
  });
35085
35193
 
35086
- // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.57+2c070f091b619667/node_modules/@gobing-ai/ts-db/dist/migrate.js
35194
+ // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.62+39fe09901e9c1f39/node_modules/@gobing-ai/ts-db/dist/migrate.js
35087
35195
  async function ensureJournalTable(adapter, table3) {
35088
35196
  await adapter.exec(`CREATE TABLE IF NOT EXISTS "${table3}" (` + "id INTEGER PRIMARY KEY AUTOINCREMENT, " + "hash text NOT NULL, " + "created_at numeric" + ")");
35089
35197
  }
@@ -35158,7 +35266,19 @@ var init_migrate = __esm(() => {
35158
35266
  init_embedded_migrations();
35159
35267
  });
35160
35268
 
35161
- // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.57+2c070f091b619667/node_modules/@gobing-ai/ts-db/dist/queue-job-dao.js
35269
+ // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.62+39fe09901e9c1f39/node_modules/@gobing-ai/ts-db/dist/queue-job-dao.js
35270
+ function assertValidTimeoutMs(value) {
35271
+ if (value === undefined || value === null)
35272
+ return;
35273
+ if (typeof value !== "number" || !Number.isInteger(value) || value <= 0) {
35274
+ throw new RangeError(`enqueue timeoutMs must be a positive integer or null; received ${String(value)}`);
35275
+ }
35276
+ }
35277
+ function timeoutColumns(timeoutMs) {
35278
+ if (timeoutMs === undefined)
35279
+ return {};
35280
+ return timeoutMs === null ? { timeoutUnlimited: 1 } : { timeoutMs };
35281
+ }
35162
35282
  var QueueJobDao;
35163
35283
  var init_queue_job_dao = __esm(() => {
35164
35284
  init_drizzle_orm();
@@ -35169,6 +35289,7 @@ var init_queue_job_dao = __esm(() => {
35169
35289
  super(adapter, queueJobs, [queueJobs.id], "queue_jobs");
35170
35290
  }
35171
35291
  async enqueue(type, payload, options) {
35292
+ assertValidTimeoutMs(options?.timeoutMs);
35172
35293
  const now = this.now();
35173
35294
  const id = crypto.randomUUID();
35174
35295
  await this.create({
@@ -35179,11 +35300,15 @@ var init_queue_job_dao = __esm(() => {
35179
35300
  attempts: 0,
35180
35301
  maxRetries: options?.maxRetries ?? 3,
35181
35302
  nextRetryAt: options?.delay !== undefined ? now + options.delay : now,
35182
- ...options?.ttlMs !== undefined ? { expiresAt: now + options.ttlMs } : {}
35303
+ ...options?.ttlMs !== undefined ? { expiresAt: now + options.ttlMs } : {},
35304
+ ...timeoutColumns(options?.timeoutMs)
35183
35305
  });
35184
35306
  return id;
35185
35307
  }
35186
35308
  async enqueueBatch(jobs) {
35309
+ for (const job of jobs) {
35310
+ assertValidTimeoutMs(job.timeoutMs);
35311
+ }
35187
35312
  const now = this.now();
35188
35313
  const ids = [];
35189
35314
  const rows = jobs.map((job) => {
@@ -35197,7 +35322,8 @@ var init_queue_job_dao = __esm(() => {
35197
35322
  attempts: 0,
35198
35323
  maxRetries: job.maxRetries ?? 3,
35199
35324
  nextRetryAt: job.delay !== undefined ? now + job.delay : now,
35200
- ...job.ttlMs !== undefined ? { expiresAt: now + job.ttlMs } : {}
35325
+ ...job.ttlMs !== undefined ? { expiresAt: now + job.ttlMs } : {},
35326
+ ...timeoutColumns(job.timeoutMs)
35201
35327
  };
35202
35328
  });
35203
35329
  if (rows.length > 0) {
@@ -35235,20 +35361,37 @@ var init_queue_job_dao = __esm(() => {
35235
35361
  const result = await this.db.select().from(queueJobs).where(sql`${queueJobs.status} = 'pending' AND (${queueJobs.nextRetryAt} IS NULL OR ${queueJobs.nextRetryAt} <= ${now})`).orderBy(queueJobs.createdAt).limit(batchSize);
35236
35362
  return result;
35237
35363
  }
35238
- async claimReady(batchSize) {
35364
+ async claimReady(batchSize, options) {
35239
35365
  const limit = Math.floor(batchSize);
35240
35366
  if (limit <= 0)
35241
35367
  return [];
35242
35368
  const now = this.now();
35243
- const result = await this.db.update(queueJobs).set({ status: "processing", processingAt: now, updatedAt: now }).where(sql`${queueJobs.id} IN (
35369
+ const leaseMs = options?.leaseMs;
35370
+ const leased = leaseMs !== undefined;
35371
+ const leaseExpiresAt = leased ? now + leaseMs : null;
35372
+ const reclaimable = sql`(
35373
+ ${queueJobs.status} = 'pending'
35374
+ AND (${queueJobs.nextRetryAt} IS NULL OR ${queueJobs.nextRetryAt} <= ${now})
35375
+ ) OR (
35376
+ ${queueJobs.status} = 'processing'
35377
+ AND ${queueJobs.attemptToken} IS NOT NULL
35378
+ AND ${queueJobs.leaseExpiresAt} IS NOT NULL
35379
+ AND ${queueJobs.leaseExpiresAt} <= ${now}
35380
+ )`;
35381
+ const result = await this.db.update(queueJobs).set({
35382
+ status: "processing",
35383
+ processingAt: now,
35384
+ updatedAt: now,
35385
+ attemptToken: leased ? sql`lower(hex(randomblob(16)))` : null,
35386
+ leaseExpiresAt
35387
+ }).where(sql`${queueJobs.id} IN (
35244
35388
  SELECT id
35245
35389
  FROM ${queueJobs}
35246
- WHERE status = 'pending'
35247
- AND (next_retry_at IS NULL OR next_retry_at <= ${now})
35390
+ WHERE ${reclaimable}
35248
35391
  ORDER BY created_at
35249
35392
  LIMIT ${limit}
35250
35393
  )
35251
- AND ${queueJobs.status} = 'pending'`).returning();
35394
+ AND ${reclaimable}`).returning();
35252
35395
  return result;
35253
35396
  }
35254
35397
  async markProcessing(ids) {
@@ -35257,32 +35400,53 @@ var init_queue_job_dao = __esm(() => {
35257
35400
  const now = this.now();
35258
35401
  await this.db.update(queueJobs).set({ status: "processing", processingAt: now, updatedAt: now }).where(and(inArray(queueJobs.id, ids), eq(queueJobs.status, "pending")));
35259
35402
  }
35260
- async markCompleted(id) {
35261
- await this.update(id, {
35403
+ async markCompleted(id, attemptToken) {
35404
+ const where = attemptToken === undefined ? eq(queueJobs.id, id) : and(eq(queueJobs.id, id), eq(queueJobs.attemptToken, attemptToken));
35405
+ const result = await this.db.update(queueJobs).set({
35262
35406
  status: "completed",
35263
- processingAt: null
35264
- });
35265
- }
35266
- async markFailed(id, attempts, error51) {
35267
- await this.update(id, {
35407
+ processingAt: null,
35408
+ attemptToken: null,
35409
+ leaseExpiresAt: null,
35410
+ updatedAt: this.now()
35411
+ }).where(where);
35412
+ return result.changes === 1;
35413
+ }
35414
+ async markFailed(id, attempts, error51, attemptToken) {
35415
+ const where = attemptToken === undefined ? eq(queueJobs.id, id) : and(eq(queueJobs.id, id), eq(queueJobs.attemptToken, attemptToken));
35416
+ const result = await this.db.update(queueJobs).set({
35268
35417
  status: "failed",
35269
35418
  attempts,
35270
35419
  lastError: error51,
35271
- processingAt: null
35272
- });
35273
- }
35274
- async markForRetry(id, attempts, errorMessage2, nextRetryAt) {
35275
- await this.update(id, {
35420
+ processingAt: null,
35421
+ attemptToken: null,
35422
+ leaseExpiresAt: null,
35423
+ updatedAt: this.now()
35424
+ }).where(where);
35425
+ return result.changes === 1;
35426
+ }
35427
+ async markForRetry(id, attempts, errorMessage2, nextRetryAt, attemptToken) {
35428
+ const where = attemptToken === undefined ? eq(queueJobs.id, id) : and(eq(queueJobs.id, id), eq(queueJobs.attemptToken, attemptToken));
35429
+ const result = await this.db.update(queueJobs).set({
35276
35430
  status: "pending",
35277
35431
  attempts,
35278
35432
  lastError: errorMessage2,
35279
35433
  nextRetryAt,
35280
- processingAt: null
35281
- });
35434
+ processingAt: null,
35435
+ attemptToken: null,
35436
+ leaseExpiresAt: null,
35437
+ updatedAt: this.now()
35438
+ }).where(where);
35439
+ return result.changes === 1;
35440
+ }
35441
+ async renewLease(id, attemptToken, leaseMs) {
35442
+ const now = this.now();
35443
+ const result = await this.db.update(queueJobs).set({ leaseExpiresAt: now + leaseMs, updatedAt: now }).where(and(eq(queueJobs.id, id), eq(queueJobs.attemptToken, attemptToken), eq(queueJobs.status, "processing")));
35444
+ return result.changes === 1;
35282
35445
  }
35283
35446
  async resetStuckJobs(visibilityTimeout) {
35284
35447
  const cutoff = this.now() - visibilityTimeout;
35285
- const result = await this.db.update(queueJobs).set({ status: "pending", processingAt: null, updatedAt: this.now() }).where(sql`${queueJobs.status} = 'processing' AND ${queueJobs.processingAt} IS NOT NULL AND ${queueJobs.processingAt} <= ${cutoff}`);
35448
+ const result = await this.db.update(queueJobs).set({ status: "pending", processingAt: null, updatedAt: this.now() }).where(sql`${queueJobs.status} = 'processing' AND ${queueJobs.processingAt} IS NOT NULL AND ${queueJobs.processingAt} <= ${cutoff}
35449
+ AND ${queueJobs.attemptToken} IS NULL`);
35286
35450
  return result.changes;
35287
35451
  }
35288
35452
  async failExpiredJobs() {
@@ -35299,7 +35463,7 @@ var init_queue_job_dao = __esm(() => {
35299
35463
  };
35300
35464
  });
35301
35465
 
35302
- // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.57+2c070f091b619667/node_modules/@gobing-ai/ts-db/dist/index.js
35466
+ // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.62+39fe09901e9c1f39/node_modules/@gobing-ai/ts-db/dist/index.js
35303
35467
  var exports_dist = {};
35304
35468
  __export(exports_dist, {
35305
35469
  embeddedMigrations: () => embeddedMigrations,
@@ -37432,7 +37596,7 @@ var require_extra_typings = __commonJS((exports, module) => {
37432
37596
  exports.createArgument = (name, description) => new commander.Argument(name, description);
37433
37597
  });
37434
37598
 
37435
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.57+a23fed366e68a588/node_modules/@gobing-ai/ts-runtime/dist/config.js
37599
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.62+9fbf929b070eed81/node_modules/@gobing-ai/ts-runtime/dist/config.js
37436
37600
  function parseYamlObject(text3) {
37437
37601
  let parsed;
37438
37602
  try {
@@ -37559,7 +37723,7 @@ var init_config2 = __esm(() => {
37559
37723
  };
37560
37724
  });
37561
37725
 
37562
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.57+a23fed366e68a588/node_modules/@gobing-ai/ts-runtime/dist/file-system-node.js
37726
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.62+9fbf929b070eed81/node_modules/@gobing-ai/ts-runtime/dist/file-system-node.js
37563
37727
  import { appendFileSync as appendFileSync3, cpSync as cpSync2, createReadStream as createReadStream3, createWriteStream as createWriteStream3, existsSync as existsSync2, mkdirSync as mkdirSync2, readdirSync as readdirSync2, readFileSync as readFileSync4, realpathSync as realpathSync2, renameSync as renameSync2, rmSync as rmSync2, statSync as statSync3, writeFileSync as writeFileSync3 } from "fs";
37564
37728
  import { dirname as dirname2, resolve as resolvePath3 } from "path";
37565
37729
  function createNodeFileSystem3(root, paths) {
@@ -37646,7 +37810,7 @@ function findProjectRoot3(startDir) {
37646
37810
  }
37647
37811
  var init_file_system_node2 = () => {};
37648
37812
 
37649
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.57+a23fed366e68a588/node_modules/@gobing-ai/ts-runtime/dist/db-errors.js
37813
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.62+9fbf929b070eed81/node_modules/@gobing-ai/ts-runtime/dist/db-errors.js
37650
37814
  var D1NotConfiguredError2, DbModuleNotInstalledError3;
37651
37815
  var init_db_errors2 = __esm(() => {
37652
37816
  D1NotConfiguredError2 = class D1NotConfiguredError2 extends Error {
@@ -37663,7 +37827,7 @@ var init_db_errors2 = __esm(() => {
37663
37827
  };
37664
37828
  });
37665
37829
 
37666
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.57+a23fed366e68a588/node_modules/@gobing-ai/ts-runtime/dist/file-system-cf.js
37830
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.62+9fbf929b070eed81/node_modules/@gobing-ai/ts-runtime/dist/file-system-cf.js
37667
37831
  function createCfFileSystem2() {
37668
37832
  return {
37669
37833
  getProjectRoot: () => "/bundle",
@@ -37702,7 +37866,7 @@ function createCfFileSystem2() {
37702
37866
  }
37703
37867
  var UNSUPPORTED = "FileSystem is not available on Cloudflare Workers. Use D1, KV, or R2 for persistent storage.";
37704
37868
 
37705
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.57+a23fed366e68a588/node_modules/@gobing-ai/ts-runtime/dist/runtime-cf.js
37869
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.62+9fbf929b070eed81/node_modules/@gobing-ai/ts-runtime/dist/runtime-cf.js
37706
37870
  var exports_runtime_cf = {};
37707
37871
  __export(exports_runtime_cf, {
37708
37872
  cloudflareWorkersFactory: () => cloudflareWorkersFactory2
@@ -37740,12 +37904,12 @@ var init_runtime_cf = __esm(() => {
37740
37904
  };
37741
37905
  });
37742
37906
 
37743
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.57+a23fed366e68a588/node_modules/@gobing-ai/ts-runtime/dist/process-executor.js
37907
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.62+9fbf929b070eed81/node_modules/@gobing-ai/ts-runtime/dist/process-executor.js
37744
37908
  import { isatty as isatty2 } from "tty";
37745
37909
  function processEventSeverity2(reason, exitCode) {
37746
37910
  if (reason === "error")
37747
37911
  return "error";
37748
- if (reason === "timeout")
37912
+ if (reason === "timeout" || reason === "cancelled")
37749
37913
  return "warning";
37750
37914
  if (exitCode !== null && exitCode !== 0)
37751
37915
  return "warning";
@@ -37772,15 +37936,19 @@ class NodeProcessExecutor3 {
37772
37936
  }
37773
37937
  async runUntraced(options) {
37774
37938
  const args = options.args ?? [];
37939
+ const deadline = resolveDeadline2(options.timeout, this.config.defaultTimeout);
37940
+ const killGraceMs = resolveKillGraceMs2(options.killGraceMs);
37941
+ const groupOwned = process.platform !== "win32" && (typeof deadline === "number" || options.signal !== undefined);
37775
37942
  const execaOptions = buildExecaOptions2({
37776
37943
  cwd: options.cwd ?? this.config.paths?.cwd,
37777
37944
  env: options.env,
37778
- timeout: options.timeout ?? this.config.defaultTimeout,
37945
+ timeout: typeof deadline === "number" ? deadline : undefined,
37779
37946
  maxOutput: options.maxOutput ?? this.config.defaultMaxOutput,
37780
37947
  rejectOnError: options.rejectOnError ?? false,
37781
37948
  outputPolicy: this.config.output,
37782
37949
  forceBuffered: options.forceBuffered ?? false,
37783
- signal: options.signal
37950
+ signal: options.signal,
37951
+ groupOwned
37784
37952
  });
37785
37953
  const startedAt = Date.now();
37786
37954
  const startedIso = new Date(startedAt).toISOString();
@@ -37801,8 +37969,17 @@ class NodeProcessExecutor3 {
37801
37969
  severity: "info",
37802
37970
  ...options.label !== undefined ? { label: options.label } : {}
37803
37971
  });
37972
+ let ownership;
37804
37973
  try {
37805
37974
  const subprocess = execa(options.command, args, execaOptions);
37975
+ if (groupOwned && subprocess.pid !== undefined) {
37976
+ ownership = ownProcessGroupLifecycle2({
37977
+ pid: subprocess.pid,
37978
+ deadlineMs: typeof deadline === "number" ? deadline : undefined,
37979
+ signal: options.signal,
37980
+ graceMs: killGraceMs
37981
+ });
37982
+ }
37806
37983
  if (subprocess.pid !== undefined) {
37807
37984
  this.config.registry?.update(registryId, { pid: subprocess.pid });
37808
37985
  if (options.onSpawn !== undefined) {
@@ -37811,10 +37988,11 @@ class NodeProcessExecutor3 {
37811
37988
  } catch {}
37812
37989
  }
37813
37990
  }
37814
- const stopGroupCancellation = observeProcessGroupCancellation2(subprocess, options.signal);
37991
+ const stopOwnedTermination = () => ownership?.stop();
37815
37992
  observeOutput2(subprocess.stdout, "stdout", options.onOutput);
37816
37993
  observeOutput2(subprocess.stderr, "stderr", options.onOutput);
37817
- const result = await subprocess.finally(stopGroupCancellation);
37994
+ const result = await subprocess.finally(stopOwnedTermination);
37995
+ const ownedOutcome = ownership !== undefined ? await ownership.finish() : undefined;
37818
37996
  const processResult = {
37819
37997
  command: options.command,
37820
37998
  args,
@@ -37822,13 +38000,15 @@ class NodeProcessExecutor3 {
37822
38000
  stdout: asString2(result.stdout),
37823
38001
  stderr: asString2(result.stderr),
37824
38002
  ...result.signalDescription !== undefined ? { signal: result.signalDescription } : {},
37825
- durationMs: result.durationMs
38003
+ durationMs: result.durationMs,
38004
+ outcome: ownedOutcome ?? (result.signalDescription !== undefined ? "signal" : "exit")
37826
38005
  };
37827
38006
  this.completeRegistry(registryId, processResult.exitCode);
37828
38007
  this.emitExitedFromResult(options, processResult, result);
37829
38008
  return processResult;
37830
38009
  } catch (error51) {
37831
38010
  const failed = error51;
38011
+ const ownedOutcome = ownership !== undefined ? await ownership.finish() : undefined;
37832
38012
  const processResult = {
37833
38013
  command: options.command,
37834
38014
  args,
@@ -37836,7 +38016,8 @@ class NodeProcessExecutor3 {
37836
38016
  stdout: asString2(failed.stdout),
37837
38017
  stderr: asString2(failed.stderr),
37838
38018
  ...failed.signalDescription !== undefined ? { signal: failed.signalDescription } : failed.signal !== undefined ? { signal: failed.signal } : {},
37839
- durationMs: failed.durationMs ?? Date.now() - startedAt
38019
+ durationMs: failed.durationMs ?? Date.now() - startedAt,
38020
+ outcome: ownedOutcome ?? classifyFailedCompletion2(failed)
37840
38021
  };
37841
38022
  this.completeRegistry(registryId, processResult.exitCode);
37842
38023
  this.emitExitedFromResult(options, processResult, error51, error51);
@@ -37936,7 +38117,7 @@ class NodeProcessExecutor3 {
37936
38117
  });
37937
38118
  }
37938
38119
  emitExitedFromResult(options, result, completion, error51) {
37939
- const reason = isTimedOut2(completion) ? "timeout" : result.signal !== undefined ? "signal" : error51 ? "error" : "exit";
38120
+ const reason = result.outcome ?? (isTimedOut2(completion) ? "timeout" : result.signal !== undefined ? "signal" : error51 ? "error" : "exit");
37940
38121
  this.emitProcessEvent("process.exited", {
37941
38122
  command: result.command,
37942
38123
  args: result.args,
@@ -38083,30 +38264,110 @@ function buildExecaOptions2(opts) {
38083
38264
  ...mode === "pipe" ? { stdout: "pipe", stderr: "pipe" } : streamToTerminal ? { stdout: ["inherit", "pipe"], stderr: ["inherit", "pipe"] } : { all: true },
38084
38265
  ...opts.cwd !== undefined ? { cwd: opts.cwd } : {},
38085
38266
  ...opts.env !== undefined ? { env: opts.env } : {},
38086
- ...opts.timeout !== undefined ? { timeout: opts.timeout } : {},
38267
+ ...opts.timeout !== undefined && !opts.groupOwned ? { timeout: opts.timeout } : {},
38087
38268
  ...opts.maxOutput !== undefined ? { maxBuffer: opts.maxOutput } : {},
38088
- ...opts.signal !== undefined ? {
38089
- cancelSignal: opts.signal,
38090
- ...process.platform !== "win32" ? { detached: true } : {}
38091
- } : {}
38269
+ ...opts.groupOwned ? { detached: true } : {},
38270
+ ...opts.signal !== undefined && !opts.groupOwned ? { cancelSignal: opts.signal } : {}
38092
38271
  };
38093
38272
  }
38094
- function observeProcessGroupCancellation2(subprocess, signal) {
38095
- const pid = subprocess.pid;
38096
- if (signal === undefined || process.platform === "win32" || pid === undefined)
38097
- return () => {};
38098
- const abort = () => {
38099
- try {
38100
- process.kill(-pid, "SIGTERM");
38101
- } catch {
38102
- subprocess.kill("SIGTERM");
38273
+ function resolveDeadline2(optionTimeout, defaultTimeout) {
38274
+ if (optionTimeout === null)
38275
+ return null;
38276
+ const candidate = optionTimeout === undefined ? defaultTimeout : optionTimeout;
38277
+ if (candidate === undefined || candidate === null)
38278
+ return candidate;
38279
+ if (!Number.isInteger(candidate) || candidate <= 0 || candidate > MAX_TIMEOUT_MS2) {
38280
+ throw new TypeError(`Process timeout must be a positive integer within the native timer range (1-${MAX_TIMEOUT_MS2} ms), got ${String(candidate)}`);
38281
+ }
38282
+ return candidate;
38283
+ }
38284
+ function resolveKillGraceMs2(optionGraceMs) {
38285
+ if (optionGraceMs === undefined)
38286
+ return DEFAULT_KILL_GRACE_MS2;
38287
+ if (!Number.isInteger(optionGraceMs) || optionGraceMs < 0 || optionGraceMs > MAX_TIMEOUT_MS2) {
38288
+ throw new TypeError(`Process killGraceMs must be a non-negative integer within the native timer range (0-${MAX_TIMEOUT_MS2} ms), got ${String(optionGraceMs)}`);
38289
+ }
38290
+ return optionGraceMs;
38291
+ }
38292
+ function groupExists2(pid) {
38293
+ try {
38294
+ process.kill(-pid, 0);
38295
+ return true;
38296
+ } catch (error51) {
38297
+ return error51.code === "EPERM";
38298
+ }
38299
+ }
38300
+ function signalGroup2(pid, signal) {
38301
+ try {
38302
+ process.kill(-pid, signal);
38303
+ return true;
38304
+ } catch {
38305
+ return false;
38306
+ }
38307
+ }
38308
+ async function waitForGroupExit2(pid, budgetMs) {
38309
+ const giveUpAt = Date.now() + budgetMs;
38310
+ while (groupExists2(pid)) {
38311
+ if (Date.now() >= giveUpAt)
38312
+ return false;
38313
+ await new Promise((resolve) => setTimeout(resolve, GROUP_POLL_INTERVAL_MS2));
38314
+ }
38315
+ return true;
38316
+ }
38317
+ async function reapProcessGroup2(pid, graceMs) {
38318
+ if (!signalGroup2(pid, "SIGTERM"))
38319
+ return false;
38320
+ if (await waitForGroupExit2(pid, graceMs))
38321
+ return true;
38322
+ signalGroup2(pid, "SIGKILL");
38323
+ await waitForGroupExit2(pid, GROUP_KILL_PATIENCE_MS2);
38324
+ return true;
38325
+ }
38326
+ function ownProcessGroupLifecycle2(ownership) {
38327
+ const { pid, graceMs } = ownership;
38328
+ let trigger;
38329
+ let termination;
38330
+ let timer;
38331
+ const initiate = (reason) => {
38332
+ if (trigger !== undefined)
38333
+ return;
38334
+ trigger = reason;
38335
+ termination = reapProcessGroup2(pid, graceMs);
38336
+ };
38337
+ if (ownership.deadlineMs !== undefined) {
38338
+ timer = setTimeout(() => initiate("timeout"), ownership.deadlineMs);
38339
+ }
38340
+ const onAbort = () => initiate("cancelled");
38341
+ if (ownership.signal !== undefined) {
38342
+ if (ownership.signal.aborted)
38343
+ onAbort();
38344
+ else
38345
+ ownership.signal.addEventListener("abort", onAbort, { once: true });
38346
+ }
38347
+ return {
38348
+ finish: async () => {
38349
+ if (trigger === undefined) {
38350
+ await reapProcessGroup2(pid, graceMs);
38351
+ return;
38352
+ }
38353
+ return await termination ? trigger : undefined;
38354
+ },
38355
+ stop: () => {
38356
+ if (timer !== undefined)
38357
+ clearTimeout(timer);
38358
+ ownership.signal?.removeEventListener("abort", onAbort);
38103
38359
  }
38104
38360
  };
38105
- if (signal.aborted)
38106
- abort();
38107
- else
38108
- signal.addEventListener("abort", abort, { once: true });
38109
- return () => signal.removeEventListener("abort", abort);
38361
+ }
38362
+ function classifyFailedCompletion2(error51) {
38363
+ if (error51.timedOut === true)
38364
+ return "timeout";
38365
+ if (error51.isCanceled === true)
38366
+ return "cancelled";
38367
+ if (error51.isTerminated === true || error51.signalDescription !== undefined || error51.signal !== undefined) {
38368
+ return "signal";
38369
+ }
38370
+ return "error";
38110
38371
  }
38111
38372
  function observeOutput2(stream, name, observer) {
38112
38373
  if (!stream || !observer)
@@ -38144,14 +38405,14 @@ function isTimedOut2(error51) {
38144
38405
  function errorMessage2(error51) {
38145
38406
  return error51 instanceof Error ? error51.message : String(error51);
38146
38407
  }
38147
- var ProcessExecutor2;
38408
+ var ProcessExecutor2, MAX_TIMEOUT_MS2 = 2147483647, DEFAULT_KILL_GRACE_MS2 = 5000, GROUP_POLL_INTERVAL_MS2 = 25, GROUP_KILL_PATIENCE_MS2 = 2000;
38148
38409
  var init_process_executor2 = __esm(() => {
38149
38410
  init_execa();
38150
38411
  init_config2();
38151
38412
  ProcessExecutor2 = NodeProcessExecutor3;
38152
38413
  });
38153
38414
 
38154
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.57+a23fed366e68a588/node_modules/@gobing-ai/ts-runtime/dist/runtime-node-bun.js
38415
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.62+9fbf929b070eed81/node_modules/@gobing-ai/ts-runtime/dist/runtime-node-bun.js
38155
38416
  var exports_runtime_node_bun = {};
38156
38417
  __export(exports_runtime_node_bun, {
38157
38418
  nodeBunFactory: () => nodeBunFactory3,
@@ -38219,7 +38480,7 @@ var init_runtime_node_bun2 = __esm(() => {
38219
38480
  };
38220
38481
  });
38221
38482
 
38222
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.57+a23fed366e68a588/node_modules/@gobing-ai/ts-runtime/dist/platform.js
38483
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.62+9fbf929b070eed81/node_modules/@gobing-ai/ts-runtime/dist/platform.js
38223
38484
  function isCloudflareWorkerRuntime2() {
38224
38485
  return globalThis.navigator?.userAgent?.startsWith("Cloudflare-Workers") ?? false;
38225
38486
  }
@@ -38249,7 +38510,7 @@ function _resetRuntimeFactory2() {
38249
38510
  }
38250
38511
  var _factory, _runtimeName;
38251
38512
 
38252
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.57+a23fed366e68a588/node_modules/@gobing-ai/ts-runtime/dist/context.js
38513
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.62+9fbf929b070eed81/node_modules/@gobing-ai/ts-runtime/dist/context.js
38253
38514
  class RuntimeContext2 {
38254
38515
  scope;
38255
38516
  runtimeName;
@@ -38339,7 +38600,7 @@ var init_context2 = __esm(() => {
38339
38600
  init_runtime_node_bun2();
38340
38601
  });
38341
38602
 
38342
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.57+a23fed366e68a588/node_modules/@gobing-ai/ts-runtime/dist/path.js
38603
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.62+9fbf929b070eed81/node_modules/@gobing-ai/ts-runtime/dist/path.js
38343
38604
  function normalizeSeparators2(path7) {
38344
38605
  return path7.replaceAll("\\", "/");
38345
38606
  }
@@ -38462,7 +38723,7 @@ var init_path2 = __esm(() => {
38462
38723
  SEP2 = globalThis.process?.platform === "win32" ? "\\" : "/";
38463
38724
  });
38464
38725
 
38465
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.57+a23fed366e68a588/node_modules/@gobing-ai/ts-runtime/dist/fs.js
38726
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.62+9fbf929b070eed81/node_modules/@gobing-ai/ts-runtime/dist/fs.js
38466
38727
  async function ensureDirForFile2(path7, fs2 = createNodeFileSystem3()) {
38467
38728
  await fs2.ensureDir(dirnamePath(path7));
38468
38729
  }
@@ -38530,7 +38791,7 @@ var init_fs = __esm(() => {
38530
38791
  init_path2();
38531
38792
  });
38532
38793
 
38533
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.57+a23fed366e68a588/node_modules/@gobing-ai/ts-runtime/dist/process-registry.js
38794
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.62+9fbf929b070eed81/node_modules/@gobing-ai/ts-runtime/dist/process-registry.js
38534
38795
  function allocateId() {
38535
38796
  nextIdSeq += 1;
38536
38797
  return `pe_${Date.now().toString(36)}_${nextIdSeq.toString(36)}`;
@@ -38654,7 +38915,7 @@ function createInMemoryProcessRegistry2(options) {
38654
38915
  }
38655
38916
  var nextIdSeq = 0;
38656
38917
 
38657
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.57+a23fed366e68a588/node_modules/@gobing-ai/ts-runtime/dist/runtime-paths.js
38918
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.62+9fbf929b070eed81/node_modules/@gobing-ai/ts-runtime/dist/runtime-paths.js
38658
38919
  function ambientRuntimePaths2() {
38659
38920
  const cwd = getProcessCwd2();
38660
38921
  return { cwd, home: getHomeDir() ?? cwd };
@@ -38664,7 +38925,7 @@ var init_runtime_paths = __esm(() => {
38664
38925
  init_path2();
38665
38926
  });
38666
38927
 
38667
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.57+a23fed366e68a588/node_modules/@gobing-ai/ts-runtime/dist/schema-validation.js
38928
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.62+9fbf929b070eed81/node_modules/@gobing-ai/ts-runtime/dist/schema-validation.js
38668
38929
  async function loadStructuredConfig(path7, options = {}) {
38669
38930
  const content = await (options.fileSystem ?? createNodeFileSystem3()).readFile(path7);
38670
38931
  return await parseStructuredConfig(content, path7, options);
@@ -38947,10 +39208,10 @@ var init_schema_validation2 = __esm(() => {
38947
39208
  defaultResolve = typeof Bun !== "undefined" ? (specifier, from) => Bun.resolveSync(specifier, from) : undefined;
38948
39209
  });
38949
39210
 
38950
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.57+a23fed366e68a588/node_modules/@gobing-ai/ts-runtime/dist/types.js
39211
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.62+9fbf929b070eed81/node_modules/@gobing-ai/ts-runtime/dist/types.js
38951
39212
  var init_types2 = () => {};
38952
39213
 
38953
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.57+a23fed366e68a588/node_modules/@gobing-ai/ts-runtime/dist/index.js
39214
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.62+9fbf929b070eed81/node_modules/@gobing-ai/ts-runtime/dist/index.js
38954
39215
  var exports_dist2 = {};
38955
39216
  __export(exports_dist2, {
38956
39217
  writeJsonFile: () => writeJsonFile2,
@@ -39821,9 +40082,12 @@ function resolveSchemaSpecifier(specifier, manifestSpecifier) {
39821
40082
  }
39822
40083
  function resolveConfigLayers(cwd) {
39823
40084
  const layers = {};
39824
- const projectConfig = join4(cwd ?? process.cwd(), SPUR_CONFIG_DIR, SPUR_CONFIG_FILE);
39825
- if (existsSync4(projectConfig))
39826
- layers.project = projectConfig;
40085
+ const skipProject = cwd === undefined && process.env.SPUR_SKIP_PROJECT_CONFIG === "true";
40086
+ if (!skipProject) {
40087
+ const projectConfig = join4(cwd ?? process.cwd(), SPUR_CONFIG_DIR, SPUR_CONFIG_FILE);
40088
+ if (existsSync4(projectConfig))
40089
+ layers.project = projectConfig;
40090
+ }
39827
40091
  if (process.env.SPUR_SKIP_GLOBAL_CONFIG !== "true" && existsSync4(GLOBAL_CONFIG_FILE)) {
39828
40092
  layers.global = GLOBAL_CONFIG_FILE;
39829
40093
  }
@@ -39846,7 +40110,7 @@ function makeEmbeddedReader(embeddedSchemas) {
39846
40110
  return createNodeFileSystem3().readFile(path8);
39847
40111
  };
39848
40112
  }
39849
- async function loadSpurConfig(cwd = process.cwd(), opts) {
40113
+ async function loadSpurConfig(cwd, opts) {
39850
40114
  const layers = resolveConfigLayers(cwd);
39851
40115
  if (layers.global === undefined && layers.project === undefined) {
39852
40116
  return spurConfigSchema.parse({});
@@ -42006,7 +42270,7 @@ var init_mod = __esm(() => {
42006
42270
  init_config3();
42007
42271
  });
42008
42272
 
42009
- // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.57+33b96fa7d4032676/node_modules/@gobing-ai/ts-infra/dist/logger.js
42273
+ // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.62+c884df390da1a1e8/node_modules/@gobing-ai/ts-infra/dist/logger.js
42010
42274
  function toLogTapeLevel(level) {
42011
42275
  return level === "warn" ? "warning" : level;
42012
42276
  }
@@ -43559,7 +43823,7 @@ var require_src = __commonJS((exports) => {
43559
43823
  };
43560
43824
  });
43561
43825
 
43562
- // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.57+33b96fa7d4032676/node_modules/@gobing-ai/ts-infra/dist/telemetry/sdk.js
43826
+ // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.62+c884df390da1a1e8/node_modules/@gobing-ai/ts-infra/dist/telemetry/sdk.js
43563
43827
  function getTelemetryConfig(configPartial = {}) {
43564
43828
  return {
43565
43829
  enabled: configPartial.enabled ?? CONFIG_DEFAULTS.enabled,
@@ -43595,7 +43859,7 @@ var init_sdk = __esm(() => {
43595
43859
  resolvedConfig = getTelemetryConfig();
43596
43860
  });
43597
43861
 
43598
- // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.57+33b96fa7d4032676/node_modules/@gobing-ai/ts-infra/dist/telemetry/tracing.js
43862
+ // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.62+c884df390da1a1e8/node_modules/@gobing-ai/ts-infra/dist/telemetry/tracing.js
43599
43863
  function isSuppressed(tracer2) {
43600
43864
  return tracer2 === undefined && !getResolvedConfig().enabled;
43601
43865
  }
@@ -43635,7 +43899,7 @@ var init_tracing2 = __esm(() => {
43635
43899
  import_api2 = __toESM(require_src(), 1);
43636
43900
  });
43637
43901
 
43638
- // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.57+33b96fa7d4032676/node_modules/@gobing-ai/ts-infra/dist/telemetry/metrics.js
43902
+ // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.62+c884df390da1a1e8/node_modules/@gobing-ai/ts-infra/dist/telemetry/metrics.js
43639
43903
  function getMeter() {
43640
43904
  return import_api3.metrics.getMeter(METER_NAME, METER_VERSION);
43641
43905
  }
@@ -43732,7 +43996,7 @@ var init_metrics = __esm(() => {
43732
43996
  instruments = {};
43733
43997
  });
43734
43998
 
43735
- // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.57+33b96fa7d4032676/node_modules/@gobing-ai/ts-infra/dist/event-bus/event-bus.js
43999
+ // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.62+c884df390da1a1e8/node_modules/@gobing-ai/ts-infra/dist/event-bus/event-bus.js
43736
44000
  function busLogger() {
43737
44001
  if (!_busLogger)
43738
44002
  _busLogger = getLogger2("event-bus");
@@ -43985,7 +44249,7 @@ var init_event_bus = __esm(() => {
43985
44249
  init_metrics();
43986
44250
  });
43987
44251
 
43988
- // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.57+33b96fa7d4032676/node_modules/@gobing-ai/ts-infra/dist/event-bus/default-observers.js
44252
+ // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.62+c884df390da1a1e8/node_modules/@gobing-ai/ts-infra/dist/event-bus/default-observers.js
43989
44253
  function obsLogger() {
43990
44254
  if (!_obsLogger)
43991
44255
  _obsLogger = getLogger2("event-bus.observers");
@@ -44049,7 +44313,7 @@ var init_default_observers = __esm(() => {
44049
44313
  init_event_bus();
44050
44314
  });
44051
44315
 
44052
- // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.57+33b96fa7d4032676/node_modules/@gobing-ai/ts-infra/dist/event-bus/file-observer.js
44316
+ // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.62+c884df390da1a1e8/node_modules/@gobing-ai/ts-infra/dist/event-bus/file-observer.js
44053
44317
  function parentDir(filePath) {
44054
44318
  const idx = filePath.lastIndexOf("/");
44055
44319
  return idx <= 0 ? "." : filePath.slice(0, idx);
@@ -44147,7 +44411,84 @@ var init_file_observer = __esm(() => {
44147
44411
  init_logger3();
44148
44412
  });
44149
44413
 
44150
- // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.57+33b96fa7d4032676/node_modules/@gobing-ai/ts-infra/dist/internals/drain.js
44414
+ // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.62+c884df390da1a1e8/node_modules/@gobing-ai/ts-infra/dist/execution-policy.js
44415
+ function resolveExecutionTimeoutMs(scope, value, inherited) {
44416
+ if (value === undefined) {
44417
+ return inherited ?? null;
44418
+ }
44419
+ if (value === null) {
44420
+ return null;
44421
+ }
44422
+ if (typeof value !== "number" || !Number.isInteger(value) || value <= 0) {
44423
+ throw new RangeError(`${scope} timeoutMs must be a positive integer or null; received ${String(value)}`);
44424
+ }
44425
+ return value;
44426
+ }
44427
+ async function runWithExecutionDeadline(action, options) {
44428
+ const deadlineMs = options.timeoutMs ?? null;
44429
+ const controller = new AbortController;
44430
+ const cancellation = { reason: undefined };
44431
+ const context4 = {
44432
+ signal: controller.signal,
44433
+ deadlineMs,
44434
+ get cancellationReason() {
44435
+ return cancellation.reason;
44436
+ }
44437
+ };
44438
+ if (options.signal !== undefined) {
44439
+ if (options.signal.aborted) {
44440
+ cancellation.reason = "cancelled";
44441
+ controller.abort();
44442
+ } else {
44443
+ const abortFromCaller = () => {
44444
+ if (cancellation.reason === undefined)
44445
+ cancellation.reason = "cancelled";
44446
+ controller.abort();
44447
+ };
44448
+ options.signal.addEventListener("abort", abortFromCaller, { once: true });
44449
+ }
44450
+ }
44451
+ const startMs = performance.now();
44452
+ let timer;
44453
+ if (deadlineMs !== null) {
44454
+ timer = setTimeout(() => {
44455
+ if (cancellation.reason === undefined)
44456
+ cancellation.reason = "timeout";
44457
+ controller.abort();
44458
+ }, deadlineMs);
44459
+ }
44460
+ let result;
44461
+ let error51;
44462
+ let failed = false;
44463
+ try {
44464
+ result = await action(context4);
44465
+ } catch (caught) {
44466
+ error51 = caught;
44467
+ failed = true;
44468
+ } finally {
44469
+ if (timer !== undefined)
44470
+ clearTimeout(timer);
44471
+ }
44472
+ const elapsedMs = performance.now() - startMs;
44473
+ const timedOut = cancellation.reason === "timeout";
44474
+ if (failed) {
44475
+ return { outcome: "error", error: error51, elapsedMs, timedOut };
44476
+ }
44477
+ if (timedOut) {
44478
+ return {
44479
+ outcome: "timeout",
44480
+ error: new Error(`execution deadline (${String(deadlineMs)}ms) expired`),
44481
+ elapsedMs,
44482
+ timedOut
44483
+ };
44484
+ }
44485
+ if (cancellation.reason === "cancelled") {
44486
+ return { outcome: "cancelled", elapsedMs, timedOut };
44487
+ }
44488
+ return { outcome: "completed", result, elapsedMs, timedOut };
44489
+ }
44490
+
44491
+ // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.62+c884df390da1a1e8/node_modules/@gobing-ai/ts-infra/dist/internals/drain.js
44151
44492
  function settleWithin(promise2, deadline) {
44152
44493
  const remaining = deadline - Date.now();
44153
44494
  if (remaining <= 0)
@@ -50755,6 +51096,33 @@ function derivedWarnings(derived) {
50755
51096
  return warnings;
50756
51097
  }
50757
51098
 
51099
+ // ../../packages/domain/src/analytics/derived-assistant-duration.ts
51100
+ async function deriveMissingAssistantDurations(db2, ceilingMs) {
51101
+ await db2.run(`
51102
+ WITH ordered AS (
51103
+ SELECT record_hash AS recordHash,
51104
+ role,
51105
+ duration_ms,
51106
+ CAST(ROUND((unixepoch(ts, 'subsec') - unixepoch(LAG(ts) OVER (
51107
+ PARTITION BY source, session_id ORDER BY seq
51108
+ ), 'subsec')) * 1000) AS INTEGER) AS deltaMs
51109
+ FROM history_message
51110
+ WHERE ts IS NOT NULL AND ts LIKE '____-__-__T%'
51111
+ )
51112
+ UPDATE history_message
51113
+ SET duration_ms = ordered.deltaMs,
51114
+ duration_source = 'derived'
51115
+ FROM ordered
51116
+ WHERE history_message.record_hash = ordered.recordHash
51117
+ AND ordered.role = 'assistant'
51118
+ AND ordered.duration_ms IS NULL
51119
+ AND history_message.duration_ms IS NULL
51120
+ AND ordered.deltaMs IS NOT NULL
51121
+ AND ordered.deltaMs > 0
51122
+ AND ordered.deltaMs <= ${ceilingMs}
51123
+ `);
51124
+ }
51125
+
50758
51126
  // ../../packages/domain/src/analytics/tool-name-sql.ts
50759
51127
  var EFFECTIVE_TOOL_NAME_SQL = `CASE
50760
51128
  WHEN tc.tool_name IS NOT NULL AND TRIM(tc.tool_name) != '' AND tc.tool_name != 'unknown'
@@ -54205,6 +54573,10 @@ async function recordHistoryBoardRollupMeta(db2, historyVersion, refreshedAt = n
54205
54573
  VALUES (1, ?, ?)
54206
54574
  ON CONFLICT(id) DO UPDATE SET history_version = excluded.history_version, refreshed_at = excluded.refreshed_at`, historyVersion, refreshedAt);
54207
54575
  }
54576
+ async function latestHistoryImportedAt(db2) {
54577
+ const rows = await db2.queryAll("SELECT MAX(imported_at) AS v FROM history_message");
54578
+ return rows[0]?.v ?? null;
54579
+ }
54208
54580
  async function hasHistoryBoardRollupRows(db2, table3) {
54209
54581
  const validTables = {
54210
54582
  history_board_message_5m: true,
@@ -54277,8 +54649,8 @@ END`;
54277
54649
  ];
54278
54650
  });
54279
54651
 
54280
- // ../../node_modules/.bun/@gobing-ai+ts-llm-jsonl-importer@0.4.57+f66f1d7f0d37c145/node_modules/@gobing-ai/ts-llm-jsonl-importer/dist/errors.js
54281
- var HistoryImportError;
54652
+ // ../../node_modules/.bun/@gobing-ai+ts-llm-jsonl-importer@0.4.62+f66f1d7f0d37c145/node_modules/@gobing-ai/ts-llm-jsonl-importer/dist/errors.js
54653
+ var HistoryImportError, ImportCancelledError;
54282
54654
  var init_errors5 = __esm(() => {
54283
54655
  HistoryImportError = class HistoryImportError extends Error {
54284
54656
  details;
@@ -54288,10 +54660,45 @@ var init_errors5 = __esm(() => {
54288
54660
  this.name = "HistoryImportError";
54289
54661
  }
54290
54662
  };
54663
+ ImportCancelledError = class ImportCancelledError extends HistoryImportError {
54664
+ abortReason;
54665
+ constructor(message = "Import cancelled by abort signal", abortReason) {
54666
+ super(message, abortReason);
54667
+ this.abortReason = abortReason;
54668
+ this.name = "ImportCancelledError";
54669
+ }
54670
+ };
54291
54671
  });
54292
54672
 
54293
- // ../../node_modules/.bun/@gobing-ai+ts-llm-jsonl-importer@0.4.57+f66f1d7f0d37c145/node_modules/@gobing-ai/ts-llm-jsonl-importer/dist/schema-sql.js
54294
- var HISTORY_IMPORT_SCHEMA_VERSION = "0.4.56", HISTORY_IMPORT_SCHEMA_SQL;
54673
+ // ../../node_modules/.bun/@gobing-ai+ts-llm-jsonl-importer@0.4.62+f66f1d7f0d37c145/node_modules/@gobing-ai/ts-llm-jsonl-importer/dist/cancellation.js
54674
+ function throwIfImportAborted(signal) {
54675
+ if (signal?.aborted) {
54676
+ throw new ImportCancelledError("Import cancelled by abort signal", signal.reason);
54677
+ }
54678
+ }
54679
+ var init_cancellation = __esm(() => {
54680
+ init_errors5();
54681
+ });
54682
+
54683
+ // ../../node_modules/.bun/@gobing-ai+ts-llm-jsonl-importer@0.4.62+f66f1d7f0d37c145/node_modules/@gobing-ai/ts-llm-jsonl-importer/dist/hash.js
54684
+ import { createHash as createHash3 } from "crypto";
54685
+ function stableJson(value) {
54686
+ if (Array.isArray(value)) {
54687
+ return `[${value.map((entry) => entry === undefined ? "null" : stableJson(entry)).join(",")}]`;
54688
+ }
54689
+ if (value !== null && typeof value === "object") {
54690
+ const record2 = value;
54691
+ return `{${Object.keys(record2).filter((key) => record2[key] !== undefined).sort().map((key) => `${JSON.stringify(key)}:${stableJson(record2[key])}`).join(",")}}`;
54692
+ }
54693
+ return JSON.stringify(value) ?? "null";
54694
+ }
54695
+ function sha256(value) {
54696
+ return createHash3("sha256").update(stableJson(value)).digest("hex");
54697
+ }
54698
+ var init_hash = () => {};
54699
+
54700
+ // ../../node_modules/.bun/@gobing-ai+ts-llm-jsonl-importer@0.4.62+f66f1d7f0d37c145/node_modules/@gobing-ai/ts-llm-jsonl-importer/dist/schema-sql.js
54701
+ var HISTORY_IMPORT_SCHEMA_VERSION = "0.4.62", HISTORY_IMPORT_SCHEMA_SQL;
54295
54702
  var init_schema_sql = __esm(() => {
54296
54703
  HISTORY_IMPORT_SCHEMA_SQL = `
54297
54704
  CREATE TABLE IF NOT EXISTS history_import_checkpoint (
@@ -54413,24 +54820,7 @@ CREATE INDEX IF NOT EXISTS idx_history_skill_call_invocation_kind
54413
54820
  `.trim();
54414
54821
  });
54415
54822
 
54416
- // ../../node_modules/.bun/@gobing-ai+ts-llm-jsonl-importer@0.4.57+f66f1d7f0d37c145/node_modules/@gobing-ai/ts-llm-jsonl-importer/dist/hash.js
54417
- import { createHash as createHash3 } from "crypto";
54418
- function stableJson(value) {
54419
- if (Array.isArray(value)) {
54420
- return `[${value.map((entry) => entry === undefined ? "null" : stableJson(entry)).join(",")}]`;
54421
- }
54422
- if (value !== null && typeof value === "object") {
54423
- const record2 = value;
54424
- return `{${Object.keys(record2).filter((key) => record2[key] !== undefined).sort().map((key) => `${JSON.stringify(key)}:${stableJson(record2[key])}`).join(",")}}`;
54425
- }
54426
- return JSON.stringify(value) ?? "null";
54427
- }
54428
- function sha256(value) {
54429
- return createHash3("sha256").update(stableJson(value)).digest("hex");
54430
- }
54431
- var init_hash = () => {};
54432
-
54433
- // ../../node_modules/.bun/@gobing-ai+ts-llm-jsonl-importer@0.4.57+f66f1d7f0d37c145/node_modules/@gobing-ai/ts-llm-jsonl-importer/dist/mappers.js
54823
+ // ../../node_modules/.bun/@gobing-ai+ts-llm-jsonl-importer@0.4.62+f66f1d7f0d37c145/node_modules/@gobing-ai/ts-llm-jsonl-importer/dist/mappers.js
54434
54824
  function identityFieldMap(keys) {
54435
54825
  const map2 = {};
54436
54826
  for (const key of keys) {
@@ -56004,7 +56394,7 @@ var init_mappers = __esm(() => {
56004
56394
  GEMINI_SCHEMA = passthroughSchema;
56005
56395
  });
56006
56396
 
56007
- // ../../node_modules/.bun/@gobing-ai+ts-llm-jsonl-importer@0.4.57+f66f1d7f0d37c145/node_modules/@gobing-ai/ts-llm-jsonl-importer/dist/sources.js
56397
+ // ../../node_modules/.bun/@gobing-ai+ts-llm-jsonl-importer@0.4.62+f66f1d7f0d37c145/node_modules/@gobing-ai/ts-llm-jsonl-importer/dist/sources.js
56008
56398
  function firstString(...values) {
56009
56399
  for (const value of values) {
56010
56400
  if (typeof value === "string" && value.length > 0)
@@ -56161,7 +56551,7 @@ var init_sources = __esm(() => {
56161
56551
  };
56162
56552
  });
56163
56553
 
56164
- // ../../node_modules/.bun/@gobing-ai+ts-llm-jsonl-importer@0.4.57+f66f1d7f0d37c145/node_modules/@gobing-ai/ts-llm-jsonl-importer/dist/jsonl-importer-dao.js
56554
+ // ../../node_modules/.bun/@gobing-ai+ts-llm-jsonl-importer@0.4.62+f66f1d7f0d37c145/node_modules/@gobing-ai/ts-llm-jsonl-importer/dist/jsonl-importer-dao.js
56165
56555
  function targetTableFor(table3) {
56166
56556
  if (!VALID_TABLE_NAME.test(table3)) {
56167
56557
  throw new HistoryImportError(`Invalid history ETL target table: ${table3}`, { table: table3 });
@@ -56358,26 +56748,6 @@ async function ledgerExistingHashes(db2, hashes) {
56358
56748
  }
56359
56749
  return existing;
56360
56750
  }
56361
- async function findAssistantDurationCandidates(db2) {
56362
- const candidateSql = `
56363
- WITH ordered AS (
56364
- SELECT record_hash,
56365
- role,
56366
- duration_ms,
56367
- ts,
56368
- LAG(ts) OVER (PARTITION BY source, session_id ORDER BY seq) AS prev_ts
56369
- FROM history_message
56370
- WHERE ts IS NOT NULL AND ts LIKE '____-__-__T%'
56371
- )
56372
- SELECT record_hash AS recordHash,
56373
- CAST(ROUND((unixepoch(ts, 'subsec') - unixepoch(prev_ts, 'subsec')) * 1000) AS INTEGER) AS deltaMs
56374
- FROM ordered
56375
- WHERE role = 'assistant' AND duration_ms IS NULL AND prev_ts IS NOT NULL`;
56376
- return db2.queryAll(candidateSql);
56377
- }
56378
- async function updateAssistantDuration(db2, recordHash, durationMs, durationSource) {
56379
- await db2.run("UPDATE history_message SET duration_ms = ?, duration_source = ? WHERE record_hash = ? AND duration_ms IS NULL", durationMs, durationSource, recordHash);
56380
- }
56381
56751
  async function reconcileFullImport(db2, source, desiredHashes, discoveredFiles, dryRun) {
56382
56752
  const ledgerRows = await db2.queryAll("SELECT record_hash, target_table FROM history_import_ledger WHERE source = ?", source);
56383
56753
  const stale = [];
@@ -56604,30 +56974,7 @@ var init_jsonl_importer_dao = __esm(() => {
56604
56974
  ARGS_TOOL_NAME_KEYS = ["tool", "tool_name", "toolName", "name", "command"];
56605
56975
  });
56606
56976
 
56607
- // ../../node_modules/.bun/@gobing-ai+ts-llm-jsonl-importer@0.4.57+f66f1d7f0d37c145/node_modules/@gobing-ai/ts-llm-jsonl-importer/dist/assistant-duration.js
56608
- async function deriveAssistantDurations(db2) {
56609
- const rows = await findAssistantDurationCandidates(db2);
56610
- let derived = 0;
56611
- let skippedOverCeiling = 0;
56612
- for (const row of rows) {
56613
- if (row.deltaMs === null || row.deltaMs <= 0)
56614
- continue;
56615
- if (row.deltaMs > DERIVED_DURATION_CEILING_MS) {
56616
- skippedOverCeiling += 1;
56617
- continue;
56618
- }
56619
- await updateAssistantDuration(db2, row.recordHash, row.deltaMs, DURATION_SOURCE_DERIVED);
56620
- derived += 1;
56621
- }
56622
- return { derived, skippedOverCeiling };
56623
- }
56624
- var DURATION_SOURCE_DERIVED = "derived", DERIVED_DURATION_CEILING_MS;
56625
- var init_assistant_duration = __esm(() => {
56626
- init_jsonl_importer_dao();
56627
- DERIVED_DURATION_CEILING_MS = 30 * 60 * 1000;
56628
- });
56629
-
56630
- // ../../node_modules/.bun/@gobing-ai+ts-llm-jsonl-importer@0.4.57+f66f1d7f0d37c145/node_modules/@gobing-ai/ts-llm-jsonl-importer/dist/redaction.js
56977
+ // ../../node_modules/.bun/@gobing-ai+ts-llm-jsonl-importer@0.4.62+f66f1d7f0d37c145/node_modules/@gobing-ai/ts-llm-jsonl-importer/dist/redaction.js
56631
56978
  function redactValue(value, rules = DEFAULT_REDACTION_RULES) {
56632
56979
  if (typeof value === "string") {
56633
56980
  return rules.reduce((current, rule) => current.replace(rule.pattern, rule.replacement), value);
@@ -56679,7 +57026,7 @@ var init_redaction = __esm(() => {
56679
57026
  ];
56680
57027
  });
56681
57028
 
56682
- // ../../node_modules/.bun/@gobing-ai+ts-llm-jsonl-importer@0.4.57+f66f1d7f0d37c145/node_modules/@gobing-ai/ts-llm-jsonl-importer/dist/importer.js
57029
+ // ../../node_modules/.bun/@gobing-ai+ts-llm-jsonl-importer@0.4.62+f66f1d7f0d37c145/node_modules/@gobing-ai/ts-llm-jsonl-importer/dist/importer.js
56683
57030
  function toolCallKey(source, sessionId, callId) {
56684
57031
  return `${source}${TOOL_CALL_KEY_SEP}${sessionId}${TOOL_CALL_KEY_SEP}${callId}`;
56685
57032
  }
@@ -56708,12 +57055,14 @@ function attachableDuration(timing, messageTsMs) {
56708
57055
  };
56709
57056
  }
56710
57057
  async function runJsonlImport(source, options) {
57058
+ throwIfImportAborted(options.signal);
56711
57059
  const definition = resolveSourceDefinition(source);
56712
57060
  const resolvedSource = definition.source;
56713
57061
  const fileSystem = options.fileSystem ?? createNodeFileSystem3();
56714
57062
  await applyHistoryImportSchema(options.db);
56715
57063
  const mode = options.mode ?? "incremental";
56716
57064
  const files = await discoverFiles(definition, options.roots, options.files, fileSystem, options.paths);
57065
+ throwIfImportAborted(options.signal);
56717
57066
  if (mode === "full" && !options.dryRun) {
56718
57067
  await resetCheckpoints(options.db, resolvedSource, files);
56719
57068
  }
@@ -56732,6 +57081,7 @@ async function runJsonlImport(source, options) {
56732
57081
  const codexLastAssistant = new Map;
56733
57082
  const checkpoints = mode === "incremental" ? await loadSourceCheckpoints(options.db, resolvedSource) : undefined;
56734
57083
  for (const file2 of files) {
57084
+ throwIfImportAborted(options.signal);
56735
57085
  const entry = checkpoints?.get(file2);
56736
57086
  const checkpoint = entry?.line ?? 0;
56737
57087
  if (checkpoints !== undefined && entry !== undefined && entry.size !== null && entry.mtimeMs !== null) {
@@ -56743,6 +57093,7 @@ async function runJsonlImport(source, options) {
56743
57093
  }
56744
57094
  let lineNumber = 0;
56745
57095
  for await (const rawLine of readLines(fileSystem, file2)) {
57096
+ throwIfImportAborted(options.signal);
56746
57097
  lineNumber += 1;
56747
57098
  const line = rawLine.trim();
56748
57099
  if (line.length === 0 || lineNumber <= checkpoint)
@@ -56913,22 +57264,23 @@ async function runJsonlImport(source, options) {
56913
57264
  checkpointUpdates += 1;
56914
57265
  }
56915
57266
  }
56916
- if (!options.dryRun && mode === "incremental") {
56917
- const statAfter = await safeStat(fileSystem, file2);
56918
- const identityDegraded = entry !== undefined && entry.size === null && entry.mtimeMs === null;
56919
- const identityChanged = entry !== undefined && (entry.size !== (statAfter?.size ?? null) || entry.mtimeMs !== (statAfter?.mtimeMs ?? null));
56920
- if (statAfter !== null && !identityDegraded && (entry === undefined || identityChanged)) {
56921
- await options.db.batch([
56922
- checkpointUpsertOp(resolvedSource, file2, Math.max(lineNumber, checkpoint), options.now, {
56923
- size: statAfter.size,
56924
- mtimeMs: statAfter.mtimeMs
56925
- })
56926
- ]);
56927
- checkpointUpdates += 1;
56928
- }
57267
+ }
57268
+ if (!options.dryRun && mode === "incremental") {
57269
+ const statAfter = await safeStat(fileSystem, file2);
57270
+ const identityDegraded = entry !== undefined && entry.size === null && entry.mtimeMs === null;
57271
+ const identityChanged = entry !== undefined && (entry.size !== (statAfter?.size ?? null) || entry.mtimeMs !== (statAfter?.mtimeMs ?? null));
57272
+ if (statAfter !== null && !identityDegraded && (entry === undefined || identityChanged)) {
57273
+ await options.db.batch([
57274
+ checkpointUpsertOp(resolvedSource, file2, Math.max(lineNumber, checkpoint), options.now, {
57275
+ size: statAfter.size,
57276
+ mtimeMs: statAfter.mtimeMs
57277
+ })
57278
+ ]);
57279
+ checkpointUpdates += 1;
56929
57280
  }
56930
57281
  }
56931
57282
  }
57283
+ throwIfImportAborted(options.signal);
56932
57284
  let reconciliation;
56933
57285
  if (mode === "full") {
56934
57286
  reconciliation = await reconcileFullImport(options.db, resolvedSource, desiredHashes, files, options.dryRun ?? false);
@@ -57076,6 +57428,7 @@ async function* readLines(fileSystem, file2) {
57076
57428
  var TOOL_CALL_KEY_SEP = "\x00";
57077
57429
  var init_importer = __esm(() => {
57078
57430
  init_dist5();
57431
+ init_cancellation();
57079
57432
  init_hash();
57080
57433
  init_jsonl_importer_dao();
57081
57434
  init_mappers();
@@ -57083,8 +57436,9 @@ var init_importer = __esm(() => {
57083
57436
  init_sources();
57084
57437
  });
57085
57438
 
57086
- // ../../node_modules/.bun/@gobing-ai+ts-llm-jsonl-importer@0.4.57+f66f1d7f0d37c145/node_modules/@gobing-ai/ts-llm-jsonl-importer/dist/opencode-importer.js
57439
+ // ../../node_modules/.bun/@gobing-ai+ts-llm-jsonl-importer@0.4.62+f66f1d7f0d37c145/node_modules/@gobing-ai/ts-llm-jsonl-importer/dist/opencode-importer.js
57087
57440
  async function runOpenCodeImport(options) {
57441
+ throwIfImportAborted(options.signal);
57088
57442
  const paths = options.paths ?? ambientRuntimePaths2();
57089
57443
  const sourceDatabase = options.sourceDatabase ?? joinPath2(paths.home, ".local/share/opencode/opencode.db");
57090
57444
  const fileSystem = options.fileSystem ?? createNodeFileSystem3();
@@ -57110,6 +57464,7 @@ async function runOpenCodeImport(options) {
57110
57464
  let reconciliation;
57111
57465
  try {
57112
57466
  while (true) {
57467
+ throwIfImportAborted(options.signal);
57113
57468
  const messages = await readOpenCodeMessages(sourceDb, lastTime, lastId, PAGE_SIZE);
57114
57469
  if (messages.length === 0)
57115
57470
  break;
@@ -57173,6 +57528,7 @@ async function runOpenCodeImport(options) {
57173
57528
  }
57174
57529
  reconciliation = { staleTargetRows, staleLedgerRows, staleCheckpointRows };
57175
57530
  }
57531
+ throwIfImportAborted(options.signal);
57176
57532
  if (operations2.length > 0)
57177
57533
  await options.db.batch(operations2);
57178
57534
  } finally {
@@ -57365,13 +57721,14 @@ var SOURCE = "opencode", PAGE_SIZE = 250;
57365
57721
  var init_opencode_importer = __esm(() => {
57366
57722
  init_dist4();
57367
57723
  init_dist5();
57724
+ init_cancellation();
57368
57725
  init_hash();
57369
57726
  init_jsonl_importer_dao();
57370
57727
  init_mappers();
57371
57728
  init_redaction();
57372
57729
  });
57373
57730
 
57374
- // ../../node_modules/.bun/@gobing-ai+ts-llm-jsonl-importer@0.4.57+f66f1d7f0d37c145/node_modules/@gobing-ai/ts-llm-jsonl-importer/dist/tables.js
57731
+ // ../../node_modules/.bun/@gobing-ai+ts-llm-jsonl-importer@0.4.62+f66f1d7f0d37c145/node_modules/@gobing-ai/ts-llm-jsonl-importer/dist/tables.js
57375
57732
  var BOOKKEEPING_HISTORY_TABLES, IMPORTER_OWNED_TABLES;
57376
57733
  var init_tables = __esm(() => {
57377
57734
  init_jsonl_importer_dao();
@@ -57384,9 +57741,8 @@ var init_tables = __esm(() => {
57384
57741
  ];
57385
57742
  });
57386
57743
 
57387
- // ../../node_modules/.bun/@gobing-ai+ts-llm-jsonl-importer@0.4.57+f66f1d7f0d37c145/node_modules/@gobing-ai/ts-llm-jsonl-importer/dist/index.js
57744
+ // ../../node_modules/.bun/@gobing-ai+ts-llm-jsonl-importer@0.4.62+f66f1d7f0d37c145/node_modules/@gobing-ai/ts-llm-jsonl-importer/dist/index.js
57388
57745
  var init_dist6 = __esm(() => {
57389
- init_assistant_duration();
57390
57746
  init_importer();
57391
57747
  init_mappers();
57392
57748
  init_opencode_importer();
@@ -57479,6 +57835,20 @@ async function checkImporterSchemaVersion(db2) {
57479
57835
  remediation
57480
57836
  };
57481
57837
  }
57838
+ async function repairImporterSchemaVersion(db2) {
57839
+ const installed = HISTORY_IMPORT_SCHEMA_VERSION;
57840
+ for (const statement of HISTORY_IMPORT_SCHEMA_SQL.split(";").map((s2) => s2.trim()).filter((s2) => s2.length > 0)) {
57841
+ await db2.exec(statement);
57842
+ }
57843
+ await db2.run('DELETE FROM "__spur_cli_migrations" WHERE id LIKE ? AND id != ?', [
57844
+ `${IMPORTER_SCHEMA_LEDGER_PREFIX}%`,
57845
+ `${IMPORTER_SCHEMA_LEDGER_PREFIX}${installed}`
57846
+ ]);
57847
+ await db2.run('INSERT OR REPLACE INTO "__spur_cli_migrations" (id, applied_at) VALUES (?, ?)', [
57848
+ `${IMPORTER_SCHEMA_LEDGER_PREFIX}${installed}`,
57849
+ Date.now()
57850
+ ]);
57851
+ }
57482
57852
  var IMPORTER_SCHEMA_LEDGER_PREFIX = "importer_schema@";
57483
57853
  var init_importer_schema_version = __esm(() => {
57484
57854
  init_dist6();
@@ -60241,7 +60611,7 @@ function observationIsNewer(leftAt, leftId, rightAt, rightId) {
60241
60611
  }
60242
60612
  var AGENT_EXECUTOR_UPDATE_COLUMNS = "project_id, executor_name, observation_id, observed_at, agent, model, disabled, applied_observation_id, applied_at, attempts, retry_after, last_error";
60243
60613
 
60244
- // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.57+2c070f091b619667/node_modules/@gobing-ai/ts-db/dist/schema/drizzle-internals.js
60614
+ // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.62+39fe09901e9c1f39/node_modules/@gobing-ai/ts-db/dist/schema/drizzle-internals.js
60245
60615
  function sqlExpressionToText(value) {
60246
60616
  if (typeof value !== "object" || value === null || !("queryChunks" in value)) {
60247
60617
  return;
@@ -60264,7 +60634,7 @@ function getDrizzleTableName(table3) {
60264
60634
  return String(table3[nameSym]);
60265
60635
  }
60266
60636
 
60267
- // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.57+2c070f091b619667/node_modules/@gobing-ai/ts-db/dist/schema/ddl.js
60637
+ // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.62+39fe09901e9c1f39/node_modules/@gobing-ai/ts-db/dist/schema/ddl.js
60268
60638
  function quoteIdent(name) {
60269
60639
  return `"${name.replace(/"/g, '""')}"`;
60270
60640
  }
@@ -60636,7 +61006,7 @@ var init_drizzle_zod = __esm(() => {
60636
61006
  };
60637
61007
  });
60638
61008
 
60639
- // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.57+2c070f091b619667/node_modules/@gobing-ai/ts-db/dist/schema/define-table.js
61009
+ // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.62+39fe09901e9c1f39/node_modules/@gobing-ai/ts-db/dist/schema/define-table.js
60640
61010
  function defineTable(name, columns2) {
60641
61011
  const table3 = sqliteTable(name, columns2);
60642
61012
  let insert2;
@@ -60670,7 +61040,7 @@ var init_define_table = __esm(() => {
60670
61040
  init_ddl();
60671
61041
  });
60672
61042
 
60673
- // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.57+2c070f091b619667/node_modules/@gobing-ai/ts-db/dist/schema/index.js
61043
+ // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.62+39fe09901e9c1f39/node_modules/@gobing-ai/ts-db/dist/schema/index.js
60674
61044
  var exports_schema = {};
60675
61045
  __export(exports_schema, {
60676
61046
  text: () => text,
@@ -61365,7 +61735,7 @@ var init_dao = __esm(() => {
61365
61735
  init_workspace_dao();
61366
61736
  });
61367
61737
 
61368
- // ../../node_modules/.bun/@gobing-ai+ts-dual-workflow-engine@0.4.57+df32fe902834add3/node_modules/@gobing-ai/ts-dual-workflow-engine/dist/errors.js
61738
+ // ../../node_modules/.bun/@gobing-ai+ts-dual-workflow-engine@0.4.62+df32fe902834add3/node_modules/@gobing-ai/ts-dual-workflow-engine/dist/errors.js
61369
61739
  var WorkflowValidationError, FSMError, RunCollisionError, WorkflowResumeError;
61370
61740
  var init_errors6 = __esm(() => {
61371
61741
  WorkflowValidationError = class WorkflowValidationError extends Error {
@@ -61400,12 +61770,12 @@ var init_errors6 = __esm(() => {
61400
61770
  };
61401
61771
  });
61402
61772
 
61403
- // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.57+33b96fa7d4032676/node_modules/@gobing-ai/ts-infra/dist/event-bus/index.js
61773
+ // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.62+c884df390da1a1e8/node_modules/@gobing-ai/ts-infra/dist/event-bus/index.js
61404
61774
  var init_event_bus2 = __esm(() => {
61405
61775
  init_event_bus();
61406
61776
  });
61407
61777
 
61408
- // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.57+33b96fa7d4032676/node_modules/@gobing-ai/ts-infra/dist/api-client.js
61778
+ // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.62+c884df390da1a1e8/node_modules/@gobing-ai/ts-infra/dist/api-client.js
61409
61779
  function sanitizeUrlForObservability(url2) {
61410
61780
  try {
61411
61781
  const parsed = new URL(url2);
@@ -61636,12 +62006,12 @@ var init_api_client = __esm(() => {
61636
62006
  };
61637
62007
  });
61638
62008
 
61639
- // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.57+33b96fa7d4032676/node_modules/@gobing-ai/ts-infra/dist/telemetry/index.js
62009
+ // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.62+c884df390da1a1e8/node_modules/@gobing-ai/ts-infra/dist/telemetry/index.js
61640
62010
  var init_telemetry = __esm(() => {
61641
62011
  init_tracing2();
61642
62012
  });
61643
62013
 
61644
- // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.57+33b96fa7d4032676/node_modules/@gobing-ai/ts-infra/dist/index.js
62014
+ // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.62+c884df390da1a1e8/node_modules/@gobing-ai/ts-infra/dist/index.js
61645
62015
  var init_dist7 = __esm(() => {
61646
62016
  init_api_client();
61647
62017
  init_event_bus2();
@@ -61649,7 +62019,7 @@ var init_dist7 = __esm(() => {
61649
62019
  init_telemetry();
61650
62020
  });
61651
62021
 
61652
- // ../../node_modules/.bun/@gobing-ai+ts-dual-workflow-engine@0.4.57+df32fe902834add3/node_modules/@gobing-ai/ts-dual-workflow-engine/dist/run-lifecycle.js
62022
+ // ../../node_modules/.bun/@gobing-ai+ts-dual-workflow-engine@0.4.62+df32fe902834add3/node_modules/@gobing-ai/ts-dual-workflow-engine/dist/run-lifecycle.js
61653
62023
  function runtimeBuiltins(workflowName, stateOrNodeId, runId, transitionsTaken, mode) {
61654
62024
  return {
61655
62025
  workflow: workflowName,
@@ -61933,7 +62303,7 @@ var init_run_lifecycle = __esm(() => {
61933
62303
  ];
61934
62304
  });
61935
62305
 
61936
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.57+a23fed366e68a588/node_modules/@gobing-ai/ts-runtime/dist/extension/capability-registry.js
62306
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.62+9fbf929b070eed81/node_modules/@gobing-ai/ts-runtime/dist/extension/capability-registry.js
61937
62307
  class CapabilityRegistry {
61938
62308
  kind;
61939
62309
  capabilities = new Map;
@@ -61964,7 +62334,7 @@ class CapabilityRegistry {
61964
62334
  }
61965
62335
  }
61966
62336
 
61967
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.57+a23fed366e68a588/node_modules/@gobing-ai/ts-runtime/dist/extension/extension-path.js
62337
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.62+9fbf929b070eed81/node_modules/@gobing-ai/ts-runtime/dist/extension/extension-path.js
61968
62338
  function assertRelativeExtensionPath(path9, options = {}) {
61969
62339
  const where = options.sourceName !== undefined ? ` declared by "${options.sourceName}"` : "";
61970
62340
  if (/^([/\\]|[A-Za-z]:[/\\])/.test(path9)) {
@@ -61975,7 +62345,7 @@ function assertRelativeExtensionPath(path9, options = {}) {
61975
62345
  }
61976
62346
  }
61977
62347
 
61978
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.57+a23fed366e68a588/node_modules/@gobing-ai/ts-runtime/dist/extension/extension-loader.js
62348
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.62+9fbf929b070eed81/node_modules/@gobing-ai/ts-runtime/dist/extension/extension-loader.js
61979
62349
  import { isAbsolute, resolve } from "path";
61980
62350
  async function loadExtensionModules(refs, options, register) {
61981
62351
  if (refs.length === 0)
@@ -62014,17 +62384,17 @@ async function loadExtensionModules(refs, options, register) {
62014
62384
  }
62015
62385
  var init_extension_loader = () => {};
62016
62386
 
62017
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.57+a23fed366e68a588/node_modules/@gobing-ai/ts-runtime/dist/extension/index.js
62387
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.62+9fbf929b070eed81/node_modules/@gobing-ai/ts-runtime/dist/extension/index.js
62018
62388
  var init_extension = __esm(() => {
62019
62389
  init_extension_loader();
62020
62390
  });
62021
62391
 
62022
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.57+a23fed366e68a588/node_modules/@gobing-ai/ts-runtime/dist/extension.js
62392
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.62+9fbf929b070eed81/node_modules/@gobing-ai/ts-runtime/dist/extension.js
62023
62393
  var init_extension2 = __esm(() => {
62024
62394
  init_extension();
62025
62395
  });
62026
62396
 
62027
- // ../../node_modules/.bun/@gobing-ai+ts-dual-workflow-engine@0.4.57+df32fe902834add3/node_modules/@gobing-ai/ts-dual-workflow-engine/dist/schema.js
62397
+ // ../../node_modules/.bun/@gobing-ai+ts-dual-workflow-engine@0.4.62+df32fe902834add3/node_modules/@gobing-ai/ts-dual-workflow-engine/dist/schema.js
62028
62398
  var RESERVED_VAR_NAMES, IDENTIFIER, VarsSchema, EnvSchema, relativeExtensionPath, WorkflowExtensionsSchema, ActionDefSchema, GuardDefSchema, StateMachineWorkflowDefSchema, TransitionFlowWorkflowDefSchema, WorkflowDefSchema;
62029
62399
  var init_schema2 = __esm(() => {
62030
62400
  init_extension2();
@@ -62126,7 +62496,7 @@ var init_schema2 = __esm(() => {
62126
62496
  WorkflowDefSchema = exports_external.union([StateMachineWorkflowDefSchema, TransitionFlowWorkflowDefSchema]);
62127
62497
  });
62128
62498
 
62129
- // ../../node_modules/.bun/@gobing-ai+ts-dual-workflow-engine@0.4.57+df32fe902834add3/node_modules/@gobing-ai/ts-dual-workflow-engine/dist/config.js
62499
+ // ../../node_modules/.bun/@gobing-ai+ts-dual-workflow-engine@0.4.62+df32fe902834add3/node_modules/@gobing-ai/ts-dual-workflow-engine/dist/config.js
62130
62500
  async function loadWorkflowDef(path9, options = {}) {
62131
62501
  return parseWorkflowDef(await loadStructuredConfig(path9, options), path9);
62132
62502
  }
@@ -62293,7 +62663,7 @@ var init_config4 = __esm(() => {
62293
62663
  RUNTIME_TEMPLATE_NAMESPACES = new Set(RUNTIME_BUILTIN_KEYS);
62294
62664
  });
62295
62665
 
62296
- // ../../node_modules/.bun/@gobing-ai+ts-dual-workflow-engine@0.4.57+df32fe902834add3/node_modules/@gobing-ai/ts-dual-workflow-engine/dist/extensions.js
62666
+ // ../../node_modules/.bun/@gobing-ai+ts-dual-workflow-engine@0.4.62+df32fe902834add3/node_modules/@gobing-ai/ts-dual-workflow-engine/dist/extensions.js
62297
62667
  function collectWorkflowExtensions(sourceName, sourceDir, extensions) {
62298
62668
  if (extensions === undefined)
62299
62669
  return [];
@@ -62357,7 +62727,7 @@ var init_extensions = __esm(() => {
62357
62727
  init_errors6();
62358
62728
  });
62359
62729
 
62360
- // ../../node_modules/.bun/@gobing-ai+ts-dual-workflow-engine@0.4.57+df32fe902834add3/node_modules/@gobing-ai/ts-dual-workflow-engine/dist/host.js
62730
+ // ../../node_modules/.bun/@gobing-ai+ts-dual-workflow-engine@0.4.62+df32fe902834add3/node_modules/@gobing-ai/ts-dual-workflow-engine/dist/host.js
62361
62731
  class WorkflowEngineHost {
62362
62732
  lifecycleBus;
62363
62733
  actions = new CapabilityRegistry("workflow action");
@@ -62527,7 +62897,7 @@ var init_host = __esm(() => {
62527
62897
  init_errors6();
62528
62898
  });
62529
62899
 
62530
- // ../../node_modules/.bun/@gobing-ai+ts-dual-workflow-engine@0.4.57+df32fe902834add3/node_modules/@gobing-ai/ts-dual-workflow-engine/dist/schema-sql.js
62900
+ // ../../node_modules/.bun/@gobing-ai+ts-dual-workflow-engine@0.4.62+df32fe902834add3/node_modules/@gobing-ai/ts-dual-workflow-engine/dist/schema-sql.js
62531
62901
  var WORKFLOW_ENGINE_SCHEMA_SQL;
62532
62902
  var init_schema_sql2 = __esm(() => {
62533
62903
  WORKFLOW_ENGINE_SCHEMA_SQL = `
@@ -62600,7 +62970,7 @@ CREATE TABLE IF NOT EXISTS action_runs (
62600
62970
  `.trim();
62601
62971
  });
62602
62972
 
62603
- // ../../node_modules/.bun/@gobing-ai+ts-dual-workflow-engine@0.4.57+df32fe902834add3/node_modules/@gobing-ai/ts-dual-workflow-engine/dist/persistence.js
62973
+ // ../../node_modules/.bun/@gobing-ai+ts-dual-workflow-engine@0.4.62+df32fe902834add3/node_modules/@gobing-ai/ts-dual-workflow-engine/dist/persistence.js
62604
62974
  async function applyWorkflowEngineSchema(db2) {
62605
62975
  for (const statement of WORKFLOW_ENGINE_SCHEMA_SQL.split(";")) {
62606
62976
  const sql4 = statement.trim();
@@ -62916,7 +63286,7 @@ var init_persistence = __esm(() => {
62916
63286
  init_schema_sql2();
62917
63287
  });
62918
63288
 
62919
- // ../../node_modules/.bun/@gobing-ai+ts-dual-workflow-engine@0.4.57+df32fe902834add3/node_modules/@gobing-ai/ts-dual-workflow-engine/dist/variables.js
63289
+ // ../../node_modules/.bun/@gobing-ai+ts-dual-workflow-engine@0.4.62+df32fe902834add3/node_modules/@gobing-ai/ts-dual-workflow-engine/dist/variables.js
62920
63290
  function mergeVars(workflowVars = {}, overrideVars = {}) {
62921
63291
  return { ...workflowVars, ...overrideVars };
62922
63292
  }
@@ -62976,7 +63346,7 @@ var init_variables = __esm(() => {
62976
63346
  TEMPLATE_REF = /\$\{([^}]+)\}/g;
62977
63347
  });
62978
63348
 
62979
- // ../../node_modules/.bun/@gobing-ai+ts-dual-workflow-engine@0.4.57+df32fe902834add3/node_modules/@gobing-ai/ts-dual-workflow-engine/dist/action-step.js
63349
+ // ../../node_modules/.bun/@gobing-ai+ts-dual-workflow-engine@0.4.62+df32fe902834add3/node_modules/@gobing-ai/ts-dual-workflow-engine/dist/action-step.js
62980
63350
  async function runActionStep(action, vars, deps) {
62981
63351
  const { host, persistence, lifecycle, stateOrNodeId, runId } = deps;
62982
63352
  const resolved = resolveTemplates(action.options ?? {}, {
@@ -63040,7 +63410,7 @@ var init_action_step = __esm(() => {
63040
63410
  init_variables();
63041
63411
  });
63042
63412
 
63043
- // ../../node_modules/.bun/@gobing-ai+ts-dual-workflow-engine@0.4.57+df32fe902834add3/node_modules/@gobing-ai/ts-dual-workflow-engine/dist/state-machine.js
63413
+ // ../../node_modules/.bun/@gobing-ai+ts-dual-workflow-engine@0.4.62+df32fe902834add3/node_modules/@gobing-ai/ts-dual-workflow-engine/dist/state-machine.js
63044
63414
  class StateMachineDriver {
63045
63415
  options;
63046
63416
  constructor(options) {
@@ -63176,7 +63546,7 @@ var init_state_machine = __esm(() => {
63176
63546
  EMPTY_OUTCOME = { outcome: "completed", result: undefined, setVars: undefined };
63177
63547
  });
63178
63548
 
63179
- // ../../node_modules/.bun/@gobing-ai+ts-dual-workflow-engine@0.4.57+df32fe902834add3/node_modules/@gobing-ai/ts-dual-workflow-engine/dist/transition-flow.js
63549
+ // ../../node_modules/.bun/@gobing-ai+ts-dual-workflow-engine@0.4.62+df32fe902834add3/node_modules/@gobing-ai/ts-dual-workflow-engine/dist/transition-flow.js
63180
63550
  class TransitionFlowDriver {
63181
63551
  options;
63182
63552
  constructor(options) {
@@ -63295,7 +63665,7 @@ var init_transition_flow = __esm(() => {
63295
63665
  init_variables();
63296
63666
  });
63297
63667
 
63298
- // ../../node_modules/.bun/@gobing-ai+ts-dual-workflow-engine@0.4.57+df32fe902834add3/node_modules/@gobing-ai/ts-dual-workflow-engine/dist/service.js
63668
+ // ../../node_modules/.bun/@gobing-ai+ts-dual-workflow-engine@0.4.62+df32fe902834add3/node_modules/@gobing-ai/ts-dual-workflow-engine/dist/service.js
63299
63669
  class WorkflowService {
63300
63670
  host;
63301
63671
  persistence;
@@ -63520,7 +63890,7 @@ var init_service = __esm(() => {
63520
63890
  init_variables();
63521
63891
  });
63522
63892
 
63523
- // ../../node_modules/.bun/@gobing-ai+ts-dual-workflow-engine@0.4.57+df32fe902834add3/node_modules/@gobing-ai/ts-dual-workflow-engine/dist/index.js
63893
+ // ../../node_modules/.bun/@gobing-ai+ts-dual-workflow-engine@0.4.62+df32fe902834add3/node_modules/@gobing-ai/ts-dual-workflow-engine/dist/index.js
63524
63894
  var init_dist8 = __esm(() => {
63525
63895
  init_config4();
63526
63896
  init_extensions();
@@ -63530,7 +63900,7 @@ var init_dist8 = __esm(() => {
63530
63900
  init_service();
63531
63901
  });
63532
63902
 
63533
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.57+df32fe902834add3/node_modules/@gobing-ai/ts-rule-engine/dist/config/extensions.js
63903
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.62+df32fe902834add3/node_modules/@gobing-ai/ts-rule-engine/dist/config/extensions.js
63534
63904
  function collectExtensions(sourceName, sourceDir, extensions) {
63535
63905
  if (extensions === undefined)
63536
63906
  return [];
@@ -63544,7 +63914,7 @@ function collectExtensions(sourceName, sourceDir, extensions) {
63544
63914
  }
63545
63915
  var init_extensions2 = () => {};
63546
63916
 
63547
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.57+df32fe902834add3/node_modules/@gobing-ai/ts-rule-engine/dist/types.js
63917
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.62+df32fe902834add3/node_modules/@gobing-ai/ts-rule-engine/dist/types.js
63548
63918
  function createFinding(rule, message, filePath, extras = {}) {
63549
63919
  return {
63550
63920
  ruleId: rule.id,
@@ -63615,7 +63985,7 @@ var init_types3 = __esm(() => {
63615
63985
  });
63616
63986
  });
63617
63987
 
63618
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.57+df32fe902834add3/node_modules/@gobing-ai/ts-rule-engine/dist/config/loader.js
63988
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.62+df32fe902834add3/node_modules/@gobing-ai/ts-rule-engine/dist/config/loader.js
63619
63989
  async function loadPreset(name, options) {
63620
63990
  const merged = await buildMergedRoots(options.roots.map((root) => resolvePath4(root)), options.fileSystem);
63621
63991
  const presetPath = findMergedPreset(merged, name);
@@ -63830,7 +64200,7 @@ var init_loader2 = __esm(() => {
63830
64200
  FIX_MODE_AUTHORITY = { none: 0, suggest: 1, auto: 2 };
63831
64201
  });
63832
64202
 
63833
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.57+df32fe902834add3/node_modules/@gobing-ai/ts-rule-engine/dist/fixers/test-stub-fixer.js
64203
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.62+df32fe902834add3/node_modules/@gobing-ai/ts-rule-engine/dist/fixers/test-stub-fixer.js
63834
64204
  function normalizeFindingPath(filePath) {
63835
64205
  if (!filePath || isAbsolutePath2(filePath))
63836
64206
  return null;
@@ -63895,7 +64265,7 @@ var init_test_stub_fixer = __esm(() => {
63895
64265
  realFileSystem = createNodeFileSystem3();
63896
64266
  });
63897
64267
 
63898
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.57+df32fe902834add3/node_modules/@gobing-ai/ts-rule-engine/dist/fixers/fixers.js
64268
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.62+df32fe902834add3/node_modules/@gobing-ai/ts-rule-engine/dist/fixers/fixers.js
63899
64269
  function registerBuiltinFixers(host, exec) {
63900
64270
  const regexFixer = new RegexFixerProvider;
63901
64271
  const pathFixer = new PathFixerProvider;
@@ -64098,7 +64468,7 @@ var init_fixers = __esm(() => {
64098
64468
  };
64099
64469
  });
64100
64470
 
64101
- // ../../node_modules/.bun/@gobing-ai+ts-ai-runner@0.4.57+ad4f281a0a4e1620/node_modules/@gobing-ai/ts-ai-runner/dist/agents/shims.js
64471
+ // ../../node_modules/.bun/@gobing-ai+ts-ai-runner@0.4.62+04fc4d7e2750298b/node_modules/@gobing-ai/ts-ai-runner/dist/agents/shims.js
64102
64472
  function getAgentSessionCapability(agent) {
64103
64473
  return AGENT_SESSION_CAPABILITY[agent];
64104
64474
  }
@@ -64394,7 +64764,7 @@ var init_shims = __esm(() => {
64394
64764
  ALIAS_TO_CANONICAL = Object.fromEntries(Object.values(AGENT_SHIMS).flatMap((shim) => (shim.aliases ?? []).map((alias3) => [alias3, shim.name])));
64395
64765
  });
64396
64766
 
64397
- // ../../node_modules/.bun/@gobing-ai+ts-ai-runner@0.4.57+ad4f281a0a4e1620/node_modules/@gobing-ai/ts-ai-runner/dist/identity.js
64767
+ // ../../node_modules/.bun/@gobing-ai+ts-ai-runner@0.4.62+04fc4d7e2750298b/node_modules/@gobing-ai/ts-ai-runner/dist/identity.js
64398
64768
  function buildIdentityPreamble(ctx) {
64399
64769
  const sections = [
64400
64770
  `You are agent \`${ctx.agentId}\` (${ctx.agentType}) in workspace \`${ctx.workspace}\`.`
@@ -64470,7 +64840,7 @@ var init_identity = __esm(() => {
64470
64840
  init_dist5();
64471
64841
  });
64472
64842
 
64473
- // ../../node_modules/.bun/@gobing-ai+ts-ai-runner@0.4.57+ad4f281a0a4e1620/node_modules/@gobing-ai/ts-ai-runner/dist/quota.js
64843
+ // ../../node_modules/.bun/@gobing-ai+ts-ai-runner@0.4.62+04fc4d7e2750298b/node_modules/@gobing-ai/ts-ai-runner/dist/quota.js
64474
64844
  import { createHash as createHash4 } from "crypto";
64475
64845
  function classifyQuotaErrorRecord(record2) {
64476
64846
  const bounded = record2.length > MAX_QUOTA_EVIDENCE_BYTES ? record2.slice(-MAX_QUOTA_EVIDENCE_BYTES) : record2;
@@ -64571,7 +64941,7 @@ var init_quota = __esm(() => {
64571
64941
  ]);
64572
64942
  });
64573
64943
 
64574
- // ../../node_modules/.bun/@gobing-ai+ts-ai-runner@0.4.57+ad4f281a0a4e1620/node_modules/@gobing-ai/ts-ai-runner/dist/slash-command.js
64944
+ // ../../node_modules/.bun/@gobing-ai+ts-ai-runner@0.4.62+04fc4d7e2750298b/node_modules/@gobing-ai/ts-ai-runner/dist/slash-command.js
64575
64945
  function isClaudeStyleSlashCommand(input) {
64576
64946
  return SLASH_COMMAND_RE2.test(input);
64577
64947
  }
@@ -64599,7 +64969,7 @@ var init_slash_command = __esm(() => {
64599
64969
  SLASH_COMMAND_RE2 = /^\/([a-zA-Z0-9._-]+):([a-zA-Z0-9._-]+)(\s.*)?$/;
64600
64970
  });
64601
64971
 
64602
- // ../../node_modules/.bun/@gobing-ai+ts-ai-runner@0.4.57+ad4f281a0a4e1620/node_modules/@gobing-ai/ts-ai-runner/dist/ai-runner.js
64972
+ // ../../node_modules/.bun/@gobing-ai+ts-ai-runner@0.4.62+04fc4d7e2750298b/node_modules/@gobing-ai/ts-ai-runner/dist/ai-runner.js
64603
64973
  function buildCorrelationEnv(correlation) {
64604
64974
  return {
64605
64975
  [AGENT_RUN_ID_ENV]: correlation.runId,
@@ -64757,7 +65127,7 @@ var init_ai_runner = __esm(() => {
64757
65127
  init_slash_command();
64758
65128
  });
64759
65129
 
64760
- // ../../node_modules/.bun/@gobing-ai+ts-ai-runner@0.4.57+ad4f281a0a4e1620/node_modules/@gobing-ai/ts-ai-runner/dist/agent-detector.js
65130
+ // ../../node_modules/.bun/@gobing-ai+ts-ai-runner@0.4.62+04fc4d7e2750298b/node_modules/@gobing-ai/ts-ai-runner/dist/agent-detector.js
64761
65131
  class AgentDetector {
64762
65132
  runner;
64763
65133
  timeout;
@@ -64833,7 +65203,7 @@ var init_agent_detector = __esm(() => {
64833
65203
  VERSION_PATTERN = /(?<version>\d+\.\d+(?:\.\d+)?)/;
64834
65204
  });
64835
65205
 
64836
- // ../../node_modules/.bun/@gobing-ai+ts-ai-runner@0.4.57+ad4f281a0a4e1620/node_modules/@gobing-ai/ts-ai-runner/dist/agent-spec.js
65206
+ // ../../node_modules/.bun/@gobing-ai+ts-ai-runner@0.4.62+04fc4d7e2750298b/node_modules/@gobing-ai/ts-ai-runner/dist/agent-spec.js
64837
65207
  function validateAgentId(id) {
64838
65208
  if (!/^[a-z][a-z0-9_-]{1,63}$/.test(id)) {
64839
65209
  throw new ValueError(`Invalid agent id "${id}": expected 2-64 chars, lowercase alphanumeric, "_" or "-"`);
@@ -64932,7 +65302,7 @@ var init_agent_spec = __esm(() => {
64932
65302
  };
64933
65303
  });
64934
65304
 
64935
- // ../../node_modules/.bun/@gobing-ai+ts-ai-runner@0.4.57+ad4f281a0a4e1620/node_modules/@gobing-ai/ts-ai-runner/dist/agents/auth-shims.js
65305
+ // ../../node_modules/.bun/@gobing-ai+ts-ai-runner@0.4.62+04fc4d7e2750298b/node_modules/@gobing-ai/ts-ai-runner/dist/agents/auth-shims.js
64936
65306
  function isNonEmpty(value) {
64937
65307
  return value !== undefined && value.trim().length > 0;
64938
65308
  }
@@ -65057,7 +65427,7 @@ var init_auth_shims = __esm(() => {
65057
65427
  };
65058
65428
  });
65059
65429
 
65060
- // ../../node_modules/.bun/@gobing-ai+ts-ai-runner@0.4.57+ad4f281a0a4e1620/node_modules/@gobing-ai/ts-ai-runner/dist/model-health-probe.js
65430
+ // ../../node_modules/.bun/@gobing-ai+ts-ai-runner@0.4.62+04fc4d7e2750298b/node_modules/@gobing-ai/ts-ai-runner/dist/model-health-probe.js
65061
65431
  class ModelHealthProbeRegistry {
65062
65432
  probes = new Map;
65063
65433
  register(providerPrefix, probe) {
@@ -65181,7 +65551,7 @@ var init_model_health_probe = __esm(() => {
65181
65551
  };
65182
65552
  });
65183
65553
 
65184
- // ../../node_modules/.bun/@gobing-ai+ts-ai-runner@0.4.57+ad4f281a0a4e1620/node_modules/@gobing-ai/ts-ai-runner/dist/doctor-runner.js
65554
+ // ../../node_modules/.bun/@gobing-ai+ts-ai-runner@0.4.62+04fc4d7e2750298b/node_modules/@gobing-ai/ts-ai-runner/dist/doctor-runner.js
65185
65555
  class DoctorRunner {
65186
65556
  detector;
65187
65557
  runner;
@@ -65321,15 +65691,15 @@ var init_doctor_runner = __esm(() => {
65321
65691
  init_model_health_probe();
65322
65692
  });
65323
65693
 
65324
- // ../../node_modules/.bun/@gobing-ai+ts-ai-runner@0.4.57+ad4f281a0a4e1620/node_modules/@gobing-ai/ts-ai-runner/dist/events.js
65694
+ // ../../node_modules/.bun/@gobing-ai+ts-ai-runner@0.4.62+04fc4d7e2750298b/node_modules/@gobing-ai/ts-ai-runner/dist/events.js
65325
65695
  var init_events = () => {};
65326
65696
 
65327
- // ../../node_modules/.bun/@gobing-ai+ts-ai-runner@0.4.57+ad4f281a0a4e1620/node_modules/@gobing-ai/ts-ai-runner/dist/messages.js
65697
+ // ../../node_modules/.bun/@gobing-ai+ts-ai-runner@0.4.62+04fc4d7e2750298b/node_modules/@gobing-ai/ts-ai-runner/dist/messages.js
65328
65698
  function formatMessage(msg) {
65329
65699
  return `[task from=${msg.fromId ?? "operator"} id=${msg.id}] ${msg.body}`;
65330
65700
  }
65331
65701
 
65332
- // ../../node_modules/.bun/@gobing-ai+ts-ai-runner@0.4.57+ad4f281a0a4e1620/node_modules/@gobing-ai/ts-ai-runner/dist/team-agent-process.js
65702
+ // ../../node_modules/.bun/@gobing-ai+ts-ai-runner@0.4.62+04fc4d7e2750298b/node_modules/@gobing-ai/ts-ai-runner/dist/team-agent-process.js
65333
65703
  import { Buffer as Buffer5 } from "buffer";
65334
65704
 
65335
65705
  class TeamAgentProcess {
@@ -65494,7 +65864,7 @@ var init_team_agent_process = __esm(() => {
65494
65864
  init_quota();
65495
65865
  });
65496
65866
 
65497
- // ../../node_modules/.bun/@gobing-ai+ts-ai-runner@0.4.57+ad4f281a0a4e1620/node_modules/@gobing-ai/ts-ai-runner/dist/team-orchestrator.js
65867
+ // ../../node_modules/.bun/@gobing-ai+ts-ai-runner@0.4.62+04fc4d7e2750298b/node_modules/@gobing-ai/ts-ai-runner/dist/team-orchestrator.js
65498
65868
  class TeamOrchestrator {
65499
65869
  configDir;
65500
65870
  inbox;
@@ -65635,7 +66005,7 @@ var init_team_orchestrator = __esm(() => {
65635
66005
  init_team_agent_process();
65636
66006
  });
65637
66007
 
65638
- // ../../node_modules/.bun/@gobing-ai+ts-ai-runner@0.4.57+ad4f281a0a4e1620/node_modules/@gobing-ai/ts-ai-runner/dist/index.js
66008
+ // ../../node_modules/.bun/@gobing-ai+ts-ai-runner@0.4.62+04fc4d7e2750298b/node_modules/@gobing-ai/ts-ai-runner/dist/index.js
65639
66009
  var exports_dist3 = {};
65640
66010
  __export(exports_dist3, {
65641
66011
  validateAgentId: () => validateAgentId,
@@ -65695,7 +66065,7 @@ var init_dist9 = __esm(() => {
65695
66065
  init_team_orchestrator();
65696
66066
  });
65697
66067
 
65698
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.57+df32fe902834add3/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/config-access.js
66068
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.62+df32fe902834add3/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/config-access.js
65699
66069
  function stringArray(value) {
65700
66070
  return Array.isArray(value) && value.every((item) => typeof item === "string") ? value : undefined;
65701
66071
  }
@@ -65733,7 +66103,7 @@ function parseInlineFlags(source) {
65733
66103
  return { flags, rest: source.slice(match[0].length) };
65734
66104
  }
65735
66105
 
65736
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.57+df32fe902834add3/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/glob-match.js
66106
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.62+df32fe902834add3/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/glob-match.js
65737
66107
  function matchesAny(path9, patterns) {
65738
66108
  if (patterns === undefined || patterns.length === 0)
65739
66109
  return true;
@@ -65781,7 +66151,7 @@ function escapeRegExp(value) {
65781
66151
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
65782
66152
  }
65783
66153
 
65784
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.57+df32fe902834add3/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/file-discovery.js
66154
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.62+df32fe902834add3/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/file-discovery.js
65785
66155
  async function discoverFiles2(options) {
65786
66156
  const fs3 = options.fs ?? createNodeFileSystem3();
65787
66157
  const allFiles = await walkDir2(options.workdir, fs3, DEFAULT_EXCLUDES);
@@ -65812,12 +66182,12 @@ var init_file_discovery = __esm(() => {
65812
66182
  DEFAULT_EXCLUDES = new Set([".git", "node_modules", "dist", ".coverage", ".astro", ".wrangler"]);
65813
66183
  });
65814
66184
 
65815
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.57+df32fe902834add3/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/file-utils.js
66185
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.62+df32fe902834add3/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/file-utils.js
65816
66186
  var init_file_utils = __esm(() => {
65817
66187
  init_file_discovery();
65818
66188
  });
65819
66189
 
65820
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.57+df32fe902834add3/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/agent-detection-evaluator.js
66190
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.62+df32fe902834add3/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/agent-detection-evaluator.js
65821
66191
  class AgentDetectionEvaluator {
65822
66192
  detector;
65823
66193
  constructor(detector = new AgentDetector) {
@@ -65845,7 +66215,7 @@ var init_agent_detection_evaluator = __esm(() => {
65845
66215
  init_file_utils();
65846
66216
  });
65847
66217
 
65848
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.57+df32fe902834add3/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/coverage-gate-evaluator.js
66218
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.62+df32fe902834add3/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/coverage-gate-evaluator.js
65849
66219
  class CoverageGateEvaluator {
65850
66220
  constructor() {}
65851
66221
  async evaluate(rule, context4) {
@@ -65929,7 +66299,7 @@ var init_coverage_gate_evaluator = __esm(() => {
65929
66299
  init_file_utils();
65930
66300
  });
65931
66301
 
65932
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.57+df32fe902834add3/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/exit-code-evaluator.js
66302
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.62+df32fe902834add3/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/exit-code-evaluator.js
65933
66303
  class ExitCodeEvaluator {
65934
66304
  executor;
65935
66305
  constructor(executor = nodeBunFactory3.createProcessExecutor()) {
@@ -65965,7 +66335,7 @@ var init_exit_code_evaluator = __esm(() => {
65965
66335
  init_file_utils();
65966
66336
  });
65967
66337
 
65968
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.57+df32fe902834add3/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/forbidden-import-evaluator.js
66338
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.62+df32fe902834add3/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/forbidden-import-evaluator.js
65969
66339
  class ForbiddenImportEvaluator {
65970
66340
  constructor() {}
65971
66341
  async evaluate(rule, context4) {
@@ -66046,7 +66416,7 @@ var init_forbidden_import_evaluator = __esm(() => {
66046
66416
  init_file_utils();
66047
66417
  });
66048
66418
 
66049
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.57+df32fe902834add3/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/import-boundary-evaluator.js
66419
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.62+df32fe902834add3/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/import-boundary-evaluator.js
66050
66420
  class ImportBoundaryEvaluator {
66051
66421
  async evaluate(rule, context4) {
66052
66422
  const config4 = rule.evaluator.config ?? {};
@@ -66139,7 +66509,7 @@ var init_import_boundary_evaluator = __esm(() => {
66139
66509
  init_file_utils();
66140
66510
  });
66141
66511
 
66142
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.57+df32fe902834add3/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/path-evaluator.js
66512
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.62+df32fe902834add3/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/path-evaluator.js
66143
66513
  class PathEvaluator {
66144
66514
  constructor() {}
66145
66515
  async evaluate(rule, context4) {
@@ -66200,7 +66570,7 @@ var init_path_evaluator = __esm(() => {
66200
66570
  init_file_utils();
66201
66571
  });
66202
66572
 
66203
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.57+df32fe902834add3/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/regex-evaluator.js
66573
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.62+df32fe902834add3/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/regex-evaluator.js
66204
66574
  class RegexEvaluator {
66205
66575
  constructor() {}
66206
66576
  async evaluate(rule, context4) {
@@ -66278,7 +66648,7 @@ var init_regex_evaluator = __esm(() => {
66278
66648
  init_file_utils();
66279
66649
  });
66280
66650
 
66281
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.57+df32fe902834add3/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/ripgrep-evaluator.js
66651
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.62+df32fe902834add3/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/ripgrep-evaluator.js
66282
66652
  class RipgrepEvaluator {
66283
66653
  executor;
66284
66654
  constructor(executor = nodeBunFactory3.createProcessExecutor()) {
@@ -66371,7 +66741,7 @@ var init_ripgrep_evaluator = __esm(() => {
66371
66741
  init_file_utils();
66372
66742
  });
66373
66743
 
66374
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.57+df32fe902834add3/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/schema-artifact-evaluator.js
66744
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.62+df32fe902834add3/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/schema-artifact-evaluator.js
66375
66745
  class SchemaArtifactEvaluator {
66376
66746
  constructor() {}
66377
66747
  async evaluate(rule, context4) {
@@ -66449,7 +66819,7 @@ var init_schema_artifact_evaluator = __esm(() => {
66449
66819
  init_file_utils();
66450
66820
  });
66451
66821
 
66452
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.57+df32fe902834add3/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/secrets-scanner-evaluator.js
66822
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.62+df32fe902834add3/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/secrets-scanner-evaluator.js
66453
66823
  class SecretsScannerEvaluator {
66454
66824
  constructor() {}
66455
66825
  async evaluate(rule, context4) {
@@ -66517,7 +66887,7 @@ var init_secrets_scanner_evaluator = __esm(() => {
66517
66887
  ALL_CATEGORIES = Object.keys(BUILTIN_PATTERNS);
66518
66888
  });
66519
66889
 
66520
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.57+df32fe902834add3/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/sg-evaluator.js
66890
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.62+df32fe902834add3/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/sg-evaluator.js
66521
66891
  class SgEvaluator {
66522
66892
  executor;
66523
66893
  constructor(executor = nodeBunFactory3.createProcessExecutor()) {
@@ -66614,7 +66984,7 @@ var init_sg_evaluator = __esm(() => {
66614
66984
  init_file_utils();
66615
66985
  });
66616
66986
 
66617
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.57+df32fe902834add3/node_modules/@gobing-ai/ts-rule-engine/dist/resolvers/test-path-resolver.js
66987
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.62+df32fe902834add3/node_modules/@gobing-ai/ts-rule-engine/dist/resolvers/test-path-resolver.js
66618
66988
  class TypeScriptTestPathResolver {
66619
66989
  name = "typescript";
66620
66990
  constructor() {}
@@ -66697,7 +67067,7 @@ function testify(rel) {
66697
67067
  return `${dir}test_${base2}`;
66698
67068
  }
66699
67069
 
66700
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.57+df32fe902834add3/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/test-location-evaluator.js
67070
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.62+df32fe902834add3/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/test-location-evaluator.js
66701
67071
  class TestLocationEvaluator {
66702
67072
  resolvers;
66703
67073
  constructor(resolvers) {
@@ -66772,7 +67142,7 @@ var init_test_location_evaluator = __esm(() => {
66772
67142
  TYPESCRIPT_FALLBACK = new TypeScriptTestPathResolver;
66773
67143
  });
66774
67144
 
66775
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.57+df32fe902834add3/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/tsdoc-export-evaluator.js
67145
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.62+df32fe902834add3/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/tsdoc-export-evaluator.js
66776
67146
  class TsdocExportEvaluator {
66777
67147
  constructor() {}
66778
67148
  async evaluate(rule, context4) {
@@ -66857,7 +67227,7 @@ var init_tsdoc_export_evaluator = __esm(() => {
66857
67227
  };
66858
67228
  });
66859
67229
 
66860
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.57+df32fe902834add3/node_modules/@gobing-ai/ts-rule-engine/dist/formatters/json.js
67230
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.62+df32fe902834add3/node_modules/@gobing-ai/ts-rule-engine/dist/formatters/json.js
66861
67231
  class JsonFormatter {
66862
67232
  constructor() {}
66863
67233
  format(result) {
@@ -66865,7 +67235,7 @@ class JsonFormatter {
66865
67235
  }
66866
67236
  }
66867
67237
 
66868
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.57+df32fe902834add3/node_modules/@gobing-ai/ts-rule-engine/dist/formatters/text.js
67238
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.62+df32fe902834add3/node_modules/@gobing-ai/ts-rule-engine/dist/formatters/text.js
66869
67239
  class TextFormatter {
66870
67240
  constructor() {}
66871
67241
  format(result) {
@@ -66879,7 +67249,7 @@ class TextFormatter {
66879
67249
  }
66880
67250
  }
66881
67251
 
66882
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.57+df32fe902834add3/node_modules/@gobing-ai/ts-rule-engine/dist/host/builtins.js
67252
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.62+df32fe902834add3/node_modules/@gobing-ai/ts-rule-engine/dist/host/builtins.js
66883
67253
  function registerBuiltins(host, executor) {
66884
67254
  const path9 = new PathEvaluator;
66885
67255
  host.evaluators.register("regex", new RegexEvaluator, "builtin");
@@ -66919,7 +67289,7 @@ var init_builtins = __esm(() => {
66919
67289
  init_tsdoc_export_evaluator();
66920
67290
  });
66921
67291
 
66922
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.57+df32fe902834add3/node_modules/@gobing-ai/ts-rule-engine/dist/host/rule-engine-host.js
67292
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.62+df32fe902834add3/node_modules/@gobing-ai/ts-rule-engine/dist/host/rule-engine-host.js
66923
67293
  class RuleEngineHost {
66924
67294
  evaluators;
66925
67295
  formatters;
@@ -66936,7 +67306,7 @@ var init_rule_engine_host = __esm(() => {
66936
67306
  init_extension2();
66937
67307
  });
66938
67308
 
66939
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.57+df32fe902834add3/node_modules/@gobing-ai/ts-rule-engine/dist/engine.js
67309
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.62+df32fe902834add3/node_modules/@gobing-ai/ts-rule-engine/dist/engine.js
66940
67310
  class RuleEngine {
66941
67311
  host;
66942
67312
  events;
@@ -67122,7 +67492,7 @@ var init_engine = __esm(() => {
67122
67492
  init_types3();
67123
67493
  });
67124
67494
 
67125
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.57+df32fe902834add3/node_modules/@gobing-ai/ts-rule-engine/dist/host/bundled-rules.js
67495
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.62+df32fe902834add3/node_modules/@gobing-ai/ts-rule-engine/dist/host/bundled-rules.js
67126
67496
  async function bundledRulesRoot() {
67127
67497
  if (cachedRoot2 !== undefined)
67128
67498
  return cachedRoot2;
@@ -67173,7 +67543,7 @@ var init_bundled_rules = __esm(() => {
67173
67543
  init_dist5();
67174
67544
  });
67175
67545
 
67176
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.57+df32fe902834add3/node_modules/@gobing-ai/ts-rule-engine/dist/persistence/db-adapter.js
67546
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.62+df32fe902834add3/node_modules/@gobing-ai/ts-rule-engine/dist/persistence/db-adapter.js
67177
67547
  class DbRulePersistenceAdapter {
67178
67548
  db;
67179
67549
  constructor(db2) {
@@ -67207,7 +67577,7 @@ class DbRulePersistenceAdapter {
67207
67577
  }
67208
67578
  }
67209
67579
 
67210
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.57+df32fe902834add3/node_modules/@gobing-ai/ts-rule-engine/dist/persistence/schema.js
67580
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.62+df32fe902834add3/node_modules/@gobing-ai/ts-rule-engine/dist/persistence/schema.js
67211
67581
  var RULE_ENGINE_SCHEMA_SQL = `
67212
67582
  CREATE TABLE IF NOT EXISTS rule_runs (
67213
67583
  id TEXT PRIMARY KEY,
@@ -67253,10 +67623,10 @@ CREATE TABLE IF NOT EXISTS rule_eval_runs (
67253
67623
  CREATE INDEX IF NOT EXISTS idx_rule_eval_runs_run_id ON rule_eval_runs (run_id);
67254
67624
  `;
67255
67625
 
67256
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.57+df32fe902834add3/node_modules/@gobing-ai/ts-rule-engine/dist/persistence/index.js
67626
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.62+df32fe902834add3/node_modules/@gobing-ai/ts-rule-engine/dist/persistence/index.js
67257
67627
  var init_persistence2 = () => {};
67258
67628
 
67259
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.57+df32fe902834add3/node_modules/@gobing-ai/ts-rule-engine/dist/index.js
67629
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.62+df32fe902834add3/node_modules/@gobing-ai/ts-rule-engine/dist/index.js
67260
67630
  var init_dist10 = __esm(() => {
67261
67631
  init_extensions2();
67262
67632
  init_loader2();
@@ -67391,11 +67761,13 @@ async function applyCliMigrations(adapter, migrations = CLI_MIGRATIONS) {
67391
67761
  const runsStatusDoneSkip = migration.id === "0017_spur_cli_runs_status_completed_to_done" && (!await tableExists(adapter, "runs") || !await columnExists(adapter, "runs", "status"));
67392
67762
  const historyBoardToolStatsColumnsSkip = migration.id === "0031_spur_cli_history_board_tool_stats_columns" && (!await tableExists(adapter, "history_board_tool_stats") || await columnExists(adapter, "history_board_tool_stats", "fresh_input_tokens"));
67393
67763
  const queueJobsActiveIndexSkip = migration.id === "0027_spur_cli_history_refresh_active_unique" && !await tableExists(adapter, "queue_jobs");
67764
+ const queueJobsDeadlineLeaseSkip = migration.id === "0041_spur_cli_queue_jobs_deadline_lease_columns" && !await tableExists(adapter, "queue_jobs");
67394
67765
  const historyToolIdentitySkip = migration.id === "0034_spur_cli_history_tool_identity" && !await tableExists(adapter, "history_tool_call");
67395
67766
  const historyMeasureVectorSkip = migration.id === "0035_spur_cli_history_measure_vector" && (!await tableExists(adapter, "history_board_message_5m") || await columnExists(adapter, "history_board_message_5m", "cache_write_tokens"));
67396
67767
  const historyRollupWatermarkSkip = migration.id === "0036_spur_cli_history_rollup_watermark" && (await tableExists(adapter, "history_board_rollup_watermark") || !await tableExists(adapter, "history_message") || !await columnExists(adapter, "history_message", "imported_at"));
67397
67768
  const boundedRollupDerivationsSkip = migration.id === "0039_spur_cli_bounded_rollup_derivations" && (!await tableExists(adapter, "history_message") || !await columnExists(adapter, "history_message", "source_file") || !await tableExists(adapter, "history_board_source_daily"));
67398
- if (shouldApplySql && !sequenceIndexSkip && !argsRawSkip && !runsStatusDoneSkip && !nameOccurredIndexSkip && !historyBoardQueryIndexesSkip && !historyPerformanceIndexesSkip && !historyToolCallIndexesSkip && !historyBoardCoveringIndexesSkip && !historyBoardToolStatsColumnsSkip && !callIdSkip && !tsNullableSkip && !queueJobsActiveIndexSkip && !historyToolIdentitySkip && !historyMeasureVectorSkip && !historyRollupWatermarkSkip && !boundedRollupDerivationsSkip) {
67769
+ const historyModelFallbackIndexSkip = migration.id === "0042_spur_cli_history_model_fallback_index" && (!await tableExists(adapter, "history_message") || !await columnExists(adapter, "history_message", "model"));
67770
+ if (shouldApplySql && !sequenceIndexSkip && !argsRawSkip && !runsStatusDoneSkip && !nameOccurredIndexSkip && !historyBoardQueryIndexesSkip && !historyPerformanceIndexesSkip && !historyToolCallIndexesSkip && !historyBoardCoveringIndexesSkip && !historyBoardToolStatsColumnsSkip && !callIdSkip && !tsNullableSkip && !queueJobsActiveIndexSkip && !queueJobsDeadlineLeaseSkip && !historyToolIdentitySkip && !historyMeasureVectorSkip && !historyRollupWatermarkSkip && !boundedRollupDerivationsSkip && !historyModelFallbackIndexSkip) {
67399
67771
  for (const statement of splitSqlStatements(migration.sql)) {
67400
67772
  await adapter.exec(statement);
67401
67773
  }
@@ -67482,7 +67854,11 @@ CREATE TABLE IF NOT EXISTS queue_jobs (
67482
67854
  next_retry_at INTEGER,
67483
67855
  last_error TEXT,
67484
67856
  processing_at INTEGER,
67485
- expires_at INTEGER
67857
+ expires_at INTEGER,
67858
+ timeout_ms INTEGER,
67859
+ timeout_unlimited INTEGER NOT NULL DEFAULT 0,
67860
+ attempt_token TEXT,
67861
+ lease_expires_at INTEGER
67486
67862
  );
67487
67863
 
67488
67864
  CREATE INDEX IF NOT EXISTS queue_jobs_ready_idx ON queue_jobs (status, next_retry_at, created_at);
@@ -67599,6 +67975,10 @@ ALTER TABLE history_message ADD COLUMN request_id TEXT;
67599
67975
  CREATE INDEX IF NOT EXISTS idx_history_message_request_id
67600
67976
  ON history_message (request_id)
67601
67977
  WHERE request_id IS NOT NULL;
67978
+ `, HISTORY_MESSAGE_MODEL_FALLBACK_INDEX_SCHEMA_SQL = `
67979
+ CREATE INDEX IF NOT EXISTS idx_history_message_session_model
67980
+ ON history_message (source, session_id)
67981
+ WHERE model IS NOT NULL AND model != '' AND model != 'unknown';
67602
67982
  `, HISTORY_ETL_TABLES_DROP_SCHEMA_SQL = `
67603
67983
  DROP TABLE IF EXISTS history_etl_pi;
67604
67984
  DROP TABLE IF EXISTS history_etl_claude;
@@ -68101,6 +68481,11 @@ CREATE INDEX IF NOT EXISTS idx_history_message_source_file
68101
68481
 
68102
68482
  ALTER TABLE history_board_source_daily ADD COLUMN raw_messages INTEGER NOT NULL DEFAULT 0;
68103
68483
  ALTER TABLE history_board_source_daily ADD COLUMN last_imported_at TEXT;
68484
+ `, QUEUE_JOBS_DEADLINE_LEASE_COLUMNS_SCHEMA_SQL = `
68485
+ ALTER TABLE queue_jobs ADD COLUMN timeout_ms INTEGER;
68486
+ ALTER TABLE queue_jobs ADD COLUMN timeout_unlimited INTEGER NOT NULL DEFAULT 0;
68487
+ ALTER TABLE queue_jobs ADD COLUMN attempt_token TEXT;
68488
+ ALTER TABLE queue_jobs ADD COLUMN lease_expires_at INTEGER;
68104
68489
  `, CLI_MIGRATIONS, CLI_MIGRATION_FILE_MARKER = "_spur_cli_";
68105
68490
  var init_migrations = __esm(() => {
68106
68491
  init_dist8();
@@ -68269,11 +68654,20 @@ ${HISTORY_TASK_SESSION_SCHEMA_SQL}
68269
68654
  {
68270
68655
  id: "0040_spur_cli_agent_executor_updates",
68271
68656
  sql: AGENT_EXECUTOR_UPDATES_SCHEMA_SQL
68657
+ },
68658
+ {
68659
+ id: "0041_spur_cli_queue_jobs_deadline_lease_columns",
68660
+ sql: QUEUE_JOBS_DEADLINE_LEASE_COLUMNS_SCHEMA_SQL,
68661
+ addColumnIfMissing: { table: "queue_jobs", column: "timeout_ms" }
68662
+ },
68663
+ {
68664
+ id: "0042_spur_cli_history_model_fallback_index",
68665
+ sql: HISTORY_MESSAGE_MODEL_FALLBACK_INDEX_SCHEMA_SQL
68272
68666
  }
68273
68667
  ];
68274
68668
  });
68275
68669
 
68276
- // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.57+33b96fa7d4032676/node_modules/@gobing-ai/ts-infra/dist/job-queue/db-job-queue.js
68670
+ // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.62+c884df390da1a1e8/node_modules/@gobing-ai/ts-infra/dist/job-queue/db-job-queue.js
68277
68671
  function queueLogger() {
68278
68672
  if (!_queueLogger)
68279
68673
  _queueLogger = getLogger2("job-queue");
@@ -68329,6 +68723,9 @@ class DBQueueConsumer {
68329
68723
  baseDelay;
68330
68724
  maxDelay;
68331
68725
  drainTimeoutMs;
68726
+ drainPolicy;
68727
+ defaultTimeoutMs;
68728
+ activeAttempts = new Map;
68332
68729
  eventSink;
68333
68730
  timer = null;
68334
68731
  running = false;
@@ -68343,6 +68740,11 @@ class DBQueueConsumer {
68343
68740
  this.baseDelay = nonNegativeFiniteConfig("baseDelay", config4.baseDelay ?? 1000);
68344
68741
  this.maxDelay = nonNegativeFiniteConfig("maxDelay", config4.maxDelay ?? 60000);
68345
68742
  this.drainTimeoutMs = nonNegativeFiniteConfig("drainTimeoutMs", config4.drainTimeoutMs ?? 30000);
68743
+ this.drainPolicy = config4.drainPolicy ?? "bounded";
68744
+ if (config4.defaultTimeoutMs !== undefined && config4.defaultTimeoutMs !== null && (typeof config4.defaultTimeoutMs !== "number" || !Number.isInteger(config4.defaultTimeoutMs) || config4.defaultTimeoutMs <= 0)) {
68745
+ throw new RangeError(`Queue consumer defaultTimeoutMs must be a positive integer or null; received ${String(config4.defaultTimeoutMs)}`);
68746
+ }
68747
+ this.defaultTimeoutMs = config4.defaultTimeoutMs ?? null;
68346
68748
  this.eventSink = resolveEventSink(config4);
68347
68749
  }
68348
68750
  register(type, handler) {
@@ -68373,13 +68775,24 @@ class DBQueueConsumer {
68373
68775
  clearTimeout(this.timer);
68374
68776
  this.timer = null;
68375
68777
  }
68778
+ const drainToCompletion = this.drainPolicy === "drain-to-completion";
68376
68779
  const deadline = Date.now() + this.drainTimeoutMs;
68377
68780
  const pending = this.pollPromise;
68378
68781
  if (pending !== null) {
68379
- await settleWithin(pending, deadline);
68782
+ if (drainToCompletion) {
68783
+ await pending;
68784
+ } else {
68785
+ await settleWithin(pending, deadline);
68786
+ }
68380
68787
  }
68381
- while (this.inFlight > 0 && Date.now() < deadline) {
68382
- await sleep(10);
68788
+ if (drainToCompletion) {
68789
+ while (this.inFlight > 0) {
68790
+ await sleep(10);
68791
+ }
68792
+ } else {
68793
+ while (this.inFlight > 0 && Date.now() < deadline) {
68794
+ await sleep(10);
68795
+ }
68383
68796
  }
68384
68797
  if (wasRunning) {
68385
68798
  const drained = this.inFlight === 0;
@@ -68396,6 +68809,14 @@ class DBQueueConsumer {
68396
68809
  }
68397
68810
  }
68398
68811
  }
68812
+ async cancel(jobId) {
68813
+ const attempt = this.activeAttempts.get(jobId);
68814
+ if (attempt === undefined)
68815
+ return false;
68816
+ attempt.state.cancelled = true;
68817
+ attempt.controller.abort();
68818
+ return true;
68819
+ }
68399
68820
  async stats() {
68400
68821
  return this.dao.getStats();
68401
68822
  }
@@ -68403,7 +68824,7 @@ class DBQueueConsumer {
68403
68824
  return traceAsync("queue.poll", async () => {
68404
68825
  await this.dao.resetStuckJobs(this.visibilityTimeout);
68405
68826
  await this.dao.failExpiredJobs();
68406
- const jobs = await this.dao.claimReady(this.batchSize);
68827
+ const jobs = await this.dao.claimReady(this.batchSize, { leaseMs: this.visibilityTimeout });
68407
68828
  let processed = 0;
68408
68829
  for (let index2 = 0;index2 < jobs.length; index2 += this.maxConcurrency) {
68409
68830
  const batch = jobs.slice(index2, index2 + this.maxConcurrency);
@@ -68449,7 +68870,7 @@ class DBQueueConsumer {
68449
68870
  try {
68450
68871
  job = toJob(record2);
68451
68872
  } catch (error51) {
68452
- await this.failOrRetry(record2, error51, 0);
68873
+ await this.failOrRetry(record2, error51, 0, record2.attemptToken ?? undefined);
68453
68874
  return;
68454
68875
  }
68455
68876
  return traceAsync("queue.job.process", async () => {
@@ -68459,37 +68880,109 @@ class DBQueueConsumer {
68459
68880
  "queue.job_attempt": job.attempts
68460
68881
  });
68461
68882
  const handler = this.handlers.get(job.type);
68883
+ const token = record2.attemptToken ?? undefined;
68462
68884
  if (handler === undefined) {
68463
- await this.failOrRetry(job, new Error(`No handler registered for job type "${job.type}"`), 0);
68885
+ await this.failOrRetry(job, new Error(`No handler registered for job type "${job.type}"`), 0, token);
68464
68886
  return;
68465
68887
  }
68466
- const startMs = performance.now();
68888
+ let attemptWake;
68889
+ const attempt = { lost: false, cancelled: false, settled: false };
68890
+ const attemptAbort = new AbortController;
68891
+ if (token !== undefined) {
68892
+ const { promise: settledSignal, resolve: resolveSettled } = Promise.withResolvers();
68893
+ attempt.settledSignal = settledSignal;
68894
+ attemptWake = resolveSettled;
68895
+ this.activeAttempts.set(record2.id, { controller: attemptAbort, state: attempt });
68896
+ }
68897
+ const renewal = token === undefined ? undefined : this.renewUntilSettled(record2.id, token, attempt, attemptAbort).catch(() => {});
68467
68898
  try {
68468
- await handler(job);
68469
- await this.dao.markCompleted(job.id);
68470
- const durationMs = performance.now() - startMs;
68471
- getQueueJobCompletedTotal().add(1, { type: job.type });
68472
- getQueueJobProcessingDuration().record(durationMs, { type: job.type });
68473
- const completed = {
68474
- jobId: job.id,
68475
- type: job.type,
68476
- durationMs: Number.isFinite(durationMs) ? durationMs : 0,
68477
- attempt: job.attempts,
68478
- severity: "info"
68479
- };
68480
- await this.eventSink?.bus.emit("queue.job.completed", completed);
68481
- } catch (error51) {
68899
+ const resolvedTimeout = resolveExecutionTimeoutMs(`job "${job.type}"`, record2.timeoutUnlimited === 1 ? null : record2.timeoutMs ?? undefined, this.defaultTimeoutMs);
68900
+ const startMs = performance.now();
68901
+ const outcome = await runWithExecutionDeadline((context4) => handler(job, context4), {
68902
+ timeoutMs: resolvedTimeout,
68903
+ signal: attemptAbort.signal
68904
+ });
68905
+ attempt.settled = true;
68906
+ attemptWake?.();
68482
68907
  const durationMs = performance.now() - startMs;
68483
- getQueueJobProcessingDuration().record(durationMs, { type: job.type });
68484
- await this.failOrRetry(job, error51, Number.isFinite(durationMs) ? durationMs : 0);
68908
+ getQueueJobProcessingDuration().record(Number.isFinite(durationMs) ? durationMs : 0, {
68909
+ type: job.type
68910
+ });
68911
+ if (attempt.lost) {
68912
+ queueLogger().warn("queue attempt lost lease ownership; acknowledgement fenced", {
68913
+ jobId: record2.id,
68914
+ type: job.type,
68915
+ outcome: outcome.outcome
68916
+ });
68917
+ return;
68918
+ }
68919
+ if (outcome.outcome === "error") {
68920
+ await this.failOrRetry(job, outcome.error, Number.isFinite(durationMs) ? durationMs : 0, token);
68921
+ } else if (outcome.timedOut) {
68922
+ await this.failOrRetry(job, outcome.error, Number.isFinite(durationMs) ? durationMs : 0, token);
68923
+ } else if (attempt.cancelled) {
68924
+ const message = "job cancelled";
68925
+ const applied = await this.dao.markFailed(record2.id, job.attempts + 1, message, token);
68926
+ if (applied) {
68927
+ getQueueJobFailedTotal().add(1, { type: job.type });
68928
+ await this.eventSink?.bus.emit("queue.job.failed", {
68929
+ jobId: job.id,
68930
+ type: job.type,
68931
+ error: message,
68932
+ attempt: job.attempts + 1,
68933
+ maxRetries: job.maxRetries,
68934
+ durationMs: Number.isFinite(durationMs) ? durationMs : 0,
68935
+ severity: "warning"
68936
+ });
68937
+ }
68938
+ } else {
68939
+ await this.dao.markCompleted(record2.id, token);
68940
+ getQueueJobCompletedTotal().add(1, { type: job.type });
68941
+ const completed = {
68942
+ jobId: job.id,
68943
+ type: job.type,
68944
+ durationMs: Number.isFinite(durationMs) ? durationMs : 0,
68945
+ attempt: job.attempts,
68946
+ severity: "info"
68947
+ };
68948
+ await this.eventSink?.bus.emit("queue.job.completed", completed);
68949
+ }
68950
+ } finally {
68951
+ attempt.settled = true;
68952
+ attemptWake?.();
68953
+ if (renewal !== undefined)
68954
+ await renewal;
68955
+ if (token !== undefined)
68956
+ this.activeAttempts.delete(record2.id);
68485
68957
  }
68486
68958
  });
68487
68959
  }
68488
- async failOrRetry(job, error51, durationMs) {
68960
+ async renewUntilSettled(id, token, attempt, attemptAbort) {
68961
+ const intervalMs = Math.max(1, Math.floor(this.visibilityTimeout / 3));
68962
+ while (!attempt.settled) {
68963
+ await Promise.race([sleep(intervalMs), attempt.settledSignal]);
68964
+ if (attempt.settled)
68965
+ return;
68966
+ let renewed = false;
68967
+ try {
68968
+ renewed = await this.dao.renewLease(id, token, this.visibilityTimeout);
68969
+ } catch {
68970
+ renewed = false;
68971
+ }
68972
+ if (attempt.settled)
68973
+ return;
68974
+ if (!renewed) {
68975
+ attempt.lost = true;
68976
+ attemptAbort.abort();
68977
+ return;
68978
+ }
68979
+ }
68980
+ }
68981
+ async failOrRetry(job, error51, durationMs, attemptToken) {
68489
68982
  const attempts = job.attempts + 1;
68490
68983
  const message = error51 instanceof Error ? error51.message : String(error51);
68491
68984
  if (attempts >= job.maxRetries) {
68492
- await this.dao.markFailed(job.id, attempts, message);
68985
+ await this.dao.markFailed(job.id, attempts, message, attemptToken);
68493
68986
  getQueueJobFailedTotal().add(1, { type: job.type });
68494
68987
  const failed = {
68495
68988
  jobId: job.id,
@@ -68505,7 +68998,7 @@ class DBQueueConsumer {
68505
68998
  }
68506
68999
  const delay = Math.min(this.maxDelay, this.baseDelay * 2 ** Math.max(0, attempts - 1));
68507
69000
  const nextRetryAt = Date.now() + delay;
68508
- await this.dao.markForRetry(job.id, attempts, message, nextRetryAt);
69001
+ await this.dao.markForRetry(job.id, attempts, message, nextRetryAt, attemptToken);
68509
69002
  const retrying = {
68510
69003
  jobId: job.id,
68511
69004
  type: job.type,
@@ -68530,7 +69023,8 @@ function toJob(record2) {
68530
69023
  updatedAt: record2.updatedAt,
68531
69024
  nextRetryAt: record2.nextRetryAt,
68532
69025
  lastError: record2.lastError,
68533
- processingAt: record2.processingAt
69026
+ processingAt: record2.processingAt,
69027
+ timeoutMs: record2.timeoutUnlimited === 1 ? null : record2.timeoutMs ?? undefined
68534
69028
  };
68535
69029
  }
68536
69030
  function sleep(ms) {
@@ -68576,7 +69070,7 @@ var init_db_job_queue = __esm(() => {
68576
69070
  init_tracing2();
68577
69071
  });
68578
69072
 
68579
- // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.57+33b96fa7d4032676/node_modules/@gobing-ai/ts-infra/dist/job-queue-db.js
69073
+ // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.62+c884df390da1a1e8/node_modules/@gobing-ai/ts-infra/dist/job-queue-db.js
68580
69074
  var exports_job_queue_db = {};
68581
69075
  __export(exports_job_queue_db, {
68582
69076
  DBQueueConsumer: () => DBQueueConsumer,
@@ -68893,8 +69387,9 @@ async function failStaleSchedulerCustomJob(db2, id, now, reason) {
68893
69387
  const changed = await db2.queryFirst("SELECT changes() AS n");
68894
69388
  return (changed?.n ?? 0) > 0;
68895
69389
  }
68896
- async function failOrphanedProcessingJobs(db2, now) {
68897
- const orphan = await db2.queryFirst(`SELECT count(*) AS cnt FROM queue_jobs WHERE status = 'processing'`);
69390
+ async function failOrphanedProcessingJobs(db2, now, exemptJobNames = []) {
69391
+ const exemptFilter = exemptJobNames.length === 0 ? "" : ` AND NOT (type = 'scheduler.custom' AND json_extract(payload, '$.name') IN (${exemptJobNames.map(() => "?").join(", ")}))`;
69392
+ const orphan = await db2.queryFirst(`SELECT count(*) AS cnt FROM queue_jobs WHERE status = 'processing'${exemptFilter}`, ...exemptJobNames);
68898
69393
  const count3 = orphan?.cnt ?? 0;
68899
69394
  if (count3 > 0) {
68900
69395
  await db2.run(`UPDATE queue_jobs
@@ -68902,7 +69397,7 @@ async function failOrphanedProcessingJobs(db2, now) {
68902
69397
  last_error = 'Process terminated: server restarted while job was in flight',
68903
69398
  processing_at = NULL,
68904
69399
  updated_at = ?
68905
- WHERE status = 'processing'`, now);
69400
+ WHERE status = 'processing'${exemptFilter}`, now, ...exemptJobNames);
68906
69401
  }
68907
69402
  return count3;
68908
69403
  }
@@ -71215,6 +71710,7 @@ __export(exports_src, {
71215
71710
  resolvePricing: () => resolvePricing,
71216
71711
  resetHistoryTables: () => resetHistoryTables,
71217
71712
  replaceHistoryBoardRollups: () => replaceHistoryBoardRollups,
71713
+ repairImporterSchemaVersion: () => repairImporterSchemaVersion,
71218
71714
  renderTaskTemplate: () => renderTaskTemplate,
71219
71715
  renderScenarioStub: () => renderScenarioStub,
71220
71716
  renderReport: () => renderReport,
@@ -71260,6 +71756,7 @@ __export(exports_src, {
71260
71756
  loadSqlMigrations: () => loadSqlMigrations,
71261
71757
  loadAttributionEvidence: () => loadAttributionEvidence,
71262
71758
  listAttributionSessions: () => listAttributionSessions,
71759
+ latestHistoryImportedAt: () => latestHistoryImportedAt,
71263
71760
  isTierEligible: () => isTierEligible,
71264
71761
  isStale: () => isStale,
71265
71762
  isCompatibleStageVersion: () => isCompatibleStageVersion,
@@ -71326,6 +71823,7 @@ __export(exports_src, {
71326
71823
  deterministicExecutionSchema: () => deterministicExecutionSchema,
71327
71824
  derivedWarnings: () => derivedWarnings,
71328
71825
  deriveVerifiedOutcomeStat: () => deriveVerifiedOutcomeStat,
71826
+ deriveMissingAssistantDurations: () => deriveMissingAssistantDurations,
71329
71827
  deriveDimensionMartsOps: () => deriveDimensionMartsOps,
71330
71828
  deriveDimensionMarts: () => deriveDimensionMarts,
71331
71829
  dbHealthCheck: () => dbHealthCheck,
@@ -72459,19 +72957,36 @@ function buildWorkflowSteps(def) {
72459
72957
  };
72460
72958
  });
72461
72959
  }
72960
+ function columnLabel(index2) {
72961
+ let n2 = Math.max(0, Math.floor(index2));
72962
+ let label = "";
72963
+ do {
72964
+ label = String.fromCharCode(65 + n2 % 26) + label;
72965
+ n2 = Math.floor(n2 / 26) - 1;
72966
+ } while (n2 >= 0);
72967
+ return label;
72968
+ }
72969
+ function buildStepLabels(count3) {
72970
+ const labels = [];
72971
+ for (let i2 = 0;i2 < Math.max(0, Math.floor(count3)); i2++)
72972
+ labels.push(columnLabel(i2));
72973
+ return labels;
72974
+ }
72462
72975
  function renderWorkflowTodo(def) {
72463
72976
  const kind = def.kind ?? "state-machine";
72464
72977
  const lines = [`# ${def.name} (${kind}) \u2014 declared steps`, ""];
72465
72978
  if (kind === "state-machine") {
72466
72979
  lines.push("Declared step inventory in declaration order, not a predicted execution path.", "");
72467
72980
  }
72468
- for (const step of buildWorkflowSteps(def)) {
72469
- lines.push(formatWorkflowStepLine(step));
72470
- }
72981
+ const steps = buildWorkflowSteps(def);
72982
+ const labels = buildStepLabels(steps.length);
72983
+ steps.forEach((step, i2) => {
72984
+ lines.push(formatWorkflowStepLine(step, labels[i2]));
72985
+ });
72471
72986
  return lines.join(`
72472
72987
  `);
72473
72988
  }
72474
- function formatWorkflowStepLine(step) {
72989
+ function formatWorkflowStepLine(step, label) {
72475
72990
  const markers = [
72476
72991
  step.initial ? "initial" : undefined,
72477
72992
  step.terminal ? "terminal" : undefined,
@@ -72481,13 +72996,16 @@ function formatWorkflowStepLine(step) {
72481
72996
  step.conditional ? "conditional" : undefined,
72482
72997
  step.nodeType !== undefined && step.nodeType !== "action" ? step.nodeType : undefined
72483
72998
  ].filter((m) => m !== undefined);
72484
- return markers.length > 0 ? `- [ ] ${step.id} \u2014 ${markers.join(" \xB7 ")}` : `- [ ] ${step.id}`;
72999
+ const head = label === undefined ? step.id : `${label}. ${step.id}`;
73000
+ return markers.length > 0 ? `- [ ] ${head} \u2014 ${markers.join(" \xB7 ")}` : `- [ ] ${head}`;
72485
73001
  }
72486
73002
  function renderRunPlan(def) {
72487
73003
  const kind = def.kind ?? "state-machine";
73004
+ const steps = buildWorkflowSteps(def);
73005
+ const labels = buildStepLabels(steps.length);
72488
73006
  return [
72489
73007
  `plan (${kind}) \u2014 declared inventory, not a predicted route:`,
72490
- ...buildWorkflowSteps(def).map(formatWorkflowStepLine)
73008
+ ...steps.map((step, i2) => formatWorkflowStepLine(step, labels[i2]))
72491
73009
  ].join(`
72492
73010
  `);
72493
73011
  }
@@ -72522,6 +73040,7 @@ function formatUsageSummary(usage) {
72522
73040
  return parts.length > 0 ? ` \xB7 usage ${parts.join(", ")}` : " \xB7 usage measured";
72523
73041
  }
72524
73042
  var isAgentExecution = (e) => ("executionId" in e), isActionOutput = (e) => ("stream" in e) && ("chunk" in e) && !("executionId" in e), isActionStarted = (e) => ("kind" in e) && ("node" in e), isActionFinished = (e) => ("durationMs" in e) && ("ok" in e), isPhase = (e) => ("phase" in e), isTransition = (e) => ("from" in e) && ("to" in e);
73043
+ var init_step_reporter = () => {};
72525
73044
 
72526
73045
  // ../../packages/app/src/observability/workflow-run-log-sink.ts
72527
73046
  import { closeSync as closeSync2, mkdirSync as mkdirSync5, openSync as openSync2, writeSync as writeSync2 } from "fs";
@@ -72702,6 +73221,7 @@ var DEFAULT_RUN_LOG_MAX_BYTES, MAX_STEERING_NOTE_CHARS = 1024, TRUNCATION_MARKER
72702
73221
  `, RUN_LOG_EVENT_NAMES;
72703
73222
  var init_workflow_run_log_sink = __esm(() => {
72704
73223
  init_observability();
73224
+ init_step_reporter();
72705
73225
  DEFAULT_RUN_LOG_MAX_BYTES = 1024 * 1024;
72706
73226
  RUN_LOG_EVENT_NAMES = [
72707
73227
  "workflow.run.started",
@@ -75197,66 +75717,40 @@ var init_anchor_qualifier = __esm(() => {
75197
75717
  });
75198
75718
 
75199
75719
  // ../../packages/app/src/services/bounded-child-run.ts
75200
- function resolveKillGraceMs(env) {
75720
+ function resolveKillGraceMs3(env) {
75201
75721
  const raw = env.SPUR_SCHEDULER_KILL_GRACE_MS;
75202
75722
  if (raw === undefined || raw.trim() === "")
75203
75723
  return CHILD_KILL_GRACE_MS;
75204
75724
  const parsed = Number(raw);
75205
75725
  return Number.isInteger(parsed) && parsed > 0 ? parsed : CHILD_KILL_GRACE_MS;
75206
75726
  }
75207
- function escalateGroupKill(pid) {
75208
- if (pid === undefined || process.platform === "win32")
75209
- return;
75210
- try {
75211
- process.kill(-pid, "SIGKILL");
75212
- } catch {
75213
- try {
75214
- process.kill(pid, "SIGKILL");
75215
- } catch {}
75216
- }
75217
- }
75218
75727
  async function runBoundedChild(executor, options) {
75219
- const killGraceMs = options.killGraceMs ?? CHILD_KILL_GRACE_MS;
75220
- const controller = new AbortController;
75221
- let pid;
75222
- let timedOut = false;
75223
- const deadline = setTimeout(() => {
75224
- timedOut = true;
75225
- controller.abort();
75226
- }, options.timeoutMs);
75227
- let escalation;
75228
- controller.signal.addEventListener("abort", () => {
75229
- escalation = setTimeout(() => escalateGroupKill(pid), killGraceMs);
75230
- }, { once: true });
75231
- try {
75232
- const result = await executor.run({
75233
- command: options.command,
75234
- ...options.args !== undefined ? { args: options.args } : {},
75235
- ...options.cwd !== undefined ? { cwd: options.cwd } : {},
75236
- ...options.env !== undefined ? { env: options.env } : {},
75237
- timeout: options.timeoutMs,
75238
- ...options.maxOutput !== undefined ? { maxOutput: options.maxOutput } : {},
75239
- forceBuffered: true,
75240
- signal: controller.signal,
75241
- onSpawn: (spawned) => {
75242
- pid = spawned;
75243
- }
75244
- });
75245
- const elapsedMs = result.durationMs;
75246
- let terminationReason;
75247
- if (timedOut) {
75248
- terminationReason = "timeout";
75249
- } else if (result.exitCode === null) {
75250
- terminationReason = "signal";
75251
- } else {
75252
- terminationReason = "exit";
75253
- }
75254
- return { result, timedOut, deadlineMs: options.timeoutMs, elapsedMs, terminationReason };
75255
- } finally {
75256
- clearTimeout(deadline);
75257
- if (escalation !== undefined)
75258
- clearTimeout(escalation);
75728
+ const result = await executor.run({
75729
+ command: options.command,
75730
+ ...options.args !== undefined ? { args: options.args } : {},
75731
+ ...options.cwd !== undefined ? { cwd: options.cwd } : {},
75732
+ ...options.env !== undefined ? { env: options.env } : {},
75733
+ timeout: options.timeoutMs,
75734
+ ...options.killGraceMs !== undefined ? { killGraceMs: options.killGraceMs } : {},
75735
+ ...options.maxOutput !== undefined ? { maxOutput: options.maxOutput } : {},
75736
+ forceBuffered: true
75737
+ });
75738
+ const elapsedMs = result.durationMs;
75739
+ let terminationReason;
75740
+ if (result.outcome === "timeout") {
75741
+ terminationReason = "timeout";
75742
+ } else if (result.exitCode === null) {
75743
+ terminationReason = "signal";
75744
+ } else {
75745
+ terminationReason = "exit";
75259
75746
  }
75747
+ return {
75748
+ result,
75749
+ timedOut: terminationReason === "timeout",
75750
+ deadlineMs: options.timeoutMs,
75751
+ elapsedMs,
75752
+ terminationReason
75753
+ };
75260
75754
  }
75261
75755
  function describeBoundedFailure(parts) {
75262
75756
  const { subject, outcome, outputTail } = parts;
@@ -75265,6 +75759,23 @@ function describeBoundedFailure(parts) {
75265
75759
  return base2;
75266
75760
  return `${base2}; shell chain did not complete, so later configured stages did not run ` + `and any exit_code in the tail is a subcommand result, not the chain verdict: ${outputTail}`;
75267
75761
  }
75762
+ function terminateJobChildren(registry2, signal) {
75763
+ const signaled = [];
75764
+ for (const execution of registry2.listExecutions({ running: true })) {
75765
+ if (execution.pid === undefined)
75766
+ continue;
75767
+ try {
75768
+ process.kill(-execution.pid, 0);
75769
+ process.kill(-execution.pid, signal);
75770
+ } catch {
75771
+ try {
75772
+ process.kill(execution.pid, signal);
75773
+ } catch {}
75774
+ }
75775
+ signaled.push(execution.pid);
75776
+ }
75777
+ return signaled;
75778
+ }
75268
75779
  var CHILD_KILL_GRACE_MS = 5000;
75269
75780
 
75270
75781
  // ../../packages/app/src/services/verify-verdict.ts
@@ -80981,6 +81492,41 @@ var init_event_names = __esm(() => {
80981
81492
  }));
80982
81493
  });
80983
81494
 
81495
+ // ../../packages/app/src/services/execution-policy.ts
81496
+ function parseTimeoutInput(raw) {
81497
+ const normalized = raw.trim();
81498
+ if (normalized.toLowerCase() === "none")
81499
+ return null;
81500
+ if (!/^[0-9]+$/.test(normalized)) {
81501
+ throw new Error(`invalid timeout "${raw}": expected a positive integer number of milliseconds or 'none'`);
81502
+ }
81503
+ const value = Number(normalized);
81504
+ if (value === 0 || value > MAX_TIMEOUT_INPUT_MS) {
81505
+ throw new Error(`invalid timeout "${raw}": expected a positive integer number of milliseconds or 'none'`);
81506
+ }
81507
+ return value;
81508
+ }
81509
+ function normalizeLegacyTimeoutMs(raw, fallbackMs) {
81510
+ if (raw === undefined || raw.trim() === "")
81511
+ return fallbackMs;
81512
+ if (raw.trim().toLowerCase() === "none")
81513
+ return null;
81514
+ const parsed = Number(raw);
81515
+ return Number.isInteger(parsed) && parsed > 0 ? parsed : fallbackMs;
81516
+ }
81517
+ function assertCanonicalTimeoutMs(value) {
81518
+ if (value === undefined)
81519
+ return;
81520
+ if (value === null)
81521
+ return null;
81522
+ if (typeof value === "number" && Number.isInteger(value) && value > 0 && value <= MAX_TIMEOUT_INPUT_MS) {
81523
+ return value;
81524
+ }
81525
+ throw new Error(`invalid canonical timeout ${JSON.stringify(value)}: expected undefined, null, or a positive integer \u2264 ${MAX_TIMEOUT_INPUT_MS}`);
81526
+ }
81527
+ var MAX_TIMEOUT_INPUT_MS = 2147483647;
81528
+ var init_execution_policy = () => {};
81529
+
80984
81530
  // ../../packages/app/src/services/feature-service.ts
80985
81531
  function isFeatureActionName(action) {
80986
81532
  return FEATURE_ACTION_NAMES.includes(action);
@@ -84250,8 +84796,7 @@ class LiveHistoryBoardService {
84250
84796
  modelComparison: compItems
84251
84797
  };
84252
84798
  }
84253
- async getSources() {
84254
- const db2 = await this.resolveDb();
84799
+ async buildSources(db2) {
84255
84800
  if (!db2) {
84256
84801
  return {
84257
84802
  overview: {
@@ -84334,6 +84879,17 @@ class LiveHistoryBoardService {
84334
84879
  }));
84335
84880
  return projectSources(Array.from(sources.values()), daily, databaseBytes2);
84336
84881
  }
84882
+ async getSources() {
84883
+ const db2 = await this.resolveDb();
84884
+ const data = await this.buildSources(db2);
84885
+ if (!db2)
84886
+ return data;
84887
+ const lastImportedAt = await latestHistoryImportedAt(db2);
84888
+ return {
84889
+ ...data,
84890
+ overview: { ...data.overview, lastImportedAt: lastImportedAt ?? data.overview.lastImportedAt }
84891
+ };
84892
+ }
84337
84893
  async triggerImport(mode) {
84338
84894
  if (!this.deps.triggerImport) {
84339
84895
  throw new Error("History import queue is not configured");
@@ -84413,22 +84969,14 @@ var init_job_exclusion_guard = __esm(() => {
84413
84969
 
84414
84970
  // ../../packages/app/src/services/scheduler-custom-job-service.ts
84415
84971
  function resolveSchedulerCustomTimeoutMs(env) {
84416
- const raw = env.SPUR_SCHEDULER_CUSTOM_TIMEOUT_MS;
84417
- if (raw === undefined || raw.trim() === "")
84418
- return SCHEDULER_CUSTOM_TIMEOUT_MS;
84419
- const parsed = Number(raw);
84420
- return Number.isInteger(parsed) && parsed > 0 ? parsed : SCHEDULER_CUSTOM_TIMEOUT_MS;
84972
+ return normalizeLegacyTimeoutMs(env.SPUR_SCHEDULER_CUSTOM_TIMEOUT_MS, SCHEDULER_CUSTOM_TIMEOUT_MS);
84421
84973
  }
84422
84974
  function schedulerJobTimeoutEnvName(name) {
84423
84975
  const snake = name.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/([^A-Za-z0-9]+)/g, "_").toUpperCase().replace(/^_+|_+$/g, "");
84424
84976
  return `SPUR_SCHEDULER_TIMEOUT_${snake}_MS`;
84425
84977
  }
84426
84978
  function resolveSchedulerJobTimeoutMs(name, env, fallbackMs) {
84427
- const raw = env[schedulerJobTimeoutEnvName(name)];
84428
- if (raw === undefined || raw.trim() === "")
84429
- return fallbackMs;
84430
- const parsed = Number(raw);
84431
- return Number.isInteger(parsed) && parsed > 0 ? parsed : fallbackMs;
84979
+ return normalizeLegacyTimeoutMs(env[schedulerJobTimeoutEnvName(name)], fallbackMs);
84432
84980
  }
84433
84981
  function outputTail(text4) {
84434
84982
  const trimmed = text4.trim();
@@ -84466,7 +85014,8 @@ async function handleSchedulerCustomJob(deps, job) {
84466
85014
  acquireExclusiveJob(exclusiveKey, `scheduler job "${payload.name}"`);
84467
85015
  }
84468
85016
  activeJobs.add(payload.name);
84469
- const timeoutMs = deps.resolveTimeoutMs?.(payload.name) ?? deps.timeoutMs ?? SCHEDULER_CUSTOM_TIMEOUT_MS;
85017
+ const resolvedPerJob = deps.resolveTimeoutMs?.(payload.name);
85018
+ const timeoutMs = resolvedPerJob !== undefined ? resolvedPerJob : deps.timeoutMs !== undefined ? deps.timeoutMs : SCHEDULER_CUSTOM_TIMEOUT_MS;
84470
85019
  try {
84471
85020
  const outcome = await runBoundedChild(deps.executor, {
84472
85021
  command: "/bin/sh",
@@ -84496,6 +85045,7 @@ async function handleSchedulerCustomJob(deps, job) {
84496
85045
  }
84497
85046
  var SCHEDULER_CUSTOM_JOB = "scheduler.custom", SCHEDULER_CUSTOM_TIMEOUT_MS = 600000, SCHEDULER_CUSTOM_MAX_OUTPUT = 1e6, activeJobs;
84498
85047
  var init_scheduler_custom_job_service = __esm(() => {
85048
+ init_execution_policy();
84499
85049
  init_job_exclusion_guard();
84500
85050
  activeJobs = new Set;
84501
85051
  });
@@ -84503,13 +85053,15 @@ var init_scheduler_custom_job_service = __esm(() => {
84503
85053
  // ../../packages/app/src/services/history-refresh-service.ts
84504
85054
  function parsePayload(raw) {
84505
85055
  const candidate = typeof raw === "string" ? safeJsonParse(raw) : raw;
85056
+ const candidateTimeoutMs = candidate?.timeoutMs;
84506
85057
  const trigger = candidate?.trigger === "task-done" || candidate?.trigger === "pipeline-run" || candidate?.trigger === "manual" || candidate?.trigger === "schedule" ? candidate.trigger : "task-done";
84507
85058
  return {
84508
85059
  trigger,
84509
85060
  triggerId: typeof candidate?.triggerId === "string" ? candidate.triggerId : null,
84510
85061
  windowStart: typeof candidate?.windowStart === "number" ? candidate.windowStart : 0,
84511
85062
  windowEnd: typeof candidate?.windowEnd === "number" ? candidate.windowEnd : 0,
84512
- ...candidate?.importMode === "full" || candidate?.importMode === "incremental" ? { importMode: candidate.importMode } : {}
85063
+ ...candidate?.importMode === "full" || candidate?.importMode === "incremental" ? { importMode: candidate.importMode } : {},
85064
+ ...candidateTimeoutMs === null || typeof candidateTimeoutMs === "number" && Number.isInteger(candidateTimeoutMs) && candidateTimeoutMs > 0 ? { timeoutMs: candidateTimeoutMs } : {}
84513
85065
  };
84514
85066
  }
84515
85067
  function safeJsonParse(raw) {
@@ -84546,12 +85098,14 @@ function validateHistoryRefreshPayload(raw) {
84546
85098
  if (importMode !== undefined && importMode !== "full" && importMode !== "incremental") {
84547
85099
  throw new Error(`history refresh payload has invalid importMode: ${JSON.stringify(importMode)}`);
84548
85100
  }
85101
+ const timeoutMs = assertCanonicalTimeoutMs(candidate.timeoutMs);
84549
85102
  return {
84550
85103
  trigger,
84551
85104
  triggerId,
84552
85105
  windowStart,
84553
85106
  windowEnd,
84554
- ...importMode !== undefined ? { importMode } : {}
85107
+ ...importMode !== undefined ? { importMode } : {},
85108
+ ...timeoutMs !== undefined ? { timeoutMs } : {}
84555
85109
  };
84556
85110
  }
84557
85111
  function parseHistoryRefreshContext(raw) {
@@ -84567,6 +85121,7 @@ function parseHistoryRefreshContext(raw) {
84567
85121
  }
84568
85122
  async function enqueueHistoryRefresh(db2, options) {
84569
85123
  const triggerConfig = resolveHistoryRefreshTrigger(options.config);
85124
+ const explicitTimeoutMs = assertCanonicalTimeoutMs(options.timeoutMs);
84570
85125
  let enabled = triggerConfig.onCompletion;
84571
85126
  if (options.trigger === "manual")
84572
85127
  enabled = true;
@@ -84579,7 +85134,8 @@ async function enqueueHistoryRefresh(db2, options) {
84579
85134
  triggerId: options.triggerId ?? null,
84580
85135
  windowStart: now2,
84581
85136
  windowEnd: now2,
84582
- ...options.importMode !== undefined ? { importMode: options.importMode } : {}
85137
+ ...options.importMode !== undefined ? { importMode: options.importMode } : {},
85138
+ ...explicitTimeoutMs !== undefined ? { timeoutMs: explicitTimeoutMs } : {}
84583
85139
  };
84584
85140
  const result = await enqueueCoalesced(db2, {
84585
85141
  type: HISTORY_REFRESH_JOB,
@@ -84596,25 +85152,32 @@ async function enqueueHistoryRefresh(db2, options) {
84596
85152
  triggerId: prev.triggerId,
84597
85153
  windowStart: Math.min(prev.windowStart, curr.windowStart),
84598
85154
  windowEnd: Math.max(prev.windowEnd, curr.windowEnd),
84599
- ...importMode !== undefined ? { importMode } : {}
85155
+ ...importMode !== undefined ? { importMode } : {},
85156
+ ...prev.timeoutMs !== undefined ? { timeoutMs: prev.timeoutMs } : {},
85157
+ ...prev.timeoutMs === undefined && curr.timeoutMs !== undefined ? { timeoutMs: curr.timeoutMs } : {}
84600
85158
  };
84601
85159
  }
84602
85160
  });
84603
85161
  return { status: result.status, jobId: result.jobId, payload: parsePayload(result.payload) };
84604
85162
  }
84605
- function resolveHistoryRefreshTimeoutMs(env) {
84606
- const raw = env.SPUR_HISTORY_REFRESH_TIMEOUT_MS;
84607
- if (raw === undefined || raw.trim() === "")
84608
- return SCHEDULER_CUSTOM_TIMEOUT_MS;
84609
- const parsed = Number(raw);
84610
- return Number.isInteger(parsed) && parsed > 0 ? parsed : SCHEDULER_CUSTOM_TIMEOUT_MS;
85163
+ function resolveHistoryRefreshTimeoutMs(env, canonical) {
85164
+ if (canonical !== undefined)
85165
+ return assertCanonicalTimeoutMs(canonical);
85166
+ return normalizeLegacyTimeoutMs(env.SPUR_HISTORY_REFRESH_TIMEOUT_MS, SCHEDULER_CUSTOM_TIMEOUT_MS);
84611
85167
  }
84612
85168
  async function handleHistoryRefreshJob(deps, job) {
84613
85169
  const payload = validateHistoryRefreshPayload(job.payload);
84614
85170
  const split = splitLaunchCommand(deps.invocation, 'history refresh "invocation"');
84615
85171
  if ("error" in split)
84616
85172
  throw new Error(split.error);
84617
- const timeoutMs = deps.timeoutMs ?? SCHEDULER_CUSTOM_TIMEOUT_MS;
85173
+ const resolvedCanonical = payload.timeoutMs;
85174
+ const timeoutMs = resolvedCanonical !== undefined ? resolvedCanonical : deps.timeoutMs !== undefined ? deps.timeoutMs : SCHEDULER_CUSTOM_TIMEOUT_MS;
85175
+ let propagatedSourceTimeout;
85176
+ if (timeoutMs === null) {
85177
+ propagatedSourceTimeout = "none";
85178
+ } else if (timeoutMs !== SCHEDULER_CUSTOM_TIMEOUT_MS) {
85179
+ propagatedSourceTimeout = String(timeoutMs);
85180
+ }
84618
85181
  acquireExclusiveJob(HISTORY_PRODUCER_EXCLUSIVE_KEY, "history.refresh");
84619
85182
  let outcome;
84620
85183
  try {
@@ -84623,7 +85186,11 @@ async function handleHistoryRefreshJob(deps, job) {
84623
85186
  args: [...split.leadingArgs, "--no-logo", "history", "daily"],
84624
85187
  cwd: deps.cwd,
84625
85188
  timeoutMs,
84626
- ...deps.databaseUrl !== undefined ? { env: { [HISTORY_REFRESH_CONTEXT_ENV]: JSON.stringify(payload), DATABASE_URL: deps.databaseUrl } } : { env: { [HISTORY_REFRESH_CONTEXT_ENV]: JSON.stringify(payload) } },
85189
+ env: {
85190
+ [HISTORY_REFRESH_CONTEXT_ENV]: JSON.stringify(payload),
85191
+ ...propagatedSourceTimeout !== undefined ? { [HISTORY_SOURCE_TIMEOUT_ENV]: propagatedSourceTimeout } : {},
85192
+ ...deps.databaseUrl !== undefined ? { DATABASE_URL: deps.databaseUrl } : {}
85193
+ },
84627
85194
  maxOutput: HISTORY_REFRESH_MAX_OUTPUT
84628
85195
  });
84629
85196
  } finally {
@@ -84647,11 +85214,12 @@ async function handleHistoryRefreshJob(deps, job) {
84647
85214
  throw new Error(`history daily exited ${result.exitCode}${stdoutDetail || stderrDetail}`);
84648
85215
  }
84649
85216
  }
84650
- var HISTORY_REFRESH_JOB = "history.refresh", HISTORY_REFRESH_CONTEXT_ENV = "SPUR_HISTORY_REFRESH_CONTEXT", HISTORY_REFRESH_MAX_OUTPUT = 1e6;
85217
+ var HISTORY_SOURCE_TIMEOUT_ENV = "SPUR_HISTORY_SOURCE_TIMEOUT_MS", HISTORY_REFRESH_JOB = "history.refresh", HISTORY_REFRESH_CONTEXT_ENV = "SPUR_HISTORY_REFRESH_CONTEXT", HISTORY_REFRESH_MAX_OUTPUT = 1e6;
84651
85218
  var init_history_refresh_service = __esm(() => {
84652
85219
  init_src();
84653
85220
  init_src2();
84654
85221
  init_split_launch_command();
85222
+ init_execution_policy();
84655
85223
  init_job_exclusion_guard();
84656
85224
  init_scheduler_custom_job_service();
84657
85225
  });
@@ -84918,6 +85486,20 @@ function assertPiImporterSafe(input) {
84918
85486
  }
84919
85487
  throw new UnsafeHistoryImporterError(`refusing full pi history import \u2014 installed @gobing-ai/ts-llm-jsonl-importer version ` + `${input.importerVersion ?? "unknown"} destroys history_tool_call.args_raw on pi imports ` + `(todo-tool arguments collapse to their first line; bash commands are lost). ` + `Minimum safe version: ${MIN_SAFE_PI_BASH_IMPORTER_VERSION}. ` + `Remedy: upgrade or relink @gobing-ai/ts-libs to a release containing ts-libs commit 96762d5, ` + `or preview the import with --dry-run.`, input.importerVersion ?? "unknown");
84920
85488
  }
85489
+ async function raceSourceImport(importPromise, source, timeoutMs) {
85490
+ const signal = AbortSignal.timeout(timeoutMs);
85491
+ const deadline = new Promise((_, reject) => {
85492
+ signal.addEventListener("abort", () => reject(new Error(`source '${source}' exceeded ${timeoutMs}ms timeout`, { cause: signal.reason })), { once: true });
85493
+ });
85494
+ try {
85495
+ await Promise.race([importPromise, deadline]);
85496
+ return "completed";
85497
+ } catch (e) {
85498
+ if (signal.aborted)
85499
+ return "timeout";
85500
+ throw e;
85501
+ }
85502
+ }
84921
85503
  async function buildRefreshCoverage(db2, selector, fanOut) {
84922
85504
  const statusBySource = new Map(fanOut.entries.map((e) => [e.source, e.status]));
84923
85505
  const refreshed = FULL_FIDELITY_SOURCES.filter((s3) => statusBySource.has(s3) && statusBySource.get(s3) !== "failed");
@@ -84990,7 +85572,7 @@ class HistoryService {
84990
85572
  });
84991
85573
  }
84992
85574
  await dao2.alignMessageProvenance();
84993
- await deriveAssistantDurations(db2);
85575
+ await deriveMissingAssistantDurations(db2, DERIVED_DURATION_CEILING_MS2);
84994
85576
  }
84995
85577
  const attribution2 = await this.attributeSource({
84996
85578
  source: parsedSource,
@@ -85176,7 +85758,7 @@ class HistoryService {
85176
85758
  dryRun: opts.dryRun === true,
85177
85759
  sources
85178
85760
  });
85179
- const timeoutMs = opts.sourceTimeout ?? DEFAULT_SOURCE_TIMEOUT_MS;
85761
+ const timeoutMs = opts.sourceTimeout !== undefined ? opts.sourceTimeout : DEFAULT_SOURCE_TIMEOUT_MS;
85180
85762
  const entries = [];
85181
85763
  const warnings = [];
85182
85764
  const attribution2 = emptyAttributionSummary();
@@ -85281,16 +85863,11 @@ class HistoryService {
85281
85863
  mode: opts.mode ?? (opts.file !== undefined && opts.file.length > 0 ? "force-file" : "incremental"),
85282
85864
  dryRun: opts.dryRun
85283
85865
  });
85284
- const abort = new AbortController;
85285
- const timer = setTimeout(() => abort.abort(new Error(`source '${source}' exceeded ${timeoutMs}ms timeout`)), timeoutMs);
85286
- const timeoutPromise = new Promise((_, reject) => {
85287
- abort.signal.addEventListener("abort", () => reject(abort.signal.reason));
85288
- });
85289
85866
  let result;
85290
- try {
85291
- result = await Promise.race([importPromise, timeoutPromise]);
85292
- } catch (e) {
85293
- if (abort.signal.aborted) {
85867
+ if (timeoutMs === null) {
85868
+ result = await importPromise;
85869
+ } else {
85870
+ if (await raceSourceImport(importPromise, source, timeoutMs) === "timeout") {
85294
85871
  const elapsedMs = Date.now() - attemptStartedAt;
85295
85872
  const detail = `source '${source}' exceeded its ${timeoutMs}ms budget ` + `(elapsed ${elapsedMs}ms); import attempt was abandoned mid-flight and may still hold the write lock until the process exits`;
85296
85873
  sourceWarnings.push({ code: "source-timeout", source, detail });
@@ -85313,9 +85890,7 @@ class HistoryService {
85313
85890
  sourceAttribution: null
85314
85891
  };
85315
85892
  }
85316
- throw e;
85317
- } finally {
85318
- clearTimeout(timer);
85893
+ result = await importPromise;
85319
85894
  }
85320
85895
  const hasDegradedInput = result.parseErrors.length > 0 || result.validationErrors.length > 0;
85321
85896
  const status = result.scannedFiles === 0 ? DEFERRED_SOURCES.includes(source) ? "deferred" : "empty" : hasDegradedInput ? "degraded" : "ok";
@@ -85664,7 +86239,7 @@ function pruneReports(cwd, retentionDays = REPORT_RETENTION_DAYS, now2 = new Dat
85664
86239
  }
85665
86240
  return pruned.sort((a2, b) => a2.localeCompare(b));
85666
86241
  }
85667
- var MIN_SAFE_PI_BASH_IMPORTER_VERSION = "0.4.49", UnsafeHistoryImporterError, SOURCES, FULL_FIDELITY_SOURCES, DEFERRED_SOURCES, MODES, MAX_ERROR_SAMPLES2 = 20, DEFAULT_SOURCE_TIMEOUT_MS = 600000, HISTORY_BUSY_ABORT_THRESHOLD = 2, HISTORY_BUSY_ERROR_PATTERN, REPORT_RETENTION_DAYS = 90;
86242
+ var MIN_SAFE_PI_BASH_IMPORTER_VERSION = "0.4.49", UnsafeHistoryImporterError, SOURCES, FULL_FIDELITY_SOURCES, DEFERRED_SOURCES, MODES, MAX_ERROR_SAMPLES2 = 20, DEFAULT_SOURCE_TIMEOUT_MS = 600000, DERIVED_DURATION_CEILING_MS2, HISTORY_BUSY_ABORT_THRESHOLD = 2, HISTORY_BUSY_ERROR_PATTERN, REPORT_RETENTION_DAYS = 90;
85668
86243
  var init_history_service = __esm(() => {
85669
86244
  init_src2();
85670
86245
  init_dist6();
@@ -85698,6 +86273,7 @@ var init_history_service = __esm(() => {
85698
86273
  FULL_FIDELITY_SOURCES = ["claude", "codex", "pi", "omp", "agy", "grok"];
85699
86274
  DEFERRED_SOURCES = ["gemini", "opencode", "antigravity", "openclaw", "hermes"];
85700
86275
  MODES = ["full", "incremental", "force-file"];
86276
+ DERIVED_DURATION_CEILING_MS2 = 30 * 60 * 1000;
85701
86277
  HISTORY_BUSY_ERROR_PATTERN = /database is locked|SQLITE_BUSY/i;
85702
86278
  });
85703
86279
 
@@ -87745,10 +88321,10 @@ class HttpRequestActionRunner {
87745
88321
  if (!Object.hasOwn(ALLOWED_METHODS, method)) {
87746
88322
  return { ok: false, error: `http.request: unsupported method: ${method}` };
87747
88323
  }
87748
- if (timeoutMs <= 0 || timeoutMs > MAX_TIMEOUT_MS2) {
88324
+ if (timeoutMs <= 0 || timeoutMs > MAX_TIMEOUT_MS4) {
87749
88325
  return {
87750
88326
  ok: false,
87751
- error: `http.request: timeoutMs must be 1\u2013${MAX_TIMEOUT_MS2}, got ${timeoutMs}`
88327
+ error: `http.request: timeoutMs must be 1\u2013${MAX_TIMEOUT_MS4}, got ${timeoutMs}`
87752
88328
  };
87753
88329
  }
87754
88330
  if (maxResponseBytes <= 0) {
@@ -87857,7 +88433,7 @@ function asNumberArray(value) {
87857
88433
  return [];
87858
88434
  return value.filter((v) => typeof v === "number" && !Number.isNaN(v));
87859
88435
  }
87860
- var KIND10 = "http.request", ALLOWED_METHODS, DEFAULT_TIMEOUT_MS3 = 30000, MAX_TIMEOUT_MS2 = 120000, DEFAULT_MAX_RESPONSE_BYTES = 1048576, HEADER_NAME_RE, HEADER_VALUE_RE;
88436
+ var KIND10 = "http.request", ALLOWED_METHODS, DEFAULT_TIMEOUT_MS3 = 30000, MAX_TIMEOUT_MS4 = 120000, DEFAULT_MAX_RESPONSE_BYTES = 1048576, HEADER_NAME_RE, HEADER_VALUE_RE;
87861
88437
  var init_http_request = __esm(() => {
87862
88438
  ALLOWED_METHODS = {
87863
88439
  GET: true,
@@ -87876,6 +88452,21 @@ var init_http_request = __esm(() => {
87876
88452
  import crypto4 from "crypto";
87877
88453
  import { tmpdir as tmpdir2 } from "os";
87878
88454
  import { dirname as dirname15, join as join21, resolve as resolve8, sep as sep4 } from "path";
88455
+ function taskDocumentShapeError(content) {
88456
+ const doc2 = MarkdownDocument.parse(content, "task");
88457
+ const heading = CANONICAL_TASK_HEADING_RE.exec(doc2.bodyWithoutFrontmatter);
88458
+ if (heading === null) {
88459
+ return "no canonical task heading (`## <WBS>. <title>`) was found";
88460
+ }
88461
+ if ((heading[2] ?? "").trim() === "") {
88462
+ return `the canonical task heading for WBS ${heading[1]} has an empty title`;
88463
+ }
88464
+ const present = TASK_SPEC_SECTIONS.filter((section) => doc2.hasSection(section));
88465
+ if (present.length === 0) {
88466
+ return `no task specification section (${TASK_SPEC_SECTIONS.join(", ")}) was found`;
88467
+ }
88468
+ return null;
88469
+ }
87879
88470
  async function readProofInputContents(fileSystem, workdir, options) {
87880
88471
  for (const name of ["taskFile", "featureFile"]) {
87881
88472
  const value = options[name];
@@ -87906,11 +88497,21 @@ async function readProofInputContents(fileSystem, workdir, options) {
87906
88497
  if (!stat2.isFile()) {
87907
88498
  return { error: `${name} is not a regular file: ${resolved}` };
87908
88499
  }
88500
+ let content;
87909
88501
  try {
87910
- return { content: await fileSystem.readFile(resolved) };
88502
+ content = await fileSystem.readFile(resolved);
87911
88503
  } catch (error51) {
87912
88504
  return { error: `${name} is not readable: ${error51.message}` };
87913
88505
  }
88506
+ if (name === "taskFile") {
88507
+ const shapeError = taskDocumentShapeError(content);
88508
+ if (shapeError !== null) {
88509
+ return {
88510
+ error: `taskFile is not a task document: ${raw} (resolved ${resolved}) \u2014 ${shapeError}. ` + "Supply the task specification markdown itself: a `## <WBS>. <title>` heading plus at " + `least one of ${TASK_SPEC_SECTIONS.join(", ")}. A file containing only a path is not ` + "dereferenced."
88511
+ };
88512
+ }
88513
+ }
88514
+ return { content };
87914
88515
  };
87915
88516
  const task = await readOne("taskFile");
87916
88517
  if (task.error !== undefined)
@@ -88024,7 +88625,7 @@ async function computeProofInputFingerprint(options = {}) {
88024
88625
  const hash2 = crypto4.createHash("sha256").update(canonical, "utf8").digest("hex");
88025
88626
  return `sha256:${hash2}`;
88026
88627
  }
88027
- var ProofCaptureError, DEFAULT_EXCLUDE_GLOBS, ProofInputFingerprint;
88628
+ var ProofCaptureError, TASK_SPEC_SECTIONS, CANONICAL_TASK_HEADING_RE, DEFAULT_EXCLUDE_GLOBS, ProofInputFingerprint;
88028
88629
  var init_proof_input_fingerprint = __esm(() => {
88029
88630
  init_src2();
88030
88631
  init_dist5();
@@ -88037,6 +88638,8 @@ var init_proof_input_fingerprint = __esm(() => {
88037
88638
  this.name = "ProofCaptureError";
88038
88639
  }
88039
88640
  };
88641
+ TASK_SPEC_SECTIONS = ["Background", "Requirements", "Acceptance Criteria", "Design", "Plan"];
88642
+ CANONICAL_TASK_HEADING_RE = /^##[ \t]+(\d+)\.[ \t]+(\S.*)$/m;
88040
88643
  DEFAULT_EXCLUDE_GLOBS = ["docs/tasks*", "docs/features*"];
88041
88644
  ProofInputFingerprint = {
88042
88645
  compute: computeProofInputFingerprint
@@ -90129,7 +90732,7 @@ var init_workflow_service = __esm(() => {
90129
90732
  });
90130
90733
 
90131
90734
  // ../../packages/app/src/services/inline-run-setup.ts
90132
- import { resolve as resolve11 } from "path";
90735
+ import { join as join24, resolve as resolve11 } from "path";
90133
90736
  function parseIdentityMetadata(raw) {
90134
90737
  if (raw.trim() === "")
90135
90738
  return {};
@@ -90143,9 +90746,7 @@ function parseIdentityMetadata(raw) {
90143
90746
  }
90144
90747
  }
90145
90748
  async function openInlineRunProjectDb(workdir) {
90146
- const { join: join24, resolve: resolve12 } = await import("path");
90147
- const { mkdirSync: mkdirSync7 } = await import("fs");
90148
- const url2 = join24(resolve12(workdir), ".spur", "spur.db");
90749
+ const url2 = join24(resolve11(workdir), ".spur", "spur.db");
90149
90750
  mkdirSync7(join24(url2, ".."), { recursive: true });
90150
90751
  const adapter = await createMigratedDb({ url: url2 });
90151
90752
  return { adapter, close: () => adapter.close() };
@@ -90173,7 +90774,7 @@ async function createOrAttachInlineRun(input) {
90173
90774
  const source = { path: resolved.path, layer: resolved.layer, workdir };
90174
90775
  const db2 = await input.getDb();
90175
90776
  const runDao = new RunDao(db2);
90176
- const existing = await runDao.traceRowById(runId) ?? undefined;
90777
+ const existing = await runDao.traceRowById(runId);
90177
90778
  if (existing !== undefined) {
90178
90779
  const metadata = parseIdentityMetadata(existing.metadata_json);
90179
90780
  if (metadata === null) {
@@ -90238,9 +90839,12 @@ async function createOrAttachInlineRun(input) {
90238
90839
  status: "running",
90239
90840
  started_at: new Date().toISOString(),
90240
90841
  completed_at: null,
90241
- metadata_json: "{}"
90842
+ metadata_json: JSON.stringify({
90843
+ definitionDigest: digest,
90844
+ workflowVersion: version3,
90845
+ definitionSource: source
90846
+ })
90242
90847
  });
90243
- await runDao.stampRunIdentity(runId, digest, version3, source);
90244
90848
  return {
90245
90849
  ok: true,
90246
90850
  attached: false,
@@ -90254,11 +90858,13 @@ async function createOrAttachInlineRun(input) {
90254
90858
  status: "running"
90255
90859
  };
90256
90860
  }
90257
- var init_inline_run_setup = __esm(() => {
90861
+ var mkdirSync7;
90862
+ var init_inline_run_setup = __esm(async () => {
90258
90863
  init_src2();
90259
90864
  init_dist8();
90260
90865
  init_workflow_resolver();
90261
90866
  init_workflow_service();
90867
+ ({ mkdirSync: mkdirSync7 } = await import("fs"));
90262
90868
  });
90263
90869
 
90264
90870
  // ../../packages/app/src/services/job-worker-service.ts
@@ -90955,17 +91561,17 @@ var init_process_inventory_service = __esm(() => {
90955
91561
  });
90956
91562
 
90957
91563
  // ../../packages/app/src/services/project-registry.ts
90958
- import { existsSync as existsSync6, mkdirSync as mkdirSync7, readFileSync as readFileSync9, realpathSync as realpathSync5, renameSync as renameSync4, rmSync as rmSync5, writeFileSync as writeFileSync5 } from "fs";
91564
+ import { existsSync as existsSync6, mkdirSync as mkdirSync8, readFileSync as readFileSync9, realpathSync as realpathSync5, renameSync as renameSync4, rmSync as rmSync5, writeFileSync as writeFileSync5 } from "fs";
90959
91565
  import { connect, createServer } from "net";
90960
91566
  import { homedir as homedir7 } from "os";
90961
- import { dirname as dirname17, join as join24, resolve as resolve12 } from "path";
91567
+ import { dirname as dirname17, join as join25, resolve as resolve12 } from "path";
90962
91568
  function normalizeProjectPath(pathInput) {
90963
91569
  const trimmed = pathInput.trim();
90964
91570
  let expanded = trimmed;
90965
91571
  if (trimmed === "~") {
90966
91572
  expanded = homedir7();
90967
91573
  } else if (trimmed.startsWith("~/")) {
90968
- expanded = join24(homedir7(), trimmed.slice(2));
91574
+ expanded = join25(homedir7(), trimmed.slice(2));
90969
91575
  } else {
90970
91576
  expanded = resolve12(trimmed);
90971
91577
  }
@@ -91062,7 +91668,7 @@ class ProjectRegistry {
91062
91668
  const lockParent = dirname17(this.lockDir);
91063
91669
  if (!existsSync6(lockParent)) {
91064
91670
  try {
91065
- mkdirSync7(lockParent, { recursive: true });
91671
+ mkdirSync8(lockParent, { recursive: true });
91066
91672
  } catch (err) {
91067
91673
  const code = err?.code;
91068
91674
  if (code === "EPERM" || code === "EACCES") {
@@ -91075,7 +91681,7 @@ class ProjectRegistry {
91075
91681
  let acquired = false;
91076
91682
  for (let i2 = 0;i2 < maxTries; i2++) {
91077
91683
  try {
91078
- mkdirSync7(this.lockDir);
91684
+ mkdirSync8(this.lockDir);
91079
91685
  acquired = true;
91080
91686
  break;
91081
91687
  } catch (err) {
@@ -91089,7 +91695,7 @@ class ProjectRegistry {
91089
91695
  if (!acquired) {
91090
91696
  try {
91091
91697
  rmSync5(this.lockDir, { recursive: true, force: true });
91092
- mkdirSync7(this.lockDir);
91698
+ mkdirSync8(this.lockDir);
91093
91699
  acquired = true;
91094
91700
  } catch {
91095
91701
  throw new Error(`Failed to acquire lock for project registry: ${this.lockDir}`);
@@ -91120,7 +91726,7 @@ class ProjectRegistry {
91120
91726
  writeRaw(data) {
91121
91727
  const parentDir2 = dirname17(this.filePath);
91122
91728
  if (!existsSync6(parentDir2)) {
91123
- mkdirSync7(parentDir2, { recursive: true });
91729
+ mkdirSync8(parentDir2, { recursive: true });
91124
91730
  }
91125
91731
  const tmpFile = `${this.filePath}.${Date.now()}.${Math.random().toString(36).slice(2)}.tmp`;
91126
91732
  const content = JSON.stringify(data, null, 2);
@@ -91405,7 +92011,7 @@ var init_project_start = __esm(() => {
91405
92011
 
91406
92012
  // ../../packages/app/src/services/rule-service.ts
91407
92013
  import { homedir as homedir8 } from "os";
91408
- import { delimiter, join as join25, relative as relative4, resolve as resolve14 } from "path";
92014
+ import { delimiter, join as join26, relative as relative4, resolve as resolve14 } from "path";
91409
92015
 
91410
92016
  class RuleService {
91411
92017
  context;
@@ -91600,13 +92206,13 @@ ${ruleIds.join(`
91600
92206
  const hasGlobalOverride = globalOverride !== undefined && globalOverride.length > 0;
91601
92207
  layers.push({
91602
92208
  id: "global",
91603
- path: hasGlobalOverride ? resolve14(cwd, globalOverride) : join25(homedir8(), GLOBAL_RULES_DIR),
92209
+ path: hasGlobalOverride ? resolve14(cwd, globalOverride) : join26(homedir8(), GLOBAL_RULES_DIR),
91604
92210
  priority: 10
91605
92211
  });
91606
92212
  if (opts.includeBundled && !hasGlobalOverride) {
91607
92213
  const bundledConfig = bundledConfigRoot();
91608
92214
  if (bundledConfig !== null) {
91609
- layers.push({ id: "bundled-config", path: join25(bundledConfig, "rules"), priority: 15 });
92215
+ layers.push({ id: "bundled-config", path: join26(bundledConfig, "rules"), priority: 15 });
91610
92216
  }
91611
92217
  const bundled = await bundledRulesRoot();
91612
92218
  if (bundled !== null)
@@ -91819,7 +92425,7 @@ ${ruleIds.join(`
91819
92425
  const { fs: fs3 } = this.context;
91820
92426
  for (const root of await this.ruleRoots()) {
91821
92427
  for (const ext of ["yaml", "yml", "json"]) {
91822
- if (await fs3.exists(join25(root, `${name}.${ext}`)))
92428
+ if (await fs3.exists(join26(root, `${name}.${ext}`)))
91823
92429
  return true;
91824
92430
  }
91825
92431
  }
@@ -91855,7 +92461,7 @@ ${ruleIds.join(`
91855
92461
  for (const layer of await this.existingRuleSourceLayers()) {
91856
92462
  const entries = await this.context.fs.readDir(layer.path);
91857
92463
  const named = entries.map((entry) => ({ entry, name: entry.match(/^([\w-]+)\.(?:ya?ml|json)$/i)?.[1] ?? "" })).filter(({ name }) => name.length > 0);
91858
- const stats = await Promise.all(named.map(({ entry }) => this.context.fs.stat(join25(layer.path, entry))));
92464
+ const stats = await Promise.all(named.map(({ entry }) => this.context.fs.stat(join26(layer.path, entry))));
91859
92465
  named.forEach(({ name }, i2) => {
91860
92466
  if (stats[i2]?.isFile())
91861
92467
  candidates.add(name);
@@ -91877,7 +92483,7 @@ ${ruleIds.join(`
91877
92483
  for (const layer of layers) {
91878
92484
  for (const file2 of await this.listRuleFilesInLayer(layer.path)) {
91879
92485
  if (!merged.has(file2))
91880
- merged.set(file2, { absolutePath: join25(layer.path, file2), source: layer.id });
92486
+ merged.set(file2, { absolutePath: join26(layer.path, file2), source: layer.id });
91881
92487
  }
91882
92488
  }
91883
92489
  const files = [];
@@ -91945,7 +92551,7 @@ ${ruleIds.join(`
91945
92551
  for (const entry of entries.sort()) {
91946
92552
  if (entry === "presets")
91947
92553
  continue;
91948
- const stat2 = await this.context.fs.stat(join25(root, entry));
92554
+ const stat2 = await this.context.fs.stat(join26(root, entry));
91949
92555
  if (stat2?.isDirectory())
91950
92556
  dirs.push(entry);
91951
92557
  }
@@ -91953,12 +92559,12 @@ ${ruleIds.join(`
91953
92559
  }
91954
92560
  async listRuleFiles(root, relativeDir) {
91955
92561
  const { fs: fs3 } = this.context;
91956
- const dir = join25(root, relativeDir);
92562
+ const dir = join26(root, relativeDir);
91957
92563
  const entries = await fs3.readDir(dir);
91958
92564
  const files = [];
91959
92565
  for (const entry of entries.sort()) {
91960
- const relativePath2 = relativeDir.length === 0 ? entry : join25(relativeDir, entry);
91961
- const absolutePath = join25(root, relativePath2);
92566
+ const relativePath2 = relativeDir.length === 0 ? entry : join26(relativeDir, entry);
92567
+ const absolutePath = join26(root, relativePath2);
91962
92568
  const stat2 = await fs3.stat(absolutePath);
91963
92569
  if (stat2?.isDirectory()) {
91964
92570
  files.push(...await this.listRuleFiles(root, relativePath2));
@@ -92092,8 +92698,8 @@ var init_rule_service = __esm(() => {
92092
92698
  init_dist10();
92093
92699
  init_agent_execution();
92094
92700
  init_system_event_envelope();
92095
- LOCAL_RULES_DIR = join25(".spur", "rules");
92096
- GLOBAL_RULES_DIR = join25(".config", "spur", "rules");
92701
+ LOCAL_RULES_DIR = join26(".spur", "rules");
92702
+ GLOBAL_RULES_DIR = join26(".config", "spur", "rules");
92097
92703
  SEVERITY_RANK2 = {
92098
92704
  info: 0,
92099
92705
  warning: 1,
@@ -93228,7 +93834,7 @@ var init_task_size_precheck = __esm(() => {
93228
93834
  });
93229
93835
 
93230
93836
  // ../../packages/app/src/services/task-service.ts
93231
- import { dirname as dirname19, isAbsolute as isAbsolute7, join as join26, relative as relative5 } from "path";
93837
+ import { dirname as dirname19, isAbsolute as isAbsolute7, join as join27, relative as relative5 } from "path";
93232
93838
  function renderRosterTable(rows) {
93233
93839
  const header = `| WBS | Sub-task | Status |
93234
93840
  | --- | -------- | ------ |`;
@@ -93719,7 +94325,7 @@ ${templateBackground}`;
93719
94325
  if (!featureId2 || featureId2.length === 0)
93720
94326
  return [];
93721
94327
  const tasksDir = dirname19(taskFilePath);
93722
- const featuresDir = join26(tasksDir, "..", "features");
94328
+ const featuresDir = join27(tasksDir, "..", "features");
93723
94329
  const featurePath = await (async () => {
93724
94330
  try {
93725
94331
  for (const name of await this.ctx.fs.readDir(featuresDir)) {
@@ -94503,7 +95109,7 @@ var init_task_verdict = __esm(() => {
94503
95109
  });
94504
95110
 
94505
95111
  // ../../packages/app/src/services/team-service.ts
94506
- import { join as join27, resolve as resolve15 } from "path";
95112
+ import { join as join28, resolve as resolve15 } from "path";
94507
95113
 
94508
95114
  class TeamService {
94509
95115
  ctx;
@@ -94511,7 +95117,7 @@ class TeamService {
94511
95117
  orchestratorPromise;
94512
95118
  constructor(ctx) {
94513
95119
  this.ctx = ctx;
94514
- this.configDir = join27(ctx.cwd, ".spur", "agents");
95120
+ this.configDir = join28(ctx.cwd, ".spur", "agents");
94515
95121
  }
94516
95122
  async sendMessage(fromId, toId, body, replyTo) {
94517
95123
  validateAgentId(toId);
@@ -94983,7 +95589,7 @@ class TeamService {
94983
95589
  async resolveTaskFile(taskId) {
94984
95590
  const fs3 = this.ctx.fs;
94985
95591
  const { foldersConfig } = await resolvePlanningFolders(fs3);
94986
- const dirs = [...new Set([foldersConfig.active_folder, ...Object.keys(foldersConfig.folders)])].map((dir) => join27(this.ctx.cwd, dir));
95592
+ const dirs = [...new Set([foldersConfig.active_folder, ...Object.keys(foldersConfig.folders)])].map((dir) => join28(this.ctx.cwd, dir));
94987
95593
  return await TaskLocator.forDirs(fs3, dirs).findPathByWbs(taskId);
94988
95594
  }
94989
95595
  }
@@ -95020,7 +95626,7 @@ var init_team_service = __esm(() => {
95020
95626
 
95021
95627
  // ../../packages/app/src/services/token-ledger-service.ts
95022
95628
  import { closeSync as closeSync3, existsSync as existsSync8, fstatSync, openSync as openSync3, readSync } from "fs";
95023
- import { join as join28 } from "path";
95629
+ import { join as join29 } from "path";
95024
95630
  function isSparseToolActivity(events2) {
95025
95631
  return events2.length === 0 || !events2.some((e) => TOOL_ACTIVITY_TYPES.has(e.type));
95026
95632
  }
@@ -95121,7 +95727,7 @@ class TokenLedgerService {
95121
95727
  ledgerPath;
95122
95728
  chunkSize;
95123
95729
  constructor(options) {
95124
- this.ledgerPath = options.ledgerPath ?? join28(options.cwd, TOKEN_LEDGER_RELATIVE_PATH);
95730
+ this.ledgerPath = options.ledgerPath ?? join29(options.cwd, TOKEN_LEDGER_RELATIVE_PATH);
95125
95731
  this.chunkSize = options.chunkSize ?? DEFAULT_CHUNK;
95126
95732
  }
95127
95733
  get path() {
@@ -95162,7 +95768,7 @@ class TokenLedgerService {
95162
95768
  var TOOL_ACTIVITY_TYPES, TOKEN_LEDGER_DEFAULT_LIMIT = 200, TOKEN_LEDGER_MAX_LIMIT = 1000, TOKEN_LEDGER_RELATIVE_PATH, DEFAULT_CHUNK;
95163
95769
  var init_token_ledger_service = __esm(() => {
95164
95770
  TOOL_ACTIVITY_TYPES = new Set(["read", "write", "bash", "grep", "glob"]);
95165
- TOKEN_LEDGER_RELATIVE_PATH = join28(".spur", "context", "token-ledger.jsonl");
95771
+ TOKEN_LEDGER_RELATIVE_PATH = join29(".spur", "context", "token-ledger.jsonl");
95166
95772
  DEFAULT_CHUNK = 64 * 1024;
95167
95773
  });
95168
95774
 
@@ -96098,7 +96704,7 @@ var init_progress_follow = __esm(() => {
96098
96704
  });
96099
96705
 
96100
96706
  // ../../packages/app/src/workflow/trace-writer.ts
96101
- import { dirname as dirname20, join as join29 } from "path";
96707
+ import { dirname as dirname20, join as join30 } from "path";
96102
96708
 
96103
96709
  class WorkflowTraceWriter {
96104
96710
  path;
@@ -96106,7 +96712,7 @@ class WorkflowTraceWriter {
96106
96712
  pending = Promise.resolve();
96107
96713
  constructor(cwd, runId) {
96108
96714
  const safeRunId = runId.replace(/[^A-Za-z0-9._-]/g, "_");
96109
- this.path = join29(cwd, ".spur", "workflow", `${safeRunId}.jsonl`);
96715
+ this.path = join30(cwd, ".spur", "workflow", `${safeRunId}.jsonl`);
96110
96716
  }
96111
96717
  attach(bus) {
96112
96718
  bus.on("workflow.run.started", (event) => this.enqueue("workflow.run.started", event));
@@ -96141,6 +96747,69 @@ var init_trace_writer = __esm(() => {
96141
96747
  init_dist5();
96142
96748
  });
96143
96749
 
96750
+ // ../../packages/app/src/workflow/workflow-inventory.ts
96751
+ function parseWorkflowInventory(json3) {
96752
+ if (typeof json3 !== "object" || json3 === null) {
96753
+ return { ok: false, error: "workflow inventory: projection is not an object" };
96754
+ }
96755
+ const obj = json3;
96756
+ const name = typeof obj.name === "string" ? obj.name : "";
96757
+ const kind = typeof obj.kind === "string" ? obj.kind : "";
96758
+ const format2 = typeof obj.format === "string" ? obj.format : "";
96759
+ const version3 = typeof obj.version === "string" || typeof obj.version === "number" ? String(obj.version) : "";
96760
+ const definitionDigest = typeof obj.definitionDigest === "string" ? obj.definitionDigest : "";
96761
+ if (name === "")
96762
+ return { ok: false, error: "workflow inventory: missing name" };
96763
+ if (kind === "")
96764
+ return { ok: false, error: "workflow inventory: missing kind" };
96765
+ if (definitionDigest === "")
96766
+ return { ok: false, error: "workflow inventory: missing definitionDigest" };
96767
+ if (!Array.isArray(obj.steps) || obj.steps.length === 0) {
96768
+ return { ok: false, error: "workflow inventory: no declared steps" };
96769
+ }
96770
+ const steps = [];
96771
+ for (const raw of obj.steps) {
96772
+ if (typeof raw !== "object" || raw === null) {
96773
+ return { ok: false, error: "workflow inventory: a step is not an object" };
96774
+ }
96775
+ const s3 = raw;
96776
+ if (typeof s3.id !== "string" || s3.id === "") {
96777
+ return { ok: false, error: "workflow inventory: a step is missing its id" };
96778
+ }
96779
+ steps.push({
96780
+ id: s3.id,
96781
+ initial: s3.initial === true,
96782
+ terminal: s3.terminal === true,
96783
+ failure: s3.failure === true,
96784
+ pause: s3.pause === true,
96785
+ loopBack: s3.loopBack === true,
96786
+ conditional: s3.conditional === true,
96787
+ ...typeof s3.nodeType === "string" ? { nodeType: s3.nodeType } : {},
96788
+ ...typeof s3.description === "string" ? { description: s3.description } : {}
96789
+ });
96790
+ }
96791
+ return {
96792
+ ok: true,
96793
+ inventory: { name, kind, format: format2, version: version3, definitionDigest, steps }
96794
+ };
96795
+ }
96796
+ function assertInventoryIdentity(inventory, expectedDigest) {
96797
+ if (expectedDigest === "") {
96798
+ return { ok: false, error: "workflow inventory: no expected definition digest to bind against" };
96799
+ }
96800
+ if (inventory.definitionDigest !== expectedDigest) {
96801
+ return {
96802
+ ok: false,
96803
+ error: `workflow inventory: identity drift \u2014 projected ${inventory.definitionDigest} \u2260 execution ${expectedDigest}`
96804
+ };
96805
+ }
96806
+ return { ok: true };
96807
+ }
96808
+ function renderEventTrace(events2) {
96809
+ return events2.map((e) => `${e.at} ${e.kind}${e.detail !== undefined && e.detail !== "" ? ` \u2014 ${e.detail}` : ""}`).join(`
96810
+ `);
96811
+ }
96812
+
96144
96813
  // ../../packages/app/src/index.ts
96145
96814
  var exports_src2 = {};
96146
96815
  __export(exports_src2, {
@@ -96152,6 +96821,7 @@ __export(exports_src2, {
96152
96821
  validateHistoryRefreshPayload: () => validateHistoryRefreshPayload,
96153
96822
  toEnvelopeJson: () => toEnvelopeJson,
96154
96823
  toEnvelopeError: () => toEnvelopeError,
96824
+ terminateJobChildren: () => terminateJobChildren,
96155
96825
  tailTokenLedgerFile: () => tailTokenLedgerFile,
96156
96826
  systemEventProjectContext: () => systemEventProjectContext,
96157
96827
  systemEventCatalogEntry: () => systemEventCatalogEntry,
@@ -96177,7 +96847,7 @@ __export(exports_src2, {
96177
96847
  resolveRetentionQuotas: () => resolveRetentionQuotas,
96178
96848
  resolvePlanningFolders: () => resolvePlanningFolders,
96179
96849
  resolveOutputLogConfig: () => resolveOutputLogConfig,
96180
- resolveKillGraceMs: () => resolveKillGraceMs,
96850
+ resolveKillGraceMs: () => resolveKillGraceMs3,
96181
96851
  resolveHistoryRefreshTimeoutMs: () => resolveHistoryRefreshTimeoutMs,
96182
96852
  resolveFogRange: () => resolveFogRange,
96183
96853
  resolveConfiguredTaskDirs: () => resolveConfiguredTaskDirs,
@@ -96190,6 +96860,7 @@ __export(exports_src2, {
96190
96860
  renderSolutionFromDiff: () => renderSolutionFromDiff,
96191
96861
  renderRunPlan: () => renderRunPlan,
96192
96862
  renderReview: () => renderReview,
96863
+ renderEventTrace: () => renderEventTrace,
96193
96864
  renderEscalationMarkdown: () => renderEscalationMarkdown,
96194
96865
  renderActionHeartbeat: () => renderActionHeartbeat,
96195
96866
  releaseExclusiveJob: () => releaseExclusiveJob,
@@ -96214,7 +96885,9 @@ __export(exports_src2, {
96214
96885
  prepareBatchTaskReady: () => prepareBatchTaskReady,
96215
96886
  portBindingAvailable: () => portBindingAvailable,
96216
96887
  permissionFailureEvidence: () => permissionFailureEvidence,
96888
+ parseWorkflowInventory: () => parseWorkflowInventory,
96217
96889
  parseVerdict: () => parseVerdict,
96890
+ parseTimeoutInput: () => parseTimeoutInput,
96218
96891
  parseSteeringPolicy: () => parseSteeringPolicy,
96219
96892
  parsePsOutput: () => parsePsOutput,
96220
96893
  parseLedgerLine: () => parseLedgerLine,
@@ -96224,6 +96897,7 @@ __export(exports_src2, {
96224
96897
  openInlineRunProjectDb: () => openInlineRunProjectDb,
96225
96898
  normalizeSystemEventPayload: () => normalizeSystemEventPayload,
96226
96899
  normalizeProjectPath: () => normalizeProjectPath,
96900
+ normalizeLegacyTimeoutMs: () => normalizeLegacyTimeoutMs,
96227
96901
  looksLikeOpaqueId: () => looksLikeOpaqueId,
96228
96902
  isSystemEventEnvelopeV2: () => isSystemEventEnvelopeV2,
96229
96903
  isRecordAuthoredReview: () => isRecordAuthoredReview,
@@ -96294,6 +96968,8 @@ __export(exports_src2, {
96294
96968
  attributeSessions: () => attributeSessions,
96295
96969
  attachAgentQuotaUpdates: () => attachAgentQuotaUpdates,
96296
96970
  assertPiImporterSafe: () => assertPiImporterSafe,
96971
+ assertInventoryIdentity: () => assertInventoryIdentity,
96972
+ assertCanonicalTimeoutMs: () => assertCanonicalTimeoutMs,
96297
96973
  anchorQualify: () => anchorQualify,
96298
96974
  aggregateBatchVerdicts: () => aggregateBatchVerdicts,
96299
96975
  acquireExclusiveJob: () => acquireExclusiveJob,
@@ -96363,6 +97039,7 @@ __export(exports_src2, {
96363
97039
  ObservableWorkflowAdapter: () => ObservableWorkflowAdapter,
96364
97040
  MockHistoryBoardService: () => MockHistoryBoardService,
96365
97041
  MIN_SAFE_PI_BASH_IMPORTER_VERSION: () => MIN_SAFE_PI_BASH_IMPORTER_VERSION,
97042
+ MAX_TIMEOUT_INPUT_MS: () => MAX_TIMEOUT_INPUT_MS,
96366
97043
  MAX_QUOTA_DRAIN_ATTEMPTS_PER_ACTIVATION: () => MAX_QUOTA_DRAIN_ATTEMPTS_PER_ACTIVATION,
96367
97044
  LockTimeoutError: () => LockTimeoutError,
96368
97045
  LiveHistoryBoardService: () => LiveHistoryBoardService,
@@ -96372,6 +97049,7 @@ __export(exports_src2, {
96372
97049
  HitlInputActionRunner: () => HitlInputActionRunner,
96373
97050
  HitlConfirmActionRunner: () => HitlConfirmActionRunner,
96374
97051
  HistoryService: () => HistoryService,
97052
+ HISTORY_SOURCE_TIMEOUT_ENV: () => HISTORY_SOURCE_TIMEOUT_ENV,
96375
97053
  HISTORY_REFRESH_JOB: () => HISTORY_REFRESH_JOB,
96376
97054
  HISTORY_REFRESH_CONTEXT_ENV: () => HISTORY_REFRESH_CONTEXT_ENV,
96377
97055
  HISTORY_PRODUCER_EXCLUSIVE_KEY: () => HISTORY_PRODUCER_EXCLUSIVE_KEY,
@@ -96394,6 +97072,7 @@ __export(exports_src2, {
96394
97072
  DependencyMutationError: () => DependencyMutationError,
96395
97073
  DEFAULT_SYSTEM_EVENT_RETENTION_QUOTA: () => DEFAULT_SYSTEM_EVENT_RETENTION_QUOTA,
96396
97074
  DEFAULT_STALL_MS: () => DEFAULT_STALL_MS,
97075
+ DEFAULT_SOURCE_TIMEOUT_MS: () => DEFAULT_SOURCE_TIMEOUT_MS,
96397
97076
  DEFAULT_RUN_LOG_MAX_BYTES: () => DEFAULT_RUN_LOG_MAX_BYTES,
96398
97077
  DEFAULT_READY_PREPARE_TIMEOUT_MS: () => DEFAULT_READY_PREPARE_TIMEOUT_MS,
96399
97078
  DEFAULT_FEATURE_MATRIX: () => DEFAULT_FEATURE_MATRIX,
@@ -96423,6 +97102,7 @@ var init_src3 = __esm(async () => {
96423
97102
  init_done_transition_guard();
96424
97103
  init_event_bridge();
96425
97104
  init_event_names();
97105
+ init_execution_policy();
96426
97106
  init_failure_classification();
96427
97107
  init_feature_check();
96428
97108
  init_feature_service();
@@ -96432,7 +97112,6 @@ var init_src3 = __esm(async () => {
96432
97112
  init_history_board_service();
96433
97113
  init_history_refresh_service();
96434
97114
  init_history_service();
96435
- init_inline_run_setup();
96436
97115
  init_job_exclusion_guard();
96437
97116
  init_occupant_wait();
96438
97117
  init_pipeline_run_link();
@@ -96481,8 +97160,12 @@ var init_src3 = __esm(async () => {
96481
97160
  init_progress_projection();
96482
97161
  init_proof_input_fingerprint();
96483
97162
  init_steering();
97163
+ init_step_reporter();
96484
97164
  init_trace_writer();
96485
- await init_idea_handoff_cli();
97165
+ await __promiseAll([
97166
+ init_inline_run_setup(),
97167
+ init_idea_handoff_cli()
97168
+ ]);
96486
97169
  });
96487
97170
 
96488
97171
  // ../../node_modules/.bun/sisteransi@1.0.5/node_modules/sisteransi/src/index.js
@@ -96568,19 +97251,19 @@ var {
96568
97251
  // src/index.ts
96569
97252
  init_loader();
96570
97253
 
96571
- // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.57+33b96fa7d4032676/node_modules/@gobing-ai/ts-infra/dist/application-node.js
97254
+ // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.62+c884df390da1a1e8/node_modules/@gobing-ai/ts-infra/dist/application-node.js
96572
97255
  init_dist4();
96573
97256
  init_dist5();
96574
97257
  import { appendFileSync as appendFileSync4, mkdirSync as mkdirSync3, readFileSync as readFileSync5 } from "fs";
96575
97258
  import { dirname as dirname6 } from "path";
96576
97259
 
96577
- // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.57+33b96fa7d4032676/node_modules/@gobing-ai/ts-infra/dist/application/index.js
97260
+ // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.62+c884df390da1a1e8/node_modules/@gobing-ai/ts-infra/dist/application/index.js
96578
97261
  init_default_observers();
96579
97262
  init_event_bus();
96580
97263
  init_file_observer();
96581
97264
  init_logger3();
96582
97265
 
96583
- // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.57+33b96fa7d4032676/node_modules/@gobing-ai/ts-infra/dist/scheduler/noop.js
97266
+ // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.62+c884df390da1a1e8/node_modules/@gobing-ai/ts-infra/dist/scheduler/noop.js
96584
97267
  class NoopSchedulerAdapter {
96585
97268
  constructor() {}
96586
97269
  register(_cron, _action) {}
@@ -96588,7 +97271,7 @@ class NoopSchedulerAdapter {
96588
97271
  async stop() {}
96589
97272
  }
96590
97273
 
96591
- // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.57+33b96fa7d4032676/node_modules/@gobing-ai/ts-infra/dist/scheduler/factory.js
97274
+ // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.62+c884df390da1a1e8/node_modules/@gobing-ai/ts-infra/dist/scheduler/factory.js
96592
97275
  function initScheduler(adapter, cronEntries) {
96593
97276
  const resolved = adapter ?? new NoopSchedulerAdapter;
96594
97277
  if (cronEntries) {
@@ -96599,7 +97282,7 @@ function initScheduler(adapter, cronEntries) {
96599
97282
  return resolved;
96600
97283
  }
96601
97284
 
96602
- // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.57+33b96fa7d4032676/node_modules/@gobing-ai/ts-infra/dist/application/plugins/builtins.js
97285
+ // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.62+c884df390da1a1e8/node_modules/@gobing-ai/ts-infra/dist/application/plugins/builtins.js
96603
97286
  init_logger3();
96604
97287
  init_metrics();
96605
97288
  init_sdk();
@@ -96692,7 +97375,7 @@ function dbPlugin(db2) {
96692
97375
  };
96693
97376
  }
96694
97377
 
96695
- // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.57+33b96fa7d4032676/node_modules/@gobing-ai/ts-infra/dist/application/plugins/host.js
97378
+ // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.62+c884df390da1a1e8/node_modules/@gobing-ai/ts-infra/dist/application/plugins/host.js
96696
97379
  init_logger3();
96697
97380
 
96698
97381
  class PluginHost {
@@ -96780,7 +97463,7 @@ class PluginHost {
96780
97463
  }
96781
97464
  }
96782
97465
 
96783
- // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.57+33b96fa7d4032676/node_modules/@gobing-ai/ts-infra/dist/application/index.js
97466
+ // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.62+c884df390da1a1e8/node_modules/@gobing-ai/ts-infra/dist/application/index.js
96784
97467
  async function performShutdown(state, reason) {
96785
97468
  if (state.stopped)
96786
97469
  return;
@@ -96811,7 +97494,8 @@ async function runApplication(options) {
96811
97494
  const schedulerConfig = {
96812
97495
  enabled: schedOpts?.enabled ?? false,
96813
97496
  autoStart: schedOpts?.autoStart ?? true,
96814
- jobs: schedOpts?.jobs ?? []
97497
+ jobs: schedOpts?.jobs ?? [],
97498
+ timeoutMs: resolveExecutionTimeoutMs("bootstrap.scheduler", schedOpts?.timeoutMs)
96815
97499
  };
96816
97500
  const eventsEnabled = options.config?.events?.enabled ?? true;
96817
97501
  const eventsLifecycle = options.config?.events?.lifecycle ?? true;
@@ -96891,8 +97575,7 @@ async function runApplication(options) {
96891
97575
  throw error51;
96892
97576
  }
96893
97577
  }
96894
-
96895
- // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.57+33b96fa7d4032676/node_modules/@gobing-ai/ts-infra/dist/scheduler/cron.js
97578
+ // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.62+c884df390da1a1e8/node_modules/@gobing-ai/ts-infra/dist/scheduler/cron.js
96896
97579
  var MAX_SCAN_MINUTES = 8 * 366 * 24 * 60;
96897
97580
  var DAYS_OF_WEEK = 7;
96898
97581
  function parseField(raw, min, max) {
@@ -97006,7 +97689,8 @@ function nextCronTime(expr, nowMs) {
97006
97689
  throw new RangeError(`unsupported cron expression "${expr.source}": no matching time within 8 years`);
97007
97690
  }
97008
97691
 
97009
- // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.57+33b96fa7d4032676/node_modules/@gobing-ai/ts-infra/dist/scheduler/node.js
97692
+ // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.62+c884df390da1a1e8/node_modules/@gobing-ai/ts-infra/dist/scheduler/node.js
97693
+ init_logger3();
97010
97694
  init_metrics();
97011
97695
  var MAX_TIMEOUT = 2147483647;
97012
97696
  function parseInterval(cron) {
@@ -97031,6 +97715,7 @@ function parseInterval(cron) {
97031
97715
  class NodeSchedulerAdapter {
97032
97716
  entries = [];
97033
97717
  drainTimeoutMs;
97718
+ defaultTimeoutMs;
97034
97719
  now;
97035
97720
  running = false;
97036
97721
  inflight = new Set;
@@ -97040,23 +97725,25 @@ class NodeSchedulerAdapter {
97040
97725
  throw new RangeError(`NodeSchedulerAdapter drainTimeoutMs must be a non-negative finite number; received ${drainTimeoutMs}`);
97041
97726
  }
97042
97727
  this.drainTimeoutMs = drainTimeoutMs ?? 30000;
97728
+ this.defaultTimeoutMs = resolveExecutionTimeoutMs("NodeSchedulerAdapter", config4.timeoutMs ?? undefined);
97043
97729
  this.now = now ?? (() => Date.now());
97044
97730
  }
97045
- register(cron, action) {
97046
- const entry = this.parseEntry(cron, action);
97731
+ register(cron, action, options) {
97732
+ const entry = this.parseEntry(cron, action, options);
97047
97733
  this.entries.push(entry);
97048
97734
  if (this.running) {
97049
97735
  this.startEntry(entry);
97050
97736
  }
97051
97737
  }
97052
- parseEntry(cron, action) {
97738
+ parseEntry(cron, action, options) {
97739
+ const timeoutMs = resolveExecutionTimeoutMs(`scheduler entry "${cron}"`, options?.timeoutMs, this.defaultTimeoutMs);
97053
97740
  const intervalMs = parseInterval(cron);
97054
97741
  if (intervalMs !== undefined) {
97055
- return { kind: "interval", cron, action, intervalMs };
97742
+ return { kind: "interval", cron, action, intervalMs, timeoutMs };
97056
97743
  }
97057
97744
  const expr = parseCronExpression(cron);
97058
97745
  const target = nextCronTime(expr, this.now()).getTime();
97059
- return { kind: "cron", cron, action, expr, target };
97746
+ return { kind: "cron", cron, action, expr, target, timeoutMs };
97060
97747
  }
97061
97748
  async start() {
97062
97749
  if (this.running)
@@ -97138,7 +97825,17 @@ class NodeSchedulerAdapter {
97138
97825
  const startMs = performance.now();
97139
97826
  getSchedulerJobExecutedTotal().add(1, { cron: entry.cron });
97140
97827
  try {
97141
- await entry.action();
97828
+ const outcome = await runWithExecutionDeadline(entry.action, { timeoutMs: entry.timeoutMs });
97829
+ if (outcome.timedOut) {
97830
+ getSchedulerJobFailedTotal().add(1, { cron: entry.cron });
97831
+ getLogger2("scheduler").warn("scheduled tick exceeded its execution deadline", {
97832
+ cron: entry.cron,
97833
+ timeoutMs: entry.timeoutMs,
97834
+ elapsedMs: Math.round(outcome.elapsedMs)
97835
+ });
97836
+ } else if (outcome.outcome === "error") {
97837
+ getSchedulerJobFailedTotal().add(1, { cron: entry.cron });
97838
+ }
97142
97839
  } catch {
97143
97840
  getSchedulerJobFailedTotal().add(1, { cron: entry.cron });
97144
97841
  } finally {
@@ -97146,7 +97843,7 @@ class NodeSchedulerAdapter {
97146
97843
  }
97147
97844
  }
97148
97845
  }
97149
- // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.57+33b96fa7d4032676/node_modules/@gobing-ai/ts-infra/dist/telemetry/otel-node.js
97846
+ // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.62+c884df390da1a1e8/node_modules/@gobing-ai/ts-infra/dist/telemetry/otel-node.js
97150
97847
  var import_api14 = __toESM(require_src(), 1);
97151
97848
 
97152
97849
  // ../../node_modules/.bun/@opentelemetry+exporter-metrics-otlp-http@0.218.0+e40b0dfdd726a224/node_modules/@opentelemetry/exporter-metrics-otlp-http/build/esm/OTLPMetricExporterBase.js
@@ -100162,7 +100859,7 @@ class OTLPTraceExporter extends import_otlp_exporter_base2.OTLPExporterBase {
100162
100859
  }), JsonTraceSerializer));
100163
100860
  }
100164
100861
  }
100165
- // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.57+33b96fa7d4032676/node_modules/@gobing-ai/ts-infra/dist/telemetry/otel-node.js
100862
+ // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.62+c884df390da1a1e8/node_modules/@gobing-ai/ts-infra/dist/telemetry/otel-node.js
100166
100863
  var import_resources2 = __toESM(require_src4(), 1);
100167
100864
  var import_sdk_trace_node = __toESM(require_src8(), 1);
100168
100865
  var import_semantic_conventions = __toESM(require_src2(), 1);
@@ -100213,14 +100910,14 @@ async function shutdownNodeTelemetry() {
100213
100910
  await Promise.all(pending);
100214
100911
  }
100215
100912
 
100216
- // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.57+33b96fa7d4032676/node_modules/@gobing-ai/ts-infra/dist/application-node.js
100913
+ // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.62+c884df390da1a1e8/node_modules/@gobing-ai/ts-infra/dist/application-node.js
100217
100914
  class ConfigValidationError extends Error {
100218
100915
  constructor(message) {
100219
100916
  super(message);
100220
100917
  this.name = "ConfigValidationError";
100221
100918
  }
100222
100919
  }
100223
- var MAX_TIMEOUT_MS = 2147483647;
100920
+ var MAX_TIMEOUT_MS3 = 2147483647;
100224
100921
  function validateAppConfig(validator, raw, section, filePath) {
100225
100922
  if (typeof validator === "object" && "safeParse" in validator) {
100226
100923
  const result = validator.safeParse(raw);
@@ -100241,7 +100938,14 @@ function validateAppConfig(validator, raw, section, filePath) {
100241
100938
  }
100242
100939
  throw new ConfigValidationError(`Unsupported validator shape for section "${section}"`);
100243
100940
  }
100244
- var MAX_INTERVAL_MINUTES = Math.floor(MAX_TIMEOUT_MS / 60000);
100941
+ function resolveBootstrapSchedulerTimeout(value) {
100942
+ try {
100943
+ return resolveExecutionTimeoutMs("bootstrap.scheduler", value);
100944
+ } catch {
100945
+ throw new ConfigValidationError(`bootstrap.scheduler.timeoutMs must be a positive integer or null; received ${String(value)}`);
100946
+ }
100947
+ }
100948
+ var MAX_INTERVAL_MINUTES = Math.floor(MAX_TIMEOUT_MS3 / 60000);
100245
100949
  function normalizeSchedulerJobs(raw) {
100246
100950
  if (raw === undefined || raw === null)
100247
100951
  return [];
@@ -100259,6 +100963,14 @@ function normalizeSchedulerJobs(raw) {
100259
100963
  const command = typeof entry.command === "string" ? entry.command.trim() : "";
100260
100964
  const cron = typeof entry.cron === "string" ? entry.cron.trim() : "";
100261
100965
  const interval = entry.intervalMinutes;
100966
+ const rawTimeout = entry.timeoutMs;
100967
+ let timeoutMs;
100968
+ if (rawTimeout !== undefined) {
100969
+ if (rawTimeout !== null && (typeof rawTimeout !== "number" || !Number.isInteger(rawTimeout) || rawTimeout <= 0)) {
100970
+ throw new ConfigValidationError(`bootstrap.scheduler.jobs.${index2}.timeoutMs must be a positive integer or null`);
100971
+ }
100972
+ timeoutMs = rawTimeout;
100973
+ }
100262
100974
  if (name === "") {
100263
100975
  throw new ConfigValidationError(`bootstrap.scheduler.jobs.${index2}.name must be a non-empty string`);
100264
100976
  }
@@ -100278,7 +100990,7 @@ function normalizeSchedulerJobs(raw) {
100278
100990
  if (typeof interval !== "number" || !Number.isInteger(interval) || interval < 1 || interval > MAX_INTERVAL_MINUTES) {
100279
100991
  throw new ConfigValidationError(`bootstrap.scheduler.jobs.${index2}.intervalMinutes must be an integer in 1..${MAX_INTERVAL_MINUTES}`);
100280
100992
  }
100281
- jobs.push({ name, command, intervalMinutes: interval });
100993
+ jobs.push({ name, command, intervalMinutes: interval, timeoutMs });
100282
100994
  } else {
100283
100995
  if (cron === "") {
100284
100996
  throw new ConfigValidationError(`bootstrap.scheduler.jobs.${index2}.cron must be a non-empty string`);
@@ -100289,7 +101001,7 @@ function normalizeSchedulerJobs(raw) {
100289
101001
  const detail = error51 instanceof Error ? error51.message : String(error51);
100290
101002
  throw new ConfigValidationError(`bootstrap.scheduler.jobs.${index2}.cron: ${detail}`);
100291
101003
  }
100292
- jobs.push({ name, command, cron });
101004
+ jobs.push({ name, command, cron, timeoutMs });
100293
101005
  }
100294
101006
  });
100295
101007
  return jobs;
@@ -100358,10 +101070,11 @@ async function runNodeApplication(options) {
100358
101070
  const schedulerConfig = {
100359
101071
  enabled: schedulerOpts.enabled === true,
100360
101072
  autoStart: schedulerOpts.autoStart,
100361
- jobs: normalizeSchedulerJobs(schedulerOpts.jobs)
101073
+ jobs: normalizeSchedulerJobs(schedulerOpts.jobs),
101074
+ timeoutMs: resolveBootstrapSchedulerTimeout(schedulerOpts.timeoutMs)
100362
101075
  };
100363
101076
  if (schedulerConfig.enabled) {
100364
- schedulerConfig.adapter = schedulerOpts.adapter ?? new NodeSchedulerAdapter;
101077
+ schedulerConfig.adapter = schedulerOpts.adapter ?? new NodeSchedulerAdapter({ timeoutMs: schedulerConfig.timeoutMs });
100365
101078
  if (schedulerOpts.entries) {
100366
101079
  schedulerConfig.entries = schedulerOpts.entries;
100367
101080
  }
@@ -104719,7 +105432,7 @@ function waitFail(context4, options, code, message) {
104719
105432
  // src/release-ops.ts
104720
105433
  init_dist5();
104721
105434
  import { existsSync as existsSync10, readdirSync as readdirSync7, readFileSync as readFileSync10 } from "fs";
104722
- import { join as join30 } from "path";
105435
+ import { join as join31 } from "path";
104723
105436
  var SEMVER = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z-.]+)?(?:\+[0-9A-Za-z-.]+)?$/;
104724
105437
  function releaseTag(config4, version3) {
104725
105438
  return `${config4.packageName}${config4.tagVersionSeparator}${version3}`;
@@ -104765,7 +105478,7 @@ async function readJson(path9) {
104765
105478
  function rootWorkspaceGlobs(repoRoot) {
104766
105479
  let root;
104767
105480
  try {
104768
- root = JSON.parse(readFileSync10(join30(repoRoot, "package.json"), "utf8"));
105481
+ root = JSON.parse(readFileSync10(join31(repoRoot, "package.json"), "utf8"));
104769
105482
  } catch {
104770
105483
  return [];
104771
105484
  }
@@ -104782,11 +105495,11 @@ function workspaceDirs(repoRoot) {
104782
105495
  if (!glob.endsWith("/*"))
104783
105496
  continue;
104784
105497
  const parent = glob.slice(0, -2);
104785
- const parentAbs = join30(repoRoot, parent);
105498
+ const parentAbs = join31(repoRoot, parent);
104786
105499
  if (!existsSync10(parentAbs))
104787
105500
  continue;
104788
105501
  for (const entry of readdirSync7(parentAbs, { withFileTypes: true })) {
104789
- if (entry.isDirectory() && existsSync10(join30(parentAbs, entry.name, "package.json"))) {
105502
+ if (entry.isDirectory() && existsSync10(join31(parentAbs, entry.name, "package.json"))) {
104790
105503
  dirs.push(`${parent}/${entry.name}`);
104791
105504
  }
104792
105505
  }
@@ -104794,14 +105507,14 @@ function workspaceDirs(repoRoot) {
104794
105507
  return dirs;
104795
105508
  }
104796
105509
  async function releaseContext(repoRoot) {
104797
- const rootManifest = await readJson(join30(repoRoot, "package.json"));
105510
+ const rootManifest = await readJson(join31(repoRoot, "package.json"));
104798
105511
  if (rootManifest === null) {
104799
105512
  throw new Error(`no package.json found at ${repoRoot} \u2014 builder operates on a package workspace root.`);
104800
105513
  }
104801
105514
  const rootName = typeof rootManifest.name === "string" ? rootManifest.name : "root";
104802
105515
  const workspaces2 = [];
104803
105516
  for (const dir of workspaceDirs(repoRoot)) {
104804
- const manifest = await readJson(join30(repoRoot, dir, "package.json"));
105517
+ const manifest = await readJson(join31(repoRoot, dir, "package.json"));
104805
105518
  if (manifest === null)
104806
105519
  continue;
104807
105520
  workspaces2.push({
@@ -104814,7 +105527,7 @@ async function releaseContext(repoRoot) {
104814
105527
  const shortName = (name) => name.split("/").pop() ?? name;
104815
105528
  const packages = new Map;
104816
105529
  for (const ws of workspaces2) {
104817
- const versionSourceFile = existsSync10(join30(repoRoot, ws.dir, "src", "config.ts")) ? `${ws.dir}/src/config.ts` : undefined;
105530
+ const versionSourceFile = existsSync10(join31(repoRoot, ws.dir, "src", "config.ts")) ? `${ws.dir}/src/config.ts` : undefined;
104818
105531
  packages.set(shortName(ws.name), {
104819
105532
  packageDir: ws.dir,
104820
105533
  packageName: ws.name,
@@ -104849,7 +105562,7 @@ async function updateWorkspacePins(ctx, pkgName, oldVersion, newVersion, output2
104849
105562
  const changed = [];
104850
105563
  for (const dir of workspaceDirs(ctx.repoRoot)) {
104851
105564
  const relPath = `${dir}/package.json`;
104852
- const manifest = await readJson(join30(ctx.repoRoot, relPath));
105565
+ const manifest = await readJson(join31(ctx.repoRoot, relPath));
104853
105566
  if (manifest === null)
104854
105567
  continue;
104855
105568
  let dirty = false;
@@ -104866,7 +105579,7 @@ async function updateWorkspacePins(ctx, pkgName, oldVersion, newVersion, output2
104866
105579
  }
104867
105580
  }
104868
105581
  if (dirty) {
104869
- await Bun.write(join30(ctx.repoRoot, relPath), `${JSON.stringify(manifest, null, 4)}
105582
+ await Bun.write(join31(ctx.repoRoot, relPath), `${JSON.stringify(manifest, null, 4)}
104870
105583
  `);
104871
105584
  output2.write(` \u21B3 ${relPath}: ${pkgName} workspace pin ${oldVersion} \u2192 ${newVersion}`);
104872
105585
  changed.push(relPath);
@@ -104875,7 +105588,7 @@ async function updateWorkspacePins(ctx, pkgName, oldVersion, newVersion, output2
104875
105588
  return changed;
104876
105589
  }
104877
105590
  async function updateVersionSourceFile(ctx, filePath, previous, next, output2) {
104878
- const absPath = join30(ctx.repoRoot, filePath);
105591
+ const absPath = join31(ctx.repoRoot, filePath);
104879
105592
  const content = await Bun.file(absPath).text();
104880
105593
  const updated = content.replace(/(binaryVersion:\s*['"])\d+\.\d+\.\d+(?:-[0-9A-Za-z-.]+)?(?:\+[0-9A-Za-z-.]+)?(['"])/, `$1${next}$2`);
104881
105594
  if (updated === content) {
@@ -104887,7 +105600,7 @@ async function updateVersionSourceFile(ctx, filePath, previous, next, output2) {
104887
105600
  return true;
104888
105601
  }
104889
105602
  async function syncMarketplaceAndPlugins(ctx, version3, staged, output2) {
104890
- const marketplacePath = join30(ctx.repoRoot, ".claude-plugin", "marketplace.json");
105603
+ const marketplacePath = join31(ctx.repoRoot, ".claude-plugin", "marketplace.json");
104891
105604
  const marketplaceFile = Bun.file(marketplacePath);
104892
105605
  if (!await marketplaceFile.exists())
104893
105606
  return;
@@ -104917,7 +105630,7 @@ async function syncMarketplaceAndPlugins(ctx, version3, staged, output2) {
104917
105630
  output2.write(`Bumped marketplace plugins to ${version3}`);
104918
105631
  }
104919
105632
  for (const entry of plugins) {
104920
- const pluginJsonPath = join30(ctx.repoRoot, entry.source, "plugin.json");
105633
+ const pluginJsonPath = join31(ctx.repoRoot, entry.source, "plugin.json");
104921
105634
  const pluginJson = await readJson(pluginJsonPath);
104922
105635
  if (pluginJson === null) {
104923
105636
  output2.write(` \u26A0 plugin.json not found at ${entry.source}/plugin.json \u2014 skipping`);
@@ -104966,7 +105679,7 @@ async function bumpVersion(ctx, config4, version3, options, output2) {
104966
105679
  if (await npmViewVersion(ctx.repoRoot, config4.packageName, version3)) {
104967
105680
  throw new Error(`${config4.packageName}@${version3} is already published on npm. Use a new version.`);
104968
105681
  }
104969
- const manifestPath = join30(ctx.repoRoot, config4.packageDir, "package.json");
105682
+ const manifestPath = join31(ctx.repoRoot, config4.packageDir, "package.json");
104970
105683
  const manifest = await readJson(manifestPath);
104971
105684
  if (manifest === null)
104972
105685
  throw new Error(`missing or malformed manifest at ${config4.packageDir}/package.json`);
@@ -104983,7 +105696,7 @@ async function bumpVersion(ctx, config4, version3, options, output2) {
104983
105696
  }
104984
105697
  staged.push(...await updateWorkspacePins(ctx, config4.packageName, previous, version3, output2));
104985
105698
  await syncMarketplaceAndPlugins(ctx, version3, staged, output2);
104986
- const lockPath = join30(ctx.repoRoot, "bun.lock");
105699
+ const lockPath = join31(ctx.repoRoot, "bun.lock");
104987
105700
  if (existsSync10(lockPath) && Bun.file(lockPath).size > 0)
104988
105701
  staged.push("bun.lock");
104989
105702
  await git2(ctx.repoRoot, ["add", ...staged]);
@@ -105039,7 +105752,7 @@ async function bumpAll(ctx, version3, options, output2) {
105039
105752
  }
105040
105753
  const staged = [];
105041
105754
  for (const config4 of configs) {
105042
- const manifestPath = join30(ctx.repoRoot, config4.packageDir, "package.json");
105755
+ const manifestPath = join31(ctx.repoRoot, config4.packageDir, "package.json");
105043
105756
  const manifest = await readJson(manifestPath);
105044
105757
  if (manifest === null) {
105045
105758
  throw new Error(`missing or malformed manifest at ${config4.packageDir}/package.json`);
@@ -105058,7 +105771,7 @@ async function bumpAll(ctx, version3, options, output2) {
105058
105771
  staged.push(...await updateWorkspacePins(ctx, config4.packageName, previous, version3, output2));
105059
105772
  }
105060
105773
  await syncMarketplaceAndPlugins(ctx, version3, staged, output2);
105061
- const lockPath = join30(ctx.repoRoot, "bun.lock");
105774
+ const lockPath = join31(ctx.repoRoot, "bun.lock");
105062
105775
  if (existsSync10(lockPath) && Bun.file(lockPath).size > 0)
105063
105776
  staged.push("bun.lock");
105064
105777
  await git2(ctx.repoRoot, ["add", ...staged]);
@@ -105721,13 +106434,18 @@ import { createRequire } from "module";
105721
106434
  var CLI_CONFIG = {
105722
106435
  binaryName: "spur",
105723
106436
  binaryLabel: "spur",
105724
- binaryVersion: "0.3.77",
106437
+ binaryVersion: "0.3.80",
105725
106438
  configDir: ".spur",
105726
106439
  configFile: ".spur/config.yaml",
105727
106440
  databaseFile: ".spur/spur.db"
105728
106441
  };
105729
106442
 
105730
106443
  // src/commands/history.ts
106444
+ function resolveCliSourceTimeout(explicit, env) {
106445
+ if (explicit !== undefined)
106446
+ return parseTimeoutInput(explicit);
106447
+ return normalizeLegacyTimeoutMs(env[HISTORY_SOURCE_TIMEOUT_ENV], DEFAULT_SOURCE_TIMEOUT_MS);
106448
+ }
105731
106449
  async function resolveImportProvenance(fs3) {
105732
106450
  const binary = process.argv[1] ?? "unknown";
105733
106451
  let importer = "unknown";
@@ -105768,7 +106486,7 @@ function registerHistoryCommand(program2, context4) {
105768
106486
  cwd: context4.cwd
105769
106487
  });
105770
106488
  };
105771
- noun.command("import").description("Import agent conversation JSONL. `--source all` fans out across all sources with " + "per-source failure isolation (task 0470). A single source is the n=1 case of " + "the same contract \u2014 never two import paths.").option(...SHARED_OPTIONS.sourceHistory, "all").option(...SHARED_OPTIONS.fileHistoryJsonl).option("--root <path>", "Scan a history root").option(...SHARED_OPTIONS.modeHistory).option(...SHARED_OPTIONS.dryRunHistoryScan).option("--source-timeout <ms>", "Per-source timeout in milliseconds (default 600000 = 10 min)", "600000").option(...SHARED_OPTIONS.jsonSupported).option(...SHARED_OPTIONS.jsonEnvelope).action(async (options) => {
106489
+ noun.command("import").description("Import agent conversation JSONL. `--source all` fans out across all sources with " + "per-source failure isolation (task 0470). A single source is the n=1 case of " + "the same contract \u2014 never two import paths.").option(...SHARED_OPTIONS.sourceHistory, "all").option(...SHARED_OPTIONS.fileHistoryJsonl).option("--root <path>", "Scan a history root").option(...SHARED_OPTIONS.modeHistory).option(...SHARED_OPTIONS.dryRunHistoryScan).option("--source-timeout <ms|none>", `Per-source timeout in milliseconds or 'none' (default ${DEFAULT_SOURCE_TIMEOUT_MS} = 10 min)`).option(...SHARED_OPTIONS.jsonSupported).option(...SHARED_OPTIONS.jsonEnvelope).action(async (options) => {
105772
106490
  const source = options.source ?? "all";
105773
106491
  if (options.file && source === "all") {
105774
106492
  context4.output.write(options.json ? toEnvelopeJson({ status: "error", message: '--file requires a single --source, not "all".' }, {
@@ -105782,7 +106500,18 @@ function registerHistoryCommand(program2, context4) {
105782
106500
  context4.setExitCode(1);
105783
106501
  return;
105784
106502
  }
105785
- const sourceTimeout = Number.parseInt(options.sourceTimeout ?? "600000", 10) || 600000;
106503
+ let sourceTimeout;
106504
+ try {
106505
+ sourceTimeout = resolveCliSourceTimeout(options.sourceTimeout, process.env);
106506
+ } catch (e) {
106507
+ const timeoutMsg = `spur history import: ${e instanceof Error ? e.message : String(e)}`;
106508
+ context4.output.write(options.json ? toEnvelopeJson({ status: "error", message: timeoutMsg }, {
106509
+ enveloped: options.jsonEnvelope,
106510
+ error: { code: "INTERNAL_ERROR", message: timeoutMsg, details: { cliCode: "usage" } }
106511
+ }) : timeoutMsg);
106512
+ context4.setExitCode(1);
106513
+ return;
106514
+ }
105786
106515
  const mode = options.mode ?? (options.file ? "force-file" : "incremental");
105787
106516
  if (mode !== "full" && mode !== "incremental" && mode !== "force-file") {
105788
106517
  const modeMsg = `Invalid history import mode "${mode}". Expected one of: full, incremental, force-file`;
@@ -105924,9 +106653,20 @@ ${formatFanOutResult(fanOut)}`);
105924
106653
  context4.setExitCode(1);
105925
106654
  }
105926
106655
  });
105927
- noun.command("daily").description("Run-once daily pipeline: import-all (fan-out, per-source isolation) \u2192 analyze \u2192 write " + "artifact \u2192 prune reports older than 90 days (task 0470 R6). Import uses checkpoint " + "resume, so a missed night self-heals on the next run with no gap and no double-count.").option("--since <iso>", "Inclusive lower bound on message timestamp for the report (not the import)").option("--until <iso>", "Inclusive upper bound on message timestamp for the report").option("--source-timeout <ms>", "Per-source import timeout in milliseconds (default 600000 = 10 min)", "600000").option("--root <path>", "History root override (default: per-source platform dir)").option(...SHARED_OPTIONS.jsonDaily).option(...SHARED_OPTIONS.jsonEnvelope).option("--mode <name>", "Render the artifact as a .md sidecar in this mode after analyze (e.g. forensics)").action(async (options) => {
106656
+ noun.command("daily").description("Run-once daily pipeline: import-all (fan-out, per-source isolation) \u2192 analyze \u2192 write " + "artifact \u2192 prune reports older than 90 days (task 0470 R6). Import uses checkpoint " + "resume, so a missed night self-heals on the next run with no gap and no double-count.").option("--since <iso>", "Inclusive lower bound on message timestamp for the report (not the import)").option("--until <iso>", "Inclusive upper bound on message timestamp for the report").option("--source-timeout <ms|none>", `Per-source import timeout in milliseconds or 'none' (default ${DEFAULT_SOURCE_TIMEOUT_MS} = 10 min)`).option("--root <path>", "History root override (default: per-source platform dir)").option(...SHARED_OPTIONS.jsonDaily).option(...SHARED_OPTIONS.jsonEnvelope).option("--mode <name>", "Render the artifact as a .md sidecar in this mode after analyze (e.g. forensics)").action(async (options) => {
106657
+ let sourceTimeout;
106658
+ try {
106659
+ sourceTimeout = resolveCliSourceTimeout(options.sourceTimeout, process.env);
106660
+ } catch (e) {
106661
+ const timeoutMsg = `spur history daily: ${e instanceof Error ? e.message : String(e)}`;
106662
+ context4.output.write(options.json ? toEnvelopeJson({ error: timeoutMsg }, {
106663
+ enveloped: options.jsonEnvelope,
106664
+ error: { code: "INTERNAL_ERROR", message: timeoutMsg }
106665
+ }) : timeoutMsg);
106666
+ context4.setExitCode(1);
106667
+ return;
106668
+ }
105928
106669
  const svc = await makeService2();
105929
- const sourceTimeout = Number.parseInt(options.sourceTimeout ?? "600000", 10) || 600000;
105930
106670
  let refresh;
105931
106671
  try {
105932
106672
  refresh = parseHistoryRefreshContext(process.env[HISTORY_REFRESH_CONTEXT_ENV]);
@@ -106106,7 +106846,7 @@ init_loader();
106106
106846
  init_src2();
106107
106847
  init_dist10();
106108
106848
  import { homedir as homedir9 } from "os";
106109
- import { join as join31, resolve as resolve17 } from "path";
106849
+ import { join as join32, resolve as resolve17 } from "path";
106110
106850
 
106111
106851
  // src/config/scaffold-manifest.ts
106112
106852
  var SCAFFOLD_MANIFEST = [
@@ -106135,8 +106875,8 @@ var SCAFFOLD_MANIFEST = [
106135
106875
  ];
106136
106876
 
106137
106877
  // src/commands/init.ts
106138
- var GLOBAL_CONFIG_DIR = join31(".config", "spur");
106139
- var GLOBAL_RULES_DIR2 = join31(GLOBAL_CONFIG_DIR, "rules");
106878
+ var GLOBAL_CONFIG_DIR = join32(".config", "spur");
106879
+ var GLOBAL_RULES_DIR2 = join32(GLOBAL_CONFIG_DIR, "rules");
106140
106880
  var GLOBAL_CONFIG_EXAMPLE = BUNDLED_GLOBAL_CONFIG;
106141
106881
  var GLOBAL_CONFIG_FILE2 = "config.yaml";
106142
106882
  var INDEXED_CONTEXT_MARKER = "## Indexed context";
@@ -106167,7 +106907,7 @@ If \`.spur/context/\` is absent, proceed normally. Never block work on its absen
106167
106907
  `;
106168
106908
  function globalRulesRoot(context4) {
106169
106909
  const override = context4.env.SPUR_GLOBAL_RULES_DIR;
106170
- return override !== undefined && override.length > 0 ? resolve17(context4.cwd, override) : join31(homedir9(), GLOBAL_RULES_DIR2);
106910
+ return override !== undefined && override.length > 0 ? resolve17(context4.cwd, override) : join32(homedir9(), GLOBAL_RULES_DIR2);
106171
106911
  }
106172
106912
  async function writeIfNew(context4, path9, content, force, result) {
106173
106913
  if (!force && await context4.fs.exists(path9)) {
@@ -106184,18 +106924,18 @@ async function seedGlobalRules(context4) {
106184
106924
  const target = globalRulesRoot(context4);
106185
106925
  let written = 0;
106186
106926
  for (const relPath of await listBundledRuleFiles()) {
106187
- const destination = join31(target, relPath);
106927
+ const destination = join32(target, relPath);
106188
106928
  if (await context4.fs.exists(destination))
106189
106929
  continue;
106190
- await context4.fs.ensureDir(join31(target, ...relPath.split("/").slice(0, -1)));
106191
- await context4.fs.writeFile(destination, await context4.fs.readFile(join31(source, relPath)));
106930
+ await context4.fs.ensureDir(join32(target, ...relPath.split("/").slice(0, -1)));
106931
+ await context4.fs.writeFile(destination, await context4.fs.readFile(join32(source, relPath)));
106192
106932
  written += 1;
106193
106933
  }
106194
106934
  return written;
106195
106935
  }
106196
106936
  function resolveGlobalConfigDir(context4) {
106197
106937
  const globalOverride = context4.env.SPUR_GLOBAL_RULES_DIR;
106198
- return globalOverride !== undefined && globalOverride.length > 0 ? resolve17(context4.cwd, globalOverride) : join31(homedir9(), GLOBAL_CONFIG_DIR);
106938
+ return globalOverride !== undefined && globalOverride.length > 0 ? resolve17(context4.cwd, globalOverride) : join32(homedir9(), GLOBAL_CONFIG_DIR);
106199
106939
  }
106200
106940
  async function seedGlobalConfig(context4) {
106201
106941
  const source = bundledConfigRoot();
@@ -106206,15 +106946,15 @@ async function seedGlobalConfig(context4) {
106206
106946
  for (const relPath of listBundledConfigFiles()) {
106207
106947
  if (relPath === GLOBAL_CONFIG_EXAMPLE)
106208
106948
  continue;
106209
- const destination = join31(target, relPath);
106949
+ const destination = join32(target, relPath);
106210
106950
  if (await context4.fs.exists(destination))
106211
106951
  continue;
106212
- await context4.fs.ensureDir(join31(target, ...relPath.split("/").slice(0, -1)));
106213
- await context4.fs.writeFile(destination, await context4.fs.readFile(join31(source, relPath)));
106952
+ await context4.fs.ensureDir(join32(target, ...relPath.split("/").slice(0, -1)));
106953
+ await context4.fs.writeFile(destination, await context4.fs.readFile(join32(source, relPath)));
106214
106954
  written += 1;
106215
106955
  }
106216
- const examplePath = join31(source, GLOBAL_CONFIG_EXAMPLE);
106217
- const globalConfigPath = join31(target, GLOBAL_CONFIG_FILE2);
106956
+ const examplePath = join32(source, GLOBAL_CONFIG_EXAMPLE);
106957
+ const globalConfigPath = join32(target, GLOBAL_CONFIG_FILE2);
106218
106958
  if (await context4.fs.exists(examplePath) && !await context4.fs.exists(globalConfigPath)) {
106219
106959
  await context4.fs.ensureDir(target);
106220
106960
  await context4.fs.writeFile(globalConfigPath, await context4.fs.readFile(examplePath));
@@ -106229,10 +106969,10 @@ function registerInitCommand(program2, context4, options = {}) {
106229
106969
  const minimal = options2.minimal === true;
106230
106970
  const adoptGlobal = options2.adoptGlobalConfig === true;
106231
106971
  const projectName = options2.name ?? "default";
106232
- const configPath = join31(context4.cwd, CLI_CONFIG.configFile);
106972
+ const configPath = join32(context4.cwd, CLI_CONFIG.configFile);
106233
106973
  const result = { created: [], skipped: [] };
106234
106974
  const globalConfigDir = resolveGlobalConfigDir(context4);
106235
- const globalConfigPath = join31(globalConfigDir, GLOBAL_CONFIG_FILE2);
106975
+ const globalConfigPath = join32(globalConfigDir, GLOBAL_CONFIG_FILE2);
106236
106976
  const globalConfigExists = await context4.fs.exists(globalConfigPath);
106237
106977
  let misplacedKeys = [];
106238
106978
  if (globalConfigExists) {
@@ -106245,20 +106985,20 @@ function registerInitCommand(program2, context4, options = {}) {
106245
106985
  const configRoot = bundledConfigRoot();
106246
106986
  if (configRoot !== null && !minimal) {
106247
106987
  for (const relPath of listBundledProjectSeedFiles()) {
106248
- const sourcePath = join31(configRoot, relPath);
106988
+ const sourcePath = join32(configRoot, relPath);
106249
106989
  if (!await context4.fs.exists(sourcePath))
106250
106990
  continue;
106251
- const targetPath = join31(context4.cwd, CLI_CONFIG.configDir, relPath);
106252
- await context4.fs.ensureDir(join31(targetPath, ".."));
106991
+ const targetPath = join32(context4.cwd, CLI_CONFIG.configDir, relPath);
106992
+ await context4.fs.ensureDir(join32(targetPath, ".."));
106253
106993
  await writeIfNew(context4, targetPath, await context4.fs.readFile(sourcePath), false, result);
106254
106994
  }
106255
106995
  for (const entry of SCAFFOLD_MANIFEST) {
106256
- const sourcePath = join31(configRoot, entry.source);
106996
+ const sourcePath = join32(configRoot, entry.source);
106257
106997
  if (!await context4.fs.exists(sourcePath))
106258
106998
  continue;
106259
- const baseDir = entry.root === true ? context4.cwd : join31(context4.cwd, CLI_CONFIG.configDir);
106260
- const targetPath = join31(baseDir, entry.target);
106261
- await context4.fs.ensureDir(join31(targetPath, ".."));
106999
+ const baseDir = entry.root === true ? context4.cwd : join32(context4.cwd, CLI_CONFIG.configDir);
107000
+ const targetPath = join32(baseDir, entry.target);
107001
+ await context4.fs.ensureDir(join32(targetPath, ".."));
106262
107002
  const entryForce = false;
106263
107003
  let body = await context4.fs.readFile(sourcePath);
106264
107004
  if (entry.target === "AGENTS.md") {
@@ -106275,7 +107015,7 @@ function registerInitCommand(program2, context4, options = {}) {
106275
107015
  const backupPath = `${globalConfigPath}.bak-${new Date().toISOString().replace(/[:.]/g, "-")}`;
106276
107016
  await context4.fs.writeFile(backupPath, await context4.fs.readFile(globalConfigPath));
106277
107017
  const bundledRoot = bundledConfigRoot();
106278
- const examplePath = bundledRoot !== null ? join31(bundledRoot, GLOBAL_CONFIG_EXAMPLE) : null;
107018
+ const examplePath = bundledRoot !== null ? join32(bundledRoot, GLOBAL_CONFIG_EXAMPLE) : null;
106279
107019
  if (examplePath !== null && await context4.fs.exists(examplePath)) {
106280
107020
  await context4.fs.writeFile(globalConfigPath, await context4.fs.readFile(examplePath));
106281
107021
  adoptedGlobal = true;
@@ -106345,13 +107085,13 @@ function registerInitCommand(program2, context4, options = {}) {
106345
107085
  ].join(`
106346
107086
  `)}
106347
107087
  `;
106348
- await context4.fs.ensureDir(join31(context4.cwd, CLI_CONFIG.configDir));
107088
+ await context4.fs.ensureDir(join32(context4.cwd, CLI_CONFIG.configDir));
106349
107089
  await context4.fs.writeFile(configPath, configYaml);
106350
107090
  result.created.push(configPath);
106351
- const agentsDir = join31(context4.cwd, CLI_CONFIG.configDir, "agents");
107091
+ const agentsDir = join32(context4.cwd, CLI_CONFIG.configDir, "agents");
106352
107092
  await context4.fs.ensureDir(agentsDir);
106353
- await writeIfNew(context4, join31(agentsDir, ".gitkeep"), "", force, result);
106354
- const gitignorePath = join31(context4.cwd, ".gitignore");
107093
+ await writeIfNew(context4, join32(agentsDir, ".gitkeep"), "", force, result);
107094
+ const gitignorePath = join32(context4.cwd, ".gitignore");
106355
107095
  const contextEntry = ".spur/context/";
106356
107096
  if (await context4.fs.exists(gitignorePath)) {
106357
107097
  const existing = await context4.fs.readFile(gitignorePath);
@@ -106371,20 +107111,20 @@ ${contextEntry}
106371
107111
  const configRoot = bundledConfigRoot();
106372
107112
  if (configRoot !== null) {
106373
107113
  for (const relPath of listBundledProjectSeedFiles()) {
106374
- const sourcePath = join31(configRoot, relPath);
107114
+ const sourcePath = join32(configRoot, relPath);
106375
107115
  if (!await context4.fs.exists(sourcePath))
106376
107116
  continue;
106377
- const targetPath = join31(context4.cwd, CLI_CONFIG.configDir, relPath);
106378
- await context4.fs.ensureDir(join31(targetPath, ".."));
107117
+ const targetPath = join32(context4.cwd, CLI_CONFIG.configDir, relPath);
107118
+ await context4.fs.ensureDir(join32(targetPath, ".."));
106379
107119
  await writeIfNew(context4, targetPath, await context4.fs.readFile(sourcePath), force, result);
106380
107120
  }
106381
107121
  for (const entry of SCAFFOLD_MANIFEST) {
106382
- const sourcePath = join31(configRoot, entry.source);
107122
+ const sourcePath = join32(configRoot, entry.source);
106383
107123
  if (!await context4.fs.exists(sourcePath))
106384
107124
  continue;
106385
- const baseDir = entry.root === true ? context4.cwd : join31(context4.cwd, CLI_CONFIG.configDir);
106386
- const targetPath = join31(baseDir, entry.target);
106387
- await context4.fs.ensureDir(join31(targetPath, ".."));
107125
+ const baseDir = entry.root === true ? context4.cwd : join32(context4.cwd, CLI_CONFIG.configDir);
107126
+ const targetPath = join32(baseDir, entry.target);
107127
+ await context4.fs.ensureDir(join32(targetPath, ".."));
106388
107128
  const entryForce = entry.preserve === true ? false : force;
106389
107129
  let body = await context4.fs.readFile(sourcePath);
106390
107130
  if (entry.target === "AGENTS.md") {
@@ -106397,7 +107137,7 @@ ${contextEntry}
106397
107137
  }
106398
107138
  }
106399
107139
  }
106400
- const agentsMdPath = join31(context4.cwd, "AGENTS.md");
107140
+ const agentsMdPath = join32(context4.cwd, "AGENTS.md");
106401
107141
  if (await context4.fs.exists(agentsMdPath)) {
106402
107142
  const existing = await context4.fs.readFile(agentsMdPath);
106403
107143
  if (!existing.includes(INDEXED_CONTEXT_MARKER)) {
@@ -106438,11 +107178,11 @@ ${contextEntry}
106438
107178
  // src/commands/maintain.ts
106439
107179
  init_src();
106440
107180
  init_src2();
106441
- import { join as join32 } from "path";
107181
+ import { join as join33 } from "path";
106442
107182
  function registerMaintainCommand(program2, context4, options = {}) {
106443
107183
  program2.command("maintain", { hidden: options.hidden === true }).summary("run database maintenance (optimize, WAL checkpoint truncation, optional VACUUM)").description("Run database maintenance: PRAGMA optimize, WAL checkpoint truncation; optionally VACUUM compaction.").option("--vacuum", "Run VACUUM defragmentation and page compaction", false).option(...SHARED_OPTIONS.jsonSupported).option(...SHARED_OPTIONS.jsonEnvelope).action(async (options2) => {
106444
107184
  const db2 = await context4.getDb();
106445
- const dbPath = context4.env.DATABASE_URL ?? join32(context4.cwd, DEFAULT_DATABASE_URL);
107185
+ const dbPath = context4.env.DATABASE_URL ?? join33(context4.cwd, DEFAULT_DATABASE_URL);
106446
107186
  const result = await maintainDatabase(db2, {
106447
107187
  vacuum: options2.vacuum === true,
106448
107188
  dbPath: dbPath.startsWith(":memory:") ? undefined : dbPath
@@ -106842,10 +107582,10 @@ async function waitForPendingDrain(agentService, teamService, pin, timeoutMs, si
106842
107582
 
106843
107583
  // src/commands/migrate.ts
106844
107584
  init_src2();
106845
- import { join as join33 } from "path";
107585
+ import { join as join34 } from "path";
106846
107586
  function registerMigrateCommand(program2, context4, options = {}) {
106847
107587
  program2.command("migrate", { hidden: options.hidden === true }).summary("apply CLI-owned schema migrations").option(...SHARED_OPTIONS.json).option(...SHARED_OPTIONS.jsonEnvelope).action(async (options2) => {
106848
- const migrations = await loadSqlMigrations(join33(context4.cwd, "drizzle")).catch(() => {
107588
+ const migrations = await loadSqlMigrations(join34(context4.cwd, "drizzle")).catch(() => {
106849
107589
  return;
106850
107590
  });
106851
107591
  const applied = await applyCliMigrations(await context4.getDb(), migrations);
@@ -107285,7 +108025,7 @@ function formatTraceDetail(detail) {
107285
108025
  // src/commands/serve.ts
107286
108026
  init_src();
107287
108027
  import { statSync as statSync7 } from "fs";
107288
- import { join as join37, resolve as resolve19 } from "path";
108028
+ import { join as join38, resolve as resolve19 } from "path";
107289
108029
 
107290
108030
  // ../server/src/index.ts
107291
108031
  init_src();
@@ -107295,11 +108035,11 @@ init_src();
107295
108035
  init_loader();
107296
108036
  init_src2();
107297
108037
  await init_src3();
107298
- import { basename as basename13, dirname as dirname22, isAbsolute as isAbsolute8, join as join36 } from "path";
108038
+ import { basename as basename13, dirname as dirname22, isAbsolute as isAbsolute8, join as join37 } from "path";
107299
108039
  init_dist5();
107300
108040
 
107301
108041
  // ../server/src/bootstrap.ts
107302
- import { join as join34 } from "path";
108042
+ import { join as join35 } from "path";
107303
108043
 
107304
108044
  // ../../node_modules/.bun/radash@12.1.1/node_modules/radash/dist/esm/async.mjs
107305
108045
  var guard = (func, shouldGuard) => {
@@ -117066,7 +117806,7 @@ function createApp(appRt, opts) {
117066
117806
  app.use("*", async (c3, next) => {
117067
117807
  const pathname = c3.req.path === "/" ? "/index.html" : c3.req.path;
117068
117808
  try {
117069
- const file2 = Bun.file(join34(webDistPath, pathname));
117809
+ const file2 = Bun.file(join35(webDistPath, pathname));
117070
117810
  if (await file2.exists()) {
117071
117811
  const headers = new Headers({ "content-type": file2.type });
117072
117812
  return new Response(file2.stream(), { headers });
@@ -117079,7 +117819,7 @@ function createApp(appRt, opts) {
117079
117819
  return c3.json({ error: "Not Found" }, 404);
117080
117820
  }
117081
117821
  try {
117082
- const indexFile = Bun.file(join34(webDistPath, "index.html"));
117822
+ const indexFile = Bun.file(join35(webDistPath, "index.html"));
117083
117823
  if (await indexFile.exists()) {
117084
117824
  return new Response(indexFile.stream(), {
117085
117825
  headers: { "content-type": "text/html; charset=utf-8" }
@@ -117101,7 +117841,7 @@ init_loader();
117101
117841
  init_src2();
117102
117842
  init_dist5();
117103
117843
  await init_src3();
117104
- import { dirname as dirname21, join as join35 } from "path";
117844
+ import { dirname as dirname21, join as join36 } from "path";
117105
117845
  var NOOP_OUTPUT = { write: (_s) => {}, error: (_s) => {} };
117106
117846
 
117107
117847
  class LazyPlanningEventEmitter {
@@ -117137,7 +117877,7 @@ class NotConfiguredError extends Error {
117137
117877
  function createServerContext(appRt, options) {
117138
117878
  const cwd = options.cwd;
117139
117879
  const fs3 = options.fs;
117140
- const dbUrl = options.dbUrl ?? join35(cwd, DEFAULT_DATABASE_URL);
117880
+ const dbUrl = options.dbUrl ?? join36(cwd, DEFAULT_DATABASE_URL);
117141
117881
  const eventsBus = options.eventsBus ?? appRt.events;
117142
117882
  const jobQueueEnabled = options.jobQueueEnabled ?? false;
117143
117883
  const eventProjectContext = systemEventProjectContext(cwd);
@@ -117436,7 +118176,7 @@ async function loadServerSectionMatrix(projectRoot) {
117436
118176
  }
117437
118177
  const root = bundledConfigRoot();
117438
118178
  if (root !== null) {
117439
- const matrixPath = join36(root, "tasks", "section-matrix.yaml");
118179
+ const matrixPath = join37(root, "tasks", "section-matrix.yaml");
117440
118180
  if (await nodeFs.exists(matrixPath)) {
117441
118181
  return await loadStructuredSpurConfig(matrixPath, {
117442
118182
  validateJsonSchema: false
@@ -117445,7 +118185,7 @@ async function loadServerSectionMatrix(projectRoot) {
117445
118185
  }
117446
118186
  throw new Error(`no canonical section-matrix found for task creation (F92 R1); tried:
117447
118187
  ` + ` - ${localPath}
117448
- ` + (root !== null ? ` - ${join36(root, "tasks", "section-matrix.yaml")}
118188
+ ` + (root !== null ? ` - ${join37(root, "tasks", "section-matrix.yaml")}
117449
118189
  ` : "") + "copy/generate section-matrix.yaml from the canonical build-time matrix asset (repo `config` `tasks` tree) into one of those paths");
117450
118190
  }
117451
118191
  var SYSTEM_EVENTS_PRUNE_JOB = "system-events-prune";
@@ -117454,6 +118194,7 @@ var TASK_ACTION_JOB = "task-action";
117454
118194
  var FEATURE_ACTION_JOB = "feature-action";
117455
118195
  var SYSTEM_EVENTS_PRUNE_CRON = "300000";
117456
118196
  var SMOKE_CRON = "600000";
118197
+ var JOB_WORKER_START_DELAY_MS = 30000;
117457
118198
  var defaultDeps = {
117458
118199
  serverBootstrapConfig,
117459
118200
  runNodeApplication,
@@ -117464,8 +118205,8 @@ var defaultDeps = {
117464
118205
  resolveConfigFile
117465
118206
  };
117466
118207
  function registerSchedulerEntries(scheduler3, ctx, jobs = [], options = {}) {
117467
- const timeoutMs = options.timeoutMs ?? resolveSchedulerCustomTimeoutMs(process.env);
117468
118208
  const env = options.env ?? process.env;
118209
+ const timeoutMs = options.timeoutMs !== undefined ? options.timeoutMs : resolveSchedulerCustomTimeoutMs(env);
117469
118210
  const registrations = [];
117470
118211
  const now2 = Date.now();
117471
118212
  const register = (cron, name, action) => {
@@ -117509,16 +118250,17 @@ function registerSchedulerEntries(scheduler3, ctx, jobs = [], options = {}) {
117509
118250
  });
117510
118251
  for (const job of jobs) {
117511
118252
  const schedule = job.cron ?? String(job.intervalMinutes * 60000);
117512
- const sweepThresholdMs = resolveSchedulerJobTimeoutMs(job.name, env, timeoutMs) + resolveKillGraceMs(env);
118253
+ const jobPolicy = resolveSchedulerJobTimeoutMs(job.name, env, timeoutMs);
118254
+ const sweepThresholdMs = jobPolicy === null ? null : jobPolicy + resolveKillGraceMs3(env);
117513
118255
  register(schedule, `${SCHEDULER_CUSTOM_JOB}:${job.name}`, async () => {
117514
118256
  const queue = await ctx.jobQueue();
117515
118257
  const db2 = await ctx.getDb();
117516
118258
  const active = await findActiveSchedulerCustomJob(db2, job.name);
117517
118259
  if (active !== undefined) {
117518
118260
  const now3 = Date.now();
117519
- const stale = active.status === "processing" && now3 - (active.processingAt ?? active.updatedAt) > sweepThresholdMs;
118261
+ const stale = sweepThresholdMs !== null && active.status === "processing" && now3 - (active.processingAt ?? active.updatedAt) > sweepThresholdMs;
117520
118262
  if (stale) {
117521
- const reason = `watchdog: processing exceeded ${sweepThresholdMs}ms`;
118263
+ const reason = `age-sweep: processing exceeded ${sweepThresholdMs}ms`;
117522
118264
  const swept = await failStaleSchedulerCustomJob(db2, active.id, now3, reason);
117523
118265
  if (swept) {
117524
118266
  ctx.eventBus().emit("scheduler.job.executed", {
@@ -117661,15 +118403,15 @@ async function handleFeatureActionJob(ctx, env, payload) {
117661
118403
  await runFeatureActionJob(ctx, env, payload);
117662
118404
  }
117663
118405
  async function resolveWebDistPath(configuredPath, projectRoot = process.cwd()) {
117664
- const candidates = configuredPath && configuredPath.trim() !== "" ? [isAbsolute8(configuredPath) ? configuredPath : join36(projectRoot, configuredPath)] : [
117665
- join36(projectRoot, "dist/web"),
117666
- join36(import.meta.dir, "web"),
117667
- join36(dirname22(process.execPath), "web"),
117668
- join36(dirname22(process.execPath), "../web"),
117669
- join36(import.meta.dir, "../../../dist/web")
118406
+ const candidates = configuredPath && configuredPath.trim() !== "" ? [isAbsolute8(configuredPath) ? configuredPath : join37(projectRoot, configuredPath)] : [
118407
+ join37(projectRoot, "dist/web"),
118408
+ join37(import.meta.dir, "web"),
118409
+ join37(dirname22(process.execPath), "web"),
118410
+ join37(dirname22(process.execPath), "../web"),
118411
+ join37(import.meta.dir, "../../../dist/web")
117670
118412
  ];
117671
118413
  for (const candidate of candidates) {
117672
- if (await Bun.file(join36(candidate, "index.html")).exists()) {
118414
+ if (await Bun.file(join37(candidate, "index.html")).exists()) {
117673
118415
  return candidate;
117674
118416
  }
117675
118417
  }
@@ -117708,6 +118450,8 @@ async function startServer(options, deps = defaultDeps) {
117708
118450
  ...spurConfig !== undefined ? { spurConfig } : {}
117709
118451
  });
117710
118452
  let jobWorker;
118453
+ let jobProcessRegistry;
118454
+ let jobWorkerStartTimer;
117711
118455
  let quotaConsumer;
117712
118456
  try {
117713
118457
  quotaConsumer = startAgentQuotaUpdateConsumer(ctx.eventBus(), {
@@ -117774,7 +118518,10 @@ async function startServer(options, deps = defaultDeps) {
117774
118518
  }
117775
118519
  const app = deps.createApp(appRt, { fs: fs3, ctx });
117776
118520
  const schedulerCustomTimeoutMs = resolveSchedulerCustomTimeoutMs(env);
117777
- if (bootConfig.jobqueue.enabled) {
118521
+ const startJobQueueWorker = async () => {
118522
+ if (jobWorker !== undefined)
118523
+ return;
118524
+ jobProcessRegistry = createInMemoryProcessRegistry2();
117778
118525
  const registry2 = new JobHandlerRegistry;
117779
118526
  const retentionQuotas = resolveRetentionQuotas(bootConfig.events.retention);
117780
118527
  registry2.register(SYSTEM_EVENTS_PRUNE_JOB, async () => {
@@ -117784,7 +118531,7 @@ async function startServer(options, deps = defaultDeps) {
117784
118531
  registry2.register(SMOKE_JOB, async () => {});
117785
118532
  registry2.register(TASK_ACTION_JOB, (payload) => handleTaskActionJob(ctx, env, payload));
117786
118533
  registry2.register(FEATURE_ACTION_JOB, (payload) => handleFeatureActionJob(ctx, env, payload));
117787
- const childExecutor = new NodeProcessExecutor3;
118534
+ const childExecutor = new NodeProcessExecutor3({ registry: jobProcessRegistry });
117788
118535
  registry2.register(HISTORY_REFRESH_JOB, async (job) => {
117789
118536
  await emitQueueJobStarted(ctx, job);
117790
118537
  return handleHistoryRefreshJob({
@@ -117803,7 +118550,8 @@ async function startServer(options, deps = defaultDeps) {
117803
118550
  resolveTimeoutMs: (name) => resolveSchedulerJobTimeoutMs(name, env, schedulerCustomTimeoutMs)
117804
118551
  }, job);
117805
118552
  });
117806
- const orphanCount = await failOrphanedProcessingJobs(await ctx.getDb(), Date.now());
118553
+ const unlimitedJobNames = appRt.config.scheduler.jobs.filter((job) => resolveSchedulerJobTimeoutMs(job.name, env, schedulerCustomTimeoutMs) === null).map((job) => job.name);
118554
+ const orphanCount = await failOrphanedProcessingJobs(await ctx.getDb(), Date.now(), unlimitedJobNames);
117807
118555
  if (orphanCount > 0) {
117808
118556
  appRt.logger.warn("Swept orphaned processing jobs at startup", {
117809
118557
  count: orphanCount
@@ -117815,7 +118563,7 @@ async function startServer(options, deps = defaultDeps) {
117815
118563
  });
117816
118564
  await jobWorker.start();
117817
118565
  appRt.logger.info("Job worker started");
117818
- }
118566
+ };
117819
118567
  if (scheduler3) {
117820
118568
  registerSchedulerEntries(scheduler3, ctx, appRt.config.scheduler.jobs, {
117821
118569
  timeoutMs: schedulerCustomTimeoutMs,
@@ -117836,6 +118584,15 @@ async function startServer(options, deps = defaultDeps) {
117836
118584
  } catch (err) {
117837
118585
  appRt.logger.warn("Failed to register project in ProjectRegistry", { error: String(err) });
117838
118586
  }
118587
+ if (bootConfig.jobqueue.enabled) {
118588
+ jobWorkerStartTimer = setTimeout(() => {
118589
+ jobWorkerStartTimer = undefined;
118590
+ startJobQueueWorker().catch((error51) => {
118591
+ appRt.logger.error("Job worker startup failed", { error: String(error51) });
118592
+ });
118593
+ }, options.jobWorkerStartDelayMs ?? JOB_WORKER_START_DELAY_MS);
118594
+ jobWorkerStartTimer.unref?.();
118595
+ }
117839
118596
  let shuttingDown = false;
117840
118597
  const onSigInt = () => {
117841
118598
  shutdown("SIGINT");
@@ -117850,6 +118607,13 @@ async function startServer(options, deps = defaultDeps) {
117850
118607
  process.off("SIGINT", onSigInt);
117851
118608
  process.off("SIGTERM", onSigTerm);
117852
118609
  appRt.logger.info("Shutting down server", { signal });
118610
+ if (jobWorkerStartTimer) {
118611
+ clearTimeout(jobWorkerStartTimer);
118612
+ jobWorkerStartTimer = undefined;
118613
+ }
118614
+ if (jobProcessRegistry) {
118615
+ terminateJobChildren(jobProcessRegistry, "SIGTERM");
118616
+ }
117853
118617
  if (quotaConsumer) {
117854
118618
  try {
117855
118619
  await quotaConsumer.stop();
@@ -117871,6 +118635,9 @@ async function startServer(options, deps = defaultDeps) {
117871
118635
  }
117872
118636
  server.stop(true);
117873
118637
  await appRt.stop("shutdown");
118638
+ if (jobProcessRegistry) {
118639
+ terminateJobChildren(jobProcessRegistry, "SIGKILL");
118640
+ }
117874
118641
  process.exit(0);
117875
118642
  };
117876
118643
  process.on("SIGINT", onSigInt);
@@ -117926,7 +118693,7 @@ var SQLITE_BUSY_MESSAGE_CONSTANTS = Object.freeze({
117926
118693
 
117927
118694
  // src/commands/serve.ts
117928
118695
  function resolveServeDbUrl(cwd, env, configuredUrl) {
117929
- return env.DATABASE_URL === undefined ? join37(cwd, DEFAULT_DATABASE_URL) : configuredUrl;
118696
+ return env.DATABASE_URL === undefined ? join38(cwd, DEFAULT_DATABASE_URL) : configuredUrl;
117930
118697
  }
117931
118698
  function resolveServeCwd(cwd) {
117932
118699
  const resolved = resolve19(cwd);
@@ -117972,7 +118739,7 @@ function registerServeCommand(program2, context4, options = {}) {
117972
118739
  ...projectRoot !== undefined ? { cwd: projectRoot } : {}
117973
118740
  });
117974
118741
  } catch (err) {
117975
- writeJsonError(context4.output, options2, err instanceof Error ? err.message : String(err));
118742
+ writeJsonError(context4.output, options2, errorMessage4(err));
117976
118743
  if (context4.env?.SPUR_DEBUG === "1" && err instanceof Error && err.stack) {
117977
118744
  writeJsonError(context4.output, options2, err.stack, "INTERNAL_ERROR");
117978
118745
  }
@@ -117982,7 +118749,7 @@ function registerServeCommand(program2, context4, options = {}) {
117982
118749
  }
117983
118750
 
117984
118751
  // src/commands/status.ts
117985
- import { join as join38 } from "path";
118752
+ import { join as join39 } from "path";
117986
118753
 
117987
118754
  // src/git-context.ts
117988
118755
  init_dist5();
@@ -118030,8 +118797,8 @@ function registerStatusCommand(program2, context4, options = {}) {
118030
118797
  }
118031
118798
  async function runStatusCore(path9, options, context4) {
118032
118799
  const [packageJsonExists, spurConfigExists, git3, agentSpecs] = await Promise.all([
118033
- context4.fs.exists(join38(context4.cwd, "package.json")),
118034
- context4.fs.exists(join38(context4.cwd, ".spur", "config.yaml")),
118800
+ context4.fs.exists(join39(context4.cwd, "package.json")),
118801
+ context4.fs.exists(join39(context4.cwd, ".spur", "config.yaml")),
118035
118802
  gitContext(context4.cwd),
118036
118803
  listAgentSpecIds(context4)
118037
118804
  ]);
@@ -118059,14 +118826,14 @@ async function runStatusCore(path9, options, context4) {
118059
118826
  return status.ok ? 0 : 1;
118060
118827
  }
118061
118828
  async function listAgentSpecIds(context4) {
118062
- const dir = join38(context4.cwd, ".spur", "agents");
118829
+ const dir = join39(context4.cwd, ".spur", "agents");
118063
118830
  if (!await context4.fs.exists(dir))
118064
118831
  return [];
118065
118832
  const entries = await context4.fs.readDir(dir);
118066
118833
  return entries.filter((entry) => entry.endsWith(".yaml") || entry.endsWith(".yml")).map((entry) => entry.replace(/\.ya?ml$/, "")).sort();
118067
118834
  }
118068
118835
  async function readTargetStatus(context4, targetPath) {
118069
- const resolved = join38(context4.cwd, targetPath);
118836
+ const resolved = join39(context4.cwd, targetPath);
118070
118837
  const stat2 = await context4.fs.stat(resolved);
118071
118838
  if (stat2 === null)
118072
118839
  throw new CommandError(`status failed: path does not exist at ${resolved}`);
@@ -118081,7 +118848,7 @@ await init_src3();
118081
118848
  import { existsSync as existsSync11, readFileSync as readFileSync11 } from "fs";
118082
118849
  import { rm, writeFile } from "fs/promises";
118083
118850
  import { tmpdir as tmpdir3 } from "os";
118084
- import { dirname as dirname23, join as join39 } from "path";
118851
+ import { dirname as dirname23, join as join40 } from "path";
118085
118852
  // schemas/section-matrix.schema.json
118086
118853
  var section_matrix_schema_default = {
118087
118854
  $schema: "http://json-schema.org/draft-07/schema#",
@@ -119240,7 +120007,7 @@ ${result.content}`);
119240
120007
  let forcedDoneVerdict;
119241
120008
  if (status === "done") {
119242
120009
  const current = await svc.show(wbs);
119243
- const verdictDir = options.verdictDir ?? join39(context4.cwd, ".spur", "run");
120010
+ const verdictDir = options.verdictDir ?? join40(context4.cwd, ".spur", "run");
119244
120011
  const loaded = await readVerdictArtifact(context4.fs, verdictDir, wbs);
119245
120012
  const guardOutcome = evaluateDoneTransition({
119246
120013
  wbs,
@@ -119550,7 +120317,7 @@ ${result.content}`);
119550
120317
  agentSelector: options.agent,
119551
120318
  cwd: context4.cwd
119552
120319
  });
119553
- tmpPath = join39(tmpdir3(), `spur-ready-batch-${Date.now()}-${Math.random().toString(36).slice(2, 8)}.json`);
120320
+ tmpPath = join40(tmpdir3(), `spur-ready-batch-${Date.now()}-${Math.random().toString(36).slice(2, 8)}.json`);
119554
120321
  await writeFile(tmpPath, `${JSON.stringify(prepared.items, null, 2)}
119555
120322
  `);
119556
120323
  batchFile = tmpPath;
@@ -119963,13 +120730,13 @@ function loadTemplateBodies(projectRoot, variant) {
119963
120730
  if (cached2 !== undefined)
119964
120731
  return cached2;
119965
120732
  let bodies = {};
119966
- const localPath = join39(projectRoot, ".spur", "tasks", "templates", `${variant}.md`);
120733
+ const localPath = join40(projectRoot, ".spur", "tasks", "templates", `${variant}.md`);
119967
120734
  if (existsSync11(localPath)) {
119968
120735
  bodies = extractTemplateBodies(readFileSync11(localPath, "utf8"));
119969
120736
  } else {
119970
120737
  const root = bundledConfigRoot();
119971
120738
  if (root !== null) {
119972
- const templatePath = join39(root, "templates", "task", `${variant}.md`);
120739
+ const templatePath = join40(root, "templates", "task", `${variant}.md`);
119973
120740
  if (existsSync11(templatePath)) {
119974
120741
  bodies = extractTemplateBodies(readFileSync11(templatePath, "utf8"));
119975
120742
  }
@@ -120027,7 +120794,7 @@ async function loadSectionMatrixUncached(projectRoot) {
120027
120794
  }
120028
120795
  const root = bundledConfigRoot();
120029
120796
  if (root !== null) {
120030
- const matrixPath = join39(root, "tasks", "section-matrix.yaml");
120797
+ const matrixPath = join40(root, "tasks", "section-matrix.yaml");
120031
120798
  if (await fs3.exists(matrixPath)) {
120032
120799
  const data = await loadStructuredSpurConfig(matrixPath, {
120033
120800
  validateJsonSchema: true,
@@ -120038,7 +120805,7 @@ async function loadSectionMatrixUncached(projectRoot) {
120038
120805
  }
120039
120806
  throw new Error(`no canonical section-matrix found for task section authority (F92 R1); tried:
120040
120807
  ` + ` - ${localPath}
120041
- ` + (root !== null ? ` - ${join39(root, "tasks", "section-matrix.yaml")}
120808
+ ` + (root !== null ? ` - ${join40(root, "tasks", "section-matrix.yaml")}
120042
120809
  ` : "") + "copy/generate section-matrix.yaml from the canonical build-time matrix asset (repo `config` `tasks` tree) into one of those paths");
120043
120810
  }
120044
120811
 
@@ -120335,7 +121102,7 @@ init_dist7();
120335
121102
  init_dist5();
120336
121103
  await init_src3();
120337
121104
  import { closeSync as closeSync5, fstatSync as fstatSync3, openSync as openSync5, readSync as readSync3 } from "fs";
120338
- import { join as join40 } from "path";
121105
+ import { join as join41 } from "path";
120339
121106
  import { createInterface } from "readline";
120340
121107
  import { setTimeout as sleep5 } from "timers/promises";
120341
121108
 
@@ -120514,9 +121281,9 @@ async function writeWorkflowPlanArtifact(cwd, runId, resolved, secretValues) {
120514
121281
  }))
120515
121282
  });
120516
121283
  const { mkdir, writeFile: writeFile2 } = await import("fs/promises");
120517
- const artifactsDir = join40(cwd, ".spur", "run");
121284
+ const artifactsDir = join41(cwd, ".spur", "run");
120518
121285
  await mkdir(artifactsDir, { recursive: true });
120519
- const artifactPath = join40(artifactsDir, `${runId}-workflow-plan.json`);
121286
+ const artifactPath = join41(artifactsDir, `${runId}-workflow-plan.json`);
120520
121287
  await writeFile2(artifactPath, payload);
120521
121288
  return artifactPath;
120522
121289
  }
@@ -120574,7 +121341,7 @@ function resolveWorkflowPaths(config4) {
120574
121341
  for (const path9 of paths) {
120575
121342
  if (path9.startsWith(BUNDLED_PATH_PREFIX)) {
120576
121343
  if (bundledRoot !== null)
120577
- expanded.push(join40(bundledRoot, path9.slice(BUNDLED_PATH_PREFIX.length)));
121344
+ expanded.push(join41(bundledRoot, path9.slice(BUNDLED_PATH_PREFIX.length)));
120578
121345
  } else {
120579
121346
  expanded.push(path9);
120580
121347
  }
@@ -120813,7 +121580,7 @@ Monitor with: spur workflow trace ${runId2} --follow`);
120813
121580
  }
120814
121581
  const runLog = options.log === false ? undefined : new WorkflowRunLogSink({
120815
121582
  bus,
120816
- dir: join40(context4.cwd, ".spur", "run"),
121583
+ dir: join41(context4.cwd, ".spur", "run"),
120817
121584
  runId,
120818
121585
  ...planPreview !== undefined ? { planPreview } : {},
120819
121586
  ...resolveOutputLogConfig(context4.spurConfig ?? null)
@@ -121083,14 +121850,21 @@ Monitor with: spur workflow trace ${runId2} --follow`);
121083
121850
  const def = resolvedDefinition.workflow;
121084
121851
  if (options.json) {
121085
121852
  if (options.format === "todo") {
121086
- context4.output.write(toJson2({
121853
+ const projection = {
121087
121854
  name: def.name,
121088
121855
  kind: def.kind ?? "state-machine",
121089
121856
  format: "todo",
121090
121857
  version: resolvedDefinition.workflow.version ?? null,
121091
121858
  definitionDigest: resolvedDefinition.digest,
121092
121859
  steps: buildWorkflowSteps(def)
121093
- }));
121860
+ };
121861
+ const parsed = parseWorkflowInventory(projection);
121862
+ if (!parsed.ok) {
121863
+ writeJsonError(context4.output, options, `workflow show: invalid todo projection \u2014 ${parsed.error}`, "VALIDATION_FAILED");
121864
+ context4.setExitCode(1);
121865
+ return;
121866
+ }
121867
+ context4.output.write(toJson2(projection));
121094
121868
  } else {
121095
121869
  context4.output.write(toJson2({
121096
121870
  name: def.name,
@@ -121345,7 +122119,7 @@ function readRunLogChunk(logPath, offset) {
121345
122119
  }
121346
122120
  }
121347
122121
  async function followRunLog(service, runId, dir, pollMs, write, wait = (ms) => sleep5(ms)) {
121348
- const logPath = join40(dir, ".spur", "run", `${runId}.log`);
122122
+ const logPath = join41(dir, ".spur", "run", `${runId}.log`);
121349
122123
  let offset = 0;
121350
122124
  let everRead = false;
121351
122125
  while (true) {
@@ -121408,7 +122182,7 @@ init_src();
121408
122182
  init_src2();
121409
122183
  init_dist5();
121410
122184
  await init_src3();
121411
- import { dirname as dirname24, join as join41, resolve as resolve20 } from "path";
122185
+ import { dirname as dirname24, join as join42, resolve as resolve20 } from "path";
121412
122186
  import { isatty as isatty4 } from "tty";
121413
122187
 
121414
122188
  // ../../node_modules/.bun/@clack+core@1.4.1/node_modules/@clack/core/dist/index.mjs
@@ -122873,7 +123647,7 @@ function createCliContext(options) {
122873
123647
  function noopSetExitCode(_code) {}
122874
123648
  async function createMigratedDbAdapter(cwd = process.cwd(), env = process.env, dbUrl) {
122875
123649
  const config4 = buildConfigFromEnv(env);
122876
- const configuredUrl = env.DATABASE_URL === undefined ? join41(cwd, DEFAULT_DATABASE_URL) : config4.database.url;
123650
+ const configuredUrl = env.DATABASE_URL === undefined ? join42(cwd, DEFAULT_DATABASE_URL) : config4.database.url;
122877
123651
  const url2 = dbUrl ?? configuredUrl;
122878
123652
  if (url2 !== IN_MEMORY_DATABASE_URL) {
122879
123653
  await createNodeFileSystem3().ensureDir(dirname24(url2));
@@ -122887,11 +123661,11 @@ async function main(argv = process.argv.slice(2), options = {}) {
122887
123661
  let exitCode = 0;
122888
123662
  const cwd = options.cwd ?? process.cwd();
122889
123663
  const env = options.env ?? process.env;
122890
- const configFile = resolveConfigFile(cwd);
123664
+ const configFile = resolveConfigFile(options.cwd);
122891
123665
  const db2 = options.db ?? await createMigratedDbAdapter(cwd, env, options.dbUrl);
122892
123666
  let spurConfig;
122893
123667
  try {
122894
- spurConfig = await loadSpurConfig(cwd, { embeddedSchemas: EMBEDDED_SPUR_SCHEMAS });
123668
+ spurConfig = await loadSpurConfig(options.cwd, { embeddedSchemas: EMBEDDED_SPUR_SCHEMAS });
122895
123669
  } catch (err) {
122896
123670
  const message = errorMessage4(err);
122897
123671
  if (argv.includes("--json")) {