@gobing-ai/spur 0.3.30 → 0.3.32

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 (68) hide show
  1. package/config/rules/strict/runtime-boundaries.yaml +2 -1
  2. package/config/workflows/basic.yaml +92 -14
  3. package/config/workflows/docs-pipeline.yaml +71 -25
  4. package/config/workflows/feature-dev.yaml +104 -35
  5. package/config/workflows/feature-lifecycle.yaml +10 -2
  6. package/config/workflows/idea-pipeline.yaml +118 -54
  7. package/config/workflows/planning-pipeline.yaml +78 -30
  8. package/config/workflows/task-lifecycle.yaml +10 -2
  9. package/config/workflows/task-pipeline.yaml +219 -56
  10. package/config/workflows/wayfinder-resolution.yaml +64 -27
  11. package/config/workflows/wrapup-pipeline.yaml +79 -39
  12. package/package.json +9 -9
  13. package/schemas/state-machine-workflow.schema.json +5 -0
  14. package/spur.js +1302 -707
  15. package/web/_astro/BoardApp.CBVEjvSW.js +1 -0
  16. package/web/_astro/{BoardApp.BwAdAgK9.js → BoardApp.CyyIiK76.js} +74 -74
  17. package/web/_astro/TaskDetail.BfUuRsfM.js +1 -0
  18. package/web/_astro/{arc.CO-I_PZe.js → arc.C7fql4LL.js} +1 -1
  19. package/web/_astro/{architectureDiagram-3BPJPVTR.Si-A1Ncl.js → architectureDiagram-3BPJPVTR.IJPsRX-V.js} +1 -1
  20. package/web/_astro/{blockDiagram-GPEHLZMM.CeNy_ta6.js → blockDiagram-GPEHLZMM.B1AUwBxq.js} +1 -1
  21. package/web/_astro/{c4Diagram-AAUBKEIU.BgN_scEU.js → c4Diagram-AAUBKEIU.BuSJPR3z.js} +1 -1
  22. package/web/_astro/channel.D3uZSASb.js +1 -0
  23. package/web/_astro/{chunk-2J33WTMH.C3oDtmdi.js → chunk-2J33WTMH.CaOPTr9l.js} +1 -1
  24. package/web/_astro/{chunk-4BX2VUAB.Bv8Fxh4H.js → chunk-4BX2VUAB.DQzxExn8.js} +1 -1
  25. package/web/_astro/{chunk-55IACEB6.BT-TqtV6.js → chunk-55IACEB6.IIb103c7.js} +1 -1
  26. package/web/_astro/{chunk-727SXJPM.DkGMsI0B.js → chunk-727SXJPM.DOYz1K1b.js} +1 -1
  27. package/web/_astro/{chunk-AQP2D5EJ.B_-SqGnF.js → chunk-AQP2D5EJ.CFcfW-wt.js} +1 -1
  28. package/web/_astro/{chunk-FMBD7UC4.DwR3skMv.js → chunk-FMBD7UC4.C7Hn0jo3.js} +1 -1
  29. package/web/_astro/{chunk-ND2GUHAM.BaTQutFl.js → chunk-ND2GUHAM.Ba83VQ1m.js} +1 -1
  30. package/web/_astro/{chunk-QZHKN3VN.BJMSqqJp.js → chunk-QZHKN3VN.CizNbIIP.js} +1 -1
  31. package/web/_astro/{classDiagram-4FO5ZUOK.SaqCNUPF.js → classDiagram-4FO5ZUOK.2NYYPXHq.js} +1 -1
  32. package/web/_astro/{classDiagram-v2-Q7XG4LA2.SaqCNUPF.js → classDiagram-v2-Q7XG4LA2.2NYYPXHq.js} +1 -1
  33. package/web/_astro/{cose-bilkent-S5V4N54A.BkmjA_E2.js → cose-bilkent-S5V4N54A.DwBqVzml.js} +1 -1
  34. package/web/_astro/{dagre-BM42HDAG.CNUvWhrX.js → dagre-BM42HDAG.DqnYsHWG.js} +1 -1
  35. package/web/_astro/{diagram-2AECGRRQ.C68Cg9uE.js → diagram-2AECGRRQ.D8TKhgy-.js} +1 -1
  36. package/web/_astro/{diagram-5GNKFQAL.DbHkPLEn.js → diagram-5GNKFQAL.BUD6Prj5.js} +1 -1
  37. package/web/_astro/{diagram-KO2AKTUF.Dt0EdErs.js → diagram-KO2AKTUF.Cvg7hmND.js} +1 -1
  38. package/web/_astro/{diagram-LMA3HP47.Bixs6mnm.js → diagram-LMA3HP47.FUG_B9eS.js} +1 -1
  39. package/web/_astro/{diagram-OG6HWLK6.B4KBI6KT.js → diagram-OG6HWLK6.XsBZQ0hQ.js} +1 -1
  40. package/web/_astro/{erDiagram-TEJ5UH35.C_4i9Xvx.js → erDiagram-TEJ5UH35.M_LSGgLQ.js} +1 -1
  41. package/web/_astro/{flowDiagram-I6XJVG4X.CfGFczAY.js → flowDiagram-I6XJVG4X.DZikZBRa.js} +1 -1
  42. package/web/_astro/{ganttDiagram-6RSMTGT7.C2AIvtmg.js → ganttDiagram-6RSMTGT7.DTKuEmXY.js} +1 -1
  43. package/web/_astro/{gitGraphDiagram-PVQCEYII.HlXuGqQg.js → gitGraphDiagram-PVQCEYII.Dt-E9XmY.js} +1 -1
  44. package/web/_astro/index.yAse9IaO.css +1 -0
  45. package/web/_astro/{infoDiagram-5YYISTIA.BWQJsJ4Z.js → infoDiagram-5YYISTIA.D5snevU1.js} +1 -1
  46. package/web/_astro/{ishikawaDiagram-YF4QCWOH.CCHgIHik.js → ishikawaDiagram-YF4QCWOH.DmXdBf9W.js} +1 -1
  47. package/web/_astro/{journeyDiagram-JHISSGLW.D9M6YwOJ.js → journeyDiagram-JHISSGLW.HPJSRkkU.js} +1 -1
  48. package/web/_astro/{kanban-definition-UN3LZRKU.K20D-a1_.js → kanban-definition-UN3LZRKU.BxCqCnL1.js} +1 -1
  49. package/web/_astro/{linear.DeP5Qsol.js → linear.BnXQUpS-.js} +1 -1
  50. package/web/_astro/{mermaid.core.ClzxNMPU.js → mermaid.core.D-ZeOmpg.js} +4 -4
  51. package/web/_astro/{mindmap-definition-RKZ34NQL.CGn9RUNj.js → mindmap-definition-RKZ34NQL.DxrftiFR.js} +1 -1
  52. package/web/_astro/{pieDiagram-4H26LBE5.CoA92Csb.js → pieDiagram-4H26LBE5.D-ZcWxwg.js} +1 -1
  53. package/web/_astro/{quadrantDiagram-W4KKPZXB.CJ_UPjM1.js → quadrantDiagram-W4KKPZXB.C-dVJ7GW.js} +1 -1
  54. package/web/_astro/{requirementDiagram-4Y6WPE33.KkEPNNUU.js → requirementDiagram-4Y6WPE33.lalE_R8X.js} +1 -1
  55. package/web/_astro/{sankeyDiagram-5OEKKPKP.CoCWbQgh.js → sankeyDiagram-5OEKKPKP.C4uPynAF.js} +1 -1
  56. package/web/_astro/{sequenceDiagram-3UESZ5HK.DUFpV3EP.js → sequenceDiagram-3UESZ5HK.uBOenkqh.js} +1 -1
  57. package/web/_astro/{stateDiagram-AJRCARHV.CgWX0_9m.js → stateDiagram-AJRCARHV.CQOusSP2.js} +1 -1
  58. package/web/_astro/{stateDiagram-v2-BHNVJYJU.BKUXeGSf.js → stateDiagram-v2-BHNVJYJU.Bw0ZdjYb.js} +1 -1
  59. package/web/_astro/{timeline-definition-PNZ67QCA.CXayAguY.js → timeline-definition-PNZ67QCA.DbkPA5ce.js} +1 -1
  60. package/web/_astro/{vennDiagram-CIIHVFJN.DPak_Elh.js → vennDiagram-CIIHVFJN.CIKhzwoA.js} +1 -1
  61. package/web/_astro/{wardley-L42UT6IY.t3ZlDKd0.js → wardley-L42UT6IY.0P-tqaFs.js} +1 -1
  62. package/web/_astro/{wardleyDiagram-YWT4CUSO.CwPSVASQ.js → wardleyDiagram-YWT4CUSO.DrwTvM_M.js} +1 -1
  63. package/web/_astro/{xychartDiagram-2RQKCTM6.CJfrQgpF.js → xychartDiagram-2RQKCTM6.BgJsQVms.js} +1 -1
  64. package/web/index.html +2 -2
  65. package/web/_astro/BoardApp.Bk3T0o6s.js +0 -1
  66. package/web/_astro/TaskDetail.L36slEqE.js +0 -1
  67. package/web/_astro/channel.CYdGnFHM.js +0 -1
  68. package/web/_astro/index.D5Wqk2Tw.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.16/node_modules/@gobing-ai/ts-utils/dist/errors.js
50
+ // ../../node_modules/.bun/@gobing-ai+ts-utils@0.4.18/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.16/node_modules/@gobing-ai/ts-utils/dist/api-response.js
61
+ // ../../node_modules/.bun/@gobing-ai+ts-utils@0.4.18/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.16/node_modules/@gobing-ai/ts-utils/dist/cursor.js
84
+ // ../../node_modules/.bun/@gobing-ai+ts-utils@0.4.18/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.16/node_modules/@gobing-ai/ts-utils/dist/object.js
87
+ // ../../node_modules/.bun/@gobing-ai+ts-utils@0.4.18/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
  }
@@ -102,7 +102,7 @@ function deepMerge(target, source) {
102
102
  return result;
103
103
  }
104
104
 
