@gobing-ai/spur 0.3.23 → 0.3.25

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 (61) hide show
  1. package/config/config.example.yaml +74 -31
  2. package/config/templates/AGENTS.md +1 -0
  3. package/config/workflows/docs-pipeline.yaml +9 -0
  4. package/config/workflows/idea-pipeline.yaml +78 -18
  5. package/package.json +9 -9
  6. package/schemas/spur-config.schema.json +4 -4
  7. package/schemas/task-batch.schema.json +12 -0
  8. package/spur.js +1893 -477
  9. package/web/_astro/{BoardApp.b8HvN_Uy.js → BoardApp.DH9wI8y7.js} +72 -72
  10. package/web/_astro/BoardApp.DZiwQCP7.js +1 -0
  11. package/web/_astro/{TaskDetail.BRr8ebLd.js → TaskDetail.Dsk9DnGY.js} +1 -1
  12. package/web/_astro/{arc.D9I3ghf3.js → arc.Biv8k-OR.js} +1 -1
  13. package/web/_astro/{architectureDiagram-3BPJPVTR.B60VjZ82.js → architectureDiagram-3BPJPVTR.BrtW5Sn7.js} +1 -1
  14. package/web/_astro/{blockDiagram-GPEHLZMM.B8xiXsGb.js → blockDiagram-GPEHLZMM.CTpFZJhW.js} +1 -1
  15. package/web/_astro/{c4Diagram-AAUBKEIU.D8Q9jRJZ.js → c4Diagram-AAUBKEIU._ZJj9H0G.js} +1 -1
  16. package/web/_astro/channel.Ih1_8SAH.js +1 -0
  17. package/web/_astro/{chunk-2J33WTMH.BVcogAuP.js → chunk-2J33WTMH.DD9mDiEM.js} +1 -1
  18. package/web/_astro/{chunk-4BX2VUAB.pDodeppW.js → chunk-4BX2VUAB.ENDQu3TH.js} +1 -1
  19. package/web/_astro/{chunk-55IACEB6.5dYLJxJn.js → chunk-55IACEB6.DLpJf8Xp.js} +1 -1
  20. package/web/_astro/{chunk-727SXJPM.C6GTI_n6.js → chunk-727SXJPM.BR6vsvIV.js} +1 -1
  21. package/web/_astro/{chunk-AQP2D5EJ.kgDX41bQ.js → chunk-AQP2D5EJ.DGOBzXM6.js} +1 -1
  22. package/web/_astro/{chunk-FMBD7UC4.CSnAi74r.js → chunk-FMBD7UC4.DrghnRtZ.js} +1 -1
  23. package/web/_astro/{chunk-ND2GUHAM.qQ8ldUUT.js → chunk-ND2GUHAM.BqIqI3oO.js} +1 -1
  24. package/web/_astro/{chunk-QZHKN3VN.PzPFlVri.js → chunk-QZHKN3VN.CJ2RJt0K.js} +1 -1
  25. package/web/_astro/{classDiagram-4FO5ZUOK.C7RulRYW.js → classDiagram-4FO5ZUOK.Qu1p6crX.js} +1 -1
  26. package/web/_astro/{classDiagram-v2-Q7XG4LA2.C7RulRYW.js → classDiagram-v2-Q7XG4LA2.Qu1p6crX.js} +1 -1
  27. package/web/_astro/{cose-bilkent-S5V4N54A.Dw4HZHnT.js → cose-bilkent-S5V4N54A.7VrRetJ0.js} +1 -1
  28. package/web/_astro/{dagre-BM42HDAG.MmGbZWM_.js → dagre-BM42HDAG.r4lTyJXQ.js} +1 -1
  29. package/web/_astro/{diagram-2AECGRRQ.DugWYvZC.js → diagram-2AECGRRQ.DahExuaR.js} +1 -1
  30. package/web/_astro/{diagram-5GNKFQAL.BL7D0BMi.js → diagram-5GNKFQAL.B-dSm3op.js} +1 -1
  31. package/web/_astro/{diagram-KO2AKTUF.B9GQMjwM.js → diagram-KO2AKTUF.CQGlKa6a.js} +1 -1
  32. package/web/_astro/{diagram-LMA3HP47.CbHPvhbu.js → diagram-LMA3HP47.CJ6nomD-.js} +1 -1
  33. package/web/_astro/{diagram-OG6HWLK6.Bx6GcKpw.js → diagram-OG6HWLK6.ijGl68sM.js} +1 -1
  34. package/web/_astro/{erDiagram-TEJ5UH35._sHJgXD7.js → erDiagram-TEJ5UH35.DBAe2Iin.js} +1 -1
  35. package/web/_astro/{flowDiagram-I6XJVG4X.D305L8m4.js → flowDiagram-I6XJVG4X.z3iXF6Ku.js} +1 -1
  36. package/web/_astro/{ganttDiagram-6RSMTGT7.qjJ4kX00.js → ganttDiagram-6RSMTGT7.DP2ouiRt.js} +1 -1
  37. package/web/_astro/{gitGraphDiagram-PVQCEYII.D-t4MzVO.js → gitGraphDiagram-PVQCEYII.DizVfgVG.js} +1 -1
  38. package/web/_astro/index.D3Y4Q0mx.css +1 -0
  39. package/web/_astro/{infoDiagram-5YYISTIA.WwSNIljZ.js → infoDiagram-5YYISTIA.BPTTgL0f.js} +1 -1
  40. package/web/_astro/{ishikawaDiagram-YF4QCWOH.EWsUYdpd.js → ishikawaDiagram-YF4QCWOH.CXdMa4-b.js} +1 -1
  41. package/web/_astro/{journeyDiagram-JHISSGLW.CqKE9hNP.js → journeyDiagram-JHISSGLW.ChSKZ-1j.js} +1 -1
  42. package/web/_astro/{kanban-definition-UN3LZRKU.BDCCufoa.js → kanban-definition-UN3LZRKU.DUUlW-km.js} +1 -1
  43. package/web/_astro/{linear.CyHEakPy.js → linear.AzHrSiEQ.js} +1 -1
  44. package/web/_astro/{mermaid.core.B-9P_KDH.js → mermaid.core.BajX78G7.js} +4 -4
  45. package/web/_astro/{mindmap-definition-RKZ34NQL.bCxpjW2r.js → mindmap-definition-RKZ34NQL.BmdUHcac.js} +1 -1
  46. package/web/_astro/{pieDiagram-4H26LBE5.x_CRiGMu.js → pieDiagram-4H26LBE5.Dh59Ur2v.js} +1 -1
  47. package/web/_astro/{quadrantDiagram-W4KKPZXB.CnbQkwo7.js → quadrantDiagram-W4KKPZXB.D2qYQmFj.js} +1 -1
  48. package/web/_astro/{requirementDiagram-4Y6WPE33.CfeJKVEP.js → requirementDiagram-4Y6WPE33.DW5nnrHl.js} +1 -1
  49. package/web/_astro/{sankeyDiagram-5OEKKPKP.COYpTQlG.js → sankeyDiagram-5OEKKPKP.CMl1PJrk.js} +1 -1
  50. package/web/_astro/{sequenceDiagram-3UESZ5HK.DVzoeimO.js → sequenceDiagram-3UESZ5HK.U5dq1PA0.js} +1 -1
  51. package/web/_astro/{stateDiagram-AJRCARHV.BVL5te9q.js → stateDiagram-AJRCARHV.zrt55DVx.js} +1 -1
  52. package/web/_astro/{stateDiagram-v2-BHNVJYJU.C_x1GbV0.js → stateDiagram-v2-BHNVJYJU.CKBGQ0dG.js} +1 -1
  53. package/web/_astro/{timeline-definition-PNZ67QCA.DtZPbOJL.js → timeline-definition-PNZ67QCA.CKAlx9rU.js} +1 -1
  54. package/web/_astro/{vennDiagram-CIIHVFJN.pexoUOut.js → vennDiagram-CIIHVFJN.Bgt1eF06.js} +1 -1
  55. package/web/_astro/{wardley-L42UT6IY.DLG5pKRN.js → wardley-L42UT6IY.BF02aioC.js} +1 -1
  56. package/web/_astro/{wardleyDiagram-YWT4CUSO.CnYrM4tH.js → wardleyDiagram-YWT4CUSO.C94wxcnJ.js} +1 -1
  57. package/web/_astro/{xychartDiagram-2RQKCTM6.kBrlce_Y.js → xychartDiagram-2RQKCTM6.BwK7SRoW.js} +1 -1
  58. package/web/index.html +2 -2
  59. package/web/_astro/BoardApp.LygxZgOy.js +0 -1
  60. package/web/_astro/channel.BToTp7Lu.js +0 -1
  61. package/web/_astro/index.CmVh0AUD.css +0 -1
package/spur.js CHANGED
@@ -47,7 +47,7 @@ var __export = (target, all) => {
47
47
  var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
48
48
  var __require = import.meta.require;
49
49
 
50
- // ../../node_modules/.bun/@gobing-ai+ts-utils@0.4.11/node_modules/@gobing-ai/ts-utils/dist/errors.js
50
+ // ../../node_modules/.bun/@gobing-ai+ts-utils@0.4.14/node_modules/@gobing-ai/ts-utils/dist/errors.js
51
51
  var ErrorCode;
52
52
  var init_errors = __esm(() => {
53
53
  ErrorCode = {
@@ -58,7 +58,7 @@ var init_errors = __esm(() => {
58
58
  };
59
59
  });
60
60
 
61
- // ../../node_modules/.bun/@gobing-ai+ts-utils@0.4.11/node_modules/@gobing-ai/ts-utils/dist/api-response.js
61
+ // ../../node_modules/.bun/@gobing-ai+ts-utils@0.4.14/node_modules/@gobing-ai/ts-utils/dist/api-response.js
62
62
  var API_ERROR_CODES, ERROR_CODE_TO_HTTP, CLIENT_SAFE_CODES;
63
63
  var init_api_response = __esm(() => {
64
64
  init_errors();
@@ -81,10 +81,10 @@ var init_api_response = __esm(() => {
81
81
  CLIENT_SAFE_CODES = new Set([ErrorCode.NotFound, ErrorCode.Validation, ErrorCode.Conflict]);
82
82
  });
83
83
 
84
- // ../../node_modules/.bun/@gobing-ai+ts-utils@0.4.11/node_modules/@gobing-ai/ts-utils/dist/cursor.js
84
+ // ../../node_modules/.bun/@gobing-ai+ts-utils@0.4.14/node_modules/@gobing-ai/ts-utils/dist/cursor.js
85
85
  var init_cursor = () => {};
86
86
 
87
- // ../../node_modules/.bun/@gobing-ai+ts-utils@0.4.11/node_modules/@gobing-ai/ts-utils/dist/object.js
87
+ // ../../node_modules/.bun/@gobing-ai+ts-utils@0.4.14/node_modules/@gobing-ai/ts-utils/dist/object.js
88
88
  function isPlainObject(value) {
89
89
  return typeof value === "object" && value !== null && !Array.isArray(value);
90
90
  }
@@ -100,7 +100,7 @@ function deepMerge(target, source) {
100
100
  return result;
101
101
  }
102
102
 
103
- // ../../node_modules/.bun/@gobing-ai+ts-utils@0.4.11/node_modules/@gobing-ai/ts-utils/dist/output.js
103
+ // ../../node_modules/.bun/@gobing-ai+ts-utils@0.4.14/node_modules/@gobing-ai/ts-utils/dist/output.js
104
104
  function processStream(name) {
105
105
  const proc = globalThis.process;
106
106
  const stream = proc?.[name];
@@ -121,7 +121,7 @@ function echoError(message, target = defaultStderrTarget ?? processStream("stder
121
121
  }
122
122
  var defaultStdoutTarget, defaultStderrTarget;
123
123
 
124
- // ../../node_modules/.bun/@gobing-ai+ts-utils@0.4.11/node_modules/@gobing-ai/ts-utils/dist/index.js
124
+ // ../../node_modules/.bun/@gobing-ai+ts-utils@0.4.14/node_modules/@gobing-ai/ts-utils/dist/index.js
125
125
  var init_dist = __esm(() => {
126
126
  init_api_response();
127
127
  init_cursor();
@@ -21743,7 +21743,7 @@ var init_zod = __esm(() => {
21743
21743
  init_external();
21744
21744
  });
21745
21745
 
21746
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.11+92240aa39452d4a9/node_modules/@gobing-ai/ts-runtime/dist/config.js
21746
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.14+c51d44ed7d8b072c/node_modules/@gobing-ai/ts-runtime/dist/config.js
21747
21747
  function getProcessEnv() {
21748
21748
  return process.env;
21749
21749
  }
@@ -21821,7 +21821,7 @@ var init_config = __esm(() => {
21821
21821
  };
21822
21822
  });
21823
21823
 
21824
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.11+92240aa39452d4a9/node_modules/@gobing-ai/ts-runtime/dist/file-system-node.js
21824
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.14+c51d44ed7d8b072c/node_modules/@gobing-ai/ts-runtime/dist/file-system-node.js
21825
21825
  import { appendFileSync, cpSync, createReadStream, createWriteStream, existsSync, mkdirSync, readdirSync, readFileSync, realpathSync, renameSync, rmSync, statSync, writeFileSync } from "fs";
21826
21826
  import { dirname, resolve as resolvePath } from "path";
21827
21827
  function createNodeFileSystem(root) {
@@ -21908,7 +21908,7 @@ function findProjectRoot(startDir) {
21908
21908
  }
21909
21909
  var init_file_system_node = () => {};
21910
21910
 
21911
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.11+92240aa39452d4a9/node_modules/@gobing-ai/ts-runtime/dist/db-errors.js
21911
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.14+c51d44ed7d8b072c/node_modules/@gobing-ai/ts-runtime/dist/db-errors.js
21912
21912
  var DbModuleNotInstalledError;
21913
21913
  var init_db_errors = __esm(() => {
21914
21914
  DbModuleNotInstalledError = class DbModuleNotInstalledError extends Error {
@@ -29099,7 +29099,7 @@ var init_execa = __esm(() => {
29099
29099
  } = getIpcExport());
29100
29100
  });
29101
29101
 
29102
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.11+92240aa39452d4a9/node_modules/@gobing-ai/ts-runtime/dist/process-executor.js
29102
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.14+c51d44ed7d8b072c/node_modules/@gobing-ai/ts-runtime/dist/process-executor.js
29103
29103
  import { isatty } from "tty";
29104
29104
 
29105
29105
  class NodeProcessExecutor {
@@ -29141,7 +29141,11 @@ class NodeProcessExecutor {
29141
29141
  ...options.label !== undefined ? { label: options.label } : {}
29142
29142
  });
29143
29143
  try {
29144
- const result = await execa(options.command, args, execaOptions);
29144
+ const subprocess = execa(options.command, args, execaOptions);
29145
+ const stopGroupCancellation = observeProcessGroupCancellation(subprocess, options.signal);
29146
+ observeOutput(subprocess.stdout, "stdout", options.onOutput);
29147
+ observeOutput(subprocess.stderr, "stderr", options.onOutput);
29148
+ const result = await subprocess.finally(stopGroupCancellation);
29145
29149
  const processResult = {
29146
29150
  command: options.command,
29147
29151
  args,
@@ -29367,9 +29371,42 @@ function buildExecaOptions(opts) {
29367
29371
  ...opts.env !== undefined ? { env: opts.env } : {},
29368
29372
  ...opts.timeout !== undefined ? { timeout: opts.timeout } : {},
29369
29373
  ...opts.maxOutput !== undefined ? { maxBuffer: opts.maxOutput } : {},
29370
- ...opts.signal !== undefined ? { cancelSignal: opts.signal } : {}
29374
+ ...opts.signal !== undefined ? {
29375
+ cancelSignal: opts.signal,
29376
+ ...process.platform !== "win32" ? { detached: true } : {}
29377
+ } : {}
29371
29378
  };
29372
29379
  }
29380
+ function observeProcessGroupCancellation(subprocess, signal) {
29381
+ const pid = subprocess.pid;
29382
+ if (signal === undefined || process.platform === "win32" || pid === undefined)
29383
+ return () => {};
29384
+ const abort = () => {
29385
+ try {
29386
+ process.kill(-pid, "SIGTERM");
29387
+ } catch {
29388
+ subprocess.kill("SIGTERM");
29389
+ }
29390
+ };
29391
+ if (signal.aborted)
29392
+ abort();
29393
+ else
29394
+ signal.addEventListener("abort", abort, { once: true });
29395
+ return () => signal.removeEventListener("abort", abort);
29396
+ }
29397
+ function observeOutput(stream, name, observer) {
29398
+ if (!stream || !observer)
29399
+ return;
29400
+ stream.on("data", (chunk) => {
29401
+ try {
29402
+ observer({
29403
+ stream: name,
29404
+ chunk: asString(chunk),
29405
+ timestamp: new Date().toISOString()
29406
+ });
29407
+ } catch {}
29408
+ });
29409
+ }
29373
29410
  function asString(value) {
29374
29411
  if (typeof value === "string")
29375
29412
  return value;
@@ -29392,7 +29429,7 @@ var init_process_executor = __esm(() => {
29392
29429
  init_execa();
29393
29430
  });
29394
29431
 
29395
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.11+92240aa39452d4a9/node_modules/@gobing-ai/ts-runtime/dist/runtime-node-bun.js
29432
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.14+c51d44ed7d8b072c/node_modules/@gobing-ai/ts-runtime/dist/runtime-node-bun.js
29396
29433
  function getNodeFileSystem() {
29397
29434
  if (!_nodeFileSystem)
29398
29435
  _nodeFileSystem = createNodeFileSystem();
@@ -29452,7 +29489,7 @@ var init_runtime_node_bun = __esm(() => {
29452
29489
  };
29453
29490
  });
29454
29491
 
29455
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.11+92240aa39452d4a9/node_modules/@gobing-ai/ts-runtime/dist/context.js
29492
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.14+c51d44ed7d8b072c/node_modules/@gobing-ai/ts-runtime/dist/context.js
29456
29493
  class RuntimeContext {
29457
29494
  scope;
29458
29495
  runtimeName;
@@ -29522,7 +29559,7 @@ var init_context = __esm(() => {
29522
29559
  init_runtime_node_bun();
29523
29560
  });
29524
29561
 
29525
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.11+92240aa39452d4a9/node_modules/@gobing-ai/ts-runtime/dist/path.js
29562
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.14+c51d44ed7d8b072c/node_modules/@gobing-ai/ts-runtime/dist/path.js
29526
29563
  function normalizeSeparators(path6) {
29527
29564
  return path6.replaceAll("\\", "/");
29528
29565
  }
@@ -29583,17 +29620,17 @@ var init_path = __esm(() => {
29583
29620
  SEP = globalThis.process?.platform === "win32" ? "\\" : "/";
29584
29621
  });
29585
29622
 
29586
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.11+92240aa39452d4a9/node_modules/@gobing-ai/ts-runtime/dist/schema-validation.js
29623
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.14+c51d44ed7d8b072c/node_modules/@gobing-ai/ts-runtime/dist/schema-validation.js
29587
29624
  var REMOTE_SCHEMA_MAX_BYTES;
29588
29625
  var init_schema_validation = __esm(() => {
29589
29626
  init_dist2();
29590
29627
  REMOTE_SCHEMA_MAX_BYTES = 5 * 1024 * 1024;
29591
29628
  });
29592
29629
 
29593
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.11+92240aa39452d4a9/node_modules/@gobing-ai/ts-runtime/dist/types.js
29630
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.14+c51d44ed7d8b072c/node_modules/@gobing-ai/ts-runtime/dist/types.js
29594
29631
  var init_types = () => {};
29595
29632
 
29596
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.11+92240aa39452d4a9/node_modules/@gobing-ai/ts-runtime/dist/index.js
29633
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.14+c51d44ed7d8b072c/node_modules/@gobing-ai/ts-runtime/dist/index.js
29597
29634
  var init_dist3 = __esm(() => {
29598
29635
  init_config();
29599
29636
  init_context();
@@ -29602,7 +29639,7 @@ var init_dist3 = __esm(() => {
29602
29639
  init_types();
29603
29640
  });
29604
29641
 
29605
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.11+92240aa39452d4a9/node_modules/@gobing-ai/ts-runtime/dist/bun-sqlite.js
29642
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.14+c51d44ed7d8b072c/node_modules/@gobing-ai/ts-runtime/dist/bun-sqlite.js
29606
29643
  import { Database } from "bun:sqlite";
29607
29644
  var init_bun_sqlite = () => {};
29608
29645
 
@@ -33794,7 +33831,7 @@ var init_bun_sqlite2 = __esm(() => {
33794
33831
  init_session2();
33795
33832
  });
33796
33833
 
33797
- // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.11+3ced2785cbe4d5ed/node_modules/@gobing-ai/ts-db/dist/schema/inbox-messages.js
33834
+ // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.14+eb0d628435e6547b/node_modules/@gobing-ai/ts-db/dist/schema/inbox-messages.js
33798
33835
  var inboxMessages;
33799
33836
  var init_inbox_messages = __esm(() => {
33800
33837
  init_sqlite_core();
@@ -33813,7 +33850,7 @@ var init_inbox_messages = __esm(() => {
33813
33850
  }, (table2) => [index("idx_inbox_messages_to_status").on(table2.toId, table2.status)]);
33814
33851
  });
33815
33852
 
33816
- // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.11+3ced2785cbe4d5ed/node_modules/@gobing-ai/ts-db/dist/schema/common.js
33853
+ // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.14+eb0d628435e6547b/node_modules/@gobing-ai/ts-db/dist/schema/common.js
33817
33854
  function nowTimestamp() {
33818
33855
  return Date.now();
33819
33856
  }
@@ -33842,7 +33879,7 @@ var init_common3 = __esm(() => {
33842
33879
  appendOnlyColumns = buildAppendOnlyColumns();
33843
33880
  });
33844
33881
 
33845
- // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.11+3ced2785cbe4d5ed/node_modules/@gobing-ai/ts-db/dist/schema/queue-jobs.js
33882
+ // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.14+eb0d628435e6547b/node_modules/@gobing-ai/ts-db/dist/schema/queue-jobs.js
33846
33883
  var queueJobs;
33847
33884
  var init_queue_jobs = __esm(() => {
33848
33885
  init_sqlite_core();
@@ -33862,7 +33899,7 @@ var init_queue_jobs = __esm(() => {
33862
33899
  }, (table2) => [index("queue_jobs_ready_idx").on(table2.status, table2.nextRetryAt, table2.createdAt)]);
33863
33900
  });
33864
33901
 
33865
- // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.11+3ced2785cbe4d5ed/node_modules/@gobing-ai/ts-db/dist/schema/runtime.js
33902
+ // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.14+eb0d628435e6547b/node_modules/@gobing-ai/ts-db/dist/schema/runtime.js
33866
33903
  var exports_runtime = {};
33867
33904
  __export(exports_runtime, {
33868
33905
  queueJobs: () => queueJobs,
@@ -33873,7 +33910,7 @@ var init_runtime = __esm(() => {
33873
33910
  init_queue_jobs();
33874
33911
  });
33875
33912
 
33876
- // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.11+3ced2785cbe4d5ed/node_modules/@gobing-ai/ts-db/dist/adapters/bun-sqlite.js
33913
+ // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.14+eb0d628435e6547b/node_modules/@gobing-ai/ts-db/dist/adapters/bun-sqlite.js
33877
33914
  var exports_bun_sqlite = {};
33878
33915
  __export(exports_bun_sqlite, {
33879
33916
  BunSqliteAdapter: () => BunSqliteAdapter
@@ -34162,7 +34199,7 @@ var init_d1 = __esm(() => {
34162
34199
  init_session3();
34163
34200
  });
34164
34201
 
34165
- // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.11+3ced2785cbe4d5ed/node_modules/@gobing-ai/ts-db/dist/adapters/d1.js
34202
+ // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.14+eb0d628435e6547b/node_modules/@gobing-ai/ts-db/dist/adapters/d1.js
34166
34203
  var exports_d1 = {};
34167
34204
  __export(exports_d1, {
34168
34205
  D1Adapter: () => D1Adapter
@@ -34226,7 +34263,7 @@ var init_d12 = __esm(() => {
34226
34263
  init_runtime();
34227
34264
  });
34228
34265
 
34229
- // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.11+3ced2785cbe4d5ed/node_modules/@gobing-ai/ts-db/dist/adapter.js
34266
+ // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.14+eb0d628435e6547b/node_modules/@gobing-ai/ts-db/dist/adapter.js
34230
34267
  async function createDbAdapter(config3) {
34231
34268
  switch (config3.driver) {
34232
34269
  case "bun-sqlite": {
@@ -34266,7 +34303,7 @@ var init_drizzle_orm = __esm(() => {
34266
34303
  init_view_common();
34267
34304
  });
34268
34305
 
34269
- // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.11+3ced2785cbe4d5ed/node_modules/@gobing-ai/ts-db/dist/query-spec.js
34306
+ // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.14+eb0d628435e6547b/node_modules/@gobing-ai/ts-db/dist/query-spec.js
34270
34307
  function compilePredicate(predicate) {
34271
34308
  if ("and" in predicate) {
34272
34309
  const parts = predicate.and.map(compilePredicate).filter((p) => p !== undefined);
@@ -34304,7 +34341,7 @@ var init_query_spec = __esm(() => {
34304
34341
  };
34305
34342
  });
34306
34343
 
34307
- // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.11+3ced2785cbe4d5ed/node_modules/@gobing-ai/ts-db/dist/base-dao.js
34344
+ // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.14+eb0d628435e6547b/node_modules/@gobing-ai/ts-db/dist/base-dao.js
34308
34345
  function asSelectQuery(query) {
34309
34346
  return query;
34310
34347
  }
@@ -34346,7 +34383,7 @@ var init_base_dao = __esm(() => {
34346
34383
  init_query_spec();
34347
34384
  });
34348
34385
 
34349
- // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.11+3ced2785cbe4d5ed/node_modules/@gobing-ai/ts-db/dist/embedded-migrations.js
34386
+ // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.14+eb0d628435e6547b/node_modules/@gobing-ai/ts-db/dist/embedded-migrations.js
34350
34387
  var embeddedMigrations;
34351
34388
  var init_embedded_migrations = __esm(() => {
34352
34389
  embeddedMigrations = [
@@ -34378,7 +34415,7 @@ var init_embedded_migrations = __esm(() => {
34378
34415
  ];
34379
34416
  });
34380
34417
 
34381
- // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.11+3ced2785cbe4d5ed/node_modules/@gobing-ai/ts-db/dist/entity-dao.js
34418
+ // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.14+eb0d628435e6547b/node_modules/@gobing-ai/ts-db/dist/entity-dao.js
34382
34419
  var EntityDao;
34383
34420
  var init_entity_dao = __esm(() => {
34384
34421
  init_drizzle_orm();
@@ -34540,7 +34577,7 @@ var init_entity_dao = __esm(() => {
34540
34577
  };
34541
34578
  });
34542
34579
 
34543
- // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.11+3ced2785cbe4d5ed/node_modules/@gobing-ai/ts-db/dist/inbox-message-dao.js
34580
+ // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.14+eb0d628435e6547b/node_modules/@gobing-ai/ts-db/dist/inbox-message-dao.js
34544
34581
  function isPromiseLike(value) {
34545
34582
  return value !== null && (typeof value === "object" || typeof value === "function") && typeof value.then === "function";
34546
34583
  }
@@ -34713,7 +34750,7 @@ var init_migrator2 = __esm(() => {
34713
34750
  init_migrator();
34714
34751
  });
34715
34752
 
34716
- // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.11+3ced2785cbe4d5ed/node_modules/@gobing-ai/ts-db/dist/migrate.js
34753
+ // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.14+eb0d628435e6547b/node_modules/@gobing-ai/ts-db/dist/migrate.js
34717
34754
  async function ensureJournalTable(adapter, table3) {
34718
34755
  await adapter.exec(`CREATE TABLE IF NOT EXISTS "${table3}" (` + "id INTEGER PRIMARY KEY AUTOINCREMENT, " + "hash text NOT NULL, " + "created_at numeric" + ")");
34719
34756
  }
@@ -34788,7 +34825,7 @@ var init_migrate = __esm(() => {
34788
34825
  init_embedded_migrations();
34789
34826
  });
34790
34827
 
34791
- // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.11+3ced2785cbe4d5ed/node_modules/@gobing-ai/ts-db/dist/queue-job-dao.js
34828
+ // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.14+eb0d628435e6547b/node_modules/@gobing-ai/ts-db/dist/queue-job-dao.js
34792
34829
  var QueueJobDao;
34793
34830
  var init_queue_job_dao = __esm(() => {
34794
34831
  init_drizzle_orm();
@@ -34929,7 +34966,7 @@ var init_queue_job_dao = __esm(() => {
34929
34966
  };
34930
34967
  });
34931
34968
 
34932
- // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.11+3ced2785cbe4d5ed/node_modules/@gobing-ai/ts-db/dist/index.js
34969
+ // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.14+eb0d628435e6547b/node_modules/@gobing-ai/ts-db/dist/index.js
34933
34970
  var exports_dist = {};
34934
34971
  __export(exports_dist, {
34935
34972
  embeddedMigrations: () => embeddedMigrations,
@@ -37077,6 +37114,8 @@ var init_finding_codes = __esm(() => {
37077
37114
  "L2.forbidden-section",
37078
37115
  "L2.disallowed-section",
37079
37116
  "L3.requirements-format",
37117
+ "L3.requirements-empty",
37118
+ "L3.ac-empty",
37080
37119
  "L3.solution-file-line",
37081
37120
  "L3.review-priority-table",
37082
37121
  "L3.testing-coverage",
@@ -37108,7 +37147,9 @@ var init_finding_codes = __esm(() => {
37108
37147
  "L4.uncovered-task-scenario",
37109
37148
  "L4.uncovered-feature-scenario",
37110
37149
  "L4.verifying-incomplete-tasks",
37111
- "L4.dogfood-missing"
37150
+ "L4.dogfood-missing",
37151
+ "L4.scenario-unverified",
37152
+ "L4.stale-line-anchor"
37112
37153
  ];
37113
37154
  FINDING_CODES = {
37114
37155
  L1_MARKDOWN_PARSE: "L1.markdown-parse",
@@ -37117,6 +37158,8 @@ var init_finding_codes = __esm(() => {
37117
37158
  L2_FORBIDDEN_SECTION: "L2.forbidden-section",
37118
37159
  L2_DISALLOWED_SECTION: "L2.disallowed-section",
37119
37160
  L3_REQUIREMENTS_FORMAT: "L3.requirements-format",
37161
+ L3_REQUIREMENTS_EMPTY: "L3.requirements-empty",
37162
+ L3_AC_EMPTY: "L3.ac-empty",
37120
37163
  L3_SOLUTION_FILE_LINE: "L3.solution-file-line",
37121
37164
  L3_REVIEW_PRIORITY_TABLE: "L3.review-priority-table",
37122
37165
  L3_TESTING_COVERAGE: "L3.testing-coverage",
@@ -37148,7 +37191,9 @@ var init_finding_codes = __esm(() => {
37148
37191
  L4_UNCOVERED_TASK_SCENARIO: "L4.uncovered-task-scenario",
37149
37192
  L4_UNCOVERED_FEATURE_SCENARIO: "L4.uncovered-feature-scenario",
37150
37193
  L4_VERIFYING_INCOMPLETE_TASKS: "L4.verifying-incomplete-tasks",
37151
- L4_DOGFOOD_MISSING: "L4.dogfood-missing"
37194
+ L4_DOGFOOD_MISSING: "L4.dogfood-missing",
37195
+ L4_SCENARIO_UNVERIFIED: "L4.scenario-unverified",
37196
+ L4_STALE_LINE_ANCHOR: "L4.stale-line-anchor"
37152
37197
  };
37153
37198
  });
37154
37199
 
@@ -37156,6 +37201,9 @@ var init_finding_codes = __esm(() => {
37156
37201
  function folderPathWithDefault(defaultPath) {
37157
37202
  return exports_external.preprocess((v) => v == null ? undefined : v, exports_external.string().default(defaultPath));
37158
37203
  }
37204
+ function normalizeExecutorTier(value) {
37205
+ return value === "capable" ? "capable-1" : value;
37206
+ }
37159
37207
  function normalizeMember(member) {
37160
37208
  return typeof member === "string" ? { executor: member } : { ...member };
37161
37209
  }
@@ -37196,7 +37244,7 @@ function buildConfigFromEnv(env = process.env) {
37196
37244
  }
37197
37245
  });
37198
37246
  }
37199
- var SPUR_ENV_VARS, SPUR_LOG_LEVELS, DEFAULT_TASKS_DIR = "docs/tasks", DEFAULT_FEATURES_DIR = "docs/features", DEFAULT_DATABASE_URL = ".spur/spur.db", IN_MEMORY_DATABASE_URL = ":memory:", folderConfigSchema, tasksConfigSchema, featuresConfigSchema, AgentExecutorConfigSchema, AGENT_ID_REGEX, TeamMemberConfigSchema, TeamConfigSchema, AgentConfigSchema, RulesConfigSchema, WorkflowsConfigSchema, RedactionConfigSchema, spurConfigSchema, configSchema2;
37247
+ var SPUR_ENV_VARS, SPUR_LOG_LEVELS, DEFAULT_TASKS_DIR = "docs/tasks", DEFAULT_FEATURES_DIR = "docs/features", DEFAULT_DATABASE_URL = ".spur/spur.db", IN_MEMORY_DATABASE_URL = ":memory:", folderConfigSchema, tasksConfigSchema, featuresConfigSchema, EXECUTOR_CAPABILITY_TIERS, executorCapabilityTierSchema, AgentExecutorConfigSchema, AGENT_ID_REGEX, TeamMemberConfigSchema, TeamConfigSchema, AgentConfigSchema, RulesConfigSchema, WorkflowsConfigSchema, RedactionConfigSchema, spurConfigSchema, configSchema2;
37200
37248
  var init_src = __esm(() => {
37201
37249
  init_zod();
37202
37250
  init_finding_codes();
@@ -37232,11 +37280,17 @@ var init_src = __esm(() => {
37232
37280
  featuresConfigSchema = exports_external.object({
37233
37281
  dir: folderPathWithDefault(DEFAULT_FEATURES_DIR)
37234
37282
  });
37283
+ EXECUTOR_CAPABILITY_TIERS = ["cheap", "standard", "capable-1", "capable-2", "capable-3"];
37284
+ executorCapabilityTierSchema = exports_external.preprocess((value) => {
37285
+ if (value === undefined || value === null)
37286
+ return;
37287
+ return normalizeExecutorTier(value);
37288
+ }, exports_external.enum(EXECUTOR_CAPABILITY_TIERS).optional());
37235
37289
  AgentExecutorConfigSchema = exports_external.object({
37236
37290
  name: exports_external.string().min(1),
37237
37291
  agent: exports_external.string().min(1),
37238
37292
  model: exports_external.string().min(1).optional(),
37239
- tier: exports_external.enum(["cheap", "standard", "capable"]).optional()
37293
+ tier: executorCapabilityTierSchema.optional()
37240
37294
  });
37241
37295
  AGENT_ID_REGEX = /^[a-z][a-z0-9_-]{1,63}$/;
37242
37296
  TeamMemberConfigSchema = exports_external.union([
@@ -37354,7 +37408,7 @@ var init_src = __esm(() => {
37354
37408
  });
37355
37409
  });
37356
37410
 
37357
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.11+b7f415af47cc46d8/node_modules/@gobing-ai/ts-runtime/dist/config.js
37411
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.14+1afbae169bbe5102/node_modules/@gobing-ai/ts-runtime/dist/config.js
37358
37412
  function parseYamlObject(text3) {
37359
37413
  let parsed;
37360
37414
  try {
@@ -37481,7 +37535,7 @@ var init_config2 = __esm(() => {
37481
37535
  };
37482
37536
  });
37483
37537
 
37484
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.11+b7f415af47cc46d8/node_modules/@gobing-ai/ts-runtime/dist/file-system-node.js
37538
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.14+1afbae169bbe5102/node_modules/@gobing-ai/ts-runtime/dist/file-system-node.js
37485
37539
  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";
37486
37540
  import { dirname as dirname2, resolve as resolvePath3 } from "path";
37487
37541
  function createNodeFileSystem3(root) {
@@ -37568,7 +37622,7 @@ function findProjectRoot3(startDir) {
37568
37622
  }
37569
37623
  var init_file_system_node2 = () => {};
37570
37624
 
37571
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.11+b7f415af47cc46d8/node_modules/@gobing-ai/ts-runtime/dist/db-errors.js
37625
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.14+1afbae169bbe5102/node_modules/@gobing-ai/ts-runtime/dist/db-errors.js
37572
37626
  var D1NotConfiguredError2, DbModuleNotInstalledError3;
37573
37627
  var init_db_errors2 = __esm(() => {
37574
37628
  D1NotConfiguredError2 = class D1NotConfiguredError2 extends Error {
@@ -37585,7 +37639,7 @@ var init_db_errors2 = __esm(() => {
37585
37639
  };
37586
37640
  });
37587
37641
 
37588
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.11+b7f415af47cc46d8/node_modules/@gobing-ai/ts-runtime/dist/file-system-cf.js
37642
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.14+1afbae169bbe5102/node_modules/@gobing-ai/ts-runtime/dist/file-system-cf.js
37589
37643
  function createCfFileSystem2() {
37590
37644
  return {
37591
37645
  getProjectRoot: () => "/bundle",
@@ -37624,7 +37678,7 @@ function createCfFileSystem2() {
37624
37678
  }
37625
37679
  var UNSUPPORTED = "FileSystem is not available on Cloudflare Workers. Use D1, KV, or R2 for persistent storage.";
37626
37680
 
37627
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.11+b7f415af47cc46d8/node_modules/@gobing-ai/ts-runtime/dist/runtime-cf.js
37681
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.14+1afbae169bbe5102/node_modules/@gobing-ai/ts-runtime/dist/runtime-cf.js
37628
37682
  var exports_runtime_cf = {};
37629
37683
  __export(exports_runtime_cf, {
37630
37684
  cloudflareWorkersFactory: () => cloudflareWorkersFactory2
@@ -37662,7 +37716,7 @@ var init_runtime_cf = __esm(() => {
37662
37716
  };
37663
37717
  });
37664
37718
 
37665
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.11+b7f415af47cc46d8/node_modules/@gobing-ai/ts-runtime/dist/process-executor.js
37719
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.14+1afbae169bbe5102/node_modules/@gobing-ai/ts-runtime/dist/process-executor.js
37666
37720
  import { isatty as isatty2 } from "tty";
37667
37721
 
37668
37722
  class NodeProcessExecutor3 {
@@ -37704,7 +37758,11 @@ class NodeProcessExecutor3 {
37704
37758
  ...options.label !== undefined ? { label: options.label } : {}
37705
37759
  });
37706
37760
  try {
37707
- const result = await execa(options.command, args, execaOptions);
37761
+ const subprocess = execa(options.command, args, execaOptions);
37762
+ const stopGroupCancellation = observeProcessGroupCancellation2(subprocess, options.signal);
37763
+ observeOutput2(subprocess.stdout, "stdout", options.onOutput);
37764
+ observeOutput2(subprocess.stderr, "stderr", options.onOutput);
37765
+ const result = await subprocess.finally(stopGroupCancellation);
37708
37766
  const processResult = {
37709
37767
  command: options.command,
37710
37768
  args,
@@ -37970,9 +38028,42 @@ function buildExecaOptions2(opts) {
37970
38028
  ...opts.env !== undefined ? { env: opts.env } : {},
37971
38029
  ...opts.timeout !== undefined ? { timeout: opts.timeout } : {},
37972
38030
  ...opts.maxOutput !== undefined ? { maxBuffer: opts.maxOutput } : {},
37973
- ...opts.signal !== undefined ? { cancelSignal: opts.signal } : {}
38031
+ ...opts.signal !== undefined ? {
38032
+ cancelSignal: opts.signal,
38033
+ ...process.platform !== "win32" ? { detached: true } : {}
38034
+ } : {}
37974
38035
  };
37975
38036
  }
38037
+ function observeProcessGroupCancellation2(subprocess, signal) {
38038
+ const pid = subprocess.pid;
38039
+ if (signal === undefined || process.platform === "win32" || pid === undefined)
38040
+ return () => {};
38041
+ const abort = () => {
38042
+ try {
38043
+ process.kill(-pid, "SIGTERM");
38044
+ } catch {
38045
+ subprocess.kill("SIGTERM");
38046
+ }
38047
+ };
38048
+ if (signal.aborted)
38049
+ abort();
38050
+ else
38051
+ signal.addEventListener("abort", abort, { once: true });
38052
+ return () => signal.removeEventListener("abort", abort);
38053
+ }
38054
+ function observeOutput2(stream, name, observer) {
38055
+ if (!stream || !observer)
38056
+ return;
38057
+ stream.on("data", (chunk) => {
38058
+ try {
38059
+ observer({
38060
+ stream: name,
38061
+ chunk: asString2(chunk),
38062
+ timestamp: new Date().toISOString()
38063
+ });
38064
+ } catch {}
38065
+ });
38066
+ }
37976
38067
  function asString2(value) {
37977
38068
  if (typeof value === "string")
37978
38069
  return value;
@@ -38002,7 +38093,7 @@ var init_process_executor2 = __esm(() => {
38002
38093
  ProcessExecutor2 = NodeProcessExecutor3;
38003
38094
  });
38004
38095
 
38005
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.11+b7f415af47cc46d8/node_modules/@gobing-ai/ts-runtime/dist/runtime-node-bun.js
38096
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.14+1afbae169bbe5102/node_modules/@gobing-ai/ts-runtime/dist/runtime-node-bun.js
38006
38097
  var exports_runtime_node_bun = {};
38007
38098
  __export(exports_runtime_node_bun, {
38008
38099
  nodeBunFactory: () => nodeBunFactory3,
@@ -38070,7 +38161,7 @@ var init_runtime_node_bun2 = __esm(() => {
38070
38161
  };
38071
38162
  });
38072
38163
 
38073
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.11+b7f415af47cc46d8/node_modules/@gobing-ai/ts-runtime/dist/platform.js
38164
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.14+1afbae169bbe5102/node_modules/@gobing-ai/ts-runtime/dist/platform.js
38074
38165
  function isCloudflareWorkerRuntime2() {
38075
38166
  return globalThis.navigator?.userAgent?.startsWith("Cloudflare-Workers") ?? false;
38076
38167
  }
@@ -38100,7 +38191,7 @@ function _resetRuntimeFactory2() {
38100
38191
  }
38101
38192
  var _factory, _runtimeName;
38102
38193
 
38103
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.11+b7f415af47cc46d8/node_modules/@gobing-ai/ts-runtime/dist/context.js
38194
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.14+1afbae169bbe5102/node_modules/@gobing-ai/ts-runtime/dist/context.js
38104
38195
  class RuntimeContext2 {
38105
38196
  scope;
38106
38197
  runtimeName;
@@ -38190,7 +38281,7 @@ var init_context2 = __esm(() => {
38190
38281
  init_runtime_node_bun2();
38191
38282
  });
38192
38283
 
38193
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.11+b7f415af47cc46d8/node_modules/@gobing-ai/ts-runtime/dist/path.js
38284
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.14+1afbae169bbe5102/node_modules/@gobing-ai/ts-runtime/dist/path.js
38194
38285
  function normalizeSeparators2(path7) {
38195
38286
  return path7.replaceAll("\\", "/");
38196
38287
  }
@@ -38313,7 +38404,7 @@ var init_path2 = __esm(() => {
38313
38404
  SEP2 = globalThis.process?.platform === "win32" ? "\\" : "/";
38314
38405
  });
38315
38406
 
38316
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.11+b7f415af47cc46d8/node_modules/@gobing-ai/ts-runtime/dist/fs.js
38407
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.14+1afbae169bbe5102/node_modules/@gobing-ai/ts-runtime/dist/fs.js
38317
38408
  async function ensureDirForFile2(path7, fs2 = createNodeFileSystem3()) {
38318
38409
  await fs2.ensureDir(dirnamePath(path7));
38319
38410
  }
@@ -38364,7 +38455,7 @@ var init_fs = __esm(() => {
38364
38455
  init_path2();
38365
38456
  });
38366
38457
 
38367
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.11+b7f415af47cc46d8/node_modules/@gobing-ai/ts-runtime/dist/process-registry.js
38458
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.14+1afbae169bbe5102/node_modules/@gobing-ai/ts-runtime/dist/process-registry.js
38368
38459
  function allocateId() {
38369
38460
  nextIdSeq += 1;
38370
38461
  return `pe_${Date.now().toString(36)}_${nextIdSeq.toString(36)}`;
@@ -38488,7 +38579,7 @@ function createInMemoryProcessRegistry2(options) {
38488
38579
  }
38489
38580
  var nextIdSeq = 0;
38490
38581
 
38491
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.11+b7f415af47cc46d8/node_modules/@gobing-ai/ts-runtime/dist/schema-validation.js
38582
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.14+1afbae169bbe5102/node_modules/@gobing-ai/ts-runtime/dist/schema-validation.js
38492
38583
  async function loadStructuredConfig(path7, options = {}) {
38493
38584
  const content = await (options.fileSystem ?? createNodeFileSystem3()).readFile(path7);
38494
38585
  return await parseStructuredConfig(content, path7, options);
@@ -38765,10 +38856,10 @@ var init_schema_validation2 = __esm(() => {
38765
38856
  defaultResolve = typeof Bun !== "undefined" ? (specifier, from) => Bun.resolveSync(specifier, from) : undefined;
38766
38857
  });
38767
38858
 
38768
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.11+b7f415af47cc46d8/node_modules/@gobing-ai/ts-runtime/dist/types.js
38859
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.14+1afbae169bbe5102/node_modules/@gobing-ai/ts-runtime/dist/types.js
38769
38860
  var init_types2 = () => {};
38770
38861
 
38771
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.11+b7f415af47cc46d8/node_modules/@gobing-ai/ts-runtime/dist/index.js
38862
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.14+1afbae169bbe5102/node_modules/@gobing-ai/ts-runtime/dist/index.js
38772
38863
  var exports_dist2 = {};
38773
38864
  __export(exports_dist2, {
38774
38865
  writeJsonFile: () => writeJsonFile2,
@@ -40917,7 +41008,7 @@ var init_mod = __esm(() => {
40917
41008
  init_config3();
40918
41009
  });
40919
41010
 
40920
- // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.11+08507d81f591b81d/node_modules/@gobing-ai/ts-infra/dist/logger.js
41011
+ // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.14+a32276a722fab3c8/node_modules/@gobing-ai/ts-infra/dist/logger.js
40921
41012
  function toLogTapeLevel(level) {
40922
41013
  return level === "warn" ? "warning" : level;
40923
41014
  }
@@ -42470,7 +42561,7 @@ var require_src = __commonJS((exports) => {
42470
42561
  };
42471
42562
  });
42472
42563
 
42473
- // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.11+08507d81f591b81d/node_modules/@gobing-ai/ts-infra/dist/telemetry/sdk.js
42564
+ // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.14+a32276a722fab3c8/node_modules/@gobing-ai/ts-infra/dist/telemetry/sdk.js
42474
42565
  function getTelemetryConfig(configPartial = {}) {
42475
42566
  return {
42476
42567
  enabled: configPartial.enabled ?? CONFIG_DEFAULTS.enabled,
@@ -42506,7 +42597,7 @@ var init_sdk = __esm(() => {
42506
42597
  resolvedConfig = getTelemetryConfig();
42507
42598
  });
42508
42599
 
42509
- // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.11+08507d81f591b81d/node_modules/@gobing-ai/ts-infra/dist/telemetry/tracing.js
42600
+ // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.14+a32276a722fab3c8/node_modules/@gobing-ai/ts-infra/dist/telemetry/tracing.js
42510
42601
  function isSuppressed(tracer2) {
42511
42602
  return tracer2 === undefined && !getResolvedConfig().enabled;
42512
42603
  }
@@ -42546,7 +42637,7 @@ var init_tracing2 = __esm(() => {
42546
42637
  import_api2 = __toESM(require_src(), 1);
42547
42638
  });
42548
42639
 
42549
- // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.11+08507d81f591b81d/node_modules/@gobing-ai/ts-infra/dist/telemetry/metrics.js
42640
+ // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.14+a32276a722fab3c8/node_modules/@gobing-ai/ts-infra/dist/telemetry/metrics.js
42550
42641
  function getMeter() {
42551
42642
  return import_api3.metrics.getMeter(METER_NAME, METER_VERSION);
42552
42643
  }
@@ -42643,7 +42734,7 @@ var init_metrics = __esm(() => {
42643
42734
  instruments = {};
42644
42735
  });
42645
42736
 
42646
- // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.11+08507d81f591b81d/node_modules/@gobing-ai/ts-infra/dist/event-bus/event-bus.js
42737
+ // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.14+a32276a722fab3c8/node_modules/@gobing-ai/ts-infra/dist/event-bus/event-bus.js
42647
42738
  function busLogger() {
42648
42739
  if (!_busLogger)
42649
42740
  _busLogger = getLogger2("event-bus");
@@ -42888,7 +42979,7 @@ var init_event_bus = __esm(() => {
42888
42979
  init_metrics();
42889
42980
  });
42890
42981
 
42891
- // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.11+08507d81f591b81d/node_modules/@gobing-ai/ts-infra/dist/event-bus/default-observers.js
42982
+ // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.14+a32276a722fab3c8/node_modules/@gobing-ai/ts-infra/dist/event-bus/default-observers.js
42892
42983
  function obsLogger() {
42893
42984
  if (!_obsLogger)
42894
42985
  _obsLogger = getLogger2("event-bus.observers");
@@ -42952,7 +43043,7 @@ var init_default_observers = __esm(() => {
42952
43043
  init_event_bus();
42953
43044
  });
42954
43045
 
42955
- // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.11+08507d81f591b81d/node_modules/@gobing-ai/ts-infra/dist/event-bus/file-observer.js
43046
+ // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.14+a32276a722fab3c8/node_modules/@gobing-ai/ts-infra/dist/event-bus/file-observer.js
42956
43047
  function parentDir(filePath) {
42957
43048
  const idx = filePath.lastIndexOf("/");
42958
43049
  return idx <= 0 ? "." : filePath.slice(0, idx);
@@ -43050,7 +43141,7 @@ var init_file_observer = __esm(() => {
43050
43141
  init_logger3();
43051
43142
  });
43052
43143
 
43053
- // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.11+08507d81f591b81d/node_modules/@gobing-ai/ts-infra/dist/scheduler/node.js
43144
+ // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.14+a32276a722fab3c8/node_modules/@gobing-ai/ts-infra/dist/scheduler/node.js
43054
43145
  function parseInterval(cron) {
43055
43146
  const trimmed = cron.trim();
43056
43147
  const num = Number(trimmed);
@@ -43124,7 +43215,7 @@ var init_node3 = __esm(() => {
43124
43215
  init_metrics();
43125
43216
  });
43126
43217
 
43127
- // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.11+08507d81f591b81d/node_modules/@gobing-ai/ts-infra/dist/scheduler-node.js
43218
+ // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.14+a32276a722fab3c8/node_modules/@gobing-ai/ts-infra/dist/scheduler-node.js
43128
43219
  var exports_scheduler_node = {};
43129
43220
  __export(exports_scheduler_node, {
43130
43221
  NodeSchedulerAdapter: () => NodeSchedulerAdapter
@@ -49380,6 +49471,168 @@ var init_errors5 = __esm(() => {
49380
49471
  };
49381
49472
  });
49382
49473
 
49474
+ // ../../packages/app/src/observability/agent-execution.ts
49475
+ class AgentExecutionLifecycle {
49476
+ observer;
49477
+ secrets;
49478
+ heartbeatMs;
49479
+ sequence = 0;
49480
+ correlation;
49481
+ queue = [];
49482
+ draining = false;
49483
+ dropped = 0;
49484
+ heartbeat;
49485
+ startedAt = 0;
49486
+ timeoutMs;
49487
+ outputCarry = { stdout: "", stderr: "" };
49488
+ constructor(observer, correlation, secrets, heartbeatMs = 30000) {
49489
+ this.observer = observer;
49490
+ this.secrets = secrets;
49491
+ this.heartbeatMs = heartbeatMs;
49492
+ this.correlation = correlation ?? {
49493
+ runId: crypto.randomUUID(),
49494
+ executionId: crypto.randomUUID()
49495
+ };
49496
+ }
49497
+ get identity() {
49498
+ return this.correlation;
49499
+ }
49500
+ start(detail) {
49501
+ this.startedAt = Date.now();
49502
+ this.timeoutMs = detail.timeoutMs;
49503
+ this.emit({
49504
+ kind: "started",
49505
+ agent: redactAndBound(detail.agent, this.secrets, 256),
49506
+ ...detail.model !== undefined ? { model: redactAndBound(detail.model, this.secrets, 256) } : {},
49507
+ invocation: redactAndBound(detail.invocation, this.secrets, 512),
49508
+ ...detail.timeoutMs !== undefined ? { timeoutMs: detail.timeoutMs } : {}
49509
+ });
49510
+ if (!this.observer || this.heartbeatMs <= 0)
49511
+ return;
49512
+ this.heartbeat = setInterval(() => {
49513
+ this.drain();
49514
+ this.emit({
49515
+ kind: "heartbeat",
49516
+ elapsedMs: Math.max(0, Date.now() - this.startedAt),
49517
+ ...this.timeoutMs !== undefined ? { timeoutMs: this.timeoutMs } : {}
49518
+ });
49519
+ }, this.heartbeatMs);
49520
+ this.heartbeat.unref?.();
49521
+ }
49522
+ observe(output2) {
49523
+ if (!this.observer)
49524
+ return;
49525
+ const combined = this.outputCarry[output2.stream] + output2.chunk;
49526
+ const { ready, carry } = redactStreamingValue(combined, this.secrets);
49527
+ this.outputCarry[output2.stream] = carry;
49528
+ if (ready === "")
49529
+ return;
49530
+ this.enqueueOutput(output2.stream, ready);
49531
+ }
49532
+ finish(detail) {
49533
+ if (this.heartbeat !== undefined)
49534
+ clearInterval(this.heartbeat);
49535
+ this.heartbeat = undefined;
49536
+ for (const stream of ["stdout", "stderr"]) {
49537
+ const carry = this.outputCarry[stream];
49538
+ this.outputCarry[stream] = "";
49539
+ if (carry !== "")
49540
+ this.enqueueOutput(stream, redactAndBound(carry, this.secrets, MAX_CHUNK_CHARS));
49541
+ }
49542
+ this.drain();
49543
+ if (this.dropped > 0) {
49544
+ this.emit({ kind: "dropped", chunks: this.dropped });
49545
+ this.dropped = 0;
49546
+ }
49547
+ const cancelled = detail.signal !== undefined || detail.reason === "cancelled";
49548
+ this.emit({
49549
+ kind: "finished",
49550
+ outcome: cancelled ? "cancelled" : detail.exitCode === 0 ? "done" : "failed",
49551
+ exitCode: detail.exitCode,
49552
+ durationMs: detail.durationMs,
49553
+ ...detail.signal !== undefined ? { signal: detail.signal } : {},
49554
+ usage: "unavailable",
49555
+ ...detail.reason !== undefined ? { reason: redactAndBound(detail.reason, this.secrets, 512) } : {}
49556
+ });
49557
+ }
49558
+ enqueueOutput(stream, chunk) {
49559
+ if (this.queue.length >= MAX_PENDING_CHUNKS) {
49560
+ this.dropped += 1;
49561
+ return;
49562
+ }
49563
+ this.queue.push(this.makeEvent({
49564
+ kind: "output",
49565
+ stream,
49566
+ chunk: redactAndBound(chunk, this.secrets, MAX_CHUNK_CHARS)
49567
+ }));
49568
+ this.scheduleDrain();
49569
+ }
49570
+ scheduleDrain() {
49571
+ if (this.draining)
49572
+ return;
49573
+ this.draining = true;
49574
+ queueMicrotask(() => this.drain());
49575
+ }
49576
+ drain() {
49577
+ this.draining = false;
49578
+ while (this.queue.length > 0) {
49579
+ const event = this.queue.shift();
49580
+ if (event !== undefined)
49581
+ this.deliver(event);
49582
+ }
49583
+ }
49584
+ emit(detail) {
49585
+ this.deliver(this.makeEvent(detail));
49586
+ }
49587
+ makeEvent(detail) {
49588
+ this.sequence += 1;
49589
+ return {
49590
+ ...detail,
49591
+ schemaVersion: 1,
49592
+ eventId: crypto.randomUUID(),
49593
+ sequence: this.sequence,
49594
+ runId: this.correlation.runId,
49595
+ executionId: this.correlation.executionId,
49596
+ ...this.correlation.actionId !== undefined ? { actionId: this.correlation.actionId } : {},
49597
+ at: new Date().toISOString()
49598
+ };
49599
+ }
49600
+ deliver(event) {
49601
+ try {
49602
+ this.observer?.(event);
49603
+ } catch {}
49604
+ }
49605
+ }
49606
+ function configuredSecretValues(env) {
49607
+ return Object.entries(env).filter(([key, value]) => /(?:secret|token|password|api[_-]?key|credential)/i.test(key) && (value?.length ?? 0) >= 4).map(([, value]) => value);
49608
+ }
49609
+ function redactAndBound(value, secrets, maxChars) {
49610
+ let redacted = value.replace(SECRET_PATTERN, "[REDACTED]");
49611
+ for (const secret of secrets) {
49612
+ if (secret !== "")
49613
+ redacted = redacted.split(secret).join("[REDACTED]");
49614
+ }
49615
+ return redacted.length <= maxChars ? redacted : `${redacted.slice(0, maxChars)}\u2026`;
49616
+ }
49617
+ function redactStreamingValue(value, secrets) {
49618
+ const redacted = redactAndBound(value, secrets, Number.MAX_SAFE_INTEGER);
49619
+ let carryLength = 0;
49620
+ for (const secret of secrets) {
49621
+ for (let length = 1;length < secret.length && length <= redacted.length; length += 1) {
49622
+ if (redacted.endsWith(secret.slice(0, length)))
49623
+ carryLength = Math.max(carryLength, length);
49624
+ }
49625
+ }
49626
+ return {
49627
+ ready: carryLength === 0 ? redacted : redacted.slice(0, -carryLength),
49628
+ carry: carryLength === 0 ? "" : redacted.slice(-carryLength)
49629
+ };
49630
+ }
49631
+ var MAX_CHUNK_CHARS = 4096, MAX_PENDING_CHUNKS = 64, SECRET_PATTERN;
49632
+ var init_agent_execution = __esm(() => {
49633
+ SECRET_PATTERN = /(?:sk-[a-z0-9_-]{8,}|(?:api[_-]?key|token|secret|password)\s*[:=]\s*\S+|bearer\s+\S+)/gi;
49634
+ });
49635
+
49383
49636
  // ../../packages/domain/src/analytics/models.ts
49384
49637
  function resolvePricing(model) {
49385
49638
  if (model === undefined || model.length === 0)
@@ -50657,7 +50910,7 @@ var init_drizzle_zod = __esm(() => {
50657
50910
  };
50658
50911
  });
50659
50912
 
50660
- // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.11+3ced2785cbe4d5ed/node_modules/@gobing-ai/ts-db/dist/schema/drizzle-internals.js
50913
+ // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.14+eb0d628435e6547b/node_modules/@gobing-ai/ts-db/dist/schema/drizzle-internals.js
50661
50914
  function sqlExpressionToText(value) {
50662
50915
  if (typeof value !== "object" || value === null || !("queryChunks" in value)) {
50663
50916
  return;
@@ -50680,7 +50933,7 @@ function getDrizzleTableName(table3) {
50680
50933
  return String(table3[nameSym]);
50681
50934
  }
50682
50935
 
50683
- // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.11+3ced2785cbe4d5ed/node_modules/@gobing-ai/ts-db/dist/schema/ddl.js
50936
+ // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.14+eb0d628435e6547b/node_modules/@gobing-ai/ts-db/dist/schema/ddl.js
50684
50937
  function quoteIdent(name) {
50685
50938
  return `"${name.replace(/"/g, '""')}"`;
50686
50939
  }
@@ -50774,7 +51027,7 @@ var init_ddl = __esm(() => {
50774
51027
  init_sqlite_core();
50775
51028
  });
50776
51029
 
50777
- // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.11+3ced2785cbe4d5ed/node_modules/@gobing-ai/ts-db/dist/schema/define-table.js
51030
+ // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.14+eb0d628435e6547b/node_modules/@gobing-ai/ts-db/dist/schema/define-table.js
50778
51031
  function defineTable(name, columns2) {
50779
51032
  const table3 = sqliteTable(name, columns2);
50780
51033
  let insert2;
@@ -50808,7 +51061,7 @@ var init_define_table = __esm(() => {
50808
51061
  init_ddl();
50809
51062
  });
50810
51063
 
50811
- // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.11+3ced2785cbe4d5ed/node_modules/@gobing-ai/ts-db/dist/schema/index.js
51064
+ // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.14+eb0d628435e6547b/node_modules/@gobing-ai/ts-db/dist/schema/index.js
50812
51065
  var init_schema = __esm(() => {
50813
51066
  init_sqlite_core();
50814
51067
  init_define_table();
@@ -51137,6 +51390,15 @@ var init_run_dao = __esm(() => {
51137
51390
  stampMetadata(runId, metadata) {
51138
51391
  return this.adapter.run("UPDATE runs SET metadata_json = ? WHERE id = ?", JSON.stringify(metadata), runId);
51139
51392
  }
51393
+ stampFailureReason(runId, reason) {
51394
+ return this.adapter.run(`UPDATE runs
51395
+ SET metadata_json = json_set(
51396
+ COALESCE(NULLIF(metadata_json, ''), '{}'),
51397
+ '$.failureReason',
51398
+ ?1
51399
+ )
51400
+ WHERE id = ?2`, reason, runId);
51401
+ }
51140
51402
  listStaleRuns(cutoffIso) {
51141
51403
  return this.adapter.queryAll(`SELECT id, status, started_at
51142
51404
  FROM runs
@@ -51384,7 +51646,7 @@ var init_dao = __esm(() => {
51384
51646
  init_workspace_dao();
51385
51647
  });
51386
51648
 
51387
- // ../../node_modules/.bun/@gobing-ai+ts-dual-workflow-engine@0.4.11+eb8900f44f4776f9/node_modules/@gobing-ai/ts-dual-workflow-engine/dist/errors.js
51649
+ // ../../node_modules/.bun/@gobing-ai+ts-dual-workflow-engine@0.4.14+eb8900f44f4776f9/node_modules/@gobing-ai/ts-dual-workflow-engine/dist/errors.js
51388
51650
  var WorkflowValidationError, FSMError, RunCollisionError, WorkflowResumeError;
51389
51651
  var init_errors6 = __esm(() => {
51390
51652
  WorkflowValidationError = class WorkflowValidationError extends Error {
@@ -51419,12 +51681,12 @@ var init_errors6 = __esm(() => {
51419
51681
  };
51420
51682
  });
51421
51683
 
51422
- // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.11+08507d81f591b81d/node_modules/@gobing-ai/ts-infra/dist/event-bus/index.js
51684
+ // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.14+a32276a722fab3c8/node_modules/@gobing-ai/ts-infra/dist/event-bus/index.js
51423
51685
  var init_event_bus2 = __esm(() => {
51424
51686
  init_event_bus();
51425
51687
  });
51426
51688
 
51427
- // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.11+08507d81f591b81d/node_modules/@gobing-ai/ts-infra/dist/api-client.js
51689
+ // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.14+a32276a722fab3c8/node_modules/@gobing-ai/ts-infra/dist/api-client.js
51428
51690
  function sanitizeUrlForObservability(url2) {
51429
51691
  try {
51430
51692
  const parsed = new URL(url2);
@@ -51654,12 +51916,12 @@ var init_api_client = __esm(() => {
51654
51916
  };
51655
51917
  });
51656
51918
 
51657
- // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.11+08507d81f591b81d/node_modules/@gobing-ai/ts-infra/dist/telemetry/index.js
51919
+ // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.14+a32276a722fab3c8/node_modules/@gobing-ai/ts-infra/dist/telemetry/index.js
51658
51920
  var init_telemetry = __esm(() => {
51659
51921
  init_tracing2();
51660
51922
  });
51661
51923
 
51662
- // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.11+08507d81f591b81d/node_modules/@gobing-ai/ts-infra/dist/index.js
51924
+ // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.14+a32276a722fab3c8/node_modules/@gobing-ai/ts-infra/dist/index.js
51663
51925
  var init_dist6 = __esm(() => {
51664
51926
  init_api_client();
51665
51927
  init_event_bus2();
@@ -51667,7 +51929,7 @@ var init_dist6 = __esm(() => {
51667
51929
  init_telemetry();
51668
51930
  });
51669
51931
 
51670
- // ../../node_modules/.bun/@gobing-ai+ts-dual-workflow-engine@0.4.11+eb8900f44f4776f9/node_modules/@gobing-ai/ts-dual-workflow-engine/dist/run-lifecycle.js
51932
+ // ../../node_modules/.bun/@gobing-ai+ts-dual-workflow-engine@0.4.14+eb8900f44f4776f9/node_modules/@gobing-ai/ts-dual-workflow-engine/dist/run-lifecycle.js
51671
51933
  function runtimeBuiltins(workflowName, stateOrNodeId, runId, transitionsTaken, mode) {
51672
51934
  return {
51673
51935
  workflow: workflowName,
@@ -51740,9 +52002,12 @@ class RunLifecycle {
51740
52002
  return await loop(lifecycle);
51741
52003
  }, { attributes: { "workflow.name": workflowName, "workflow.mode": mode } });
51742
52004
  }
51743
- async enter(stateOrNodeId, transitionsTaken, persist = true) {
52005
+ async enter(stateOrNodeId, transitionsTaken, persist = true, vars) {
51744
52006
  if (persist) {
51745
- await this.persistence.saveWorkflowState(this.runId, stateOrNodeId, { transitionsTaken });
52007
+ const data = { transitionsTaken };
52008
+ if (vars !== undefined)
52009
+ data.effectiveVars = vars;
52010
+ await this.persistence.saveWorkflowState(this.runId, stateOrNodeId, data);
51746
52011
  await this.persistence.savePhase(this.runId, stateOrNodeId, "running");
51747
52012
  }
51748
52013
  addSpanEvent("workflow.node.enter", {
@@ -51756,8 +52021,11 @@ class RunLifecycle {
51756
52021
  transitionsTaken
51757
52022
  });
51758
52023
  }
51759
- async commitHop(from, to, trigger, transitionsTaken, phase) {
51760
- await this.persistence.commitTransition(this.runId, from, to, trigger, to, { transitionsTaken }, phase);
52024
+ async commitHop(from, to, trigger, transitionsTaken, phase, vars) {
52025
+ const data = { transitionsTaken };
52026
+ if (vars !== undefined)
52027
+ data.effectiveVars = vars;
52028
+ await this.persistence.commitTransition(this.runId, from, to, trigger, to, data, phase);
51761
52029
  addSpanEvent("workflow.node.transition", {
51762
52030
  runId: this.runId,
51763
52031
  from,
@@ -51814,7 +52082,11 @@ class RunLifecycle {
51814
52082
  this.logger.warn("workflow run failed", { finalState, transitionsTaken, reason });
51815
52083
  return this.result("failed", finalState, transitionsTaken, reason);
51816
52084
  }
51817
- async pause(stateOrNodeId, transitionsTaken) {
52085
+ async pause(stateOrNodeId, transitionsTaken, vars) {
52086
+ const data = { transitionsTaken };
52087
+ if (vars !== undefined)
52088
+ data.effectiveVars = vars;
52089
+ await this.persistence.saveWorkflowState(this.runId, stateOrNodeId, data);
51818
52090
  await this.persistence.savePhase(this.runId, stateOrNodeId, "paused");
51819
52091
  await this.persistence.finalizeRun(this.runId, "paused", new Date().toISOString());
51820
52092
  this.logger.info("workflow run paused", { stateOrNodeId, transitionsTaken });
@@ -51916,7 +52188,7 @@ var init_run_lifecycle = __esm(() => {
51916
52188
  ];
51917
52189
  });
51918
52190
 
51919
- // ../../node_modules/.bun/@gobing-ai+ts-dual-workflow-engine@0.4.11+eb8900f44f4776f9/node_modules/@gobing-ai/ts-dual-workflow-engine/dist/schema.js
52191
+ // ../../node_modules/.bun/@gobing-ai+ts-dual-workflow-engine@0.4.14+eb8900f44f4776f9/node_modules/@gobing-ai/ts-dual-workflow-engine/dist/schema.js
51920
52192
  var RESERVED_VAR_NAMES, IDENTIFIER, VarsSchema, EnvSchema, ActionDefSchema, GuardDefSchema, StateMachineWorkflowDefSchema, TransitionFlowWorkflowDefSchema, WorkflowDefSchema;
51921
52193
  var init_schema2 = __esm(() => {
51922
52194
  init_zod();
@@ -52000,7 +52272,7 @@ var init_schema2 = __esm(() => {
52000
52272
  WorkflowDefSchema = exports_external.union([StateMachineWorkflowDefSchema, TransitionFlowWorkflowDefSchema]);
52001
52273
  });
52002
52274
 
52003
- // ../../node_modules/.bun/@gobing-ai+ts-dual-workflow-engine@0.4.11+eb8900f44f4776f9/node_modules/@gobing-ai/ts-dual-workflow-engine/dist/config.js
52275
+ // ../../node_modules/.bun/@gobing-ai+ts-dual-workflow-engine@0.4.14+eb8900f44f4776f9/node_modules/@gobing-ai/ts-dual-workflow-engine/dist/config.js
52004
52276
  async function loadWorkflowDef(path9, options = {}) {
52005
52277
  return parseWorkflowDef(await loadStructuredConfig(path9, options), path9);
52006
52278
  }
@@ -52159,7 +52431,7 @@ var init_config4 = __esm(() => {
52159
52431
  RUNTIME_TEMPLATE_NAMESPACES = new Set(RUNTIME_BUILTIN_KEYS);
52160
52432
  });
52161
52433
 
52162
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.11+b7f415af47cc46d8/node_modules/@gobing-ai/ts-runtime/dist/extension/capability-registry.js
52434
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.14+1afbae169bbe5102/node_modules/@gobing-ai/ts-runtime/dist/extension/capability-registry.js
52163
52435
  class CapabilityRegistry {
52164
52436
  kind;
52165
52437
  capabilities = new Map;
@@ -52190,7 +52462,7 @@ class CapabilityRegistry {
52190
52462
  }
52191
52463
  }
52192
52464
 
52193
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.11+b7f415af47cc46d8/node_modules/@gobing-ai/ts-runtime/dist/extension/extension-path.js
52465
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.14+1afbae169bbe5102/node_modules/@gobing-ai/ts-runtime/dist/extension/extension-path.js
52194
52466
  function assertRelativeExtensionPath(path9, options = {}) {
52195
52467
  const where = options.sourceName !== undefined ? ` declared by "${options.sourceName}"` : "";
52196
52468
  if (/^([/\\]|[A-Za-z]:[/\\])/.test(path9)) {
@@ -52201,20 +52473,20 @@ function assertRelativeExtensionPath(path9, options = {}) {
52201
52473
  }
52202
52474
  }
52203
52475
 
52204
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.11+b7f415af47cc46d8/node_modules/@gobing-ai/ts-runtime/dist/extension/extension-loader.js
52476
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.14+1afbae169bbe5102/node_modules/@gobing-ai/ts-runtime/dist/extension/extension-loader.js
52205
52477
  var init_extension_loader = () => {};
52206
52478
 
52207
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.11+b7f415af47cc46d8/node_modules/@gobing-ai/ts-runtime/dist/extension/index.js
52479
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.14+1afbae169bbe5102/node_modules/@gobing-ai/ts-runtime/dist/extension/index.js
52208
52480
  var init_extension = __esm(() => {
52209
52481
  init_extension_loader();
52210
52482
  });
52211
52483
 
52212
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.11+b7f415af47cc46d8/node_modules/@gobing-ai/ts-runtime/dist/extension.js
52484
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.14+1afbae169bbe5102/node_modules/@gobing-ai/ts-runtime/dist/extension.js
52213
52485
  var init_extension2 = __esm(() => {
52214
52486
  init_extension();
52215
52487
  });
52216
52488
 
52217
- // ../../node_modules/.bun/@gobing-ai+ts-dual-workflow-engine@0.4.11+eb8900f44f4776f9/node_modules/@gobing-ai/ts-dual-workflow-engine/dist/host.js
52489
+ // ../../node_modules/.bun/@gobing-ai+ts-dual-workflow-engine@0.4.14+eb8900f44f4776f9/node_modules/@gobing-ai/ts-dual-workflow-engine/dist/host.js
52218
52490
  class WorkflowEngineHost {
52219
52491
  lifecycleBus;
52220
52492
  actions = new CapabilityRegistry("workflow action");
@@ -52383,7 +52655,7 @@ var init_host = __esm(() => {
52383
52655
  init_errors6();
52384
52656
  });
52385
52657
 
52386
- // ../../node_modules/.bun/@gobing-ai+ts-dual-workflow-engine@0.4.11+eb8900f44f4776f9/node_modules/@gobing-ai/ts-dual-workflow-engine/dist/schema-sql.js
52658
+ // ../../node_modules/.bun/@gobing-ai+ts-dual-workflow-engine@0.4.14+eb8900f44f4776f9/node_modules/@gobing-ai/ts-dual-workflow-engine/dist/schema-sql.js
52387
52659
  var WORKFLOW_ENGINE_SCHEMA_SQL;
52388
52660
  var init_schema_sql = __esm(() => {
52389
52661
  WORKFLOW_ENGINE_SCHEMA_SQL = `
@@ -52456,7 +52728,7 @@ CREATE TABLE IF NOT EXISTS action_runs (
52456
52728
  `.trim();
52457
52729
  });
52458
52730
 
52459
- // ../../node_modules/.bun/@gobing-ai+ts-dual-workflow-engine@0.4.11+eb8900f44f4776f9/node_modules/@gobing-ai/ts-dual-workflow-engine/dist/persistence.js
52731
+ // ../../node_modules/.bun/@gobing-ai+ts-dual-workflow-engine@0.4.14+eb8900f44f4776f9/node_modules/@gobing-ai/ts-dual-workflow-engine/dist/persistence.js
52460
52732
  async function applyWorkflowEngineSchema(db2) {
52461
52733
  for (const statement of WORKFLOW_ENGINE_SCHEMA_SQL.split(";")) {
52462
52734
  const sql4 = statement.trim();
@@ -52608,6 +52880,21 @@ class DbWorkflowPersistenceAdapter {
52608
52880
  const row = await this.db.queryFirst("SELECT state FROM workflow_states WHERE run_id = ? ORDER BY created_at DESC, rowid DESC LIMIT 1", runId);
52609
52881
  return row?.state;
52610
52882
  }
52883
+ async loadLatestStateSnapshot(runId) {
52884
+ await this.ensureSchema();
52885
+ const row = await this.db.queryFirst("SELECT state, data_json FROM workflow_states WHERE run_id = ? ORDER BY created_at DESC, rowid DESC LIMIT 1", runId);
52886
+ if (row == null)
52887
+ return;
52888
+ let data = {};
52889
+ if (row.data_json !== null && row.data_json.length > 0) {
52890
+ try {
52891
+ data = JSON.parse(row.data_json);
52892
+ } catch {
52893
+ data = {};
52894
+ }
52895
+ }
52896
+ return { state: row.state, data };
52897
+ }
52611
52898
  async listPausedRuns(options) {
52612
52899
  await this.ensureSchema();
52613
52900
  const where = ["status = 'paused'"];
@@ -52710,6 +52997,12 @@ class MemoryWorkflowPersistenceAdapter {
52710
52997
  const last = this.states.findLast((s) => s.runId === runId);
52711
52998
  return last?.state;
52712
52999
  }
53000
+ async loadLatestStateSnapshot(runId) {
53001
+ const last = this.states.findLast((s) => s.runId === runId);
53002
+ if (last === undefined)
53003
+ return;
53004
+ return { state: last.state, data: last.data };
53005
+ }
52713
53006
  async listPausedRuns(options) {
52714
53007
  let runs2 = [...this.runs.values()].filter((r) => r.status === "paused");
52715
53008
  if (options?.workflowName !== undefined) {
@@ -52724,7 +53017,7 @@ var init_persistence = __esm(() => {
52724
53017
  init_schema_sql();
52725
53018
  });
52726
53019
 
52727
- // ../../node_modules/.bun/@gobing-ai+ts-dual-workflow-engine@0.4.11+eb8900f44f4776f9/node_modules/@gobing-ai/ts-dual-workflow-engine/dist/variables.js
53020
+ // ../../node_modules/.bun/@gobing-ai+ts-dual-workflow-engine@0.4.14+eb8900f44f4776f9/node_modules/@gobing-ai/ts-dual-workflow-engine/dist/variables.js
52728
53021
  function mergeVars(workflowVars = {}, overrideVars = {}) {
52729
53022
  return { ...workflowVars, ...overrideVars };
52730
53023
  }
@@ -52784,7 +53077,7 @@ var init_variables = __esm(() => {
52784
53077
  TEMPLATE_REF = /\$\{([^}]+)\}/g;
52785
53078
  });
52786
53079
 
52787
- // ../../node_modules/.bun/@gobing-ai+ts-dual-workflow-engine@0.4.11+eb8900f44f4776f9/node_modules/@gobing-ai/ts-dual-workflow-engine/dist/action-step.js
53080
+ // ../../node_modules/.bun/@gobing-ai+ts-dual-workflow-engine@0.4.14+eb8900f44f4776f9/node_modules/@gobing-ai/ts-dual-workflow-engine/dist/action-step.js
52788
53081
  async function runActionStep(action, vars, deps) {
52789
53082
  const { host, persistence, lifecycle, stateOrNodeId, runId } = deps;
52790
53083
  const resolved = resolveTemplates(action.options ?? {}, {
@@ -52799,6 +53092,7 @@ async function runActionStep(action, vars, deps) {
52799
53092
  try {
52800
53093
  result = await host.runAction(action.kind, resolved, {
52801
53094
  runId,
53095
+ actionId,
52802
53096
  workdir: deps.options.workdir,
52803
53097
  stateOrNodeId,
52804
53098
  vars,
@@ -52841,7 +53135,7 @@ var init_action_step = __esm(() => {
52841
53135
  init_variables();
52842
53136
  });
52843
53137
 
52844
- // ../../node_modules/.bun/@gobing-ai+ts-dual-workflow-engine@0.4.11+eb8900f44f4776f9/node_modules/@gobing-ai/ts-dual-workflow-engine/dist/state-machine.js
53138
+ // ../../node_modules/.bun/@gobing-ai+ts-dual-workflow-engine@0.4.14+eb8900f44f4776f9/node_modules/@gobing-ai/ts-dual-workflow-engine/dist/state-machine.js
52845
53139
  class StateMachineDriver {
52846
53140
  options;
52847
53141
  constructor(options) {
@@ -52875,7 +53169,7 @@ class StateMachineDriver {
52875
53169
  isResume = false;
52876
53170
  persistedViaHop = true;
52877
53171
  } else {
52878
- await lifecycle.enter(current.id, transitionsTaken, !persistedViaHop);
53172
+ await lifecycle.enter(current.id, transitionsTaken, !persistedViaHop, vars);
52879
53173
  const enter = options.dryRun ? EMPTY_OUTCOME : await runActionSequence(current.onEnter ?? [], vars, {
52880
53174
  host: this.options.host,
52881
53175
  persistence: this.options.persistence,
@@ -52900,7 +53194,7 @@ class StateMachineDriver {
52900
53194
  return await lifecycle.fail(current.id, transitionsTaken, lastActionResult?.error);
52901
53195
  }
52902
53196
  if (current.pause === true) {
52903
- return await lifecycle.pause(current.id, transitionsTaken);
53197
+ return await lifecycle.pause(current.id, transitionsTaken, vars);
52904
53198
  }
52905
53199
  }
52906
53200
  const outbound = workflow.transitions.filter((transition) => transition.from === current?.id);
@@ -52939,7 +53233,7 @@ class StateMachineDriver {
52939
53233
  await lifecycle.commitHop(current.id, nextTransition.to, nextTransition.trigger ?? null, transitionsTaken, {
52940
53234
  phase: nextTransition.to,
52941
53235
  status: "running"
52942
- });
53236
+ }, vars);
52943
53237
  if (transitionsTaken > iterationBound) {
52944
53238
  return await lifecycle.fail(current.id, transitionsTaken, "iteration-bound-exceeded");
52945
53239
  }
@@ -52975,7 +53269,7 @@ var init_state_machine = __esm(() => {
52975
53269
  EMPTY_OUTCOME = { outcome: "completed", result: undefined };
52976
53270
  });
52977
53271
 
52978
- // ../../node_modules/.bun/@gobing-ai+ts-dual-workflow-engine@0.4.11+eb8900f44f4776f9/node_modules/@gobing-ai/ts-dual-workflow-engine/dist/transition-flow.js
53272
+ // ../../node_modules/.bun/@gobing-ai+ts-dual-workflow-engine@0.4.14+eb8900f44f4776f9/node_modules/@gobing-ai/ts-dual-workflow-engine/dist/transition-flow.js
52979
53273
  class TransitionFlowDriver {
52980
53274
  options;
52981
53275
  constructor(options) {
@@ -53009,7 +53303,7 @@ class TransitionFlowDriver {
53009
53303
  isResume = false;
53010
53304
  persistedViaHop = true;
53011
53305
  } else {
53012
- await lifecycle.enter(current.id, transitionsTaken, !persistedViaHop);
53306
+ await lifecycle.enter(current.id, transitionsTaken, !persistedViaHop, vars);
53013
53307
  if (options.dryRun) {
53014
53308
  if (current.action !== undefined) {
53015
53309
  lastActionResult = undefined;
@@ -53039,7 +53333,7 @@ class TransitionFlowDriver {
53039
53333
  }
53040
53334
  }
53041
53335
  if (current.pause === true) {
53042
- return await lifecycle.pause(current.id, transitionsTaken);
53336
+ return await lifecycle.pause(current.id, transitionsTaken, vars);
53043
53337
  }
53044
53338
  }
53045
53339
  const outbound = workflow.edges.filter((edge2) => edge2.from === current?.id);
@@ -53059,7 +53353,7 @@ class TransitionFlowDriver {
53059
53353
  await lifecycle.commitHop(current.id, edge.to, edge.condition?.kind ?? null, transitionsTaken, {
53060
53354
  phase: edge.to,
53061
53355
  status: "running"
53062
- });
53356
+ }, vars);
53063
53357
  if (transitionsTaken > iterationBound) {
53064
53358
  return await lifecycle.fail(current.id, transitionsTaken, "iteration-bound-exceeded");
53065
53359
  }
@@ -53093,7 +53387,7 @@ var init_transition_flow = __esm(() => {
53093
53387
  init_variables();
53094
53388
  });
53095
53389
 
53096
- // ../../node_modules/.bun/@gobing-ai+ts-dual-workflow-engine@0.4.11+eb8900f44f4776f9/node_modules/@gobing-ai/ts-dual-workflow-engine/dist/service.js
53390
+ // ../../node_modules/.bun/@gobing-ai+ts-dual-workflow-engine@0.4.14+eb8900f44f4776f9/node_modules/@gobing-ai/ts-dual-workflow-engine/dist/service.js
53097
53391
  class WorkflowService {
53098
53392
  host;
53099
53393
  persistence;
@@ -53183,20 +53477,24 @@ class WorkflowService {
53183
53477
  if (currentState === undefined) {
53184
53478
  throw new WorkflowResumeError(`Run "${runId}" has no persisted state to resume from`);
53185
53479
  }
53480
+ const snapshot = await this.persistence.loadLatestStateSnapshot(runId);
53481
+ const persistedVars = extractEffectiveVars(snapshot?.data);
53482
+ const restoredVars = mergeVars(persistedVars, options?.vars);
53483
+ const mergedOptions = { ...options, vars: restoredVars };
53186
53484
  const extKey = run.external_key ?? undefined;
53187
53485
  await this.persistence.finalizeRun(runId, "running", "");
53188
- const events = this.resolveEvents(options?.events);
53486
+ const events = this.resolveEvents(mergedOptions.events);
53189
53487
  events?.emit("workflow.run.resumed", { runId, node: currentState, externalKey: extKey });
53190
53488
  if (workflow.kind === "transition-flow") {
53191
53489
  return await new TransitionFlowDriver({
53192
53490
  host: this.host,
53193
53491
  persistence: this.persistence
53194
- }).resume(workflow, runId, currentState, extKey, { ...options, events });
53492
+ }).resume(workflow, runId, currentState, extKey, mergedOptions);
53195
53493
  }
53196
53494
  return await new StateMachineDriver({
53197
53495
  host: this.host,
53198
53496
  persistence: this.persistence
53199
- }).resume(workflow, runId, currentState, extKey, { ...options, events });
53497
+ }).resume(workflow, runId, currentState, extKey, mergedOptions);
53200
53498
  }
53201
53499
  async listPausedRuns(options) {
53202
53500
  return await this.persistence.listPausedRuns(options);
@@ -53281,6 +53579,21 @@ class WorkflowService {
53281
53579
  return { allowed: false, ...denial };
53282
53580
  }
53283
53581
  }
53582
+ function extractEffectiveVars(data) {
53583
+ if (data === undefined)
53584
+ return {};
53585
+ const raw = data.effectiveVars;
53586
+ if (raw === undefined || raw === null)
53587
+ return {};
53588
+ if (typeof raw !== "object")
53589
+ return {};
53590
+ const filtered = {};
53591
+ for (const [key, value] of Object.entries(raw)) {
53592
+ if (typeof value === "string")
53593
+ filtered[key] = value;
53594
+ }
53595
+ return filtered;
53596
+ }
53284
53597
  var init_service = __esm(() => {
53285
53598
  init_dist6();
53286
53599
  init_config4();
@@ -53291,7 +53604,7 @@ var init_service = __esm(() => {
53291
53604
  init_variables();
53292
53605
  });
53293
53606
 
53294
- // ../../node_modules/.bun/@gobing-ai+ts-dual-workflow-engine@0.4.11+eb8900f44f4776f9/node_modules/@gobing-ai/ts-dual-workflow-engine/dist/index.js
53607
+ // ../../node_modules/.bun/@gobing-ai+ts-dual-workflow-engine@0.4.14+eb8900f44f4776f9/node_modules/@gobing-ai/ts-dual-workflow-engine/dist/index.js
53295
53608
  var init_dist7 = __esm(() => {
53296
53609
  init_config4();
53297
53610
  init_host();
@@ -53300,7 +53613,7 @@ var init_dist7 = __esm(() => {
53300
53613
  init_service();
53301
53614
  });
53302
53615
 
53303
- // ../../node_modules/.bun/@gobing-ai+ts-llm-jsonl-importer@0.4.11+d5c29f764bf0e6e0/node_modules/@gobing-ai/ts-llm-jsonl-importer/dist/errors.js
53616
+ // ../../node_modules/.bun/@gobing-ai+ts-llm-jsonl-importer@0.4.14+d5c29f764bf0e6e0/node_modules/@gobing-ai/ts-llm-jsonl-importer/dist/errors.js
53304
53617
  var HistoryImportError;
53305
53618
  var init_errors7 = __esm(() => {
53306
53619
  HistoryImportError = class HistoryImportError extends Error {
@@ -53313,7 +53626,7 @@ var init_errors7 = __esm(() => {
53313
53626
  };
53314
53627
  });
53315
53628
 
53316
- // ../../node_modules/.bun/@gobing-ai+ts-llm-jsonl-importer@0.4.11+d5c29f764bf0e6e0/node_modules/@gobing-ai/ts-llm-jsonl-importer/dist/hash.js
53629
+ // ../../node_modules/.bun/@gobing-ai+ts-llm-jsonl-importer@0.4.14+d5c29f764bf0e6e0/node_modules/@gobing-ai/ts-llm-jsonl-importer/dist/hash.js
53317
53630
  import { createHash } from "crypto";
53318
53631
  function stableJson(value) {
53319
53632
  if (Array.isArray(value)) {
@@ -53330,7 +53643,7 @@ function sha256(value) {
53330
53643
  }
53331
53644
  var init_hash = () => {};
53332
53645
 
53333
- // ../../node_modules/.bun/@gobing-ai+ts-llm-jsonl-importer@0.4.11+d5c29f764bf0e6e0/node_modules/@gobing-ai/ts-llm-jsonl-importer/dist/redaction.js
53646
+ // ../../node_modules/.bun/@gobing-ai+ts-llm-jsonl-importer@0.4.14+d5c29f764bf0e6e0/node_modules/@gobing-ai/ts-llm-jsonl-importer/dist/redaction.js
53334
53647
  function redactValue(value, rules = DEFAULT_REDACTION_RULES) {
53335
53648
  if (typeof value === "string") {
53336
53649
  return rules.reduce((current, rule) => current.replace(rule.pattern, rule.replacement), value);
@@ -53367,7 +53680,7 @@ var init_redaction = __esm(() => {
53367
53680
  ];
53368
53681
  });
53369
53682
 
53370
- // ../../node_modules/.bun/@gobing-ai+ts-llm-jsonl-importer@0.4.11+d5c29f764bf0e6e0/node_modules/@gobing-ai/ts-llm-jsonl-importer/dist/schema-sql.js
53683
+ // ../../node_modules/.bun/@gobing-ai+ts-llm-jsonl-importer@0.4.14+d5c29f764bf0e6e0/node_modules/@gobing-ai/ts-llm-jsonl-importer/dist/schema-sql.js
53371
53684
  var HISTORY_IMPORT_SCHEMA_SQL;
53372
53685
  var init_schema_sql2 = __esm(() => {
53373
53686
  HISTORY_IMPORT_SCHEMA_SQL = `
@@ -53454,7 +53767,7 @@ CREATE TABLE IF NOT EXISTS history_etl_openclaw (
53454
53767
  `.trim();
53455
53768
  });
53456
53769
 
53457
- // ../../node_modules/.bun/@gobing-ai+ts-llm-jsonl-importer@0.4.11+d5c29f764bf0e6e0/node_modules/@gobing-ai/ts-llm-jsonl-importer/dist/sources.js
53770
+ // ../../node_modules/.bun/@gobing-ai+ts-llm-jsonl-importer@0.4.14+d5c29f764bf0e6e0/node_modules/@gobing-ai/ts-llm-jsonl-importer/dist/sources.js
53458
53771
  function firstString(...values) {
53459
53772
  for (const value of values) {
53460
53773
  if (typeof value === "string" && value.length > 0)
@@ -53542,7 +53855,7 @@ var init_sources = __esm(() => {
53542
53855
  };
53543
53856
  });
53544
53857
 
53545
- // ../../node_modules/.bun/@gobing-ai+ts-llm-jsonl-importer@0.4.11+d5c29f764bf0e6e0/node_modules/@gobing-ai/ts-llm-jsonl-importer/dist/importer.js
53858
+ // ../../node_modules/.bun/@gobing-ai+ts-llm-jsonl-importer@0.4.14+d5c29f764bf0e6e0/node_modules/@gobing-ai/ts-llm-jsonl-importer/dist/importer.js
53546
53859
  async function applyHistoryImportSchema(db2) {
53547
53860
  for (const statement of HISTORY_IMPORT_SCHEMA_SQL.split(";")) {
53548
53861
  const sql4 = statement.trim();
@@ -53779,13 +54092,13 @@ var init_importer = __esm(() => {
53779
54092
  VALID_TABLE_NAME = /^history_etl_[a-z_]+$/;
53780
54093
  });
53781
54094
 
53782
- // ../../node_modules/.bun/@gobing-ai+ts-llm-jsonl-importer@0.4.11+d5c29f764bf0e6e0/node_modules/@gobing-ai/ts-llm-jsonl-importer/dist/index.js
54095
+ // ../../node_modules/.bun/@gobing-ai+ts-llm-jsonl-importer@0.4.14+d5c29f764bf0e6e0/node_modules/@gobing-ai/ts-llm-jsonl-importer/dist/index.js
53783
54096
  var init_dist8 = __esm(() => {
53784
54097
  init_importer();
53785
54098
  init_schema_sql2();
53786
54099
  });
53787
54100
 
53788
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.11+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/config/extensions.js
54101
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.14+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/config/extensions.js
53789
54102
  function collectExtensions(sourceName, sourceDir, extensions) {
53790
54103
  if (extensions === undefined)
53791
54104
  return [];
@@ -53801,7 +54114,7 @@ var init_extensions = __esm(() => {
53801
54114
  init_dist5();
53802
54115
  });
53803
54116
 
53804
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.11+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/types.js
54117
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.14+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/types.js
53805
54118
  function createFinding(rule, message, filePath, extras = {}) {
53806
54119
  return {
53807
54120
  ruleId: rule.id,
@@ -53872,7 +54185,7 @@ var init_types3 = __esm(() => {
53872
54185
  });
53873
54186
  });
53874
54187
 
53875
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.11+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/config/loader.js
54188
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.14+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/config/loader.js
53876
54189
  async function loadPreset(name, options) {
53877
54190
  const merged = await buildMergedRoots(options.roots.map((root) => resolvePath4(root)), options.fileSystem);
53878
54191
  const presetPath = findMergedPreset(merged, name);
@@ -54087,7 +54400,7 @@ var init_loader2 = __esm(() => {
54087
54400
  FIX_MODE_AUTHORITY = { none: 0, suggest: 1, auto: 2 };
54088
54401
  });
54089
54402
 
54090
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.11+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/fixers/test-stub-fixer.js
54403
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.14+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/fixers/test-stub-fixer.js
54091
54404
  function normalizeFindingPath(filePath) {
54092
54405
  if (!filePath || isAbsolutePath2(filePath))
54093
54406
  return null;
@@ -54152,7 +54465,7 @@ var init_test_stub_fixer = __esm(() => {
54152
54465
  realFileSystem = createNodeFileSystem3();
54153
54466
  });
54154
54467
 
54155
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.11+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/fixers/fixers.js
54468
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.14+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/fixers/fixers.js
54156
54469
  function registerBuiltinFixers(host, exec) {
54157
54470
  const regexFixer = new RegexFixerProvider;
54158
54471
  const pathFixer = new PathFixerProvider;
@@ -54355,7 +54668,7 @@ var init_fixers = __esm(() => {
54355
54668
  };
54356
54669
  });
54357
54670
 
54358
- // ../../node_modules/.bun/@gobing-ai+ts-ai-runner@0.4.11+267785e00c831d29/node_modules/@gobing-ai/ts-ai-runner/dist/agents/shims.js
54671
+ // ../../node_modules/.bun/@gobing-ai+ts-ai-runner@0.4.14+127e896e9a498407/node_modules/@gobing-ai/ts-ai-runner/dist/agents/shims.js
54359
54672
  function resolveAgentName(input) {
54360
54673
  if (isCanonicalName(input))
54361
54674
  return input;
@@ -54606,7 +54919,7 @@ var init_shims = __esm(() => {
54606
54919
  ALIAS_TO_CANONICAL = Object.fromEntries(Object.values(AGENT_SHIMS).flatMap((shim) => (shim.aliases ?? []).map((alias3) => [alias3, shim.name])));
54607
54920
  });
54608
54921
 
54609
- // ../../node_modules/.bun/@gobing-ai+ts-ai-runner@0.4.11+267785e00c831d29/node_modules/@gobing-ai/ts-ai-runner/dist/identity.js
54922
+ // ../../node_modules/.bun/@gobing-ai+ts-ai-runner@0.4.14+127e896e9a498407/node_modules/@gobing-ai/ts-ai-runner/dist/identity.js
54610
54923
  function buildIdentityPreamble(ctx) {
54611
54924
  const sections = [
54612
54925
  `You are agent \`${ctx.agentId}\` (${ctx.agentType}) in workspace \`${ctx.workspace}\`.`
@@ -54666,7 +54979,7 @@ var init_identity = __esm(() => {
54666
54979
  init_dist5();
54667
54980
  });
54668
54981
 
54669
- // ../../node_modules/.bun/@gobing-ai+ts-ai-runner@0.4.11+267785e00c831d29/node_modules/@gobing-ai/ts-ai-runner/dist/slash-command.js
54982
+ // ../../node_modules/.bun/@gobing-ai+ts-ai-runner@0.4.14+127e896e9a498407/node_modules/@gobing-ai/ts-ai-runner/dist/slash-command.js
54670
54983
  function isClaudeStyleSlashCommand(input) {
54671
54984
  return SLASH_COMMAND_RE.test(input);
54672
54985
  }
@@ -54694,7 +55007,7 @@ var init_slash_command = __esm(() => {
54694
55007
  SLASH_COMMAND_RE = /^\/([a-zA-Z0-9._-]+):([a-zA-Z0-9._-]+)(\s.*)?$/;
54695
55008
  });
54696
55009
 
54697
- // ../../node_modules/.bun/@gobing-ai+ts-ai-runner@0.4.11+267785e00c831d29/node_modules/@gobing-ai/ts-ai-runner/dist/ai-runner.js
55010
+ // ../../node_modules/.bun/@gobing-ai+ts-ai-runner@0.4.14+127e896e9a498407/node_modules/@gobing-ai/ts-ai-runner/dist/ai-runner.js
54698
55011
  class AiRunner {
54699
55012
  processExecutor;
54700
55013
  defaultCwd;
@@ -54747,7 +55060,12 @@ class AiRunner {
54747
55060
  async invoke(agent, operation, command, options, forceBuffered) {
54748
55061
  const label = `ai-runner.${agent}.${operation}`;
54749
55062
  this.logger.debug("invoke", { label, command: command.command, args: command.args.join(" ") });
54750
- this.events?.emit("agent.invoke.start", { agent, operation, label });
55063
+ this.events?.emit("agent.invoke.start", {
55064
+ agent,
55065
+ operation,
55066
+ label,
55067
+ ...options.correlation !== undefined ? { correlation: options.correlation } : {}
55068
+ });
54751
55069
  const result = await this.processExecutor.run({
54752
55070
  command: command.command,
54753
55071
  args: command.args,
@@ -54756,7 +55074,8 @@ class AiRunner {
54756
55074
  forceBuffered,
54757
55075
  cwd: options.cwd ?? this.defaultCwd,
54758
55076
  timeout: options.timeout ?? this.defaultTimeout,
54759
- ...options.signal !== undefined ? { signal: options.signal } : {}
55077
+ ...options.signal !== undefined ? { signal: options.signal } : {},
55078
+ ...options.onOutput !== undefined ? { onOutput: options.onOutput } : {}
54760
55079
  });
54761
55080
  if (result.exitCode !== 0) {
54762
55081
  this.logger.error("invoke exited non-zero", {
@@ -54771,7 +55090,8 @@ class AiRunner {
54771
55090
  label,
54772
55091
  exitCode: result.exitCode,
54773
55092
  ...result.signal !== undefined ? { signal: result.signal } : {},
54774
- durationMs: result.durationMs
55093
+ durationMs: result.durationMs,
55094
+ ...options.correlation !== undefined ? { correlation: options.correlation } : {}
54775
55095
  });
54776
55096
  return {
54777
55097
  exitCode: result.exitCode,
@@ -54812,7 +55132,7 @@ var init_ai_runner = __esm(() => {
54812
55132
  init_slash_command();
54813
55133
  });
54814
55134
 
54815
- // ../../node_modules/.bun/@gobing-ai+ts-ai-runner@0.4.11+267785e00c831d29/node_modules/@gobing-ai/ts-ai-runner/dist/agent-detector.js
55135
+ // ../../node_modules/.bun/@gobing-ai+ts-ai-runner@0.4.14+127e896e9a498407/node_modules/@gobing-ai/ts-ai-runner/dist/agent-detector.js
54816
55136
  class AgentDetector {
54817
55137
  runner;
54818
55138
  timeout;
@@ -54888,7 +55208,7 @@ var init_agent_detector = __esm(() => {
54888
55208
  VERSION_PATTERN = /(?<version>\d+\.\d+(?:\.\d+)?)/;
54889
55209
  });
54890
55210
 
54891
- // ../../node_modules/.bun/@gobing-ai+ts-ai-runner@0.4.11+267785e00c831d29/node_modules/@gobing-ai/ts-ai-runner/dist/agent-spec.js
55211
+ // ../../node_modules/.bun/@gobing-ai+ts-ai-runner@0.4.14+127e896e9a498407/node_modules/@gobing-ai/ts-ai-runner/dist/agent-spec.js
54892
55212
  function validateAgentId(id) {
54893
55213
  if (!/^[a-z][a-z0-9_-]{1,63}$/.test(id)) {
54894
55214
  throw new ValueError(`Invalid agent id "${id}": expected 2-64 chars, lowercase alphanumeric, "_" or "-"`);
@@ -54985,7 +55305,7 @@ var init_agent_spec = __esm(() => {
54985
55305
  };
54986
55306
  });
54987
55307
 
54988
- // ../../node_modules/.bun/@gobing-ai+ts-ai-runner@0.4.11+267785e00c831d29/node_modules/@gobing-ai/ts-ai-runner/dist/agents/auth-shims.js
55308
+ // ../../node_modules/.bun/@gobing-ai+ts-ai-runner@0.4.14+127e896e9a498407/node_modules/@gobing-ai/ts-ai-runner/dist/agents/auth-shims.js
54989
55309
  function isNonEmpty(value) {
54990
55310
  return value !== undefined && value.trim().length > 0;
54991
55311
  }
@@ -55090,7 +55410,7 @@ var init_auth_shims = __esm(() => {
55090
55410
  };
55091
55411
  });
55092
55412
 
55093
- // ../../node_modules/.bun/@gobing-ai+ts-ai-runner@0.4.11+267785e00c831d29/node_modules/@gobing-ai/ts-ai-runner/dist/model-health-probe.js
55413
+ // ../../node_modules/.bun/@gobing-ai+ts-ai-runner@0.4.14+127e896e9a498407/node_modules/@gobing-ai/ts-ai-runner/dist/model-health-probe.js
55094
55414
  class ModelHealthProbeRegistry {
55095
55415
  probes = new Map;
55096
55416
  register(providerPrefix, probe) {
@@ -55198,7 +55518,7 @@ var init_model_health_probe = __esm(() => {
55198
55518
  };
55199
55519
  });
55200
55520
 
55201
- // ../../node_modules/.bun/@gobing-ai+ts-ai-runner@0.4.11+267785e00c831d29/node_modules/@gobing-ai/ts-ai-runner/dist/doctor-runner.js
55521
+ // ../../node_modules/.bun/@gobing-ai+ts-ai-runner@0.4.14+127e896e9a498407/node_modules/@gobing-ai/ts-ai-runner/dist/doctor-runner.js
55202
55522
  class DoctorRunner {
55203
55523
  detector;
55204
55524
  runner;
@@ -55335,15 +55655,15 @@ var init_doctor_runner = __esm(() => {
55335
55655
  init_model_health_probe();
55336
55656
  });
55337
55657
 
55338
- // ../../node_modules/.bun/@gobing-ai+ts-ai-runner@0.4.11+267785e00c831d29/node_modules/@gobing-ai/ts-ai-runner/dist/events.js
55658
+ // ../../node_modules/.bun/@gobing-ai+ts-ai-runner@0.4.14+127e896e9a498407/node_modules/@gobing-ai/ts-ai-runner/dist/events.js
55339
55659
  var init_events = () => {};
55340
55660
 
55341
- // ../../node_modules/.bun/@gobing-ai+ts-ai-runner@0.4.11+267785e00c831d29/node_modules/@gobing-ai/ts-ai-runner/dist/messages.js
55661
+ // ../../node_modules/.bun/@gobing-ai+ts-ai-runner@0.4.14+127e896e9a498407/node_modules/@gobing-ai/ts-ai-runner/dist/messages.js
55342
55662
  function formatMessage(msg) {
55343
55663
  return `[task from=${msg.fromId ?? "operator"} id=${msg.id}] ${msg.body}`;
55344
55664
  }
55345
55665
 
55346
- // ../../node_modules/.bun/@gobing-ai+ts-ai-runner@0.4.11+267785e00c831d29/node_modules/@gobing-ai/ts-ai-runner/dist/team-agent-process.js
55666
+ // ../../node_modules/.bun/@gobing-ai+ts-ai-runner@0.4.14+127e896e9a498407/node_modules/@gobing-ai/ts-ai-runner/dist/team-agent-process.js
55347
55667
  import { Buffer as Buffer5 } from "buffer";
55348
55668
 
55349
55669
  class TeamAgentProcess {
@@ -55477,7 +55797,7 @@ var init_team_agent_process = __esm(() => {
55477
55797
  init_dist5();
55478
55798
  });
55479
55799
 
55480
- // ../../node_modules/.bun/@gobing-ai+ts-ai-runner@0.4.11+267785e00c831d29/node_modules/@gobing-ai/ts-ai-runner/dist/team-orchestrator.js
55800
+ // ../../node_modules/.bun/@gobing-ai+ts-ai-runner@0.4.14+127e896e9a498407/node_modules/@gobing-ai/ts-ai-runner/dist/team-orchestrator.js
55481
55801
  class TeamOrchestrator {
55482
55802
  configDir;
55483
55803
  inbox;
@@ -55606,7 +55926,7 @@ var init_team_orchestrator = __esm(() => {
55606
55926
  init_team_agent_process();
55607
55927
  });
55608
55928
 
55609
- // ../../node_modules/.bun/@gobing-ai+ts-ai-runner@0.4.11+267785e00c831d29/node_modules/@gobing-ai/ts-ai-runner/dist/index.js
55929
+ // ../../node_modules/.bun/@gobing-ai+ts-ai-runner@0.4.14+127e896e9a498407/node_modules/@gobing-ai/ts-ai-runner/dist/index.js
55610
55930
  var exports_dist3 = {};
55611
55931
  __export(exports_dist3, {
55612
55932
  validateAgentId: () => validateAgentId,
@@ -55654,7 +55974,7 @@ var init_dist9 = __esm(() => {
55654
55974
  init_team_orchestrator();
55655
55975
  });
55656
55976
 
55657
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.11+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/config-access.js
55977
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.14+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/config-access.js
55658
55978
  function stringArray(value) {
55659
55979
  return Array.isArray(value) && value.every((item) => typeof item === "string") ? value : undefined;
55660
55980
  }
@@ -55692,7 +56012,7 @@ function parseInlineFlags(source) {
55692
56012
  return { flags, rest: source.slice(match[0].length) };
55693
56013
  }
55694
56014
 
55695
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.11+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/glob-match.js
56015
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.14+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/glob-match.js
55696
56016
  function matchesAny(path9, patterns) {
55697
56017
  if (patterns === undefined || patterns.length === 0)
55698
56018
  return true;
@@ -55740,7 +56060,7 @@ function escapeRegExp(value) {
55740
56060
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
55741
56061
  }
55742
56062
 
55743
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.11+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/file-discovery.js
56063
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.14+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/file-discovery.js
55744
56064
  async function discoverFiles2(options) {
55745
56065
  const fs3 = options.fs ?? createNodeFileSystem3();
55746
56066
  const allFiles = await walkDir2(options.workdir, fs3, DEFAULT_EXCLUDES);
@@ -55768,12 +56088,12 @@ var init_file_discovery = __esm(() => {
55768
56088
  DEFAULT_EXCLUDES = new Set([".git", "node_modules", "dist", ".coverage", ".astro", ".wrangler"]);
55769
56089
  });
55770
56090
 
55771
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.11+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/file-utils.js
56091
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.14+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/file-utils.js
55772
56092
  var init_file_utils = __esm(() => {
55773
56093
  init_file_discovery();
55774
56094
  });
55775
56095
 
55776
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.11+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/agent-detection-evaluator.js
56096
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.14+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/agent-detection-evaluator.js
55777
56097
  class AgentDetectionEvaluator {
55778
56098
  detector;
55779
56099
  constructor(detector = new AgentDetector) {
@@ -55801,7 +56121,7 @@ var init_agent_detection_evaluator = __esm(() => {
55801
56121
  init_file_utils();
55802
56122
  });
55803
56123
 
55804
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.11+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/coverage-gate-evaluator.js
56124
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.14+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/coverage-gate-evaluator.js
55805
56125
  class CoverageGateEvaluator {
55806
56126
  constructor() {}
55807
56127
  async evaluate(rule, context4) {
@@ -55885,7 +56205,7 @@ var init_coverage_gate_evaluator = __esm(() => {
55885
56205
  init_file_utils();
55886
56206
  });
55887
56207
 
55888
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.11+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/exit-code-evaluator.js
56208
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.14+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/exit-code-evaluator.js
55889
56209
  class ExitCodeEvaluator {
55890
56210
  executor;
55891
56211
  constructor(executor = nodeBunFactory3.createProcessExecutor()) {
@@ -55921,7 +56241,7 @@ var init_exit_code_evaluator = __esm(() => {
55921
56241
  init_file_utils();
55922
56242
  });
55923
56243
 
55924
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.11+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/forbidden-import-evaluator.js
56244
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.14+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/forbidden-import-evaluator.js
55925
56245
  class ForbiddenImportEvaluator {
55926
56246
  constructor() {}
55927
56247
  async evaluate(rule, context4) {
@@ -56002,7 +56322,7 @@ var init_forbidden_import_evaluator = __esm(() => {
56002
56322
  init_file_utils();
56003
56323
  });
56004
56324
 
56005
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.11+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/import-boundary-evaluator.js
56325
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.14+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/import-boundary-evaluator.js
56006
56326
  class ImportBoundaryEvaluator {
56007
56327
  async evaluate(rule, context4) {
56008
56328
  const config4 = rule.evaluator.config ?? {};
@@ -56095,7 +56415,7 @@ var init_import_boundary_evaluator = __esm(() => {
56095
56415
  init_file_utils();
56096
56416
  });
56097
56417
 
56098
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.11+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/path-evaluator.js
56418
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.14+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/path-evaluator.js
56099
56419
  class PathEvaluator {
56100
56420
  constructor() {}
56101
56421
  async evaluate(rule, context4) {
@@ -56156,7 +56476,7 @@ var init_path_evaluator = __esm(() => {
56156
56476
  init_file_utils();
56157
56477
  });
56158
56478
 
56159
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.11+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/regex-evaluator.js
56479
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.14+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/regex-evaluator.js
56160
56480
  class RegexEvaluator {
56161
56481
  constructor() {}
56162
56482
  async evaluate(rule, context4) {
@@ -56234,7 +56554,7 @@ var init_regex_evaluator = __esm(() => {
56234
56554
  init_file_utils();
56235
56555
  });
56236
56556
 
56237
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.11+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/ripgrep-evaluator.js
56557
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.14+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/ripgrep-evaluator.js
56238
56558
  class RipgrepEvaluator {
56239
56559
  executor;
56240
56560
  constructor(executor = nodeBunFactory3.createProcessExecutor()) {
@@ -56327,7 +56647,7 @@ var init_ripgrep_evaluator = __esm(() => {
56327
56647
  init_file_utils();
56328
56648
  });
56329
56649
 
56330
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.11+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/schema-artifact-evaluator.js
56650
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.14+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/schema-artifact-evaluator.js
56331
56651
  class SchemaArtifactEvaluator {
56332
56652
  constructor() {}
56333
56653
  async evaluate(rule, context4) {
@@ -56405,7 +56725,7 @@ var init_schema_artifact_evaluator = __esm(() => {
56405
56725
  init_file_utils();
56406
56726
  });
56407
56727
 
56408
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.11+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/secrets-scanner-evaluator.js
56728
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.14+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/secrets-scanner-evaluator.js
56409
56729
  class SecretsScannerEvaluator {
56410
56730
  constructor() {}
56411
56731
  async evaluate(rule, context4) {
@@ -56473,7 +56793,7 @@ var init_secrets_scanner_evaluator = __esm(() => {
56473
56793
  ALL_CATEGORIES = Object.keys(BUILTIN_PATTERNS);
56474
56794
  });
56475
56795
 
56476
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.11+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/sg-evaluator.js
56796
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.14+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/sg-evaluator.js
56477
56797
  class SgEvaluator {
56478
56798
  executor;
56479
56799
  constructor(executor = nodeBunFactory3.createProcessExecutor()) {
@@ -56570,7 +56890,7 @@ var init_sg_evaluator = __esm(() => {
56570
56890
  init_file_utils();
56571
56891
  });
56572
56892
 
56573
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.11+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/resolvers/test-path-resolver.js
56893
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.14+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/resolvers/test-path-resolver.js
56574
56894
  class TypeScriptTestPathResolver {
56575
56895
  name = "typescript";
56576
56896
  constructor() {}
@@ -56653,7 +56973,7 @@ function testify(rel) {
56653
56973
  return `${dir}test_${base2}`;
56654
56974
  }
56655
56975
 
56656
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.11+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/test-location-evaluator.js
56976
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.14+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/test-location-evaluator.js
56657
56977
  class TestLocationEvaluator {
56658
56978
  resolvers;
56659
56979
  constructor(resolvers) {
@@ -56728,7 +57048,7 @@ var init_test_location_evaluator = __esm(() => {
56728
57048
  TYPESCRIPT_FALLBACK = new TypeScriptTestPathResolver;
56729
57049
  });
56730
57050
 
56731
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.11+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/tsdoc-export-evaluator.js
57051
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.14+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/tsdoc-export-evaluator.js
56732
57052
  class TsdocExportEvaluator {
56733
57053
  constructor() {}
56734
57054
  async evaluate(rule, context4) {
@@ -56813,7 +57133,7 @@ var init_tsdoc_export_evaluator = __esm(() => {
56813
57133
  };
56814
57134
  });
56815
57135
 
56816
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.11+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/formatters/json.js
57136
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.14+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/formatters/json.js
56817
57137
  class JsonFormatter {
56818
57138
  constructor() {}
56819
57139
  format(result) {
@@ -56821,7 +57141,7 @@ class JsonFormatter {
56821
57141
  }
56822
57142
  }
56823
57143
 
56824
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.11+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/formatters/text.js
57144
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.14+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/formatters/text.js
56825
57145
  class TextFormatter {
56826
57146
  constructor() {}
56827
57147
  format(result) {
@@ -56835,7 +57155,7 @@ class TextFormatter {
56835
57155
  }
56836
57156
  }
56837
57157
 
56838
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.11+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/host/builtins.js
57158
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.14+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/host/builtins.js
56839
57159
  function registerBuiltins(host, executor) {
56840
57160
  const path9 = new PathEvaluator;
56841
57161
  host.evaluators.register("regex", new RegexEvaluator, "builtin");
@@ -56875,7 +57195,7 @@ var init_builtins = __esm(() => {
56875
57195
  init_tsdoc_export_evaluator();
56876
57196
  });
56877
57197
 
56878
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.11+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/host/rule-engine-host.js
57198
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.14+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/host/rule-engine-host.js
56879
57199
  class RuleEngineHost {
56880
57200
  evaluators;
56881
57201
  formatters;
@@ -56892,7 +57212,7 @@ var init_rule_engine_host = __esm(() => {
56892
57212
  init_extension2();
56893
57213
  });
56894
57214
 
56895
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.11+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/engine.js
57215
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.14+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/engine.js
56896
57216
  class RuleEngine {
56897
57217
  host;
56898
57218
  events;
@@ -57063,7 +57383,7 @@ var init_engine = __esm(() => {
57063
57383
  init_types3();
57064
57384
  });
57065
57385
 
57066
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.11+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/host/bundled-rules.js
57386
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.14+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/host/bundled-rules.js
57067
57387
  async function bundledRulesRoot() {
57068
57388
  if (cachedRoot2 !== undefined)
57069
57389
  return cachedRoot2;
@@ -57114,7 +57434,7 @@ var init_bundled_rules = __esm(() => {
57114
57434
  init_dist5();
57115
57435
  });
57116
57436
 
57117
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.11+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/persistence/db-adapter.js
57437
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.14+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/persistence/db-adapter.js
57118
57438
  class DbRulePersistenceAdapter {
57119
57439
  db;
57120
57440
  constructor(db2) {
@@ -57148,7 +57468,7 @@ class DbRulePersistenceAdapter {
57148
57468
  }
57149
57469
  }
57150
57470
 
57151
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.11+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/persistence/schema.js
57471
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.14+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/persistence/schema.js
57152
57472
  var RULE_ENGINE_SCHEMA_SQL = `
57153
57473
  CREATE TABLE IF NOT EXISTS rule_runs (
57154
57474
  id TEXT PRIMARY KEY,
@@ -57194,10 +57514,10 @@ CREATE TABLE IF NOT EXISTS rule_eval_runs (
57194
57514
  CREATE INDEX IF NOT EXISTS idx_rule_eval_runs_run_id ON rule_eval_runs (run_id);
57195
57515
  `;
57196
57516
 
57197
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.11+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/persistence/index.js
57517
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.14+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/persistence/index.js
57198
57518
  var init_persistence2 = () => {};
57199
57519
 
57200
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.11+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/index.js
57520
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.14+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/index.js
57201
57521
  var init_dist10 = __esm(() => {
57202
57522
  init_extensions();
57203
57523
  init_loader2();
@@ -57396,7 +57716,7 @@ ${SYSTEM_EVENTS_SCHEMA_SQL}
57396
57716
  ];
57397
57717
  });
57398
57718
 
57399
- // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.11+08507d81f591b81d/node_modules/@gobing-ai/ts-infra/dist/job-queue/db-job-queue.js
57719
+ // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.14+a32276a722fab3c8/node_modules/@gobing-ai/ts-infra/dist/job-queue/db-job-queue.js
57400
57720
  function queueLogger() {
57401
57721
  if (!_queueLogger)
57402
57722
  _queueLogger = getLogger2("job-queue");
@@ -57624,7 +57944,7 @@ var init_db_job_queue = __esm(() => {
57624
57944
  init_tracing2();
57625
57945
  });
57626
57946
 
57627
- // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.11+08507d81f591b81d/node_modules/@gobing-ai/ts-infra/dist/job-queue-db.js
57947
+ // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.14+a32276a722fab3c8/node_modules/@gobing-ai/ts-infra/dist/job-queue-db.js
57628
57948
  var exports_job_queue_db = {};
57629
57949
  __export(exports_job_queue_db, {
57630
57950
  DBQueueConsumer: () => DBQueueConsumer,
@@ -57688,6 +58008,9 @@ function bumpStageSchemaMajor(version3) {
57688
58008
  function bumpStageSchemaMinor(version3) {
57689
58009
  return { major: version3.major, minor: version3.minor + 1 };
57690
58010
  }
58011
+ function normalizeCapabilityTier(value) {
58012
+ return value === LEGACY_CAPABLE_ALIAS ? "capable-1" : value;
58013
+ }
57691
58014
  function isTierEligible(candidateTier, minTier) {
57692
58015
  return TIER_RANK[candidateTier] >= TIER_RANK[minTier];
57693
58016
  }
@@ -57759,7 +58082,7 @@ function parseStageRecord(raw, consumerVersion = STAGE_REGISTRY_SCHEMA_VERSION)
57759
58082
  function getCanonicalStage(idOrAlias) {
57760
58083
  return CANONICAL_STAGE_BY_KEY.get(idOrAlias);
57761
58084
  }
57762
- var STAGE_REGISTRY_SCHEMA_VERSION, stageSchemaVersionSchema, AUTHORITY_LANES, MUTATION_CLASSES, EXECUTION_KINDS, inlineExecutionSchema, subprocessExecutionSchema, deterministicExecutionSchema, hitlExecutionSchema, irreversibleExecutionSchema, executionKindSchema, ARTIFACT_DIRECTIONS, stageArtifactSchema, stageGateSchema, stageRetryPolicySchema, stageModelPolicySchema, TIER_RANK, CONTEXT_LAYER_NAMES, stageContextLayerSchema, stageEventSchema, STAGE_ID_PATTERN, stageRecordSchema, StageRegistryError, REGISTERED_CANONICAL_STAGES, CANONICAL_STAGE_BY_KEY;
58085
+ var STAGE_REGISTRY_SCHEMA_VERSION, stageSchemaVersionSchema, AUTHORITY_LANES, MUTATION_CLASSES, EXECUTION_KINDS, inlineExecutionSchema, subprocessExecutionSchema, deterministicExecutionSchema, hitlExecutionSchema, irreversibleExecutionSchema, executionKindSchema, ARTIFACT_DIRECTIONS, stageArtifactSchema, stageGateSchema, stageRetryPolicySchema, CAPABILITY_TIERS, LEGACY_CAPABLE_ALIAS = "capable", capabilityTierSchema, stageModelPolicySchema, TIER_RANK, CONTEXT_LAYER_NAMES, stageContextLayerSchema, stageEventSchema, STAGE_ID_PATTERN, stageRecordSchema, StageRegistryError, REGISTERED_CANONICAL_STAGES, CANONICAL_STAGE_BY_KEY;
57763
58086
  var init_schema4 = __esm(() => {
57764
58087
  init_zod();
57765
58088
  STAGE_REGISTRY_SCHEMA_VERSION = {
@@ -57833,10 +58156,12 @@ var init_schema4 = __esm(() => {
57833
58156
  terminal_stop: exports_external.enum(["block", "escalate", "fail"]),
57834
58157
  timeout_seconds: exports_external.number().int().positive().optional()
57835
58158
  }).strict();
58159
+ CAPABILITY_TIERS = ["cheap", "standard", "capable-1", "capable-2", "capable-3"];
58160
+ capabilityTierSchema = exports_external.preprocess(normalizeCapabilityTier, exports_external.enum(CAPABILITY_TIERS));
57836
58161
  stageModelPolicySchema = exports_external.object({
57837
- min_tier: exports_external.enum(["cheap", "standard", "capable"]),
58162
+ min_tier: capabilityTierSchema,
57838
58163
  fallback: exports_external.array(exports_external.object({
57839
- tier: exports_external.enum(["cheap", "standard", "capable"]),
58164
+ tier: capabilityTierSchema,
57840
58165
  trigger: exports_external.enum(["gate-fail", "timeout", "insufficient-evidence", "retry-exhausted"])
57841
58166
  }).strict()),
57842
58167
  override_key: exports_external.string().optional()
@@ -57844,7 +58169,9 @@ var init_schema4 = __esm(() => {
57844
58169
  TIER_RANK = {
57845
58170
  cheap: 1,
57846
58171
  standard: 2,
57847
- capable: 3
58172
+ "capable-1": 3,
58173
+ "capable-2": 4,
58174
+ "capable-3": 5
57848
58175
  };
57849
58176
  CONTEXT_LAYER_NAMES = [
57850
58177
  "harness-policy",
@@ -57906,7 +58233,7 @@ var init_schema4 = __esm(() => {
57906
58233
  retry: { max_attempts: 3, terminal_stop: "block" },
57907
58234
  model_policy: {
57908
58235
  min_tier: "standard",
57909
- fallback: [{ tier: "capable", trigger: "gate-fail" }]
58236
+ fallback: [{ tier: "capable-2", trigger: "gate-fail" }]
57910
58237
  },
57911
58238
  context_layers: [],
57912
58239
  observability: [],
@@ -57924,8 +58251,8 @@ var init_schema4 = __esm(() => {
57924
58251
  mutation_class: "corpus",
57925
58252
  retry: { max_attempts: 3, terminal_stop: "block" },
57926
58253
  model_policy: {
57927
- min_tier: "capable",
57928
- fallback: []
58254
+ min_tier: "capable-2",
58255
+ fallback: [{ tier: "capable-3", trigger: "gate-fail" }]
57929
58256
  },
57930
58257
  context_layers: [],
57931
58258
  observability: [],
@@ -57945,8 +58272,8 @@ var init_schema4 = __esm(() => {
57945
58272
  model_policy: {
57946
58273
  min_tier: "standard",
57947
58274
  fallback: [
57948
- { tier: "capable", trigger: "gate-fail" },
57949
- { tier: "capable", trigger: "timeout" }
58275
+ { tier: "capable-1", trigger: "gate-fail" },
58276
+ { tier: "capable-1", trigger: "timeout" }
57950
58277
  ]
57951
58278
  },
57952
58279
  context_layers: [],
@@ -57966,7 +58293,7 @@ var init_schema4 = __esm(() => {
57966
58293
  retry: { max_attempts: 3, terminal_stop: "block" },
57967
58294
  model_policy: {
57968
58295
  min_tier: "standard",
57969
- fallback: [{ tier: "capable", trigger: "gate-fail" }]
58296
+ fallback: [{ tier: "capable-1", trigger: "gate-fail" }]
57970
58297
  },
57971
58298
  context_layers: [],
57972
58299
  observability: [],
@@ -57984,7 +58311,7 @@ var init_schema4 = __esm(() => {
57984
58311
  mutation_class: "verdict",
57985
58312
  retry: { max_attempts: 2, terminal_stop: "escalate" },
57986
58313
  model_policy: {
57987
- min_tier: "capable",
58314
+ min_tier: "capable-1",
57988
58315
  fallback: []
57989
58316
  },
57990
58317
  context_layers: [],
@@ -58004,7 +58331,7 @@ var init_schema4 = __esm(() => {
58004
58331
  retry: { max_attempts: 2, terminal_stop: "block" },
58005
58332
  model_policy: {
58006
58333
  min_tier: "standard",
58007
- fallback: [{ tier: "capable", trigger: "gate-fail" }]
58334
+ fallback: [{ tier: "capable-1", trigger: "gate-fail" }]
58008
58335
  },
58009
58336
  context_layers: [],
58010
58337
  observability: [],
@@ -58023,7 +58350,7 @@ var init_schema4 = __esm(() => {
58023
58350
  retry: { max_attempts: 2, terminal_stop: "block" },
58024
58351
  model_policy: {
58025
58352
  min_tier: "standard",
58026
- fallback: [{ tier: "capable", trigger: "gate-fail" }]
58353
+ fallback: [{ tier: "capable-1", trigger: "gate-fail" }]
58027
58354
  },
58028
58355
  context_layers: [],
58029
58356
  observability: [],
@@ -58041,7 +58368,7 @@ var init_schema4 = __esm(() => {
58041
58368
  mutation_class: "driver",
58042
58369
  retry: { max_attempts: 3, terminal_stop: "block" },
58043
58370
  model_policy: {
58044
- min_tier: "capable",
58371
+ min_tier: "capable-1",
58045
58372
  fallback: []
58046
58373
  },
58047
58374
  context_layers: [],
@@ -59297,6 +59624,9 @@ var init_schema6 = __esm(() => {
59297
59624
  name: exports_external.string().min(1, "name is required"),
59298
59625
  background: exports_external.string().optional(),
59299
59626
  requirements: exports_external.string().optional(),
59627
+ design: exports_external.string().optional(),
59628
+ plan: exports_external.string().optional(),
59629
+ acceptance_criteria: exports_external.string().optional(),
59300
59630
  feature_id: featureId,
59301
59631
  parent_wbs: wbsString,
59302
59632
  priority: exports_external.enum(PRIORITIES).optional(),
@@ -59674,6 +60004,7 @@ __export(exports_src, {
59674
60004
  normalizeTaskStatusSafe: () => normalizeTaskStatusSafe,
59675
60005
  normalizeTaskStatus: () => normalizeTaskStatus,
59676
60006
  normalizeFeatureStatus: () => normalizeFeatureStatus,
60007
+ normalizeCapabilityTier: () => normalizeCapabilityTier,
59677
60008
  mergeStubs: () => mergeStubs,
59678
60009
  matrixEntrySchema: () => matrixEntrySchema,
59679
60010
  matchEtlPayloads: () => matchEtlPayloads,
@@ -59726,6 +60057,7 @@ __export(exports_src, {
59726
60057
  cleanupTempFiles: () => cleanupTempFiles,
59727
60058
  checkLayerStale: () => checkLayerStale,
59728
60059
  checkAcCoverage: () => checkAcCoverage,
60060
+ capabilityTierSchema: () => capabilityTierSchema,
59729
60061
  canonicalJson: () => canonicalJson,
59730
60062
  cacheHitRatio: () => cacheHitRatio,
59731
60063
  bumpStageSchemaMinor: () => bumpStageSchemaMinor,
@@ -59779,6 +60111,7 @@ __export(exports_src, {
59779
60111
  MarkdownDocument: () => MarkdownDocument,
59780
60112
  MUTATION_CLASSES: () => MUTATION_CLASSES,
59781
60113
  MODEL_PRICING: () => MODEL_PRICING,
60114
+ LEGACY_CAPABLE_ALIAS: () => LEGACY_CAPABLE_ALIAS,
59782
60115
  InlineContext: () => InlineContext,
59783
60116
  InboxRecentDao: () => InboxRecentDao,
59784
60117
  InboxMessageDao: () => InboxMessageDao,
@@ -59796,6 +60129,7 @@ __export(exports_src, {
59796
60129
  CLI_SCHEMA_SQL: () => CLI_SCHEMA_SQL,
59797
60130
  CLI_MIGRATION_FILE_MARKER: () => CLI_MIGRATION_FILE_MARKER,
59798
60131
  CLI_MIGRATIONS: () => CLI_MIGRATIONS,
60132
+ CAPABILITY_TIERS: () => CAPABILITY_TIERS,
59799
60133
  CACHEABILITY_CLASSES: () => CACHEABILITY_CLASSES,
59800
60134
  ArtifactDao: () => ArtifactDao,
59801
60135
  ActionRunDao: () => ActionRunDao,
@@ -59881,7 +60215,10 @@ class AgentService {
59881
60215
  return results.some((result) => !result.usable && result.tier === 1) ? 1 : 0;
59882
60216
  }
59883
60217
  async run(prompt, flags, deps) {
59884
- const outcome = await this.executeRun(prompt, flags, deps, { silent: false });
60218
+ const outcome = await this.executeRun(prompt, flags, deps, {
60219
+ silent: false,
60220
+ execution: this.defaultExecutionOptions(flags)
60221
+ });
59885
60222
  if (!outcome.ok) {
59886
60223
  this.ctx.output.error(outcome.message);
59887
60224
  return outcome.exitCode;
@@ -59913,8 +60250,12 @@ class AgentService {
59913
60250
  stderr: result.stderr
59914
60251
  };
59915
60252
  }
59916
- async runTraced(prompt, flags, deps) {
59917
- const outcome = await this.executeRun(prompt, flags, deps, { silent: true, nonInteractive: true });
60253
+ async runTraced(prompt, flags, deps, execution) {
60254
+ const outcome = await this.executeRun(prompt, flags, deps, {
60255
+ silent: true,
60256
+ nonInteractive: true,
60257
+ execution: execution ?? this.defaultExecutionOptions(flags)
60258
+ });
59918
60259
  if (!outcome.ok) {
59919
60260
  return { exitCode: outcome.exitCode, stdout: "", message: outcome.message };
59920
60261
  }
@@ -60023,22 +60364,59 @@ class AgentService {
60023
60364
  let result;
60024
60365
  const controller = new AbortController;
60025
60366
  const onTerminate = () => controller.abort();
60367
+ const onExternalAbort = () => controller.abort();
60368
+ const lifecycle = new AgentExecutionLifecycle(options.execution?.observer, options.execution?.correlation, configuredSecretValues(this.ctx.env), options.execution?.heartbeatMs);
60369
+ lifecycle.start({
60370
+ agent,
60371
+ ...model !== undefined ? { model } : {},
60372
+ invocation: `${invocation.command} ${invocation.argv.join(" ")}`,
60373
+ ...timeoutMs !== undefined ? { timeoutMs } : {}
60374
+ });
60375
+ const dispatchStartedAt = Date.now();
60026
60376
  try {
60027
60377
  process.on("SIGTERM", onTerminate);
60028
60378
  process.on("SIGINT", onTerminate);
60379
+ options.execution?.signal?.addEventListener("abort", onExternalAbort, { once: true });
60380
+ if (options.execution?.signal?.aborted === true)
60381
+ controller.abort();
60029
60382
  result = await runner.runPromptCommand(agent, promptOptions, {
60030
60383
  cwd: cwd || undefined,
60031
60384
  ...timeoutMs !== undefined ? { timeout: timeoutMs } : {},
60032
- signal: controller.signal
60385
+ signal: controller.signal,
60386
+ correlation: lifecycle.identity,
60387
+ onOutput: (output2) => lifecycle.observe(output2)
60033
60388
  });
60034
60389
  } catch (error51) {
60390
+ lifecycle.finish({
60391
+ exitCode: null,
60392
+ durationMs: Date.now() - dispatchStartedAt,
60393
+ ...controller.signal.aborted ? { reason: "cancelled" } : {},
60394
+ ...!controller.signal.aborted ? { reason: error51 instanceof Error ? error51.message : String(error51) } : {}
60395
+ });
60035
60396
  return { ok: false, exitCode: 2, message: error51 instanceof Error ? error51.message : String(error51) };
60036
60397
  } finally {
60037
60398
  process.off("SIGTERM", onTerminate);
60038
60399
  process.off("SIGINT", onTerminate);
60400
+ options.execution?.signal?.removeEventListener("abort", onExternalAbort);
60039
60401
  }
60402
+ lifecycle.finish({
60403
+ exitCode: result.exitCode,
60404
+ durationMs: result.durationMs,
60405
+ ...result.signal !== undefined ? { signal: result.signal } : {},
60406
+ ...controller.signal.aborted && result.signal === undefined ? { reason: "cancelled" } : {}
60407
+ });
60040
60408
  return { ok: true, result, invocation };
60041
60409
  }
60410
+ defaultExecutionOptions(flags) {
60411
+ const runId = stringFlag(flags, "run-id", "") || crypto.randomUUID();
60412
+ const observer = this.ctx.events === undefined ? undefined : (event) => {
60413
+ this.ctx.events?.emit("agent.execution", event);
60414
+ };
60415
+ return {
60416
+ correlation: { runId, executionId: crypto.randomUUID() },
60417
+ ...observer !== undefined ? { observer } : {}
60418
+ };
60419
+ }
60042
60420
  hasWarnedDeprecation = false;
60043
60421
  warnDeprecationOnce(message) {
60044
60422
  if (!this.hasWarnedDeprecation) {
@@ -60050,7 +60428,7 @@ class AgentService {
60050
60428
  const raw = stringFlag(flags, "agent", "auto");
60051
60429
  if (raw === "auto")
60052
60430
  return this.resolveAgentAuto(prompt, flags, doctorRunner);
60053
- return this.resolveAgentExplicit(raw, doctorRunner, "explicit");
60431
+ return this.resolveExecutorSelector(raw, doctorRunner, "explicit");
60054
60432
  }
60055
60433
  async resolveAgentAuto(prompt, flags, doctorRunner) {
60056
60434
  const config4 = this.ctx.agentConfig;
@@ -60175,16 +60553,6 @@ class AgentService {
60175
60553
  return usable.result;
60176
60554
  return { ok: true, agent: canonical, source };
60177
60555
  }
60178
- async resolveAgentExplicit(name, doctorRunner, source) {
60179
- const canonical = resolveAgentName(name);
60180
- if (canonical === undefined) {
60181
- return { ok: false, exitCode: 2, message: `Unknown agent: ${name}` };
60182
- }
60183
- const usable = await this.checkUsable(canonical, doctorRunner);
60184
- if (!usable.ok)
60185
- return usable.result;
60186
- return { ok: true, agent: canonical, source };
60187
- }
60188
60556
  async checkUsable(canonical, doctorRunner) {
60189
60557
  const result = await doctorRunner.runOne(canonical);
60190
60558
  if (!result.installed) {
@@ -60354,13 +60722,15 @@ function parseTagsFlag(flags) {
60354
60722
  return tags.length > 0 ? tags : undefined;
60355
60723
  }
60356
60724
  function getExecutorTier(executor) {
60357
- if (executor.tier)
60358
- return executor.tier;
60725
+ if (executor.tier) {
60726
+ const declared = executor.tier;
60727
+ return declared === "capable" ? "capable-1" : declared;
60728
+ }
60359
60729
  const combined = `${executor.name} ${executor.model ?? ""} ${executor.agent}`.toLowerCase();
60360
60730
  if (/\b(cheap|haiku|flash|lite|mini|fast)\b/.test(combined))
60361
60731
  return "cheap";
60362
60732
  if (/\b(capable|opus|pro|sonnet|r1|o1|o3|expert)\b/.test(combined))
60363
- return "capable";
60733
+ return "capable-1";
60364
60734
  return "standard";
60365
60735
  }
60366
60736
  var TRACE_SAFE_SLASH_COMMAND, TRACE_SAFE_SLASH_TOKEN, SENSITIVE_FLAG, SENSITIVE_INLINE_FLAG;
@@ -60368,6 +60738,7 @@ var init_agent_service = __esm(() => {
60368
60738
  init_src2();
60369
60739
  init_dist9();
60370
60740
  init_dist5();
60741
+ init_agent_execution();
60371
60742
  TRACE_SAFE_SLASH_COMMAND = /^(?:\/skill:(?:sp|rd3)-|\/(?:sp|rd3)[:-]|\$(?:sp|rd3)-)[A-Za-z0-9._-]+(?:\s|$)/;
60372
60743
  TRACE_SAFE_SLASH_TOKEN = /^(?:\d{4}|--(?:auto|next|force|bdd)|--(?:mode|fix|focus)|implement|test|review|verify|all|none|blockers-first|quick|requirements|background|constraints|acceptance)$/;
60373
60744
  SENSITIVE_FLAG = /^--?(?:api[-_]?key|authorization|credential|password|secret|token)$/i;
@@ -60902,12 +61273,23 @@ function evaluateDoneTransition(input) {
60902
61273
  if (targetStatus === currentStatus) {
60903
61274
  return { kind: "noop", fromStatus: currentStatus, message: formatNoopMessage(wbs, currentStatus) };
60904
61275
  }
60905
- if (artifact === undefined) {
60906
- return { kind: "allow", reason: "no-artifact" };
60907
- }
60908
61276
  if (forced) {
60909
61277
  return { kind: "allow", reason: "forced" };
60910
61278
  }
61279
+ if (artifact === undefined) {
61280
+ const verdictPath2 = `.spur/run/${wbs}-verdict.json`;
61281
+ return {
61282
+ kind: "deny",
61283
+ verdict: "UNKNOWN",
61284
+ message: [
61285
+ `Cannot transition task ${wbs} to done: missing verify verdict artifact.`,
61286
+ ` task: ${taskFilePath}`,
61287
+ ` verdict: ${verdictPath2} (not found)`,
61288
+ ` remediation: re-run \`/sp:dev-verify ${wbs}\` until PASS (writes the artifact), ` + `or override with \`spur task update ${wbs} done --force-done --reason "<why>"\`.`
61289
+ ].join(`
61290
+ `)
61291
+ };
61292
+ }
60911
61293
  const computed = computeAggregate(artifact);
60912
61294
  const effective = harshnessMax(artifact.verdict, computed);
60913
61295
  if (effective === "PASS") {
@@ -61176,10 +61558,22 @@ var init_planning_check_base = __esm(() => {
61176
61558
  // ../../packages/app/src/services/feature-check.ts
61177
61559
  var exports_feature_check = {};
61178
61560
  __export(exports_feature_check, {
61561
+ defaultVerdictRunDir: () => defaultVerdictRunDir,
61179
61562
  FeatureCheckService: () => FeatureCheckService,
61180
61563
  DEFAULT_FEATURE_MATRIX: () => DEFAULT_FEATURE_MATRIX
61181
61564
  });
61182
61565
  import { dirname as dirname8, join as join7 } from "path";
61566
+ function defaultVerdictRunDir(tasksDir) {
61567
+ const norm = tasksDir.replace(/\\/g, "/");
61568
+ if (/\/docs\/tasks\d*$/.test(norm) || /\/docs\/tasks$/.test(norm)) {
61569
+ return join7(dirname8(dirname8(tasksDir)), ".spur", "run");
61570
+ }
61571
+ return join7(dirname8(tasksDir), ".spur", "run");
61572
+ }
61573
+ function rowMatchesScenario(id, sc) {
61574
+ const stripped = id.replace(/^Scenario:\s*/i, "").trim();
61575
+ return normalizeTitle(id) === sc.normalized || normalizeTitle(stripped) === sc.normalized || id === sc.alias || stripped === sc.alias;
61576
+ }
61183
61577
  var DEFAULT_FEATURE_MATRIX, FeatureCheckService;
61184
61578
  var init_feature_check = __esm(() => {
61185
61579
  init_src2();
@@ -61245,7 +61639,8 @@ var init_feature_check = __esm(() => {
61245
61639
  await this.checkChildrenLimit(featureId2, options.featuresDir, findings);
61246
61640
  }
61247
61641
  const dogfoodDir = options?.dogfoodDir ?? (options?.featuresDir ? join7(dirname8(options.featuresDir), "dogfood") : undefined);
61248
- await this.runL4(doc2, featureId2, status, options?.tasksDir, dogfoodDir, findings);
61642
+ const runDir = options?.runDir ?? (options?.tasksDir ? defaultVerdictRunDir(options.tasksDir) : undefined);
61643
+ await this.runL4(doc2, featureId2, status, options?.tasksDir, dogfoodDir, runDir, findings);
61249
61644
  return { id: featureId2, ...this.summarizeWithStatus(status, findings, strict, options?.severityOverrides) };
61250
61645
  }
61251
61646
  runL3(doc2, findings) {
@@ -61377,13 +61772,14 @@ var init_feature_check = __esm(() => {
61377
61772
  });
61378
61773
  }
61379
61774
  }
61380
- async runL4(doc2, featureId2, status, tasksDir, dogfoodDir, findings) {
61775
+ async runL4(doc2, featureId2, status, tasksDir, dogfoodDir, runDir, findings) {
61381
61776
  if (tasksDir === undefined)
61382
61777
  return;
61383
61778
  let linkedTasks = 0;
61384
61779
  const incompleteTasks = [];
61385
61780
  const linkedTaskAc = [];
61386
61781
  const linkedTaskSolutions = [];
61782
+ const linkedTaskRecords = [];
61387
61783
  try {
61388
61784
  const entries = await this.fs.readDir(tasksDir);
61389
61785
  for (const entry of entries) {
@@ -61398,13 +61794,15 @@ var init_feature_check = __esm(() => {
61398
61794
  continue;
61399
61795
  linkedTasks += 1;
61400
61796
  const tStatus = tfm.status ?? "backlog";
61797
+ const wbs = entry.match(/^(\d{4})_/)?.[1] ?? entry;
61401
61798
  if (tStatus !== "done" && tStatus !== "cancelled") {
61402
- incompleteTasks.push(entry.match(/^(\d{4})_/)?.[1] ?? entry);
61799
+ incompleteTasks.push(wbs);
61403
61800
  }
61404
61801
  const tac = stripAcFence(taskDoc.getSection("Acceptance Criteria") ?? "");
61405
61802
  if (tac.trim().length > 0)
61406
61803
  linkedTaskAc.push(tac);
61407
61804
  linkedTaskSolutions.push(taskDoc.getSection("Solution") ?? "");
61805
+ linkedTaskRecords.push({ wbs, status: tStatus, ac: tac });
61408
61806
  } catch {
61409
61807
  findings.push({
61410
61808
  layer: "L4",
@@ -61446,6 +61844,7 @@ var init_feature_check = __esm(() => {
61446
61844
  });
61447
61845
  }
61448
61846
  }
61847
+ await this.checkScenarioSatisfaction(acBody, linkedTaskRecords, runDir, findings);
61449
61848
  if (status === "verifying" && incompleteTasks.length > 0) {
61450
61849
  findings.push({
61451
61850
  layer: "L4",
@@ -61481,9 +61880,152 @@ var init_feature_check = __esm(() => {
61481
61880
  }
61482
61881
  }
61483
61882
  }
61883
+ async checkScenarioSatisfaction(featureAc, linkedTasks, runDir, findings) {
61884
+ if (linkedTasks.length === 0 || runDir === undefined)
61885
+ return;
61886
+ const parsed = parseFeature(featureAc);
61887
+ if (parsed === null)
61888
+ return;
61889
+ if (parsed.scenarios.length === 0)
61890
+ return;
61891
+ const scenarioAliases = parsed.scenarios.map((s, i2) => ({
61892
+ title: s.name,
61893
+ normalized: normalizeTitle(s.name),
61894
+ alias: `AC-${i2 + 1}`
61895
+ }));
61896
+ const covers = {};
61897
+ for (const sc of scenarioAliases) {
61898
+ const linked = [];
61899
+ for (const task of linkedTasks) {
61900
+ const taskCov = checkAcCoverage(`Feature: x
61901
+ Scenario: ${sc.title}
61902
+ Given x`, task.ac);
61903
+ if (!taskCov.orphans.includes(sc.title)) {
61904
+ linked.push({ wbs: task.wbs, status: task.status });
61905
+ }
61906
+ }
61907
+ covers[sc.title] = linked;
61908
+ }
61909
+ for (const sc of scenarioAliases) {
61910
+ const linked = covers[sc.title] ?? [];
61911
+ if (linked.length === 0)
61912
+ continue;
61913
+ const verified = await this.isScenarioVerified(sc, linked, runDir);
61914
+ if (!verified) {
61915
+ findings.push({
61916
+ layer: "L4",
61917
+ code: FINDING_CODES.L4_SCENARIO_UNVERIFIED,
61918
+ severity: "warning",
61919
+ section: "Acceptance Criteria",
61920
+ message: `Feature scenario "${sc.title}" is linked but unverified: covering task(s) ` + `${linked.map((l) => l.wbs).join(", ")} have no PASS verdict with MET requirement`
61921
+ });
61922
+ }
61923
+ }
61924
+ }
61925
+ async isScenarioVerified(sc, linked, runDir) {
61926
+ for (const task of linked) {
61927
+ if (task.status !== "done")
61928
+ continue;
61929
+ const artifact = await this.readVerdictArtifact(runDir, task.wbs);
61930
+ if (artifact === null)
61931
+ continue;
61932
+ if (artifact.verdict !== "PASS")
61933
+ continue;
61934
+ const rows = [...artifact.requirements, ...artifact.acceptanceCriteria];
61935
+ const matched = rows.find((r) => rowMatchesScenario(r.id, sc));
61936
+ if (matched !== undefined && matched.status === "MET")
61937
+ return true;
61938
+ }
61939
+ return false;
61940
+ }
61941
+ async readVerdictArtifact(runDir, wbs) {
61942
+ try {
61943
+ const raw = await this.fs.readFile(join7(runDir, `${wbs}-verdict.json`));
61944
+ const parsed = JSON.parse(raw);
61945
+ if (typeof parsed.verdict !== "string")
61946
+ return null;
61947
+ const pickRows = (arr) => (arr ?? []).filter((r) => typeof r?.id === "string" && typeof r?.status === "string").map((r) => ({ id: r.id, status: r.status }));
61948
+ const requirements = pickRows(parsed.requirements);
61949
+ const acceptanceCriteria = pickRows(parsed.acceptanceCriteria);
61950
+ if (requirements.length === 0 && acceptanceCriteria.length === 0)
61951
+ return null;
61952
+ return { verdict: parsed.verdict, requirements, acceptanceCriteria };
61953
+ } catch {
61954
+ return null;
61955
+ }
61956
+ }
61484
61957
  };
61485
61958
  });
61486
61959
 
61960
+ // ../../packages/app/src/services/task-locator.ts
61961
+ import { resolve as resolvePath5 } from "path";
61962
+ async function readDirIfPresent(fs3, dir) {
61963
+ try {
61964
+ return await fs3.readDir(dir);
61965
+ } catch (error51) {
61966
+ const code = error51.code;
61967
+ if (code === "ENOENT" || code === "ENOTDIR")
61968
+ return [];
61969
+ throw error51;
61970
+ }
61971
+ }
61972
+
61973
+ class TaskLocator {
61974
+ fs;
61975
+ dirs;
61976
+ constructor(source) {
61977
+ this.fs = source.fs;
61978
+ if ("dirs" in source) {
61979
+ this.dirs = [...new Set(source.dirs)];
61980
+ return;
61981
+ }
61982
+ const folderKeys = source.foldersConfig ? Object.keys(source.foldersConfig.folders) : [];
61983
+ this.dirs = [...new Set([source.tasksDir, ...folderKeys.map((key) => source.fs.resolve(key))])];
61984
+ }
61985
+ static forSingleDir(fs3, dir) {
61986
+ return new TaskLocator({ fs: fs3, tasksDir: dir });
61987
+ }
61988
+ static forDirs(fs3, dirs) {
61989
+ return new TaskLocator({ fs: fs3, dirs });
61990
+ }
61991
+ folderDirs() {
61992
+ return this.dirs;
61993
+ }
61994
+ async findByWbs(wbs) {
61995
+ const prefix = `${wbs}_`;
61996
+ for (const dir of this.folderDirs()) {
61997
+ for (const name of await readDirIfPresent(this.fs, dir)) {
61998
+ if (name.startsWith(prefix) && name.endsWith(".md")) {
61999
+ return { wbs, name, filePath: `${dir}/${name}` };
62000
+ }
62001
+ }
62002
+ }
62003
+ return null;
62004
+ }
62005
+ async findPathByWbs(wbs) {
62006
+ return (await this.findByWbs(wbs))?.filePath ?? null;
62007
+ }
62008
+ async exactMatch(target) {
62009
+ const normalized = resolvePath5(target);
62010
+ for (const dir of this.folderDirs()) {
62011
+ for (const name of await readDirIfPresent(this.fs, dir)) {
62012
+ const captured = TASK_FILENAME_RE.exec(name);
62013
+ if (captured === null)
62014
+ continue;
62015
+ const filePath = `${dir}/${name}`;
62016
+ if (resolvePath5(filePath) === normalized) {
62017
+ return { wbs: captured[1], name, filePath };
62018
+ }
62019
+ }
62020
+ }
62021
+ return null;
62022
+ }
62023
+ }
62024
+ var TASK_FILENAME_RE;
62025
+ var init_task_locator = __esm(() => {
62026
+ TASK_FILENAME_RE = /^(\d{4})_(.+)\.md$/;
62027
+ });
62028
+
61487
62029
  // ../../packages/app/src/services/feature-service.ts
61488
62030
  function stripLeadingSectionHeader(body, sectionName) {
61489
62031
  const match = body.match(/^(\s*)#{1,6}\s+(.+?)\s*(?:\n|$)/);
@@ -61674,7 +62216,8 @@ class FeatureService {
61674
62216
  const checkSvc = new FeatureCheckService(this.ctx.fs);
61675
62217
  const res = await checkSvc.check(feature.filePath, featureId2, {
61676
62218
  featuresDir: this.ctx.featuresDir,
61677
- tasksDir: this.ctx.tasksDir
62219
+ tasksDir: this.ctx.tasksDir,
62220
+ runDir: defaultVerdictRunDir(this.ctx.tasksDir)
61678
62221
  });
61679
62222
  const l4Errors = res.findings.filter((f) => f.layer === "L4" && f.severity === "error");
61680
62223
  return { pass: l4Errors.length === 0, findings: res.findings };
@@ -61798,36 +62341,45 @@ class FeatureService {
61798
62341
  results
61799
62342
  };
61800
62343
  }
62344
+ taskLocator() {
62345
+ return new TaskLocator({
62346
+ fs: this.ctx.fs,
62347
+ tasksDir: this.ctx.tasksDir,
62348
+ foldersConfig: this.ctx.foldersConfig
62349
+ });
62350
+ }
61801
62351
  async collectTasksByFeature() {
61802
62352
  const byFeature = new Map;
61803
- let names;
61804
- try {
61805
- names = await this.ctx.fs.readDir(this.ctx.tasksDir);
61806
- } catch {
61807
- return byFeature;
61808
- }
61809
- for (const name of names) {
61810
- if (!/^\d{4}_.+\.md$/.test(name))
61811
- continue;
61812
- const wbs = name.match(/^(\d{4})_/)?.[1];
61813
- if (!wbs)
61814
- continue;
62353
+ for (const dir of this.taskLocator().folderDirs()) {
62354
+ let names;
61815
62355
  try {
61816
- const raw = await this.ctx.fs.readFile(`${this.ctx.tasksDir}/${name}`);
61817
- const doc2 = MarkdownDocument.parse(raw, "task");
61818
- const fm = doc2.frontmatterData ?? {};
61819
- const fid = fm.feature_id ?? fm["feature-id"];
61820
- if (fid === undefined)
62356
+ names = await this.ctx.fs.readDir(dir);
62357
+ } catch {
62358
+ continue;
62359
+ }
62360
+ for (const name of names) {
62361
+ if (!/^\d{4}_.+\.md$/.test(name))
61821
62362
  continue;
61822
- const row = {
61823
- wbs,
61824
- name: fm.name ?? wbs,
61825
- status: fm.status ?? "backlog"
61826
- };
61827
- const list = byFeature.get(fid) ?? [];
61828
- list.push(row);
61829
- byFeature.set(fid, list);
61830
- } catch {}
62363
+ const wbs = name.match(/^(\d{4})_/)?.[1];
62364
+ if (!wbs)
62365
+ continue;
62366
+ try {
62367
+ const raw = await this.ctx.fs.readFile(`${dir}/${name}`);
62368
+ const doc2 = MarkdownDocument.parse(raw, "task");
62369
+ const fm = doc2.frontmatterData ?? {};
62370
+ const fid = fm.feature_id ?? fm["feature-id"];
62371
+ if (fid === undefined)
62372
+ continue;
62373
+ const row = {
62374
+ wbs,
62375
+ name: fm.name ?? wbs,
62376
+ status: fm.status ?? "backlog"
62377
+ };
62378
+ const list = byFeature.get(fid) ?? [];
62379
+ list.push(row);
62380
+ byFeature.set(fid, list);
62381
+ } catch {}
62382
+ }
61831
62383
  }
61832
62384
  for (const list of byFeature.values()) {
61833
62385
  list.sort((a2, b) => a2.wbs.localeCompare(b.wbs));
@@ -61925,26 +62477,29 @@ class FeatureService {
61925
62477
  }
61926
62478
  async tasksWithFeatureIds(ids) {
61927
62479
  const out = [];
61928
- let names;
61929
- try {
61930
- names = await this.ctx.fs.readDir(this.ctx.tasksDir);
61931
- } catch {
61932
- return out;
61933
- }
61934
- for (const name of names) {
61935
- if (!/^\d{4}_.+\.md$/.test(name))
61936
- continue;
61937
- const wbs = name.match(/^(\d{4})_/)?.[1];
61938
- if (!wbs)
61939
- continue;
62480
+ for (const dir of this.taskLocator().folderDirs()) {
62481
+ let names;
61940
62482
  try {
61941
- const raw = await this.ctx.fs.readFile(`${this.ctx.tasksDir}/${name}`);
61942
- const fm = MarkdownDocument.parse(raw, "task").frontmatterData ?? {};
61943
- const fid = fm.feature_id ?? fm["feature-id"];
61944
- if (fid !== undefined && ids.has(fid)) {
61945
- out.push({ wbs, path: `${this.ctx.tasksDir}/${name}`, featureId: fid });
61946
- }
61947
- } catch {}
62483
+ names = await this.ctx.fs.readDir(dir);
62484
+ } catch {
62485
+ continue;
62486
+ }
62487
+ for (const name of names) {
62488
+ if (!/^\d{4}_.+\.md$/.test(name))
62489
+ continue;
62490
+ const wbs = name.match(/^(\d{4})_/)?.[1];
62491
+ if (!wbs)
62492
+ continue;
62493
+ try {
62494
+ const path9 = `${dir}/${name}`;
62495
+ const raw = await this.ctx.fs.readFile(path9);
62496
+ const fm = MarkdownDocument.parse(raw, "task").frontmatterData ?? {};
62497
+ const fid = fm.feature_id ?? fm["feature-id"];
62498
+ if (fid !== undefined && ids.has(fid)) {
62499
+ out.push({ wbs, path: path9, featureId: fid });
62500
+ }
62501
+ } catch {}
62502
+ }
61948
62503
  }
61949
62504
  out.sort((a2, b) => a2.wbs.localeCompare(b.wbs));
61950
62505
  return out;
@@ -62120,6 +62675,7 @@ var FEATURE_FILE_RE;
62120
62675
  var init_feature_service = __esm(() => {
62121
62676
  init_src2();
62122
62677
  init_feature_check();
62678
+ init_task_locator();
62123
62679
  FEATURE_FILE_RE = /^([A-Z][1-9]*)_(.+)\.md$/;
62124
62680
  });
62125
62681
 
@@ -63564,75 +64120,6 @@ var init_system_event_emitter = __esm(() => {
63564
64120
  init_system_event_tap();
63565
64121
  });
63566
64122
 
63567
- // ../../packages/app/src/services/task-locator.ts
63568
- import { resolve as resolvePath5 } from "path";
63569
- async function readDirIfPresent(fs3, dir) {
63570
- try {
63571
- return await fs3.readDir(dir);
63572
- } catch (error51) {
63573
- const code = error51.code;
63574
- if (code === "ENOENT" || code === "ENOTDIR")
63575
- return [];
63576
- throw error51;
63577
- }
63578
- }
63579
-
63580
- class TaskLocator {
63581
- fs;
63582
- dirs;
63583
- constructor(source) {
63584
- this.fs = source.fs;
63585
- if ("dirs" in source) {
63586
- this.dirs = [...new Set(source.dirs)];
63587
- return;
63588
- }
63589
- const folderKeys = source.foldersConfig ? Object.keys(source.foldersConfig.folders) : [];
63590
- this.dirs = [...new Set([source.tasksDir, ...folderKeys.map((key) => source.fs.resolve(key))])];
63591
- }
63592
- static forSingleDir(fs3, dir) {
63593
- return new TaskLocator({ fs: fs3, tasksDir: dir });
63594
- }
63595
- static forDirs(fs3, dirs) {
63596
- return new TaskLocator({ fs: fs3, dirs });
63597
- }
63598
- folderDirs() {
63599
- return this.dirs;
63600
- }
63601
- async findByWbs(wbs) {
63602
- const prefix = `${wbs}_`;
63603
- for (const dir of this.folderDirs()) {
63604
- for (const name of await readDirIfPresent(this.fs, dir)) {
63605
- if (name.startsWith(prefix) && name.endsWith(".md")) {
63606
- return { wbs, name, filePath: `${dir}/${name}` };
63607
- }
63608
- }
63609
- }
63610
- return null;
63611
- }
63612
- async findPathByWbs(wbs) {
63613
- return (await this.findByWbs(wbs))?.filePath ?? null;
63614
- }
63615
- async exactMatch(target) {
63616
- const normalized = resolvePath5(target);
63617
- for (const dir of this.folderDirs()) {
63618
- for (const name of await readDirIfPresent(this.fs, dir)) {
63619
- const captured = TASK_FILENAME_RE.exec(name);
63620
- if (captured === null)
63621
- continue;
63622
- const filePath = `${dir}/${name}`;
63623
- if (resolvePath5(filePath) === normalized) {
63624
- return { wbs: captured[1], name, filePath };
63625
- }
63626
- }
63627
- }
63628
- return null;
63629
- }
63630
- }
63631
- var TASK_FILENAME_RE;
63632
- var init_task_locator = __esm(() => {
63633
- TASK_FILENAME_RE = /^(\d{4})_(.+)\.md$/;
63634
- });
63635
-
63636
64123
  // ../../packages/app/src/services/task-check.ts
63637
64124
  import { dirname as dirname9, join as join9 } from "path";
63638
64125
  function isPlaceholderBody(body) {
@@ -63692,6 +64179,36 @@ function isProseOnlyReview(body) {
63692
64179
  return !stripped.split(`
63693
64180
  `).some((l) => l.includes("|"));
63694
64181
  }
64182
+ function resolveProjectRootFromTasksDir(tasksDir) {
64183
+ const norm = tasksDir.replace(/\\/g, "/");
64184
+ if (/\/docs\/tasks\d*$/.test(norm) || /\/docs\/tasks$/.test(norm)) {
64185
+ return dirname9(dirname9(tasksDir));
64186
+ }
64187
+ return dirname9(tasksDir);
64188
+ }
64189
+ function extractBacktickLineAnchors(body) {
64190
+ const out = [];
64191
+ const re = /`([^`\n]+?:(\d+)(?:-(\d+))?)`/g;
64192
+ let m = re.exec(body);
64193
+ while (m !== null) {
64194
+ const raw = m[1] ?? "";
64195
+ const startStr = m[2];
64196
+ const endStr = m[3];
64197
+ if (startStr) {
64198
+ const pathPart = raw.replace(/:(\d+)(?:-(\d+))?$/, "");
64199
+ const hasExt = /\.\w{1,8}$/.test(pathPart.split("/").pop() ?? "");
64200
+ if (pathPart && !pathPart.includes("://") && hasExt) {
64201
+ const startLine = Number(startStr);
64202
+ const endLine = endStr !== undefined ? Number(endStr) : undefined;
64203
+ if (Number.isFinite(startLine) && startLine >= 1) {
64204
+ out.push({ raw, path: pathPart, startLine, endLine });
64205
+ }
64206
+ }
64207
+ }
64208
+ m = re.exec(body);
64209
+ }
64210
+ return out;
64211
+ }
63695
64212
  function hasAdjacentFileLineColumns(body) {
63696
64213
  const lines = body.split(`
63697
64214
  `);
@@ -63754,7 +64271,7 @@ var init_task_check = __esm(() => {
63754
64271
  this.runL3(doc2, entry, status, findings);
63755
64272
  const tasksDir = dirname9(filePath);
63756
64273
  const featuresDir = join9(dirname9(tasksDir), "features");
63757
- await this.runL4(doc2, fm, findings, featuresDir, tasksDir);
64274
+ await this.runL4(doc2, fm, status, findings, featuresDir, tasksDir);
63758
64275
  await this.runL4Rollup(doc2, wbs, status, findings, tasksDir);
63759
64276
  if (status !== "done" && status !== "cancelled") {
63760
64277
  await this.runL4Readiness(doc2, fm, wbs, status, findings, tasksDir);
@@ -63762,6 +64279,26 @@ var init_task_check = __esm(() => {
63762
64279
  return { wbs, ...this.summarizeWithStatus(status, findings, strict, options?.severityOverrides) };
63763
64280
  }
63764
64281
  runL3(doc2, entry, status, findings) {
64282
+ const reqBodyRaw = doc2.getSection("Requirements");
64283
+ if (reqBodyRaw !== null && isPlaceholderBody(reqBodyRaw)) {
64284
+ findings.push({
64285
+ layer: "L3",
64286
+ code: FINDING_CODES.L3_REQUIREMENTS_EMPTY,
64287
+ severity: "error",
64288
+ section: "Requirements",
64289
+ message: "Requirements is placeholder-only \u2014 a task with no requirements is unverifiable"
64290
+ });
64291
+ }
64292
+ const acBodyRaw = doc2.getSection("Acceptance Criteria");
64293
+ if (acBodyRaw !== null && isPlaceholderBody(stripAcFence(acBodyRaw))) {
64294
+ findings.push({
64295
+ layer: "L3",
64296
+ code: FINDING_CODES.L3_AC_EMPTY,
64297
+ severity: "error",
64298
+ section: "Acceptance Criteria",
64299
+ message: "Acceptance Criteria is placeholder-only \u2014 populate with real scenarios"
64300
+ });
64301
+ }
63765
64302
  const reqBody = doc2.getSection("Requirements");
63766
64303
  if (reqBody !== null && !isPlaceholderBody(reqBody)) {
63767
64304
  const blocks = reqBody.trim().split(/\n\s*\n/).filter((b) => b.trim().length > 0);
@@ -63853,7 +64390,7 @@ var init_task_check = __esm(() => {
63853
64390
  }
63854
64391
  }
63855
64392
  }
63856
- async runL4(doc2, fm, findings, featuresDir, tasksDir) {
64393
+ async runL4(doc2, fm, status, findings, featuresDir, tasksDir) {
63857
64394
  const template = typeof fm.template === "string" ? fm.template : DEFAULT_TASK_VARIANT;
63858
64395
  if (template === DEFAULT_TASK_VARIANT) {
63859
64396
  const designBody = doc2.getSection("Design");
@@ -63867,6 +64404,7 @@ var init_task_check = __esm(() => {
63867
64404
  });
63868
64405
  }
63869
64406
  }
64407
+ await this.checkLineAnchors(doc2, tasksDir, findings);
63870
64408
  const featureId2 = fm.feature_id ?? fm["feature-id"];
63871
64409
  const parentWbs = fm.parent_wbs ?? fm["parent-wbs"];
63872
64410
  const deps = fm.dependencies;
@@ -63882,7 +64420,9 @@ var init_task_check = __esm(() => {
63882
64420
  });
63883
64421
  } else {
63884
64422
  const featureStatus = await this.readFeatureStatus(featurePath);
63885
- if (featureStatus === "done" || featureStatus === "cancelled") {
64423
+ const featureTerminal = featureStatus === "done" || featureStatus === "cancelled";
64424
+ const taskTerminal = status === "done" || status === "cancelled";
64425
+ if (featureTerminal && !taskTerminal) {
63886
64426
  findings.push({
63887
64427
  layer: "L4",
63888
64428
  code: FINDING_CODES.L4_FEATURE_TERMINAL,
@@ -64100,6 +64640,54 @@ var init_task_check = __esm(() => {
64100
64640
  return null;
64101
64641
  }
64102
64642
  }
64643
+ async checkLineAnchors(doc2, tasksDir, findings) {
64644
+ const projectRoot = resolveProjectRootFromTasksDir(tasksDir);
64645
+ for (const section of ["Testing", "Solution"]) {
64646
+ const body = doc2.getSection(section);
64647
+ if (body === null || isPlaceholderBody(body))
64648
+ continue;
64649
+ const citations = extractBacktickLineAnchors(body);
64650
+ let reported = 0;
64651
+ for (const cite of citations) {
64652
+ if (reported >= 5)
64653
+ break;
64654
+ const abs = join9(projectRoot, cite.path);
64655
+ let exists2 = false;
64656
+ try {
64657
+ exists2 = await this.fs.exists(abs);
64658
+ } catch {
64659
+ exists2 = false;
64660
+ }
64661
+ if (!exists2) {
64662
+ findings.push({
64663
+ layer: "L4",
64664
+ code: FINDING_CODES.L4_STALE_LINE_ANCHOR,
64665
+ severity: "warning",
64666
+ section,
64667
+ message: `Stale line anchor \`${cite.raw}\` \u2014 file not found at ${cite.path} (from project root)`
64668
+ });
64669
+ reported++;
64670
+ continue;
64671
+ }
64672
+ try {
64673
+ const raw = await this.fs.readFile(abs);
64674
+ const lineCount = raw.split(`
64675
+ `).length;
64676
+ const end = cite.endLine ?? cite.startLine;
64677
+ if (cite.startLine < 1 || end > lineCount) {
64678
+ findings.push({
64679
+ layer: "L4",
64680
+ code: FINDING_CODES.L4_STALE_LINE_ANCHOR,
64681
+ severity: "warning",
64682
+ section,
64683
+ message: `Stale line anchor \`${cite.raw}\` \u2014 line ${cite.startLine}${cite.endLine ? `-${cite.endLine}` : ""} outside file (${lineCount} lines)`
64684
+ });
64685
+ reported++;
64686
+ }
64687
+ } catch {}
64688
+ }
64689
+ }
64690
+ }
64103
64691
  async findChildren(tasksDir, wbs) {
64104
64692
  const kids = [];
64105
64693
  for (const dir of this.locatorFor(tasksDir).folderDirs()) {
@@ -64503,9 +65091,24 @@ function renderCreatedTaskContent(params) {
64503
65091
  if (params.tags !== undefined && params.tags.length > 0) {
64504
65092
  content = patchFrontmatterField(content, "tags", `[${params.tags.map((tag) => JSON.stringify(tag)).join(", ")}]`);
64505
65093
  }
65094
+ const sectionPatches = {};
64506
65095
  if ((params.requirements ?? "").trim() !== "") {
65096
+ sectionPatches.Requirements = bulletizeRequirements(params.requirements ?? "");
65097
+ }
65098
+ if ((params.design ?? "").trim() !== "") {
65099
+ sectionPatches.Design = (params.design ?? "").trim();
65100
+ }
65101
+ if ((params.plan ?? "").trim() !== "") {
65102
+ sectionPatches.Plan = (params.plan ?? "").trim();
65103
+ }
65104
+ if ((params.acceptanceCriteria ?? "").trim() !== "") {
65105
+ sectionPatches["Acceptance Criteria"] = (params.acceptanceCriteria ?? "").trim();
65106
+ }
65107
+ if (Object.keys(sectionPatches).length > 0) {
64507
65108
  const doc2 = MarkdownDocument.parse(content, "task");
64508
- doc2.replaceSection("Requirements", bulletizeRequirements(params.requirements ?? ""));
65109
+ for (const [section, body] of Object.entries(sectionPatches)) {
65110
+ doc2.replaceSection(section, body);
65111
+ }
64509
65112
  content = doc2.serialize();
64510
65113
  }
64511
65114
  return content;
@@ -64586,6 +65189,26 @@ class TaskService {
64586
65189
  const templateBodies = this.ctx.resolveTemplateBodies?.(variant) ?? {};
64587
65190
  return { ...templateBodies, ...taskBodies };
64588
65191
  }
65192
+ async findDuplicateFollowUp(featureId2, title, withinSec) {
65193
+ const siblings = await this.list({ featureId: featureId2 });
65194
+ const now = Date.now();
65195
+ const lowerTitle = title.toLowerCase();
65196
+ for (const t of siblings) {
65197
+ if ((t.name ?? "").toLowerCase() !== lowerTitle)
65198
+ continue;
65199
+ const createdAt = t.frontmatter?.created_at;
65200
+ if (typeof createdAt !== "string")
65201
+ continue;
65202
+ const createdMs = Date.parse(createdAt);
65203
+ if (Number.isNaN(createdMs))
65204
+ continue;
65205
+ const ageSec = (now - createdMs) / 1000;
65206
+ if (ageSec >= 0 && ageSec <= withinSec) {
65207
+ return { wbs: t.wbs, name: t.name };
65208
+ }
65209
+ }
65210
+ return null;
65211
+ }
64589
65212
  async create(params) {
64590
65213
  const folder = this.ctx.tasksDir;
64591
65214
  let background = "";
@@ -64595,6 +65218,12 @@ class TaskService {
64595
65218
  const variant = params.template ?? (params.featureId !== undefined ? "feature-impl" : DEFAULT_TASK_VARIANT);
64596
65219
  const status = params.status ?? (params.featureId !== undefined ? "todo" : "backlog");
64597
65220
  return this.writeService.createAllocated(folder, async () => {
65221
+ if (params.dedupeWithinSec !== undefined && params.featureId !== undefined) {
65222
+ const collision = await this.findDuplicateFollowUp(params.featureId, params.title, params.dedupeWithinSec);
65223
+ if (collision !== null) {
65224
+ throw new DuplicateFollowUpError(collision.wbs, collision.name, params.title);
65225
+ }
65226
+ }
64598
65227
  const wbs = await this.allocateWbs();
64599
65228
  const slug = this.slugify(params.title);
64600
65229
  const filePath = this.resolveTaskPath(wbs, slug);
@@ -64996,7 +65625,7 @@ ${issues}`);
64996
65625
  if (!background && item.feature_id !== undefined && item.feature_id !== null) {
64997
65626
  background = await this.deriveBackground(item.feature_id);
64998
65627
  }
64999
- const hasSpec = background !== "" || (item.requirements ?? "").trim() !== "";
65628
+ const hasSpec = background !== "" || (item.requirements ?? "").trim() !== "" || (item.design ?? "").trim() !== "" || (item.plan ?? "").trim() !== "" || (item.acceptance_criteria ?? "").trim() !== "";
65000
65629
  const status = hasSpec ? "todo" : "backlog";
65001
65630
  const variant = item.template ?? (item.feature_id !== undefined && item.feature_id !== null ? "feature-impl" : DEFAULT_TASK_VARIANT);
65002
65631
  return this.writeService.createAllocated(folder, async () => {
@@ -65018,7 +65647,10 @@ ${issues}`);
65018
65647
  parentWbs: item.parent_wbs ?? undefined,
65019
65648
  priority: item.priority,
65020
65649
  tags: item.tags,
65021
- requirements: item.requirements
65650
+ requirements: item.requirements,
65651
+ design: item.design,
65652
+ plan: item.plan,
65653
+ acceptanceCriteria: item.acceptance_criteria
65022
65654
  });
65023
65655
  const ref2 = { kind: "task", id: wbs, filePath, folder };
65024
65656
  return { ref: ref2, content: content2 };
@@ -65042,6 +65674,15 @@ ${issues}`);
65042
65674
  if ((item.requirements ?? "").trim() !== "") {
65043
65675
  taskBodies.Requirements = bulletizeRequirements(item.requirements ?? "");
65044
65676
  }
65677
+ if ((item.design ?? "").trim() !== "") {
65678
+ taskBodies.Design = (item.design ?? "").trim();
65679
+ }
65680
+ if ((item.plan ?? "").trim() !== "") {
65681
+ taskBodies.Plan = (item.plan ?? "").trim();
65682
+ }
65683
+ if ((item.acceptance_criteria ?? "").trim() !== "") {
65684
+ taskBodies["Acceptance Criteria"] = (item.acceptance_criteria ?? "").trim();
65685
+ }
65045
65686
  const content = buildTaskSkeleton({
65046
65687
  wbs,
65047
65688
  title: item.name,
@@ -65223,7 +65864,7 @@ ${block}` : block);
65223
65864
  return title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 60);
65224
65865
  }
65225
65866
  }
65226
- var DependencyMutationError, SectionMutationError, ROSTER_START = "<!-- AUTO-GENERATED by spur task refresh-roster -->", ROSTER_END = "<!-- END AUTO-GENERATED -->", ROSTER_REGION_RE, TASK_ACTION_COMMANDS, DEFAULT_CREATION_SECTIONS;
65867
+ var DependencyMutationError, SectionMutationError, DuplicateFollowUpError, ROSTER_START = "<!-- AUTO-GENERATED by spur task refresh-roster -->", ROSTER_END = "<!-- END AUTO-GENERATED -->", ROSTER_REGION_RE, TASK_ACTION_COMMANDS, DEFAULT_CREATION_SECTIONS;
65227
65868
  var init_task_service = __esm(() => {
65228
65869
  init_src2();
65229
65870
  init_task_locator();
@@ -65244,6 +65885,18 @@ var init_task_service = __esm(() => {
65244
65885
  this.code = code;
65245
65886
  }
65246
65887
  };
65888
+ DuplicateFollowUpError = class DuplicateFollowUpError extends Error {
65889
+ existingWbs;
65890
+ existingName;
65891
+ attemptedName;
65892
+ constructor(existingWbs, existingName, attemptedName) {
65893
+ super(`duplicate-follow-up: task ${existingWbs} ("${existingName}") already exists under this feature ` + `with an identical name. Reuse ${existingWbs} or pass --allow-duplicate-name to override.`);
65894
+ this.name = "DuplicateFollowUpError";
65895
+ this.existingWbs = existingWbs;
65896
+ this.existingName = existingName;
65897
+ this.attemptedName = attemptedName;
65898
+ }
65899
+ };
65247
65900
  ROSTER_REGION_RE = /<!--[ \t]*AUTO-GENERATED[^>]*-->[\s\S]*?<!--[ \t]*END[ \t]*AUTO-GENERATED[^>]*-->/g;
65248
65901
  TASK_ACTION_COMMANDS = {
65249
65902
  refine: (wbs) => `/sp:dev-refine ${wbs} --auto`,
@@ -65446,6 +66099,50 @@ function extractChecks(_text, taskCheckPassed, acceptanceCriteria) {
65446
66099
  }
65447
66100
  return checks4;
65448
66101
  }
66102
+ function classifyTaskOutcome(taskStatus, verdict) {
66103
+ const status = String(taskStatus).toLowerCase();
66104
+ if (NOT_STARTED_STATUSES[status]) {
66105
+ return "NOT-STARTED";
66106
+ }
66107
+ const v = String(verdict ?? "UNKNOWN").toUpperCase();
66108
+ if (v === "PASS" || v === "PARTIAL" || v === "FAIL")
66109
+ return v;
66110
+ return "UNKNOWN";
66111
+ }
66112
+ function aggregateBatchVerdicts(results) {
66113
+ const notStarted = results.filter((r) => r.outcome === "NOT-STARTED");
66114
+ const rolledUp = results.filter((r) => r.outcome !== "NOT-STARTED");
66115
+ const counts = {
66116
+ PASS: rolledUp.filter((r) => r.outcome === "PASS").length,
66117
+ PARTIAL: rolledUp.filter((r) => r.outcome === "PARTIAL").length,
66118
+ FAIL: rolledUp.filter((r) => r.outcome === "FAIL").length,
66119
+ UNKNOWN: rolledUp.filter((r) => r.outcome === "UNKNOWN").length
66120
+ };
66121
+ let verdict;
66122
+ if (rolledUp.length === 0) {
66123
+ verdict = "UNKNOWN";
66124
+ } else if (counts.FAIL > 0) {
66125
+ verdict = "FAIL";
66126
+ } else if (counts.PARTIAL > 0 || counts.UNKNOWN > 0) {
66127
+ verdict = "PARTIAL";
66128
+ } else {
66129
+ verdict = "PASS";
66130
+ }
66131
+ const summary = `${counts.PASS} PASS, ${counts.PARTIAL} PARTIAL, ${counts.FAIL} FAIL, ${notStarted.length} NOT-STARTED (excluded)`;
66132
+ return { verdict, rolledUp, notStarted, summary };
66133
+ }
66134
+ var NOT_STARTED_STATUSES;
66135
+ var init_task_verdict = __esm(() => {
66136
+ NOT_STARTED_STATUSES = {
66137
+ backlog: true,
66138
+ todo: true,
66139
+ wip: false,
66140
+ testing: false,
66141
+ blocked: true,
66142
+ done: false,
66143
+ cancelled: false
66144
+ };
66145
+ });
65449
66146
 
65450
66147
  // ../../packages/app/src/services/team-service.ts
65451
66148
  import { join as join10 } from "path";
@@ -66098,6 +66795,247 @@ var init_token_ledger_watcher = __esm(() => {
66098
66795
  init_token_ledger_service();
66099
66796
  });
66100
66797
 
66798
+ // ../../packages/app/src/workflow/steering.ts
66799
+ class WorkflowSteeringController {
66800
+ onAck;
66801
+ secrets;
66802
+ allowedActors;
66803
+ onState;
66804
+ active;
66805
+ seen = new Set;
66806
+ constructor(onAck, secrets = [], allowedActors = new Set(["operator"]), onState) {
66807
+ this.onAck = onAck;
66808
+ this.secrets = secrets;
66809
+ this.allowedActors = allowedActors;
66810
+ this.onState = onState;
66811
+ }
66812
+ get snapshot() {
66813
+ const active = this.active;
66814
+ return active === undefined ? undefined : {
66815
+ runId: active.runId,
66816
+ actionId: active.actionId,
66817
+ state: active.state,
66818
+ version: active.version
66819
+ };
66820
+ }
66821
+ begin(runId, actionId, policy) {
66822
+ this.active = {
66823
+ runId,
66824
+ actionId,
66825
+ state: "running",
66826
+ version: 1,
66827
+ attempts: 1,
66828
+ lastOk: false,
66829
+ policy,
66830
+ controller: new AbortController
66831
+ };
66832
+ this.publishState(this.active);
66833
+ return this.active.controller.signal;
66834
+ }
66835
+ async boundary(ok) {
66836
+ const active = this.requireActive();
66837
+ active.lastOk = ok;
66838
+ if (active.controller.signal.aborted)
66839
+ return { operation: "abort" };
66840
+ if (!active.policy.boundary)
66841
+ return { operation: "continue" };
66842
+ active.state = "boundary";
66843
+ active.version += 1;
66844
+ this.publishState(active);
66845
+ return await new Promise((resolve2) => {
66846
+ const settle = (decision) => {
66847
+ clearTimeout(timer);
66848
+ resolve2(decision);
66849
+ };
66850
+ const timer = setTimeout(() => {
66851
+ if (active.resolve !== settle)
66852
+ return;
66853
+ active.resolve = undefined;
66854
+ this.acceptDefault(active, resolve2);
66855
+ }, active.policy.timeoutMs);
66856
+ timer.unref?.();
66857
+ active.resolve = settle;
66858
+ });
66859
+ }
66860
+ nextAttempt() {
66861
+ const active = this.requireActive();
66862
+ active.attempts += 1;
66863
+ active.state = "running";
66864
+ active.version += 1;
66865
+ active.controller = new AbortController;
66866
+ this.publishState(active);
66867
+ return active.controller.signal;
66868
+ }
66869
+ complete() {
66870
+ const active = this.active;
66871
+ if (active === undefined)
66872
+ return;
66873
+ active.state = "completed";
66874
+ active.version += 1;
66875
+ active.resolve = undefined;
66876
+ this.publishState(active);
66877
+ }
66878
+ submit(command) {
66879
+ const active = this.active;
66880
+ if (command.commandId.trim() === "")
66881
+ return this.reject(command, active, "command id is required");
66882
+ if (this.seen.has(command.commandId))
66883
+ return this.reject(command, active, "duplicate command");
66884
+ this.seen.add(command.commandId);
66885
+ if (active === undefined)
66886
+ return this.reject(command, active, "no active action");
66887
+ if (!this.allowedActors.has(command.actor))
66888
+ return this.reject(command, active, "unauthorized actor");
66889
+ const deadlineMs = Date.parse(command.deadlineAt);
66890
+ if (!Number.isFinite(deadlineMs))
66891
+ return this.reject(command, active, "invalid command deadline");
66892
+ if (deadlineMs < Date.now())
66893
+ return this.reject(command, active, "command deadline elapsed");
66894
+ if (command.runId !== active.runId || command.actionId !== active.actionId) {
66895
+ return this.reject(command, active, "target does not match the active action");
66896
+ }
66897
+ if (command.expectedState !== active.state || command.expectedVersion !== active.version) {
66898
+ return this.reject(command, active, "stale state or version");
66899
+ }
66900
+ if (active.state === "completed") {
66901
+ return this.reject(command, active, "completed action history is immutable");
66902
+ }
66903
+ if (command.operation === "abort") {
66904
+ active.controller.abort();
66905
+ active.resolve?.({ operation: "abort" });
66906
+ active.resolve = undefined;
66907
+ return this.accept(command, active);
66908
+ }
66909
+ if (active.state !== "boundary" || active.resolve === undefined) {
66910
+ return this.reject(command, active, "action is not paused at a steering boundary");
66911
+ }
66912
+ if (command.operation === "retry") {
66913
+ const retry = active.policy.retry;
66914
+ if (active.lastOk)
66915
+ return this.reject(command, active, "retry requires a failed attempt");
66916
+ if (retry === undefined || retry.idempotent !== true) {
66917
+ return this.reject(command, active, "unsafe retry: no explicit idempotent retry policy");
66918
+ }
66919
+ if (active.attempts >= retry.maxAttempts) {
66920
+ return this.reject(command, active, "retry limit reached");
66921
+ }
66922
+ }
66923
+ if (command.operation === "note" && (command.note === undefined || command.note.trim() === "")) {
66924
+ return this.reject(command, active, "note text is required");
66925
+ }
66926
+ const note = command.operation === "note" && command.note !== undefined ? redactAndBound(command.note, this.secrets, 1024) : undefined;
66927
+ active.resolve({ operation: command.operation, ...note !== undefined ? { note } : {} });
66928
+ active.resolve = undefined;
66929
+ return this.accept(command, active, note);
66930
+ }
66931
+ submitLine(line, deadlineMs = 30000) {
66932
+ const active = this.active;
66933
+ if (active === undefined)
66934
+ return;
66935
+ const trimmed = line.trim();
66936
+ const [rawOperation, ...rest] = trimmed.split(/\s+/);
66937
+ if (!["continue", "note", "retry", "abort"].includes(rawOperation ?? ""))
66938
+ return;
66939
+ const operation = rawOperation;
66940
+ return this.submit({
66941
+ commandId: crypto.randomUUID(),
66942
+ runId: active.runId,
66943
+ actionId: active.actionId,
66944
+ expectedState: active.state,
66945
+ expectedVersion: active.version,
66946
+ operation,
66947
+ ...operation === "note" ? { note: rest.join(" ") } : {},
66948
+ actor: "operator",
66949
+ deadlineAt: new Date(Date.now() + deadlineMs).toISOString()
66950
+ });
66951
+ }
66952
+ acceptDefault(active, resolve2) {
66953
+ const command = {
66954
+ commandId: crypto.randomUUID(),
66955
+ runId: active.runId,
66956
+ actionId: active.actionId,
66957
+ expectedState: active.state,
66958
+ expectedVersion: active.version,
66959
+ operation: "continue",
66960
+ actor: "system-timeout",
66961
+ deadlineAt: new Date().toISOString()
66962
+ };
66963
+ const ack = {
66964
+ schemaVersion: 1,
66965
+ commandId: command.commandId,
66966
+ runId: command.runId,
66967
+ actionId: command.actionId,
66968
+ operation: command.operation,
66969
+ actor: command.actor,
66970
+ accepted: true,
66971
+ state: active.state,
66972
+ version: active.version,
66973
+ reason: "boundary timeout defaulted to continue",
66974
+ at: new Date().toISOString()
66975
+ };
66976
+ this.onAck?.(ack);
66977
+ resolve2({ operation: "continue" });
66978
+ }
66979
+ accept(command, active, note) {
66980
+ return this.publish({
66981
+ schemaVersion: 1,
66982
+ commandId: command.commandId,
66983
+ runId: command.runId,
66984
+ actionId: command.actionId,
66985
+ operation: command.operation,
66986
+ actor: command.actor,
66987
+ accepted: true,
66988
+ state: active.state,
66989
+ version: active.version,
66990
+ ...note !== undefined ? { note } : {},
66991
+ at: new Date().toISOString()
66992
+ });
66993
+ }
66994
+ reject(command, active, reason) {
66995
+ return this.publish({
66996
+ schemaVersion: 1,
66997
+ commandId: command.commandId,
66998
+ runId: command.runId,
66999
+ actionId: command.actionId,
67000
+ operation: command.operation,
67001
+ actor: command.actor,
67002
+ accepted: false,
67003
+ state: active?.state ?? "completed",
67004
+ version: active?.version ?? 0,
67005
+ reason,
67006
+ at: new Date().toISOString()
67007
+ });
67008
+ }
67009
+ publish(ack) {
67010
+ this.onAck?.(ack);
67011
+ return ack;
67012
+ }
67013
+ requireActive() {
67014
+ if (this.active === undefined)
67015
+ throw new Error("no active steering action");
67016
+ return this.active;
67017
+ }
67018
+ publishState(active) {
67019
+ this.onState?.({
67020
+ runId: active.runId,
67021
+ actionId: active.actionId,
67022
+ state: active.state,
67023
+ version: active.version
67024
+ });
67025
+ }
67026
+ }
67027
+ function parseSteeringPolicy(options) {
67028
+ const boundary2 = options.steeringBoundary === true;
67029
+ const timeoutMs = typeof options.steeringTimeoutMs === "number" && Number.isFinite(options.steeringTimeoutMs) && Number.isInteger(options.steeringTimeoutMs) && options.steeringTimeoutMs > 0 ? Math.min(options.steeringTimeoutMs, MAX_STEERING_TIMEOUT_MS) : 30000;
67030
+ const rawRetry = options.retryPolicy;
67031
+ const retry = typeof rawRetry === "object" && rawRetry !== null && "idempotent" in rawRetry && rawRetry.idempotent === true && "maxAttempts" in rawRetry && typeof rawRetry.maxAttempts === "number" && Number.isFinite(rawRetry.maxAttempts) && Number.isInteger(rawRetry.maxAttempts) && rawRetry.maxAttempts > 1 ? { idempotent: true, maxAttempts: Math.min(rawRetry.maxAttempts, MAX_STEERING_ATTEMPTS) } : undefined;
67032
+ return { boundary: boundary2, timeoutMs, ...retry !== undefined ? { retry } : {} };
67033
+ }
67034
+ var MAX_STEERING_TIMEOUT_MS = 300000, MAX_STEERING_ATTEMPTS = 10;
67035
+ var init_steering = __esm(() => {
67036
+ init_agent_execution();
67037
+ });
67038
+
66101
67039
  // ../../packages/app/src/workflow/actions/agent-run.ts
66102
67040
  import { execFile } from "child_process";
66103
67041
  import { existsSync as existsSync7 } from "fs";
@@ -66106,9 +67044,13 @@ import { dirname as dirname11, isAbsolute as isAbsolute2, join as join12 } from
66106
67044
  import { promisify as promisify3 } from "util";
66107
67045
 
66108
67046
  class AgentRunActionRunner {
67047
+ observabilityBus;
67048
+ steeringController;
66109
67049
  kind = KIND;
66110
67050
  agentService;
66111
- constructor(agentService) {
67051
+ constructor(agentService, observabilityBus, steeringController) {
67052
+ this.observabilityBus = observabilityBus;
67053
+ this.steeringController = steeringController;
66112
67054
  this.agentService = agentService;
66113
67055
  }
66114
67056
  async execute(options, context4) {
@@ -66151,7 +67093,44 @@ class AgentRunActionRunner {
66151
67093
  const expectFile = asOptionalString(options.expectFile);
66152
67094
  const capture = asOptionalBoolean(options.capture) || answerFile !== undefined;
66153
67095
  const agentLabel = agent ?? "<default>";
66154
- const traced = await this.agentService.runTraced(input, flags);
67096
+ const observer = this.observabilityBus === undefined ? undefined : (event2) => {
67097
+ this.observabilityBus?.emit("workflow.agent", event2);
67098
+ };
67099
+ const actionId = context4.actionId ?? `${context4.runId}:${context4.stateOrNodeId}`;
67100
+ const steeringPolicy = parseSteeringPolicy(options);
67101
+ let steeringSignal = this.steeringController?.begin(context4.runId, actionId, steeringPolicy);
67102
+ let steeringNote;
67103
+ let traced;
67104
+ while (true) {
67105
+ traced = await this.agentService.runTraced(input, flags, undefined, {
67106
+ correlation: {
67107
+ runId: context4.runId,
67108
+ executionId: crypto.randomUUID(),
67109
+ actionId
67110
+ },
67111
+ ...observer !== undefined ? { observer } : {},
67112
+ ...steeringSignal !== undefined ? { signal: steeringSignal } : {}
67113
+ });
67114
+ if (this.steeringController === undefined)
67115
+ break;
67116
+ const decision = await this.steeringController.boundary(traced.exitCode === 0);
67117
+ if (decision.operation === "retry") {
67118
+ steeringSignal = this.steeringController.nextAttempt();
67119
+ continue;
67120
+ }
67121
+ if (decision.operation === "note")
67122
+ steeringNote = decision.note;
67123
+ if (decision.operation === "abort" && traced.exitCode === 0) {
67124
+ traced = {
67125
+ ...traced,
67126
+ exitCode: 3,
67127
+ signal: "STEERING_ABORT",
67128
+ message: "aborted at steering boundary"
67129
+ };
67130
+ }
67131
+ break;
67132
+ }
67133
+ this.steeringController?.complete();
66155
67134
  const { exitCode, stdout: answer } = traced;
66156
67135
  const ok = exitCode === 0;
66157
67136
  const invocation = traced.invocation;
@@ -66179,7 +67158,10 @@ class AgentRunActionRunner {
66179
67158
  ok,
66180
67159
  data: buildResultData(exitCode, agentLabel, capture, answer, invocation),
66181
67160
  error: error51,
66182
- setVars: ok ? { __agentSession: "open" } : undefined
67161
+ setVars: ok ? {
67162
+ __agentSession: "open",
67163
+ ...steeringNote !== undefined ? { __steeringNote: steeringNote } : {}
67164
+ } : undefined
66183
67165
  };
66184
67166
  }
66185
67167
  }
@@ -66287,6 +67269,7 @@ ${text4.slice(text4.length - maxChars)}`;
66287
67269
  }
66288
67270
  var execFileAsync, PARTIAL_ARTIFACT_TAIL_CHARS = 4000, KIND = "agent.run";
66289
67271
  var init_agent_run = __esm(() => {
67272
+ init_steering();
66290
67273
  execFileAsync = promisify3(execFile);
66291
67274
  });
66292
67275
 
@@ -66815,7 +67798,7 @@ var init_rule_check = __esm(() => {
66815
67798
  // ../../packages/app/src/workflow/builtins.ts
66816
67799
  function registerSpurBuiltins(host, options) {
66817
67800
  const fileSystem = options.fileSystem ?? createNodeFileSystem3();
66818
- host.registerAction(new AgentRunActionRunner(options.agentService), "builtin");
67801
+ host.registerAction(new AgentRunActionRunner(options.agentService, options.observabilityBus, options.steeringController), "builtin");
66819
67802
  host.registerAction(new RuleCheckActionRunner(options.ruleService), "builtin");
66820
67803
  host.registerAction(new FileExistsActionRunner(fileSystem), "builtin");
66821
67804
  host.registerAction(new FileReadActionRunner(fileSystem), "builtin");
@@ -66841,56 +67824,125 @@ var init_builtins2 = __esm(() => {
66841
67824
  });
66842
67825
 
66843
67826
  // ../../packages/app/src/workflow/observability.ts
67827
+ function bounded(value) {
67828
+ const redacted = value.replace(SECRET_PATTERN2, "[REDACTED]");
67829
+ return redacted.length <= MAX_FIELD_LENGTH ? redacted : `${redacted.slice(0, MAX_FIELD_LENGTH)}\u2026`;
67830
+ }
67831
+ function projectActionMetadata(kind, options) {
67832
+ if (options === undefined)
67833
+ return;
67834
+ const metadata = {};
67835
+ if (typeof options.agent === "string" && options.agent !== "")
67836
+ metadata.agent = bounded(options.agent);
67837
+ if (typeof options.model === "string" && options.model !== "")
67838
+ metadata.model = bounded(options.model);
67839
+ if (typeof options.timeoutMs === "number" && Number.isFinite(options.timeoutMs)) {
67840
+ metadata.timeoutMs = Math.max(0, options.timeoutMs);
67841
+ }
67842
+ const input = typeof options.input === "string" ? options.input.trim() : "";
67843
+ if (kind === "agent.run" && input !== "") {
67844
+ const command = input.startsWith("/") ? input.split(/\s+/, 1)[0] : undefined;
67845
+ metadata.invocation = command === undefined ? `[prompt ${input.length} chars]` : bounded(command);
67846
+ } else if (kind === "shell" && typeof options.command === "string") {
67847
+ metadata.invocation = "[shell command redacted]";
67848
+ }
67849
+ return Object.keys(metadata).length === 0 ? undefined : metadata;
67850
+ }
67851
+ function projectResult(result) {
67852
+ if (result === undefined)
67853
+ return;
67854
+ if (typeof result !== "object" || result === null)
67855
+ return { usage: "unavailable" };
67856
+ const error51 = "error" in result && typeof result.error === "string" ? bounded(result.error) : undefined;
67857
+ return { ...error51 !== undefined ? { error: error51 } : {}, usage: "unavailable" };
67858
+ }
67859
+
66844
67860
  class ObservableWorkflowAdapter {
66845
67861
  inner;
66846
67862
  bus;
67863
+ sequences = new Map;
67864
+ workflowNames = new Map;
67865
+ actions = new Map;
66847
67866
  constructor(inner, bus) {
66848
67867
  this.inner = inner;
66849
67868
  this.bus = bus;
66850
67869
  }
66851
67870
  async createRun(record2) {
66852
67871
  await this.inner.createRun(record2);
67872
+ this.workflowNames.set(record2.id, record2.workflow_name);
66853
67873
  await this.bus.emit("workflow.run.started", {
66854
- runId: record2.id,
66855
- workflowName: record2.workflow_name,
66856
- at: now()
67874
+ ...this.envelope(record2.id),
67875
+ workflowName: record2.workflow_name
66857
67876
  });
66858
67877
  }
66859
67878
  async finalizeRun(runId, status, completedAt) {
66860
67879
  await this.inner.finalizeRun(runId, status, completedAt);
66861
- await this.bus.emit("workflow.run.finalized", { runId, status, at: completedAt });
67880
+ await this.bus.emit("workflow.run.finalized", { ...this.envelope(runId, completedAt), status });
66862
67881
  }
66863
67882
  async savePhase(runId, phase, status) {
66864
67883
  await this.inner.savePhase(runId, phase, status);
66865
- await this.bus.emit("workflow.phase", { runId, phase, status, at: now() });
67884
+ await this.bus.emit("workflow.phase", { ...this.envelope(runId), phase, status });
66866
67885
  }
66867
67886
  async saveTransition(runId, from, to, trigger) {
66868
67887
  await this.inner.saveTransition(runId, from, to, trigger);
66869
- await this.bus.emit("workflow.transition", { runId, from, to, trigger, at: now() });
67888
+ await this.bus.emit("workflow.transition", { ...this.envelope(runId), from, to, trigger });
66870
67889
  }
66871
67890
  async commitTransition(runId, from, to, trigger, _state, _data, phase) {
66872
67891
  await this.inner.commitTransition(runId, from, to, trigger, _state, _data, phase);
66873
- await this.bus.emit("workflow.transition", { runId, from, to, trigger, at: now() });
67892
+ await this.bus.emit("workflow.transition", { ...this.envelope(runId), from, to, trigger });
66874
67893
  if (phase) {
66875
- await this.bus.emit("workflow.phase", { runId, phase: phase.phase, status: phase.status, at: now() });
67894
+ await this.bus.emit("workflow.phase", {
67895
+ ...this.envelope(runId),
67896
+ phase: phase.phase,
67897
+ status: phase.status
67898
+ });
66876
67899
  }
66877
67900
  }
66878
- async saveActionStart(runId, node, kind) {
66879
- const actionId = await this.inner.saveActionStart(runId, node, kind);
66880
- await this.bus.emit("workflow.action.started", { runId, actionId, node, kind, at: now() });
67901
+ async saveActionStart(runId, node, kind, options) {
67902
+ const actionId = await this.inner.saveActionStart(runId, node, kind, options);
67903
+ this.actions.set(actionId, { runId, node, kind });
67904
+ const metadata = projectActionMetadata(kind, options);
67905
+ await this.bus.emit("workflow.action.started", {
67906
+ ...this.envelope(runId),
67907
+ actionId,
67908
+ node,
67909
+ kind,
67910
+ ...metadata !== undefined ? { metadata } : {}
67911
+ });
66881
67912
  return actionId;
66882
67913
  }
66883
67914
  async saveActionFinalize(actionId, status, durationMs, ok, result, redactor) {
66884
67915
  await this.inner.saveActionFinalize(actionId, status, durationMs, ok, result, redactor);
67916
+ const action = this.actions.get(actionId);
67917
+ if (action === undefined) {
67918
+ return;
67919
+ }
67920
+ this.actions.delete(actionId);
67921
+ const projected = projectResult(result);
66885
67922
  await this.bus.emit("workflow.action.finished", {
66886
- runId: "",
67923
+ ...this.envelope(action.runId),
66887
67924
  actionId,
66888
67925
  status,
66889
67926
  durationMs,
66890
67927
  ok,
66891
- at: now()
67928
+ node: action.node,
67929
+ kind: action.kind,
67930
+ ...projected !== undefined ? { result: projected } : {}
66892
67931
  });
66893
67932
  }
67933
+ envelope(runId, at = now()) {
67934
+ const sequence = (this.sequences.get(runId) ?? 0) + 1;
67935
+ this.sequences.set(runId, sequence);
67936
+ const workflowName = this.workflowNames.get(runId);
67937
+ return {
67938
+ schemaVersion: 1,
67939
+ eventId: crypto.randomUUID(),
67940
+ sequence,
67941
+ runId,
67942
+ ...workflowName !== undefined ? { workflowName } : {},
67943
+ at
67944
+ };
67945
+ }
66894
67946
  saveWorkflowState(runId, state, data) {
66895
67947
  return this.inner.saveWorkflowState(runId, state, data);
66896
67948
  }
@@ -66912,11 +67964,17 @@ class ObservableWorkflowAdapter {
66912
67964
  loadCurrentState(runId) {
66913
67965
  return this.inner.loadCurrentState(runId);
66914
67966
  }
67967
+ loadLatestStateSnapshot(runId) {
67968
+ return this.inner.loadLatestStateSnapshot(runId);
67969
+ }
66915
67970
  listPausedRuns(options) {
66916
67971
  return this.inner.listPausedRuns(options);
66917
67972
  }
66918
67973
  }
66919
- var now = () => new Date().toISOString();
67974
+ var now = () => new Date().toISOString(), MAX_FIELD_LENGTH = 256, SECRET_PATTERN2;
67975
+ var init_observability = __esm(() => {
67976
+ SECRET_PATTERN2 = /(?:sk-[a-z0-9_-]{8,}|(?:api[_-]?key|token|secret|password)\s*[:=]\s*\S+|bearer\s+\S+)/gi;
67977
+ });
66920
67978
 
66921
67979
  // ../../packages/app/src/services/workflow-service.ts
66922
67980
  import { access as access2, lstat, readdir as readdir3, readFile as readFile2, realpath } from "fs/promises";
@@ -67011,14 +68069,16 @@ class WorkflowAppService {
67011
68069
  const eventsBus = this.ctx.events?.();
67012
68070
  const svc = await this.createEngineService({
67013
68071
  recordSelfPid: opts.recordSelfPid === true,
67014
- events: eventsBus
68072
+ events: eventsBus,
68073
+ steeringController: opts.steeringController
67015
68074
  });
67016
68075
  const runId = opts.runId ?? crypto.randomUUID();
67017
68076
  const isDry = opts.dryRun === true;
68077
+ const runVars = { ...opts.vars ?? {}, __runId: runId };
67018
68078
  const result = await svc.runFile(file2, {
67019
68079
  workdir: this.ctx.cwd,
67020
68080
  runId,
67021
- ...opts.vars ? { vars: opts.vars } : {},
68081
+ vars: runVars,
67022
68082
  ...isDry ? { dryRun: true } : {},
67023
68083
  ...eventsBus !== undefined ? { events: bridgeEventBus(eventsBus) } : {}
67024
68084
  });
@@ -67026,6 +68086,7 @@ class WorkflowAppService {
67026
68086
  const db2 = await this.ctx.getDb();
67027
68087
  await new RunDao(db2).stampMetadata(runId, { dryRun: true });
67028
68088
  }
68089
+ await this.stampFailureReason(runId, result);
67029
68090
  await this.maybeLinkPipelineRun(file2, runId, opts);
67030
68091
  return result;
67031
68092
  }
@@ -67106,8 +68167,16 @@ class WorkflowAppService {
67106
68167
  throw new Error(`Cannot resume run "${runId}": workflow definition "${target.workflow_name}" not found in the workflow search paths.`);
67107
68168
  }
67108
68169
  const result = await svc.resumeRun(workflow, runId, { workdir: this.ctx.cwd });
68170
+ await this.stampFailureReason(runId, result);
67109
68171
  return result;
67110
68172
  }
68173
+ async stampFailureReason(runId, result) {
68174
+ if (result.status !== "failed" || typeof result.reason !== "string" || result.reason === "") {
68175
+ return;
68176
+ }
68177
+ const db2 = await this.ctx.getDb();
68178
+ await new RunDao(db2).stampFailureReason(runId, result.reason);
68179
+ }
67111
68180
  async resolveWorkflowDefByName(name) {
67112
68181
  const listing = await this.list();
67113
68182
  const entry = listing.entries.find((e) => e.name === name && e.valid);
@@ -67233,19 +68302,21 @@ class WorkflowAppService {
67233
68302
  }
67234
68303
  async createEngineService(opts = {}) {
67235
68304
  const host = createDefaultWorkflowEngineHost();
68305
+ const bus = this.ctx.observabilityBus?.();
67236
68306
  registerSpurBuiltins(host, {
67237
68307
  agentService: this.ctx.agentService(),
67238
68308
  ruleService: this.ctx.ruleService(),
67239
68309
  hitlResponder: this.ctx.hitlResponder(),
67240
68310
  httpRequester: this.ctx.httpRequester?.(),
67241
- hostAllowlist: this.ctx.hostAllowlist?.()
68311
+ hostAllowlist: this.ctx.hostAllowlist?.(),
68312
+ ...bus !== undefined ? { observabilityBus: bus } : {},
68313
+ ...opts.steeringController !== undefined ? { steeringController: opts.steeringController } : {}
67242
68314
  });
67243
68315
  const db2 = await this.ctx.getDb();
67244
68316
  let persistence2 = new DbWorkflowPersistenceAdapter(db2);
67245
68317
  if (opts.recordSelfPid === true) {
67246
68318
  persistence2 = withSelfPidRecording(persistence2, db2);
67247
68319
  }
67248
- const bus = this.ctx.observabilityBus?.();
67249
68320
  const adapter = bus ? new ObservableWorkflowAdapter(persistence2, bus) : persistence2;
67250
68321
  return new WorkflowService(host, adapter);
67251
68322
  }
@@ -67311,9 +68382,13 @@ async function extractWorkflowMeta(filePath) {
67311
68382
  }
67312
68383
  function rowToTraceEntry(row) {
67313
68384
  let isDryRun = false;
68385
+ let failureReason;
67314
68386
  try {
67315
68387
  const meta3 = JSON.parse(row.metadata_json);
67316
68388
  isDryRun = meta3.dryRun === true;
68389
+ if (typeof meta3.failureReason === "string" && meta3.failureReason !== "") {
68390
+ failureReason = meta3.failureReason;
68391
+ }
67317
68392
  } catch {}
67318
68393
  return {
67319
68394
  runId: row.id,
@@ -67322,7 +68397,8 @@ function rowToTraceEntry(row) {
67322
68397
  status: row.status,
67323
68398
  startedAt: row.started_at,
67324
68399
  completedAt: row.completed_at,
67325
- isDryRun
68400
+ isDryRun,
68401
+ ...failureReason !== undefined ? { failureReason } : {}
67326
68402
  };
67327
68403
  }
67328
68404
  var TASK_PIPELINE_WORKFLOW = "task-pipeline", EMBEDDED_SCHEMA_PREFIX = "\x00embedded-spur", SPUR_SCHEMA_MANIFEST = "@gobing-ai/spur/package.json", PIPELINE_LINK_KIND = "pipeline";
@@ -67331,6 +68407,7 @@ var init_workflow_service = __esm(() => {
67331
68407
  init_dist7();
67332
68408
  init_dist5();
67333
68409
  init_builtins2();
68410
+ init_observability();
67334
68411
  });
67335
68412
 
67336
68413
  // ../../packages/app/src/workflow/lifecycle-adapter.ts
@@ -67478,19 +68555,72 @@ var init_lifecycle_adapter = __esm(() => {
67478
68555
  });
67479
68556
 
67480
68557
  // ../../packages/app/src/workflow/step-reporter.ts
67481
- function renderStepLine(event2) {
68558
+ function renderStepLine(event2, options = {}) {
68559
+ const detail = options.detail ?? "invocation";
68560
+ if (isAgentExecution(event2)) {
68561
+ if (event2.kind === "output") {
68562
+ if (detail === "minimal")
68563
+ return null;
68564
+ const label = event2.stream === "stderr" ? "stderr" : "stdout";
68565
+ return `[run ${event2.runId}] ${label}> ${event2.chunk.replace(/\s+$/, "")}`;
68566
+ }
68567
+ if (event2.kind === "heartbeat") {
68568
+ if (detail === "minimal")
68569
+ return null;
68570
+ const budget = event2.timeoutMs === undefined ? "unbounded" : `${formatDuration(Math.max(0, event2.timeoutMs - event2.elapsedMs))} remaining`;
68571
+ return `[run ${event2.runId}] \u2026 agent execution \xB7 elapsed=${formatDuration(event2.elapsedMs)} \xB7 budget=${budget}`;
68572
+ }
68573
+ if (event2.kind === "dropped") {
68574
+ return detail === "minimal" ? null : `[run ${event2.runId}] \u2026 output pressure \xB7 dropped=${event2.chunks} chunks`;
68575
+ }
68576
+ if (event2.kind === "started") {
68577
+ if (detail === "minimal")
68578
+ return null;
68579
+ const model = event2.model === undefined ? "" : `(${event2.model})`;
68580
+ const full2 = detail === "full" ? ` \xB7 execution=${event2.executionId}` : "";
68581
+ return `[run ${event2.runId}] agent=${event2.agent}${model} => ${event2.invocation}${full2}`;
68582
+ }
68583
+ const mark = event2.outcome === "done" ? "\u2713" : "\u2717";
68584
+ const full = detail === "full" ? ` \xB7 execution=${event2.executionId}` : "";
68585
+ return detail === "minimal" ? null : `[run ${event2.runId}] ${mark} agent ${event2.outcome} (${formatDuration(event2.durationMs)}) \xB7 usage ${event2.usage}${event2.reason ? ` \xB7 ${event2.reason}` : ""}${full}`;
68586
+ }
67482
68587
  if (isActionFinished(event2)) {
67483
68588
  const mark = event2.ok ? "\u2713" : "\u2717";
67484
- return ` ${mark} ${event2.status} (${formatDuration(event2.durationMs)})`;
68589
+ if (detail === "minimal")
68590
+ return ` ${mark} ${event2.status} (${formatDuration(event2.durationMs)})`;
68591
+ const failure = event2.result?.error === undefined ? "" : ` \xB7 ${event2.result.error}`;
68592
+ const full = detail === "full" ? ` \xB7 action=${event2.actionId} \xB7 seq=${event2.sequence}` : "";
68593
+ return `[run ${event2.runId}] ${mark} ${event2.node}/${event2.kind} (${formatDuration(event2.durationMs)}) \xB7 usage ${event2.result?.usage ?? "unavailable"}${failure}${full}`;
67485
68594
  }
67486
68595
  if (isActionStarted(event2)) {
67487
- return ` \u2192 ${event2.node}: ${event2.kind}\u2026`;
68596
+ if (detail === "minimal")
68597
+ return ` \u2192 ${event2.node}: ${event2.kind}\u2026`;
68598
+ const agent = event2.metadata?.agent ?? "unavailable";
68599
+ const model = event2.metadata?.model ?? "unavailable";
68600
+ const invocation = event2.metadata?.invocation ?? "unavailable";
68601
+ const timeout = event2.metadata?.timeoutMs === undefined ? "unbounded" : formatDuration(event2.metadata.timeoutMs);
68602
+ const full = detail === "full" ? ` \xB7 action=${event2.actionId} \xB7 seq=${event2.sequence}` : "";
68603
+ return `[run ${event2.runId}] \u2192 ${event2.node}/${event2.kind} \xB7 agent=${agent} \xB7 model=${model} => ${invocation} \xB7 timeout=${timeout}${full}`;
67488
68604
  }
67489
68605
  if (isPhase(event2)) {
67490
- return `\u25B6 ${event2.phase} [${event2.status}]`;
68606
+ return detail === "minimal" ? `\u25B6 ${event2.phase} [${event2.status}]` : `[run ${event2.runId}] \u25B6 ${event2.phase} [${event2.status}]`;
68607
+ }
68608
+ if (isTransition(event2)) {
68609
+ if (detail !== "full")
68610
+ return null;
68611
+ return `[run ${event2.runId}] \u21AA ${event2.from} \u2192 ${event2.to}${event2.trigger ? ` [${event2.trigger}]` : ""} \xB7 seq=${event2.sequence}`;
67491
68612
  }
67492
68613
  return null;
67493
68614
  }
68615
+ function renderActionHeartbeat(event2, elapsedMs, options = {}) {
68616
+ const detail = options.detail ?? "invocation";
68617
+ if (detail === "minimal")
68618
+ return null;
68619
+ const elapsed = formatDuration(elapsedMs);
68620
+ const budget = event2.metadata?.timeoutMs === undefined ? "unbounded" : `${formatDuration(Math.max(0, event2.metadata.timeoutMs - elapsedMs))} remaining`;
68621
+ const full = detail === "full" ? ` \xB7 action=${event2.actionId}` : "";
68622
+ return `[run ${event2.runId}] \u2026 ${event2.node}/${event2.kind} \xB7 elapsed=${elapsed} \xB7 budget=${budget}${full}`;
68623
+ }
67494
68624
  function renderRunPlan(def) {
67495
68625
  const steps = def.kind === "transition-flow" ? def.nodes.map((n2) => n2.id) : def.states.map((s) => s.id);
67496
68626
  return `plan: ${steps.join(" \u2192 ")}`;
@@ -67503,7 +68633,49 @@ function formatDuration(ms) {
67503
68633
  const seconds = totalSeconds % 60;
67504
68634
  return `${minutes}m ${seconds}s`;
67505
68635
  }
67506
- var isActionStarted = (e) => ("kind" in e) && ("node" in e), isActionFinished = (e) => ("durationMs" in e) && ("ok" in e), isPhase = (e) => ("phase" in e);
68636
+ var isAgentExecution = (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);
68637
+
68638
+ // ../../packages/app/src/workflow/trace-writer.ts
68639
+ import { appendFile, mkdir as mkdir2 } from "fs/promises";
68640
+ import { dirname as dirname12, join as join14 } from "path";
68641
+
68642
+ class WorkflowTraceWriter {
68643
+ path;
68644
+ sequence = 0;
68645
+ pending = Promise.resolve();
68646
+ constructor(cwd, runId) {
68647
+ const safeRunId = runId.replace(/[^A-Za-z0-9._-]/g, "_");
68648
+ this.path = join14(cwd, ".spur", "runs", "workflow", `${safeRunId}.jsonl`);
68649
+ }
68650
+ attach(bus) {
68651
+ bus.on("workflow.run.started", (event2) => this.enqueue("workflow.run.started", event2));
68652
+ bus.on("workflow.run.finalized", (event2) => this.enqueue("workflow.run.finalized", event2));
68653
+ bus.on("workflow.phase", (event2) => this.enqueue("workflow.phase", event2));
68654
+ bus.on("workflow.transition", (event2) => this.enqueue("workflow.transition", event2));
68655
+ bus.on("workflow.action.started", (event2) => this.enqueue("workflow.action.started", event2));
68656
+ bus.on("workflow.action.finished", (event2) => this.enqueue("workflow.action.finished", event2));
68657
+ bus.on("workflow.agent", (event2) => this.enqueue("workflow.agent", event2));
68658
+ bus.on("workflow.steering", (event2) => this.enqueue("workflow.steering", event2));
68659
+ }
68660
+ async flush() {
68661
+ await this.pending;
68662
+ }
68663
+ enqueue(type, event2) {
68664
+ this.sequence += 1;
68665
+ const line = `${JSON.stringify({
68666
+ traceSchemaVersion: 1,
68667
+ traceSequence: this.sequence,
68668
+ type,
68669
+ event: event2
68670
+ })}
68671
+ `;
68672
+ this.pending = this.pending.then(async () => {
68673
+ await mkdir2(dirname12(this.path), { recursive: true });
68674
+ await appendFile(this.path, line, "utf8");
68675
+ });
68676
+ }
68677
+ }
68678
+ var init_trace_writer = () => {};
67507
68679
 
67508
68680
  // ../../packages/app/src/index.ts
67509
68681
  var exports_src2 = {};
@@ -67517,11 +68689,14 @@ __export(exports_src2, {
67517
68689
  renderSolutionFromDiff: () => renderSolutionFromDiff,
67518
68690
  renderRunPlan: () => renderRunPlan,
67519
68691
  renderReview: () => renderReview,
68692
+ renderActionHeartbeat: () => renderActionHeartbeat,
67520
68693
  registerSystemEventTap: () => registerSystemEventTap,
67521
68694
  registerSpurBuiltins: () => registerSpurBuiltins,
68695
+ redactAndBound: () => redactAndBound,
67522
68696
  readVerdictArtifact: () => readVerdictArtifact,
67523
68697
  readVerdict: () => readVerdict,
67524
68698
  parseVerdict: () => parseVerdict,
68699
+ parseSteeringPolicy: () => parseSteeringPolicy,
67525
68700
  parsePsOutput: () => parsePsOutput,
67526
68701
  parseLedgerLine: () => parseLedgerLine,
67527
68702
  parseEtimeToSeconds: () => parseEtimeToSeconds,
@@ -67537,9 +68712,14 @@ __export(exports_src2, {
67537
68712
  evaluateDoneTransition: () => evaluateDoneTransition,
67538
68713
  deriveVerdict: () => deriveVerdict,
67539
68714
  createPsProcessInspector: () => createPsProcessInspector,
68715
+ configuredSecretValues: () => configuredSecretValues,
67540
68716
  computeAggregate: () => computeAggregate,
68717
+ classifyTaskOutcome: () => classifyTaskOutcome,
67541
68718
  clampToolUseLimit: () => clampToolUseLimit,
67542
68719
  bridgeEventBus: () => bridgeEventBus,
68720
+ aggregateBatchVerdicts: () => aggregateBatchVerdicts,
68721
+ WorkflowTraceWriter: () => WorkflowTraceWriter,
68722
+ WorkflowSteeringController: () => WorkflowSteeringController,
67543
68723
  WorkflowAppService: () => WorkflowAppService,
67544
68724
  UnsupportedProcessPlatformError: () => UnsupportedProcessPlatformError,
67545
68725
  TokenLedgerWatcher: () => TokenLedgerWatcher,
@@ -67588,17 +68768,20 @@ __export(exports_src2, {
67588
68768
  FeatureCheckService: () => FeatureCheckService,
67589
68769
  FINDING_CODES: () => FINDING_CODES,
67590
68770
  FEATURE_LIFECYCLE_PROFILE: () => FEATURE_LIFECYCLE_PROFILE,
68771
+ DuplicateFollowUpError: () => DuplicateFollowUpError,
67591
68772
  DependencyMutationError: () => DependencyMutationError,
67592
68773
  DEFAULT_FEATURE_MATRIX: () => DEFAULT_FEATURE_MATRIX,
67593
68774
  CorpusMigrator: () => CorpusMigrator,
67594
68775
  BusPlanningEventEmitter: () => BusPlanningEventEmitter,
67595
68776
  AgentService: () => AgentService,
67596
68777
  AgentRunActionRunner: () => AgentRunActionRunner,
68778
+ AgentExecutionLifecycle: () => AgentExecutionLifecycle,
67597
68779
  ALL_FINDING_CODES: () => ALL_FINDING_CODES
67598
68780
  });
67599
68781
  var init_src3 = __esm(() => {
67600
68782
  init_planning_folders();
67601
68783
  init_errors5();
68784
+ init_agent_execution();
67602
68785
  init_agent_service();
67603
68786
  init_corpus_migrator();
67604
68787
  init_event_names();
@@ -67619,6 +68802,7 @@ var init_src3 = __esm(() => {
67619
68802
  init_task_record();
67620
68803
  init_task_scaffold();
67621
68804
  init_task_service();
68805
+ init_task_verdict();
67622
68806
  init_team_service();
67623
68807
  init_token_ledger_service();
67624
68808
  init_token_ledger_watcher();
@@ -67630,6 +68814,9 @@ var init_src3 = __esm(() => {
67630
68814
  init_rule_check();
67631
68815
  init_builtins2();
67632
68816
  init_lifecycle_adapter();
68817
+ init_observability();
68818
+ init_steering();
68819
+ init_trace_writer();
67633
68820
  });
67634
68821
 
67635
68822
  // ../../node_modules/.bun/sisteransi@1.0.5/node_modules/sisteransi/src/index.js
@@ -67710,19 +68897,19 @@ var {
67710
68897
  init_src();
67711
68898
  init_loader();
67712
68899
 
67713
- // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.11+08507d81f591b81d/node_modules/@gobing-ai/ts-infra/dist/application-node.js
68900
+ // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.14+a32276a722fab3c8/node_modules/@gobing-ai/ts-infra/dist/application-node.js
67714
68901
  init_dist4();
67715
68902
  init_dist5();
67716
68903
  import { appendFileSync as appendFileSync4, mkdirSync as mkdirSync3, readFileSync as readFileSync5 } from "fs";
67717
68904
  import { dirname as dirname5 } from "path";
67718
68905
 
67719
- // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.11+08507d81f591b81d/node_modules/@gobing-ai/ts-infra/dist/application/index.js
68906
+ // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.14+a32276a722fab3c8/node_modules/@gobing-ai/ts-infra/dist/application/index.js
67720
68907
  init_default_observers();
67721
68908
  init_event_bus();
67722
68909
  init_file_observer();
67723
68910
  init_logger3();
67724
68911
 
67725
- // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.11+08507d81f591b81d/node_modules/@gobing-ai/ts-infra/dist/scheduler/noop.js
68912
+ // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.14+a32276a722fab3c8/node_modules/@gobing-ai/ts-infra/dist/scheduler/noop.js
67726
68913
  class NoopSchedulerAdapter {
67727
68914
  constructor() {}
67728
68915
  register(_cron, _action) {}
@@ -67730,7 +68917,7 @@ class NoopSchedulerAdapter {
67730
68917
  async stop() {}
67731
68918
  }
67732
68919
 
67733
- // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.11+08507d81f591b81d/node_modules/@gobing-ai/ts-infra/dist/scheduler/factory.js
68920
+ // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.14+a32276a722fab3c8/node_modules/@gobing-ai/ts-infra/dist/scheduler/factory.js
67734
68921
  function initScheduler(adapter, cronEntries) {
67735
68922
  const resolved = adapter ?? new NoopSchedulerAdapter;
67736
68923
  if (cronEntries) {
@@ -67741,7 +68928,7 @@ function initScheduler(adapter, cronEntries) {
67741
68928
  return resolved;
67742
68929
  }
67743
68930
 
67744
- // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.11+08507d81f591b81d/node_modules/@gobing-ai/ts-infra/dist/application/plugins/builtins.js
68931
+ // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.14+a32276a722fab3c8/node_modules/@gobing-ai/ts-infra/dist/application/plugins/builtins.js
67745
68932
  init_logger3();
67746
68933
  init_metrics();
67747
68934
  init_sdk();
@@ -67834,7 +69021,7 @@ function dbPlugin(db2) {
67834
69021
  };
67835
69022
  }
67836
69023
 
67837
- // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.11+08507d81f591b81d/node_modules/@gobing-ai/ts-infra/dist/application/plugins/host.js
69024
+ // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.14+a32276a722fab3c8/node_modules/@gobing-ai/ts-infra/dist/application/plugins/host.js
67838
69025
  init_logger3();
67839
69026
 
67840
69027
  class PluginHost {
@@ -67922,7 +69109,7 @@ class PluginHost {
67922
69109
  }
67923
69110
  }
67924
69111
 
67925
- // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.11+08507d81f591b81d/node_modules/@gobing-ai/ts-infra/dist/application/index.js
69112
+ // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.14+a32276a722fab3c8/node_modules/@gobing-ai/ts-infra/dist/application/index.js
67926
69113
  async function performShutdown(state, reason) {
67927
69114
  if (state.stopped)
67928
69115
  return;
@@ -68033,10 +69220,10 @@ async function runApplication(options) {
68033
69220
  }
68034
69221
  }
68035
69222
 
68036
- // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.11+08507d81f591b81d/node_modules/@gobing-ai/ts-infra/dist/application-node.js
69223
+ // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.14+a32276a722fab3c8/node_modules/@gobing-ai/ts-infra/dist/application-node.js
68037
69224
  init_scheduler_node();
68038
69225
 
68039
- // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.11+08507d81f591b81d/node_modules/@gobing-ai/ts-infra/dist/telemetry/otel-node.js
69226
+ // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.14+a32276a722fab3c8/node_modules/@gobing-ai/ts-infra/dist/telemetry/otel-node.js
68040
69227
  var import_api14 = __toESM(require_src(), 1);
68041
69228
 
68042
69229
  // ../../node_modules/.bun/@opentelemetry+exporter-metrics-otlp-http@0.218.0+e40b0dfdd726a224/node_modules/@opentelemetry/exporter-metrics-otlp-http/build/esm/OTLPMetricExporterBase.js
@@ -71052,7 +72239,7 @@ class OTLPTraceExporter extends import_otlp_exporter_base2.OTLPExporterBase {
71052
72239
  }), JsonTraceSerializer));
71053
72240
  }
71054
72241
  }
71055
- // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.11+08507d81f591b81d/node_modules/@gobing-ai/ts-infra/dist/telemetry/otel-node.js
72242
+ // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.14+a32276a722fab3c8/node_modules/@gobing-ai/ts-infra/dist/telemetry/otel-node.js
71056
72243
  var import_resources2 = __toESM(require_src4(), 1);
71057
72244
  var import_sdk_trace_node = __toESM(require_src8(), 1);
71058
72245
  var import_semantic_conventions = __toESM(require_src2(), 1);
@@ -71103,7 +72290,7 @@ async function shutdownNodeTelemetry() {
71103
72290
  await Promise.all(pending);
71104
72291
  }
71105
72292
 
71106
- // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.11+08507d81f591b81d/node_modules/@gobing-ai/ts-infra/dist/application-node.js
72293
+ // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.14+a32276a722fab3c8/node_modules/@gobing-ai/ts-infra/dist/application-node.js
71107
72294
  class ConfigValidationError extends Error {
71108
72295
  constructor(message) {
71109
72296
  super(message);
@@ -75194,7 +76381,7 @@ init_loader();
75194
76381
  init_src2();
75195
76382
  import { existsSync as existsSync8 } from "fs";
75196
76383
  import { homedir as homedir4 } from "os";
75197
- import { join as join14, resolve as resolve3 } from "path";
76384
+ import { join as join15, resolve as resolve3 } from "path";
75198
76385
 
75199
76386
  // src/workflow/resolve-spur-bin.ts
75200
76387
  import { basename as basename2 } from "path";
@@ -75212,22 +76399,22 @@ function resolveSpurBin() {
75212
76399
  }
75213
76400
 
75214
76401
  // src/workflow/make-lifecycle-adapter.ts
75215
- var GLOBAL_CONFIG_DIR = join14(".config", "spur");
76402
+ var GLOBAL_CONFIG_DIR = join15(".config", "spur");
75216
76403
  function globalConfigRoot(context4) {
75217
76404
  const override = context4.env.SPUR_GLOBAL_RULES_DIR;
75218
- return override !== undefined && override.length > 0 ? resolve3(context4.cwd, override) : join14(homedir4(), GLOBAL_CONFIG_DIR);
76405
+ return override !== undefined && override.length > 0 ? resolve3(context4.cwd, override) : join15(homedir4(), GLOBAL_CONFIG_DIR);
75219
76406
  }
75220
76407
  function resolveWorkflowPath(context4, profile) {
75221
76408
  const bundledRoot = bundledConfigRoot();
75222
76409
  if (bundledRoot !== null) {
75223
- const bundledPath = join14(bundledRoot, "workflows", `${profile.workflowName}.yaml`);
76410
+ const bundledPath = join15(bundledRoot, "workflows", `${profile.workflowName}.yaml`);
75224
76411
  if (existsSync8(bundledPath))
75225
76412
  return bundledPath;
75226
76413
  }
75227
- const projectPath = join14(context4.cwd, ".spur", "workflows", `${profile.workflowName}.yaml`);
76414
+ const projectPath = join15(context4.cwd, ".spur", "workflows", `${profile.workflowName}.yaml`);
75228
76415
  if (existsSync8(projectPath))
75229
76416
  return projectPath;
75230
- const globalPath = join14(globalConfigRoot(context4), "workflows", `${profile.workflowName}.yaml`);
76417
+ const globalPath = join15(globalConfigRoot(context4), "workflows", `${profile.workflowName}.yaml`);
75231
76418
  if (existsSync8(globalPath))
75232
76419
  return globalPath;
75233
76420
  return null;
@@ -75482,6 +76669,7 @@ function registerFeatureCommand(program2, context4) {
75482
76669
  strict,
75483
76670
  featuresDir,
75484
76671
  tasksDir,
76672
+ runDir: context4.fs.resolve(".spur/run"),
75485
76673
  severityOverrides: resolved.severityOverrides
75486
76674
  });
75487
76675
  results.push(result);
@@ -75554,7 +76742,13 @@ async function makeService(context4, folderOverride) {
75554
76742
  ...lifecycle ? { lifecycle } : {},
75555
76743
  emitter: makePlanningEmitter(context4)
75556
76744
  });
75557
- return new FeatureService({ fs: context4.fs, writeService, featuresDir, tasksDir });
76745
+ return new FeatureService({
76746
+ fs: context4.fs,
76747
+ writeService,
76748
+ featuresDir,
76749
+ tasksDir,
76750
+ foldersConfig: resolved.foldersConfig
76751
+ });
75558
76752
  }
75559
76753
  async function assertFeatureCheckPass(context4, id, folderOverride, strict) {
75560
76754
  const resolved = await resolvePlanningFolders(context4.fs);
@@ -75622,13 +76816,13 @@ init_loader();
75622
76816
  init_src2();
75623
76817
  init_dist10();
75624
76818
  import { homedir as homedir5 } from "os";
75625
- import { join as join15, resolve as resolve4 } from "path";
76819
+ import { join as join16, resolve as resolve4 } from "path";
75626
76820
 
75627
76821
  // src/config.ts
75628
76822
  var CLI_CONFIG = {
75629
76823
  binaryName: "spur",
75630
76824
  binaryLabel: "spur",
75631
- binaryVersion: "0.3.23",
76825
+ binaryVersion: "0.3.25",
75632
76826
  configDir: ".spur",
75633
76827
  configFile: ".spur/config.yaml",
75634
76828
  databaseFile: ".spur/spur.db"
@@ -75679,8 +76873,8 @@ var SCAFFOLD_MANIFEST = [
75679
76873
  ];
75680
76874
 
75681
76875
  // src/commands/init.ts
75682
- var GLOBAL_CONFIG_DIR2 = join15(".config", "spur");
75683
- var GLOBAL_RULES_DIR2 = join15(GLOBAL_CONFIG_DIR2, "rules");
76876
+ var GLOBAL_CONFIG_DIR2 = join16(".config", "spur");
76877
+ var GLOBAL_RULES_DIR2 = join16(GLOBAL_CONFIG_DIR2, "rules");
75684
76878
  var GLOBAL_CONFIG_EXAMPLE = "config.example.yaml";
75685
76879
  var GLOBAL_CONFIG_FILE2 = "config.yaml";
75686
76880
  var INDEXED_CONTEXT_MARKER = "## Indexed context";
@@ -75711,7 +76905,7 @@ If \`.spur/context/\` is absent, proceed normally. Never block work on its absen
75711
76905
  `;
75712
76906
  function globalRulesRoot(context4) {
75713
76907
  const override = context4.env.SPUR_GLOBAL_RULES_DIR;
75714
- return override !== undefined && override.length > 0 ? resolve4(context4.cwd, override) : join15(homedir5(), GLOBAL_RULES_DIR2);
76908
+ return override !== undefined && override.length > 0 ? resolve4(context4.cwd, override) : join16(homedir5(), GLOBAL_RULES_DIR2);
75715
76909
  }
75716
76910
  async function writeIfNew(context4, path9, content, force, result) {
75717
76911
  if (!force && await context4.fs.exists(path9)) {
@@ -75728,11 +76922,11 @@ async function seedGlobalRules(context4) {
75728
76922
  const target = globalRulesRoot(context4);
75729
76923
  let written = 0;
75730
76924
  for (const relPath of await listBundledRuleFiles()) {
75731
- const destination = join15(target, relPath);
76925
+ const destination = join16(target, relPath);
75732
76926
  if (await context4.fs.exists(destination))
75733
76927
  continue;
75734
- await context4.fs.ensureDir(join15(target, ...relPath.split("/").slice(0, -1)));
75735
- await context4.fs.writeFile(destination, await context4.fs.readFile(join15(source, relPath)));
76928
+ await context4.fs.ensureDir(join16(target, ...relPath.split("/").slice(0, -1)));
76929
+ await context4.fs.writeFile(destination, await context4.fs.readFile(join16(source, relPath)));
75736
76930
  written += 1;
75737
76931
  }
75738
76932
  return written;
@@ -75742,20 +76936,20 @@ async function seedGlobalConfig(context4) {
75742
76936
  if (source === null)
75743
76937
  return 0;
75744
76938
  const globalOverride = context4.env.SPUR_GLOBAL_RULES_DIR;
75745
- const target = globalOverride !== undefined && globalOverride.length > 0 ? resolve4(context4.cwd, globalOverride) : join15(homedir5(), GLOBAL_CONFIG_DIR2);
76939
+ const target = globalOverride !== undefined && globalOverride.length > 0 ? resolve4(context4.cwd, globalOverride) : join16(homedir5(), GLOBAL_CONFIG_DIR2);
75746
76940
  let written = 0;
75747
76941
  for (const relPath of listBundledConfigFiles()) {
75748
76942
  if (relPath === GLOBAL_CONFIG_EXAMPLE)
75749
76943
  continue;
75750
- const destination = join15(target, relPath);
76944
+ const destination = join16(target, relPath);
75751
76945
  if (await context4.fs.exists(destination))
75752
76946
  continue;
75753
- await context4.fs.ensureDir(join15(target, ...relPath.split("/").slice(0, -1)));
75754
- await context4.fs.writeFile(destination, await context4.fs.readFile(join15(source, relPath)));
76947
+ await context4.fs.ensureDir(join16(target, ...relPath.split("/").slice(0, -1)));
76948
+ await context4.fs.writeFile(destination, await context4.fs.readFile(join16(source, relPath)));
75755
76949
  written += 1;
75756
76950
  }
75757
- const examplePath = join15(source, GLOBAL_CONFIG_EXAMPLE);
75758
- const globalConfigPath = join15(target, GLOBAL_CONFIG_FILE2);
76951
+ const examplePath = join16(source, GLOBAL_CONFIG_EXAMPLE);
76952
+ const globalConfigPath = join16(target, GLOBAL_CONFIG_FILE2);
75759
76953
  if (await context4.fs.exists(examplePath) && !await context4.fs.exists(globalConfigPath)) {
75760
76954
  await context4.fs.ensureDir(target);
75761
76955
  await context4.fs.writeFile(globalConfigPath, await context4.fs.readFile(examplePath));
@@ -75769,7 +76963,7 @@ function registerInitCommand(program2, context4) {
75769
76963
  const force = options.force === true;
75770
76964
  const minimal = options.minimal === true;
75771
76965
  const projectName = options.name ?? "default";
75772
- const configPath = join15(context4.cwd, CLI_CONFIG.configFile);
76966
+ const configPath = join16(context4.cwd, CLI_CONFIG.configFile);
75773
76967
  if (!force && await context4.fs.exists(configPath)) {
75774
76968
  const message = `Already initialized: ${CLI_CONFIG.configFile}. Use --force to overwrite.`;
75775
76969
  context4.output.write(json3 ? toJson2({ ok: false, reason: "already-initialized", config: CLI_CONFIG.configFile }) : message);
@@ -75782,7 +76976,7 @@ function registerInitCommand(program2, context4) {
75782
76976
  `# Generated by \`spur init\``,
75783
76977
  "",
75784
76978
  '$schema: "@gobing-ai/spur/schemas/spur-config.schema.json"',
75785
- `version: "1"`,
76979
+ `version: "1.1"`,
75786
76980
  `name: ${projectName}`,
75787
76981
  "",
75788
76982
  "bootstrap:",
@@ -75804,13 +76998,13 @@ function registerInitCommand(program2, context4) {
75804
76998
  ].join(`
75805
76999
  `)}
75806
77000
  `;
75807
- await context4.fs.ensureDir(join15(context4.cwd, CLI_CONFIG.configDir));
77001
+ await context4.fs.ensureDir(join16(context4.cwd, CLI_CONFIG.configDir));
75808
77002
  await context4.fs.writeFile(configPath, configYaml);
75809
77003
  result.created.push(configPath);
75810
- const agentsDir = join15(context4.cwd, CLI_CONFIG.configDir, "agents");
77004
+ const agentsDir = join16(context4.cwd, CLI_CONFIG.configDir, "agents");
75811
77005
  await context4.fs.ensureDir(agentsDir);
75812
- await writeIfNew(context4, join15(agentsDir, ".gitkeep"), "", force, result);
75813
- const gitignorePath = join15(context4.cwd, ".gitignore");
77006
+ await writeIfNew(context4, join16(agentsDir, ".gitkeep"), "", force, result);
77007
+ const gitignorePath = join16(context4.cwd, ".gitignore");
75814
77008
  const contextEntry = ".spur/context/";
75815
77009
  if (await context4.fs.exists(gitignorePath)) {
75816
77010
  const existing = await context4.fs.readFile(gitignorePath);
@@ -75832,20 +77026,20 @@ ${contextEntry}
75832
77026
  for (const relPath of listBundledProjectSeedFiles()) {
75833
77027
  if (relPath.startsWith("templates/docs/"))
75834
77028
  continue;
75835
- const sourcePath = join15(configRoot, relPath);
77029
+ const sourcePath = join16(configRoot, relPath);
75836
77030
  if (!await context4.fs.exists(sourcePath))
75837
77031
  continue;
75838
- const targetPath = join15(context4.cwd, CLI_CONFIG.configDir, relPath);
75839
- await context4.fs.ensureDir(join15(targetPath, ".."));
77032
+ const targetPath = join16(context4.cwd, CLI_CONFIG.configDir, relPath);
77033
+ await context4.fs.ensureDir(join16(targetPath, ".."));
75840
77034
  await writeIfNew(context4, targetPath, await context4.fs.readFile(sourcePath), force, result);
75841
77035
  }
75842
77036
  for (const entry of SCAFFOLD_MANIFEST) {
75843
- const sourcePath = join15(configRoot, entry.source);
77037
+ const sourcePath = join16(configRoot, entry.source);
75844
77038
  if (!await context4.fs.exists(sourcePath))
75845
77039
  continue;
75846
- const baseDir = entry.root === true ? context4.cwd : join15(context4.cwd, CLI_CONFIG.configDir);
75847
- const targetPath = join15(baseDir, entry.target);
75848
- await context4.fs.ensureDir(join15(targetPath, ".."));
77040
+ const baseDir = entry.root === true ? context4.cwd : join16(context4.cwd, CLI_CONFIG.configDir);
77041
+ const targetPath = join16(baseDir, entry.target);
77042
+ await context4.fs.ensureDir(join16(targetPath, ".."));
75849
77043
  const entryForce = entry.preserve === true ? false : force;
75850
77044
  let body = await context4.fs.readFile(sourcePath);
75851
77045
  if (entry.target === "AGENTS.md") {
@@ -75858,7 +77052,7 @@ ${contextEntry}
75858
77052
  }
75859
77053
  }
75860
77054
  }
75861
- const agentsMdPath = join15(context4.cwd, "AGENTS.md");
77055
+ const agentsMdPath = join16(context4.cwd, "AGENTS.md");
75862
77056
  if (await context4.fs.exists(agentsMdPath)) {
75863
77057
  const existing = await context4.fs.readFile(agentsMdPath);
75864
77058
  if (!existing.includes(INDEXED_CONTEXT_MARKER)) {
@@ -76036,10 +77230,10 @@ function parseInterval2(raw) {
76036
77230
 
76037
77231
  // src/commands/migrate.ts
76038
77232
  init_src2();
76039
- import { join as join16 } from "path";
77233
+ import { join as join17 } from "path";
76040
77234
  function registerMigrateCommand(program2, context4) {
76041
77235
  program2.command("migrate").summary("apply CLI-owned schema migrations").option("--json", "Output machine-readable JSON").action(async (options) => {
76042
- const migrations = await loadSqlMigrations(join16(context4.cwd, "drizzle")).catch(() => {
77236
+ const migrations = await loadSqlMigrations(join17(context4.cwd, "drizzle")).catch(() => {
76043
77237
  return;
76044
77238
  });
76045
77239
  const applied = await applyCliMigrations(await context4.getDb(), migrations);
@@ -76291,7 +77485,7 @@ function formatTraceDetail(detail) {
76291
77485
 
76292
77486
  // src/commands/serve.ts
76293
77487
  init_src();
76294
- import { join as join20 } from "path";
77488
+ import { join as join21 } from "path";
76295
77489
 
76296
77490
  // ../server/src/index.ts
76297
77491
  init_src();
@@ -76301,11 +77495,11 @@ init_src3();
76301
77495
  init_src();
76302
77496
  init_loader();
76303
77497
  init_src2();
76304
- import { dirname as dirname13, isAbsolute as isAbsolute3, join as join19 } from "path";
77498
+ import { dirname as dirname14, isAbsolute as isAbsolute3, join as join20 } from "path";
76305
77499
  init_dist5();
76306
77500
 
76307
77501
  // ../server/src/bootstrap.ts
76308
- import { join as join17 } from "path";
77502
+ import { join as join18 } from "path";
76309
77503
 
76310
77504
  // ../../node_modules/.bun/radash@12.1.1/node_modules/radash/dist/esm/async.mjs
76311
77505
  var guard = (func, shouldGuard) => {
@@ -84945,7 +86139,7 @@ function createApp(appRt, opts) {
84945
86139
  app.use("*", async (c3, next) => {
84946
86140
  const pathname = c3.req.path === "/" ? "/index.html" : c3.req.path;
84947
86141
  try {
84948
- const file2 = Bun.file(join17(webDistPath, pathname));
86142
+ const file2 = Bun.file(join18(webDistPath, pathname));
84949
86143
  if (await file2.exists()) {
84950
86144
  const headers = new Headers({ "content-type": file2.type });
84951
86145
  return new Response(file2.stream(), { headers });
@@ -84958,7 +86152,7 @@ function createApp(appRt, opts) {
84958
86152
  return c3.json({ error: "Not Found" }, 404);
84959
86153
  }
84960
86154
  try {
84961
- const indexFile = Bun.file(join17(webDistPath, "index.html"));
86155
+ const indexFile = Bun.file(join18(webDistPath, "index.html"));
84962
86156
  if (await indexFile.exists()) {
84963
86157
  return new Response(indexFile.stream(), {
84964
86158
  headers: { "content-type": "text/html; charset=utf-8" }
@@ -84979,7 +86173,7 @@ init_src3();
84979
86173
  init_src();
84980
86174
  init_src2();
84981
86175
  init_dist5();
84982
- import { dirname as dirname12, join as join18 } from "path";
86176
+ import { dirname as dirname13, join as join19 } from "path";
84983
86177
  var NOOP_OUTPUT = { write: (_s) => {}, error: (_s) => {} };
84984
86178
 
84985
86179
  class LazyPlanningEventEmitter {
@@ -85014,7 +86208,7 @@ class NotConfiguredError extends Error {
85014
86208
  function createServerContext(appRt, options) {
85015
86209
  const cwd = options.cwd;
85016
86210
  const fs3 = options.fs;
85017
- const dbUrl = options.dbUrl ?? join18(cwd, DEFAULT_DATABASE_URL);
86211
+ const dbUrl = options.dbUrl ?? join19(cwd, DEFAULT_DATABASE_URL);
85018
86212
  const eventsBus = options.eventsBus ?? appRt.events;
85019
86213
  const jobQueueEnabled = options.jobQueueEnabled ?? false;
85020
86214
  const folders = options.folders ?? DEFAULT_PLANNING_FOLDERS;
@@ -85048,7 +86242,7 @@ function createServerContext(appRt, options) {
85048
86242
  if (!dbPromise) {
85049
86243
  const created = (async () => {
85050
86244
  if (dbUrl !== IN_MEMORY_DATABASE_URL) {
85051
- await fs3.ensureDir(dirname12(dbUrl));
86245
+ await fs3.ensureDir(dirname13(dbUrl));
85052
86246
  }
85053
86247
  return createMigratedDbViaRuntime({ url: dbUrl });
85054
86248
  })();
@@ -85085,6 +86279,7 @@ function createServerContext(appRt, options) {
85085
86279
  writeService: new PlanningWriteService({ fs: fs3, projectName: "spur", emitter: lazyEmitter }),
85086
86280
  featuresDir: folders.featuresDir,
85087
86281
  tasksDir: folders.tasksDir,
86282
+ foldersConfig: folders.foldersConfig,
85088
86283
  projectName: "spur"
85089
86284
  });
85090
86285
  }
@@ -85314,15 +86509,15 @@ async function runTaskActionJob(ctx, env, payload, createAgentService = createTa
85314
86509
  }
85315
86510
  }
85316
86511
  async function resolveWebDistPath(configuredPath) {
85317
- const candidates = configuredPath && configuredPath.trim() !== "" ? [isAbsolute3(configuredPath) ? configuredPath : join19(process.cwd(), configuredPath)] : [
85318
- join19(process.cwd(), "dist/web"),
85319
- join19(import.meta.dir, "web"),
85320
- join19(dirname13(process.execPath), "web"),
85321
- join19(dirname13(process.execPath), "../web"),
85322
- join19(import.meta.dir, "../../../dist/web")
86512
+ const candidates = configuredPath && configuredPath.trim() !== "" ? [isAbsolute3(configuredPath) ? configuredPath : join20(process.cwd(), configuredPath)] : [
86513
+ join20(process.cwd(), "dist/web"),
86514
+ join20(import.meta.dir, "web"),
86515
+ join20(dirname14(process.execPath), "web"),
86516
+ join20(dirname14(process.execPath), "../web"),
86517
+ join20(import.meta.dir, "../../../dist/web")
85323
86518
  ];
85324
86519
  for (const candidate of candidates) {
85325
- if (await Bun.file(join19(candidate, "index.html")).exists()) {
86520
+ if (await Bun.file(join20(candidate, "index.html")).exists()) {
85326
86521
  return candidate;
85327
86522
  }
85328
86523
  }
@@ -85338,7 +86533,7 @@ async function startServer(options, deps = defaultDeps) {
85338
86533
  async start(appRt) {
85339
86534
  const fs3 = deps.createNodeFileSystem(process.cwd());
85340
86535
  if (options.dbUrl && options.dbUrl !== IN_MEMORY_DATABASE_URL) {
85341
- await fs3.ensureDir(dirname13(options.dbUrl));
86536
+ await fs3.ensureDir(dirname14(options.dbUrl));
85342
86537
  }
85343
86538
  let scheduler3;
85344
86539
  if (bootConfig.scheduler.enabled) {
@@ -85452,7 +86647,7 @@ if (false) {}
85452
86647
 
85453
86648
  // src/commands/serve.ts
85454
86649
  function resolveServeDbUrl(cwd, env, configuredUrl) {
85455
- return env.DATABASE_URL === undefined ? join20(cwd, DEFAULT_DATABASE_URL) : configuredUrl;
86650
+ return env.DATABASE_URL === undefined ? join21(cwd, DEFAULT_DATABASE_URL) : configuredUrl;
85456
86651
  }
85457
86652
  function registerServeCommand(program2, context4) {
85458
86653
  program2.command("serve").summary("start the Spur web server (local fallback)").option("--port <n>", "Server port (env: PORT, default: 3000)", parseInt).option("--host <addr>", "Bind address (env: HOST, default: localhost)").option("--no-open", "Skip opening the browser").option("--cwd <path>", "Working directory", context4.cwd).option("--json", "Output { port, url, pid } and exit").action(async (options) => {
@@ -85491,7 +86686,7 @@ function registerServeCommand(program2, context4) {
85491
86686
  }
85492
86687
 
85493
86688
  // src/commands/status.ts
85494
- import { join as join21 } from "path";
86689
+ import { join as join22 } from "path";
85495
86690
 
85496
86691
  // src/errors.ts
85497
86692
  class CommandError extends Error {
@@ -85562,8 +86757,8 @@ function registerStatusCommand(program2, context4) {
85562
86757
  }
85563
86758
  async function runStatusCore(path9, options, context4) {
85564
86759
  const [packageJsonExists, spurConfigExists, git, agentSpecs] = await Promise.all([
85565
- context4.fs.exists(join21(context4.cwd, "package.json")),
85566
- context4.fs.exists(join21(context4.cwd, ".spur", "config.yaml")),
86760
+ context4.fs.exists(join22(context4.cwd, "package.json")),
86761
+ context4.fs.exists(join22(context4.cwd, ".spur", "config.yaml")),
85567
86762
  gitContext(context4.cwd),
85568
86763
  listAgentSpecIds(context4)
85569
86764
  ]);
@@ -85591,14 +86786,14 @@ async function runStatusCore(path9, options, context4) {
85591
86786
  return status.ok ? 0 : 1;
85592
86787
  }
85593
86788
  async function listAgentSpecIds(context4) {
85594
- const dir = join21(context4.cwd, ".spur", "agents");
86789
+ const dir = join22(context4.cwd, ".spur", "agents");
85595
86790
  if (!await context4.fs.exists(dir))
85596
86791
  return [];
85597
86792
  const entries = await context4.fs.readDir(dir);
85598
86793
  return entries.filter((entry) => entry.endsWith(".yaml") || entry.endsWith(".yml")).map((entry) => entry.replace(/\.ya?ml$/, "")).sort();
85599
86794
  }
85600
86795
  async function readTargetStatus(context4, targetPath) {
85601
- const resolved = join21(context4.cwd, targetPath);
86796
+ const resolved = join22(context4.cwd, targetPath);
85602
86797
  const stat2 = await context4.fs.stat(resolved);
85603
86798
  if (stat2 === null)
85604
86799
  throw new CommandError(`status failed: path does not exist at ${resolved}`);
@@ -85610,7 +86805,7 @@ init_src3();
85610
86805
  init_loader();
85611
86806
  init_src2();
85612
86807
  import { existsSync as existsSync9, readFileSync as readFileSync8 } from "fs";
85613
- import { join as join22 } from "path";
86808
+ import { join as join23 } from "path";
85614
86809
  // schemas/section-matrix.schema.json
85615
86810
  var section_matrix_schema_default = {
85616
86811
  $schema: "http://json-schema.org/draft-07/schema#",
@@ -85695,7 +86890,7 @@ var spur_config_schema_default = {
85695
86890
  },
85696
86891
  version: {
85697
86892
  type: "string",
85698
- description: "Config schema version. Currently '1'."
86893
+ description: "Config schema version (string, not integer). Current recommended: '1.1' (ADR-033 executor tiers + planning blocks). '1' remains accepted for backward compatibility."
85699
86894
  },
85700
86895
  name: {
85701
86896
  type: "string",
@@ -85812,15 +87007,15 @@ var spur_config_schema_default = {
85812
87007
  },
85813
87008
  tier: {
85814
87009
  type: "string",
85815
- enum: ["cheap", "standard", "capable"],
85816
- description: "Capability tier for stage-registry adaptive model routing (ADR-033). A stage starts on the cheapest eligible executor meeting its model_policy min_tier and escalates along the fallback chain on objective signals."
87010
+ enum: ["cheap", "standard", "capable-1", "capable-2", "capable-3"],
87011
+ description: "Capability tier for stage-registry adaptive model routing (ADR-033, 0343). Live values: cheap | standard | capable-1 | capable-2 | capable-3 (1=low output quality, 3=high within the capable band). A stage starts on the cheapest eligible executor meeting its model_policy min_tier. Bare legacy `capable` is accepted at runtime (zod preprocess \u2192 capable-1) during the deprecation window but is not part of this editor enum."
85817
87012
  }
85818
87013
  }
85819
87014
  }
85820
87015
  },
85821
87016
  "default-by-phase": {
85822
87017
  type: "object",
85823
- description: "Map of dev phase (e.g. dev-run, dev-review) to executor selector. A configured phase mapping is authoritative \u2014 a broken mapping fails fast and does NOT fall back to `default`.",
87018
+ description: "DEPRECATED (ADR-033). Backward-compat map of dev phase (e.g. dev-run, dev-review) to executor selector. Prefer executor `tier` + stage-registry model_policy. When present, a matching phase mapping is still authoritative (fail-fast; no fall back to `default`) and shadows stage routing for that phase.",
85824
87019
  additionalProperties: {
85825
87020
  type: "string"
85826
87021
  }
@@ -86201,7 +87396,7 @@ function renderMigrationReport(report, dryRun, corpusDir) {
86201
87396
  }
86202
87397
  function registerTaskCommand(program2, context4) {
86203
87398
  const task = program2.command("task").summary("manage tasks");
86204
- task.command("create").summary("Create a new task with race-safe WBS allocation.").argument("<title>", "Task title").option("--feature <id>", "Feature ID for traceability and Goal\u2192Background derivation").option("--parent <wbs>", "Parent WBS for sub-task grouping").option("--template <variant>", `Template variant (${TASK_VARIANTS.join("|")})`).option("--folder <path>", "Custom tasks folder").option("--json", "Output machine-readable JSON").action(async (title2, options) => {
87399
+ task.command("create").summary("Create a new task with race-safe WBS allocation.").argument("<title>", "Task title").option("--feature <id>", "Feature ID for traceability and Goal\u2192Background derivation").option("--parent <wbs>", "Parent WBS for sub-task grouping").option("--template <variant>", `Template variant (${TASK_VARIANTS.join("|")})`).option("--folder <path>", "Custom tasks folder").option("--dedupe-within <seconds>", "Refuse creation if an existing task under the same feature has an identical name created within the last N seconds (task 0341 R4)", (v) => Number.parseInt(v, 10)).option("--allow-duplicate-name", "Override the --dedupe-within guard (creates anyway)").option("--json", "Output machine-readable JSON").action(async (title2, options) => {
86205
87400
  if (options.template !== undefined && !TASK_VARIANTS.includes(options.template)) {
86206
87401
  context4.output.error(`Unknown template variant "${options.template}". Valid: ${TASK_VARIANTS.join(", ")}`);
86207
87402
  context4.setExitCode(2);
@@ -86213,7 +87408,8 @@ function registerTaskCommand(program2, context4) {
86213
87408
  title: title2,
86214
87409
  featureId: options.feature,
86215
87410
  parentWbs: options.parent,
86216
- template: options.template
87411
+ template: options.template,
87412
+ dedupeWithinSec: options.allowDuplicateName ? undefined : options.dedupeWithin
86217
87413
  });
86218
87414
  if (options.json) {
86219
87415
  context4.output.write(toJson2(result));
@@ -86221,8 +87417,13 @@ function registerTaskCommand(program2, context4) {
86221
87417
  context4.output.write(`Created task ${result.ref.id}: ${result.ref.filePath}`);
86222
87418
  }
86223
87419
  } catch (err) {
86224
- context4.output.error(String(err));
86225
- context4.setExitCode(1);
87420
+ if (err instanceof DuplicateFollowUpError) {
87421
+ context4.output.error(err.message);
87422
+ context4.setExitCode(3);
87423
+ } else {
87424
+ context4.output.error(String(err));
87425
+ context4.setExitCode(1);
87426
+ }
86226
87427
  }
86227
87428
  });
86228
87429
  task.command("show").summary("Show a task by WBS.").argument("<wbs>", "Task WBS number").option("--folder <path>", "Custom tasks folder").option("--json", "Output machine-readable JSON").action(async (wbs, options) => {
@@ -86288,7 +87489,7 @@ ${result.content}`);
86288
87489
  let forcedDoneVerdict;
86289
87490
  if (status === "done") {
86290
87491
  const current = await svc.show(wbs);
86291
- const verdictDir = options.verdictDir ?? join22(context4.cwd, ".spur", "run");
87492
+ const verdictDir = options.verdictDir ?? join23(context4.cwd, ".spur", "run");
86292
87493
  const loaded = await readVerdictArtifact(context4.fs, verdictDir, wbs);
86293
87494
  const guardOutcome = evaluateDoneTransition({
86294
87495
  wbs,
@@ -86312,10 +87513,10 @@ ${result.content}`);
86312
87513
  context4.setExitCode(1);
86313
87514
  return;
86314
87515
  }
86315
- if (guardOutcome.reason === "forced" && loaded.artifact !== undefined) {
87516
+ if (guardOutcome.reason === "forced") {
86316
87517
  forcedDone = true;
86317
87518
  forcedDoneReason = options.reason;
86318
- forcedDoneVerdict = loaded.artifact.verdict;
87519
+ forcedDoneVerdict = loaded.artifact?.verdict ?? "UNKNOWN";
86319
87520
  }
86320
87521
  }
86321
87522
  const result = await svc.updateStatus(wbs, status);
@@ -86605,6 +87806,53 @@ ${result.content}`);
86605
87806
  context4.setExitCode(1);
86606
87807
  }
86607
87808
  });
87809
+ task.command("verifyall-aggregate").summary("Aggregate per-task verify outcomes into a deterministic batch verdict (verifyall).").description("Reads a JSON array of per-task outcomes ({wbs,outcome}) and emits the batch " + "verdict with NOT-STARTED tasks excluded from the rollup. Replaces agent-discretion " + "rollup prose (dev-operations.md \xA73a) with deterministic code (task 0341).").option("--from-file <path>", "Path to JSON array of {wbs,outcome[,reason]} rows").option("--json", "Output machine-readable JSON").action(async (options) => {
87810
+ const inputPath = options.fromFile ?? ".spur/run/verifyall-batch-input.json";
87811
+ let raw2;
87812
+ try {
87813
+ raw2 = readFileSync8(inputPath, "utf-8");
87814
+ } catch {
87815
+ context4.output.error(`Batch input file not found: ${inputPath}`);
87816
+ context4.setExitCode(1);
87817
+ return;
87818
+ }
87819
+ let rows;
87820
+ try {
87821
+ const parsed = JSON.parse(raw2);
87822
+ if (!Array.isArray(parsed)) {
87823
+ throw new Error("expected a JSON array");
87824
+ }
87825
+ rows = parsed;
87826
+ } catch (err) {
87827
+ context4.output.error(`Invalid batch input JSON: ${String(err)}`);
87828
+ context4.setExitCode(1);
87829
+ return;
87830
+ }
87831
+ const results = rows.map((r2) => {
87832
+ const outcome = r2.outcome.toUpperCase();
87833
+ if (!["PASS", "PARTIAL", "FAIL", "NOT-STARTED", "UNKNOWN"].includes(outcome)) {
87834
+ throw new Error(`Invalid outcome for ${r2.wbs}: ${r2.outcome}`);
87835
+ }
87836
+ return {
87837
+ wbs: r2.wbs,
87838
+ outcome,
87839
+ reason: r2.reason
87840
+ };
87841
+ });
87842
+ const aggregation = aggregateBatchVerdicts(results);
87843
+ if (options.json) {
87844
+ context4.output.write(JSON.stringify(aggregation, null, 2));
87845
+ } else {
87846
+ context4.output.write(`Batch verdict: ${aggregation.verdict}`);
87847
+ context4.output.write(aggregation.summary);
87848
+ if (aggregation.notStarted.length > 0) {
87849
+ context4.output.write(`NOT-STARTED (excluded from rollup): ${aggregation.notStarted.map((r2) => r2.wbs).join(", ")}`);
87850
+ }
87851
+ }
87852
+ if (aggregation.verdict === "FAIL") {
87853
+ context4.setExitCode(1);
87854
+ }
87855
+ });
86608
87856
  task.command("check").summary("Validate a task file through the four-layer check (design \xA73).").argument("[wbs]", "Task WBS number (validates all tasks in the folder when omitted)").option("--strict", "Elevate ALL warnings to failures").option("--strict-core", "Gate variant: fail only on hard-core errors (the testing\u2192done guard)").option("--folder <path>", "Custom tasks folder").option("--json", "Output machine-readable JSON").action(async (wbs, options) => {
86609
87857
  const svc = await makeCheckService(context4);
86610
87858
  const json3 = options.json === true;
@@ -86782,7 +88030,7 @@ function loadTemplateContent(projectRoot, variant) {
86782
88030
  return templateContentCache.get(variant);
86783
88031
  if (templateMissSet.has(variant))
86784
88032
  return;
86785
- const localPath = join22(projectRoot, ".spur", "tasks", "templates", `${variant}.md`);
88033
+ const localPath = join23(projectRoot, ".spur", "tasks", "templates", `${variant}.md`);
86786
88034
  if (existsSync9(localPath)) {
86787
88035
  const content = readFileSync8(localPath, "utf8");
86788
88036
  templateContentCache.set(variant, content);
@@ -86790,7 +88038,7 @@ function loadTemplateContent(projectRoot, variant) {
86790
88038
  }
86791
88039
  const root = bundledConfigRoot();
86792
88040
  if (root !== null) {
86793
- const templatePath = join22(root, "templates", "task", `${variant}.md`);
88041
+ const templatePath = join23(root, "templates", "task", `${variant}.md`);
86794
88042
  if (existsSync9(templatePath)) {
86795
88043
  const content = readFileSync8(templatePath, "utf8");
86796
88044
  templateContentCache.set(variant, content);
@@ -86806,13 +88054,13 @@ function loadTemplateBodies(projectRoot, variant) {
86806
88054
  if (cached2 !== undefined)
86807
88055
  return cached2;
86808
88056
  let bodies = {};
86809
- const localPath = join22(projectRoot, ".spur", "tasks", "templates", `${variant}.md`);
88057
+ const localPath = join23(projectRoot, ".spur", "tasks", "templates", `${variant}.md`);
86810
88058
  if (existsSync9(localPath)) {
86811
88059
  bodies = extractTemplateBodies(readFileSync8(localPath, "utf8"));
86812
88060
  } else {
86813
88061
  const root = bundledConfigRoot();
86814
88062
  if (root !== null) {
86815
- const templatePath = join22(root, "templates", "task", `${variant}.md`);
88063
+ const templatePath = join23(root, "templates", "task", `${variant}.md`);
86816
88064
  if (existsSync9(templatePath)) {
86817
88065
  bodies = extractTemplateBodies(readFileSync8(templatePath, "utf8"));
86818
88066
  }
@@ -86854,7 +88102,7 @@ async function loadSectionMatrix(projectRoot) {
86854
88102
  return promise2;
86855
88103
  }
86856
88104
  async function loadSectionMatrixUncached(projectRoot) {
86857
- const localPath = join22(projectRoot, ".spur", "tasks", "section-matrix.yaml");
88105
+ const localPath = join23(projectRoot, ".spur", "tasks", "section-matrix.yaml");
86858
88106
  if (existsSync9(localPath)) {
86859
88107
  const data = await loadStructuredSpurConfig(localPath, {
86860
88108
  validateJsonSchema: true,
@@ -86864,7 +88112,7 @@ async function loadSectionMatrixUncached(projectRoot) {
86864
88112
  }
86865
88113
  const root = bundledConfigRoot();
86866
88114
  if (root !== null) {
86867
- const matrixPath = join22(root, "tasks", "section-matrix.yaml");
88115
+ const matrixPath = join23(root, "tasks", "section-matrix.yaml");
86868
88116
  if (existsSync9(matrixPath)) {
86869
88117
  const data = await loadStructuredSpurConfig(matrixPath, {
86870
88118
  validateJsonSchema: true,
@@ -87124,6 +88372,8 @@ init_dist7();
87124
88372
  init_dist6();
87125
88373
  import { spawn as spawn2 } from "child_process";
87126
88374
  import { resolve as resolve5 } from "path";
88375
+ import { createInterface } from "readline";
88376
+ import { setTimeout as sleep3 } from "timers/promises";
87127
88377
  function parseVars(raw2) {
87128
88378
  if (raw2 === undefined) {
87129
88379
  return;
@@ -87146,6 +88396,11 @@ function parseVars(raw2) {
87146
88396
  }
87147
88397
  return vars;
87148
88398
  }
88399
+ function submitSteeringLine(controller, output2, line) {
88400
+ const ack = controller.submitLine(line);
88401
+ if (ack === undefined)
88402
+ output2.error(`[steer] ignored command: ${line}`);
88403
+ }
87149
88404
  async function resolveWorkflowPaths(cwd) {
87150
88405
  try {
87151
88406
  const config4 = await loadSpurConfig(cwd, { embeddedSchemas: EMBEDDED_SPUR_SCHEMAS });
@@ -87178,19 +88433,47 @@ ${result.errors.map((m) => ` - ${m}`).join(`
87178
88433
  }
87179
88434
  context4.setExitCode(result.valid ? 0 : 1);
87180
88435
  });
87181
- workflow.command("run").description("Execute a workflow definition.").argument("<file>", "Workflow YAML file").option("--run-id <id>", "Persisted run id for workflow run").option("--vars <json>", `Per-run variable overrides as a JSON object, e.g. '{"taskId":"0042"}'`).option("--dry-run", "Validate and walk transitions without executing actions").option("--async", "Start the workflow in the background and exit immediately \u2014 monitor with `spur workflow trace <run-id>`").option("--no-plan", "Suppress the run-start plan preview (synchronous runs only)").option("--json", "Output machine-readable JSON where supported").action(async (file2, options) => {
88436
+ workflow.command("run").description("Execute a workflow definition.").argument("<file>", "Workflow YAML file").option("--run-id <id>", "Persisted run id for workflow run").option("--vars <json>", `Per-run variable overrides as a JSON object, e.g. '{"taskId":"0042"}'`).option("--dry-run", "Validate and walk transitions without executing actions").option("--async", "Start the workflow in the background and exit immediately \u2014 monitor with `spur workflow trace <run-id>`").option("--no-plan", "Suppress the run-start plan preview (synchronous runs only)").option("--quiet", "Suppress plan and per-step progress; keep the final summary").option("--silent", "Suppress all routine output; errors still set a non-zero exit status").option("--verbose", "Include transitions and correlation diagnostics in human progress").option("--detail <level>", "Human detail level: minimal, invocation, or full").option("--trace-file", "Append a redacted schema-versioned JSONL trace under .spur/runs/workflow/").option("--steer", "Accept local in-process steering commands on stdin at declared action boundaries").option("--json", "Output machine-readable JSON where supported").action(async (file2, options) => {
88437
+ const json3 = options.json === true;
88438
+ const silent = !json3 && options.silent === true;
88439
+ const quiet = !json3 && options.quiet === true;
88440
+ if (!json3 && options.quiet === true && options.verbose === true) {
88441
+ context4.output.error("--quiet and --verbose are mutually exclusive");
88442
+ context4.setExitCode(2);
88443
+ return;
88444
+ }
88445
+ if (!json3 && options.silent === true && (options.quiet === true || options.verbose === true)) {
88446
+ context4.output.error("--silent cannot be combined with --quiet or --verbose");
88447
+ context4.setExitCode(2);
88448
+ return;
88449
+ }
88450
+ if (options.steer === true && (json3 || options.async === true)) {
88451
+ context4.output.error("--steer is synchronous and in-process; it cannot be combined with --json or --async");
88452
+ context4.setExitCode(2);
88453
+ return;
88454
+ }
88455
+ const requestedDetail = options.detail;
88456
+ if (requestedDetail !== undefined && !["minimal", "invocation", "full"].includes(requestedDetail)) {
88457
+ context4.output.error("--detail must be one of: minimal, invocation, full");
88458
+ context4.setExitCode(2);
88459
+ return;
88460
+ }
88461
+ const detail = options.verbose === true ? "full" : requestedDetail ?? "invocation";
87182
88462
  if (options.async) {
87183
- const runId = options.runId || crypto.randomUUID();
88463
+ const runId2 = options.runId || crypto.randomUUID();
87184
88464
  const spurParts = resolveSpurBin().split(" ");
87185
88465
  const spurBin = spurParts[0] ?? process.execPath;
87186
88466
  const spurArgs = spurParts.slice(1);
87187
- const cmd = [...spurArgs, "workflow", "run", file2, "--run-id", runId];
88467
+ const cmd = [...spurArgs, "workflow", "run", file2, "--run-id", runId2];
87188
88468
  if (options.vars) {
87189
88469
  cmd.push("--vars", options.vars);
87190
88470
  }
87191
88471
  if (options.dryRun) {
87192
88472
  cmd.push("--dry-run");
87193
88473
  }
88474
+ if (options.traceFile) {
88475
+ cmd.push("--trace-file");
88476
+ }
87194
88477
  try {
87195
88478
  const child = spawn2(spurBin, cmd, {
87196
88479
  stdio: "ignore",
@@ -87200,50 +88483,121 @@ ${result.errors.map((m) => ` - ${m}`).join(`
87200
88483
  child.unref();
87201
88484
  } catch {
87202
88485
  const result2 = await makeSvc(options.json).run(file2, {
87203
- runId,
88486
+ runId: runId2,
87204
88487
  vars: { spurBin: resolveSpurBin(), ...parseVars(options.vars) },
87205
88488
  dryRun: options.dryRun || undefined
87206
88489
  });
87207
- context4.output.write(options.json ? toJson2(result2) : `workflow ${result2.status}: ${result2.workflowName} -> ${result2.finalState} (async spawn failed, ran sync)`);
88490
+ if (json3)
88491
+ context4.output.write(toJson2(result2));
88492
+ else if (!silent) {
88493
+ context4.output.write(`workflow ${result2.status}: ${result2.workflowName} -> ${result2.finalState} (async spawn failed, ran sync)`);
88494
+ }
87208
88495
  context4.setExitCode(result2.status === "done" ? 0 : 1);
87209
88496
  return;
87210
88497
  }
87211
- const asyncResult = { runId, status: "started", workflowName: file2 };
87212
- context4.output.write(options.json ? toJson2(asyncResult) : `Started async run: ${runId}
87213
- Monitor with: spur workflow trace ${runId}`);
88498
+ const asyncResult = { runId: runId2, status: "started", workflowName: file2 };
88499
+ if (json3)
88500
+ context4.output.write(toJson2(asyncResult));
88501
+ else if (!silent) {
88502
+ context4.output.write(`Started async run: ${runId2}
88503
+ Monitor with: spur workflow trace ${runId2} --follow`);
88504
+ }
87214
88505
  context4.setExitCode(0);
87215
88506
  return;
87216
88507
  }
87217
88508
  const vars = { spurBin: resolveSpurBin(), ...parseVars(options.vars) };
87218
- const human = options.json !== true;
88509
+ const runId = options.runId || crypto.randomUUID();
88510
+ const humanProgress = !json3 && !quiet && !silent;
87219
88511
  let bus;
87220
- if (human) {
88512
+ let traceWriter;
88513
+ const heartbeats = new Map;
88514
+ if (humanProgress || options.traceFile === true || options.steer === true) {
88515
+ bus = new EventBus;
88516
+ }
88517
+ if (options.traceFile === true && bus !== undefined) {
88518
+ traceWriter = new WorkflowTraceWriter(context4.cwd, runId);
88519
+ traceWriter.attach(bus);
88520
+ }
88521
+ if (humanProgress && bus !== undefined) {
88522
+ context4.output.write(`Run: ${runId}`);
87221
88523
  if (options.plan !== false) {
87222
88524
  try {
87223
88525
  const def = await loadWorkflowDef(resolve5(context4.cwd, file2), { validateSchema: false });
87224
88526
  context4.output.write(renderRunPlan(def));
87225
88527
  } catch {}
87226
88528
  }
87227
- bus = new EventBus;
87228
88529
  const report = (event2) => {
87229
- const line = renderStepLine(event2);
88530
+ const line = renderStepLine(event2, { detail });
87230
88531
  if (line !== null)
87231
88532
  context4.output.write(line);
87232
88533
  };
87233
88534
  bus.on("workflow.phase", report);
87234
- bus.on("workflow.action.started", report);
87235
- bus.on("workflow.action.finished", report);
88535
+ if (detail === "full")
88536
+ bus.on("workflow.transition", report);
88537
+ bus.on("workflow.action.started", (event2) => {
88538
+ report(event2);
88539
+ const startedAt2 = Date.parse(event2.at);
88540
+ const timer = setInterval(() => {
88541
+ const line = renderActionHeartbeat(event2, Math.max(0, Date.now() - startedAt2), { detail });
88542
+ if (line !== null)
88543
+ context4.output.write(line);
88544
+ }, 30000);
88545
+ timer.unref?.();
88546
+ heartbeats.set(event2.actionId, timer);
88547
+ });
88548
+ bus.on("workflow.action.finished", (event2) => {
88549
+ const timer = heartbeats.get(event2.actionId);
88550
+ if (timer !== undefined)
88551
+ clearInterval(timer);
88552
+ heartbeats.delete(event2.actionId);
88553
+ report(event2);
88554
+ });
88555
+ bus.on("workflow.agent", (event2) => {
88556
+ if (event2.kind === "started" && event2.actionId !== undefined) {
88557
+ const timer = heartbeats.get(event2.actionId);
88558
+ if (timer !== undefined)
88559
+ clearInterval(timer);
88560
+ heartbeats.delete(event2.actionId);
88561
+ }
88562
+ report(event2);
88563
+ });
88564
+ }
88565
+ const steeringController = options.steer === true ? new WorkflowSteeringController((ack) => {
88566
+ bus?.emit("workflow.steering", ack);
88567
+ context4.output.write(`[steer] ${ack.accepted ? "ack" : "nack"} ${ack.operation} \xB7 ${ack.reason ?? `version=${ack.version}`}`);
88568
+ }, configuredSecretValues(context4.env), new Set(["operator"]), (snapshot) => {
88569
+ if (snapshot.state === "boundary") {
88570
+ context4.output.write(`[steer] boundary action=${snapshot.actionId} version=${snapshot.version} \xB7 commands: continue | note <text> | retry | abort`);
88571
+ }
88572
+ }) : undefined;
88573
+ const steeringInput = steeringController === undefined ? undefined : createInterface({ input: process.stdin, terminal: process.stdin.isTTY === true });
88574
+ if (steeringInput !== undefined && steeringController !== undefined) {
88575
+ steeringInput.on("line", submitSteeringLine.bind(undefined, steeringController, context4.output));
88576
+ }
88577
+ let result;
88578
+ try {
88579
+ result = await makeSvc(json3, bus).run(file2, {
88580
+ runId,
88581
+ vars,
88582
+ dryRun: options.dryRun || undefined,
88583
+ recordSelfPid: process.env.SPUR_ASYNC_WORKER === "1",
88584
+ ...steeringController !== undefined ? { steeringController } : {}
88585
+ });
88586
+ } finally {
88587
+ for (const timer of heartbeats.values())
88588
+ clearInterval(timer);
88589
+ heartbeats.clear();
88590
+ await traceWriter?.flush();
88591
+ steeringInput?.close();
88592
+ }
88593
+ if (json3)
88594
+ context4.output.write(toJson2(result));
88595
+ else if (!silent) {
88596
+ context4.output.write(`workflow ${result.status}: ${result.workflowName} -> ${result.finalState}${typeof result.reason === "string" ? ` \u2014 ${result.reason}` : ""}`);
87236
88597
  }
87237
- const result = await makeSvc(options.json, bus).run(file2, {
87238
- runId: options.runId || undefined,
87239
- vars,
87240
- dryRun: options.dryRun || undefined,
87241
- recordSelfPid: process.env.SPUR_ASYNC_WORKER === "1"
87242
- });
87243
- context4.output.write(options.json ? toJson2(result) : `workflow ${result.status}: ${result.workflowName} -> ${result.finalState}`);
87244
88598
  context4.setExitCode(result.status === "done" ? 0 : 1);
87245
88599
  });
87246
- workflow.command("continue").description("Resume a paused (HITL) workflow run. Omit run-id to resume the most recent paused run.").argument("[run-id]", "Run ID to resume (default: the most recent paused run)").option("--yes", "Resume without prompting for confirmation").option("--json", "Output machine-readable JSON where supported").action(async (runId, options) => {
88600
+ workflow.command("continue").description("Resume a paused (HITL) workflow run. Omit run-id to resume the most recent paused run.").argument("[run-id]", "Run ID to resume (default: the most recent paused run)").option("--yes", "Skip the CLI resume confirmation (does not set the persisted HITL answer)").option("--json", "Output machine-readable JSON where supported").action(async (runId, options) => {
87247
88601
  const json3 = options.json === true;
87248
88602
  const svc = makeSvc(json3);
87249
88603
  try {
@@ -87329,7 +88683,7 @@ Monitor with: spur workflow trace ${runId}`);
87329
88683
  context4.output.write(formatListHuman(result));
87330
88684
  }
87331
88685
  });
87332
- workflow.command("trace").description("Show persisted workflow run history.").argument("[run-id]", "Run ID for per-run timeline detail").option("--workflow <name>", "Filter by workflow name").option("--status <status>", "Filter by status: done, failed, running").option("--since <iso-date>", "Filter runs started on or after this date").option("--last <n>", "Limit results (default 20)", "20").option("--json", "Output machine-readable JSON where supported").action(async (runId, options) => {
88686
+ workflow.command("trace").description("Show persisted workflow run history.").argument("[run-id]", "Run ID for per-run timeline detail").option("--workflow <name>", "Filter by workflow name").option("--status <status>", "Filter by status: done, failed, running").option("--since <iso-date>", "Filter runs started on or after this date").option("--last <n>", "Limit results (default 20)", "20").option("--follow", "Replay a run timeline and poll persisted state until it becomes terminal").option("--poll <ms>", "Follow polling interval in milliseconds", "1000").option("--json", "Output machine-readable JSON where supported").action(async (runId, options) => {
87333
88687
  const svc = makeSvc();
87334
88688
  const last = parseInt(options.last, 10);
87335
88689
  if (Number.isNaN(last) || last < 1) {
@@ -87337,11 +88691,31 @@ Monitor with: spur workflow trace ${runId}`);
87337
88691
  context4.setExitCode(1);
87338
88692
  return;
87339
88693
  }
88694
+ const pollMs = parseInt(options.poll, 10);
88695
+ if (Number.isNaN(pollMs) || pollMs < 50) {
88696
+ context4.output.error("--poll must be an integer of at least 50ms");
88697
+ context4.setExitCode(1);
88698
+ return;
88699
+ }
88700
+ if (options.follow === true && runId === undefined) {
88701
+ context4.output.error("--follow requires a run-id");
88702
+ context4.setExitCode(1);
88703
+ return;
88704
+ }
88705
+ if (options.follow === true && options.json === true) {
88706
+ context4.output.error("--follow is a human streaming mode and cannot be combined with --json");
88707
+ context4.setExitCode(1);
88708
+ return;
88709
+ }
87340
88710
  if (options.status !== undefined && !["done", "failed", "running"].includes(options.status)) {
87341
88711
  context4.output.error("--status must be one of: done, failed, running");
87342
88712
  context4.setExitCode(1);
87343
88713
  return;
87344
88714
  }
88715
+ if (options.follow === true && runId !== undefined) {
88716
+ await followTrace(svc, runId, pollMs, (line) => context4.output.write(line));
88717
+ return;
88718
+ }
87345
88719
  const result = runId ? await svc.trace(runId) : await svc.trace({
87346
88720
  workflow: options.workflow,
87347
88721
  status: options.status,
@@ -87404,22 +88778,14 @@ function formatTraceList2(result) {
87404
88778
  function formatTraceTimeline(result) {
87405
88779
  const { run, events: events2 } = result;
87406
88780
  const dryLabel = run.isDryRun ? " [DRY RUN]" : "";
88781
+ const reasonLabel = run.failureReason ? ` \u2014 ${run.failureReason}` : "";
87407
88782
  const lines = [
87408
- `Run: ${run.runId} \u2014 ${run.workflowName} (${run.mode}) \u2014 ${run.status}${dryLabel}`,
88783
+ `Run: ${run.runId} \u2014 ${run.workflowName} (${run.mode}) \u2014 ${run.status}${dryLabel}${reasonLabel}`,
87409
88784
  `Started: ${run.startedAt} Completed: ${run.completedAt ?? "-"} Events: ${events2.length}`,
87410
88785
  ""
87411
88786
  ];
87412
88787
  for (const event2 of events2) {
87413
- if (event2.kind === "phase") {
87414
- const ts = event2.startedAt ?? event2.completedAt ?? "";
87415
- lines.push(` ${event2.phase.padEnd(20)} ${event2.status.padEnd(10)} ${ts}`);
87416
- } else if (event2.kind === "transition") {
87417
- const guard2 = event2.trigger ? ` [${event2.trigger}]` : "";
87418
- lines.push(` \u2192 ${event2.to}${guard2}`);
87419
- } else {
87420
- const costSuffix = formatActionCost(event2);
87421
- lines.push(` \u26A1 ${event2.actionKind.padEnd(15)} ${event2.duration.padEnd(6)}${event2.label}${costSuffix}`);
87422
- }
88788
+ lines.push(formatTimelineEvent(event2));
87423
88789
  }
87424
88790
  const hasUnjoinedCost = events2.some((e) => e.kind === "action" && e.cost !== undefined && e.cost.totals.records === 0);
87425
88791
  if (hasUnjoinedCost) {
@@ -87428,6 +88794,56 @@ function formatTraceTimeline(result) {
87428
88794
  return lines.join(`
87429
88795
  `).trimEnd();
87430
88796
  }
88797
+ function formatTimelineEvent(event2) {
88798
+ if (event2.kind === "phase") {
88799
+ const ts = event2.startedAt ?? event2.completedAt ?? "";
88800
+ return ` ${event2.phase.padEnd(20)} ${event2.status.padEnd(10)} ${ts}`;
88801
+ }
88802
+ if (event2.kind === "transition") {
88803
+ const guard2 = event2.trigger ? ` [${event2.trigger}]` : "";
88804
+ return ` \u2192 ${event2.to}${guard2}`;
88805
+ }
88806
+ const costSuffix = formatActionCost(event2);
88807
+ return ` \u26A1 ${event2.actionKind.padEnd(15)} ${event2.duration.padEnd(6)}${event2.label}${costSuffix}`;
88808
+ }
88809
+ async function followTrace(service, runId, pollMs, write, wait = (ms) => sleep3(ms)) {
88810
+ let missingAttempts = 0;
88811
+ let timeline;
88812
+ while (true) {
88813
+ try {
88814
+ timeline = await service.trace(runId);
88815
+ break;
88816
+ } catch (error51) {
88817
+ if (!String(error51).includes("Run not found") || missingAttempts >= 20)
88818
+ throw error51;
88819
+ missingAttempts++;
88820
+ await wait(pollMs);
88821
+ }
88822
+ }
88823
+ write(formatTraceTimeline(timeline));
88824
+ const seen = new Set(timeline.events.map((event2) => JSON.stringify(event2)));
88825
+ if (isTerminalTraceStatus(timeline.run.status))
88826
+ return;
88827
+ while (true) {
88828
+ await wait(pollMs);
88829
+ const next = await service.trace(runId);
88830
+ for (const event2 of next.events) {
88831
+ const fingerprint2 = JSON.stringify(event2);
88832
+ if (seen.has(fingerprint2))
88833
+ continue;
88834
+ seen.add(fingerprint2);
88835
+ write(formatTimelineEvent(event2));
88836
+ }
88837
+ if (isTerminalTraceStatus(next.run.status)) {
88838
+ const reason = next.run.failureReason ? ` \u2014 ${next.run.failureReason}` : "";
88839
+ write(`Run finalized: ${next.run.status}${reason}`);
88840
+ return;
88841
+ }
88842
+ }
88843
+ }
88844
+ function isTerminalTraceStatus(status) {
88845
+ return status !== "running" && status !== "pending";
88846
+ }
87431
88847
  function formatActionCost(event2) {
87432
88848
  if (event2.kind !== "action")
87433
88849
  return "";
@@ -87448,7 +88864,7 @@ init_src3();
87448
88864
  init_src();
87449
88865
  init_src2();
87450
88866
  init_dist5();
87451
- import { dirname as dirname14, join as join23, resolve as resolve6 } from "path";
88867
+ import { dirname as dirname15, join as join24, resolve as resolve6 } from "path";
87452
88868
  import { isatty as isatty4 } from "tty";
87453
88869
 
87454
88870
  // ../../node_modules/.bun/@clack+core@1.4.1/node_modules/@clack/core/dist/index.mjs
@@ -88862,10 +90278,10 @@ function createCliContext(options) {
88862
90278
  function noopSetExitCode(_code) {}
88863
90279
  async function createMigratedDbAdapter(cwd = process.cwd(), env = process.env, dbUrl) {
88864
90280
  const config4 = buildConfigFromEnv(env);
88865
- const configuredUrl = env.DATABASE_URL === undefined ? join23(cwd, DEFAULT_DATABASE_URL) : config4.database.url;
90281
+ const configuredUrl = env.DATABASE_URL === undefined ? join24(cwd, DEFAULT_DATABASE_URL) : config4.database.url;
88866
90282
  const url2 = dbUrl ?? configuredUrl;
88867
90283
  if (url2 !== IN_MEMORY_DATABASE_URL) {
88868
- await createNodeFileSystem3().ensureDir(dirname14(url2));
90284
+ await createNodeFileSystem3().ensureDir(dirname15(url2));
88869
90285
  }
88870
90286
  return createMigratedDb({ url: url2 });
88871
90287
  }
@@ -88950,7 +90366,7 @@ function bannerText() {
88950
90366
  }
88951
90367
  async function runCli() {
88952
90368
  const argv = process.argv.slice(2);
88953
- if (!argv.includes("--json")) {
90369
+ if (!argv.some((arg) => arg === "--json" || arg === "--quiet" || arg === "--silent")) {
88954
90370
  consoleOutput.write(bannerText());
88955
90371
  }
88956
90372
  return main();