105
- // ../../node_modules/.bun/@gobing-ai+ts-utils@0.4.16/node_modules/@gobing-ai/ts-utils/dist/output.js
105
+ // ../../node_modules/.bun/@gobing-ai+ts-utils@0.4.18/node_modules/@gobing-ai/ts-utils/dist/output.js
106
106
  function processStream(name) {
107
107
  const proc = globalThis.process;
108
108
  const stream = proc?.[name];
@@ -123,7 +123,7 @@ function echoError(message, target = defaultStderrTarget ?? processStream("stder
123
123
  }
124
124
  var defaultStdoutTarget, defaultStderrTarget;
125
125
 
126
- // ../../node_modules/.bun/@gobing-ai+ts-utils@0.4.16/node_modules/@gobing-ai/ts-utils/dist/index.js
126
+ // ../../node_modules/.bun/@gobing-ai+ts-utils@0.4.18/node_modules/@gobing-ai/ts-utils/dist/index.js
127
127
  var init_dist = __esm(() => {
128
128
  init_api_response();
129
129
  init_cursor();
@@ -21745,7 +21745,7 @@ var init_zod = __esm(() => {
21745
21745
  init_external();
21746
21746
  });
21747
21747
 
21748
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.16+a1f7d493e005a6ca/node_modules/@gobing-ai/ts-runtime/dist/config.js
21748
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.18+3ba223a3e166c92f/node_modules/@gobing-ai/ts-runtime/dist/config.js
21749
21749
  function getProcessEnv() {
21750
21750
  return process.env;
21751
21751
  }
@@ -21823,7 +21823,7 @@ var init_config = __esm(() => {
21823
21823
  };
21824
21824
  });
21825
21825
 
21826
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.16+a1f7d493e005a6ca/node_modules/@gobing-ai/ts-runtime/dist/file-system-node.js
21826
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.18+3ba223a3e166c92f/node_modules/@gobing-ai/ts-runtime/dist/file-system-node.js
21827
21827
  import { appendFileSync, cpSync, createReadStream, createWriteStream, existsSync, mkdirSync, readdirSync, readFileSync, realpathSync, renameSync, rmSync, statSync, writeFileSync } from "fs";
21828
21828
  import { dirname, resolve as resolvePath } from "path";
21829
21829
  function createNodeFileSystem(root, paths) {
@@ -21910,7 +21910,7 @@ function findProjectRoot(startDir) {
21910
21910
  }
21911
21911
  var init_file_system_node = () => {};
21912
21912
 
21913
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.16+a1f7d493e005a6ca/node_modules/@gobing-ai/ts-runtime/dist/db-errors.js
21913
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.18+3ba223a3e166c92f/node_modules/@gobing-ai/ts-runtime/dist/db-errors.js
21914
21914
  var DbModuleNotInstalledError;
21915
21915
  var init_db_errors = __esm(() => {
21916
21916
  DbModuleNotInstalledError = class DbModuleNotInstalledError extends Error {
@@ -29101,7 +29101,7 @@ var init_execa = __esm(() => {
29101
29101
  } = getIpcExport());
29102
29102
  });
29103
29103
 
29104
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.16+a1f7d493e005a6ca/node_modules/@gobing-ai/ts-runtime/dist/process-executor.js
29104
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.18+3ba223a3e166c92f/node_modules/@gobing-ai/ts-runtime/dist/process-executor.js
29105
29105
  import { isatty } from "tty";
29106
29106
 
29107
29107
  class NodeProcessExecutor {
@@ -29144,6 +29144,14 @@ class NodeProcessExecutor {
29144
29144
  });
29145
29145
  try {
29146
29146
  const subprocess = execa(options.command, args, execaOptions);
29147
+ if (subprocess.pid !== undefined) {
29148
+ this.config.registry?.update(registryId, { pid: subprocess.pid });
29149
+ if (options.onSpawn !== undefined) {
29150
+ try {
29151
+ options.onSpawn(subprocess.pid);
29152
+ } catch {}
29153
+ }
29154
+ }
29147
29155
  const stopGroupCancellation = observeProcessGroupCancellation(subprocess, options.signal);
29148
29156
  observeOutput(subprocess.stdout, "stdout", options.onOutput);
29149
29157
  observeOutput(subprocess.stderr, "stderr", options.onOutput);
@@ -29431,7 +29439,7 @@ var init_process_executor = __esm(() => {
29431
29439
  init_execa();
29432
29440
  });
29433
29441
 
29434
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.16+a1f7d493e005a6ca/node_modules/@gobing-ai/ts-runtime/dist/runtime-node-bun.js
29442
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.18+3ba223a3e166c92f/node_modules/@gobing-ai/ts-runtime/dist/runtime-node-bun.js
29435
29443
  function getNodeFileSystem() {
29436
29444
  if (!_nodeFileSystem)
29437
29445
  _nodeFileSystem = createNodeFileSystem();
@@ -29491,7 +29499,7 @@ var init_runtime_node_bun = __esm(() => {
29491
29499
  };
29492
29500
  });
29493
29501
 
29494
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.16+a1f7d493e005a6ca/node_modules/@gobing-ai/ts-runtime/dist/context.js
29502
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.18+3ba223a3e166c92f/node_modules/@gobing-ai/ts-runtime/dist/context.js
29495
29503
  class RuntimeContext {
29496
29504
  scope;
29497
29505
  runtimeName;
@@ -29561,7 +29569,7 @@ var init_context = __esm(() => {
29561
29569
  init_runtime_node_bun();
29562
29570
  });
29563
29571
 
29564
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.16+a1f7d493e005a6ca/node_modules/@gobing-ai/ts-runtime/dist/path.js
29572
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.18+3ba223a3e166c92f/node_modules/@gobing-ai/ts-runtime/dist/path.js
29565
29573
  function normalizeSeparators(path6) {
29566
29574
  return path6.replaceAll("\\", "/");
29567
29575
  }
@@ -29622,17 +29630,17 @@ var init_path = __esm(() => {
29622
29630
  SEP = globalThis.process?.platform === "win32" ? "\\" : "/";
29623
29631
  });
29624
29632
 
29625
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.16+a1f7d493e005a6ca/node_modules/@gobing-ai/ts-runtime/dist/schema-validation.js
29633
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.18+3ba223a3e166c92f/node_modules/@gobing-ai/ts-runtime/dist/schema-validation.js
29626
29634
  var REMOTE_SCHEMA_MAX_BYTES;
29627
29635
  var init_schema_validation = __esm(() => {
29628
29636
  init_dist2();
29629
29637
  REMOTE_SCHEMA_MAX_BYTES = 5 * 1024 * 1024;
29630
29638
  });
29631
29639
 
29632
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.16+a1f7d493e005a6ca/node_modules/@gobing-ai/ts-runtime/dist/types.js
29640
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.18+3ba223a3e166c92f/node_modules/@gobing-ai/ts-runtime/dist/types.js
29633
29641
  var init_types = () => {};
29634
29642
 
29635
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.16+a1f7d493e005a6ca/node_modules/@gobing-ai/ts-runtime/dist/index.js
29643
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.18+3ba223a3e166c92f/node_modules/@gobing-ai/ts-runtime/dist/index.js
29636
29644
  var init_dist3 = __esm(() => {
29637
29645
  init_config();
29638
29646
  init_context();
@@ -29641,7 +29649,7 @@ var init_dist3 = __esm(() => {
29641
29649
  init_types();
29642
29650
  });
29643
29651
 
29644
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.16+a1f7d493e005a6ca/node_modules/@gobing-ai/ts-runtime/dist/bun-sqlite.js
29652
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.18+3ba223a3e166c92f/node_modules/@gobing-ai/ts-runtime/dist/bun-sqlite.js
29645
29653
  import { Database } from "bun:sqlite";
29646
29654
  var init_bun_sqlite = () => {};
29647
29655
 
@@ -33833,7 +33841,7 @@ var init_bun_sqlite2 = __esm(() => {
33833
33841
  init_session2();
33834
33842
  });
33835
33843
 
33836
- // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.16+fec36642bac873ee/node_modules/@gobing-ai/ts-db/dist/schema/inbox-messages.js
33844
+ // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.18+c31add66cbc22fb3/node_modules/@gobing-ai/ts-db/dist/schema/inbox-messages.js
33837
33845
  var inboxMessages;
33838
33846
  var init_inbox_messages = __esm(() => {
33839
33847
  init_sqlite_core();
@@ -33852,7 +33860,7 @@ var init_inbox_messages = __esm(() => {
33852
33860
  }, (table2) => [index("idx_inbox_messages_to_status").on(table2.toId, table2.status)]);
33853
33861
  });
33854
33862
 
33855
- // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.16+fec36642bac873ee/node_modules/@gobing-ai/ts-db/dist/schema/common.js
33863
+ // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.18+c31add66cbc22fb3/node_modules/@gobing-ai/ts-db/dist/schema/common.js
33856
33864
  function nowTimestamp() {
33857
33865
  return Date.now();
33858
33866
  }
@@ -33881,7 +33889,7 @@ var init_common3 = __esm(() => {
33881
33889
  appendOnlyColumns = buildAppendOnlyColumns();
33882
33890
  });
33883
33891
 
33884
- // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.16+fec36642bac873ee/node_modules/@gobing-ai/ts-db/dist/schema/queue-jobs.js
33892
+ // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.18+c31add66cbc22fb3/node_modules/@gobing-ai/ts-db/dist/schema/queue-jobs.js
33885
33893
  var queueJobs;
33886
33894
  var init_queue_jobs = __esm(() => {
33887
33895
  init_sqlite_core();
@@ -33901,7 +33909,7 @@ var init_queue_jobs = __esm(() => {
33901
33909
  }, (table2) => [index("queue_jobs_ready_idx").on(table2.status, table2.nextRetryAt, table2.createdAt)]);
33902
33910
  });
33903
33911
 
33904
- // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.16+fec36642bac873ee/node_modules/@gobing-ai/ts-db/dist/schema/runtime.js
33912
+ // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.18+c31add66cbc22fb3/node_modules/@gobing-ai/ts-db/dist/schema/runtime.js
33905
33913
  var exports_runtime = {};
33906
33914
  __export(exports_runtime, {
33907
33915
  queueJobs: () => queueJobs,
@@ -33912,7 +33920,7 @@ var init_runtime = __esm(() => {
33912
33920
  init_queue_jobs();
33913
33921
  });
33914
33922
 
33915
- // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.16+fec36642bac873ee/node_modules/@gobing-ai/ts-db/dist/adapters/bun-sqlite.js
33923
+ // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.18+c31add66cbc22fb3/node_modules/@gobing-ai/ts-db/dist/adapters/bun-sqlite.js
33916
33924
  var exports_bun_sqlite = {};
33917
33925
  __export(exports_bun_sqlite, {
33918
33926
  BunSqliteAdapter: () => BunSqliteAdapter
@@ -34203,7 +34211,7 @@ var init_d1 = __esm(() => {
34203
34211
  init_session3();
34204
34212
  });
34205
34213
 
34206
- // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.16+fec36642bac873ee/node_modules/@gobing-ai/ts-db/dist/adapters/d1.js
34214
+ // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.18+c31add66cbc22fb3/node_modules/@gobing-ai/ts-db/dist/adapters/d1.js
34207
34215
  var exports_d1 = {};
34208
34216
  __export(exports_d1, {
34209
34217
  D1Adapter: () => D1Adapter
@@ -34267,7 +34275,7 @@ var init_d12 = __esm(() => {
34267
34275
  init_runtime();
34268
34276
  });
34269
34277
 
34270
- // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.16+fec36642bac873ee/node_modules/@gobing-ai/ts-db/dist/adapter.js
34278
+ // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.18+c31add66cbc22fb3/node_modules/@gobing-ai/ts-db/dist/adapter.js
34271
34279
  async function createDbAdapter(config3) {
34272
34280
  switch (config3.driver) {
34273
34281
  case "bun-sqlite": {
@@ -34307,7 +34315,7 @@ var init_drizzle_orm = __esm(() => {
34307
34315
  init_view_common();
34308
34316
  });
34309
34317
 
34310
- // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.16+fec36642bac873ee/node_modules/@gobing-ai/ts-db/dist/query-spec.js
34318
+ // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.18+c31add66cbc22fb3/node_modules/@gobing-ai/ts-db/dist/query-spec.js
34311
34319
  function compilePredicate(predicate) {
34312
34320
  if ("and" in predicate) {
34313
34321
  const parts = predicate.and.map(compilePredicate).filter((p) => p !== undefined);
@@ -34345,7 +34353,7 @@ var init_query_spec = __esm(() => {
34345
34353
  };
34346
34354
  });
34347
34355
 
34348
- // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.16+fec36642bac873ee/node_modules/@gobing-ai/ts-db/dist/base-dao.js
34356
+ // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.18+c31add66cbc22fb3/node_modules/@gobing-ai/ts-db/dist/base-dao.js
34349
34357
  function asSelectQuery(query) {
34350
34358
  return query;
34351
34359
  }
@@ -34387,7 +34395,7 @@ var init_base_dao = __esm(() => {
34387
34395
  init_query_spec();
34388
34396
  });
34389
34397
 
34390
- // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.16+fec36642bac873ee/node_modules/@gobing-ai/ts-db/dist/embedded-migrations.js
34398
+ // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.18+c31add66cbc22fb3/node_modules/@gobing-ai/ts-db/dist/embedded-migrations.js
34391
34399
  var embeddedMigrations;
34392
34400
  var init_embedded_migrations = __esm(() => {
34393
34401
  embeddedMigrations = [
@@ -34419,7 +34427,7 @@ var init_embedded_migrations = __esm(() => {
34419
34427
  ];
34420
34428
  });
34421
34429
 
34422
- // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.16+fec36642bac873ee/node_modules/@gobing-ai/ts-db/dist/entity-dao.js
34430
+ // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.18+c31add66cbc22fb3/node_modules/@gobing-ai/ts-db/dist/entity-dao.js
34423
34431
  var EntityDao;
34424
34432
  var init_entity_dao = __esm(() => {
34425
34433
  init_drizzle_orm();
@@ -34581,7 +34589,7 @@ var init_entity_dao = __esm(() => {
34581
34589
  };
34582
34590
  });
34583
34591
 
34584
- // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.16+fec36642bac873ee/node_modules/@gobing-ai/ts-db/dist/inbox-message-dao.js
34592
+ // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.18+c31add66cbc22fb3/node_modules/@gobing-ai/ts-db/dist/inbox-message-dao.js
34585
34593
  function isPromiseLike(value) {
34586
34594
  return value !== null && (typeof value === "object" || typeof value === "function") && typeof value.then === "function";
34587
34595
  }
@@ -34754,7 +34762,7 @@ var init_migrator2 = __esm(() => {
34754
34762
  init_migrator();
34755
34763
  });
34756
34764
 
34757
- // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.16+fec36642bac873ee/node_modules/@gobing-ai/ts-db/dist/migrate.js
34765
+ // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.18+c31add66cbc22fb3/node_modules/@gobing-ai/ts-db/dist/migrate.js
34758
34766
  async function ensureJournalTable(adapter, table3) {
34759
34767
  await adapter.exec(`CREATE TABLE IF NOT EXISTS "${table3}" (` + "id INTEGER PRIMARY KEY AUTOINCREMENT, " + "hash text NOT NULL, " + "created_at numeric" + ")");
34760
34768
  }
@@ -34829,7 +34837,7 @@ var init_migrate = __esm(() => {
34829
34837
  init_embedded_migrations();
34830
34838
  });
34831
34839
 
34832
- // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.16+fec36642bac873ee/node_modules/@gobing-ai/ts-db/dist/queue-job-dao.js
34840
+ // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.18+c31add66cbc22fb3/node_modules/@gobing-ai/ts-db/dist/queue-job-dao.js
34833
34841
  var QueueJobDao;
34834
34842
  var init_queue_job_dao = __esm(() => {
34835
34843
  init_drizzle_orm();
@@ -34970,7 +34978,7 @@ var init_queue_job_dao = __esm(() => {
34970
34978
  };
34971
34979
  });
34972
34980
 
34973
- // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.16+fec36642bac873ee/node_modules/@gobing-ai/ts-db/dist/index.js
34981
+ // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.18+c31add66cbc22fb3/node_modules/@gobing-ai/ts-db/dist/index.js
34974
34982
  var exports_dist = {};
34975
34983
  __export(exports_dist, {
34976
34984
  embeddedMigrations: () => embeddedMigrations,
@@ -37275,7 +37283,7 @@ function buildConfigFromEnv(env = process.env) {
37275
37283
  }
37276
37284
  });
37277
37285
  }
37278
- 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, AgentOutputConfigSchema, AgentConfigSchema, RulesConfigSchema, WorkflowsConfigSchema, RedactionConfigSchema, spurConfigSchema, configSchema2;
37286
+ 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, AgentOutputConfigSchema, AgentConfigSchema, RulesConfigSchema, WorkflowsConfigSchema, WorkflowConfigSchema, RedactionConfigSchema, spurConfigSchema, configSchema2;
37279
37287
  var init_src = __esm(() => {
37280
37288
  init_zod();
37281
37289
  init_finding_codes();
@@ -37419,6 +37427,9 @@ var init_src = __esm(() => {
37419
37427
  WorkflowsConfigSchema = exports_external.object({
37420
37428
  paths: exports_external.array(exports_external.string()).optional()
37421
37429
  });
37430
+ WorkflowConfigSchema = exports_external.object({
37431
+ logRetentionDays: exports_external.number().int().positive().default(30)
37432
+ });
37422
37433
  RedactionConfigSchema = exports_external.object({
37423
37434
  enabled: exports_external.boolean().optional()
37424
37435
  });
@@ -37428,6 +37439,7 @@ var init_src = __esm(() => {
37428
37439
  agent: AgentConfigSchema.optional(),
37429
37440
  rules: RulesConfigSchema.optional(),
37430
37441
  workflows: WorkflowsConfigSchema.optional(),
37442
+ workflow: WorkflowConfigSchema.optional(),
37431
37443
  redaction: RedactionConfigSchema.optional(),
37432
37444
  tasks: tasksConfigSchema.optional(),
37433
37445
  features: featuresConfigSchema.optional()
@@ -37452,7 +37464,7 @@ var init_src = __esm(() => {
37452
37464
  });
37453
37465
  });
37454
37466
 
37455
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.16+d8250d61be64a872/node_modules/@gobing-ai/ts-runtime/dist/config.js
37467
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.18+0c3519262b4dbb18/node_modules/@gobing-ai/ts-runtime/dist/config.js
37456
37468
  function parseYamlObject(text3) {
37457
37469
  let parsed;
37458
37470
  try {
@@ -37579,7 +37591,7 @@ var init_config2 = __esm(() => {
37579
37591
  };
37580
37592
  });
37581
37593
 
37582
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.16+d8250d61be64a872/node_modules/@gobing-ai/ts-runtime/dist/file-system-node.js
37594
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.18+0c3519262b4dbb18/node_modules/@gobing-ai/ts-runtime/dist/file-system-node.js
37583
37595
  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";
37584
37596
  import { dirname as dirname2, resolve as resolvePath3 } from "path";
37585
37597
  function createNodeFileSystem3(root, paths) {
@@ -37666,7 +37678,7 @@ function findProjectRoot3(startDir) {
37666
37678
  }
37667
37679
  var init_file_system_node2 = () => {};
37668
37680
 
37669
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.16+d8250d61be64a872/node_modules/@gobing-ai/ts-runtime/dist/db-errors.js
37681
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.18+0c3519262b4dbb18/node_modules/@gobing-ai/ts-runtime/dist/db-errors.js
37670
37682
  var D1NotConfiguredError2, DbModuleNotInstalledError3;
37671
37683
  var init_db_errors2 = __esm(() => {
37672
37684
  D1NotConfiguredError2 = class D1NotConfiguredError2 extends Error {
@@ -37683,7 +37695,7 @@ var init_db_errors2 = __esm(() => {
37683
37695
  };
37684
37696
  });
37685
37697
 
37686
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.16+d8250d61be64a872/node_modules/@gobing-ai/ts-runtime/dist/file-system-cf.js
37698
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.18+0c3519262b4dbb18/node_modules/@gobing-ai/ts-runtime/dist/file-system-cf.js
37687
37699
  function createCfFileSystem2() {
37688
37700
  return {
37689
37701
  getProjectRoot: () => "/bundle",
@@ -37722,7 +37734,7 @@ function createCfFileSystem2() {
37722
37734
  }
37723
37735
  var UNSUPPORTED = "FileSystem is not available on Cloudflare Workers. Use D1, KV, or R2 for persistent storage.";
37724
37736
 
37725
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.16+d8250d61be64a872/node_modules/@gobing-ai/ts-runtime/dist/runtime-cf.js
37737
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.18+0c3519262b4dbb18/node_modules/@gobing-ai/ts-runtime/dist/runtime-cf.js
37726
37738
  var exports_runtime_cf = {};
37727
37739
  __export(exports_runtime_cf, {
37728
37740
  cloudflareWorkersFactory: () => cloudflareWorkersFactory2
@@ -37760,7 +37772,7 @@ var init_runtime_cf = __esm(() => {
37760
37772
  };
37761
37773
  });
37762
37774
 
37763
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.16+d8250d61be64a872/node_modules/@gobing-ai/ts-runtime/dist/process-executor.js
37775
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.18+0c3519262b4dbb18/node_modules/@gobing-ai/ts-runtime/dist/process-executor.js
37764
37776
  import { isatty as isatty2 } from "tty";
37765
37777
 
37766
37778
  class NodeProcessExecutor3 {
@@ -37803,6 +37815,14 @@ class NodeProcessExecutor3 {
37803
37815
  });
37804
37816
  try {
37805
37817
  const subprocess = execa(options.command, args, execaOptions);
37818
+ if (subprocess.pid !== undefined) {
37819
+ this.config.registry?.update(registryId, { pid: subprocess.pid });
37820
+ if (options.onSpawn !== undefined) {
37821
+ try {
37822
+ options.onSpawn(subprocess.pid);
37823
+ } catch {}
37824
+ }
37825
+ }
37806
37826
  const stopGroupCancellation = observeProcessGroupCancellation2(subprocess, options.signal);
37807
37827
  observeOutput2(subprocess.stdout, "stdout", options.onOutput);
37808
37828
  observeOutput2(subprocess.stderr, "stderr", options.onOutput);
@@ -38137,7 +38157,7 @@ var init_process_executor2 = __esm(() => {
38137
38157
  ProcessExecutor2 = NodeProcessExecutor3;
38138
38158
  });
38139
38159
 
38140
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.16+d8250d61be64a872/node_modules/@gobing-ai/ts-runtime/dist/runtime-node-bun.js
38160
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.18+0c3519262b4dbb18/node_modules/@gobing-ai/ts-runtime/dist/runtime-node-bun.js
38141
38161
  var exports_runtime_node_bun = {};
38142
38162
  __export(exports_runtime_node_bun, {
38143
38163
  nodeBunFactory: () => nodeBunFactory3,
@@ -38205,7 +38225,7 @@ var init_runtime_node_bun2 = __esm(() => {
38205
38225
  };
38206
38226
  });
38207
38227
 
38208
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.16+d8250d61be64a872/node_modules/@gobing-ai/ts-runtime/dist/platform.js
38228
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.18+0c3519262b4dbb18/node_modules/@gobing-ai/ts-runtime/dist/platform.js
38209
38229
  function isCloudflareWorkerRuntime2() {
38210
38230
  return globalThis.navigator?.userAgent?.startsWith("Cloudflare-Workers") ?? false;
38211
38231
  }
@@ -38235,7 +38255,7 @@ function _resetRuntimeFactory2() {
38235
38255
  }
38236
38256
  var _factory, _runtimeName;
38237
38257
 
38238
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.16+d8250d61be64a872/node_modules/@gobing-ai/ts-runtime/dist/context.js
38258
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.18+0c3519262b4dbb18/node_modules/@gobing-ai/ts-runtime/dist/context.js
38239
38259
  class RuntimeContext2 {
38240
38260
  scope;
38241
38261
  runtimeName;
@@ -38325,7 +38345,7 @@ var init_context2 = __esm(() => {
38325
38345
  init_runtime_node_bun2();
38326
38346
  });
38327
38347
 
38328
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.16+d8250d61be64a872/node_modules/@gobing-ai/ts-runtime/dist/path.js
38348
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.18+0c3519262b4dbb18/node_modules/@gobing-ai/ts-runtime/dist/path.js
38329
38349
  function normalizeSeparators2(path7) {
38330
38350
  return path7.replaceAll("\\", "/");
38331
38351
  }
@@ -38448,7 +38468,7 @@ var init_path2 = __esm(() => {
38448
38468
  SEP2 = globalThis.process?.platform === "win32" ? "\\" : "/";
38449
38469
  });
38450
38470
 
38451
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.16+d8250d61be64a872/node_modules/@gobing-ai/ts-runtime/dist/fs.js
38471
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.18+0c3519262b4dbb18/node_modules/@gobing-ai/ts-runtime/dist/fs.js
38452
38472
  async function ensureDirForFile2(path7, fs2 = createNodeFileSystem3()) {
38453
38473
  await fs2.ensureDir(dirnamePath(path7));
38454
38474
  }
@@ -38499,7 +38519,7 @@ var init_fs = __esm(() => {
38499
38519
  init_path2();
38500
38520
  });
38501
38521
 
38502
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.16+d8250d61be64a872/node_modules/@gobing-ai/ts-runtime/dist/process-registry.js
38522
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.18+0c3519262b4dbb18/node_modules/@gobing-ai/ts-runtime/dist/process-registry.js
38503
38523
  function allocateId() {
38504
38524
  nextIdSeq += 1;
38505
38525
  return `pe_${Date.now().toString(36)}_${nextIdSeq.toString(36)}`;
@@ -38623,7 +38643,7 @@ function createInMemoryProcessRegistry2(options) {
38623
38643
  }
38624
38644
  var nextIdSeq = 0;
38625
38645
 
38626
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.16+d8250d61be64a872/node_modules/@gobing-ai/ts-runtime/dist/runtime-paths.js
38646
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.18+0c3519262b4dbb18/node_modules/@gobing-ai/ts-runtime/dist/runtime-paths.js
38627
38647
  function ambientRuntimePaths2() {
38628
38648
  const cwd = getProcessCwd2();
38629
38649
  return { cwd, home: getHomeDir() ?? cwd };
@@ -38633,7 +38653,7 @@ var init_runtime_paths = __esm(() => {
38633
38653
  init_path2();
38634
38654
  });
38635
38655
 
38636
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.16+d8250d61be64a872/node_modules/@gobing-ai/ts-runtime/dist/schema-validation.js
38656
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.18+0c3519262b4dbb18/node_modules/@gobing-ai/ts-runtime/dist/schema-validation.js
38637
38657
  async function loadStructuredConfig(path7, options = {}) {
38638
38658
  const content = await (options.fileSystem ?? createNodeFileSystem3()).readFile(path7);
38639
38659
  return await parseStructuredConfig(content, path7, options);
@@ -38910,10 +38930,10 @@ var init_schema_validation2 = __esm(() => {
38910
38930
  defaultResolve = typeof Bun !== "undefined" ? (specifier, from) => Bun.resolveSync(specifier, from) : undefined;
38911
38931
  });
38912
38932
 
38913
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.16+d8250d61be64a872/node_modules/@gobing-ai/ts-runtime/dist/types.js
38933
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.18+0c3519262b4dbb18/node_modules/@gobing-ai/ts-runtime/dist/types.js
38914
38934
  var init_types2 = () => {};
38915
38935
 
38916
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.16+d8250d61be64a872/node_modules/@gobing-ai/ts-runtime/dist/index.js
38936
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.18+0c3519262b4dbb18/node_modules/@gobing-ai/ts-runtime/dist/index.js
38917
38937
  var exports_dist2 = {};
38918
38938
  __export(exports_dist2, {
38919
38939
  writeJsonFile: () => writeJsonFile2,
@@ -41064,7 +41084,7 @@ var init_mod = __esm(() => {
41064
41084
  init_config3();
41065
41085
  });
41066
41086
 
41067
- // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.16+4e212f5ced9864ce/node_modules/@gobing-ai/ts-infra/dist/logger.js
41087
+ // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.18+863f28addc1a7cdc/node_modules/@gobing-ai/ts-infra/dist/logger.js
41068
41088
  function toLogTapeLevel(level) {
41069
41089
  return level === "warn" ? "warning" : level;
41070
41090
  }
@@ -42617,7 +42637,7 @@ var require_src = __commonJS((exports) => {
42617
42637
  };
42618
42638
  });
42619
42639
 
42620
- // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.16+4e212f5ced9864ce/node_modules/@gobing-ai/ts-infra/dist/telemetry/sdk.js
42640
+ // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.18+863f28addc1a7cdc/node_modules/@gobing-ai/ts-infra/dist/telemetry/sdk.js
42621
42641
  function getTelemetryConfig(configPartial = {}) {
42622
42642
  return {
42623
42643
  enabled: configPartial.enabled ?? CONFIG_DEFAULTS.enabled,
@@ -42653,7 +42673,7 @@ var init_sdk = __esm(() => {
42653
42673
  resolvedConfig = getTelemetryConfig();
42654
42674
  });
42655
42675
 
42656
- // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.16+4e212f5ced9864ce/node_modules/@gobing-ai/ts-infra/dist/telemetry/tracing.js
42676
+ // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.18+863f28addc1a7cdc/node_modules/@gobing-ai/ts-infra/dist/telemetry/tracing.js
42657
42677
  function isSuppressed(tracer2) {
42658
42678
  return tracer2 === undefined && !getResolvedConfig().enabled;
42659
42679
  }
@@ -42693,7 +42713,7 @@ var init_tracing2 = __esm(() => {
42693
42713
  import_api2 = __toESM(require_src(), 1);
42694
42714
  });
42695
42715
 
42696
- // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.16+4e212f5ced9864ce/node_modules/@gobing-ai/ts-infra/dist/telemetry/metrics.js
42716
+ // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.18+863f28addc1a7cdc/node_modules/@gobing-ai/ts-infra/dist/telemetry/metrics.js
42697
42717
  function getMeter() {
42698
42718
  return import_api3.metrics.getMeter(METER_NAME, METER_VERSION);
42699
42719
  }
@@ -42790,7 +42810,7 @@ var init_metrics = __esm(() => {
42790
42810
  instruments = {};
42791
42811
  });
42792
42812
 
42793
- // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.16+4e212f5ced9864ce/node_modules/@gobing-ai/ts-infra/dist/event-bus/event-bus.js
42813
+ // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.18+863f28addc1a7cdc/node_modules/@gobing-ai/ts-infra/dist/event-bus/event-bus.js
42794
42814
  function busLogger() {
42795
42815
  if (!_busLogger)
42796
42816
  _busLogger = getLogger2("event-bus");
@@ -43035,7 +43055,7 @@ var init_event_bus = __esm(() => {
43035
43055
  init_metrics();
43036
43056
  });
43037
43057
 
43038
- // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.16+4e212f5ced9864ce/node_modules/@gobing-ai/ts-infra/dist/event-bus/default-observers.js
43058
+ // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.18+863f28addc1a7cdc/node_modules/@gobing-ai/ts-infra/dist/event-bus/default-observers.js
43039
43059
  function obsLogger() {
43040
43060
  if (!_obsLogger)
43041
43061
  _obsLogger = getLogger2("event-bus.observers");
@@ -43099,7 +43119,7 @@ var init_default_observers = __esm(() => {
43099
43119
  init_event_bus();
43100
43120
  });
43101
43121
 
43102
- // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.16+4e212f5ced9864ce/node_modules/@gobing-ai/ts-infra/dist/event-bus/file-observer.js
43122
+ // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.18+863f28addc1a7cdc/node_modules/@gobing-ai/ts-infra/dist/event-bus/file-observer.js
43103
43123
  function parentDir(filePath) {
43104
43124
  const idx = filePath.lastIndexOf("/");
43105
43125
  return idx <= 0 ? "." : filePath.slice(0, idx);
@@ -43197,7 +43217,7 @@ var init_file_observer = __esm(() => {
43197
43217
  init_logger3();
43198
43218
  });
43199
43219
 
43200
- // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.16+4e212f5ced9864ce/node_modules/@gobing-ai/ts-infra/dist/internals/drain.js
43220
+ // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.18+863f28addc1a7cdc/node_modules/@gobing-ai/ts-infra/dist/internals/drain.js
43201
43221
  function settleWithin(promise2, deadline) {
43202
43222
  const remaining = deadline - Date.now();
43203
43223
  if (remaining <= 0)
@@ -43212,7 +43232,7 @@ function settleWithin(promise2, deadline) {
43212
43232
  return settled;
43213
43233
  }
43214
43234
 
43215
- // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.16+4e212f5ced9864ce/node_modules/@gobing-ai/ts-infra/dist/scheduler/node.js
43235
+ // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.18+863f28addc1a7cdc/node_modules/@gobing-ai/ts-infra/dist/scheduler/node.js
43216
43236
  function parseInterval(cron) {
43217
43237
  const trimmed = cron.trim();
43218
43238
  const num = Number(trimmed);
@@ -43305,7 +43325,7 @@ var init_node3 = __esm(() => {
43305
43325
  init_metrics();
43306
43326
  });
43307
43327
 
43308
- // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.16+4e212f5ced9864ce/node_modules/@gobing-ai/ts-infra/dist/scheduler-node.js
43328
+ // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.18+863f28addc1a7cdc/node_modules/@gobing-ai/ts-infra/dist/scheduler-node.js
43309
43329
  var exports_scheduler_node = {};
43310
43330
  __export(exports_scheduler_node, {
43311
43331
  NodeSchedulerAdapter: () => NodeSchedulerAdapter
@@ -49574,6 +49594,7 @@ class AgentExecutionLifecycle {
49574
49594
  heartbeat;
49575
49595
  startedAt = 0;
49576
49596
  timeoutMs;
49597
+ pid;
49577
49598
  outputCarry = { stdout: "", stderr: "" };
49578
49599
  constructor(observer, correlation, secrets, heartbeatMs = 30000) {
49579
49600
  this.observer = observer;
@@ -49587,6 +49608,10 @@ class AgentExecutionLifecycle {
49587
49608
  get identity() {
49588
49609
  return this.correlation;
49589
49610
  }
49611
+ setPid(pid) {
49612
+ if (Number.isInteger(pid) && pid > 0)
49613
+ this.pid = pid;
49614
+ }
49590
49615
  start(detail) {
49591
49616
  this.startedAt = Date.now();
49592
49617
  this.timeoutMs = detail.timeoutMs;
@@ -49684,6 +49709,7 @@ class AgentExecutionLifecycle {
49684
49709
  runId: this.correlation.runId,
49685
49710
  executionId: this.correlation.executionId,
49686
49711
  ...this.correlation.actionId !== undefined ? { actionId: this.correlation.actionId } : {},
49712
+ ...this.pid !== undefined ? { pid: this.pid } : {},
49687
49713
  at: new Date().toISOString()
49688
49714
  };
49689
49715
  }
@@ -49723,6 +49749,436 @@ var init_agent_execution = __esm(() => {
49723
49749
  SECRET_PATTERN = /(?:sk-[a-z0-9_-]{8,}|(?:api[_-]?key|token|secret|password)\s*[:=]\s*\S+|bearer\s+\S+)/gi;
49724
49750
  });
49725
49751
 
49752
+ // ../../packages/app/src/workflow/observability.ts
49753
+ function bounded(value, maxLength = MAX_FIELD_LENGTH) {
49754
+ const redacted = value.replace(SECRET_PATTERN2, "[REDACTED]");
49755
+ return redacted.length <= maxLength ? redacted : `${redacted.slice(0, maxLength)}\u2026`;
49756
+ }
49757
+ function projectActionMetadata(kind, options) {
49758
+ if (options === undefined)
49759
+ return;
49760
+ const metadata = {};
49761
+ if (typeof options.agent === "string" && options.agent !== "")
49762
+ metadata.agent = bounded(options.agent);
49763
+ if (typeof options.model === "string" && options.model !== "")
49764
+ metadata.model = bounded(options.model);
49765
+ if (typeof options.timeoutMs === "number" && Number.isFinite(options.timeoutMs)) {
49766
+ metadata.timeoutMs = Math.max(0, options.timeoutMs);
49767
+ }
49768
+ const input = typeof options.input === "string" ? options.input.trim() : "";
49769
+ if (kind === "agent.run" && input !== "") {
49770
+ const command = input.startsWith("/") ? input.split(/\s+/, 1)[0] : undefined;
49771
+ metadata.invocation = command === undefined ? `[prompt ${input.length} chars]` : bounded(command);
49772
+ } else if (kind === "shell" && typeof options.command === "string" && options.command.trim() !== "") {
49773
+ metadata.invocation = bounded(sanitizeCommand(options.command), 80);
49774
+ } else if (kind === "note" && typeof options.message === "string" && options.message.trim() !== "") {
49775
+ metadata.invocation = bounded(options.message.trim(), 80);
49776
+ }
49777
+ return Object.keys(metadata).length === 0 ? undefined : metadata;
49778
+ }
49779
+ function sanitizeCommand(cmd) {
49780
+ const trimmed = cmd.replace(/\n+/g, " ").replace(/\s+/g, " ").trim();
49781
+ if (/(?:authorization|bearer|password|secret|token|api_key|private_key)/i.test(trimmed)) {
49782
+ return "[shell command redacted]";
49783
+ }
49784
+ return trimmed;
49785
+ }
49786
+ function projectResult(result) {
49787
+ if (result === undefined)
49788
+ return;
49789
+ if (typeof result !== "object" || result === null)
49790
+ return { usage: "unavailable" };
49791
+ const obj = result;
49792
+ const error51 = typeof obj.error === "string" ? bounded(obj.error) : undefined;
49793
+ const stderr = typeof obj.stderr === "string" && obj.stderr.trim() !== "" ? bounded(obj.stderr.trim(), 120) : undefined;
49794
+ const stdout = typeof obj.stdout === "string" && obj.stdout.trim() !== "" ? bounded(obj.stdout.trim(), 120) : undefined;
49795
+ const detail = error51 ?? stderr ?? stdout;
49796
+ return { ...detail !== undefined ? { error: detail } : {}, usage: "unavailable" };
49797
+ }
49798
+
49799
+ class ObservableWorkflowAdapter {
49800
+ inner;
49801
+ bus;
49802
+ sequences = new Map;
49803
+ workflowNames = new Map;
49804
+ actions = new Map;
49805
+ constructor(inner, bus) {
49806
+ this.inner = inner;
49807
+ this.bus = bus;
49808
+ }
49809
+ async createRun(record2) {
49810
+ await this.inner.createRun(record2);
49811
+ this.workflowNames.set(record2.id, record2.workflow_name);
49812
+ await this.bus.emit("workflow.run.started", {
49813
+ ...this.envelope(record2.id),
49814
+ workflowName: record2.workflow_name
49815
+ });
49816
+ }
49817
+ async finalizeRun(runId, status, completedAt) {
49818
+ await this.inner.finalizeRun(runId, status, completedAt);
49819
+ await this.bus.emit("workflow.run.finalized", { ...this.envelope(runId, completedAt), status });
49820
+ }
49821
+ async savePhase(runId, phase, status) {
49822
+ await this.inner.savePhase(runId, phase, status);
49823
+ await this.bus.emit("workflow.phase", { ...this.envelope(runId), phase, status });
49824
+ }
49825
+ async saveTransition(runId, from, to, trigger) {
49826
+ await this.inner.saveTransition(runId, from, to, trigger);
49827
+ await this.bus.emit("workflow.transition", { ...this.envelope(runId), from, to, trigger });
49828
+ }
49829
+ async commitTransition(runId, from, to, trigger, _state, _data, phase) {
49830
+ await this.inner.commitTransition(runId, from, to, trigger, _state, _data, phase);
49831
+ await this.bus.emit("workflow.transition", { ...this.envelope(runId), from, to, trigger });
49832
+ if (phase) {
49833
+ await this.bus.emit("workflow.phase", {
49834
+ ...this.envelope(runId),
49835
+ phase: phase.phase,
49836
+ status: phase.status
49837
+ });
49838
+ }
49839
+ }
49840
+ async saveActionStart(runId, node, kind, options) {
49841
+ const actionId = await this.inner.saveActionStart(runId, node, kind, options);
49842
+ this.actions.set(actionId, { runId, node, kind });
49843
+ const metadata = projectActionMetadata(kind, options);
49844
+ await this.bus.emit("workflow.action.started", {
49845
+ ...this.envelope(runId),
49846
+ actionId,
49847
+ node,
49848
+ kind,
49849
+ ...metadata !== undefined ? { metadata } : {}
49850
+ });
49851
+ return actionId;
49852
+ }
49853
+ async saveActionFinalize(actionId, status, durationMs, ok, result, redactor) {
49854
+ await this.inner.saveActionFinalize(actionId, status, durationMs, ok, result, redactor);
49855
+ const action = this.actions.get(actionId);
49856
+ if (action === undefined) {
49857
+ return;
49858
+ }
49859
+ this.actions.delete(actionId);
49860
+ const projected = projectResult(result);
49861
+ await this.bus.emit("workflow.action.finished", {
49862
+ ...this.envelope(action.runId),
49863
+ actionId,
49864
+ status,
49865
+ durationMs,
49866
+ ok,
49867
+ node: action.node,
49868
+ kind: action.kind,
49869
+ ...projected !== undefined ? { result: projected } : {}
49870
+ });
49871
+ }
49872
+ envelope(runId, at = now()) {
49873
+ const sequence = (this.sequences.get(runId) ?? 0) + 1;
49874
+ this.sequences.set(runId, sequence);
49875
+ const workflowName = this.workflowNames.get(runId);
49876
+ return {
49877
+ schemaVersion: 1,
49878
+ eventId: crypto.randomUUID(),
49879
+ sequence,
49880
+ runId,
49881
+ ...workflowName !== undefined ? { workflowName } : {},
49882
+ at
49883
+ };
49884
+ }
49885
+ saveWorkflowState(runId, state, data) {
49886
+ return this.inner.saveWorkflowState(runId, state, data);
49887
+ }
49888
+ loadRun(runId) {
49889
+ return this.inner.loadRun(runId);
49890
+ }
49891
+ listRuns() {
49892
+ return this.inner.listRuns();
49893
+ }
49894
+ findRunByKey(workflowName, externalKey) {
49895
+ return this.inner.findRunByKey(workflowName, externalKey);
49896
+ }
49897
+ createOrAttachRun(record2) {
49898
+ return this.inner.createOrAttachRun(record2);
49899
+ }
49900
+ reseedRun(runId, newState) {
49901
+ return this.inner.reseedRun(runId, newState);
49902
+ }
49903
+ loadCurrentState(runId) {
49904
+ return this.inner.loadCurrentState(runId);
49905
+ }
49906
+ loadLatestStateSnapshot(runId) {
49907
+ return this.inner.loadLatestStateSnapshot(runId);
49908
+ }
49909
+ listPausedRuns(options) {
49910
+ return this.inner.listPausedRuns(options);
49911
+ }
49912
+ }
49913
+ var now = () => new Date().toISOString(), MAX_FIELD_LENGTH = 256, SECRET_PATTERN2;
49914
+ var init_observability = __esm(() => {
49915
+ SECRET_PATTERN2 = /(?:sk-[a-z0-9_-]{8,}|(?:api[_-]?key|token|secret|password)\s*[:=]\s*\S+|bearer\s+\S+)/gi;
49916
+ });
49917
+
49918
+ // ../../packages/app/src/workflow/step-reporter.ts
49919
+ function runPrefix(runId, options) {
49920
+ if (options.showRunId === false)
49921
+ return "";
49922
+ return `[run ${runId.slice(0, 8)}] `;
49923
+ }
49924
+ function renderStepLine(event, options = {}) {
49925
+ const detail = options.detail ?? "invocation";
49926
+ const pfx = runPrefix(event.runId, options);
49927
+ if (isAgentExecution(event)) {
49928
+ if (event.kind === "output") {
49929
+ if (detail === "minimal")
49930
+ return null;
49931
+ const label = event.stream === "stderr" ? "stderr" : "stdout";
49932
+ return `${pfx} ${label}> ${event.chunk.replace(/\s+$/, "")}`;
49933
+ }
49934
+ if (event.kind === "heartbeat") {
49935
+ if (detail === "minimal")
49936
+ return null;
49937
+ const budget = event.timeoutMs === undefined ? "unbounded" : `${formatDuration(Math.max(0, event.timeoutMs - event.elapsedMs))} remaining`;
49938
+ const pidPart = event.pid === undefined ? "" : ` \xB7 pid=${event.pid}`;
49939
+ return `${pfx} \u2026 agent execution \xB7 elapsed=${formatDuration(event.elapsedMs)} \xB7 budget=${budget}${pidPart}`;
49940
+ }
49941
+ if (event.kind === "dropped") {
49942
+ return detail === "minimal" ? null : `${pfx}\u2026 output pressure \xB7 dropped=${event.chunks} chunks`;
49943
+ }
49944
+ if (event.kind === "started") {
49945
+ if (detail === "minimal")
49946
+ return null;
49947
+ const model = event.model === undefined ? "" : `(${event.model})`;
49948
+ const pidPart = event.pid === undefined ? "" : ` \xB7 pid=${event.pid}`;
49949
+ const full2 = detail === "full" ? ` \xB7 execution=${event.executionId}` : "";
49950
+ return `${pfx} agent=${event.agent}${model} => ${event.invocation}${pidPart}${full2}`;
49951
+ }
49952
+ const mark = event.outcome === "done" ? "\u2713" : "\u2717";
49953
+ const full = detail === "full" ? ` \xB7 execution=${event.executionId}` : "";
49954
+ const exitPart = event.exitCode !== null && event.exitCode !== undefined ? ` \xB7 exit ${event.exitCode}` : "";
49955
+ return detail === "minimal" ? null : `${pfx}${mark} agent ${event.outcome} (${formatDuration(event.durationMs)})${exitPart} \xB7 usage ${event.usage}${event.reason ? ` \xB7 ${event.reason}` : ""}${full}`;
49956
+ }
49957
+ if (isActionOutput(event)) {
49958
+ if (detail === "minimal")
49959
+ return null;
49960
+ const label = event.stream === "stderr" ? "stderr" : "stdout";
49961
+ return `${pfx} ${label}> ${event.chunk.replace(/\s+$/, "")}`;
49962
+ }
49963
+ if (isActionFinished(event)) {
49964
+ const mark = event.ok ? "\u2713" : "\u2717";
49965
+ if (detail === "minimal")
49966
+ return ` ${mark} ${event.status} (${formatDuration(event.durationMs)})`;
49967
+ const failure = event.result?.error === undefined ? "" : ` \xB7 ${event.result.error}`;
49968
+ const usagePart = event.result?.usage !== undefined && event.result.usage !== "unavailable" ? ` \xB7 usage ${event.result.usage}` : "";
49969
+ const full = detail === "full" ? ` \xB7 action=${event.actionId} \xB7 seq=${event.sequence}` : "";
49970
+ return `${pfx}${mark} ${event.node}/${event.kind} (${formatDuration(event.durationMs)})${usagePart}${failure}${full}`;
49971
+ }
49972
+ if (isActionStarted(event)) {
49973
+ if (detail === "minimal")
49974
+ return ` \u2192 ${event.node}: ${event.kind}\u2026`;
49975
+ const agentPart = event.metadata?.agent && event.metadata.agent !== "unavailable" ? ` \xB7 agent=${event.metadata.agent}` : "";
49976
+ const modelPart = event.metadata?.model && event.metadata.model !== "unavailable" ? ` \xB7 model=${event.metadata.model}` : "";
49977
+ const invocationPart = event.metadata?.invocation && event.metadata.invocation !== "unavailable" ? ` => ${event.metadata.invocation}` : "";
49978
+ const timeout = event.metadata?.timeoutMs === undefined ? "unbounded" : formatDuration(event.metadata.timeoutMs);
49979
+ const full = detail === "full" ? ` \xB7 action=${event.actionId} \xB7 seq=${event.sequence}` : "";
49980
+ return `${pfx}\u2192 ${event.node}/${event.kind}${agentPart}${modelPart}${invocationPart} \xB7 timeout=${timeout}${full}`;
49981
+ }
49982
+ if (isPhase(event)) {
49983
+ return detail === "minimal" ? `\u25B6 ${event.phase} [${event.status}]` : `${pfx}\u25B6 ${event.phase} [${event.status}]`;
49984
+ }
49985
+ if (isTransition(event)) {
49986
+ if (detail === "minimal")
49987
+ return null;
49988
+ const seqPart = detail === "full" ? ` \xB7 seq=${event.sequence}` : "";
49989
+ return `${pfx}\u21AA ${event.from} \u2192 ${event.to}${event.trigger ? ` [${event.trigger}]` : ""}${seqPart}`;
49990
+ }
49991
+ return null;
49992
+ }
49993
+ function renderActionHeartbeat(event, elapsedMs, options = {}) {
49994
+ const detail = options.detail ?? "invocation";
49995
+ if (detail === "minimal")
49996
+ return null;
49997
+ const elapsed = formatDuration(elapsedMs);
49998
+ const budget = event.metadata?.timeoutMs === undefined ? "unbounded" : `${formatDuration(Math.max(0, event.metadata.timeoutMs - elapsedMs))} remaining`;
49999
+ const full = detail === "full" ? ` \xB7 action=${event.actionId}` : "";
50000
+ return `${runPrefix(event.runId, options)}\u2026 ${event.node}/${event.kind} \xB7 elapsed=${elapsed} \xB7 budget=${budget}${full}`;
50001
+ }
50002
+ function renderRunPlan(def) {
50003
+ const steps = def.kind === "transition-flow" ? def.nodes.map((n2) => n2.id) : def.states.map((s) => s.id);
50004
+ return `plan: ${steps.join(" \u2192 ")}`;
50005
+ }
50006
+ function formatDuration(ms) {
50007
+ const totalSeconds = Math.round(ms / 1000);
50008
+ if (totalSeconds < 60)
50009
+ return `${totalSeconds}s`;
50010
+ const minutes = Math.floor(totalSeconds / 60);
50011
+ const seconds = totalSeconds % 60;
50012
+ return `${minutes}m ${seconds}s`;
50013
+ }
50014
+ var isAgentExecution = (e) => ("executionId" in e), isActionOutput = (e) => ("stream" in e) && ("chunk" in e) && !("executionId" in e), isActionStarted = (e) => ("kind" in e) && ("node" in e), isActionFinished = (e) => ("durationMs" in e) && ("ok" in e), isPhase = (e) => ("phase" in e), isTransition = (e) => ("from" in e) && ("to" in e);
50015
+
50016
+ // ../../packages/app/src/observability/workflow-run-log-sink.ts
50017
+ import { closeSync, mkdirSync as mkdirSync4, openSync, writeSync } from "fs";
50018
+ import { join as join5 } from "path";
50019
+
50020
+ class WorkflowRunLogSink {
50021
+ filePath;
50022
+ maxBytes;
50023
+ maxLines;
50024
+ planPreview;
50025
+ fd;
50026
+ bytes = 0;
50027
+ lines = 0;
50028
+ truncated = false;
50029
+ closed = false;
50030
+ headerWritten = false;
50031
+ bus;
50032
+ handlers;
50033
+ constructor(options) {
50034
+ this.filePath = join5(options.dir, `${options.runId}.log`);
50035
+ this.maxBytes = options.maxBytes ?? DEFAULT_RUN_LOG_MAX_BYTES;
50036
+ this.maxLines = options.maxLines;
50037
+ this.planPreview = options.planPreview;
50038
+ try {
50039
+ mkdirSync4(options.dir, { recursive: true });
50040
+ this.fd = openSync(this.filePath, "a");
50041
+ } catch {
50042
+ this.fd = undefined;
50043
+ }
50044
+ this.bus = options.bus;
50045
+ this.handlers = {
50046
+ "workflow.run.started": (event) => this.onRunStarted(event),
50047
+ "workflow.phase": (event) => this.onProgress(event),
50048
+ "workflow.transition": (event) => this.onProgress(event),
50049
+ "workflow.action.started": (event) => this.onProgress(event),
50050
+ "workflow.action.finished": (event) => this.onProgress(event),
50051
+ "workflow.action.output": (event) => this.onProgress(event),
50052
+ "workflow.agent": (event) => this.onAgent(event),
50053
+ "workflow.steering": (event) => this.onSteering(event),
50054
+ "workflow.run.finalized": (event) => this.onRunFinalized(event)
50055
+ };
50056
+ this.register(true);
50057
+ }
50058
+ get isTruncated() {
50059
+ return this.truncated;
50060
+ }
50061
+ close() {
50062
+ if (this.closed)
50063
+ return;
50064
+ this.closed = true;
50065
+ this.register(false);
50066
+ if (this.fd !== undefined) {
50067
+ try {
50068
+ closeSync(this.fd);
50069
+ } catch {}
50070
+ this.fd = undefined;
50071
+ }
50072
+ }
50073
+ register(attach) {
50074
+ for (const name of RUN_LOG_EVENT_NAMES) {
50075
+ if (attach)
50076
+ this.bus.on(name, this.handlers[name]);
50077
+ else
50078
+ this.bus.off(name, this.handlers[name]);
50079
+ }
50080
+ }
50081
+ onRunStarted(event) {
50082
+ if (this.headerWritten)
50083
+ return;
50084
+ this.headerWritten = true;
50085
+ this.append(`# spur workflow run ${event.runId} \u2014 ${event.workflowName} \u2014 started ${event.at}
50086
+ `);
50087
+ if (this.planPreview !== undefined)
50088
+ this.append(`# ${this.planPreview}
50089
+ `);
50090
+ }
50091
+ onProgress(event) {
50092
+ const line = renderStepLine(event, { detail: "full", showRunId: true });
50093
+ if (line === null)
50094
+ return;
50095
+ this.append(`[${event.at}] ${line}
50096
+ `);
50097
+ }
50098
+ onRunFinalized(event) {
50099
+ this.append(`
50100
+ === workflow run ${event.runId} finished \u2014 status ${event.status} \u2014 ${event.at} ===
50101
+ `);
50102
+ }
50103
+ onSteering(ack) {
50104
+ const note = ack.note === undefined ? "" : ` \xB7 ${bounded(ack.note, MAX_STEERING_NOTE_CHARS)}`;
50105
+ this.append(`[${ack.at}] [steer] ${ack.accepted ? "ack" : "nack"} ${ack.operation}${note}
50106
+ `);
50107
+ }
50108
+ onAgent(event) {
50109
+ if (this.fd === undefined || this.closed)
50110
+ return;
50111
+ switch (event.kind) {
50112
+ case "output":
50113
+ this.append(`[${event.at}] ${event.stream}: ${event.chunk}
50114
+ `);
50115
+ break;
50116
+ case "started":
50117
+ this.append(`# agent output \u2014 run ${event.runId} \u2014 ${event.agent} \u2014 ${event.at}
50118
+ `);
50119
+ this.append(`# invocation: ${event.invocation}
50120
+ `);
50121
+ break;
50122
+ case "dropped":
50123
+ this.append(`
50124
+ === [dropped] ${event.chunks} chunk(s) discarded by the lifecycle relay under backpressure ===
50125
+ `);
50126
+ break;
50127
+ case "finished":
50128
+ this.append(`
50129
+ === run ${event.outcome} (exit ${event.exitCode}) after ${event.durationMs}ms ===
50130
+ `);
50131
+ break;
50132
+ default:
50133
+ break;
50134
+ }
50135
+ }
50136
+ append(text3) {
50137
+ if (this.fd === undefined || this.closed || this.truncated)
50138
+ return;
50139
+ const textBytes = Buffer.byteLength(text3);
50140
+ const textLines = countNewlines(text3);
50141
+ if (this.bytes + textBytes > this.maxBytes || this.maxLines !== undefined && this.lines + textLines > this.maxLines) {
50142
+ this.truncated = true;
50143
+ try {
50144
+ writeSync(this.fd, TRUNCATION_MARKER);
50145
+ } catch {}
50146
+ return;
50147
+ }
50148
+ try {
50149
+ writeSync(this.fd, text3);
50150
+ this.bytes += textBytes;
50151
+ this.lines += textLines;
50152
+ } catch {}
50153
+ }
50154
+ }
50155
+ function countNewlines(text3) {
50156
+ let count3 = 0;
50157
+ for (let i2 = 0;i2 < text3.length; i2 += 1) {
50158
+ if (text3.charCodeAt(i2) === 10)
50159
+ count3 += 1;
50160
+ }
50161
+ return count3;
50162
+ }
50163
+ var DEFAULT_RUN_LOG_MAX_BYTES, MAX_STEERING_NOTE_CHARS = 1024, TRUNCATION_MARKER = `
50164
+ === [truncated] consolidated run log reached its configured bound; further lines were not written ===
50165
+ `, RUN_LOG_EVENT_NAMES;
50166
+ var init_workflow_run_log_sink = __esm(() => {
50167
+ init_observability();
50168
+ DEFAULT_RUN_LOG_MAX_BYTES = 1024 * 1024;
50169
+ RUN_LOG_EVENT_NAMES = [
50170
+ "workflow.run.started",
50171
+ "workflow.phase",
50172
+ "workflow.transition",
50173
+ "workflow.action.started",
50174
+ "workflow.action.finished",
50175
+ "workflow.action.output",
50176
+ "workflow.agent",
50177
+ "workflow.steering",
50178
+ "workflow.run.finalized"
50179
+ ];
50180
+ });
50181
+
49726
50182
  // ../../packages/domain/src/analytics/models.ts
49727
50183
  function resolvePricing(model) {
49728
50184
  if (model === undefined || model.length === 0)
@@ -51009,7 +51465,7 @@ var init_drizzle_zod = __esm(() => {
51009
51465
  };
51010
51466
  });
51011
51467
 
51012
- // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.16+fec36642bac873ee/node_modules/@gobing-ai/ts-db/dist/schema/drizzle-internals.js
51468
+ // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.18+c31add66cbc22fb3/node_modules/@gobing-ai/ts-db/dist/schema/drizzle-internals.js
51013
51469
  function sqlExpressionToText(value) {
51014
51470
  if (typeof value !== "object" || value === null || !("queryChunks" in value)) {
51015
51471
  return;
@@ -51032,7 +51488,7 @@ function getDrizzleTableName(table3) {
51032
51488
  return String(table3[nameSym]);
51033
51489
  }
51034
51490
 
51035
- // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.16+fec36642bac873ee/node_modules/@gobing-ai/ts-db/dist/schema/ddl.js
51491
+ // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.18+c31add66cbc22fb3/node_modules/@gobing-ai/ts-db/dist/schema/ddl.js
51036
51492
  function quoteIdent(name) {
51037
51493
  return `"${name.replace(/"/g, '""')}"`;
51038
51494
  }
@@ -51126,7 +51582,7 @@ var init_ddl = __esm(() => {
51126
51582
  init_sqlite_core();
51127
51583
  });
51128
51584
 
51129
- // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.16+fec36642bac873ee/node_modules/@gobing-ai/ts-db/dist/schema/define-table.js
51585
+ // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.18+c31add66cbc22fb3/node_modules/@gobing-ai/ts-db/dist/schema/define-table.js
51130
51586
  function defineTable(name, columns2) {
51131
51587
  const table3 = sqliteTable(name, columns2);
51132
51588
  let insert2;
@@ -51160,7 +51616,7 @@ var init_define_table = __esm(() => {
51160
51616
  init_ddl();
51161
51617
  });
51162
51618
 
51163
- // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.16+fec36642bac873ee/node_modules/@gobing-ai/ts-db/dist/schema/index.js
51619
+ // ../../node_modules/.bun/@gobing-ai+ts-db@0.4.18+c31add66cbc22fb3/node_modules/@gobing-ai/ts-db/dist/schema/index.js
51164
51620
  var init_schema = __esm(() => {
51165
51621
  init_sqlite_core();
51166
51622
  init_define_table();
@@ -51785,7 +52241,7 @@ var init_dao = __esm(() => {
51785
52241
  init_workspace_dao();
51786
52242
  });
51787
52243
 
51788
- // ../../node_modules/.bun/@gobing-ai+ts-dual-workflow-engine@0.4.16+eb8900f44f4776f9/node_modules/@gobing-ai/ts-dual-workflow-engine/dist/errors.js
52244
+ // ../../node_modules/.bun/@gobing-ai+ts-dual-workflow-engine@0.4.18+eb8900f44f4776f9/node_modules/@gobing-ai/ts-dual-workflow-engine/dist/errors.js
51789
52245
  var WorkflowValidationError, FSMError, RunCollisionError, WorkflowResumeError;
51790
52246
  var init_errors6 = __esm(() => {
51791
52247
  WorkflowValidationError = class WorkflowValidationError extends Error {
@@ -51820,12 +52276,12 @@ var init_errors6 = __esm(() => {
51820
52276
  };
51821
52277
  });
51822
52278
 
51823
- // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.16+4e212f5ced9864ce/node_modules/@gobing-ai/ts-infra/dist/event-bus/index.js
52279
+ // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.18+863f28addc1a7cdc/node_modules/@gobing-ai/ts-infra/dist/event-bus/index.js
51824
52280
  var init_event_bus2 = __esm(() => {
51825
52281
  init_event_bus();
51826
52282
  });
51827
52283
 
51828
- // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.16+4e212f5ced9864ce/node_modules/@gobing-ai/ts-infra/dist/api-client.js
52284
+ // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.18+863f28addc1a7cdc/node_modules/@gobing-ai/ts-infra/dist/api-client.js
51829
52285
  function sanitizeUrlForObservability(url2) {
51830
52286
  try {
51831
52287
  const parsed = new URL(url2);
@@ -52055,12 +52511,12 @@ var init_api_client = __esm(() => {
52055
52511
  };
52056
52512
  });
52057
52513
 
52058
- // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.16+4e212f5ced9864ce/node_modules/@gobing-ai/ts-infra/dist/telemetry/index.js
52514
+ // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.18+863f28addc1a7cdc/node_modules/@gobing-ai/ts-infra/dist/telemetry/index.js
52059
52515
  var init_telemetry = __esm(() => {
52060
52516
  init_tracing2();
52061
52517
  });
52062
52518
 
52063
- // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.16+4e212f5ced9864ce/node_modules/@gobing-ai/ts-infra/dist/index.js
52519
+ // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.18+863f28addc1a7cdc/node_modules/@gobing-ai/ts-infra/dist/index.js
52064
52520
  var init_dist6 = __esm(() => {
52065
52521
  init_api_client();
52066
52522
  init_event_bus2();
@@ -52068,7 +52524,7 @@ var init_dist6 = __esm(() => {
52068
52524
  init_telemetry();
52069
52525
  });
52070
52526
 
52071
- // ../../node_modules/.bun/@gobing-ai+ts-dual-workflow-engine@0.4.16+eb8900f44f4776f9/node_modules/@gobing-ai/ts-dual-workflow-engine/dist/run-lifecycle.js
52527
+ // ../../node_modules/.bun/@gobing-ai+ts-dual-workflow-engine@0.4.18+eb8900f44f4776f9/node_modules/@gobing-ai/ts-dual-workflow-engine/dist/run-lifecycle.js
52072
52528
  function runtimeBuiltins(workflowName, stateOrNodeId, runId, transitionsTaken, mode) {
52073
52529
  return {
52074
52530
  workflow: workflowName,
@@ -52327,7 +52783,7 @@ var init_run_lifecycle = __esm(() => {
52327
52783
  ];
52328
52784
  });
52329
52785
 
52330
- // ../../node_modules/.bun/@gobing-ai+ts-dual-workflow-engine@0.4.16+eb8900f44f4776f9/node_modules/@gobing-ai/ts-dual-workflow-engine/dist/schema.js
52786
+ // ../../node_modules/.bun/@gobing-ai+ts-dual-workflow-engine@0.4.18+eb8900f44f4776f9/node_modules/@gobing-ai/ts-dual-workflow-engine/dist/schema.js
52331
52787
  var RESERVED_VAR_NAMES, IDENTIFIER, VarsSchema, EnvSchema, ActionDefSchema, GuardDefSchema, StateMachineWorkflowDefSchema, TransitionFlowWorkflowDefSchema, WorkflowDefSchema;
52332
52788
  var init_schema2 = __esm(() => {
52333
52789
  init_zod();
@@ -52363,6 +52819,7 @@ var init_schema2 = __esm(() => {
52363
52819
  description: exports_external.string().optional(),
52364
52820
  initialState: exports_external.string().min(1),
52365
52821
  terminalStates: exports_external.array(exports_external.string().min(1)).optional(),
52822
+ failureStates: exports_external.array(exports_external.string().min(1)).optional(),
52366
52823
  iterationBound: exports_external.number().int().positive().optional(),
52367
52824
  defaultOnError: exports_external.enum(["fail", "continue"]).optional(),
52368
52825
  vars: VarsSchema.optional(),
@@ -52411,7 +52868,7 @@ var init_schema2 = __esm(() => {
52411
52868
  WorkflowDefSchema = exports_external.union([StateMachineWorkflowDefSchema, TransitionFlowWorkflowDefSchema]);
52412
52869
  });
52413
52870
 
52414
- // ../../node_modules/.bun/@gobing-ai+ts-dual-workflow-engine@0.4.16+eb8900f44f4776f9/node_modules/@gobing-ai/ts-dual-workflow-engine/dist/config.js
52871
+ // ../../node_modules/.bun/@gobing-ai+ts-dual-workflow-engine@0.4.18+eb8900f44f4776f9/node_modules/@gobing-ai/ts-dual-workflow-engine/dist/config.js
52415
52872
  async function loadWorkflowDef(path9, options = {}) {
52416
52873
  return parseWorkflowDef(await loadStructuredConfig(path9, options), path9);
52417
52874
  }
@@ -52427,8 +52884,16 @@ function validateStateMachine(workflow) {
52427
52884
  const ids = workflow.states.map((state) => state.id);
52428
52885
  const states = new Set(ids);
52429
52886
  const terminals = new Set(workflow.terminalStates ?? []);
52887
+ const failures = new Set(workflow.failureStates ?? []);
52430
52888
  for (const id of duplicates(ids))
52431
52889
  errors6.push(`State "${id}" is declared more than once`);
52890
+ for (const failure of failures) {
52891
+ if (!states.has(failure))
52892
+ errors6.push(`Failure state "${failure}" is not declared`);
52893
+ else if (!terminals.has(failure)) {
52894
+ errors6.push(`Failure state "${failure}" must also be declared as a terminal state`);
52895
+ }
52896
+ }
52432
52897
  if (!states.has(workflow.initialState)) {
52433
52898
  errors6.push(`Initial state "${workflow.initialState}" is not declared`);
52434
52899
  } else if (terminals.has(workflow.initialState)) {
@@ -52570,7 +53035,7 @@ var init_config4 = __esm(() => {
52570
53035
  RUNTIME_TEMPLATE_NAMESPACES = new Set(RUNTIME_BUILTIN_KEYS);
52571
53036
  });
52572
53037
 
52573
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.16+d8250d61be64a872/node_modules/@gobing-ai/ts-runtime/dist/extension/capability-registry.js
53038
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.18+0c3519262b4dbb18/node_modules/@gobing-ai/ts-runtime/dist/extension/capability-registry.js
52574
53039
  class CapabilityRegistry {
52575
53040
  kind;
52576
53041
  capabilities = new Map;
@@ -52601,7 +53066,7 @@ class CapabilityRegistry {
52601
53066
  }
52602
53067
  }
52603
53068
 
52604
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.16+d8250d61be64a872/node_modules/@gobing-ai/ts-runtime/dist/extension/extension-path.js
53069
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.18+0c3519262b4dbb18/node_modules/@gobing-ai/ts-runtime/dist/extension/extension-path.js
52605
53070
  function assertRelativeExtensionPath(path9, options = {}) {
52606
53071
  const where = options.sourceName !== undefined ? ` declared by "${options.sourceName}"` : "";
52607
53072
  if (/^([/\\]|[A-Za-z]:[/\\])/.test(path9)) {
@@ -52612,20 +53077,20 @@ function assertRelativeExtensionPath(path9, options = {}) {
52612
53077
  }
52613
53078
  }
52614
53079
 
52615
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.16+d8250d61be64a872/node_modules/@gobing-ai/ts-runtime/dist/extension/extension-loader.js
53080
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.18+0c3519262b4dbb18/node_modules/@gobing-ai/ts-runtime/dist/extension/extension-loader.js
52616
53081
  var init_extension_loader = () => {};
52617
53082
 
52618
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.16+d8250d61be64a872/node_modules/@gobing-ai/ts-runtime/dist/extension/index.js
53083
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.18+0c3519262b4dbb18/node_modules/@gobing-ai/ts-runtime/dist/extension/index.js
52619
53084
  var init_extension = __esm(() => {
52620
53085
  init_extension_loader();
52621
53086
  });
52622
53087
 
52623
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.16+d8250d61be64a872/node_modules/@gobing-ai/ts-runtime/dist/extension.js
53088
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.4.18+0c3519262b4dbb18/node_modules/@gobing-ai/ts-runtime/dist/extension.js
52624
53089
  var init_extension2 = __esm(() => {
52625
53090
  init_extension();
52626
53091
  });
52627
53092
 
52628
- // ../../node_modules/.bun/@gobing-ai+ts-dual-workflow-engine@0.4.16+eb8900f44f4776f9/node_modules/@gobing-ai/ts-dual-workflow-engine/dist/host.js
53093
+ // ../../node_modules/.bun/@gobing-ai+ts-dual-workflow-engine@0.4.18+eb8900f44f4776f9/node_modules/@gobing-ai/ts-dual-workflow-engine/dist/host.js
52629
53094
  class WorkflowEngineHost {
52630
53095
  lifecycleBus;
52631
53096
  actions = new CapabilityRegistry("workflow action");
@@ -52794,7 +53259,7 @@ var init_host = __esm(() => {
52794
53259
  init_errors6();
52795
53260
  });
52796
53261
 
52797
- // ../../node_modules/.bun/@gobing-ai+ts-dual-workflow-engine@0.4.16+eb8900f44f4776f9/node_modules/@gobing-ai/ts-dual-workflow-engine/dist/schema-sql.js
53262
+ // ../../node_modules/.bun/@gobing-ai+ts-dual-workflow-engine@0.4.18+eb8900f44f4776f9/node_modules/@gobing-ai/ts-dual-workflow-engine/dist/schema-sql.js
52798
53263
  var WORKFLOW_ENGINE_SCHEMA_SQL;
52799
53264
  var init_schema_sql = __esm(() => {
52800
53265
  WORKFLOW_ENGINE_SCHEMA_SQL = `
@@ -52867,7 +53332,7 @@ CREATE TABLE IF NOT EXISTS action_runs (
52867
53332
  `.trim();
52868
53333
  });
52869
53334
 
52870
- // ../../node_modules/.bun/@gobing-ai+ts-dual-workflow-engine@0.4.16+eb8900f44f4776f9/node_modules/@gobing-ai/ts-dual-workflow-engine/dist/persistence.js
53335
+ // ../../node_modules/.bun/@gobing-ai+ts-dual-workflow-engine@0.4.18+eb8900f44f4776f9/node_modules/@gobing-ai/ts-dual-workflow-engine/dist/persistence.js
52871
53336
  async function applyWorkflowEngineSchema(db2) {
52872
53337
  for (const statement of WORKFLOW_ENGINE_SCHEMA_SQL.split(";")) {
52873
53338
  const sql4 = statement.trim();
@@ -52911,17 +53376,17 @@ class DbWorkflowPersistenceAdapter {
52911
53376
  }
52912
53377
  async commitTransition(runId, from, to, trigger, state, data, phase) {
52913
53378
  await this.ensureSchema();
52914
- const now = Date.now();
53379
+ const now2 = Date.now();
52915
53380
  const ops = [
52916
- this.transitionRow(runId, from, to, trigger, now),
52917
- this.stateRow(runId, state, data, now)
53381
+ this.transitionRow(runId, from, to, trigger, now2),
53382
+ this.stateRow(runId, state, data, now2)
52918
53383
  ];
52919
53384
  if (phase) {
52920
- ops.push(this.phaseRow(runId, phase.phase, phase.status, now));
53385
+ ops.push(this.phaseRow(runId, phase.phase, phase.status, now2));
52921
53386
  }
52922
53387
  await this.db.batch(ops);
52923
53388
  }
52924
- transitionRow(runId, from, to, trigger, now) {
53389
+ transitionRow(runId, from, to, trigger, now2) {
52925
53390
  return {
52926
53391
  sql: `INSERT INTO transition_runs (id, run_id, from_state, to_state, trigger, status, created_at, updated_at)
52927
53392
  VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
@@ -52932,19 +53397,19 @@ class DbWorkflowPersistenceAdapter {
52932
53397
  to,
52933
53398
  trigger,
52934
53399
  "done",
52935
- now,
52936
- now
53400
+ now2,
53401
+ now2
52937
53402
  ]
52938
53403
  };
52939
53404
  }
52940
- stateRow(runId, state, data, now) {
53405
+ stateRow(runId, state, data, now2) {
52941
53406
  return {
52942
53407
  sql: `INSERT INTO workflow_states (id, run_id, state, data_json, created_at, updated_at)
52943
53408
  VALUES (?, ?, ?, ?, ?, ?)`,
52944
- params: [`${runId}:state:${state}:${crypto.randomUUID()}`, runId, state, JSON.stringify(data), now, now]
53409
+ params: [`${runId}:state:${state}:${crypto.randomUUID()}`, runId, state, JSON.stringify(data), now2, now2]
52945
53410
  };
52946
53411
  }
52947
- phaseRow(runId, phase, status, now) {
53412
+ phaseRow(runId, phase, status, now2) {
52948
53413
  return {
52949
53414
  sql: `INSERT INTO phase_runs (id, run_id, phase, status, started_at, completed_at, created_at, updated_at)
52950
53415
  VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
@@ -52953,25 +53418,25 @@ class DbWorkflowPersistenceAdapter {
52953
53418
  runId,
52954
53419
  phase,
52955
53420
  status,
52956
- new Date(now).toISOString(),
52957
- status === "running" ? null : new Date(now).toISOString(),
52958
- now,
52959
- now
53421
+ new Date(now2).toISOString(),
53422
+ status === "running" ? null : new Date(now2).toISOString(),
53423
+ now2,
53424
+ now2
52960
53425
  ]
52961
53426
  };
52962
53427
  }
52963
53428
  async saveActionStart(runId, node, kind, _options) {
52964
53429
  const id = crypto.randomUUID();
52965
- const now = Date.now();
53430
+ const now2 = Date.now();
52966
53431
  await this.db.run(`INSERT INTO action_runs (id, run_id, node, kind, status, started_at, created_at, updated_at)
52967
- VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, id, runId, node, kind, "running", new Date(now).toISOString(), now, now);
53432
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, id, runId, node, kind, "running", new Date(now2).toISOString(), now2, now2);
52968
53433
  return id;
52969
53434
  }
52970
53435
  async saveActionFinalize(actionId, status, durationMs, ok, result, _redactor) {
52971
- const now = Date.now();
53436
+ const now2 = Date.now();
52972
53437
  await this.db.run(`UPDATE action_runs
52973
53438
  SET status = ?, duration_ms = ?, ok = ?, result_json = ?, completed_at = ?, updated_at = ?
52974
- WHERE id = ?`, status, durationMs, ok ? 1 : 0, result !== undefined ? JSON.stringify(result) : null, new Date(now).toISOString(), now, actionId);
53439
+ WHERE id = ?`, status, durationMs, ok ? 1 : 0, result !== undefined ? JSON.stringify(result) : null, new Date(now2).toISOString(), now2, actionId);
52975
53440
  }
52976
53441
  async loadRun(runId) {
52977
53442
  await this.ensureSchema();
@@ -53007,10 +53472,10 @@ class DbWorkflowPersistenceAdapter {
53007
53472
  return { ...record2 };
53008
53473
  }
53009
53474
  async reseedRun(runId, newState) {
53010
- const now = Date.now();
53475
+ const now2 = Date.now();
53011
53476
  await this.ensureSchema();
53012
53477
  const previous = await this.db.queryFirst("SELECT state FROM workflow_states WHERE run_id = ? ORDER BY created_at DESC, rowid DESC LIMIT 1", runId);
53013
- await this.saveWorkflowState(runId, newState, { reseeded: true, reseededAt: new Date(now).toISOString() });
53478
+ await this.saveWorkflowState(runId, newState, { reseeded: true, reseededAt: new Date(now2).toISOString() });
53014
53479
  await this.saveTransition(runId, previous?.state ?? "", newState, "__reseed__");
53015
53480
  return { fromState: previous?.state ?? null, toState: newState };
53016
53481
  }
@@ -53156,7 +53621,7 @@ var init_persistence = __esm(() => {
53156
53621
  init_schema_sql();
53157
53622
  });
53158
53623
 
53159
- // ../../node_modules/.bun/@gobing-ai+ts-dual-workflow-engine@0.4.16+eb8900f44f4776f9/node_modules/@gobing-ai/ts-dual-workflow-engine/dist/variables.js
53624
+ // ../../node_modules/.bun/@gobing-ai+ts-dual-workflow-engine@0.4.18+eb8900f44f4776f9/node_modules/@gobing-ai/ts-dual-workflow-engine/dist/variables.js
53160
53625
  function mergeVars(workflowVars = {}, overrideVars = {}) {
53161
53626
  return { ...workflowVars, ...overrideVars };
53162
53627
  }
@@ -53216,7 +53681,7 @@ var init_variables = __esm(() => {
53216
53681
  TEMPLATE_REF = /\$\{([^}]+)\}/g;
53217
53682
  });
53218
53683
 
53219
- // ../../node_modules/.bun/@gobing-ai+ts-dual-workflow-engine@0.4.16+eb8900f44f4776f9/node_modules/@gobing-ai/ts-dual-workflow-engine/dist/action-step.js
53684
+ // ../../node_modules/.bun/@gobing-ai+ts-dual-workflow-engine@0.4.18+eb8900f44f4776f9/node_modules/@gobing-ai/ts-dual-workflow-engine/dist/action-step.js
53220
53685
  async function runActionStep(action, vars, deps) {
53221
53686
  const { host, persistence, lifecycle, stateOrNodeId, runId } = deps;
53222
53687
  const resolved = resolveTemplates(action.options ?? {}, {
@@ -53274,7 +53739,7 @@ var init_action_step = __esm(() => {
53274
53739
  init_variables();
53275
53740
  });
53276
53741
 
53277
- // ../../node_modules/.bun/@gobing-ai+ts-dual-workflow-engine@0.4.16+eb8900f44f4776f9/node_modules/@gobing-ai/ts-dual-workflow-engine/dist/state-machine.js
53742
+ // ../../node_modules/.bun/@gobing-ai+ts-dual-workflow-engine@0.4.18+eb8900f44f4776f9/node_modules/@gobing-ai/ts-dual-workflow-engine/dist/state-machine.js
53278
53743
  class StateMachineDriver {
53279
53744
  options;
53280
53745
  constructor(options) {
@@ -53290,6 +53755,7 @@ class StateMachineDriver {
53290
53755
  const runId = lifecycle.runId;
53291
53756
  const states = new Map(workflow.states.map((state) => [state.id, state]));
53292
53757
  const terminal = new Set(workflow.terminalStates ?? []);
53758
+ const failure = new Set(workflow.failureStates ?? []);
53293
53759
  let vars = mergeVars(workflow.vars, options.vars);
53294
53760
  const env = allowedEnv(workflow.env?.allow ?? [], options.env);
53295
53761
  let current = resumeFromState !== undefined ? states.get(resumeFromState) : states.get(workflow.initialState);
@@ -53327,7 +53793,7 @@ class StateMachineDriver {
53327
53793
  if (enter.result?.setVars)
53328
53794
  vars = mergeSetVars(vars, enter.result.setVars);
53329
53795
  if (enter.outcome === "terminal") {
53330
- return await lifecycle.done(current.id, transitionsTaken);
53796
+ return failure.has(current.id) ? await lifecycle.fail(current.id, transitionsTaken, `terminal:${current.id}`) : await lifecycle.done(current.id, transitionsTaken);
53331
53797
  }
53332
53798
  if (enter.outcome === "fail") {
53333
53799
  return await lifecycle.fail(current.id, transitionsTaken, lastActionResult?.error);
@@ -53338,13 +53804,14 @@ class StateMachineDriver {
53338
53804
  }
53339
53805
  const outbound = workflow.transitions.filter((transition) => transition.from === current?.id);
53340
53806
  if (terminal.has(current.id) || outbound.length === 0) {
53341
- return await lifecycle.done(current.id, transitionsTaken);
53807
+ return failure.has(current.id) ? await lifecycle.fail(current.id, transitionsTaken, `terminal:${current.id}`) : await lifecycle.done(current.id, transitionsTaken);
53342
53808
  }
53343
53809
  const nextTransition = await firstPassingTransition(outbound, this.options.host, {
53344
53810
  runId,
53345
53811
  current: current.id,
53346
53812
  vars,
53347
- lastActionResult
53813
+ lastActionResult,
53814
+ workdir: options.workdir
53348
53815
  }, lifecycle);
53349
53816
  if (nextTransition === undefined) {
53350
53817
  return await lifecycle.fail(current.id, transitionsTaken, "no-passing-transition");
@@ -53408,7 +53875,7 @@ var init_state_machine = __esm(() => {
53408
53875
  EMPTY_OUTCOME = { outcome: "completed", result: undefined };
53409
53876
  });
53410
53877
 
53411
- // ../../node_modules/.bun/@gobing-ai+ts-dual-workflow-engine@0.4.16+eb8900f44f4776f9/node_modules/@gobing-ai/ts-dual-workflow-engine/dist/transition-flow.js
53878
+ // ../../node_modules/.bun/@gobing-ai+ts-dual-workflow-engine@0.4.18+eb8900f44f4776f9/node_modules/@gobing-ai/ts-dual-workflow-engine/dist/transition-flow.js
53412
53879
  class TransitionFlowDriver {
53413
53880
  options;
53414
53881
  constructor(options) {
@@ -53483,7 +53950,8 @@ class TransitionFlowDriver {
53483
53950
  runId,
53484
53951
  current: current.id,
53485
53952
  vars,
53486
- lastActionResult
53953
+ lastActionResult,
53954
+ workdir: options.workdir
53487
53955
  }, lifecycle);
53488
53956
  if (edge === undefined) {
53489
53957
  return await lifecycle.fail(current.id, transitionsTaken, "no-passing-edge");
@@ -53526,7 +53994,7 @@ var init_transition_flow = __esm(() => {
53526
53994
  init_variables();
53527
53995
  });
53528
53996
 
53529
- // ../../node_modules/.bun/@gobing-ai+ts-dual-workflow-engine@0.4.16+eb8900f44f4776f9/node_modules/@gobing-ai/ts-dual-workflow-engine/dist/service.js
53997
+ // ../../node_modules/.bun/@gobing-ai+ts-dual-workflow-engine@0.4.18+eb8900f44f4776f9/node_modules/@gobing-ai/ts-dual-workflow-engine/dist/service.js
53530
53998
  class WorkflowService {
53531
53999
  host;
53532
54000
  persistence;
@@ -53743,7 +54211,7 @@ var init_service = __esm(() => {
53743
54211
  init_variables();
53744
54212
  });
53745
54213
 
53746
- // ../../node_modules/.bun/@gobing-ai+ts-dual-workflow-engine@0.4.16+eb8900f44f4776f9/node_modules/@gobing-ai/ts-dual-workflow-engine/dist/index.js
54214
+ // ../../node_modules/.bun/@gobing-ai+ts-dual-workflow-engine@0.4.18+eb8900f44f4776f9/node_modules/@gobing-ai/ts-dual-workflow-engine/dist/index.js
53747
54215
  var init_dist7 = __esm(() => {
53748
54216
  init_config4();
53749
54217
  init_host();
@@ -53752,7 +54220,7 @@ var init_dist7 = __esm(() => {
53752
54220
  init_service();
53753
54221
  });
53754
54222
 
53755
- // ../../node_modules/.bun/@gobing-ai+ts-llm-jsonl-importer@0.4.16+d5c29f764bf0e6e0/node_modules/@gobing-ai/ts-llm-jsonl-importer/dist/hash.js
54223
+ // ../../node_modules/.bun/@gobing-ai+ts-llm-jsonl-importer@0.4.18+d5c29f764bf0e6e0/node_modules/@gobing-ai/ts-llm-jsonl-importer/dist/hash.js
53756
54224
  import { createHash } from "crypto";
53757
54225
  function stableJson(value) {
53758
54226
  if (Array.isArray(value)) {
@@ -53769,7 +54237,7 @@ function sha256(value) {
53769
54237
  }
53770
54238
  var init_hash = () => {};
53771
54239
 
53772
- // ../../node_modules/.bun/@gobing-ai+ts-llm-jsonl-importer@0.4.16+d5c29f764bf0e6e0/node_modules/@gobing-ai/ts-llm-jsonl-importer/dist/errors.js
54240
+ // ../../node_modules/.bun/@gobing-ai+ts-llm-jsonl-importer@0.4.18+d5c29f764bf0e6e0/node_modules/@gobing-ai/ts-llm-jsonl-importer/dist/errors.js
53773
54241
  var HistoryImportError;
53774
54242
  var init_errors7 = __esm(() => {
53775
54243
  HistoryImportError = class HistoryImportError extends Error {
@@ -53782,7 +54250,7 @@ var init_errors7 = __esm(() => {
53782
54250
  };
53783
54251
  });
53784
54252
 
53785
- // ../../node_modules/.bun/@gobing-ai+ts-llm-jsonl-importer@0.4.16+d5c29f764bf0e6e0/node_modules/@gobing-ai/ts-llm-jsonl-importer/dist/schema-sql.js
54253
+ // ../../node_modules/.bun/@gobing-ai+ts-llm-jsonl-importer@0.4.18+d5c29f764bf0e6e0/node_modules/@gobing-ai/ts-llm-jsonl-importer/dist/schema-sql.js
53786
54254
  var HISTORY_IMPORT_SCHEMA_SQL;
53787
54255
  var init_schema_sql2 = __esm(() => {
53788
54256
  HISTORY_IMPORT_SCHEMA_SQL = `
@@ -53869,7 +54337,7 @@ CREATE TABLE IF NOT EXISTS history_etl_openclaw (
53869
54337
  `.trim();
53870
54338
  });
53871
54339
 
53872
- // ../../node_modules/.bun/@gobing-ai+ts-llm-jsonl-importer@0.4.16+d5c29f764bf0e6e0/node_modules/@gobing-ai/ts-llm-jsonl-importer/dist/sources.js
54340
+ // ../../node_modules/.bun/@gobing-ai+ts-llm-jsonl-importer@0.4.18+d5c29f764bf0e6e0/node_modules/@gobing-ai/ts-llm-jsonl-importer/dist/sources.js
53873
54341
  function firstString(...values) {
53874
54342
  for (const value of values) {
53875
54343
  if (typeof value === "string" && value.length > 0)
@@ -54006,15 +54474,15 @@ var init_sources = __esm(() => {
54006
54474
  };
54007
54475
  });
54008
54476
 
54009
- // ../../node_modules/.bun/@gobing-ai+ts-llm-jsonl-importer@0.4.16+d5c29f764bf0e6e0/node_modules/@gobing-ai/ts-llm-jsonl-importer/dist/jsonl-importer-dao.js
54477
+ // ../../node_modules/.bun/@gobing-ai+ts-llm-jsonl-importer@0.4.18+d5c29f764bf0e6e0/node_modules/@gobing-ai/ts-llm-jsonl-importer/dist/jsonl-importer-dao.js
54010
54478
  function targetTableFor(table3) {
54011
54479
  if (!VALID_TABLE_NAME.test(table3)) {
54012
54480
  throw new HistoryImportError(`Invalid history ETL target table: ${table3}`, { table: table3 });
54013
54481
  }
54014
54482
  return table3;
54015
54483
  }
54016
- function timestamp(now) {
54017
- return (now?.() ?? new Date).toISOString();
54484
+ function timestamp(now2) {
54485
+ return (now2?.() ?? new Date).toISOString();
54018
54486
  }
54019
54487
  function ETL_TABLE_DDL(table3) {
54020
54488
  return `CREATE TABLE IF NOT EXISTS ${table3} (
@@ -54052,26 +54520,26 @@ async function resetCheckpoints(db2, source, files) {
54052
54520
  await db2.run("DELETE FROM history_import_checkpoint WHERE source = ? AND source_file = ?", source, file2);
54053
54521
  }
54054
54522
  }
54055
- async function writeCheckpoint(db2, source, sourceFile, line, now) {
54523
+ async function writeCheckpoint(db2, source, sourceFile, line, now2) {
54056
54524
  await db2.run(`INSERT INTO history_import_checkpoint (source, source_file, last_imported_line, updated_at)
54057
54525
  VALUES (?, ?, ?, ?)
54058
54526
  ON CONFLICT(source, source_file) DO UPDATE SET
54059
54527
  last_imported_line = excluded.last_imported_line,
54060
- updated_at = excluded.updated_at`, source, sourceFile, line, timestamp(now));
54528
+ updated_at = excluded.updated_at`, source, sourceFile, line, timestamp(now2));
54061
54529
  }
54062
54530
  async function ledgerExists(db2, recordHash) {
54063
54531
  const row = await db2.queryFirst("SELECT record_hash FROM history_import_ledger WHERE record_hash = ?", recordHash);
54064
54532
  return row !== undefined && row !== null;
54065
54533
  }
54066
- async function insertRecord(db2, targetTable, recordHash, sourceFile, sourceLine, splitIndex, payload, now) {
54534
+ async function insertRecord(db2, targetTable, recordHash, sourceFile, sourceLine, splitIndex, payload, now2) {
54067
54535
  const table3 = targetTableFor(targetTable);
54068
54536
  await db2.run(`INSERT INTO ${table3} (record_hash, source_file, source_line, split_index, payload_json, imported_at)
54069
54537
  VALUES (?, ?, ?, ?, ?, ?)
54070
- ON CONFLICT(record_hash) DO NOTHING`, recordHash, sourceFile, sourceLine, splitIndex, JSON.stringify(payload), timestamp(now));
54538
+ ON CONFLICT(record_hash) DO NOTHING`, recordHash, sourceFile, sourceLine, splitIndex, JSON.stringify(payload), timestamp(now2));
54071
54539
  }
54072
- async function insertLedger(db2, recordHash, source, sourceFile, sourceLine, splitIndex, targetTable, now) {
54540
+ async function insertLedger(db2, recordHash, source, sourceFile, sourceLine, splitIndex, targetTable, now2) {
54073
54541
  await db2.run(`INSERT INTO history_import_ledger (record_hash, source, source_file, source_line, split_index, target_table, imported_at)
54074
- VALUES (?, ?, ?, ?, ?, ?, ?)`, recordHash, source, sourceFile, sourceLine, splitIndex, targetTable, timestamp(now));
54542
+ VALUES (?, ?, ?, ?, ?, ?, ?)`, recordHash, source, sourceFile, sourceLine, splitIndex, targetTable, timestamp(now2));
54075
54543
  }
54076
54544
  var init_jsonl_importer_dao = __esm(() => {
54077
54545
  init_errors7();
@@ -54079,7 +54547,7 @@ var init_jsonl_importer_dao = __esm(() => {
54079
54547
  init_sources();
54080
54548
  });
54081
54549
 
54082
- // ../../node_modules/.bun/@gobing-ai+ts-llm-jsonl-importer@0.4.16+d5c29f764bf0e6e0/node_modules/@gobing-ai/ts-llm-jsonl-importer/dist/redaction.js
54550
+ // ../../node_modules/.bun/@gobing-ai+ts-llm-jsonl-importer@0.4.18+d5c29f764bf0e6e0/node_modules/@gobing-ai/ts-llm-jsonl-importer/dist/redaction.js
54083
54551
  function redactValue(value, rules = DEFAULT_REDACTION_RULES) {
54084
54552
  if (typeof value === "string") {
54085
54553
  return rules.reduce((current, rule) => current.replace(rule.pattern, rule.replacement), value);
@@ -54116,7 +54584,7 @@ var init_redaction = __esm(() => {
54116
54584
  ];
54117
54585
  });
54118
54586
 
54119
- // ../../node_modules/.bun/@gobing-ai+ts-llm-jsonl-importer@0.4.16+d5c29f764bf0e6e0/node_modules/@gobing-ai/ts-llm-jsonl-importer/dist/importer.js
54587
+ // ../../node_modules/.bun/@gobing-ai+ts-llm-jsonl-importer@0.4.18+d5c29f764bf0e6e0/node_modules/@gobing-ai/ts-llm-jsonl-importer/dist/importer.js
54120
54588
  async function runJsonlImport(source, options) {
54121
54589
  const definition = resolveSourceDefinition(source);
54122
54590
  const resolvedSource = definition.source;
@@ -54306,13 +54774,13 @@ var init_importer = __esm(() => {
54306
54774
  init_sources();
54307
54775
  });
54308
54776
 
54309
- // ../../node_modules/.bun/@gobing-ai+ts-llm-jsonl-importer@0.4.16+d5c29f764bf0e6e0/node_modules/@gobing-ai/ts-llm-jsonl-importer/dist/index.js
54777
+ // ../../node_modules/.bun/@gobing-ai+ts-llm-jsonl-importer@0.4.18+d5c29f764bf0e6e0/node_modules/@gobing-ai/ts-llm-jsonl-importer/dist/index.js
54310
54778
  var init_dist8 = __esm(() => {
54311
54779
  init_importer();
54312
54780
  init_schema_sql2();
54313
54781
  });
54314
54782
 
54315
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.16+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/config/extensions.js
54783
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.18+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/config/extensions.js
54316
54784
  function collectExtensions(sourceName, sourceDir, extensions) {
54317
54785
  if (extensions === undefined)
54318
54786
  return [];
@@ -54328,7 +54796,7 @@ var init_extensions = __esm(() => {
54328
54796
  init_dist5();
54329
54797
  });
54330
54798
 
54331
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.16+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/types.js
54799
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.18+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/types.js
54332
54800
  function createFinding(rule, message, filePath, extras = {}) {
54333
54801
  return {
54334
54802
  ruleId: rule.id,
@@ -54399,7 +54867,7 @@ var init_types3 = __esm(() => {
54399
54867
  });
54400
54868
  });
54401
54869
 
54402
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.16+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/config/loader.js
54870
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.18+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/config/loader.js
54403
54871
  async function loadPreset(name, options) {
54404
54872
  const merged = await buildMergedRoots(options.roots.map((root) => resolvePath4(root)), options.fileSystem);
54405
54873
  const presetPath = findMergedPreset(merged, name);
@@ -54614,7 +55082,7 @@ var init_loader2 = __esm(() => {
54614
55082
  FIX_MODE_AUTHORITY = { none: 0, suggest: 1, auto: 2 };
54615
55083
  });
54616
55084
 
54617
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.16+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/fixers/test-stub-fixer.js
55085
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.18+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/fixers/test-stub-fixer.js
54618
55086
  function normalizeFindingPath(filePath) {
54619
55087
  if (!filePath || isAbsolutePath2(filePath))
54620
55088
  return null;
@@ -54679,7 +55147,7 @@ var init_test_stub_fixer = __esm(() => {
54679
55147
  realFileSystem = createNodeFileSystem3();
54680
55148
  });
54681
55149
 
54682
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.16+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/fixers/fixers.js
55150
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.18+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/fixers/fixers.js
54683
55151
  function registerBuiltinFixers(host, exec) {
54684
55152
  const regexFixer = new RegexFixerProvider;
54685
55153
  const pathFixer = new PathFixerProvider;
@@ -54882,7 +55350,7 @@ var init_fixers = __esm(() => {
54882
55350
  };
54883
55351
  });
54884
55352
 
54885
- // ../../node_modules/.bun/@gobing-ai+ts-ai-runner@0.4.16+ae22402965cb65aa/node_modules/@gobing-ai/ts-ai-runner/dist/agents/shims.js
55353
+ // ../../node_modules/.bun/@gobing-ai+ts-ai-runner@0.4.18+aa81d3e2be8bffb2/node_modules/@gobing-ai/ts-ai-runner/dist/agents/shims.js
54886
55354
  function resolveAgentName(input) {
54887
55355
  if (isCanonicalName(input))
54888
55356
  return input;
@@ -55133,7 +55601,7 @@ var init_shims = __esm(() => {
55133
55601
  ALIAS_TO_CANONICAL = Object.fromEntries(Object.values(AGENT_SHIMS).flatMap((shim) => (shim.aliases ?? []).map((alias3) => [alias3, shim.name])));
55134
55602
  });
55135
55603
 
55136
- // ../../node_modules/.bun/@gobing-ai+ts-ai-runner@0.4.16+ae22402965cb65aa/node_modules/@gobing-ai/ts-ai-runner/dist/identity.js
55604
+ // ../../node_modules/.bun/@gobing-ai+ts-ai-runner@0.4.18+aa81d3e2be8bffb2/node_modules/@gobing-ai/ts-ai-runner/dist/identity.js
55137
55605
  function buildIdentityPreamble(ctx) {
55138
55606
  const sections = [
55139
55607
  `You are agent \`${ctx.agentId}\` (${ctx.agentType}) in workspace \`${ctx.workspace}\`.`
@@ -55193,7 +55661,7 @@ var init_identity = __esm(() => {
55193
55661
  init_dist5();
55194
55662
  });
55195
55663
 
55196
- // ../../node_modules/.bun/@gobing-ai+ts-ai-runner@0.4.16+ae22402965cb65aa/node_modules/@gobing-ai/ts-ai-runner/dist/slash-command.js
55664
+ // ../../node_modules/.bun/@gobing-ai+ts-ai-runner@0.4.18+aa81d3e2be8bffb2/node_modules/@gobing-ai/ts-ai-runner/dist/slash-command.js
55197
55665
  function isClaudeStyleSlashCommand(input) {
55198
55666
  return SLASH_COMMAND_RE.test(input);
55199
55667
  }
@@ -55221,7 +55689,7 @@ var init_slash_command = __esm(() => {
55221
55689
  SLASH_COMMAND_RE = /^\/([a-zA-Z0-9._-]+):([a-zA-Z0-9._-]+)(\s.*)?$/;
55222
55690
  });
55223
55691
 
55224
- // ../../node_modules/.bun/@gobing-ai+ts-ai-runner@0.4.16+ae22402965cb65aa/node_modules/@gobing-ai/ts-ai-runner/dist/ai-runner.js
55692
+ // ../../node_modules/.bun/@gobing-ai+ts-ai-runner@0.4.18+aa81d3e2be8bffb2/node_modules/@gobing-ai/ts-ai-runner/dist/ai-runner.js
55225
55693
  function buildCorrelationEnv(correlation) {
55226
55694
  return {
55227
55695
  [AGENT_RUN_ID_ENV]: correlation.runId,
@@ -55357,7 +55825,7 @@ var init_ai_runner = __esm(() => {
55357
55825
  init_slash_command();
55358
55826
  });
55359
55827
 
55360
- // ../../node_modules/.bun/@gobing-ai+ts-ai-runner@0.4.16+ae22402965cb65aa/node_modules/@gobing-ai/ts-ai-runner/dist/agent-detector.js
55828
+ // ../../node_modules/.bun/@gobing-ai+ts-ai-runner@0.4.18+aa81d3e2be8bffb2/node_modules/@gobing-ai/ts-ai-runner/dist/agent-detector.js
55361
55829
  class AgentDetector {
55362
55830
  runner;
55363
55831
  timeout;
@@ -55433,7 +55901,7 @@ var init_agent_detector = __esm(() => {
55433
55901
  VERSION_PATTERN = /(?<version>\d+\.\d+(?:\.\d+)?)/;
55434
55902
  });
55435
55903
 
55436
- // ../../node_modules/.bun/@gobing-ai+ts-ai-runner@0.4.16+ae22402965cb65aa/node_modules/@gobing-ai/ts-ai-runner/dist/agent-spec.js
55904
+ // ../../node_modules/.bun/@gobing-ai+ts-ai-runner@0.4.18+aa81d3e2be8bffb2/node_modules/@gobing-ai/ts-ai-runner/dist/agent-spec.js
55437
55905
  function validateAgentId(id) {
55438
55906
  if (!/^[a-z][a-z0-9_-]{1,63}$/.test(id)) {
55439
55907
  throw new ValueError(`Invalid agent id "${id}": expected 2-64 chars, lowercase alphanumeric, "_" or "-"`);
@@ -55530,7 +55998,7 @@ var init_agent_spec = __esm(() => {
55530
55998
  };
55531
55999
  });
55532
56000
 
55533
- // ../../node_modules/.bun/@gobing-ai+ts-ai-runner@0.4.16+ae22402965cb65aa/node_modules/@gobing-ai/ts-ai-runner/dist/agents/auth-shims.js
56001
+ // ../../node_modules/.bun/@gobing-ai+ts-ai-runner@0.4.18+aa81d3e2be8bffb2/node_modules/@gobing-ai/ts-ai-runner/dist/agents/auth-shims.js
55534
56002
  function isNonEmpty(value) {
55535
56003
  return value !== undefined && value.trim().length > 0;
55536
56004
  }
@@ -55635,7 +56103,7 @@ var init_auth_shims = __esm(() => {
55635
56103
  };
55636
56104
  });
55637
56105
 
55638
- // ../../node_modules/.bun/@gobing-ai+ts-ai-runner@0.4.16+ae22402965cb65aa/node_modules/@gobing-ai/ts-ai-runner/dist/model-health-probe.js
56106
+ // ../../node_modules/.bun/@gobing-ai+ts-ai-runner@0.4.18+aa81d3e2be8bffb2/node_modules/@gobing-ai/ts-ai-runner/dist/model-health-probe.js
55639
56107
  class ModelHealthProbeRegistry {
55640
56108
  probes = new Map;
55641
56109
  register(providerPrefix, probe) {
@@ -55743,7 +56211,7 @@ var init_model_health_probe = __esm(() => {
55743
56211
  };
55744
56212
  });
55745
56213
 
55746
- // ../../node_modules/.bun/@gobing-ai+ts-ai-runner@0.4.16+ae22402965cb65aa/node_modules/@gobing-ai/ts-ai-runner/dist/doctor-runner.js
56214
+ // ../../node_modules/.bun/@gobing-ai+ts-ai-runner@0.4.18+aa81d3e2be8bffb2/node_modules/@gobing-ai/ts-ai-runner/dist/doctor-runner.js
55747
56215
  class DoctorRunner {
55748
56216
  detector;
55749
56217
  runner;
@@ -55880,15 +56348,15 @@ var init_doctor_runner = __esm(() => {
55880
56348
  init_model_health_probe();
55881
56349
  });
55882
56350
 
55883
- // ../../node_modules/.bun/@gobing-ai+ts-ai-runner@0.4.16+ae22402965cb65aa/node_modules/@gobing-ai/ts-ai-runner/dist/events.js
56351
+ // ../../node_modules/.bun/@gobing-ai+ts-ai-runner@0.4.18+aa81d3e2be8bffb2/node_modules/@gobing-ai/ts-ai-runner/dist/events.js
55884
56352
  var init_events = () => {};
55885
56353
 
55886
- // ../../node_modules/.bun/@gobing-ai+ts-ai-runner@0.4.16+ae22402965cb65aa/node_modules/@gobing-ai/ts-ai-runner/dist/messages.js
56354
+ // ../../node_modules/.bun/@gobing-ai+ts-ai-runner@0.4.18+aa81d3e2be8bffb2/node_modules/@gobing-ai/ts-ai-runner/dist/messages.js
55887
56355
  function formatMessage(msg) {
55888
56356
  return `[task from=${msg.fromId ?? "operator"} id=${msg.id}] ${msg.body}`;
55889
56357
  }
55890
56358
 
55891
- // ../../node_modules/.bun/@gobing-ai+ts-ai-runner@0.4.16+ae22402965cb65aa/node_modules/@gobing-ai/ts-ai-runner/dist/team-agent-process.js
56359
+ // ../../node_modules/.bun/@gobing-ai+ts-ai-runner@0.4.18+aa81d3e2be8bffb2/node_modules/@gobing-ai/ts-ai-runner/dist/team-agent-process.js
55892
56360
  import { Buffer as Buffer5 } from "buffer";
55893
56361
 
55894
56362
  class TeamAgentProcess {
@@ -56022,7 +56490,7 @@ var init_team_agent_process = __esm(() => {
56022
56490
  init_dist5();
56023
56491
  });
56024
56492
 
56025
- // ../../node_modules/.bun/@gobing-ai+ts-ai-runner@0.4.16+ae22402965cb65aa/node_modules/@gobing-ai/ts-ai-runner/dist/team-orchestrator.js
56493
+ // ../../node_modules/.bun/@gobing-ai+ts-ai-runner@0.4.18+aa81d3e2be8bffb2/node_modules/@gobing-ai/ts-ai-runner/dist/team-orchestrator.js
56026
56494
  class TeamOrchestrator {
56027
56495
  configDir;
56028
56496
  inbox;
@@ -56151,7 +56619,7 @@ var init_team_orchestrator = __esm(() => {
56151
56619
  init_team_agent_process();
56152
56620
  });
56153
56621
 
56154
- // ../../node_modules/.bun/@gobing-ai+ts-ai-runner@0.4.16+ae22402965cb65aa/node_modules/@gobing-ai/ts-ai-runner/dist/index.js
56622
+ // ../../node_modules/.bun/@gobing-ai+ts-ai-runner@0.4.18+aa81d3e2be8bffb2/node_modules/@gobing-ai/ts-ai-runner/dist/index.js
56155
56623
  var exports_dist3 = {};
56156
56624
  __export(exports_dist3, {
56157
56625
  validateAgentId: () => validateAgentId,
@@ -56202,7 +56670,7 @@ var init_dist9 = __esm(() => {
56202
56670
  init_team_orchestrator();
56203
56671
  });
56204
56672
 
56205
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.16+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/config-access.js
56673
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.18+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/config-access.js
56206
56674
  function stringArray(value) {
56207
56675
  return Array.isArray(value) && value.every((item) => typeof item === "string") ? value : undefined;
56208
56676
  }
@@ -56240,7 +56708,7 @@ function parseInlineFlags(source) {
56240
56708
  return { flags, rest: source.slice(match[0].length) };
56241
56709
  }
56242
56710
 
56243
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.16+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/glob-match.js
56711
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.18+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/glob-match.js
56244
56712
  function matchesAny(path9, patterns) {
56245
56713
  if (patterns === undefined || patterns.length === 0)
56246
56714
  return true;
@@ -56288,7 +56756,7 @@ function escapeRegExp(value) {
56288
56756
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
56289
56757
  }
56290
56758
 
56291
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.16+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/file-discovery.js
56759
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.18+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/file-discovery.js
56292
56760
  async function discoverFiles2(options) {
56293
56761
  const fs3 = options.fs ?? createNodeFileSystem3();
56294
56762
  const allFiles = await walkDir2(options.workdir, fs3, DEFAULT_EXCLUDES);
@@ -56316,12 +56784,12 @@ var init_file_discovery = __esm(() => {
56316
56784
  DEFAULT_EXCLUDES = new Set([".git", "node_modules", "dist", ".coverage", ".astro", ".wrangler"]);
56317
56785
  });
56318
56786
 
56319
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.16+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/file-utils.js
56787
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.18+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/file-utils.js
56320
56788
  var init_file_utils = __esm(() => {
56321
56789
  init_file_discovery();
56322
56790
  });
56323
56791
 
56324
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.16+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/agent-detection-evaluator.js
56792
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.18+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/agent-detection-evaluator.js
56325
56793
  class AgentDetectionEvaluator {
56326
56794
  detector;
56327
56795
  constructor(detector = new AgentDetector) {
@@ -56349,7 +56817,7 @@ var init_agent_detection_evaluator = __esm(() => {
56349
56817
  init_file_utils();
56350
56818
  });
56351
56819
 
56352
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.16+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/coverage-gate-evaluator.js
56820
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.18+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/coverage-gate-evaluator.js
56353
56821
  class CoverageGateEvaluator {
56354
56822
  constructor() {}
56355
56823
  async evaluate(rule, context4) {
@@ -56433,7 +56901,7 @@ var init_coverage_gate_evaluator = __esm(() => {
56433
56901
  init_file_utils();
56434
56902
  });
56435
56903
 
56436
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.16+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/exit-code-evaluator.js
56904
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.18+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/exit-code-evaluator.js
56437
56905
  class ExitCodeEvaluator {
56438
56906
  executor;
56439
56907
  constructor(executor = nodeBunFactory3.createProcessExecutor()) {
@@ -56469,7 +56937,7 @@ var init_exit_code_evaluator = __esm(() => {
56469
56937
  init_file_utils();
56470
56938
  });
56471
56939
 
56472
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.16+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/forbidden-import-evaluator.js
56940
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.18+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/forbidden-import-evaluator.js
56473
56941
  class ForbiddenImportEvaluator {
56474
56942
  constructor() {}
56475
56943
  async evaluate(rule, context4) {
@@ -56550,7 +57018,7 @@ var init_forbidden_import_evaluator = __esm(() => {
56550
57018
  init_file_utils();
56551
57019
  });
56552
57020
 
56553
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.16+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/import-boundary-evaluator.js
57021
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.18+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/import-boundary-evaluator.js
56554
57022
  class ImportBoundaryEvaluator {
56555
57023
  async evaluate(rule, context4) {
56556
57024
  const config4 = rule.evaluator.config ?? {};
@@ -56643,7 +57111,7 @@ var init_import_boundary_evaluator = __esm(() => {
56643
57111
  init_file_utils();
56644
57112
  });
56645
57113
 
56646
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.16+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/path-evaluator.js
57114
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.18+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/path-evaluator.js
56647
57115
  class PathEvaluator {
56648
57116
  constructor() {}
56649
57117
  async evaluate(rule, context4) {
@@ -56704,7 +57172,7 @@ var init_path_evaluator = __esm(() => {
56704
57172
  init_file_utils();
56705
57173
  });
56706
57174
 
56707
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.16+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/regex-evaluator.js
57175
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.18+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/regex-evaluator.js
56708
57176
  class RegexEvaluator {
56709
57177
  constructor() {}
56710
57178
  async evaluate(rule, context4) {
@@ -56782,7 +57250,7 @@ var init_regex_evaluator = __esm(() => {
56782
57250
  init_file_utils();
56783
57251
  });
56784
57252
 
56785
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.16+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/ripgrep-evaluator.js
57253
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.18+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/ripgrep-evaluator.js
56786
57254
  class RipgrepEvaluator {
56787
57255
  executor;
56788
57256
  constructor(executor = nodeBunFactory3.createProcessExecutor()) {
@@ -56875,7 +57343,7 @@ var init_ripgrep_evaluator = __esm(() => {
56875
57343
  init_file_utils();
56876
57344
  });
56877
57345
 
56878
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.16+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/schema-artifact-evaluator.js
57346
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.18+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/schema-artifact-evaluator.js
56879
57347
  class SchemaArtifactEvaluator {
56880
57348
  constructor() {}
56881
57349
  async evaluate(rule, context4) {
@@ -56953,7 +57421,7 @@ var init_schema_artifact_evaluator = __esm(() => {
56953
57421
  init_file_utils();
56954
57422
  });
56955
57423
 
56956
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.16+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/secrets-scanner-evaluator.js
57424
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.18+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/secrets-scanner-evaluator.js
56957
57425
  class SecretsScannerEvaluator {
56958
57426
  constructor() {}
56959
57427
  async evaluate(rule, context4) {
@@ -57021,7 +57489,7 @@ var init_secrets_scanner_evaluator = __esm(() => {
57021
57489
  ALL_CATEGORIES = Object.keys(BUILTIN_PATTERNS);
57022
57490
  });
57023
57491
 
57024
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.16+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/sg-evaluator.js
57492
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.18+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/sg-evaluator.js
57025
57493
  class SgEvaluator {
57026
57494
  executor;
57027
57495
  constructor(executor = nodeBunFactory3.createProcessExecutor()) {
@@ -57118,7 +57586,7 @@ var init_sg_evaluator = __esm(() => {
57118
57586
  init_file_utils();
57119
57587
  });
57120
57588
 
57121
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.16+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/resolvers/test-path-resolver.js
57589
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.18+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/resolvers/test-path-resolver.js
57122
57590
  class TypeScriptTestPathResolver {
57123
57591
  name = "typescript";
57124
57592
  constructor() {}
@@ -57201,7 +57669,7 @@ function testify(rel) {
57201
57669
  return `${dir}test_${base2}`;
57202
57670
  }
57203
57671
 
57204
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.16+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/test-location-evaluator.js
57672
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.18+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/test-location-evaluator.js
57205
57673
  class TestLocationEvaluator {
57206
57674
  resolvers;
57207
57675
  constructor(resolvers) {
@@ -57276,7 +57744,7 @@ var init_test_location_evaluator = __esm(() => {
57276
57744
  TYPESCRIPT_FALLBACK = new TypeScriptTestPathResolver;
57277
57745
  });
57278
57746
 
57279
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.16+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/tsdoc-export-evaluator.js
57747
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.18+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/evaluators/tsdoc-export-evaluator.js
57280
57748
  class TsdocExportEvaluator {
57281
57749
  constructor() {}
57282
57750
  async evaluate(rule, context4) {
@@ -57361,7 +57829,7 @@ var init_tsdoc_export_evaluator = __esm(() => {
57361
57829
  };
57362
57830
  });
57363
57831
 
57364
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.16+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/formatters/json.js
57832
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.18+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/formatters/json.js
57365
57833
  class JsonFormatter {
57366
57834
  constructor() {}
57367
57835
  format(result) {
@@ -57369,7 +57837,7 @@ class JsonFormatter {
57369
57837
  }
57370
57838
  }
57371
57839
 
57372
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.16+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/formatters/text.js
57840
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.18+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/formatters/text.js
57373
57841
  class TextFormatter {
57374
57842
  constructor() {}
57375
57843
  format(result) {
@@ -57383,7 +57851,7 @@ class TextFormatter {
57383
57851
  }
57384
57852
  }
57385
57853
 
57386
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.16+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/host/builtins.js
57854
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.18+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/host/builtins.js
57387
57855
  function registerBuiltins(host, executor) {
57388
57856
  const path9 = new PathEvaluator;
57389
57857
  host.evaluators.register("regex", new RegexEvaluator, "builtin");
@@ -57423,7 +57891,7 @@ var init_builtins = __esm(() => {
57423
57891
  init_tsdoc_export_evaluator();
57424
57892
  });
57425
57893
 
57426
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.16+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/host/rule-engine-host.js
57894
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.18+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/host/rule-engine-host.js
57427
57895
  class RuleEngineHost {
57428
57896
  evaluators;
57429
57897
  formatters;
@@ -57440,7 +57908,7 @@ var init_rule_engine_host = __esm(() => {
57440
57908
  init_extension2();
57441
57909
  });
57442
57910
 
57443
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.16+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/engine.js
57911
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.18+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/engine.js
57444
57912
  class RuleEngine {
57445
57913
  host;
57446
57914
  events;
@@ -57611,7 +58079,7 @@ var init_engine = __esm(() => {
57611
58079
  init_types3();
57612
58080
  });
57613
58081
 
57614
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.16+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/host/bundled-rules.js
58082
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.18+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/host/bundled-rules.js
57615
58083
  async function bundledRulesRoot() {
57616
58084
  if (cachedRoot2 !== undefined)
57617
58085
  return cachedRoot2;
@@ -57662,41 +58130,41 @@ var init_bundled_rules = __esm(() => {
57662
58130
  init_dist5();
57663
58131
  });
57664
58132
 
57665
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.16+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/persistence/db-adapter.js
58133
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.18+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/persistence/db-adapter.js
57666
58134
  class DbRulePersistenceAdapter {
57667
58135
  db;
57668
58136
  constructor(db2) {
57669
58137
  this.db = db2;
57670
58138
  }
57671
58139
  async insertRun(input) {
57672
- const now = new Date().toISOString();
58140
+ const now2 = new Date().toISOString();
57673
58141
  await this.db.run(`INSERT INTO rule_runs (id, preset, source_kind, source_value, status, rule_count,
57674
58142
  fail_on, stop_on_first, fix_mode, dry_run, metadata_json, started_at, created_at, updated_at)
57675
- VALUES (?, ?, ?, ?, 'running', ?, ?, ?, ?, ?, ?, ?, ?, ?)`, input.id, input.preset ?? null, input.sourceKind, input.sourceValue ?? null, input.ruleCount, input.failOn ?? null, input.stopOnFirst ?? null, input.fixMode, input.dryRun ? 1 : 0, input.metadataJson ?? "{}", now, now, now);
58143
+ VALUES (?, ?, ?, ?, 'running', ?, ?, ?, ?, ?, ?, ?, ?, ?)`, input.id, input.preset ?? null, input.sourceKind, input.sourceValue ?? null, input.ruleCount, input.failOn ?? null, input.stopOnFirst ?? null, input.fixMode, input.dryRun ? 1 : 0, input.metadataJson ?? "{}", now2, now2, now2);
57676
58144
  }
57677
58145
  async updateRunStatus(runId, status, findingCount, fixCount, appliedFixCount, durationMs) {
57678
- const now = new Date().toISOString();
58146
+ const now2 = new Date().toISOString();
57679
58147
  await this.db.run(`UPDATE rule_runs
57680
58148
  SET status = ?, finding_count = ?, fix_count = ?, applied_fix_count = ?,
57681
58149
  duration_ms = ?, completed_at = ?, updated_at = ?
57682
- WHERE id = ?`, status, findingCount, fixCount, appliedFixCount, durationMs, now, now, runId);
58150
+ WHERE id = ?`, status, findingCount, fixCount, appliedFixCount, durationMs, now2, now2, runId);
57683
58151
  }
57684
58152
  async insertEvalRun(input) {
57685
- const now = new Date().toISOString();
58153
+ const now2 = new Date().toISOString();
57686
58154
  await this.db.run(`INSERT INTO rule_eval_runs (id, run_id, rule_id, severity, evaluator, status,
57687
58155
  started_at, created_at, updated_at)
57688
- VALUES (?, ?, ?, ?, ?, 'running', ?, ?, ?)`, input.id, input.runId, input.ruleId, input.severity, input.evaluator, now, now, now);
58156
+ VALUES (?, ?, ?, ?, ?, 'running', ?, ?, ?)`, input.id, input.runId, input.ruleId, input.severity, input.evaluator, now2, now2, now2);
57689
58157
  }
57690
58158
  async updateEvalRun(input) {
57691
- const now = new Date().toISOString();
58159
+ const now2 = new Date().toISOString();
57692
58160
  await this.db.run(`UPDATE rule_eval_runs
57693
58161
  SET status = ?, finding_count = ?, fix_count = ?, duration_ms = ?,
57694
58162
  error = ?, findings_json = ?, fixes_json = ?, completed_at = ?, updated_at = ?
57695
- WHERE run_id = ? AND rule_id = ?`, input.status, input.findingCount, input.fixCount, input.durationMs, input.error ?? null, input.findingsJson ?? null, input.fixesJson ?? null, now, now, input.runId, input.ruleId);
58163
+ WHERE run_id = ? AND rule_id = ?`, input.status, input.findingCount, input.fixCount, input.durationMs, input.error ?? null, input.findingsJson ?? null, input.fixesJson ?? null, now2, now2, input.runId, input.ruleId);
57696
58164
  }
57697
58165
  }
57698
58166
 
57699
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.16+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/persistence/schema.js
58167
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.18+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/persistence/schema.js
57700
58168
  var RULE_ENGINE_SCHEMA_SQL = `
57701
58169
  CREATE TABLE IF NOT EXISTS rule_runs (
57702
58170
  id TEXT PRIMARY KEY,
@@ -57742,10 +58210,10 @@ CREATE TABLE IF NOT EXISTS rule_eval_runs (
57742
58210
  CREATE INDEX IF NOT EXISTS idx_rule_eval_runs_run_id ON rule_eval_runs (run_id);
57743
58211
  `;
57744
58212
 
57745
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.16+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/persistence/index.js
58213
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.18+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/persistence/index.js
57746
58214
  var init_persistence2 = () => {};
57747
58215
 
57748
- // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.16+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/index.js
58216
+ // ../../node_modules/.bun/@gobing-ai+ts-rule-engine@0.4.18+eb8900f44f4776f9/node_modules/@gobing-ai/ts-rule-engine/dist/index.js
57749
58217
  var init_dist10 = __esm(() => {
57750
58218
  init_extensions();
57751
58219
  init_loader2();
@@ -57814,7 +58282,7 @@ var init_schema3 = __esm(() => {
57814
58282
  });
57815
58283
 
57816
58284
  // ../../packages/domain/src/migrations.ts
57817
- import { join as join5 } from "path";
58285
+ import { join as join6 } from "path";
57818
58286
  async function applyCliMigrations(adapter, migrations = CLI_MIGRATIONS) {
57819
58287
  await adapter.exec('CREATE TABLE IF NOT EXISTS "__spur_cli_migrations" (id TEXT PRIMARY KEY, applied_at INTEGER NOT NULL)');
57820
58288
  let applied = 0;
@@ -57842,7 +58310,7 @@ async function loadSqlMigrations(folder) {
57842
58310
  for (const entry of entries) {
57843
58311
  migrations.push({
57844
58312
  id: entry.replace(/\.sql$/, ""),
57845
- sql: await fs3.readFile(join5(folder, entry))
58313
+ sql: await fs3.readFile(join6(folder, entry))
57846
58314
  });
57847
58315
  }
57848
58316
  return migrations.length > 0 ? migrations : CLI_MIGRATIONS;
@@ -57965,7 +58433,7 @@ ${SYSTEM_EVENTS_SCHEMA_SQL}
57965
58433
  ];
57966
58434
  });
57967
58435
 
57968
- // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.16+4e212f5ced9864ce/node_modules/@gobing-ai/ts-infra/dist/job-queue/db-job-queue.js
58436
+ // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.18+863f28addc1a7cdc/node_modules/@gobing-ai/ts-infra/dist/job-queue/db-job-queue.js
57969
58437
  function queueLogger() {
57970
58438
  if (!_queueLogger)
57971
58439
  _queueLogger = getLogger2("job-queue");
@@ -58242,7 +58710,7 @@ var init_db_job_queue = __esm(() => {
58242
58710
  init_tracing2();
58243
58711
  });
58244
58712
 
58245
- // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.16+4e212f5ced9864ce/node_modules/@gobing-ai/ts-infra/dist/job-queue-db.js
58713
+ // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.18+863f28addc1a7cdc/node_modules/@gobing-ai/ts-infra/dist/job-queue-db.js
58246
58714
  var exports_job_queue_db = {};
58247
58715
  __export(exports_job_queue_db, {
58248
58716
  DBQueueConsumer: () => DBQueueConsumer,
@@ -59213,13 +59681,13 @@ var init_envelope = __esm(() => {
59213
59681
  });
59214
59682
 
59215
59683
  // ../../packages/domain/src/planning/locks.ts
59216
- import { closeSync, fsyncSync, mkdirSync as mkdirSync4, openSync, readFileSync as readFileSync7, renameSync as renameSync3, unlinkSync, writeSync } from "fs";
59217
- import { dirname as dirname7, join as join6 } from "path";
59684
+ import { closeSync as closeSync2, fsyncSync, mkdirSync as mkdirSync5, openSync as openSync2, readFileSync as readFileSync7, renameSync as renameSync3, unlinkSync, writeSync as writeSync2 } from "fs";
59685
+ import { dirname as dirname7, join as join7 } from "path";
59218
59686
  function lockPathForEntity(folder, entityId) {
59219
- return join6(folder, `.${entityId}.lock`);
59687
+ return join7(folder, `.${entityId}.lock`);
59220
59688
  }
59221
59689
  function createLockPath(folder) {
59222
- return join6(folder, ".create.lock");
59690
+ return join7(folder, ".create.lock");
59223
59691
  }
59224
59692
  function parseLockContent(raw) {
59225
59693
  const str = typeof raw === "string" ? raw : raw.toString();
@@ -59282,14 +59750,14 @@ function acquireLock(path9, _fs, ttlMs, label) {
59282
59750
  },
59283
59751
  staleWarning
59284
59752
  });
59285
- mkdirSync4(dirname7(path9), { recursive: true });
59753
+ mkdirSync5(dirname7(path9), { recursive: true });
59286
59754
  for (let attempt = 0;attempt < 2; attempt++) {
59287
59755
  try {
59288
- const fd = openSync(path9, "wx");
59756
+ const fd = openSync2(path9, "wx");
59289
59757
  try {
59290
- writeSync(fd, content);
59758
+ writeSync2(fd, content);
59291
59759
  } finally {
59292
- closeSync(fd);
59760
+ closeSync2(fd);
59293
59761
  }
59294
59762
  return makeResult();
59295
59763
  } catch (err) {
@@ -59311,20 +59779,20 @@ function acquireLock(path9, _fs, ttlMs, label) {
59311
59779
  function fsyncPath(path9, swallowErrors = false) {
59312
59780
  let fd;
59313
59781
  try {
59314
- fd = openSync(path9, "r");
59782
+ fd = openSync2(path9, "r");
59315
59783
  fsyncSync(fd);
59316
59784
  } catch (err) {
59317
59785
  if (!swallowErrors)
59318
59786
  throw err;
59319
59787
  } finally {
59320
59788
  if (fd !== undefined)
59321
- closeSync(fd);
59789
+ closeSync2(fd);
59322
59790
  }
59323
59791
  }
59324
59792
  function atomicWrite(dest, content, entityId, fs3, projectName = "spur") {
59325
59793
  const dir = dirname7(dest);
59326
59794
  const rand = Math.random().toString(36).slice(2, 10);
59327
- const tempPath = join6(dir, `.${projectName}.${entityId}.${rand}.tmp`);
59795
+ const tempPath = join7(dir, `.${projectName}.${entityId}.${rand}.tmp`);
59328
59796
  fs3.writeFile(tempPath, content);
59329
59797
  fsyncPath(tempPath);
59330
59798
  renameSync3(tempPath, dest);
@@ -59333,7 +59801,7 @@ function atomicWrite(dest, content, entityId, fs3, projectName = "spur") {
59333
59801
  async function atomicWriteAsync(dest, content, entityId, fs3, projectName = "spur") {
59334
59802
  const dir = dirname7(dest);
59335
59803
  const rand = Math.random().toString(36).slice(2, 10);
59336
- const tempPath = join6(dir, `.${projectName}.${entityId}.${rand}.tmp`);
59804
+ const tempPath = join7(dir, `.${projectName}.${entityId}.${rand}.tmp`);
59337
59805
  await fs3.writeFile(tempPath, content);
59338
59806
  fsyncPath(tempPath);
59339
59807
  renameSync3(tempPath, dest);
@@ -59348,7 +59816,7 @@ function cleanupTempFiles(folder, entityId, fs3, projectName = "spur") {
59348
59816
  let count3 = 0;
59349
59817
  for (const entry of entries) {
59350
59818
  if (entry.startsWith(prefix) && entry.endsWith(suffix)) {
59351
- fs3.deleteFile(join6(folder, entry));
59819
+ fs3.deleteFile(join7(folder, entry));
59352
59820
  count3++;
59353
59821
  }
59354
59822
  }
@@ -60618,11 +61086,12 @@ class AgentService {
60618
61086
  const forceBuffered = silent || nonInteractive;
60619
61087
  const outputPolicy = forceBuffered ? { mode: "buffered" } : jsonOutput ? { mode: "buffered" } : { mode: "stream", isTTY: isatty3(1) };
60620
61088
  const outputMode = outputPolicy.mode;
61089
+ let publishPid;
60621
61090
  const runner = deps?.runner ?? new AiRunner({
60622
- processExecutor: new NodeProcessExecutor3({
61091
+ processExecutor: new PidObservingProcessExecutor({
60623
61092
  output: outputPolicy,
60624
61093
  ...this.ctx.processRegistry !== undefined ? { registry: this.ctx.processRegistry } : {}
60625
- }),
61094
+ }, (pid) => publishPid?.(pid)),
60626
61095
  ...this.ctx.events !== undefined ? { events: bridgeEventBus(this.ctx.events) } : {},
60627
61096
  ...this.ctx.events !== undefined ? { processEvents: bridgeEventBus(this.ctx.events) } : {}
60628
61097
  });
@@ -60651,6 +61120,7 @@ class AgentService {
60651
61120
  const onTerminate = () => controller.abort();
60652
61121
  const onExternalAbort = () => controller.abort();
60653
61122
  const lifecycle = new AgentExecutionLifecycle(options.execution?.observer, options.execution?.correlation, configuredSecretValues(this.ctx.env), options.execution?.heartbeatMs);
61123
+ publishPid = (pid) => lifecycle.setPid(pid);
60654
61124
  process.on("SIGTERM", onTerminate);
60655
61125
  process.on("SIGINT", onTerminate);
60656
61126
  options.execution?.signal?.addEventListener("abort", onExternalAbort, { once: true });
@@ -60818,7 +61288,7 @@ class AgentService {
60818
61288
  return {
60819
61289
  ok: false,
60820
61290
  exitCode: 2,
60821
- message: "'inline' selects in-session execution and cannot be passed to 'spur agent run', which always starts a subprocess. Run the backing skill directly in the current session, or use '--agent auto' for subprocess dispatch with a tier-resolved executor."
61291
+ message: "'inline' selects in-session execution and cannot be passed to 'spur agent run', which always starts a subprocess. Run the backing skill directly in the current session, or redirect pipeline stages via agent.default / '--agent auto' / '--agent <name>' (ADR-046)."
60822
61292
  };
60823
61293
  }
60824
61294
  return this.resolveExecutorSelector(raw, doctorRunner, "explicit");
@@ -61150,12 +61620,28 @@ function classifyObjectiveFailure(result) {
61150
61620
  }
61151
61621
  return;
61152
61622
  }
61153
- var TRACE_SAFE_SLASH_COMMAND, TRACE_SAFE_SLASH_TOKEN, SENSITIVE_FLAG, SENSITIVE_INLINE_FLAG;
61623
+ var PidObservingProcessExecutor, TRACE_SAFE_SLASH_COMMAND, TRACE_SAFE_SLASH_TOKEN, SENSITIVE_FLAG, SENSITIVE_INLINE_FLAG;
61154
61624
  var init_agent_service = __esm(() => {
61155
61625
  init_src2();
61156
61626
  init_dist9();
61157
61627
  init_dist5();
61158
61628
  init_agent_execution();
61629
+ PidObservingProcessExecutor = class PidObservingProcessExecutor extends NodeProcessExecutor3 {
61630
+ publishPid;
61631
+ constructor(config4, publishPid) {
61632
+ super(config4);
61633
+ this.publishPid = publishPid;
61634
+ }
61635
+ async run(options) {
61636
+ return super.run({
61637
+ ...options,
61638
+ onSpawn: (pid) => {
61639
+ options.onSpawn?.(pid);
61640
+ this.publishPid(pid);
61641
+ }
61642
+ });
61643
+ }
61644
+ };
61159
61645
  TRACE_SAFE_SLASH_COMMAND = /^(?:\/skill:(?:sp|rd3)-|\/(?:sp|rd3)[:-]|\$(?:sp|rd3)-)[A-Za-z0-9._-]+(?:\s|$)/;
61160
61646
  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)$/;
61161
61647
  SENSITIVE_FLAG = /^--?(?:api[-_]?key|authorization|credential|password|secret|token)$/i;
@@ -61163,7 +61649,7 @@ var init_agent_service = __esm(() => {
61163
61649
  });
61164
61650
 
61165
61651
  // ../../packages/app/src/services/corpus-migrator.ts
61166
- import { join as join7, relative } from "path";
61652
+ import { join as join8, relative } from "path";
61167
61653
  function applyM1(data, flags) {
61168
61654
  const raw = data.status;
61169
61655
  if (typeof raw !== "string")
@@ -61463,7 +61949,7 @@ class CorpusMigrator {
61463
61949
  }
61464
61950
  async discoverFiles() {
61465
61951
  const entries = await this.fs.readDir(this.corpusDir);
61466
- return entries.filter((name) => name.endsWith(".md")).filter((name) => name !== "kanban.md").map((name) => join7(this.corpusDir, name)).sort();
61952
+ return entries.filter((name) => name.endsWith(".md")).filter((name) => name !== "kanban.md").map((name) => join8(this.corpusDir, name)).sort();
61467
61953
  }
61468
61954
  async migrateFile(filePath, dryRun, gitDates = new Map) {
61469
61955
  const flags = [];
@@ -61770,7 +62256,7 @@ function normalizeSystemEventPayload(entry, eventPayload, secretValues = []) {
61770
62256
  }
61771
62257
  function redactSecretValue(value, secretValues) {
61772
62258
  if (typeof value === "string")
61773
- return redactAndBound(value, secretValues, MAX_FIELD_LENGTH);
62259
+ return redactAndBound(value, secretValues, MAX_FIELD_LENGTH2);
61774
62260
  if (Array.isArray(value))
61775
62261
  return value.map((item) => redactSecretValue(item, secretValues));
61776
62262
  if (value !== null && typeof value === "object") {
@@ -61784,7 +62270,7 @@ function redactSecretValues(payload, secretValues) {
61784
62270
  }
61785
62271
  return payload;
61786
62272
  }
61787
- var SYSTEM_EVENT_CATALOG, SYSTEM_EVENT_NAMES, SYSTEM_EVENT_PERSISTED_NAMES, SYSTEM_EVENT_STREAMED_NAMES, SYSTEM_EVENT_PREFIXES, SYSTEM_EVENT_DEFAULT_NAMES, SYSTEM_EVENT_DIAGNOSTIC_NAMES, PLANNING_EVENT_NAMES, SYSTEM_EVENT_CATALOG_METADATA, MAX_FIELD_LENGTH = 256;
62273
+ var SYSTEM_EVENT_CATALOG, SYSTEM_EVENT_NAMES, SYSTEM_EVENT_PERSISTED_NAMES, SYSTEM_EVENT_STREAMED_NAMES, SYSTEM_EVENT_PREFIXES, SYSTEM_EVENT_DEFAULT_NAMES, SYSTEM_EVENT_DIAGNOSTIC_NAMES, PLANNING_EVENT_NAMES, SYSTEM_EVENT_CATALOG_METADATA, MAX_FIELD_LENGTH2 = 256;
61788
62274
  var init_event_names = __esm(() => {
61789
62275
  init_agent_execution();
61790
62276
  SYSTEM_EVENT_CATALOG = [
@@ -62008,7 +62494,7 @@ __export(exports_feature_check, {
62008
62494
  FeatureCheckService: () => FeatureCheckService,
62009
62495
  DEFAULT_FEATURE_MATRIX: () => DEFAULT_FEATURE_MATRIX
62010
62496
  });
62011
- import { dirname as dirname8, join as join8 } from "path";
62497
+ import { dirname as dirname8, join as join9 } from "path";
62012
62498
  function isGroupFeature(fm) {
62013
62499
  const tags = fm.tags;
62014
62500
  return Array.isArray(tags) && tags.includes("group") ? "group" : "standard";
@@ -62106,9 +62592,9 @@ function decodeVerdictRows(source, sectionName, required2) {
62106
62592
  function defaultVerdictRunDir(tasksDir) {
62107
62593
  const norm = tasksDir.replace(/\\/g, "/");
62108
62594
  if (/\/docs\/tasks\d*$/.test(norm) || /\/docs\/tasks$/.test(norm)) {
62109
- return join8(dirname8(dirname8(tasksDir)), ".spur", "run");
62595
+ return join9(dirname8(dirname8(tasksDir)), ".spur", "run");
62110
62596
  }
62111
- return join8(dirname8(tasksDir), ".spur", "run");
62597
+ return join9(dirname8(tasksDir), ".spur", "run");
62112
62598
  }
62113
62599
  function rowMatchesScenario(id, sc) {
62114
62600
  const stripped = id.replace(/^\[[^\]]*\]\s*/, "").replace(/^Scenario:\s*/i, "").replace(/^\[[^\]]*\]\s*/, "").trim();
@@ -62207,7 +62693,7 @@ var init_feature_check = __esm(() => {
62207
62693
  await this.checkOneActiveGoal(fm, featureId2, options.featuresDir, findings, options.asStatus);
62208
62694
  await this.checkChildrenLimit(featureId2, options.featuresDir, findings);
62209
62695
  }
62210
- const dogfoodDir = options?.dogfoodDir ?? (options?.featuresDir ? join8(dirname8(options.featuresDir), "dogfood") : undefined);
62696
+ const dogfoodDir = options?.dogfoodDir ?? (options?.featuresDir ? join9(dirname8(options.featuresDir), "dogfood") : undefined);
62211
62697
  const runDir = options?.runDir ?? (options?.tasksDir ? defaultVerdictRunDir(options.tasksDir) : undefined);
62212
62698
  await this.runL4(doc2, featureId2, status, options?.tasksDir, dogfoodDir, runDir, findings);
62213
62699
  return { id: featureId2, ...this.summarizeWithStatus(status, findings, strict, options?.severityOverrides) };
@@ -63066,7 +63552,7 @@ class FeatureService {
63066
63552
  }
63067
63553
  }
63068
63554
  async applyMove(all, movedSet, mapping, affectedTasks, movedCount) {
63069
- const now = new Date().toISOString();
63555
+ const now2 = new Date().toISOString();
63070
63556
  const created = [];
63071
63557
  const removed = [];
63072
63558
  try {
@@ -63077,8 +63563,8 @@ class FeatureService {
63077
63563
  const raw = await this.ctx.fs.readFile(f.filePath);
63078
63564
  const doc2 = MarkdownDocument.parse(raw, "feature");
63079
63565
  doc2.setFrontmatterField("id", `"${newId}"`);
63080
- doc2.setFrontmatterField("updated_at", now);
63081
- appendFeatureHistory(doc2, now, `moved ${f.id} \u2192 ${newId}`);
63566
+ doc2.setFrontmatterField("updated_at", now2);
63567
+ appendFeatureHistory(doc2, now2, `moved ${f.id} \u2192 ${newId}`);
63082
63568
  const slug = basename(f.filePath).replace(/^[A-Z][1-9]*_/, "").replace(/\.md$/, "");
63083
63569
  const newPath = `${this.ctx.featuresDir}/${newId}_${slug}.md`;
63084
63570
  removed.push({ path: f.filePath, content: raw });
@@ -63094,7 +63580,7 @@ class FeatureService {
63094
63580
  const raw = await this.ctx.fs.readFile(task.path);
63095
63581
  const doc2 = MarkdownDocument.parse(raw, "task");
63096
63582
  doc2.setFrontmatterField("feature_id", newFid);
63097
- doc2.setFrontmatterField("updated_at", now);
63583
+ doc2.setFrontmatterField("updated_at", now2);
63098
63584
  removed.push({ path: task.path, content: raw });
63099
63585
  await atomicWriteAsync(task.path, doc2.serialize(), task.wbs, this.ctx.fs, this.ctx.projectName ?? "spur");
63100
63586
  }
@@ -63215,7 +63701,7 @@ class FeatureService {
63215
63701
  throw new Error(`Parent ${parentId} has reached the 9-child limit`);
63216
63702
  }
63217
63703
  templateContent(id, name) {
63218
- const now = new Date().toISOString();
63704
+ const now2 = new Date().toISOString();
63219
63705
  return `---
63220
63706
  schema_version: 1
63221
63707
  id: "${id}"
@@ -63223,8 +63709,8 @@ name: "${name}"
63223
63709
  status: backlog
63224
63710
  priority: P2
63225
63711
  tags: []
63226
- created_at: "${now}"
63227
- updated_at: "${now}"
63712
+ created_at: "${now2}"
63713
+ updated_at: "${now2}"
63228
63714
  ---
63229
63715
 
63230
63716
  # ${id}: ${name}
@@ -63428,6 +63914,37 @@ class JobWorkerService {
63428
63914
  }
63429
63915
  }
63430
63916
 
63917
+ // ../../packages/app/src/services/pipeline-run-link.ts
63918
+ async function ensurePipelineRunLink(db2, wbs, options = {}) {
63919
+ const dao2 = new TaskRunLinkDao(db2);
63920
+ const links = await dao2.listByWbs(wbs, 20);
63921
+ const existing = links.find((l) => l.kind === "pipeline");
63922
+ if (existing) {
63923
+ return {
63924
+ created: false,
63925
+ id: existing.id,
63926
+ wbs: existing.wbs,
63927
+ runId: existing.run_id,
63928
+ kind: "pipeline"
63929
+ };
63930
+ }
63931
+ const id = createId("trl");
63932
+ const runId = options.runId ?? `record:${wbs}:${Date.now()}`;
63933
+ await dao2.insert({
63934
+ id,
63935
+ wbs,
63936
+ run_id: runId,
63937
+ kind: "pipeline",
63938
+ created_at: new Date().toISOString()
63939
+ });
63940
+ return { created: true, id, wbs, runId, kind: "pipeline" };
63941
+ }
63942
+ var TASK_FORWARD_CHAIN;
63943
+ var init_pipeline_run_link = __esm(() => {
63944
+ init_src2();
63945
+ TASK_FORWARD_CHAIN = ["backlog", "todo", "wip", "testing", "done"];
63946
+ });
63947
+
63431
63948
  // ../../packages/app/src/services/planning-events.ts
63432
63949
  class BusPlanningEventEmitter {
63433
63950
  bus;
@@ -63567,10 +64084,10 @@ class PlanningWriteService {
63567
64084
  fromStatus = resolved.from;
63568
64085
  toStatus = resolved.to;
63569
64086
  }
63570
- const now = new Date().toISOString();
63571
- doc2.setFrontmatterField("updated_at", now);
64087
+ const now2 = new Date().toISOString();
64088
+ doc2.setFrontmatterField("updated_at", now2);
63572
64089
  if (statusChanged && fromStatus !== undefined && toStatus !== undefined) {
63573
- appendHistoryLine(doc2, now, fromStatus, toStatus, mutation.actor ?? "system");
64090
+ appendHistoryLine(doc2, now2, fromStatus, toStatus, mutation.actor ?? "system");
63574
64091
  }
63575
64092
  const serialized = doc2.serialize();
63576
64093
  await atomicWriteAsync(ref.filePath, serialized, ref.id, this.fs, this.projectName);
@@ -63578,7 +64095,7 @@ class PlanningWriteService {
63578
64095
  const event2 = {
63579
64096
  event: eventName,
63580
64097
  entity: { kind: domain2, id: ref.id },
63581
- at: now,
64098
+ at: now2,
63582
64099
  ...fromStatus !== undefined ? { from: fromStatus } : {},
63583
64100
  ...toStatus !== undefined ? { to: toStatus } : {}
63584
64101
  };
@@ -63893,17 +64410,17 @@ var init_process_inventory_service = __esm(() => {
63893
64410
  });
63894
64411
 
63895
64412
  // ../../packages/app/src/services/project-registry.ts
63896
- import { existsSync as existsSync5, mkdirSync as mkdirSync5, readFileSync as readFileSync8, realpathSync as realpathSync3, renameSync as renameSync4, rmSync as rmSync3, writeFileSync as writeFileSync4 } from "fs";
64413
+ import { existsSync as existsSync5, mkdirSync as mkdirSync6, readFileSync as readFileSync8, realpathSync as realpathSync3, renameSync as renameSync4, rmSync as rmSync3, writeFileSync as writeFileSync4 } from "fs";
63897
64414
  import { connect, createServer } from "net";
63898
64415
  import { homedir as homedir3 } from "os";
63899
- import { dirname as dirname9, join as join9, resolve } from "path";
64416
+ import { dirname as dirname9, join as join10, resolve } from "path";
63900
64417
  function normalizeProjectPath(pathInput) {
63901
64418
  const trimmed = pathInput.trim();
63902
64419
  let expanded = trimmed;
63903
64420
  if (trimmed === "~") {
63904
64421
  expanded = homedir3();
63905
64422
  } else if (trimmed.startsWith("~/")) {
63906
- expanded = join9(homedir3(), trimmed.slice(2));
64423
+ expanded = join10(homedir3(), trimmed.slice(2));
63907
64424
  } else {
63908
64425
  expanded = resolve(trimmed);
63909
64426
  }
@@ -63963,13 +64480,13 @@ class ProjectRegistry {
63963
64480
  async withLock(fn) {
63964
64481
  const lockParent = dirname9(this.lockDir);
63965
64482
  if (!existsSync5(lockParent)) {
63966
- mkdirSync5(lockParent, { recursive: true });
64483
+ mkdirSync6(lockParent, { recursive: true });
63967
64484
  }
63968
64485
  const maxTries = 50;
63969
64486
  let acquired = false;
63970
64487
  for (let i2 = 0;i2 < maxTries; i2++) {
63971
64488
  try {
63972
- mkdirSync5(this.lockDir);
64489
+ mkdirSync6(this.lockDir);
63973
64490
  acquired = true;
63974
64491
  break;
63975
64492
  } catch {
@@ -63979,7 +64496,7 @@ class ProjectRegistry {
63979
64496
  if (!acquired) {
63980
64497
  try {
63981
64498
  rmSync3(this.lockDir, { recursive: true, force: true });
63982
- mkdirSync5(this.lockDir);
64499
+ mkdirSync6(this.lockDir);
63983
64500
  acquired = true;
63984
64501
  } catch {
63985
64502
  throw new Error(`Failed to acquire lock for project registry: ${this.lockDir}`);
@@ -64010,7 +64527,7 @@ class ProjectRegistry {
64010
64527
  writeRaw(data) {
64011
64528
  const parentDir2 = dirname9(this.filePath);
64012
64529
  if (!existsSync5(parentDir2)) {
64013
- mkdirSync5(parentDir2, { recursive: true });
64530
+ mkdirSync6(parentDir2, { recursive: true });
64014
64531
  }
64015
64532
  const tmpFile = `${this.filePath}.${Date.now()}.${Math.random().toString(36).slice(2)}.tmp`;
64016
64533
  const content = JSON.stringify(data, null, 2);
@@ -64284,7 +64801,7 @@ var init_project_start = __esm(() => {
64284
64801
 
64285
64802
  // ../../packages/app/src/services/rule-service.ts
64286
64803
  import { homedir as homedir4 } from "os";
64287
- import { delimiter, join as join10, relative as relative2, resolve as resolve3 } from "path";
64804
+ import { delimiter, join as join11, relative as relative2, resolve as resolve3 } from "path";
64288
64805
 
64289
64806
  class RuleService {
64290
64807
  context;
@@ -64472,13 +64989,13 @@ ${ruleIds.join(`
64472
64989
  const hasGlobalOverride = globalOverride !== undefined && globalOverride.length > 0;
64473
64990
  layers.push({
64474
64991
  id: "global",
64475
- path: hasGlobalOverride ? resolve3(cwd, globalOverride) : join10(homedir4(), GLOBAL_RULES_DIR),
64992
+ path: hasGlobalOverride ? resolve3(cwd, globalOverride) : join11(homedir4(), GLOBAL_RULES_DIR),
64476
64993
  priority: 10
64477
64994
  });
64478
64995
  if (opts.includeBundled && !hasGlobalOverride) {
64479
64996
  const bundledConfig = bundledConfigRoot();
64480
64997
  if (bundledConfig !== null) {
64481
- layers.push({ id: "bundled-config", path: join10(bundledConfig, "rules"), priority: 15 });
64998
+ layers.push({ id: "bundled-config", path: join11(bundledConfig, "rules"), priority: 15 });
64482
64999
  }
64483
65000
  const bundled = await bundledRulesRoot();
64484
65001
  if (bundled !== null)
@@ -64652,7 +65169,7 @@ ${ruleIds.join(`
64652
65169
  const { fs: fs3 } = this.context;
64653
65170
  for (const root of await this.ruleRoots()) {
64654
65171
  for (const ext of ["yaml", "yml", "json"]) {
64655
- if (await fs3.exists(join10(root, `${name}.${ext}`)))
65172
+ if (await fs3.exists(join11(root, `${name}.${ext}`)))
64656
65173
  return true;
64657
65174
  }
64658
65175
  }
@@ -64688,7 +65205,7 @@ ${ruleIds.join(`
64688
65205
  for (const layer of await this.existingRuleSourceLayers()) {
64689
65206
  const entries = await this.context.fs.readDir(layer.path);
64690
65207
  const named = entries.map((entry) => ({ entry, name: entry.match(/^([\w-]+)\.(?:ya?ml|json)$/i)?.[1] ?? "" })).filter(({ name }) => name.length > 0);
64691
- const stats = await Promise.all(named.map(({ entry }) => this.context.fs.stat(join10(layer.path, entry))));
65208
+ const stats = await Promise.all(named.map(({ entry }) => this.context.fs.stat(join11(layer.path, entry))));
64692
65209
  named.forEach(({ name }, i2) => {
64693
65210
  if (stats[i2]?.isFile())
64694
65211
  candidates.add(name);
@@ -64710,7 +65227,7 @@ ${ruleIds.join(`
64710
65227
  for (const layer of layers) {
64711
65228
  for (const file2 of await this.listRuleFilesInLayer(layer.path)) {
64712
65229
  if (!merged.has(file2))
64713
- merged.set(file2, { absolutePath: join10(layer.path, file2), source: layer.id });
65230
+ merged.set(file2, { absolutePath: join11(layer.path, file2), source: layer.id });
64714
65231
  }
64715
65232
  }
64716
65233
  const files = [];
@@ -64778,7 +65295,7 @@ ${ruleIds.join(`
64778
65295
  for (const entry of entries.sort()) {
64779
65296
  if (entry === "presets")
64780
65297
  continue;
64781
- const stat = await this.context.fs.stat(join10(root, entry));
65298
+ const stat = await this.context.fs.stat(join11(root, entry));
64782
65299
  if (stat?.isDirectory())
64783
65300
  dirs.push(entry);
64784
65301
  }
@@ -64786,12 +65303,12 @@ ${ruleIds.join(`
64786
65303
  }
64787
65304
  async listRuleFiles(root, relativeDir) {
64788
65305
  const { fs: fs3 } = this.context;
64789
- const dir = join10(root, relativeDir);
65306
+ const dir = join11(root, relativeDir);
64790
65307
  const entries = await fs3.readDir(dir);
64791
65308
  const files = [];
64792
65309
  for (const entry of entries.sort()) {
64793
- const relativePath2 = relativeDir.length === 0 ? entry : join10(relativeDir, entry);
64794
- const absolutePath = join10(root, relativePath2);
65310
+ const relativePath2 = relativeDir.length === 0 ? entry : join11(relativeDir, entry);
65311
+ const absolutePath = join11(root, relativePath2);
64795
65312
  const stat = await fs3.stat(absolutePath);
64796
65313
  if (stat?.isDirectory()) {
64797
65314
  files.push(...await this.listRuleFiles(root, relativePath2));
@@ -64816,8 +65333,8 @@ var init_rule_service = __esm(() => {
64816
65333
  init_src2();
64817
65334
  init_dist6();
64818
65335
  init_dist10();
64819
- LOCAL_RULES_DIR = join10(".spur", "rules");
64820
- GLOBAL_RULES_DIR = join10(".config", "spur", "rules");
65336
+ LOCAL_RULES_DIR = join11(".spur", "rules");
65337
+ GLOBAL_RULES_DIR = join11(".config", "spur", "rules");
64821
65338
  SEVERITY_RANK2 = {
64822
65339
  info: 0,
64823
65340
  warning: 1,
@@ -64885,7 +65402,7 @@ function summarizeActionResult(resultJson, secretValues = []) {
64885
65402
  return redactValue3(parsed, secretValues);
64886
65403
  }
64887
65404
  function redactString(value, secretValues) {
64888
- return redactAndBound(value, secretValues, MAX_FIELD_LENGTH2);
65405
+ return redactAndBound(value, secretValues, MAX_FIELD_LENGTH3);
64889
65406
  }
64890
65407
  function redactValue3(value, secretValues) {
64891
65408
  if (value === null || value === undefined)
@@ -65008,7 +65525,7 @@ class RunStoreService {
65008
65525
  return { wbs, links, count: links.length };
65009
65526
  }
65010
65527
  }
65011
- var RUN_STORE_LIST_DEFAULT_LIMIT = 50, RUN_STORE_LIST_MAX_LIMIT = 200, RUN_STORE_WBS_DEFAULT_LIMIT = 50, RUN_STORE_WBS_MAX_LIMIT = 200, MAX_FIELD_LENGTH2 = 256, SENSITIVE_KEYS, RunStoreNotFoundError, RunStoreBadCursorError;
65528
+ var RUN_STORE_LIST_DEFAULT_LIMIT = 50, RUN_STORE_LIST_MAX_LIMIT = 200, RUN_STORE_WBS_DEFAULT_LIMIT = 50, RUN_STORE_WBS_MAX_LIMIT = 200, MAX_FIELD_LENGTH3 = 256, SENSITIVE_KEYS, RunStoreNotFoundError, RunStoreBadCursorError;
65012
65529
  var init_run_store_service = __esm(() => {
65013
65530
  init_src2();
65014
65531
  init_agent_execution();
@@ -65451,7 +65968,7 @@ var init_system_event_emitter = __esm(() => {
65451
65968
  });
65452
65969
 
65453
65970
  // ../../packages/app/src/services/task-check.ts
65454
- import { dirname as dirname10, join as join11 } from "path";
65971
+ import { dirname as dirname10, join as join12 } from "path";
65455
65972
  function isPlaceholderBody(body) {
65456
65973
  const stripped = body.replace(/<!--[\s\S]*?-->/g, "").replace(/^\s*>\s*TBD\s*$/gim, "").trim();
65457
65974
  return stripped.length === 0;
@@ -65600,7 +66117,7 @@ var init_task_check = __esm(() => {
65600
66117
  this.runL2(doc2, entry, findings);
65601
66118
  this.runL3(doc2, entry, status, findings);
65602
66119
  const tasksDir = dirname10(filePath);
65603
- const featuresDir = join11(dirname10(tasksDir), "features");
66120
+ const featuresDir = join12(dirname10(tasksDir), "features");
65604
66121
  await this.runL4(doc2, fm, status, findings, featuresDir, tasksDir);
65605
66122
  await this.runL4Rollup(doc2, wbs, status, findings, tasksDir);
65606
66123
  if (status !== "done" && status !== "cancelled") {
@@ -65996,7 +66513,7 @@ var init_task_check = __esm(() => {
65996
66513
  for (const cite of citations) {
65997
66514
  if (reported >= 5)
65998
66515
  break;
65999
- const abs = join11(projectRoot, cite.path);
66516
+ const abs = join12(projectRoot, cite.path);
66000
66517
  let exists2 = false;
66001
66518
  try {
66002
66519
  exists2 = await this.fs.exists(abs);
@@ -66539,7 +67056,7 @@ class TaskService {
66539
67056
  }
66540
67057
  async findDuplicateFollowUp(featureId2, title, withinSec) {
66541
67058
  const siblings = await this.list({ featureId: featureId2 });
66542
- const now = Date.now();
67059
+ const now2 = Date.now();
66543
67060
  const lowerTitle = title.toLowerCase();
66544
67061
  for (const t of siblings) {
66545
67062
  if ((t.name ?? "").toLowerCase() !== lowerTitle)
@@ -66550,7 +67067,7 @@ class TaskService {
66550
67067
  const createdMs = Date.parse(createdAt);
66551
67068
  if (Number.isNaN(createdMs))
66552
67069
  continue;
66553
- const ageSec = (now - createdMs) / 1000;
67070
+ const ageSec = (now2 - createdMs) / 1000;
66554
67071
  if (ageSec >= 0 && ageSec <= withinSec) {
66555
67072
  return { wbs: t.wbs, name: t.name };
66556
67073
  }
@@ -66575,7 +67092,7 @@ class TaskService {
66575
67092
  }
66576
67093
  const slug = this.slugify(params.title);
66577
67094
  const { wbs, filePath } = await this.allocateWbsChecked(slug);
66578
- const now = new Date().toISOString();
67095
+ const now2 = new Date().toISOString();
66579
67096
  const rawTemplate = this.ctx.resolveTemplate?.(variant);
66580
67097
  if (rawTemplate !== undefined) {
66581
67098
  const content2 = renderCreatedTaskContent({
@@ -66583,7 +67100,7 @@ class TaskService {
66583
67100
  name: params.title,
66584
67101
  wbs,
66585
67102
  background,
66586
- createdAt: now,
67103
+ createdAt: now2,
66587
67104
  status,
66588
67105
  variant,
66589
67106
  featureId: params.featureId,
@@ -66597,8 +67114,8 @@ class TaskService {
66597
67114
  `name: "${params.title}"`,
66598
67115
  `status: ${status}`,
66599
67116
  `template: ${variant}`,
66600
- `created_at: ${now}`,
66601
- `updated_at: ${now}`,
67117
+ `created_at: ${now2}`,
67118
+ `updated_at: ${now2}`,
66602
67119
  params.featureId !== undefined ? `feature_id: ${params.featureId}` : null,
66603
67120
  params.parentWbs !== undefined ? `parent_wbs: "${params.parentWbs}"` : null
66604
67121
  ].filter(Boolean).join(`
@@ -66886,11 +67403,51 @@ class TaskService {
66886
67403
  result.solutionBackfilled = true;
66887
67404
  }
66888
67405
  if (opts.transition !== undefined) {
66889
- const transitionResult = await this.writeService.transition(ref, opts.transition);
66890
- result.transitionedTo = transitionResult.toStatus;
67406
+ const target = opts.transition;
67407
+ const current = (doc2.frontmatterData?.status ?? "").toLowerCase();
67408
+ if (current === target) {
67409
+ result.transitionedTo = current;
67410
+ } else if (target === "done") {
67411
+ if (verdict.verdict !== "PASS") {
67412
+ throw new GuardDeniedError(`Cannot transition task ${wbs} to done: verify verdict is ${verdict.verdict}, not PASS. ` + `Re-run the pipeline to a PASS verdict, or use ` + `\`spur task update ${wbs} done --force-done --reason "<why>"\` to override.`);
67413
+ }
67414
+ const startIdx = TASK_FORWARD_CHAIN.indexOf(current);
67415
+ const targetIdx = TASK_FORWARD_CHAIN.indexOf(target);
67416
+ if (startIdx !== -1 && targetIdx !== -1 && targetIdx > startIdx) {
67417
+ let reached = current;
67418
+ for (let i2 = startIdx + 1;i2 <= targetIdx; i2 += 1) {
67419
+ const hop = TASK_FORWARD_CHAIN[i2];
67420
+ if (hop === "done") {
67421
+ await this.ensurePipelineRunLink(wbs);
67422
+ }
67423
+ try {
67424
+ const hopResult = await this.writeService.transition(ref, hop);
67425
+ reached = hopResult.toStatus ?? hop;
67426
+ result.transitionedTo = reached;
67427
+ } catch (err) {
67428
+ const msg = err instanceof Error ? err.message : String(err);
67429
+ throw new GuardDeniedError(`Record auto-walk for task ${wbs} stopped at '${reached}' ` + `(failed hop \u2192 '${hop}'): ${msg}. ` + `Re-run \`spur task record ${wbs} --transition done\` once the guard is green.`);
67430
+ }
67431
+ }
67432
+ } else {
67433
+ await this.ensurePipelineRunLink(wbs);
67434
+ const transitionResult = await this.writeService.transition(ref, target);
67435
+ result.transitionedTo = transitionResult.toStatus;
67436
+ }
67437
+ } else {
67438
+ const transitionResult = await this.writeService.transition(ref, target);
67439
+ result.transitionedTo = transitionResult.toStatus;
67440
+ }
66891
67441
  }
66892
67442
  return result;
66893
67443
  }
67444
+ async ensurePipelineRunLink(wbs) {
67445
+ const getDb = this.ctx.getDb;
67446
+ if (getDb === undefined)
67447
+ return;
67448
+ const db2 = await getDb();
67449
+ await ensurePipelineRunLink(db2, wbs);
67450
+ }
66894
67451
  async batchCreate(jsonPath) {
66895
67452
  const raw = await this.ctx.fs.readFile(jsonPath);
66896
67453
  let parsed;
@@ -66979,7 +67536,7 @@ ${issues}`);
66979
67536
  return this.writeService.createAllocated(folder, async () => {
66980
67537
  const slug = this.slugify(item.name);
66981
67538
  const { wbs, filePath } = await this.allocateWbsChecked(slug);
66982
- const now = new Date().toISOString();
67539
+ const now2 = new Date().toISOString();
66983
67540
  const rawTemplate = this.ctx.resolveTemplate?.(variant);
66984
67541
  if (rawTemplate !== undefined) {
66985
67542
  const content2 = renderCreatedTaskContent({
@@ -66987,7 +67544,7 @@ ${issues}`);
66987
67544
  name: item.name,
66988
67545
  wbs,
66989
67546
  background,
66990
- createdAt: now,
67547
+ createdAt: now2,
66991
67548
  status,
66992
67549
  variant,
66993
67550
  featureId: item.feature_id ?? undefined,
@@ -67007,8 +67564,8 @@ ${issues}`);
67007
67564
  `name: "${item.name}"`,
67008
67565
  `status: ${status}`,
67009
67566
  `template: ${variant}`,
67010
- `created_at: ${now}`,
67011
- `updated_at: ${now}`,
67567
+ `created_at: ${now2}`,
67568
+ `updated_at: ${now2}`,
67012
67569
  item.feature_id !== undefined ? `feature_id: ${item.feature_id}` : null,
67013
67570
  item.parent_wbs !== undefined ? `parent_wbs: "${item.parent_wbs}"` : null,
67014
67571
  item.priority !== undefined ? `priority: ${item.priority}` : null,
@@ -67228,6 +67785,8 @@ ${block}` : block);
67228
67785
  var DependencyMutationError, SectionMutationError, DuplicateFollowUpError, WbsCollisionError, ROSTER_START = "<!-- AUTO-GENERATED by spur task refresh-roster -->", ROSTER_END = "<!-- END AUTO-GENERATED -->", ROSTER_REGION_RE, TASK_ACTION_COMMANDS, DEFAULT_CREATION_SECTIONS;
67229
67786
  var init_task_service = __esm(() => {
67230
67787
  init_src2();
67788
+ init_errors5();
67789
+ init_pipeline_run_link();
67231
67790
  init_task_locator();
67232
67791
  init_task_record();
67233
67792
  DependencyMutationError = class DependencyMutationError extends Error {
@@ -67532,7 +68091,7 @@ var init_task_verdict = __esm(() => {
67532
68091
  });
67533
68092
 
67534
68093
  // ../../packages/app/src/services/team-service.ts
67535
- import { join as join12 } from "path";
68094
+ import { join as join13 } from "path";
67536
68095
 
67537
68096
  class TeamService {
67538
68097
  ctx;
@@ -67540,7 +68099,7 @@ class TeamService {
67540
68099
  orchestratorPromise;
67541
68100
  constructor(ctx) {
67542
68101
  this.ctx = ctx;
67543
- this.configDir = join12(ctx.cwd, ".spur", "agents");
68102
+ this.configDir = join13(ctx.cwd, ".spur", "agents");
67544
68103
  }
67545
68104
  async sendMessage(fromId, toId, body, replyTo) {
67546
68105
  validateAgentId(toId);
@@ -67950,7 +68509,7 @@ class TeamService {
67950
68509
  async resolveTaskFile(taskId) {
67951
68510
  const fs3 = this.ctx.fs;
67952
68511
  const { foldersConfig } = await resolvePlanningFolders(fs3);
67953
- const dirs = [...new Set([foldersConfig.active_folder, ...Object.keys(foldersConfig.folders)])].map((dir) => join12(this.ctx.cwd, dir));
68512
+ const dirs = [...new Set([foldersConfig.active_folder, ...Object.keys(foldersConfig.folders)])].map((dir) => join13(this.ctx.cwd, dir));
67954
68513
  return await TaskLocator.forDirs(fs3, dirs).findPathByWbs(taskId);
67955
68514
  }
67956
68515
  }
@@ -67986,8 +68545,8 @@ var init_team_service = __esm(() => {
67986
68545
  });
67987
68546
 
67988
68547
  // ../../packages/app/src/services/token-ledger-service.ts
67989
- import { closeSync as closeSync2, existsSync as existsSync7, fstatSync, openSync as openSync2, readSync } from "fs";
67990
- import { join as join13 } from "path";
68548
+ import { closeSync as closeSync3, existsSync as existsSync7, fstatSync, openSync as openSync3, readSync } from "fs";
68549
+ import { join as join14 } from "path";
67991
68550
  function isSparseToolActivity(events2) {
67992
68551
  return events2.length === 0 || !events2.some((e) => TOOL_ACTIVITY_TYPES.has(e.type));
67993
68552
  }
@@ -68042,7 +68601,7 @@ function tailTokenLedgerFile(filePath, limit, chunkSize = DEFAULT_CHUNK, before)
68042
68601
  return { events: [], truncated: false };
68043
68602
  }
68044
68603
  const target = limit + 1;
68045
- const fd = openSync2(filePath, "r");
68604
+ const fd = openSync3(filePath, "r");
68046
68605
  try {
68047
68606
  const { size } = fstatSync(fd);
68048
68607
  if (size === 0)
@@ -68080,7 +68639,7 @@ function tailTokenLedgerFile(filePath, limit, chunkSize = DEFAULT_CHUNK, before)
68080
68639
  truncated
68081
68640
  };
68082
68641
  } finally {
68083
- closeSync2(fd);
68642
+ closeSync3(fd);
68084
68643
  }
68085
68644
  }
68086
68645
 
@@ -68088,7 +68647,7 @@ class TokenLedgerService {
68088
68647
  ledgerPath;
68089
68648
  chunkSize;
68090
68649
  constructor(options) {
68091
- this.ledgerPath = options.ledgerPath ?? join13(options.cwd, TOKEN_LEDGER_RELATIVE_PATH);
68650
+ this.ledgerPath = options.ledgerPath ?? join14(options.cwd, TOKEN_LEDGER_RELATIVE_PATH);
68092
68651
  this.chunkSize = options.chunkSize ?? DEFAULT_CHUNK;
68093
68652
  }
68094
68653
  get path() {
@@ -68129,12 +68688,12 @@ class TokenLedgerService {
68129
68688
  var TOOL_ACTIVITY_TYPES, TOKEN_LEDGER_DEFAULT_LIMIT = 200, TOKEN_LEDGER_MAX_LIMIT = 1000, TOKEN_LEDGER_RELATIVE_PATH, DEFAULT_CHUNK;
68130
68689
  var init_token_ledger_service = __esm(() => {
68131
68690
  TOOL_ACTIVITY_TYPES = new Set(["read", "write", "bash", "grep", "glob"]);
68132
- TOKEN_LEDGER_RELATIVE_PATH = join13(".spur", "context", "token-ledger.jsonl");
68691
+ TOKEN_LEDGER_RELATIVE_PATH = join14(".spur", "context", "token-ledger.jsonl");
68133
68692
  DEFAULT_CHUNK = 64 * 1024;
68134
68693
  });
68135
68694
 
68136
68695
  // ../../packages/app/src/services/token-ledger-watcher.ts
68137
- import { closeSync as closeSync3, existsSync as existsSync8, fstatSync as fstatSync2, openSync as openSync3, readSync as readSync2, watch } from "fs";
68696
+ import { closeSync as closeSync4, existsSync as existsSync8, fstatSync as fstatSync2, openSync as openSync4, readSync as readSync2, watch } from "fs";
68138
68697
 
68139
68698
  class TokenLedgerWatcher {
68140
68699
  ledgerPath;
@@ -68193,11 +68752,11 @@ class TokenLedgerWatcher {
68193
68752
  if (!existsSync8(this.ledgerPath))
68194
68753
  return 0;
68195
68754
  try {
68196
- const fd = openSync3(this.ledgerPath, "r");
68755
+ const fd = openSync4(this.ledgerPath, "r");
68197
68756
  try {
68198
68757
  return fstatSync2(fd).size;
68199
68758
  } finally {
68200
- closeSync3(fd);
68759
+ closeSync4(fd);
68201
68760
  }
68202
68761
  } catch {
68203
68762
  return 0;
@@ -68219,7 +68778,7 @@ class TokenLedgerWatcher {
68219
68778
  }
68220
68779
  let size;
68221
68780
  try {
68222
- const fd = openSync3(this.ledgerPath, "r");
68781
+ const fd = openSync4(this.ledgerPath, "r");
68223
68782
  try {
68224
68783
  size = fstatSync2(fd).size;
68225
68784
  if (size < this.offset) {
@@ -68247,7 +68806,7 @@ class TokenLedgerWatcher {
68247
68806
  }
68248
68807
  }
68249
68808
  } finally {
68250
- closeSync3(fd);
68809
+ closeSync4(fd);
68251
68810
  }
68252
68811
  } catch {}
68253
68812
  }
@@ -68256,110 +68815,6 @@ var init_token_ledger_watcher = __esm(() => {
68256
68815
  init_token_ledger_service();
68257
68816
  });
68258
68817
 
68259
- // ../../packages/app/src/observability/run-output-sink.ts
68260
- import { closeSync as closeSync4, mkdirSync as mkdirSync6, openSync as openSync4, writeSync as writeSync2 } from "fs";
68261
- import { join as join14 } from "path";
68262
-
68263
- class RunOutputSink {
68264
- filePath;
68265
- maxBytes;
68266
- maxLines;
68267
- fd;
68268
- bytes = 0;
68269
- lines = 0;
68270
- truncated = false;
68271
- closed = false;
68272
- constructor(options) {
68273
- this.filePath = join14(options.dir, `${options.runId}-output.log`);
68274
- this.maxBytes = options.maxBytes ?? DEFAULT_OUTPUT_MAX_BYTES;
68275
- this.maxLines = options.maxLines;
68276
- try {
68277
- mkdirSync6(options.dir, { recursive: true });
68278
- this.fd = openSync4(this.filePath, "a");
68279
- } catch {
68280
- this.fd = undefined;
68281
- }
68282
- }
68283
- get isTruncated() {
68284
- return this.truncated;
68285
- }
68286
- observe(event2) {
68287
- if (this.fd === undefined || this.closed)
68288
- return;
68289
- switch (event2.kind) {
68290
- case "output":
68291
- this.appendChunk(event2.at, event2.stream, event2.chunk);
68292
- break;
68293
- case "started":
68294
- this.append(`# agent output \u2014 run ${event2.runId} \u2014 ${event2.agent} \u2014 ${event2.at}
68295
- `);
68296
- this.append(`# invocation: ${event2.invocation}
68297
- `);
68298
- break;
68299
- case "dropped":
68300
- this.append(`
68301
- === [dropped] ${event2.chunks} chunk(s) discarded by the lifecycle relay under backpressure ===
68302
- `);
68303
- break;
68304
- case "finished":
68305
- this.append(`
68306
- === run ${event2.outcome} (exit ${event2.exitCode}) after ${event2.durationMs}ms ===
68307
- `);
68308
- break;
68309
- default:
68310
- break;
68311
- }
68312
- }
68313
- close() {
68314
- if (this.closed)
68315
- return;
68316
- this.closed = true;
68317
- if (this.fd !== undefined) {
68318
- try {
68319
- closeSync4(this.fd);
68320
- } catch {}
68321
- this.fd = undefined;
68322
- }
68323
- }
68324
- appendChunk(at, stream, chunk) {
68325
- if (this.truncated)
68326
- return;
68327
- const text4 = `[${at}] ${stream}: ${chunk}
68328
- `;
68329
- const chunkBytes = Buffer.byteLength(text4);
68330
- const chunkLines = countNewlines(text4);
68331
- if (this.bytes + chunkBytes > this.maxBytes || this.maxLines !== undefined && this.lines + chunkLines > this.maxLines) {
68332
- this.truncated = true;
68333
- this.append(TRUNCATION_MARKER);
68334
- return;
68335
- }
68336
- this.append(text4);
68337
- }
68338
- append(text4) {
68339
- if (this.fd === undefined || this.closed)
68340
- return;
68341
- try {
68342
- writeSync2(this.fd, text4);
68343
- this.bytes += Buffer.byteLength(text4);
68344
- this.lines += countNewlines(text4);
68345
- } catch {}
68346
- }
68347
- }
68348
- function countNewlines(text4) {
68349
- let count3 = 0;
68350
- for (let i2 = 0;i2 < text4.length; i2 += 1) {
68351
- if (text4.charCodeAt(i2) === 10)
68352
- count3 += 1;
68353
- }
68354
- return count3;
68355
- }
68356
- var DEFAULT_OUTPUT_MAX_BYTES, TRUNCATION_MARKER = `
68357
- === [truncated] agent output capture reached its configured bound; further chunks were not written ===
68358
- `;
68359
- var init_run_output_sink = __esm(() => {
68360
- DEFAULT_OUTPUT_MAX_BYTES = 1024 * 1024;
68361
- });
68362
-
68363
68818
  // ../../packages/app/src/workflow/steering.ts
68364
68819
  class WorkflowSteeringController {
68365
68820
  onAck;
@@ -68607,13 +69062,11 @@ import { dirname as dirname12, isAbsolute as isAbsolute2, join as join15 } from
68607
69062
  class AgentRunActionRunner {
68608
69063
  observabilityBus;
68609
69064
  steeringController;
68610
- outputLog;
68611
69065
  kind = KIND;
68612
69066
  agentService;
68613
- constructor(agentService, observabilityBus, steeringController, outputLog) {
69067
+ constructor(agentService, observabilityBus, steeringController) {
68614
69068
  this.observabilityBus = observabilityBus;
68615
69069
  this.steeringController = steeringController;
68616
- this.outputLog = outputLog;
68617
69070
  this.agentService = agentService;
68618
69071
  }
68619
69072
  async execute(options, context4) {
@@ -68655,17 +69108,10 @@ class AgentRunActionRunner {
68655
69108
  flags.continue = continueFlag;
68656
69109
  const answerFile = asOptionalString(options.answerFile);
68657
69110
  const expectFile = asOptionalString(options.expectFile);
69111
+ const requireDiff = asOptionalBoolean(options.requireDiff);
68658
69112
  const capture = asOptionalBoolean(options.capture) || answerFile !== undefined;
68659
69113
  const agentLabel = agent ?? "<default>";
68660
- const outputLog = this.outputLog;
68661
- const sink = outputLog === undefined ? undefined : new RunOutputSink({
68662
- dir: join15(cwd, ".spur", "run"),
68663
- runId: context4.runId,
68664
- ...outputLog.maxBytes !== undefined ? { maxBytes: outputLog.maxBytes } : {},
68665
- ...outputLog.maxLines !== undefined ? { maxLines: outputLog.maxLines } : {}
68666
- });
68667
- const observer = this.observabilityBus === undefined && sink === undefined ? undefined : (event2) => {
68668
- sink?.observe(event2);
69114
+ const observer = this.observabilityBus === undefined ? undefined : (event2) => {
68669
69115
  this.observabilityBus?.emit("workflow.agent", event2);
68670
69116
  };
68671
69117
  const actionId = context4.actionId ?? `${context4.runId}:${context4.stateOrNodeId}`;
@@ -68734,11 +69180,19 @@ class AgentRunActionRunner {
68734
69180
  };
68735
69181
  }
68736
69182
  }
69183
+ const stepLabel = context4.stateOrNodeId;
69184
+ if (ok && requireDiff === true && !await gitHasNonCorpusChanges(cwd)) {
69185
+ return {
69186
+ ok: false,
69187
+ data: buildResultData(exitCode, agentLabel, capture, answer, invocation),
69188
+ error: `agent.run '${stepLabel}' (${agentLabel}) exited 0 but produced zero non-corpus file changes \u2014 empty implement (no-op). The implement agent must change at least one file outside docs/tasks3|docs/features; fix the implement input and re-run the pipeline.`
69189
+ };
69190
+ }
68737
69191
  if (!ok) {
68738
69192
  await writePartialWorkArtifact(context4, agentLabel, model, traced, cwd);
68739
69193
  }
68740
- const stepLabel = context4.stateOrNodeId;
68741
- const error51 = ok ? undefined : traced.signal !== undefined ? timeoutMs !== undefined ? `agent.run '${stepLabel}' (${agentLabel}) terminated by signal ${traced.signal} (configured timeout: ${timeoutMs}ms; timeout or cancellation); see partial-work artifact` : `agent.run '${stepLabel}' (${agentLabel}) was cancelled by signal ${traced.signal}; see partial-work artifact` : traced.message !== undefined ? `agent.run '${stepLabel}' (${agentLabel}) dispatch failed: ${traced.message}` : `agent.run '${stepLabel}' (${agentLabel}) exited with code ${exitCode}`;
69194
+ const partialWorkHint = `partial work (if any) preserved at .spur/run/${context4.runId}-${stepLabel}-partial.md; resume from that tree per the timed-out-implement runbook (plugins/sp/skills/spur-dev/references/execution-workflow.md)`;
69195
+ const error51 = ok ? undefined : traced.signal !== undefined ? timeoutMs !== undefined ? `agent.run '${stepLabel}' (${agentLabel}) terminated by signal ${traced.signal} (configured timeout: ${timeoutMs}ms; timeout or cancellation); ${partialWorkHint}` : `agent.run '${stepLabel}' (${agentLabel}) was cancelled by signal ${traced.signal}; ${partialWorkHint}` : traced.message !== undefined ? `agent.run '${stepLabel}' (${agentLabel}) dispatch failed: ${traced.message}` : `agent.run '${stepLabel}' (${agentLabel}) exited with code ${exitCode}; ${partialWorkHint}`;
68742
69196
  return {
68743
69197
  ok,
68744
69198
  data: buildResultData(exitCode, agentLabel, capture, answer, invocation),
@@ -68748,9 +69202,7 @@ class AgentRunActionRunner {
68748
69202
  ...steeringNote !== undefined ? { __steeringNote: steeringNote } : {}
68749
69203
  } : undefined
68750
69204
  };
68751
- } finally {
68752
- sink?.close();
68753
- }
69205
+ } finally {}
68754
69206
  }
68755
69207
  }
68756
69208
  function buildResultData(exitCode, agentLabel, capture, answer, invocation) {
@@ -68857,6 +69309,21 @@ async function gitDiffStat(cwd) {
68857
69309
  return "";
68858
69310
  }
68859
69311
  }
69312
+ async function gitHasNonCorpusChanges(cwd) {
69313
+ try {
69314
+ const result = await new NodeProcessExecutor3().run({
69315
+ command: "git",
69316
+ args: ["status", "--porcelain", "--", ".", ":(exclude)docs/tasks3/*", ":(exclude)docs/features/*"],
69317
+ cwd,
69318
+ maxOutput: 1024 * 1024,
69319
+ forceBuffered: true,
69320
+ rejectOnError: false
69321
+ });
69322
+ return result.exitCode === 0 && result.stdout.trim() !== "";
69323
+ } catch {
69324
+ return false;
69325
+ }
69326
+ }
68860
69327
  function tail(text4, maxChars) {
68861
69328
  if (text4.length <= maxChars)
68862
69329
  return text4;
@@ -68866,7 +69333,6 @@ ${text4.slice(text4.length - maxChars)}`;
68866
69333
  var PARTIAL_ARTIFACT_TAIL_CHARS = 4000, KIND = "agent.run";
68867
69334
  var init_agent_run = __esm(() => {
68868
69335
  init_dist5();
68869
- init_run_output_sink();
68870
69336
  init_steering();
68871
69337
  });
68872
69338
 
@@ -69392,10 +69858,179 @@ var init_rule_check = __esm(() => {
69392
69858
  init_utils3();
69393
69859
  });
69394
69860
 
69861
+ // ../../packages/app/src/workflow/actions/shell.ts
69862
+ function stringOption2(options, key, fallback) {
69863
+ const value = options[key];
69864
+ if (typeof value === "string")
69865
+ return value;
69866
+ if (fallback !== undefined)
69867
+ return fallback;
69868
+ throw new Error(`Action option "${key}" must be a string`);
69869
+ }
69870
+ function arrayOption2(options, key) {
69871
+ const value = options[key];
69872
+ if (value === undefined)
69873
+ return [];
69874
+ if (Array.isArray(value) && value.every((entry) => typeof entry === "string"))
69875
+ return value;
69876
+ throw new Error(`Action option "${key}" must be a string array`);
69877
+ }
69878
+
69879
+ class StreamingShellActionRunner {
69880
+ processExecutor;
69881
+ observabilityBus;
69882
+ kind = KIND11;
69883
+ constructor(processExecutor, observabilityBus) {
69884
+ this.processExecutor = processExecutor;
69885
+ this.observabilityBus = observabilityBus;
69886
+ }
69887
+ async execute(options, context4) {
69888
+ const command = stringOption2(options, "command");
69889
+ const explicitArgs = arrayOption2(options, "args");
69890
+ const usesShell = explicitArgs.length === 0;
69891
+ const spawn2 = usesShell ? { command: "/bin/sh", args: ["-c", command] } : { command, args: explicitArgs };
69892
+ const cwd = stringOption2(options, "cwd", context4.workdir);
69893
+ const env = {};
69894
+ for (const [key, value] of Object.entries({ ...process.env, ...context4.vars })) {
69895
+ if (value !== undefined)
69896
+ env[key] = value;
69897
+ }
69898
+ const pipe2 = this.processExecutor.runStreaming({
69899
+ command: spawn2.command,
69900
+ args: spawn2.args,
69901
+ ...cwd !== undefined ? { cwd } : {},
69902
+ env
69903
+ });
69904
+ let stdout = "";
69905
+ let stderr = "";
69906
+ const readers = [];
69907
+ const consume = (stream, readable2) => {
69908
+ if (readable2 === null)
69909
+ return;
69910
+ const reader = readable2.getReader();
69911
+ const decoder = new TextDecoder;
69912
+ readers.push((async () => {
69913
+ for (;; ) {
69914
+ const { done, value } = await reader.read();
69915
+ if (done)
69916
+ break;
69917
+ const chunk = decoder.decode(value, { stream: true });
69918
+ if (chunk === "")
69919
+ continue;
69920
+ if (stream === "stdout")
69921
+ stdout += chunk;
69922
+ else
69923
+ stderr += chunk;
69924
+ if (this.observabilityBus !== undefined) {
69925
+ const event2 = {
69926
+ schemaVersion: 1,
69927
+ eventId: crypto.randomUUID(),
69928
+ sequence: 0,
69929
+ runId: context4.runId,
69930
+ at: new Date().toISOString(),
69931
+ kind: KIND11,
69932
+ node: context4.stateOrNodeId,
69933
+ stream,
69934
+ chunk: bounded(chunk)
69935
+ };
69936
+ this.observabilityBus.emit("workflow.action.output", event2);
69937
+ }
69938
+ }
69939
+ const tail2 = decoder.decode();
69940
+ if (tail2 !== "") {
69941
+ if (stream === "stdout")
69942
+ stdout += tail2;
69943
+ else
69944
+ stderr += tail2;
69945
+ if (this.observabilityBus !== undefined) {
69946
+ this.observabilityBus.emit("workflow.action.output", {
69947
+ schemaVersion: 1,
69948
+ eventId: crypto.randomUUID(),
69949
+ sequence: 0,
69950
+ runId: context4.runId,
69951
+ at: new Date().toISOString(),
69952
+ kind: KIND11,
69953
+ node: context4.stateOrNodeId,
69954
+ stream,
69955
+ chunk: bounded(tail2)
69956
+ });
69957
+ }
69958
+ }
69959
+ })());
69960
+ };
69961
+ consume("stdout", pipe2.stdout);
69962
+ consume("stderr", pipe2.stderr);
69963
+ const exitCode = await pipe2.exited;
69964
+ await Promise.allSettled(readers);
69965
+ return {
69966
+ ok: exitCode === 0,
69967
+ data: { stdout, stderr, exitCode },
69968
+ ...exitCode === 0 ? {} : { error: `Command "${command}" exited with ${exitCode}` }
69969
+ };
69970
+ }
69971
+ }
69972
+ var KIND11 = "shell";
69973
+ var init_shell = __esm(() => {
69974
+ init_observability();
69975
+ });
69976
+
69977
+ // ../../packages/app/src/workflow/guards/shell.ts
69978
+ function stringOption3(options, key, fallback) {
69979
+ const value = options[key];
69980
+ if (typeof value === "string")
69981
+ return value;
69982
+ if (fallback !== undefined)
69983
+ return fallback;
69984
+ throw new Error(`Guard option "${key}" must be a string`);
69985
+ }
69986
+ function arrayOption3(options, key) {
69987
+ const value = options[key];
69988
+ if (value === undefined)
69989
+ return [];
69990
+ if (Array.isArray(value) && value.every((entry) => typeof entry === "string"))
69991
+ return value;
69992
+ throw new Error(`Guard option "${key}" must be a string array`);
69993
+ }
69994
+
69995
+ class EnvShellGuardRunner {
69996
+ processExecutor;
69997
+ kind = KIND12;
69998
+ constructor(processExecutor) {
69999
+ this.processExecutor = processExecutor;
70000
+ }
70001
+ async evaluate(options, context4) {
70002
+ const command = stringOption3(options, "command");
70003
+ const explicitArgs = arrayOption3(options, "args");
70004
+ const usesShell = explicitArgs.length === 0;
70005
+ const spawn2 = usesShell ? { command: "/bin/sh", args: ["-c", command] } : { command, args: explicitArgs };
70006
+ const cwd = options.cwd === undefined ? context4.workdir : stringOption3(options, "cwd");
70007
+ const env = {};
70008
+ for (const [key, value] of Object.entries({ ...process.env, ...context4.vars })) {
70009
+ if (value !== undefined)
70010
+ env[key] = String(value);
70011
+ }
70012
+ const result = await this.processExecutor.run({
70013
+ command: spawn2.command,
70014
+ args: spawn2.args,
70015
+ ...cwd !== undefined ? { cwd } : {},
70016
+ env,
70017
+ rejectOnError: false,
70018
+ forceBuffered: true
70019
+ });
70020
+ return {
70021
+ passed: result.exitCode === 0,
70022
+ report: { stdout: result.stdout, stderr: result.stderr, exitCode: result.exitCode }
70023
+ };
70024
+ }
70025
+ }
70026
+ var KIND12 = "shell";
70027
+
69395
70028
  // ../../packages/app/src/workflow/builtins.ts
69396
70029
  function registerSpurBuiltins(host, options) {
69397
70030
  const fileSystem = options.fileSystem ?? createNodeFileSystem3();
69398
- host.registerAction(new AgentRunActionRunner(options.agentService, options.observabilityBus, options.steeringController, options.outputLog), "builtin");
70031
+ host.registerAction(new AgentRunActionRunner(options.agentService, options.observabilityBus, options.steeringController), "builtin");
70032
+ host.registerAction(new StreamingShellActionRunner(options.processExecutor ?? new NodeProcessExecutor3, options.observabilityBus), "builtin");
70033
+ host.registerGuard(new EnvShellGuardRunner(options.processExecutor ?? new NodeProcessExecutor3), "builtin");
69399
70034
  host.registerAction(new RuleCheckActionRunner(options.ruleService), "builtin");
69400
70035
  host.registerAction(new FileExistsActionRunner(fileSystem), "builtin");
69401
70036
  host.registerAction(new FileReadActionRunner(fileSystem), "builtin");
@@ -69418,159 +70053,7 @@ var init_builtins2 = __esm(() => {
69418
70053
  init_file_read_into_var();
69419
70054
  init_http_request();
69420
70055
  init_rule_check();
69421
- });
69422
-
69423
- // ../../packages/app/src/workflow/observability.ts
69424
- function bounded(value) {
69425
- const redacted = value.replace(SECRET_PATTERN2, "[REDACTED]");
69426
- return redacted.length <= MAX_FIELD_LENGTH3 ? redacted : `${redacted.slice(0, MAX_FIELD_LENGTH3)}\u2026`;
69427
- }
69428
- function projectActionMetadata(kind, options) {
69429
- if (options === undefined)
69430
- return;
69431
- const metadata = {};
69432
- if (typeof options.agent === "string" && options.agent !== "")
69433
- metadata.agent = bounded(options.agent);
69434
- if (typeof options.model === "string" && options.model !== "")
69435
- metadata.model = bounded(options.model);
69436
- if (typeof options.timeoutMs === "number" && Number.isFinite(options.timeoutMs)) {
69437
- metadata.timeoutMs = Math.max(0, options.timeoutMs);
69438
- }
69439
- const input = typeof options.input === "string" ? options.input.trim() : "";
69440
- if (kind === "agent.run" && input !== "") {
69441
- const command = input.startsWith("/") ? input.split(/\s+/, 1)[0] : undefined;
69442
- metadata.invocation = command === undefined ? `[prompt ${input.length} chars]` : bounded(command);
69443
- } else if (kind === "shell" && typeof options.command === "string") {
69444
- metadata.invocation = "[shell command redacted]";
69445
- }
69446
- return Object.keys(metadata).length === 0 ? undefined : metadata;
69447
- }
69448
- function projectResult(result) {
69449
- if (result === undefined)
69450
- return;
69451
- if (typeof result !== "object" || result === null)
69452
- return { usage: "unavailable" };
69453
- const error51 = "error" in result && typeof result.error === "string" ? bounded(result.error) : undefined;
69454
- return { ...error51 !== undefined ? { error: error51 } : {}, usage: "unavailable" };
69455
- }
69456
-
69457
- class ObservableWorkflowAdapter {
69458
- inner;
69459
- bus;
69460
- sequences = new Map;
69461
- workflowNames = new Map;
69462
- actions = new Map;
69463
- constructor(inner, bus) {
69464
- this.inner = inner;
69465
- this.bus = bus;
69466
- }
69467
- async createRun(record2) {
69468
- await this.inner.createRun(record2);
69469
- this.workflowNames.set(record2.id, record2.workflow_name);
69470
- await this.bus.emit("workflow.run.started", {
69471
- ...this.envelope(record2.id),
69472
- workflowName: record2.workflow_name
69473
- });
69474
- }
69475
- async finalizeRun(runId, status, completedAt) {
69476
- await this.inner.finalizeRun(runId, status, completedAt);
69477
- await this.bus.emit("workflow.run.finalized", { ...this.envelope(runId, completedAt), status });
69478
- }
69479
- async savePhase(runId, phase, status) {
69480
- await this.inner.savePhase(runId, phase, status);
69481
- await this.bus.emit("workflow.phase", { ...this.envelope(runId), phase, status });
69482
- }
69483
- async saveTransition(runId, from, to, trigger) {
69484
- await this.inner.saveTransition(runId, from, to, trigger);
69485
- await this.bus.emit("workflow.transition", { ...this.envelope(runId), from, to, trigger });
69486
- }
69487
- async commitTransition(runId, from, to, trigger, _state, _data, phase) {
69488
- await this.inner.commitTransition(runId, from, to, trigger, _state, _data, phase);
69489
- await this.bus.emit("workflow.transition", { ...this.envelope(runId), from, to, trigger });
69490
- if (phase) {
69491
- await this.bus.emit("workflow.phase", {
69492
- ...this.envelope(runId),
69493
- phase: phase.phase,
69494
- status: phase.status
69495
- });
69496
- }
69497
- }
69498
- async saveActionStart(runId, node, kind, options) {
69499
- const actionId = await this.inner.saveActionStart(runId, node, kind, options);
69500
- this.actions.set(actionId, { runId, node, kind });
69501
- const metadata = projectActionMetadata(kind, options);
69502
- await this.bus.emit("workflow.action.started", {
69503
- ...this.envelope(runId),
69504
- actionId,
69505
- node,
69506
- kind,
69507
- ...metadata !== undefined ? { metadata } : {}
69508
- });
69509
- return actionId;
69510
- }
69511
- async saveActionFinalize(actionId, status, durationMs, ok, result, redactor) {
69512
- await this.inner.saveActionFinalize(actionId, status, durationMs, ok, result, redactor);
69513
- const action = this.actions.get(actionId);
69514
- if (action === undefined) {
69515
- return;
69516
- }
69517
- this.actions.delete(actionId);
69518
- const projected = projectResult(result);
69519
- await this.bus.emit("workflow.action.finished", {
69520
- ...this.envelope(action.runId),
69521
- actionId,
69522
- status,
69523
- durationMs,
69524
- ok,
69525
- node: action.node,
69526
- kind: action.kind,
69527
- ...projected !== undefined ? { result: projected } : {}
69528
- });
69529
- }
69530
- envelope(runId, at = now()) {
69531
- const sequence = (this.sequences.get(runId) ?? 0) + 1;
69532
- this.sequences.set(runId, sequence);
69533
- const workflowName = this.workflowNames.get(runId);
69534
- return {
69535
- schemaVersion: 1,
69536
- eventId: crypto.randomUUID(),
69537
- sequence,
69538
- runId,
69539
- ...workflowName !== undefined ? { workflowName } : {},
69540
- at
69541
- };
69542
- }
69543
- saveWorkflowState(runId, state, data) {
69544
- return this.inner.saveWorkflowState(runId, state, data);
69545
- }
69546
- loadRun(runId) {
69547
- return this.inner.loadRun(runId);
69548
- }
69549
- listRuns() {
69550
- return this.inner.listRuns();
69551
- }
69552
- findRunByKey(workflowName, externalKey) {
69553
- return this.inner.findRunByKey(workflowName, externalKey);
69554
- }
69555
- createOrAttachRun(record2) {
69556
- return this.inner.createOrAttachRun(record2);
69557
- }
69558
- reseedRun(runId, newState) {
69559
- return this.inner.reseedRun(runId, newState);
69560
- }
69561
- loadCurrentState(runId) {
69562
- return this.inner.loadCurrentState(runId);
69563
- }
69564
- loadLatestStateSnapshot(runId) {
69565
- return this.inner.loadLatestStateSnapshot(runId);
69566
- }
69567
- listPausedRuns(options) {
69568
- return this.inner.listPausedRuns(options);
69569
- }
69570
- }
69571
- var now = () => new Date().toISOString(), MAX_FIELD_LENGTH3 = 256, SECRET_PATTERN2;
69572
- var init_observability = __esm(() => {
69573
- SECRET_PATTERN2 = /(?:sk-[a-z0-9_-]{8,}|(?:api[_-]?key|token|secret|password)\s*[:=]\s*\S+|bearer\s+\S+)/gi;
70056
+ init_shell();
69574
70057
  });
69575
70058
 
69576
70059
  // ../../packages/app/src/services/workflow-service.ts
@@ -69674,8 +70157,18 @@ class WorkflowAppService {
69674
70157
  });
69675
70158
  const runId = opts.runId ?? crypto.randomUUID();
69676
70159
  const isDry = opts.dryRun === true;
69677
- const runVars = { ...opts.vars ?? {}, __runId: runId };
69678
- const result = await svc.runFile(file2, {
70160
+ const runVars = {
70161
+ ...await resolveDefaultAgentVar(this.ctx.cwd, opts.vars),
70162
+ ...opts.vars ?? {},
70163
+ __runId: runId
70164
+ };
70165
+ const absolute = resolve4(this.ctx.cwd, file2);
70166
+ const embedded = this.embeddedSchemaOptions();
70167
+ const workflow = await loadWorkflowDef(absolute, {
70168
+ validateSchema: true,
70169
+ ...embedded !== undefined ? embedded : {}
70170
+ });
70171
+ const result = await svc.run(workflow, {
69679
70172
  workdir: this.ctx.cwd,
69680
70173
  runId,
69681
70174
  vars: runVars,
@@ -69696,7 +70189,7 @@ class WorkflowAppService {
69696
70189
  return;
69697
70190
  let workflowName;
69698
70191
  try {
69699
- const def = await loadWorkflowDef(resolve4(this.ctx.cwd, file2));
70192
+ const def = await loadWorkflowDef(resolve4(this.ctx.cwd, file2), { validateSchema: false });
69700
70193
  workflowName = def.name;
69701
70194
  } catch {
69702
70195
  return;
@@ -69740,6 +70233,37 @@ class WorkflowAppService {
69740
70233
  cleaned: stale.map((r) => ({ runId: r.id, startedAt: r.started_at }))
69741
70234
  };
69742
70235
  }
70236
+ async cleanRunLogs(retentionDays = 30, dryRun = false) {
70237
+ const runDir = join16(this.ctx.cwd, ".spur", "run");
70238
+ const fs3 = createNodeFileSystem3();
70239
+ const cutoffMs = Date.now() - retentionDays * 24 * 60 * 60 * 1000;
70240
+ const reclaimed = [];
70241
+ const failures = [];
70242
+ let entries;
70243
+ try {
70244
+ entries = (await fs3.readDir(runDir)).filter((name) => name.endsWith(".log"));
70245
+ } catch {
70246
+ return { retentionDays, dryRun, reclaimed, failures };
70247
+ }
70248
+ for (const name of entries) {
70249
+ const path9 = join16(runDir, name);
70250
+ const stat = await fs3.stat(path9);
70251
+ if (stat === null || !stat.isFile() || stat.mtimeMs >= cutoffMs)
70252
+ continue;
70253
+ const entry = { runId: name.slice(0, -".log".length), path: path9, mtime: new Date(stat.mtimeMs).toISOString() };
70254
+ if (dryRun) {
70255
+ reclaimed.push(entry);
70256
+ continue;
70257
+ }
70258
+ try {
70259
+ await fs3.deleteFile(path9);
70260
+ reclaimed.push(entry);
70261
+ } catch (err) {
70262
+ failures.push({ path: path9, error: String(err) });
70263
+ }
70264
+ }
70265
+ return { retentionDays, dryRun, reclaimed, failures };
70266
+ }
69743
70267
  async cancel(runId) {
69744
70268
  const db2 = await this.ctx.getDb();
69745
70269
  const dao2 = new RunDao(db2);
@@ -69755,20 +70279,23 @@ class WorkflowAppService {
69755
70279
  const finalized = after?.status === "failed" && before.status !== "failed";
69756
70280
  return { runId, finalized, status: after?.status ?? "not_found", killed };
69757
70281
  }
69758
- async continuePaused(runId) {
70282
+ async continuePaused(runId, opts) {
69759
70283
  const eventsBus = this.ctx.events?.();
69760
70284
  const svc = await this.createEngineService({ events: eventsBus });
69761
70285
  const run = await svc.listPausedRuns();
69762
70286
  const target = run.find((r) => r.id === runId);
69763
70287
  if (target === undefined) {
69764
- throw new Error(`Run "${runId}" is not paused (or does not exist) \u2014 nothing to continue.`);
70288
+ throw new Error(`Run "${runId}" is not paused (or does not exist) - nothing to continue.`);
69765
70289
  }
69766
70290
  const workflow = await this.resolveWorkflowDefByName(target.workflow_name);
69767
70291
  if (workflow === null) {
69768
70292
  throw new Error(`Cannot resume run "${runId}": workflow definition "${target.workflow_name}" not found in the workflow search paths.`);
69769
70293
  }
70294
+ const hitlVar = opts?.hitlVar ?? "__hitlAnswer";
70295
+ const resumeVars = opts?.hitlAnswer !== undefined ? { [hitlVar]: opts.hitlAnswer } : undefined;
69770
70296
  const result = await svc.resumeRun(workflow, runId, {
69771
70297
  workdir: this.ctx.cwd,
70298
+ ...resumeVars !== undefined ? { vars: resumeVars } : {},
69772
70299
  ...eventsBus !== undefined ? { events: bridgeEventBus(eventsBus) } : {}
69773
70300
  });
69774
70301
  await this.stampFailureReason(runId, result);
@@ -69920,8 +70447,8 @@ class WorkflowAppService {
69920
70447
  httpRequester: this.ctx.httpRequester?.(),
69921
70448
  hostAllowlist: this.ctx.hostAllowlist?.(),
69922
70449
  ...bus !== undefined ? { observabilityBus: bus } : {},
69923
- ...opts.steeringController !== undefined ? { steeringController: opts.steeringController } : {},
69924
- outputLog: await resolveOutputLogConfig(this.ctx.cwd)
70450
+ ...processExec !== undefined ? { processExecutor: processExec } : {},
70451
+ ...opts.steeringController !== undefined ? { steeringController: opts.steeringController } : {}
69925
70452
  });
69926
70453
  const db2 = await this.ctx.getDb();
69927
70454
  let persistence2 = new DbWorkflowPersistenceAdapter(db2);
@@ -69936,6 +70463,23 @@ async function fileExists(path9) {
69936
70463
  const fs3 = createNodeFileSystem3();
69937
70464
  return await fs3.exists(path9);
69938
70465
  }
70466
+ async function resolveDefaultAgentVar(cwd, callerVars) {
70467
+ if (callerVars?.agent !== undefined)
70468
+ return {};
70469
+ try {
70470
+ const configured = (await loadSpurConfig(cwd)).agent?.default;
70471
+ return typeof configured === "string" && configured.length > 0 ? { agent: configured } : {};
70472
+ } catch {
70473
+ return {};
70474
+ }
70475
+ }
70476
+ async function resolveWorkflowLogRetentionDays(cwd) {
70477
+ try {
70478
+ return (await loadSpurConfig(cwd)).workflow?.logRetentionDays ?? 30;
70479
+ } catch {
70480
+ return 30;
70481
+ }
70482
+ }
69939
70483
  async function resolveOutputLogConfig(cwd) {
69940
70484
  try {
69941
70485
  const output2 = (await loadSpurConfig(cwd)).agent?.output;
@@ -69950,7 +70494,7 @@ async function resolveOutputLogConfig(cwd) {
69950
70494
  }
69951
70495
  }
69952
70496
  async function outputArtifactForRun(cwd, runId) {
69953
- const relative4 = join16(".spur", "run", `${runId}-output.log`);
70497
+ const relative4 = join16(".spur", "run", `${runId}.log`);
69954
70498
  return await fileExists(join16(cwd, relative4)) ? relative4 : undefined;
69955
70499
  }
69956
70500
  async function scanWorkflowFiles(rootPath, source) {
@@ -70050,7 +70594,9 @@ class LifecycleAdapter {
70050
70594
  async requestTransition(ref, currentStatus, to) {
70051
70595
  const { profile } = this.opts;
70052
70596
  const db2 = await this.opts.getDb();
70053
- const svc = new WorkflowService(createDefaultWorkflowEngineHost(), new DbWorkflowPersistenceAdapter(db2));
70597
+ const host = createDefaultWorkflowEngineHost();
70598
+ host.registerGuard(new EnvShellGuardRunner(new NodeProcessExecutor3), "builtin");
70599
+ const svc = new WorkflowService(host, new DbWorkflowPersistenceAdapter(db2));
70054
70600
  const workflow = this.bindGuardVar(await this.loadWorkflow(), ref.id);
70055
70601
  const externalKey = `${profile.entityPrefix}:${ref.id}`;
70056
70602
  const now2 = new Date().toISOString();
@@ -70184,87 +70730,6 @@ var init_lifecycle_adapter = __esm(() => {
70184
70730
  };
70185
70731
  });
70186
70732
 
70187
- // ../../packages/app/src/workflow/step-reporter.ts
70188
- function renderStepLine(event2, options = {}) {
70189
- const detail = options.detail ?? "invocation";
70190
- if (isAgentExecution(event2)) {
70191
- if (event2.kind === "output") {
70192
- if (detail === "minimal")
70193
- return null;
70194
- const label = event2.stream === "stderr" ? "stderr" : "stdout";
70195
- return `[run ${event2.runId}] ${label}> ${event2.chunk.replace(/\s+$/, "")}`;
70196
- }
70197
- if (event2.kind === "heartbeat") {
70198
- if (detail === "minimal")
70199
- return null;
70200
- const budget = event2.timeoutMs === undefined ? "unbounded" : `${formatDuration(Math.max(0, event2.timeoutMs - event2.elapsedMs))} remaining`;
70201
- return `[run ${event2.runId}] \u2026 agent execution \xB7 elapsed=${formatDuration(event2.elapsedMs)} \xB7 budget=${budget}`;
70202
- }
70203
- if (event2.kind === "dropped") {
70204
- return detail === "minimal" ? null : `[run ${event2.runId}] \u2026 output pressure \xB7 dropped=${event2.chunks} chunks`;
70205
- }
70206
- if (event2.kind === "started") {
70207
- if (detail === "minimal")
70208
- return null;
70209
- const model = event2.model === undefined ? "" : `(${event2.model})`;
70210
- const full2 = detail === "full" ? ` \xB7 execution=${event2.executionId}` : "";
70211
- return `[run ${event2.runId}] agent=${event2.agent}${model} => ${event2.invocation}${full2}`;
70212
- }
70213
- const mark = event2.outcome === "done" ? "\u2713" : "\u2717";
70214
- const full = detail === "full" ? ` \xB7 execution=${event2.executionId}` : "";
70215
- return detail === "minimal" ? null : `[run ${event2.runId}] ${mark} agent ${event2.outcome} (${formatDuration(event2.durationMs)}) \xB7 usage ${event2.usage}${event2.reason ? ` \xB7 ${event2.reason}` : ""}${full}`;
70216
- }
70217
- if (isActionFinished(event2)) {
70218
- const mark = event2.ok ? "\u2713" : "\u2717";
70219
- if (detail === "minimal")
70220
- return ` ${mark} ${event2.status} (${formatDuration(event2.durationMs)})`;
70221
- const failure = event2.result?.error === undefined ? "" : ` \xB7 ${event2.result.error}`;
70222
- const full = detail === "full" ? ` \xB7 action=${event2.actionId} \xB7 seq=${event2.sequence}` : "";
70223
- return `[run ${event2.runId}] ${mark} ${event2.node}/${event2.kind} (${formatDuration(event2.durationMs)}) \xB7 usage ${event2.result?.usage ?? "unavailable"}${failure}${full}`;
70224
- }
70225
- if (isActionStarted(event2)) {
70226
- if (detail === "minimal")
70227
- return ` \u2192 ${event2.node}: ${event2.kind}\u2026`;
70228
- const agent = event2.metadata?.agent ?? "unavailable";
70229
- const model = event2.metadata?.model ?? "unavailable";
70230
- const invocation = event2.metadata?.invocation ?? "unavailable";
70231
- const timeout = event2.metadata?.timeoutMs === undefined ? "unbounded" : formatDuration(event2.metadata.timeoutMs);
70232
- const full = detail === "full" ? ` \xB7 action=${event2.actionId} \xB7 seq=${event2.sequence}` : "";
70233
- return `[run ${event2.runId}] \u2192 ${event2.node}/${event2.kind} \xB7 agent=${agent} \xB7 model=${model} => ${invocation} \xB7 timeout=${timeout}${full}`;
70234
- }
70235
- if (isPhase(event2)) {
70236
- return detail === "minimal" ? `\u25B6 ${event2.phase} [${event2.status}]` : `[run ${event2.runId}] \u25B6 ${event2.phase} [${event2.status}]`;
70237
- }
70238
- if (isTransition(event2)) {
70239
- if (detail !== "full")
70240
- return null;
70241
- return `[run ${event2.runId}] \u21AA ${event2.from} \u2192 ${event2.to}${event2.trigger ? ` [${event2.trigger}]` : ""} \xB7 seq=${event2.sequence}`;
70242
- }
70243
- return null;
70244
- }
70245
- function renderActionHeartbeat(event2, elapsedMs, options = {}) {
70246
- const detail = options.detail ?? "invocation";
70247
- if (detail === "minimal")
70248
- return null;
70249
- const elapsed = formatDuration(elapsedMs);
70250
- const budget = event2.metadata?.timeoutMs === undefined ? "unbounded" : `${formatDuration(Math.max(0, event2.metadata.timeoutMs - elapsedMs))} remaining`;
70251
- const full = detail === "full" ? ` \xB7 action=${event2.actionId}` : "";
70252
- return `[run ${event2.runId}] \u2026 ${event2.node}/${event2.kind} \xB7 elapsed=${elapsed} \xB7 budget=${budget}${full}`;
70253
- }
70254
- function renderRunPlan(def) {
70255
- const steps = def.kind === "transition-flow" ? def.nodes.map((n2) => n2.id) : def.states.map((s) => s.id);
70256
- return `plan: ${steps.join(" \u2192 ")}`;
70257
- }
70258
- function formatDuration(ms) {
70259
- const totalSeconds = Math.round(ms / 1000);
70260
- if (totalSeconds < 60)
70261
- return `${totalSeconds}s`;
70262
- const minutes = Math.floor(totalSeconds / 60);
70263
- const seconds = totalSeconds % 60;
70264
- return `${minutes}m ${seconds}s`;
70265
- }
70266
- 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);
70267
-
70268
70733
  // ../../packages/app/src/workflow/trace-writer.ts
70269
70734
  import { dirname as dirname13, join as join17 } from "path";
70270
70735
 
@@ -70317,9 +70782,11 @@ __export(exports_src2, {
70317
70782
  summarizeActionResult: () => summarizeActionResult,
70318
70783
  startRegisteredProject: () => startRegisteredProject,
70319
70784
  setDetachedServeSpawnForTests: () => setDetachedServeSpawnForTests,
70785
+ resolveWorkflowLogRetentionDays: () => resolveWorkflowLogRetentionDays,
70320
70786
  resolveSpurServeCommand: () => resolveSpurServeCommand,
70321
70787
  resolveRetentionQuotas: () => resolveRetentionQuotas,
70322
70788
  resolvePlanningFolders: () => resolvePlanningFolders,
70789
+ resolveOutputLogConfig: () => resolveOutputLogConfig,
70323
70790
  resolveAutostartSet: () => resolveAutostartSet,
70324
70791
  renderTesting: () => renderTesting,
70325
70792
  renderStepLine: () => renderStepLine,
@@ -70352,6 +70819,7 @@ __export(exports_src2, {
70352
70819
  extractSystemEventActor: () => extractSystemEventActor,
70353
70820
  extractReviewSectionBody: () => extractReviewSectionBody,
70354
70821
  evaluateDoneTransition: () => evaluateDoneTransition,
70822
+ ensurePipelineRunLink: () => ensurePipelineRunLink,
70355
70823
  encodeRunListCursor: () => encodeRunListCursor,
70356
70824
  deriveVerdict: () => deriveVerdict,
70357
70825
  decodeRunListCursor: () => decodeRunListCursor,
@@ -70365,6 +70833,7 @@ __export(exports_src2, {
70365
70833
  aggregateBatchVerdicts: () => aggregateBatchVerdicts,
70366
70834
  WorkflowTraceWriter: () => WorkflowTraceWriter,
70367
70835
  WorkflowSteeringController: () => WorkflowSteeringController,
70836
+ WorkflowRunLogSink: () => WorkflowRunLogSink,
70368
70837
  WorkflowAppService: () => WorkflowAppService,
70369
70838
  WbsCollisionError: () => WbsCollisionError,
70370
70839
  UnsupportedProcessPlatformError: () => UnsupportedProcessPlatformError,
@@ -70379,6 +70848,7 @@ __export(exports_src2, {
70379
70848
  TOKEN_LEDGER_MAX_LIMIT: () => TOKEN_LEDGER_MAX_LIMIT,
70380
70849
  TOKEN_LEDGER_DEFAULT_LIMIT: () => TOKEN_LEDGER_DEFAULT_LIMIT,
70381
70850
  TASK_LIFECYCLE_PROFILE: () => TASK_LIFECYCLE_PROFILE,
70851
+ TASK_FORWARD_CHAIN: () => TASK_FORWARD_CHAIN,
70382
70852
  TASK_ACTION_COMMANDS: () => TASK_ACTION_COMMANDS,
70383
70853
  SystemEventEmitter: () => SystemEventEmitter,
70384
70854
  SupervisorService: () => SupervisorService,
@@ -70426,6 +70896,7 @@ __export(exports_src2, {
70426
70896
  DuplicateFollowUpError: () => DuplicateFollowUpError,
70427
70897
  DependencyMutationError: () => DependencyMutationError,
70428
70898
  DEFAULT_SYSTEM_EVENT_RETENTION_QUOTA: () => DEFAULT_SYSTEM_EVENT_RETENTION_QUOTA,
70899
+ DEFAULT_RUN_LOG_MAX_BYTES: () => DEFAULT_RUN_LOG_MAX_BYTES,
70429
70900
  DEFAULT_FEATURE_MATRIX: () => DEFAULT_FEATURE_MATRIX,
70430
70901
  CorpusMigrator: () => CorpusMigrator,
70431
70902
  BusPlanningEventEmitter: () => BusPlanningEventEmitter,
@@ -70438,6 +70909,7 @@ var init_src3 = __esm(() => {
70438
70909
  init_planning_folders();
70439
70910
  init_errors5();
70440
70911
  init_agent_execution();
70912
+ init_workflow_run_log_sink();
70441
70913
  init_agent_service();
70442
70914
  init_corpus_migrator();
70443
70915
  init_event_names();
@@ -70445,6 +70917,7 @@ var init_src3 = __esm(() => {
70445
70917
  init_feature_service();
70446
70918
  init_finding_codes2();
70447
70919
  init_history_service();
70920
+ init_pipeline_run_link();
70448
70921
  init_planning_events();
70449
70922
  init_planning_write_service();
70450
70923
  init_process_inspector();
@@ -70557,19 +71030,19 @@ var {
70557
71030
  init_src();
70558
71031
  init_loader();
70559
71032
 
70560
- // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.16+4e212f5ced9864ce/node_modules/@gobing-ai/ts-infra/dist/application-node.js
71033
+ // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.18+863f28addc1a7cdc/node_modules/@gobing-ai/ts-infra/dist/application-node.js
70561
71034
  init_dist4();
70562
71035
  init_dist5();
70563
71036
  import { appendFileSync as appendFileSync4, mkdirSync as mkdirSync3, readFileSync as readFileSync5 } from "fs";
70564
71037
  import { dirname as dirname5 } from "path";
70565
71038
 
70566
- // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.16+4e212f5ced9864ce/node_modules/@gobing-ai/ts-infra/dist/application/index.js
71039
+ // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.18+863f28addc1a7cdc/node_modules/@gobing-ai/ts-infra/dist/application/index.js
70567
71040
  init_default_observers();
70568
71041
  init_event_bus();
70569
71042
  init_file_observer();
70570
71043
  init_logger3();
70571
71044
 
70572
- // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.16+4e212f5ced9864ce/node_modules/@gobing-ai/ts-infra/dist/scheduler/noop.js
71045
+ // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.18+863f28addc1a7cdc/node_modules/@gobing-ai/ts-infra/dist/scheduler/noop.js
70573
71046
  class NoopSchedulerAdapter {
70574
71047
  constructor() {}
70575
71048
  register(_cron, _action) {}
@@ -70577,7 +71050,7 @@ class NoopSchedulerAdapter {
70577
71050
  async stop() {}
70578
71051
  }
70579
71052
 
70580
- // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.16+4e212f5ced9864ce/node_modules/@gobing-ai/ts-infra/dist/scheduler/factory.js
71053
+ // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.18+863f28addc1a7cdc/node_modules/@gobing-ai/ts-infra/dist/scheduler/factory.js
70581
71054
  function initScheduler(adapter, cronEntries) {
70582
71055
  const resolved = adapter ?? new NoopSchedulerAdapter;
70583
71056
  if (cronEntries) {
@@ -70588,7 +71061,7 @@ function initScheduler(adapter, cronEntries) {
70588
71061
  return resolved;
70589
71062
  }
70590
71063
 
70591
- // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.16+4e212f5ced9864ce/node_modules/@gobing-ai/ts-infra/dist/application/plugins/builtins.js
71064
+ // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.18+863f28addc1a7cdc/node_modules/@gobing-ai/ts-infra/dist/application/plugins/builtins.js
70592
71065
  init_logger3();
70593
71066
  init_metrics();
70594
71067
  init_sdk();
@@ -70681,7 +71154,7 @@ function dbPlugin(db2) {
70681
71154
  };
70682
71155
  }
70683
71156
 
70684
- // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.16+4e212f5ced9864ce/node_modules/@gobing-ai/ts-infra/dist/application/plugins/host.js
71157
+ // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.18+863f28addc1a7cdc/node_modules/@gobing-ai/ts-infra/dist/application/plugins/host.js
70685
71158
  init_logger3();
70686
71159
 
70687
71160
  class PluginHost {
@@ -70769,7 +71242,7 @@ class PluginHost {
70769
71242
  }
70770
71243
  }
70771
71244
 
70772
- // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.16+4e212f5ced9864ce/node_modules/@gobing-ai/ts-infra/dist/application/index.js
71245
+ // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.18+863f28addc1a7cdc/node_modules/@gobing-ai/ts-infra/dist/application/index.js
70773
71246
  async function performShutdown(state, reason) {
70774
71247
  if (state.stopped)
70775
71248
  return;
@@ -70880,10 +71353,10 @@ async function runApplication(options) {
70880
71353
  }
70881
71354
  }
70882
71355
 
70883
- // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.16+4e212f5ced9864ce/node_modules/@gobing-ai/ts-infra/dist/application-node.js
71356
+ // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.18+863f28addc1a7cdc/node_modules/@gobing-ai/ts-infra/dist/application-node.js
70884
71357
  init_scheduler_node();
70885
71358
 
70886
- // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.16+4e212f5ced9864ce/node_modules/@gobing-ai/ts-infra/dist/telemetry/otel-node.js
71359
+ // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.18+863f28addc1a7cdc/node_modules/@gobing-ai/ts-infra/dist/telemetry/otel-node.js
70887
71360
  var import_api14 = __toESM(require_src(), 1);
70888
71361
 
70889
71362
  // ../../node_modules/.bun/@opentelemetry+exporter-metrics-otlp-http@0.218.0+e40b0dfdd726a224/node_modules/@opentelemetry/exporter-metrics-otlp-http/build/esm/OTLPMetricExporterBase.js
@@ -73899,7 +74372,7 @@ class OTLPTraceExporter extends import_otlp_exporter_base2.OTLPExporterBase {
73899
74372
  }), JsonTraceSerializer));
73900
74373
  }
73901
74374
  }
73902
- // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.16+4e212f5ced9864ce/node_modules/@gobing-ai/ts-infra/dist/telemetry/otel-node.js
74375
+ // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.18+863f28addc1a7cdc/node_modules/@gobing-ai/ts-infra/dist/telemetry/otel-node.js
73903
74376
  var import_resources2 = __toESM(require_src4(), 1);
73904
74377
  var import_sdk_trace_node = __toESM(require_src8(), 1);
73905
74378
  var import_semantic_conventions = __toESM(require_src2(), 1);
@@ -73950,7 +74423,7 @@ async function shutdownNodeTelemetry() {
73950
74423
  await Promise.all(pending);
73951
74424
  }
73952
74425
 
73953
- // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.16+4e212f5ced9864ce/node_modules/@gobing-ai/ts-infra/dist/application-node.js
74426
+ // ../../node_modules/.bun/@gobing-ai+ts-infra@0.4.18+863f28addc1a7cdc/node_modules/@gobing-ai/ts-infra/dist/application-node.js
73954
74427
  class ConfigValidationError extends Error {
73955
74428
  constructor(message) {
73956
74429
  super(message);
@@ -78348,7 +78821,15 @@ function registerFeatureCommand(program2, context4) {
78348
78821
  context4.setExitCode(1);
78349
78822
  }
78350
78823
  });
78351
- feature.command("refresh").summary("Regenerate INDEX.md (ID-encoded tree) and repopulate each feature ## Tasks region.").option("--feature <id>", "Restrict the ## Tasks rewrite to one feature (INDEX.md still regenerated)").option("--folder <path>", "Custom features folder").option("--json", "Output machine-readable JSON").action(async (options) => {
78824
+ feature.command("refresh").summary("Rebuild INDEX.md and each feature ## Tasks table from task edges (docs only \u2014 no status change).").description([
78825
+ "Regenerate derived views so feature markdown matches the task graph:",
78826
+ " \u2022 docs/features/INDEX.md (ID-encoded tree)",
78827
+ " \u2022 each feature ## Tasks auto-gen region (WBS / title / status from feature_id edges)",
78828
+ "",
78829
+ "Does NOT change feature frontmatter status. For lifecycle alignment use `spur feature sync`.",
78830
+ "Use after task create/link/done when the ## Tasks table is stale."
78831
+ ].join(`
78832
+ `)).option("--feature <id>", "Restrict the ## Tasks rewrite to one feature (INDEX.md still regenerated)").option("--folder <path>", "Custom features folder").option("--json", "Output machine-readable JSON").action(async (options) => {
78352
78833
  const svc = await makeService(context4, options.folder);
78353
78834
  try {
78354
78835
  const result = await svc.refresh({ featureId: options.feature });
@@ -78410,7 +78891,16 @@ ${result.id} (${result.status}): ${result.pass ? "PASS" : "FAIL"}`);
78410
78891
  context4.setExitCode(1);
78411
78892
  }
78412
78893
  });
78413
- feature.command("sync").summary("Sync feature status with linked task states.").argument("[id]", "Feature ID to sync (optional if --all is passed)").option("--all", "Sync all features with linked tasks").option("--dry-run", "Report proposed status sync transitions without applying").option("--force", "Force applying reopen proposals without confirmation").option("--folder <path>", "Custom features folder").option("--json", "Output machine-readable JSON").action(async (id, options) => {
78894
+ feature.command("sync").summary("Align feature lifecycle status with linked task states (status only \u2014 runs transition guards).").description([
78895
+ "Derive a feature status proposal from linked task statuses and apply legal hops",
78896
+ "(e.g. all tasks done \u2192 advance toward done; reopened work \u2192 reopen the feature).",
78897
+ "",
78898
+ "Applies real lifecycle transitions \u2014 gates such as dogfood, one-active-goal, and",
78899
+ "L4 AC readiness may deny a hop. Preview with --dry-run first.",
78900
+ "",
78901
+ "Does NOT rewrite INDEX.md or ## Tasks tables. For stale rosters use `spur feature refresh`."
78902
+ ].join(`
78903
+ `)).argument("[id]", "Feature ID to sync (optional if --all is passed)").option("--all", "Sync all features with linked tasks").option("--dry-run", "Report proposed status sync transitions without applying").option("--force", "Force applying reopen proposals without confirmation").option("--folder <path>", "Custom features folder").option("--json", "Output machine-readable JSON").action(async (id, options) => {
78414
78904
  const svc = await makeService(context4, options.folder);
78415
78905
  try {
78416
78906
  if (!options.all && !id) {
@@ -78541,7 +79031,7 @@ import { join as join19, resolve as resolve6 } from "path";
78541
79031
  var CLI_CONFIG = {
78542
79032
  binaryName: "spur",
78543
79033
  binaryLabel: "spur",
78544
- binaryVersion: "0.3.30",
79034
+ binaryVersion: "0.3.32",
78545
79035
  configDir: ".spur",
78546
79036
  configFile: ".spur/config.yaml",
78547
79037
  databaseFile: ".spur/spur.db"
@@ -89406,6 +89896,11 @@ var state_machine_workflow_schema_default = {
89406
89896
  description: { type: "string" },
89407
89897
  initialState: { type: "string", minLength: 1 },
89408
89898
  terminalStates: { type: "array", items: { type: "string", minLength: 1 } },
89899
+ failureStates: {
89900
+ type: "array",
89901
+ items: { type: "string", minLength: 1 },
89902
+ description: "Subset of terminalStates whose reach finalizes the run as failed. Absent \u21D2 every terminal is a success."
89903
+ },
89409
89904
  iterationBound: { type: "integer", exclusiveMinimum: 0 },
89410
89905
  vars: {
89411
89906
  type: "object",
@@ -90235,37 +90730,22 @@ ${result.wbs} (${result.status}): ${result.pass ? "PASS" : "FAIL"}`);
90235
90730
  task.command("run-link").summary("Record a pipeline provenance link for a task (used by --next auto chains to satisfy testing\u2192done guard).").argument("<wbs>", "Task WBS number").option("--source <source>", "Link source identifier (e.g. next-auto)", "chain").option("--run-id <id>", "Explicit run_id (auto-generated when omitted)").option("--json", "Output machine-readable JSON").action(async (wbs, options) => {
90236
90731
  try {
90237
90732
  const db2 = await context4.getDb();
90238
- const dao2 = new TaskRunLinkDao(db2);
90239
- const existing = await dao2.listByWbs(wbs, 20);
90240
- if (existing.some((row) => row.kind === "pipeline")) {
90241
- const entry = existing.find((row) => row.kind === "pipeline");
90242
- if (!entry) {
90243
- context4.output.error(`Pipeline link lookup inconsistent for ${wbs}`);
90244
- context4.setExitCode(1);
90245
- return;
90246
- }
90247
- const result2 = {
90248
- id: entry.id,
90249
- wbs: entry.wbs,
90250
- runId: entry.run_id,
90251
- kind: entry.kind,
90252
- existed: true
90253
- };
90254
- if (options.json) {
90255
- context4.output.write(toJson2(result2));
90256
- } else {
90257
- context4.output.write(`Pipeline run-link already exists for ${wbs} (${entry.id}). Skipped.`);
90258
- }
90259
- return;
90260
- }
90261
- const id = createId("trl");
90262
- const runId = options.runId ?? `chain:${options.source}:${wbs}:${Date.now()}`;
90263
- await dao2.insert({ id, wbs, run_id: runId, kind: "pipeline", created_at: new Date().toISOString() });
90264
- const result = { id, wbs, runId, kind: "pipeline" };
90733
+ const ensured = await ensurePipelineRunLink(db2, wbs, {
90734
+ runId: options.runId ?? `chain:${options.source}:${wbs}:${Date.now()}`
90735
+ });
90736
+ const result = {
90737
+ id: ensured.id,
90738
+ wbs: ensured.wbs,
90739
+ runId: ensured.runId,
90740
+ kind: ensured.kind,
90741
+ existed: !ensured.created
90742
+ };
90265
90743
  if (options.json) {
90266
90744
  context4.output.write(toJson2(result));
90745
+ } else if (ensured.created) {
90746
+ context4.output.write(`Recorded pipeline run-link ${ensured.id} for task ${wbs} (source: ${options.source})`);
90267
90747
  } else {
90268
- context4.output.write(`Recorded pipeline run-link ${id} for task ${wbs} (source: ${options.source})`);
90748
+ context4.output.write(`Pipeline run-link already exists for ${wbs} (${ensured.id}). Skipped.`);
90269
90749
  }
90270
90750
  } catch (err) {
90271
90751
  context4.output.error(String(err));
@@ -90312,6 +90792,7 @@ async function makeService2(context4, folderOverride, noLifecycle = false) {
90312
90792
  fs: context4.fs,
90313
90793
  tasksDir,
90314
90794
  writeService,
90795
+ getDb: () => context4.getDb(),
90315
90796
  sectionMatrix: await loadSectionMatrix(context4.cwd),
90316
90797
  resolveTemplate: (variant) => loadTemplateContent(context4.cwd, variant),
90317
90798
  resolveTemplateBodies: (variant) => loadTemplateBodies(context4.cwd, variant),
@@ -90692,7 +91173,8 @@ init_loader();
90692
91173
  init_dist7();
90693
91174
  init_dist6();
90694
91175
  init_dist5();
90695
- import { resolve as resolve8 } from "path";
91176
+ import { closeSync as closeSync5, fstatSync as fstatSync3, openSync as openSync5, readSync as readSync3 } from "fs";
91177
+ import { join as join27, resolve as resolve8 } from "path";
90696
91178
  import { createInterface } from "readline";
90697
91179
  import { setTimeout as sleep3 } from "timers/promises";
90698
91180
  function shQuote2(value2) {
@@ -90776,7 +91258,7 @@ ${result.errors.map((m) => ` - ${m}`).join(`
90776
91258
  }
90777
91259
  context4.setExitCode(result.valid ? 0 : 1);
90778
91260
  });
90779
- 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) => {
91261
+ 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("--no-log", "Opt out of writing the consolidated .spur/run/<RUNID>.log").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) => {
90780
91262
  const json3 = options.json === true;
90781
91263
  const silent = !json3 && options.silent === true;
90782
91264
  const quiet = !json3 && options.quiet === true;
@@ -90817,6 +91299,9 @@ ${result.errors.map((m) => ` - ${m}`).join(`
90817
91299
  if (options.traceFile) {
90818
91300
  cmd.push("--trace-file");
90819
91301
  }
91302
+ if (options.log === false) {
91303
+ cmd.push("--no-log");
91304
+ }
90820
91305
  try {
90821
91306
  await spawnAsyncWorkflowWorker(spurBin, cmd);
90822
91307
  } catch {
@@ -90854,27 +91339,38 @@ Monitor with: spur workflow trace ${runId2} --follow`);
90854
91339
  traceWriter = new WorkflowTraceWriter(context4.cwd, runId);
90855
91340
  traceWriter.attach(bus);
90856
91341
  }
91342
+ let planPreview;
91343
+ if (options.plan !== false) {
91344
+ try {
91345
+ const def = await loadWorkflowDef(resolve8(context4.cwd, file2), { validateSchema: false });
91346
+ planPreview = renderRunPlan(def);
91347
+ } catch {}
91348
+ }
91349
+ const runLog = options.log === false ? undefined : new WorkflowRunLogSink({
91350
+ bus,
91351
+ dir: join27(context4.cwd, ".spur", "run"),
91352
+ runId,
91353
+ ...planPreview !== undefined ? { planPreview } : {},
91354
+ ...await resolveOutputLogConfig(context4.cwd)
91355
+ });
90857
91356
  if (humanProgress) {
90858
91357
  context4.output.write(`Run: ${runId}`);
90859
- if (options.plan !== false) {
90860
- try {
90861
- const def = await loadWorkflowDef(resolve8(context4.cwd, file2), { validateSchema: false });
90862
- context4.output.write(renderRunPlan(def));
90863
- } catch {}
90864
- }
91358
+ if (planPreview !== undefined)
91359
+ context4.output.write(planPreview);
91360
+ const stepOptions = { detail, showRunId: false };
90865
91361
  const report = (event2) => {
90866
- const line = renderStepLine(event2, { detail });
91362
+ const line = renderStepLine(event2, stepOptions);
90867
91363
  if (line !== null)
90868
91364
  context4.output.write(line);
90869
91365
  };
90870
91366
  bus.on("workflow.phase", report);
90871
- if (detail === "full")
91367
+ if (detail !== "minimal")
90872
91368
  bus.on("workflow.transition", report);
90873
91369
  bus.on("workflow.action.started", (event2) => {
90874
91370
  report(event2);
90875
91371
  const startedAt3 = Date.parse(event2.at);
90876
91372
  const timer = setInterval(() => {
90877
- const line = renderActionHeartbeat(event2, Math.max(0, Date.now() - startedAt3), { detail });
91373
+ const line = renderActionHeartbeat(event2, Math.max(0, Date.now() - startedAt3), stepOptions);
90878
91374
  if (line !== null)
90879
91375
  context4.output.write(line);
90880
91376
  }, 30000);
@@ -90897,6 +91393,7 @@ Monitor with: spur workflow trace ${runId2} --follow`);
90897
91393
  }
90898
91394
  report(event2);
90899
91395
  });
91396
+ bus.on("workflow.action.output", report);
90900
91397
  }
90901
91398
  const steeringController = options.steer === true ? new WorkflowSteeringController((ack) => {
90902
91399
  bus.emit("workflow.steering", ack);
@@ -90924,6 +91421,7 @@ Monitor with: spur workflow trace ${runId2} --follow`);
90924
91421
  clearInterval(timer);
90925
91422
  heartbeats.clear();
90926
91423
  await traceWriter?.flush();
91424
+ runLog?.close();
90927
91425
  await ledger.flush();
90928
91426
  ledger.unsubscribe();
90929
91427
  steeringInput?.close();
@@ -90935,8 +91433,18 @@ Monitor with: spur workflow trace ${runId2} --follow`);
90935
91433
  }
90936
91434
  context4.setExitCode(result.status === "done" ? 0 : 1);
90937
91435
  });
90938
- 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) => {
91436
+ 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("--answer <yes|no|cancel>", "Inject a HITL gate answer before guard re-evaluation (0433). Does not imply --yes.").option("--json", "Output machine-readable JSON where supported").action(async (runId, options) => {
90939
91437
  const json3 = options.json === true;
91438
+ let hitlAnswer;
91439
+ if (options.answer !== undefined) {
91440
+ const v = String(options.answer).toLowerCase();
91441
+ if (v !== "yes" && v !== "no" && v !== "cancel") {
91442
+ context4.output.error(`Invalid --answer value "${options.answer}" - must be yes, no, or cancel.`);
91443
+ context4.setExitCode(2);
91444
+ return;
91445
+ }
91446
+ hitlAnswer = v;
91447
+ }
90940
91448
  const bus = new EventBus;
90941
91449
  const ledger = await attachSystemEventLedger(bus, context4);
90942
91450
  const svc = makeSvc(json3, bus);
@@ -90958,13 +91466,15 @@ Monitor with: spur workflow trace ${runId2} --follow`);
90958
91466
  node: "continue"
90959
91467
  });
90960
91468
  if (answer.value !== "yes") {
90961
- context4.output.error(`Aborted \u2014 run ${latest.runId} not resumed.`);
91469
+ context4.output.error(`Aborted - run ${latest.runId} not resumed.`);
90962
91470
  context4.setExitCode(1);
90963
91471
  return;
90964
91472
  }
90965
91473
  }
90966
91474
  }
90967
- const result = await svc.continuePaused(targetId);
91475
+ const result = await svc.continuePaused(targetId, {
91476
+ ...hitlAnswer !== undefined ? { hitlAnswer } : {}
91477
+ });
90968
91478
  context4.output.write(json3 ? toJson2(result) : `workflow ${result.status}: ${result.workflowName} -> ${result.finalState}`);
90969
91479
  context4.setExitCode(result.status === "done" ? 0 : 1);
90970
91480
  } catch (err) {
@@ -90975,7 +91485,9 @@ Monitor with: spur workflow trace ${runId2} --follow`);
90975
91485
  ledger.unsubscribe();
90976
91486
  }
90977
91487
  });
90978
- workflow.command("clean").description("Finalize orphaned runs stuck in running/pending past a staleness threshold (mark as failed). " + "To cancel a single live run by id, use `spur workflow cancel <run-id>` instead.").option("--older-than <minutes>", "Staleness threshold in minutes", "30").option("--force", "Clean ALL non-terminal runs regardless of age (overrides --older-than)").option("--dry-run", "List what would be cleaned without writing").option("--json", "Output machine-readable JSON where supported").action(async (options) => {
91488
+ workflow.command("clean").description("Housekeeping: finalize orphaned runs stuck in running/pending past a staleness threshold " + "(mark as failed) and reclaim retained run logs older than workflow.logRetentionDays. " + "`--logs` scopes to log reclamation only. To cancel a single live run by id, use " + "`spur workflow cancel <run-id>` instead.").option("--older-than <minutes>", "Staleness threshold in minutes (stale-run scope only)", "30").option("--force", "Clean ALL non-terminal runs regardless of age (overrides --older-than)").option("--logs", "Scope to retained run-log reclamation only (skip stale-run finalization)").option("--dry-run", "List what would be cleaned without writing (applies to both scopes)").option("--json", "Output machine-readable JSON where supported").action(async (options) => {
91489
+ const dryRun = options.dryRun === true;
91490
+ const logsOnly = options.logs === true;
90979
91491
  const force = options.force === true;
90980
91492
  const minutes = force ? 0 : Number.parseInt(options.olderThan ?? "30", 10);
90981
91493
  if (!Number.isFinite(minutes) || minutes < 0) {
@@ -90983,19 +91495,35 @@ Monitor with: spur workflow trace ${runId2} --follow`);
90983
91495
  context4.setExitCode(2);
90984
91496
  return;
90985
91497
  }
90986
- const result = await makeSvc(options.json).clean(minutes, options.dryRun === true);
91498
+ const svc = makeSvc(options.json);
91499
+ const result = logsOnly ? undefined : await svc.clean(minutes, dryRun);
91500
+ const retentionDays = await resolveWorkflowLogRetentionDays(context4.cwd);
91501
+ const logResult2 = await svc.cleanRunLogs(retentionDays, dryRun);
90987
91502
  if (options.json) {
90988
- context4.output.write(toJson2(result));
91503
+ context4.output.write(toJson2(logsOnly ? logResult2 : { ...result, logs: logResult2 }));
90989
91504
  } else {
90990
- const verb = result.dryRun ? "Would finalize" : "Finalized";
90991
- if (result.cleaned.length === 0) {
90992
- const ageMsg = force ? "" : ` older than ${minutes}m`;
90993
- context4.output.write(`No stale runs${ageMsg}.`);
90994
- } else {
90995
- const ageMsg = force ? " (all non-terminal)" : ` (>${minutes}m)`;
90996
- context4.output.write(`${verb} ${result.cleaned.length} stale run(s)${ageMsg}:
91505
+ if (result !== undefined) {
91506
+ const verb = dryRun ? "Would finalize" : "Finalized";
91507
+ if (result.cleaned.length === 0) {
91508
+ const ageMsg = force ? "" : ` older than ${minutes}m`;
91509
+ context4.output.write(`No stale runs${ageMsg}.`);
91510
+ } else {
91511
+ const ageMsg = force ? " (all non-terminal)" : ` (>${minutes}m)`;
91512
+ context4.output.write(`${verb} ${result.cleaned.length} stale run(s)${ageMsg}:
90997
91513
  ` + result.cleaned.map((r2) => ` ${r2.runId} (started ${r2.startedAt})`).join(`
90998
91514
  `));
91515
+ }
91516
+ }
91517
+ const logVerb = dryRun ? "Would reclaim" : "Reclaimed";
91518
+ if (logResult2.reclaimed.length === 0) {
91519
+ context4.output.write(`No retained run logs older than ${retentionDays}d.`);
91520
+ } else {
91521
+ context4.output.write(`${logVerb} ${logResult2.reclaimed.length} retained run log(s) (>${retentionDays}d):
91522
+ ` + logResult2.reclaimed.map((l) => ` ${l.runId} (mtime ${l.mtime})`).join(`
91523
+ `));
91524
+ }
91525
+ for (const failure of logResult2.failures) {
91526
+ context4.output.error(`Failed to remove run log ${failure.path}: ${failure.error}`);
90999
91527
  }
91000
91528
  }
91001
91529
  });
@@ -91026,7 +91554,7 @@ Monitor with: spur workflow trace ${runId2} --follow`);
91026
91554
  context4.output.write(formatListHuman(result));
91027
91555
  }
91028
91556
  });
91029
- 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) => {
91557
+ 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("--output", "With --follow: stream .spur/run/<RUNID>.log instead of the DB timeline").option("--json", "Output machine-readable JSON where supported").action(async (runId, options) => {
91030
91558
  const svc = makeSvc();
91031
91559
  const last = parseInt(options.last, 10);
91032
91560
  if (Number.isNaN(last) || last < 1) {
@@ -91050,13 +91578,27 @@ Monitor with: spur workflow trace ${runId2} --follow`);
91050
91578
  context4.setExitCode(1);
91051
91579
  return;
91052
91580
  }
91581
+ if (options.output === true && options.follow !== true) {
91582
+ context4.output.error("--output requires --follow");
91583
+ context4.setExitCode(1);
91584
+ return;
91585
+ }
91586
+ if (options.output === true && options.json === true) {
91587
+ context4.output.error("--output is a human streaming mode and cannot be combined with --json");
91588
+ context4.setExitCode(1);
91589
+ return;
91590
+ }
91053
91591
  if (options.status !== undefined && !["done", "failed", "running"].includes(options.status)) {
91054
91592
  context4.output.error("--status must be one of: done, failed, running");
91055
91593
  context4.setExitCode(1);
91056
91594
  return;
91057
91595
  }
91058
91596
  if (options.follow === true && runId !== undefined) {
91059
- await followTrace(svc, runId, pollMs, (line) => context4.output.write(line));
91597
+ if (options.output === true) {
91598
+ await followRunLog(svc, runId, context4.cwd, pollMs, (line) => context4.output.write(line));
91599
+ } else {
91600
+ await followTrace(svc, runId, pollMs, (line) => context4.output.write(line));
91601
+ }
91060
91602
  return;
91061
91603
  }
91062
91604
  const result = runId ? await svc.trace(runId) : await svc.trace({
@@ -91128,7 +91670,7 @@ function formatTraceTimeline(result) {
91128
91670
  ""
91129
91671
  ];
91130
91672
  if (result.outputArtifact !== undefined) {
91131
- lines.push(`Agent output: ${result.outputArtifact} (tail -f for live view)`);
91673
+ lines.push(`Run log: ${result.outputArtifact} (tail -f for live view)`);
91132
91674
  lines.push("");
91133
91675
  }
91134
91676
  for (const event2 of events2) {
@@ -91191,6 +91733,59 @@ async function followTrace(service, runId, pollMs, write, wait = (ms) => sleep3(
91191
91733
  function isTerminalTraceStatus(status) {
91192
91734
  return status !== "running" && status !== "pending";
91193
91735
  }
91736
+ function readRunLogChunk(logPath, offset) {
91737
+ let fd;
91738
+ try {
91739
+ fd = openSync5(logPath, "r");
91740
+ } catch {
91741
+ return { exists: false, lines: [], offset };
91742
+ }
91743
+ try {
91744
+ const size = fstatSync3(fd).size;
91745
+ if (size <= offset)
91746
+ return { exists: true, lines: [], offset };
91747
+ const buffer = Buffer.alloc(size - offset);
91748
+ readSync3(fd, buffer, 0, buffer.length, offset);
91749
+ const text4 = buffer.toString("utf8");
91750
+ const lastNewline = text4.lastIndexOf(`
91751
+ `);
91752
+ const consumed = lastNewline >= 0 ? lastNewline + 1 : 0;
91753
+ const parts = consumed === 0 ? [] : text4.slice(0, consumed).split(`
91754
+ `);
91755
+ const lines = parts.length > 0 && parts.at(-1) === "" ? parts.slice(0, -1) : parts;
91756
+ return { exists: true, lines, offset: offset + consumed };
91757
+ } finally {
91758
+ closeSync5(fd);
91759
+ }
91760
+ }
91761
+ async function followRunLog(service, runId, dir, pollMs, write, wait = (ms) => sleep3(ms)) {
91762
+ const logPath = join27(dir, ".spur", "run", `${runId}.log`);
91763
+ let offset = 0;
91764
+ let everRead = false;
91765
+ while (true) {
91766
+ const chunk = readRunLogChunk(logPath, offset);
91767
+ if (chunk.exists && (chunk.lines.length > 0 || chunk.offset > offset)) {
91768
+ everRead = true;
91769
+ for (const line of chunk.lines)
91770
+ write(line);
91771
+ offset = chunk.offset;
91772
+ }
91773
+ let terminal = false;
91774
+ try {
91775
+ terminal = isTerminalTraceStatus((await service.trace(runId)).run.status);
91776
+ } catch (error51) {
91777
+ if (!String(error51).includes("Run not found"))
91778
+ throw error51;
91779
+ }
91780
+ if (terminal) {
91781
+ if (!everRead) {
91782
+ write(`No run log at ${logPath} \u2014 the run may have been started with --no-log.`);
91783
+ }
91784
+ return;
91785
+ }
91786
+ await wait(pollMs);
91787
+ }
91788
+ }
91194
91789
  function formatActionCost(event2) {
91195
91790
  if (event2.kind !== "action")
91196
91791
  return "";
@@ -91211,7 +91806,7 @@ init_src3();
91211
91806
  init_src();
91212
91807
  init_src2();
91213
91808
  init_dist5();
91214
- import { dirname as dirname16, join as join27, resolve as resolve9 } from "path";
91809
+ import { dirname as dirname16, join as join28, resolve as resolve9 } from "path";
91215
91810
  import { isatty as isatty4 } from "tty";
91216
91811
 
91217
91812
  // ../../node_modules/.bun/@clack+core@1.4.1/node_modules/@clack/core/dist/index.mjs
@@ -92633,7 +93228,7 @@ function createCliContext(options) {
92633
93228
  function noopSetExitCode(_code) {}
92634
93229
  async function createMigratedDbAdapter(cwd = process.cwd(), env = process.env, dbUrl) {
92635
93230
  const config4 = buildConfigFromEnv(env);
92636
- const configuredUrl = env.DATABASE_URL === undefined ? join27(cwd, DEFAULT_DATABASE_URL) : config4.database.url;
93231
+ const configuredUrl = env.DATABASE_URL === undefined ? join28(cwd, DEFAULT_DATABASE_URL) : config4.database.url;
92637
93232
  const url2 = dbUrl ?? configuredUrl;
92638
93233
  if (url2 !== IN_MEMORY_DATABASE_URL) {
92639
93234
  await createNodeFileSystem3().ensureDir(dirname16(url2));