loki-mode 10.9.1 → 10.10.2

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 (110) hide show
  1. package/.github/actions/issue-to-pr/action.yml +2 -2
  2. package/README.md +9 -3
  3. package/SKILL.md +12 -2
  4. package/VERSION +1 -1
  5. package/autonomy/lib/proof-generator.py +1 -0
  6. package/autonomy/loki +39 -9
  7. package/autonomy/receipt_jwt.py +46 -1
  8. package/autonomy/run.sh +1 -1
  9. package/autonomy/telemetry.sh +1 -1
  10. package/autonomy/trigger-server.py +1 -0
  11. package/bin/loki +12 -1
  12. package/bin/loki-postinstall.js +12 -0
  13. package/dashboard/__init__.py +1 -1
  14. package/docs/CLI-REFERENCE.md +139 -0
  15. package/docs/INSTALLATION.md +1 -1
  16. package/docs/LOKI-10-BUILD-PROMPT.md +2 -2
  17. package/docs/PLANS-INDEX.md +2 -3
  18. package/docs/SLO.md +3 -3
  19. package/docs/alternative-installations.md +1 -1
  20. package/docs/bug-fixes/agent-04-memory-fixes.md +1 -1
  21. package/docs/dev/release-checklist.md +1 -1
  22. package/{.github/workflows/loki-issue-to-pr.yml → docs/examples/loki-issue-to-pr.yml.example} +2 -0
  23. package/docs/{ONE-RUN-AUDIT.md → history/one-run-audit.md} +6 -0
  24. package/docs/{BUG-AUDIT-v6.61.0.md → history/v6.61.0-audit.md} +6 -0
  25. package/docs/{V8-AGENT-SDK-PLAN.md → history/v8-agent-sdk-plan.md} +6 -0
  26. package/docs/{CONFIG-FILE-PLAN.md → history/v8-config-plan.md} +6 -0
  27. package/docs/test-scenarios/enterprise-scenarios.md +1 -1
  28. package/docs/v10/BOARD.md +3 -0
  29. package/docs/v10/CLI-MODERN.md +190 -0
  30. package/docs/v10/DEPS.md +148 -169
  31. package/docs/v10/FAILURE-CLASSES.md +23 -2
  32. package/docs/v10/GUARDS.md +18 -0
  33. package/docs/v10/LEGACY-REMOVAL.md +334 -25
  34. package/docs/v10/PROGRESS.md +15 -0
  35. package/loki-ts/dist/loki.js +1574 -1794
  36. package/mcp/__init__.py +1 -1
  37. package/package.json +3 -2
  38. package/packages/control-plane/dist/server.js +733 -397
  39. package/packages/control-plane/src/db/fixture-cleanup.ts +47 -0
  40. package/packages/control-plane/src/server/app.ts +2 -0
  41. package/packages/control-plane/src/server/integrity.ts +48 -9
  42. package/packages/control-plane/src/server/routes/stats.ts +16 -5
  43. package/packages/control-plane/src/server/runs.ts +16 -4
  44. package/packages/control-plane/ui/dist/assets/index-BinL6h3i.css +1 -0
  45. package/packages/control-plane/ui/dist/assets/index-BrA22FUs.js +152 -0
  46. package/packages/control-plane/ui/dist/index.html +2 -2
  47. package/plugins/loki-mode/.claude-plugin/plugin.json +1 -1
  48. package/schemas/status-result.schema.json +13 -22
  49. package/skills/release-cadence.md +5 -5
  50. package/web-app/dist/assets/{AdminPage-DqMnfZrH.js → AdminPage-BvbxMtZO.js} +1 -1
  51. package/web-app/dist/assets/{Avatar-BEof29du.js → Avatar-Dc7Scg02.js} +1 -1
  52. package/web-app/dist/assets/{Badge-WD53OL5m.js → Badge-BmVuOqbM.js} +1 -1
  53. package/web-app/dist/assets/{Button-Rzk5flhU.js → Button-DvzqhxRU.js} +1 -1
  54. package/web-app/dist/assets/{CockpitPage-Bqz3PCMh.js → CockpitPage-D8AZo7Ha.js} +1 -1
  55. package/web-app/dist/assets/{ComparePage-Byo2NzAZ.js → ComparePage-NmqaTXDz.js} +1 -1
  56. package/web-app/dist/assets/{ErrorBoundary-BuE0kH3W.js → ErrorBoundary-PGl8Kzoq.js} +1 -1
  57. package/web-app/dist/assets/{EvidenceReceiptPanel-CKFHOPBx.js → EvidenceReceiptPanel-2NY4n9gv.js} +1 -1
  58. package/web-app/dist/assets/{GitHubIssuesPanel-Df_kTMqI.js → GitHubIssuesPanel-DBO_LHHM.js} +1 -1
  59. package/web-app/dist/assets/{GitHubPRsPanel-CouvcKrZ.js → GitHubPRsPanel-K7m9uJ1N.js} +1 -1
  60. package/web-app/dist/assets/{HomePage-dEpLzj0x.js → HomePage-qNQzAJxM.js} +2 -2
  61. package/web-app/dist/assets/{LoginPage-6DVjUND5.js → LoginPage-esu3OK65.js} +1 -1
  62. package/web-app/dist/assets/{MagicPage-BDQ9MOfY.js → MagicPage-CCdVvXrA.js} +1 -1
  63. package/web-app/dist/assets/{MetricsPage-CXiJ04G_.js → MetricsPage-bEvugU2t.js} +1 -1
  64. package/web-app/dist/assets/{NotFoundPage-BaXhZvyj.js → NotFoundPage-BlEjQ1wX.js} +1 -1
  65. package/web-app/dist/assets/{ProjectPage-BzE4V7Ti.js → ProjectPage-Bnug_Yzv.js} +3 -3
  66. package/web-app/dist/assets/{ProjectsPage-ipf6TLMX.js → ProjectsPage-M8r1vfpn.js} +1 -1
  67. package/web-app/dist/assets/{SettingsPage-BDxkUKN6.js → SettingsPage-NqAJpB48.js} +1 -1
  68. package/web-app/dist/assets/{ShowcasePage-YQXD72SM.js → ShowcasePage-8FggxL1P.js} +1 -1
  69. package/web-app/dist/assets/{SystemSettingsPage-Dou7zHS0.js → SystemSettingsPage-3Sz3kFOO.js} +1 -1
  70. package/web-app/dist/assets/{TeamsPage-CQarfcYr.js → TeamsPage-nV-6vdWM.js} +1 -1
  71. package/web-app/dist/assets/{TemplatesPage-CPi1R8Xj.js → TemplatesPage-Brbc_VR9.js} +1 -1
  72. package/web-app/dist/assets/{TerminalOutput-WiGhWdYD.js → TerminalOutput-D1yta5ac.js} +1 -1
  73. package/web-app/dist/assets/{activity-DOa7ejQA.js → activity-oEeKuqFe.js} +1 -1
  74. package/web-app/dist/assets/{bell-CX-VAlYN.js → bell-3NJtqBRR.js} +1 -1
  75. package/web-app/dist/assets/{bot-EDXaZmHc.js → bot-CuvJZ6x1.js} +1 -1
  76. package/web-app/dist/assets/{check-CnBdKk3L.js → check-n4289IZw.js} +1 -1
  77. package/web-app/dist/assets/{chevron-left-BJ_ydRQW.js → chevron-left-CmQeio7h.js} +1 -1
  78. package/web-app/dist/assets/{circle-alert-tz8ufhc_.js → circle-alert-CmmVW-Pd.js} +1 -1
  79. package/web-app/dist/assets/{clock-DFBZPSsH.js → clock-QFqk7GlA.js} +1 -1
  80. package/web-app/dist/assets/{cloud-uy3p_5Q7.js → cloud-BNkSokmq.js} +1 -1
  81. package/web-app/dist/assets/{code-xml-3CbYD9YL.js → code-xml-nWeketqj.js} +1 -1
  82. package/web-app/dist/assets/{database-YiNBXc9c.js → database-C83MXTmJ.js} +1 -1
  83. package/web-app/dist/assets/{dollar-sign-D0wBa4Cv.js → dollar-sign-Blg6TO4y.js} +1 -1
  84. package/web-app/dist/assets/{file-code-corner-DDdZVZ5v.js → file-code-corner-Tk11oUzm.js} +1 -1
  85. package/web-app/dist/assets/{file-plus-D09LTBqt.js → file-plus-DF_eyA6I.js} +1 -1
  86. package/web-app/dist/assets/{globe-BirTPwff.js → globe-D5hDqqmn.js} +1 -1
  87. package/web-app/dist/assets/{hammer-DaE5qgnd.js → hammer-C2hm9lzl.js} +1 -1
  88. package/web-app/dist/assets/{index-D8t7KnQf.js → index-pMxOIwXq.js} +3 -3
  89. package/web-app/dist/assets/{layers-DVVrckRY.js → layers-XWQKGXg0.js} +1 -1
  90. package/web-app/dist/assets/{loader-circle-2-nMGmcb.js → loader-circle-BRuvorm2.js} +1 -1
  91. package/web-app/dist/assets/{lock-D0Xj43pC.js → lock-BSGvQf5v.js} +1 -1
  92. package/web-app/dist/assets/{package-BB7S3egm.js → package-3FJYUYgJ.js} +1 -1
  93. package/web-app/dist/assets/{plus-BLhoMrn3.js → plus-CeIx6g9P.js} +1 -1
  94. package/web-app/dist/assets/{refresh-cw-DMc2uPLw.js → refresh-cw-ChutW42g.js} +1 -1
  95. package/web-app/dist/assets/{rotate-ccw-DM3IP9x7.js → rotate-ccw-BdSjUAk-.js} +1 -1
  96. package/web-app/dist/assets/{scroll-text-lH2uW7rB.js → scroll-text-BjFqxpw3.js} +1 -1
  97. package/web-app/dist/assets/{server-BkerZcdR.js → server-C7UEKdol.js} +1 -1
  98. package/web-app/dist/assets/{shield-alert-B8ohcUfu.js → shield-alert-DY8fpEs7.js} +1 -1
  99. package/web-app/dist/assets/{thumbs-up-CTIxLzPD.js → thumbs-up-Cz1san1g.js} +1 -1
  100. package/web-app/dist/assets/{trash-2-C3zIpsH5.js → trash-2-D2MvGYeP.js} +1 -1
  101. package/web-app/dist/assets/{trending-down-BTT7wV4Y.js → trending-down-CxO3OYOB.js} +1 -1
  102. package/web-app/dist/assets/{trending-up-BzHdCDvF.js → trending-up-DCHKPJLe.js} +1 -1
  103. package/web-app/dist/assets/{upload-HxR34-A8.js → upload-CdPJpSHC.js} +1 -1
  104. package/web-app/dist/assets/{usePolling-DDj6Fupy.js → usePolling-DAH6K3ST.js} +1 -1
  105. package/web-app/dist/assets/{user-qH5y9yNy.js → user-Byyt01W4.js} +1 -1
  106. package/web-app/dist/index.html +1 -1
  107. package/docs/BRANCH-LIFECYCLE-PLAN.md +0 -354
  108. package/docs/EXECUTION-COCKPIT-PLAN.md +0 -289
  109. package/packages/control-plane/ui/dist/assets/index-BQbFv65M.css +0 -1
  110. package/packages/control-plane/ui/dist/assets/index-Dbhu244h.js +0 -155
@@ -3696,10 +3696,10 @@ var require_resolve_block_map = __commonJS((exports) => {
3696
3696
  let offset = bm.offset;
3697
3697
  let commentEnd = null;
3698
3698
  for (const collItem of bm.items) {
3699
- const { start, key, sep: sep3, value } = collItem;
3699
+ const { start, key, sep: sep4, value } = collItem;
3700
3700
  const keyProps = resolveProps.resolveProps(start, {
3701
3701
  indicator: "explicit-key-ind",
3702
- next: key ?? sep3?.[0],
3702
+ next: key ?? sep4?.[0],
3703
3703
  offset,
3704
3704
  onError,
3705
3705
  parentIndent: bm.indent,
@@ -3713,7 +3713,7 @@ var require_resolve_block_map = __commonJS((exports) => {
3713
3713
  else if ("indent" in key && key.indent !== bm.indent)
3714
3714
  onError(offset, "BAD_INDENT", startColMsg);
3715
3715
  }
3716
- if (!keyProps.anchor && !keyProps.tag && !sep3) {
3716
+ if (!keyProps.anchor && !keyProps.tag && !sep4) {
3717
3717
  commentEnd = keyProps.end;
3718
3718
  if (keyProps.comment) {
3719
3719
  if (map.comment)
@@ -3738,7 +3738,7 @@ var require_resolve_block_map = __commonJS((exports) => {
3738
3738
  ctx.atKey = false;
3739
3739
  if (utilMapIncludes.mapIncludes(ctx, map.items, keyNode))
3740
3740
  onError(keyStart, "DUPLICATE_KEY", "Map keys must be unique");
3741
- const valueProps = resolveProps.resolveProps(sep3 ?? [], {
3741
+ const valueProps = resolveProps.resolveProps(sep4 ?? [], {
3742
3742
  indicator: "map-value-ind",
3743
3743
  next: value,
3744
3744
  offset: keyNode.range[2],
@@ -3754,7 +3754,7 @@ var require_resolve_block_map = __commonJS((exports) => {
3754
3754
  if (ctx.options.strict && keyProps.start < valueProps.found.offset - 1024)
3755
3755
  onError(keyNode.range, "KEY_OVER_1024_CHARS", "The : indicator must be at most 1024 chars after the start of an implicit block mapping key");
3756
3756
  }
3757
- const valueNode = value ? composeNode(ctx, value, valueProps, onError) : composeEmptyNode(ctx, offset, sep3, null, valueProps, onError);
3757
+ const valueNode = value ? composeNode(ctx, value, valueProps, onError) : composeEmptyNode(ctx, offset, sep4, null, valueProps, onError);
3758
3758
  if (ctx.schema.compat)
3759
3759
  utilFlowIndentCheck.flowIndentCheck(bm.indent, value, onError);
3760
3760
  offset = valueNode.range[2];
@@ -3840,7 +3840,7 @@ var require_resolve_end = __commonJS((exports) => {
3840
3840
  let comment = "";
3841
3841
  if (end) {
3842
3842
  let hasSpace = false;
3843
- let sep3 = "";
3843
+ let sep4 = "";
3844
3844
  for (const token of end) {
3845
3845
  const { source, type } = token;
3846
3846
  switch (type) {
@@ -3854,13 +3854,13 @@ var require_resolve_end = __commonJS((exports) => {
3854
3854
  if (!comment)
3855
3855
  comment = cb;
3856
3856
  else
3857
- comment += sep3 + cb;
3858
- sep3 = "";
3857
+ comment += sep4 + cb;
3858
+ sep4 = "";
3859
3859
  break;
3860
3860
  }
3861
3861
  case "newline":
3862
3862
  if (comment)
3863
- sep3 += source;
3863
+ sep4 += source;
3864
3864
  hasSpace = true;
3865
3865
  break;
3866
3866
  default:
@@ -3900,18 +3900,18 @@ var require_resolve_flow_collection = __commonJS((exports) => {
3900
3900
  let offset = fc.offset + fc.start.source.length;
3901
3901
  for (let i = 0;i < fc.items.length; ++i) {
3902
3902
  const collItem = fc.items[i];
3903
- const { start, key, sep: sep3, value } = collItem;
3903
+ const { start, key, sep: sep4, value } = collItem;
3904
3904
  const props = resolveProps.resolveProps(start, {
3905
3905
  flow: fcName,
3906
3906
  indicator: "explicit-key-ind",
3907
- next: key ?? sep3?.[0],
3907
+ next: key ?? sep4?.[0],
3908
3908
  offset,
3909
3909
  onError,
3910
3910
  parentIndent: fc.indent,
3911
3911
  startOnNewline: false
3912
3912
  });
3913
3913
  if (!props.found) {
3914
- if (!props.anchor && !props.tag && !sep3 && !value) {
3914
+ if (!props.anchor && !props.tag && !sep4 && !value) {
3915
3915
  if (i === 0 && props.comma)
3916
3916
  onError(props.comma, "UNEXPECTED_TOKEN", `Unexpected , in ${fcName}`);
3917
3917
  else if (i < fc.items.length - 1)
@@ -3963,8 +3963,8 @@ var require_resolve_flow_collection = __commonJS((exports) => {
3963
3963
  }
3964
3964
  }
3965
3965
  }
3966
- if (!isMap && !sep3 && !props.found) {
3967
- const valueNode = value ? composeNode(ctx, value, props, onError) : composeEmptyNode(ctx, props.end, sep3, null, props, onError);
3966
+ if (!isMap && !sep4 && !props.found) {
3967
+ const valueNode = value ? composeNode(ctx, value, props, onError) : composeEmptyNode(ctx, props.end, sep4, null, props, onError);
3968
3968
  coll.items.push(valueNode);
3969
3969
  offset = valueNode.range[2];
3970
3970
  if (isBlock(value))
@@ -3976,7 +3976,7 @@ var require_resolve_flow_collection = __commonJS((exports) => {
3976
3976
  if (isBlock(key))
3977
3977
  onError(keyNode.range, "BLOCK_IN_FLOW", blockMsg);
3978
3978
  ctx.atKey = false;
3979
- const valueProps = resolveProps.resolveProps(sep3 ?? [], {
3979
+ const valueProps = resolveProps.resolveProps(sep4 ?? [], {
3980
3980
  flow: fcName,
3981
3981
  indicator: "map-value-ind",
3982
3982
  next: value,
@@ -3987,8 +3987,8 @@ var require_resolve_flow_collection = __commonJS((exports) => {
3987
3987
  });
3988
3988
  if (valueProps.found) {
3989
3989
  if (!isMap && !props.found && ctx.options.strict) {
3990
- if (sep3)
3991
- for (const st of sep3) {
3990
+ if (sep4)
3991
+ for (const st of sep4) {
3992
3992
  if (st === valueProps.found)
3993
3993
  break;
3994
3994
  if (st.type === "newline") {
@@ -4005,7 +4005,7 @@ var require_resolve_flow_collection = __commonJS((exports) => {
4005
4005
  else
4006
4006
  onError(valueProps.start, "MISSING_CHAR", `Missing , or : between ${fcName} items`);
4007
4007
  }
4008
- const valueNode = value ? composeNode(ctx, value, valueProps, onError) : valueProps.found ? composeEmptyNode(ctx, valueProps.end, sep3, null, valueProps, onError) : null;
4008
+ const valueNode = value ? composeNode(ctx, value, valueProps, onError) : valueProps.found ? composeEmptyNode(ctx, valueProps.end, sep4, null, valueProps, onError) : null;
4009
4009
  if (valueNode) {
4010
4010
  if (isBlock(value))
4011
4011
  onError(valueNode.range, "BLOCK_IN_FLOW", blockMsg);
@@ -4182,7 +4182,7 @@ var require_resolve_block_scalar = __commonJS((exports) => {
4182
4182
  chompStart = i + 1;
4183
4183
  }
4184
4184
  let value = "";
4185
- let sep3 = "";
4185
+ let sep4 = "";
4186
4186
  let prevMoreIndented = false;
4187
4187
  for (let i = 0;i < contentStart; ++i)
4188
4188
  value += lines[i][0].slice(trimIndent) + `
@@ -4200,33 +4200,33 @@ var require_resolve_block_scalar = __commonJS((exports) => {
4200
4200
  indent = "";
4201
4201
  }
4202
4202
  if (type === Scalar.Scalar.BLOCK_LITERAL) {
4203
- value += sep3 + indent.slice(trimIndent) + content;
4204
- sep3 = `
4203
+ value += sep4 + indent.slice(trimIndent) + content;
4204
+ sep4 = `
4205
4205
  `;
4206
4206
  } else if (indent.length > trimIndent || content[0] === "\t") {
4207
- if (sep3 === " ")
4208
- sep3 = `
4207
+ if (sep4 === " ")
4208
+ sep4 = `
4209
4209
  `;
4210
- else if (!prevMoreIndented && sep3 === `
4210
+ else if (!prevMoreIndented && sep4 === `
4211
4211
  `)
4212
- sep3 = `
4212
+ sep4 = `
4213
4213
 
4214
4214
  `;
4215
- value += sep3 + indent.slice(trimIndent) + content;
4216
- sep3 = `
4215
+ value += sep4 + indent.slice(trimIndent) + content;
4216
+ sep4 = `
4217
4217
  `;
4218
4218
  prevMoreIndented = true;
4219
4219
  } else if (content === "") {
4220
- if (sep3 === `
4220
+ if (sep4 === `
4221
4221
  `)
4222
4222
  value += `
4223
4223
  `;
4224
4224
  else
4225
- sep3 = `
4225
+ sep4 = `
4226
4226
  `;
4227
4227
  } else {
4228
- value += sep3 + content;
4229
- sep3 = " ";
4228
+ value += sep4 + content;
4229
+ sep4 = " ";
4230
4230
  prevMoreIndented = false;
4231
4231
  }
4232
4232
  }
@@ -4406,27 +4406,27 @@ var require_resolve_flow_scalar = __commonJS((exports) => {
4406
4406
  trimBoth = /^[ \t]+|[ \t]+$/g;
4407
4407
  }
4408
4408
  let res = match2[1].replace(trimEnd, "");
4409
- let sep3 = " ";
4409
+ let sep4 = " ";
4410
4410
  let pos = line2.lastIndex;
4411
4411
  while (match2 = line2.exec(source)) {
4412
4412
  const lm = match2[1].replace(trimBoth, "");
4413
4413
  if (lm === "") {
4414
- if (sep3 === `
4414
+ if (sep4 === `
4415
4415
  `)
4416
- res += sep3;
4416
+ res += sep4;
4417
4417
  else
4418
- sep3 = `
4418
+ sep4 = `
4419
4419
  `;
4420
4420
  } else {
4421
- res += sep3 + lm;
4422
- sep3 = " ";
4421
+ res += sep4 + lm;
4422
+ sep4 = " ";
4423
4423
  }
4424
4424
  pos = line2.lastIndex;
4425
4425
  }
4426
4426
  const last = /[ \t]*(.*)/sy;
4427
4427
  last.lastIndex = pos;
4428
4428
  match2 = last.exec(source);
4429
- return res + sep3 + (match2?.[1] ?? "");
4429
+ return res + sep4 + (match2?.[1] ?? "");
4430
4430
  }
4431
4431
  function doubleQuotedValue(source, onError) {
4432
4432
  let res = "";
@@ -5207,14 +5207,14 @@ var require_cst_stringify = __commonJS((exports) => {
5207
5207
  }
5208
5208
  }
5209
5209
  }
5210
- function stringifyItem({ start, key, sep: sep3, value }) {
5210
+ function stringifyItem({ start, key, sep: sep4, value }) {
5211
5211
  let res = "";
5212
5212
  for (const st of start)
5213
5213
  res += st.source;
5214
5214
  if (key)
5215
5215
  res += stringifyToken(key);
5216
- if (sep3)
5217
- for (const st of sep3)
5216
+ if (sep4)
5217
+ for (const st of sep4)
5218
5218
  res += st.source;
5219
5219
  if (value)
5220
5220
  res += stringifyToken(value);
@@ -6362,18 +6362,18 @@ var require_parser = __commonJS((exports) => {
6362
6362
  if (this.type === "map-value-ind") {
6363
6363
  const prev = getPrevProps(this.peek(2));
6364
6364
  const start = getFirstKeyStartProps(prev);
6365
- let sep3;
6365
+ let sep4;
6366
6366
  if (scalar.end) {
6367
- sep3 = scalar.end;
6368
- sep3.push(this.sourceToken);
6367
+ sep4 = scalar.end;
6368
+ sep4.push(this.sourceToken);
6369
6369
  delete scalar.end;
6370
6370
  } else
6371
- sep3 = [this.sourceToken];
6371
+ sep4 = [this.sourceToken];
6372
6372
  const map = {
6373
6373
  type: "block-map",
6374
6374
  offset: scalar.offset,
6375
6375
  indent: scalar.indent,
6376
- items: [{ start, key: scalar, sep: sep3 }]
6376
+ items: [{ start, key: scalar, sep: sep4 }]
6377
6377
  };
6378
6378
  this.onKeyLine = true;
6379
6379
  this.stack[this.stack.length - 1] = map;
@@ -6527,15 +6527,15 @@ var require_parser = __commonJS((exports) => {
6527
6527
  } else if (isFlowToken(it.key) && !includesToken(it.sep, "newline")) {
6528
6528
  const start2 = getFirstKeyStartProps(it.start);
6529
6529
  const key = it.key;
6530
- const sep3 = it.sep;
6531
- sep3.push(this.sourceToken);
6530
+ const sep4 = it.sep;
6531
+ sep4.push(this.sourceToken);
6532
6532
  delete it.key;
6533
6533
  delete it.sep;
6534
6534
  this.stack.push({
6535
6535
  type: "block-map",
6536
6536
  offset: this.offset,
6537
6537
  indent: this.indent,
6538
- items: [{ start: start2, key, sep: sep3 }]
6538
+ items: [{ start: start2, key, sep: sep4 }]
6539
6539
  });
6540
6540
  } else if (start.length > 0) {
6541
6541
  it.sep = it.sep.concat(start, this.sourceToken);
@@ -6729,13 +6729,13 @@ var require_parser = __commonJS((exports) => {
6729
6729
  const prev = getPrevProps(parent);
6730
6730
  const start = getFirstKeyStartProps(prev);
6731
6731
  fixFlowSeqItems(fc);
6732
- const sep3 = fc.end.splice(1, fc.end.length);
6733
- sep3.push(this.sourceToken);
6732
+ const sep4 = fc.end.splice(1, fc.end.length);
6733
+ sep4.push(this.sourceToken);
6734
6734
  const map = {
6735
6735
  type: "block-map",
6736
6736
  offset: fc.offset,
6737
6737
  indent: fc.indent,
6738
- items: [{ start, key: fc, sep: sep3 }]
6738
+ items: [{ start, key: fc, sep: sep4 }]
6739
6739
  };
6740
6740
  this.onKeyLine = true;
6741
6741
  this.stack[this.stack.length - 1] = map;
@@ -6959,8 +6959,8 @@ var require_public_api = __commonJS((exports) => {
6959
6959
  });
6960
6960
 
6961
6961
  // src/server/app.ts
6962
- import { existsSync as existsSync19, statSync as statSync13 } from "fs";
6963
- import { join as join30, resolve as resolve6, sep as sep10 } from "path";
6962
+ import { existsSync as existsSync21, statSync as statSync14 } from "fs";
6963
+ import { join as join33, resolve as resolve6, sep as sep12 } from "path";
6964
6964
 
6965
6965
  // node_modules/hono/dist/request/constants.js
6966
6966
  var GET_MATCH_RESULT = Symbol();
@@ -8394,6 +8394,90 @@ var Hono3 = class extends Hono {
8394
8394
  }
8395
8395
  };
8396
8396
 
8397
+ // src/db/fixture-cleanup.ts
8398
+ import { realpathSync } from "fs";
8399
+ import { tmpdir } from "os";
8400
+ import { sep } from "path";
8401
+
8402
+ // src/db/prune.ts
8403
+ var n = (sqlite, sql, ...args) => sqlite.query(sql).get(...args).n;
8404
+ function countFor(sqlite, keys) {
8405
+ const bySource = new Map;
8406
+ for (const k of keys)
8407
+ bySource.set(k.source_id, [...bySource.get(k.source_id) ?? [], k.run_id]);
8408
+ let events = 0, sources = 0;
8409
+ for (const [src, runIds] of bySource) {
8410
+ let ev = 0;
8411
+ for (const r of runIds)
8412
+ ev += n(sqlite, "select count(*) n from events where source_id = ? and run_id = ?", src, r);
8413
+ events += ev;
8414
+ const runsLeft = n(sqlite, "select count(*) n from runs where source_id = ?", src) - runIds.length;
8415
+ const eventsLeft = n(sqlite, "select count(*) n from events where source_id = ?", src) - ev;
8416
+ if (runsLeft <= 0 && eventsLeft <= 0 && sqlite.query("select 1 x from sources where id = ?").get(src))
8417
+ sources++;
8418
+ }
8419
+ return { runs: keys.length, events, sources };
8420
+ }
8421
+ function audit(sqlite, action, actor, detail) {
8422
+ sqlite.query("insert into audit (ts, action, actor, detail) values (?, ?, ?, ?)").run(new Date().toISOString(), action, actor, JSON.stringify(detail));
8423
+ }
8424
+ var dropOrphanSources = (sqlite, ids) => {
8425
+ for (const id of ids)
8426
+ sqlite.query("delete from sources where id = ? and not exists (select 1 from runs where source_id = ?) and not exists (select 1 from events where source_id = ?)").run(id, id, id);
8427
+ };
8428
+ function removeRun(sqlite, source, run, actor = "http") {
8429
+ let out = null;
8430
+ sqlite.transaction(() => {
8431
+ if (!sqlite.query("select 1 x from runs where source_id = ? and run_id = ?").get(source, run))
8432
+ return;
8433
+ out = countFor(sqlite, [{ source_id: source, run_id: run }]);
8434
+ audit(sqlite, "run.remove", actor, { source_id: source, run_id: run, counts: out });
8435
+ sqlite.query("delete from events where source_id = ? and run_id = ?").run(source, run);
8436
+ sqlite.query("delete from runs where source_id = ? and run_id = ?").run(source, run);
8437
+ dropOrphanSources(sqlite, [source]);
8438
+ }).immediate();
8439
+ return out;
8440
+ }
8441
+
8442
+ // src/db/fixture-cleanup.ts
8443
+ var FIXTURE_REPO = "acme/widget";
8444
+ var DONE = "fixture.cleanup.done";
8445
+ var tempRoots = () => {
8446
+ const roots = new Set(["/tmp", "/private/tmp", "/var/tmp", tmpdir()]);
8447
+ for (const r of [...roots]) {
8448
+ try {
8449
+ roots.add(realpathSync(r));
8450
+ } catch {}
8451
+ }
8452
+ return [...roots].map((r) => r.replace(/[\\/]+$/, "")).filter((r) => r.length > 1);
8453
+ };
8454
+ var underTemp = (p, roots) => roots.some((r) => p === r || p.startsWith(r + sep) || p.startsWith(r + "/"));
8455
+ function cleanupLeakedFixtures(sqlite) {
8456
+ let removed = 0;
8457
+ sqlite.transaction(() => {
8458
+ if (sqlite.query("select 1 x from audit where action = ?").get(DONE))
8459
+ return;
8460
+ const roots = tempRoots();
8461
+ const tempSources = new Set(sqlite.query("select source_id, realpath from local_repos").all().filter((r) => underTemp(r.realpath, roots)).map((r) => r.source_id));
8462
+ const all = sqlite.query("select source_id, run_id, origin_repo from runs").all();
8463
+ const doomed = all.filter((r) => r.origin_repo === FIXTURE_REPO || tempSources.has(r.source_id));
8464
+ const touched = new Set;
8465
+ for (const k of doomed) {
8466
+ const ev = sqlite.query("select count(*) n from events where source_id = ? and run_id = ?").get(k.source_id, k.run_id).n;
8467
+ audit(sqlite, "fixture.cleanup", "startup", { source_id: k.source_id, run_id: k.run_id, origin_repo: k.origin_repo, events: ev });
8468
+ sqlite.query("delete from events where source_id = ? and run_id = ?").run(k.source_id, k.run_id);
8469
+ sqlite.query("delete from runs where source_id = ? and run_id = ?").run(k.source_id, k.run_id);
8470
+ touched.add(k.source_id);
8471
+ removed++;
8472
+ }
8473
+ for (const id of touched) {
8474
+ sqlite.query("delete from sources where id = ? and not exists (select 1 from runs where source_id = ?) and not exists (select 1 from events where source_id = ?)").run(id, id, id);
8475
+ }
8476
+ audit(sqlite, DONE, "startup", { removed });
8477
+ }).immediate();
8478
+ return removed;
8479
+ }
8480
+
8397
8481
  // src/db/migrate.ts
8398
8482
  import { Database as Database2 } from "bun:sqlite";
8399
8483
 
@@ -12436,7 +12520,7 @@ __export(exports_schema, {
12436
12520
  runs: () => runs,
12437
12521
  localRepos: () => localRepos,
12438
12522
  events: () => events,
12439
- audit: () => audit,
12523
+ audit: () => audit2,
12440
12524
  actions: () => actions
12441
12525
  });
12442
12526
  var sources = sqliteTable("sources", {
@@ -12495,7 +12579,7 @@ var runs = sqliteTable("runs", {
12495
12579
  index("runs_verdict").on(t.verdict),
12496
12580
  index("runs_group").on(t.groupId)
12497
12581
  ]);
12498
- var audit = sqliteTable("audit", {
12582
+ var audit2 = sqliteTable("audit", {
12499
12583
  id: integer("id").primaryKey({ autoIncrement: true }),
12500
12584
  ts: text("ts").notNull(),
12501
12585
  action: text("action").notNull(),
@@ -12698,6 +12782,8 @@ function writeAnswer(dir, source, run, answer) {
12698
12782
  // src/server/integrity.ts
12699
12783
  import { createHash, createPublicKey, verify } from "crypto";
12700
12784
  import { readFileSync as readFileSync2 } from "fs";
12785
+ import { homedir as homedir2 } from "os";
12786
+ import { join as join3 } from "path";
12701
12787
  var SHA = /^[0-9a-f]{64}$/;
12702
12788
  var SEAL_LINE = "log.sealed";
12703
12789
  var REDACTED = /\[REDACTED(?::[A-Z_]+)?\]/;
@@ -12706,17 +12792,48 @@ var UNATTESTED_SUFFIX = " (unattested)";
12706
12792
  var SUCCESS_VERDICTS = new Set(["VERIFIED", "ALREADY_SATISFIED"]);
12707
12793
  var normVerdict = (v) => typeof v === "string" ? v.trim().toUpperCase() : null;
12708
12794
  var kidOf = (pub) => createHash("sha256").update(`{"crv":"Ed25519","kty":"OKP","x":"${pub.export({ format: "jwk" }).x}"}`).digest("base64url");
12709
- function pubkeysFromEnv(env = process.env) {
12710
- const keys = (env["LOKI_CP_RECEIPT_PUBKEYS"] ?? "").split(":").map((f) => f.trim()).filter(Boolean).flatMap((f) => {
12711
- try {
12712
- return [createPublicKey(readFileSync2(f))];
12713
- } catch {
12714
- return [];
12795
+ var isEd25519 = (k) => k.asymmetricKeyType === "ed25519";
12796
+ function localKeyInfo(env = process.env) {
12797
+ const inline = env["LOKI_RECEIPT_SIGNING_KEY"]?.trim();
12798
+ const given = env["LOKI_RECEIPT_SIGNING_KEY_FILE"]?.trim();
12799
+ try {
12800
+ let pub;
12801
+ if (inline)
12802
+ pub = createPublicKey(inline);
12803
+ else {
12804
+ const file = given || join3(env["HOME"] || homedir2(), ".loki", "keys", "receipt-ed25519.pem");
12805
+ try {
12806
+ pub = createPublicKey(readFileSync2(file));
12807
+ } catch (e) {
12808
+ if (!given && e.code === "ENOENT")
12809
+ return {};
12810
+ throw e;
12811
+ }
12715
12812
  }
12716
- });
12813
+ return isEd25519(pub) ? { key: pub } : { reason: "local signing key is not an Ed25519 key" };
12814
+ } catch (e) {
12815
+ const code = e.code;
12816
+ return { reason: `local signing key ${inline ? "(LOKI_RECEIPT_SIGNING_KEY)" : given ? "file" : "default file"} could not be used: ${code ?? "malformed PEM"}` };
12817
+ }
12818
+ }
12819
+ var pemList = (raw2) => (raw2 ?? "").split(":").map((f) => f.trim()).filter(Boolean).flatMap((f) => {
12820
+ try {
12821
+ const k = createPublicKey(readFileSync2(f));
12822
+ return isEd25519(k) ? [k] : [];
12823
+ } catch {
12824
+ return [];
12825
+ }
12826
+ });
12827
+ function pubkeysFromEnv(env = process.env) {
12828
+ const info = localKeyInfo(env);
12829
+ const explicit = pemList(env["LOKI_CP_RECEIPT_PUBKEYS"]);
12830
+ const keys = [...info.key ? [info.key] : [], ...pemList(env["LOKI_RECEIPT_RETIRED_PUBKEYS"]), ...explicit];
12717
12831
  const fn = (kid) => keys.find((k) => kidOf(k) === kid);
12718
12832
  fn.configured = keys.length > 0;
12719
- fn.fingerprint = createHash("sha256").update(keys.map(kidOf).sort().join(",")).digest("hex").slice(0, 16);
12833
+ fn.strict = explicit.length > 0;
12834
+ if (info.reason)
12835
+ fn.reason = info.reason;
12836
+ fn.fingerprint = createHash("sha256").update(keys.map(kidOf).sort().join(",") + (fn.strict ? "|strict" : "")).digest("hex").slice(0, 16);
12720
12837
  return fn;
12721
12838
  }
12722
12839
  var line = (e) => JSON.stringify({ v: e.v, seq: e.seq, ts: e.ts, run: e.run, type: e.type, stage: e.stage, data: e.data });
@@ -12731,7 +12848,7 @@ function verifyRunIntegrity(evs, opts = {}) {
12731
12848
  const sealedAt = evs.map((e, i) => e.type === "receipt.sealed" ? i : -1).filter((i) => i >= 0);
12732
12849
  const finalised = doneAt >= 0 || sealedAt.length > 0;
12733
12850
  const lines = evs.map(line);
12734
- const keysConfigured = opts.keysConfigured ?? opts.pubkeyFor?.configured ?? false;
12851
+ const keysConfigured = opts.keysConfigured ?? opts.pubkeyFor?.strict ?? opts.pubkeyFor?.configured ?? false;
12735
12852
  let sigChecked = false;
12736
12853
  if (finalised) {
12737
12854
  const gap = evs.findIndex((e, i) => e.seq !== i);
@@ -12928,8 +13045,15 @@ var parseRun = (r) => ({
12928
13045
  status: r.endedAt ? "completed" : "running",
12929
13046
  elapsed_s: elapsedS(r)
12930
13047
  });
13048
+ function taskTitle(db, sourceId, runId) {
13049
+ const d = db.select({ data: events.data }).from(events).where(and(eq(events.sourceId, sourceId), eq(events.runId, runId), eq(events.type, "stage.completed"), eq(events.stage, "intake"))).orderBy(asc(events.seq)).limit(1).get()?.data;
13050
+ const raw2 = str(d?.["title"]) ?? str(d?.["task"]);
13051
+ const t = raw2?.split(`
13052
+ `)[0]?.trim();
13053
+ return t ? t.length > 140 ? `${t.slice(0, 139)}...` : t : null;
13054
+ }
12931
13055
  var withLive = (db, r) => {
12932
- const p = parseRun(r);
13056
+ const p = { ...parseRun(r), title: taskTitle(db, r.sourceId, r.runId) };
12933
13057
  return r.endedAt ? { ...p, current_stage: null, files_touched: [] } : { ...p, ...liveInfo(loadEvents(db, r.sourceId, r.runId)) };
12934
13058
  };
12935
13059
  function liveInfo(evs) {
@@ -12980,11 +13104,11 @@ var verdictFilter = (v) => {
12980
13104
  var RECOMPUTE_CHUNK = 200;
12981
13105
  function recomputeLegacy(db) {
12982
13106
  const fp = pubkeysFromEnv().fingerprint ?? "";
12983
- let n = 0;
13107
+ let n2 = 0;
12984
13108
  for (;; ) {
12985
13109
  const rows = db.select({ s: runs.sourceId, r: runs.runId }).from(runs).where(sql`${runs.attested} is null or ${runs.integrityKeyFp} is not ${fp}`).limit(RECOMPUTE_CHUNK).all();
12986
13110
  if (rows.length === 0)
12987
- return n;
13111
+ return n2;
12988
13112
  db.transaction((tx) => {
12989
13113
  const t = tx;
12990
13114
  for (const x of rows) {
@@ -12994,7 +13118,7 @@ function recomputeLegacy(db) {
12994
13118
  rebuildRun(t, x.s, x.r);
12995
13119
  }
12996
13120
  });
12997
- n += rows.length;
13121
+ n2 += rows.length;
12998
13122
  }
12999
13123
  }
13000
13124
  function listRuns(db, q) {
@@ -13016,12 +13140,14 @@ function runDetail(db, sourceId, runId) {
13016
13140
  continue;
13017
13141
  let s = stages.find((x) => x.stage === e.stage);
13018
13142
  if (!s)
13019
- stages.push(s = { stage: e.stage, started_at: null, ended_at: null, status: "started" });
13143
+ stages.push(s = { stage: e.stage, started_at: null, ended_at: null, status: "started", reason: null });
13020
13144
  if (e.type === "stage.started")
13021
13145
  s.started_at = e.ts;
13022
13146
  else {
13023
13147
  s.ended_at = e.ts;
13024
13148
  s.status = e.type.slice("stage.".length);
13149
+ const why = e.data?.reason;
13150
+ s.reason = typeof why === "string" && why ? why : null;
13025
13151
  }
13026
13152
  }
13027
13153
  const f = fold(evs);
@@ -13135,19 +13261,19 @@ function tokenGuard(token) {
13135
13261
 
13136
13262
  // src/shipper/backfill.ts
13137
13263
  import { readdirSync } from "fs";
13138
- import { join as join4 } from "path";
13264
+ import { join as join5 } from "path";
13139
13265
 
13140
13266
  // src/shipper/ship.ts
13141
13267
  import { createHash as createHash4 } from "crypto";
13142
- import { existsSync as existsSync3, readFileSync as readFileSync3, realpathSync, renameSync, writeFileSync as writeFileSync2 } from "fs";
13268
+ import { existsSync as existsSync3, readFileSync as readFileSync3, realpathSync as realpathSync2, renameSync, writeFileSync as writeFileSync2 } from "fs";
13143
13269
  import { hostname } from "os";
13144
- import { join as join3 } from "path";
13270
+ import { join as join4 } from "path";
13145
13271
  var BATCH = 200;
13146
13272
  var MAX_BYTES = 800000;
13147
13273
  function sourceId(repoDir) {
13148
13274
  let real2 = repoDir;
13149
13275
  try {
13150
- real2 = realpathSync(repoDir);
13276
+ real2 = realpathSync2(repoDir);
13151
13277
  } catch {}
13152
13278
  return createHash4("sha256").update(`${hostname()}\x00${real2}`).digest("hex").slice(0, 16);
13153
13279
  }
@@ -13156,24 +13282,24 @@ var redactEvent = (e) => ({ ...e, data: redactDeep(e.data) });
13156
13282
  var backoffMs = (failures, rnd = Math.random) => Math.min(60000, 1000 * 2 ** Math.max(0, failures - 1)) * (1 + 0.25 * rnd());
13157
13283
  function readShip(runDir, url) {
13158
13284
  try {
13159
- const s = JSON.parse(readFileSync3(join3(runDir, "ship.json"), "utf8"));
13285
+ const s = JSON.parse(readFileSync3(join4(runDir, "ship.json"), "utf8"));
13160
13286
  if (s.url === url && Number.isInteger(s.acked_seq))
13161
13287
  return { acked_seq: s.acked_seq, url };
13162
13288
  } catch {}
13163
13289
  return { acked_seq: -1, url };
13164
13290
  }
13165
13291
  function writeShip(runDir, s) {
13166
- const tmp = join3(runDir, `ship.json.${process.pid}.tmp`);
13292
+ const tmp = join4(runDir, `ship.json.${process.pid}.tmp`);
13167
13293
  writeFileSync2(tmp, JSON.stringify(s) + `
13168
13294
  `);
13169
- renameSync(tmp, join3(runDir, "ship.json"));
13295
+ renameSync(tmp, join4(runDir, "ship.json"));
13170
13296
  }
13171
13297
  async function shipRun(o) {
13172
13298
  const st = readShip(o.runDir, o.url);
13173
- const all = readEvents(join3(o.runDir, "events.jsonl"));
13299
+ const all = readEvents(join4(o.runDir, "events.jsonl"));
13174
13300
  let lines = 0;
13175
13301
  try {
13176
- lines = readFileSync3(join3(o.runDir, "events.jsonl"), "utf8").split(`
13302
+ lines = readFileSync3(join4(o.runDir, "events.jsonl"), "utf8").split(`
13177
13303
  `).filter((l) => l.trim() !== "").length;
13178
13304
  } catch {}
13179
13305
  const skipped = lines - all.length;
@@ -13216,7 +13342,7 @@ async function shipRun(o) {
13216
13342
  return { ok: true, sent, acked_seq: st.acked_seq, pending: 0 };
13217
13343
  }
13218
13344
  var shipEnabled = (env) => env.LOKI_CONTROL_URL || null;
13219
- var hasSpool = (runDir) => existsSync3(join3(runDir, "events.jsonl"));
13345
+ var hasSpool = (runDir) => existsSync3(join4(runDir, "events.jsonl"));
13220
13346
 
13221
13347
  // src/shipper/backfill.ts
13222
13348
  async function backfill(o) {
@@ -13226,7 +13352,7 @@ async function backfill(o) {
13226
13352
  return { runs: 0, sent: 0, failed: [] };
13227
13353
  const sleep = o.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
13228
13354
  const attempts = o.attempts ?? 3;
13229
- const root = join4(o.repoDir, ".loki", "runs");
13355
+ const root = join5(o.repoDir, ".loki", "runs");
13230
13356
  let ids = [];
13231
13357
  try {
13232
13358
  ids = readdirSync(root).sort();
@@ -13237,7 +13363,7 @@ async function backfill(o) {
13237
13363
  let runs2 = 0, sent = 0;
13238
13364
  const failed = [];
13239
13365
  for (const id of ids) {
13240
- const runDir = join4(root, id);
13366
+ const runDir = join5(root, id);
13241
13367
  if (!hasSpool(runDir))
13242
13368
  continue;
13243
13369
  runs2++;
@@ -13256,57 +13382,17 @@ async function backfill(o) {
13256
13382
  return { runs: runs2, sent, failed };
13257
13383
  }
13258
13384
 
13259
- // src/db/prune.ts
13260
- var n = (sqlite, sql2, ...args) => sqlite.query(sql2).get(...args).n;
13261
- function countFor(sqlite, keys) {
13262
- const bySource = new Map;
13263
- for (const k of keys)
13264
- bySource.set(k.source_id, [...bySource.get(k.source_id) ?? [], k.run_id]);
13265
- let events2 = 0, sources2 = 0;
13266
- for (const [src, runIds] of bySource) {
13267
- let ev = 0;
13268
- for (const r of runIds)
13269
- ev += n(sqlite, "select count(*) n from events where source_id = ? and run_id = ?", src, r);
13270
- events2 += ev;
13271
- const runsLeft = n(sqlite, "select count(*) n from runs where source_id = ?", src) - runIds.length;
13272
- const eventsLeft = n(sqlite, "select count(*) n from events where source_id = ?", src) - ev;
13273
- if (runsLeft <= 0 && eventsLeft <= 0 && sqlite.query("select 1 x from sources where id = ?").get(src))
13274
- sources2++;
13275
- }
13276
- return { runs: keys.length, events: events2, sources: sources2 };
13277
- }
13278
- function audit2(sqlite, action, actor, detail) {
13279
- sqlite.query("insert into audit (ts, action, actor, detail) values (?, ?, ?, ?)").run(new Date().toISOString(), action, actor, JSON.stringify(detail));
13280
- }
13281
- var dropOrphanSources = (sqlite, ids) => {
13282
- for (const id of ids)
13283
- sqlite.query("delete from sources where id = ? and not exists (select 1 from runs where source_id = ?) and not exists (select 1 from events where source_id = ?)").run(id, id, id);
13284
- };
13285
- function removeRun(sqlite, source, run, actor = "http") {
13286
- let out = null;
13287
- sqlite.transaction(() => {
13288
- if (!sqlite.query("select 1 x from runs where source_id = ? and run_id = ?").get(source, run))
13289
- return;
13290
- out = countFor(sqlite, [{ source_id: source, run_id: run }]);
13291
- audit2(sqlite, "run.remove", actor, { source_id: source, run_id: run, counts: out });
13292
- sqlite.query("delete from events where source_id = ? and run_id = ?").run(source, run);
13293
- sqlite.query("delete from runs where source_id = ? and run_id = ?").run(source, run);
13294
- dropOrphanSources(sqlite, [source]);
13295
- }).immediate();
13296
- return out;
13297
- }
13298
-
13299
13385
  // src/shipper/discover.ts
13300
- import { readFileSync as readFileSync5, realpathSync as realpathSync2, statSync as statSync2 } from "fs";
13301
- import { basename, dirname, join as join6, resolve } from "path";
13386
+ import { readFileSync as readFileSync5, realpathSync as realpathSync3, statSync as statSync2 } from "fs";
13387
+ import { basename, dirname, join as join7, resolve } from "path";
13302
13388
 
13303
13389
  // src/shipper/watch.ts
13304
13390
  import { readFileSync as readFileSync4, readdirSync as readdirSync2, statSync } from "fs";
13305
- import { homedir as homedir2 } from "os";
13306
- import { join as join5 } from "path";
13391
+ import { homedir as homedir3 } from "os";
13392
+ import { join as join6 } from "path";
13307
13393
  function registryRepos(env) {
13308
13394
  try {
13309
- const reg = JSON.parse(readFileSync4(join5(env.HOME || homedir2(), ".loki", "dashboard", "projects.json"), "utf8"));
13395
+ const reg = JSON.parse(readFileSync4(join6(env.HOME || homedir3(), ".loki", "dashboard", "projects.json"), "utf8"));
13310
13396
  return Object.values(reg.projects ?? {}).map((p) => p.path).filter((p) => typeof p === "string" && p.startsWith("/"));
13311
13397
  } catch {
13312
13398
  return [];
@@ -13318,7 +13404,7 @@ function discoverLocalRepos(repoDir, env = process.env) {
13318
13404
  const out = new Map;
13319
13405
  for (const dir of [repoDir, ...registryRepos(env)]) {
13320
13406
  try {
13321
- const real2 = realpathSync2(dir);
13407
+ const real2 = realpathSync3(dir);
13322
13408
  if (!statSync2(real2).isDirectory())
13323
13409
  continue;
13324
13410
  const id = sourceId(real2);
@@ -13346,8 +13432,8 @@ function mountRepos(act, db, peerIsLoopback2) {
13346
13432
  }
13347
13433
 
13348
13434
  // src/server/routes/artifacts.ts
13349
- import { closeSync as closeSync2, fstatSync as fstatSync2, openSync as openSync2, readSync as readSync2, realpathSync as realpathSync3 } from "fs";
13350
- import { join as join7, sep } from "path";
13435
+ import { closeSync as closeSync2, fstatSync as fstatSync2, openSync as openSync2, readSync as readSync2, realpathSync as realpathSync4 } from "fs";
13436
+ import { join as join8, sep as sep2 } from "path";
13351
13437
  var MAX_ARTIFACT_BYTES = 5 * 1024 * 1024;
13352
13438
  var EVENTS_DEFAULT = 500;
13353
13439
  var EVENTS_MAX = 1000;
@@ -13428,19 +13514,19 @@ function mount(ctx) {
13428
13514
  return c.json({ error: "artifact not available" }, 404);
13429
13515
  let runsRoot, runDir, file;
13430
13516
  try {
13431
- runsRoot = realpathSync3(join7(repo.p, ".loki", "runs"));
13432
- runDir = realpathSync3(join7(runsRoot, run));
13517
+ runsRoot = realpathSync4(join8(repo.p, ".loki", "runs"));
13518
+ runDir = realpathSync4(join8(runsRoot, run));
13433
13519
  } catch {
13434
13520
  return c.json({ error: "artifact not found" }, 404);
13435
13521
  }
13436
- if (runDir !== join7(runsRoot, run))
13522
+ if (runDir !== join8(runsRoot, run))
13437
13523
  return c.json({ error: "artifact not found" }, 404);
13438
13524
  try {
13439
- file = realpathSync3(join7(runDir, name));
13525
+ file = realpathSync4(join8(runDir, name));
13440
13526
  } catch {
13441
13527
  return c.json({ error: "artifact not found" }, 404);
13442
13528
  }
13443
- if (!file.startsWith(runDir + sep))
13529
+ if (!file.startsWith(runDir + sep2))
13444
13530
  return c.json({ error: "artifact not found" }, 404);
13445
13531
  const r = readCapped(file);
13446
13532
  if (r.status === 413)
@@ -13478,8 +13564,8 @@ function mount2(ctx) {
13478
13564
 
13479
13565
  // src/server/routes/checkpoints.ts
13480
13566
  import { execFileSync } from "child_process";
13481
- import { appendFileSync, copyFileSync, cpSync, existsSync as existsSync5, lstatSync, mkdirSync as mkdirSync3, readdirSync as readdirSync4, readFileSync as readFileSync7, realpathSync as realpathSync4, rmSync, statSync as statSync4, writeFileSync as writeFileSync3 } from "fs";
13482
- import { join as join9, sep as sep2 } from "path";
13567
+ import { appendFileSync, copyFileSync, cpSync, existsSync as existsSync5, lstatSync, mkdirSync as mkdirSync3, readdirSync as readdirSync4, readFileSync as readFileSync7, realpathSync as realpathSync5, rmSync, statSync as statSync4, writeFileSync as writeFileSync3 } from "fs";
13568
+ import { join as join10, sep as sep3 } from "path";
13483
13569
 
13484
13570
  // src/server/audit.ts
13485
13571
  var clip = (s, n2) => s === undefined ? null : s.slice(0, n2);
@@ -13494,7 +13580,7 @@ function audit3(db, e) {
13494
13580
 
13495
13581
  // src/server/routes/cost_ledger.ts
13496
13582
  import { existsSync as existsSync4, readdirSync as readdirSync3, readFileSync as readFileSync6, statSync as statSync3 } from "fs";
13497
- import { join as join8 } from "path";
13583
+ import { join as join9 } from "path";
13498
13584
  var isNum = (v) => typeof v === "number" && Number.isFinite(v);
13499
13585
  var isObj2 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
13500
13586
  var r6 = (n2) => Math.round(n2 * 1e6) / 1e6;
@@ -13525,7 +13611,7 @@ var DEFAULT_PRICING = {
13525
13611
  "gpt-5.6-luna": { input: 0.5, output: 4 }
13526
13612
  };
13527
13613
  function pricing(lokiDir) {
13528
- const d = readJson(join8(lokiDir, "pricing.json"));
13614
+ const d = readJson(join9(lokiDir, "pricing.json"));
13529
13615
  const models = isObj2(d) && isObj2(d.models) ? d.models : {};
13530
13616
  return { ...DEFAULT_PRICING, ...models };
13531
13617
  }
@@ -13538,7 +13624,7 @@ function modelCost(table, model, inp, out, cr = 0, cw = 0) {
13538
13624
  return r6((inp * ir + out * or2 + cr * crr + cw * cwr) / 1e6);
13539
13625
  }
13540
13626
  function iterationRecords(lokiDir) {
13541
- const dir = join8(lokiDir, "metrics", "efficiency");
13627
+ const dir = join9(lokiDir, "metrics", "efficiency");
13542
13628
  let names = [];
13543
13629
  try {
13544
13630
  names = readdirSync3(dir).filter((n2) => /^iteration-.*\.json$/.test(n2)).sort();
@@ -13547,7 +13633,7 @@ function iterationRecords(lokiDir) {
13547
13633
  }
13548
13634
  const out = [];
13549
13635
  for (const n2 of names) {
13550
- const d = readJson(join8(dir, n2));
13636
+ const d = readJson(join9(dir, n2));
13551
13637
  if (isObj2(d))
13552
13638
  out.push(d);
13553
13639
  }
@@ -13584,7 +13670,7 @@ function costSnapshot(lokiDir) {
13584
13670
  }
13585
13671
  }
13586
13672
  if (tIn === 0 && tOut === 0) {
13587
- const ctx = readJson(join8(lokiDir, "context", "tracking.json"));
13673
+ const ctx = readJson(join9(lokiDir, "context", "tracking.json"));
13588
13674
  const totals = isObj2(ctx) && isObj2(ctx.totals) ? ctx.totals : {};
13589
13675
  tIn = num0(totals.total_input);
13590
13676
  tOut = num0(totals.total_output);
@@ -13610,7 +13696,7 @@ function costSnapshot(lokiDir) {
13610
13696
  }
13611
13697
  }
13612
13698
  let limit = null, used = null, remaining = null;
13613
- const bd = readJson(join8(lokiDir, "metrics", "budget.json"));
13699
+ const bd = readJson(join9(lokiDir, "metrics", "budget.json"));
13614
13700
  if (isObj2(bd) && isNum(bd.limit)) {
13615
13701
  limit = bd.limit;
13616
13702
  if (recorded && !unknownUsd) {
@@ -13661,7 +13747,7 @@ function budgetSnapshot(lokiDir) {
13661
13747
  total += priced(table, d);
13662
13748
  }
13663
13749
  let limit = null;
13664
- const bd = readJson(join8(lokiDir, "metrics", "budget.json"));
13750
+ const bd = readJson(join9(lokiDir, "metrics", "budget.json"));
13665
13751
  if (isObj2(bd)) {
13666
13752
  const l = bd.limit || bd.budget_limit;
13667
13753
  limit = isNum(l) ? l : null;
@@ -13712,19 +13798,19 @@ function costTimeline(lokiDir) {
13712
13798
  }
13713
13799
  const runs2 = [];
13714
13800
  let projectTotal = 0, runsMeasured = 0;
13715
- const proofs = join8(lokiDir, "proofs");
13801
+ const proofs = join9(lokiDir, "proofs");
13716
13802
  let entries = [];
13717
13803
  try {
13718
13804
  entries = readdirSync3(proofs).sort();
13719
13805
  } catch {}
13720
13806
  for (const e of entries) {
13721
13807
  try {
13722
- if (!statSync3(join8(proofs, e)).isDirectory())
13808
+ if (!statSync3(join9(proofs, e)).isDirectory())
13723
13809
  continue;
13724
13810
  } catch {
13725
13811
  continue;
13726
13812
  }
13727
- const p = readJson(join8(proofs, e, "proof.json"));
13813
+ const p = readJson(join9(proofs, e, "proof.json"));
13728
13814
  if (!isObj2(p))
13729
13815
  continue;
13730
13816
  const cost = isObj2(p.cost) ? p.cost : {};
@@ -13754,7 +13840,7 @@ function costTimeline(lokiDir) {
13754
13840
  ...recorded || runsMeasured ? {} : { not_measured: NOT_MEASURED }
13755
13841
  };
13756
13842
  }
13757
- var lokiDirOf = (repoDir) => join8(repoDir, ".loki");
13843
+ var lokiDirOf = (repoDir) => join9(repoDir, ".loki");
13758
13844
  var lokiDirExists = (repoDir) => existsSync4(lokiDirOf(repoDir));
13759
13845
  function mount3(ctx) {
13760
13846
  const deny = { error: "loopback only without a token" };
@@ -13768,8 +13854,8 @@ var str2 = (v) => typeof v === "string" && v !== "" ? v : null;
13768
13854
  var SAFE_ID = /^[A-Za-z0-9_-]{1,128}$/;
13769
13855
  var MAX_CHECKPOINTS = 50;
13770
13856
  var validCheckpointId = (id) => SAFE_ID.test(id);
13771
- var storeOf = (repoDir) => join9(repoDir, ".loki", "state", "checkpoints");
13772
- var lokiOf = (repoDir) => join9(repoDir, ".loki");
13857
+ var storeOf = (repoDir) => join10(repoDir, ".loki", "state", "checkpoints");
13858
+ var lokiOf = (repoDir) => join10(repoDir, ".loki");
13773
13859
  var readObj = (f) => {
13774
13860
  try {
13775
13861
  const d = JSON.parse(readFileSync7(f, "utf8"));
@@ -13783,7 +13869,7 @@ function listCheckpoints(repoDir, limit = 20) {
13783
13869
  const store = storeOf(repoDir);
13784
13870
  let lines = [];
13785
13871
  try {
13786
- lines = readFileSync7(join9(store, "index.jsonl"), "utf8").split(`
13872
+ lines = readFileSync7(join10(store, "index.jsonl"), "utf8").split(`
13787
13873
  `);
13788
13874
  } catch {
13789
13875
  return [];
@@ -13811,7 +13897,7 @@ function listCheckpoints(repoDir, limit = 20) {
13811
13897
  message: str2(raw2.task) ?? str2(raw2.message) ?? str2(raw2.task_description),
13812
13898
  iteration: Number.isInteger(it) ? it : null
13813
13899
  };
13814
- const meta = readObj(join9(store, id, "metadata.json"));
13900
+ const meta = readObj(join10(store, id, "metadata.json"));
13815
13901
  if (meta) {
13816
13902
  cp.git_branch = str2(meta.git_branch);
13817
13903
  cp.provider = str2(meta.provider);
@@ -13819,7 +13905,7 @@ function listCheckpoints(repoDir, limit = 20) {
13819
13905
  cp.message ??= str2(meta.task_description);
13820
13906
  cp.git_sha ??= str2(meta.git_sha);
13821
13907
  cp.created_at ??= str2(meta.timestamp);
13822
- cp.files_count = countFiles(join9(store, id));
13908
+ cp.files_count = countFiles(join10(store, id));
13823
13909
  }
13824
13910
  out.push(cp);
13825
13911
  }
@@ -13830,7 +13916,7 @@ function countFiles(dir) {
13830
13916
  const walk = (d, top) => {
13831
13917
  for (const e of readdirSync4(d, { withFileTypes: true })) {
13832
13918
  if (e.isDirectory())
13833
- walk(join9(d, e.name), false);
13919
+ walk(join10(d, e.name), false);
13834
13920
  else if (e.isFile() && !(top && e.name === "metadata.json"))
13835
13921
  n2++;
13836
13922
  }
@@ -13841,7 +13927,7 @@ function countFiles(dir) {
13841
13927
  return n2;
13842
13928
  }
13843
13929
  function getCheckpoint(repoDir, id) {
13844
- return validCheckpointId(id) ? readObj(join9(storeOf(repoDir), id, "metadata.json")) : null;
13930
+ return validCheckpointId(id) ? readObj(join10(storeOf(repoDir), id, "metadata.json")) : null;
13845
13931
  }
13846
13932
  var gitHead = (repoDir) => {
13847
13933
  try {
@@ -13853,69 +13939,69 @@ var gitHead = (repoDir) => {
13853
13939
  function createCheckpoint(repoDir, message, now = new Date) {
13854
13940
  const store = storeOf(repoDir), loki = lokiOf(repoDir);
13855
13941
  mkdirSync3(store, { recursive: true });
13856
- const id = `chk-${stamp(now)}`, dir = join9(store, id);
13942
+ const id = `chk-${stamp(now)}`, dir = join10(store, id);
13857
13943
  mkdirSync3(dir, { recursive: true });
13858
13944
  for (const f of ["dashboard-state.json", "session.json"]) {
13859
13945
  try {
13860
- if (existsSync5(join9(loki, f)))
13861
- copyFileSync(join9(loki, f), join9(dir, f));
13946
+ if (existsSync5(join10(loki, f)))
13947
+ copyFileSync(join10(loki, f), join10(dir, f));
13862
13948
  } catch {}
13863
13949
  }
13864
13950
  try {
13865
- if (existsSync5(join9(loki, "queue")))
13866
- cpSync(join9(loki, "queue"), join9(dir, "queue"), { recursive: true });
13951
+ if (existsSync5(join10(loki, "queue")))
13952
+ cpSync(join10(loki, "queue"), join10(dir, "queue"), { recursive: true });
13867
13953
  } catch {}
13868
13954
  const meta = { id, created_at: now.toISOString(), git_sha: gitHead(repoDir), message: message || null, files: readdirSync4(dir, { withFileTypes: true }).filter((e) => e.isFile()).map((e) => e.name) };
13869
- writeFileSync3(join9(dir, "metadata.json"), JSON.stringify(meta, null, 2));
13870
- appendFileSync(join9(store, "index.jsonl"), `${JSON.stringify(meta)}
13955
+ writeFileSync3(join10(dir, "metadata.json"), JSON.stringify(meta, null, 2));
13956
+ appendFileSync(join10(store, "index.jsonl"), `${JSON.stringify(meta)}
13871
13957
  `);
13872
13958
  const mine = readdirSync4(store, { withFileTypes: true }).filter((e) => e.isDirectory() && e.name.startsWith("chk-")).map((e) => e.name).sort();
13873
13959
  while (mine.length > MAX_CHECKPOINTS)
13874
- rmSync(join9(store, mine.shift()), { recursive: true, force: true });
13960
+ rmSync(join10(store, mine.shift()), { recursive: true, force: true });
13875
13961
  return meta;
13876
13962
  }
13877
13963
  function rollbackCheckpoint(repoDir, id, now = new Date) {
13878
13964
  const store = storeOf(repoDir), loki = lokiOf(repoDir);
13879
13965
  let real2 = "", realStore = "";
13880
13966
  try {
13881
- real2 = realpathSync4(join9(store, id));
13882
- realStore = realpathSync4(store);
13967
+ real2 = realpathSync5(join10(store, id));
13968
+ realStore = realpathSync5(store);
13883
13969
  } catch {
13884
13970
  return null;
13885
13971
  }
13886
- if (!real2.startsWith(`${realStore}${sep2}`) || !statSync4(real2).isDirectory())
13972
+ if (!real2.startsWith(`${realStore}${sep3}`) || !statSync4(real2).isDirectory())
13887
13973
  return null;
13888
- const preId = `rb-pre-${stamp(now)}`, pre = join9(store, preId);
13974
+ const preId = `rb-pre-${stamp(now)}`, pre = join10(store, preId);
13889
13975
  mkdirSync3(pre, { recursive: true });
13890
13976
  const notStore = (src) => src !== store;
13891
13977
  for (const n2 of ["session.json", "dashboard-state.json", "CONTINUITY.md", "autonomy-state.json"]) {
13892
13978
  try {
13893
- if (existsSync5(join9(loki, n2)))
13894
- copyFileSync(join9(loki, n2), join9(pre, n2));
13979
+ if (existsSync5(join10(loki, n2)))
13980
+ copyFileSync(join10(loki, n2), join10(pre, n2));
13895
13981
  } catch {}
13896
13982
  }
13897
13983
  for (const d of ["state", "queue"]) {
13898
13984
  try {
13899
- if (existsSync5(join9(loki, d)))
13900
- cpSync(join9(loki, d), join9(pre, d), { recursive: true, filter: notStore });
13985
+ if (existsSync5(join10(loki, d)))
13986
+ cpSync(join10(loki, d), join10(pre, d), { recursive: true, filter: notStore });
13901
13987
  } catch {}
13902
13988
  }
13903
13989
  const preMeta = { id: preId, created_at: now.toISOString(), message: `pre-rollback snapshot (before restoring ${id})`, created_by: "control plane rollback" };
13904
- writeFileSync3(join9(pre, "metadata.json"), JSON.stringify(preMeta, null, 2));
13905
- appendFileSync(join9(store, "index.jsonl"), `${JSON.stringify(preMeta)}
13990
+ writeFileSync3(join10(pre, "metadata.json"), JSON.stringify(preMeta, null, 2));
13991
+ appendFileSync(join10(store, "index.jsonl"), `${JSON.stringify(preMeta)}
13906
13992
  `);
13907
13993
  let restored = 0;
13908
13994
  const errors = [];
13909
13995
  for (const e of readdirSync4(real2, { withFileTypes: true })) {
13910
13996
  if (e.name === "metadata.json" || e.name === "worktree-snapshot.txt")
13911
13997
  continue;
13912
- const src = join9(real2, e.name);
13998
+ const src = join10(real2, e.name);
13913
13999
  try {
13914
14000
  if (lstatSync(src).isSymbolicLink()) {
13915
14001
  errors.push(`${e.name}: symlink skipped`);
13916
14002
  continue;
13917
14003
  }
13918
- cpSync(src, join9(loki, e.name), { recursive: true, filter: (s) => s !== join9(real2, "state", "checkpoints") });
14004
+ cpSync(src, join10(loki, e.name), { recursive: true, filter: (s) => s !== join10(real2, "state", "checkpoints") });
13919
14005
  restored++;
13920
14006
  } catch (err) {
13921
14007
  errors.push(`${e.name}: ${err.message}`);
@@ -13925,14 +14011,14 @@ function rollbackCheckpoint(repoDir, id, now = new Date) {
13925
14011
  }
13926
14012
  function auditSummary(db, days, now = Date.now()) {
13927
14013
  const since = new Date(now - days * 86400000).toISOString();
13928
- const where = gte(audit.ts, since);
13929
- const total = db.select({ n: sql`count(*)` }).from(audit).where(where).get()?.n ?? 0;
13930
- const by = (col) => Object.fromEntries(db.select({ k: col, n: sql`count(*)` }).from(audit).where(where).groupBy(col).all().map((r) => [r.k, r.n]));
14014
+ const where = gte(audit2.ts, since);
14015
+ const total = db.select({ n: sql`count(*)` }).from(audit2).where(where).get()?.n ?? 0;
14016
+ const by = (col) => Object.fromEntries(db.select({ k: col, n: sql`count(*)` }).from(audit2).where(where).groupBy(col).all().map((r) => [r.k, r.n]));
13931
14017
  return {
13932
14018
  period_days: days,
13933
14019
  total_events: total,
13934
- by_action: by(audit.action),
13935
- by_user: by(audit.actor),
14020
+ by_action: by(audit2.action),
14021
+ by_user: by(audit2.actor),
13936
14022
  successful_events: null,
13937
14023
  failed_events: null,
13938
14024
  by_resource_type: null,
@@ -13998,8 +14084,8 @@ function mount4(ctx) {
13998
14084
 
13999
14085
  // src/server/routes/config.ts
14000
14086
  import { createHash as createHash5, randomBytes } from "crypto";
14001
- import { closeSync as closeSync3, fchmodSync, fsyncSync, lstatSync as lstatSync2, openSync as openSync3, readFileSync as readFileSync9, realpathSync as realpathSync5, renameSync as renameSync2, statSync as statSync6, unlinkSync, writeSync as writeSync2 } from "fs";
14002
- import { basename as basename3, dirname as dirname2, join as join11, sep as sep3 } from "path";
14087
+ import { closeSync as closeSync3, fchmodSync, fsyncSync, lstatSync as lstatSync2, openSync as openSync3, readFileSync as readFileSync9, realpathSync as realpathSync6, renameSync as renameSync2, statSync as statSync6, unlinkSync, writeSync as writeSync2 } from "fs";
14088
+ import { basename as basename3, dirname as dirname2, join as join12, sep as sep4 } from "path";
14003
14089
 
14004
14090
  // node_modules/yaml/dist/index.js
14005
14091
  var composer = require_composer();
@@ -14050,8 +14136,8 @@ var $visitAsync = visit.visitAsync;
14050
14136
  // src/server/spawn.ts
14051
14137
  import { spawn } from "child_process";
14052
14138
  import { existsSync as existsSync6, readFileSync as readFileSync8, statSync as statSync5 } from "fs";
14053
- import { homedir as homedir3 } from "os";
14054
- import { basename as basename2, join as join10, resolve as resolve2 } from "path";
14139
+ import { homedir as homedir4 } from "os";
14140
+ import { basename as basename2, join as join11, resolve as resolve2 } from "path";
14055
14141
  var ISSUE = /^[A-Za-z0-9][A-Za-z0-9._-]{0,99}\/[A-Za-z0-9._-]{1,100}#[1-9][0-9]{0,8}$/;
14056
14142
  var TASK = /^[A-Za-z0-9][A-Za-z0-9 .,_/#@+=-]{0,499}$/;
14057
14143
  var hasDotDot = (t) => t.split(/[\\/ ]/).some((seg) => seg.startsWith(".."));
@@ -14067,7 +14153,7 @@ var BUDGET = /^(?:[1-9][0-9]{0,5}|0)(?:\.[0-9]{1,2})?$/;
14067
14153
  var WORKSPACE = /^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$/;
14068
14154
  function registryRepos2(env = process.env) {
14069
14155
  try {
14070
- const j = JSON.parse(readFileSync8(join10(env.HOME || homedir3(), ".loki", "dashboard", "projects.json"), "utf8"));
14156
+ const j = JSON.parse(readFileSync8(join11(env.HOME || homedir4(), ".loki", "dashboard", "projects.json"), "utf8"));
14071
14157
  return Object.values(j.projects ?? {}).map((p) => typeof p?.path === "string" ? p.path : "").filter((p) => p && existsSync6(p) && statSync5(p).isDirectory());
14072
14158
  } catch {
14073
14159
  return [];
@@ -14512,11 +14598,11 @@ function renderVerified(doc, next, quote = needsQuote) {
14512
14598
  function resolveConfigFile(repoDir) {
14513
14599
  let root;
14514
14600
  try {
14515
- root = realpathSync5(repoDir);
14601
+ root = realpathSync6(repoDir);
14516
14602
  } catch {
14517
14603
  return { ok: false, status: 400, error: "repo directory not found" };
14518
14604
  }
14519
- const p = join11(root, "loki.yaml");
14605
+ const p = join12(root, "loki.yaml");
14520
14606
  let st;
14521
14607
  try {
14522
14608
  st = lstatSync2(p);
@@ -14526,11 +14612,11 @@ function resolveConfigFile(repoDir) {
14526
14612
  if (st.isSymbolicLink()) {
14527
14613
  let real2;
14528
14614
  try {
14529
- real2 = realpathSync5(p);
14615
+ real2 = realpathSync6(p);
14530
14616
  } catch {
14531
14617
  return { ok: false, status: 400, error: "loki.yaml is a broken symlink" };
14532
14618
  }
14533
- if (!real2.startsWith(root + sep3))
14619
+ if (!real2.startsWith(root + sep4))
14534
14620
  return { ok: false, status: 400, error: "loki.yaml is a symlink that resolves outside the repo" };
14535
14621
  if (!statSync6(real2).isFile())
14536
14622
  return { ok: false, status: 422, error: "loki.yaml is not a regular file" };
@@ -14541,7 +14627,7 @@ function resolveConfigFile(repoDir) {
14541
14627
  return { ok: true, file: p, exists: true };
14542
14628
  }
14543
14629
  function atomicWrite(file, text2, mode) {
14544
- const tmp = join11(dirname2(file), `.${basename3(file)}.${process.pid}.${randomBytes(6).toString("hex")}.tmp`);
14630
+ const tmp = join12(dirname2(file), `.${basename3(file)}.${process.pid}.${randomBytes(6).toString("hex")}.tmp`);
14545
14631
  const fd = openSync3(tmp, "wx", mode);
14546
14632
  try {
14547
14633
  writeSync2(fd, text2);
@@ -14691,14 +14777,14 @@ function mount6(ctx) {
14691
14777
  }
14692
14778
 
14693
14779
  // src/server/routes/control.ts
14694
- import { existsSync as existsSync7, realpathSync as realpathSync6 } from "fs";
14695
- import { join as join13, resolve as resolve3, sep as sep4 } from "path";
14780
+ import { existsSync as existsSync7, realpathSync as realpathSync7 } from "fs";
14781
+ import { join as join14, resolve as resolve3, sep as sep5 } from "path";
14696
14782
 
14697
14783
  // ../../loki-ts/src/util/run_pid.ts
14698
14784
  import { execFileSync as execFileSync2 } from "child_process";
14699
14785
  import { chmodSync as chmodSync2, readFileSync as readFileSync10, renameSync as renameSync3, rmSync as rmSync2, writeFileSync as writeFileSync4 } from "fs";
14700
- import { join as join12 } from "path";
14701
- var runPidPath = (runDir) => join12(runDir, "run.pid");
14786
+ import { join as join13 } from "path";
14787
+ var runPidPath = (runDir) => join13(runDir, "run.pid");
14702
14788
  function processStartTime(pid) {
14703
14789
  if (!Number.isInteger(pid) || pid <= 1)
14704
14790
  return null;
@@ -14754,14 +14840,14 @@ function mount7(ctx) {
14754
14840
  const repoRow = db.select().from(localRepos).where(eq(localRepos.sourceId, source)).get();
14755
14841
  let repo = "";
14756
14842
  try {
14757
- repo = repoRow ? realpathSync6(repoRow.realpath) : "";
14843
+ repo = repoRow ? realpathSync7(repoRow.realpath) : "";
14758
14844
  } catch {
14759
14845
  repo = "";
14760
14846
  }
14761
14847
  if (!repo)
14762
14848
  return done(404, "refused", { error: "this run's repo is not known on this machine" });
14763
- const runsRoot = join13(repo, ".loki", "runs"), runDir = resolve3(runsRoot, id);
14764
- if (!runDir.startsWith(runsRoot + sep4))
14849
+ const runsRoot = join14(repo, ".loki", "runs"), runDir = resolve3(runsRoot, id);
14850
+ if (!runDir.startsWith(runsRoot + sep5))
14765
14851
  return done(400, "refused", { error: "invalid run id" }, "path escape");
14766
14852
  return handler({ source, run: id, repo, runDir }, done);
14767
14853
  });
@@ -14808,7 +14894,7 @@ function mount7(ctx) {
14808
14894
  return done(404, "refused", { error: "run not found" });
14809
14895
  if (!blockedQuestion(loadEvents(db, a.source, a.run), row.verdict))
14810
14896
  return done(409, "refused", { error: "run is not BLOCKED, so there is nothing to resume" });
14811
- if (!existsSync7(join13(answerDir, a.source, `${a.run}.answer.txt`)))
14897
+ if (!existsSync7(join14(answerDir, a.source, `${a.run}.answer.txt`)))
14812
14898
  return done(409, "refused", { error: "no answer recorded for this run; answer it first" });
14813
14899
  return launch(a, [bin, "answer", a.run], done);
14814
14900
  });
@@ -14887,11 +14973,11 @@ function mount8(ctx) {
14887
14973
 
14888
14974
  // src/server/routes/fleet.ts
14889
14975
  import { readdirSync as readdirSync5, readFileSync as readFileSync11 } from "fs";
14890
- import { homedir as homedir4 } from "os";
14891
- import { basename as basename4, join as join14 } from "path";
14976
+ import { homedir as homedir5 } from "os";
14977
+ import { basename as basename4, join as join15 } from "path";
14892
14978
  var isNum2 = (v) => typeof v === "number" && Number.isFinite(v);
14893
14979
  var isObj5 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
14894
- var registryFile = () => join14(process.env.HOME ?? homedir4(), ".loki", "dashboard", "projects.json");
14980
+ var registryFile = () => join15(process.env.HOME ?? homedir5(), ".loki", "dashboard", "projects.json");
14895
14981
  function pidAlive(pid) {
14896
14982
  if (typeof pid !== "number" || !Number.isInteger(pid) || pid <= 0)
14897
14983
  return false;
@@ -14906,33 +14992,33 @@ function projectSnapshot(path) {
14906
14992
  const snap = { phase: "", iteration: null, cost_usd: null, started_at: null, ended_at: null };
14907
14993
  if (!path)
14908
14994
  return snap;
14909
- const loki = join14(path, ".loki");
14910
- const st = readJson(join14(loki, "dashboard-state.json"));
14995
+ const loki = join15(path, ".loki");
14996
+ const st = readJson(join15(loki, "dashboard-state.json"));
14911
14997
  if (isObj5(st)) {
14912
14998
  snap.phase = typeof st.phase === "string" ? st.phase : "";
14913
14999
  snap.iteration = Number.isInteger(st.iteration) ? st.iteration : null;
14914
15000
  }
14915
- const ss = readJson(join14(loki, "session.json"));
15001
+ const ss = readJson(join15(loki, "session.json"));
14916
15002
  if (isObj5(ss)) {
14917
15003
  const a = ss.startedAt || ss.started_at, b = ss.endedAt || ss.ended_at;
14918
15004
  snap.started_at = typeof a === "string" ? a : null;
14919
15005
  snap.ended_at = typeof b === "string" ? b : null;
14920
15006
  }
14921
15007
  let cost = 0, found = false;
14922
- const eff = join14(loki, "metrics", "efficiency");
15008
+ const eff = join15(loki, "metrics", "efficiency");
14923
15009
  let names = [];
14924
15010
  try {
14925
15011
  names = readdirSync5(eff).filter((n2) => n2.endsWith(".json"));
14926
15012
  } catch {}
14927
15013
  for (const n2 of names) {
14928
- const d = readJson(join14(eff, n2));
15014
+ const d = readJson(join15(eff, n2));
14929
15015
  if (isObj5(d) && isNum2(d.cost_usd) && recordIsMeasured(d)) {
14930
15016
  cost += d.cost_usd;
14931
15017
  found = true;
14932
15018
  }
14933
15019
  }
14934
15020
  if (!found) {
14935
- const t = readJson(join14(loki, "context", "tracking.json"));
15021
+ const t = readJson(join15(loki, "context", "tracking.json"));
14936
15022
  const totals = isObj5(t) && isObj5(t.totals) ? t.totals : null;
14937
15023
  if (totals && isNum2(totals.total_cost_usd) && recordIsMeasured({ cost_usd: totals.total_cost_usd, input_tokens: totals.total_input, output_tokens: totals.total_output })) {
14938
15024
  cost = totals.total_cost_usd;
@@ -15005,8 +15091,8 @@ function mount9(ctx) {
15005
15091
  }
15006
15092
 
15007
15093
  // src/server/routes/memory.ts
15008
- import { existsSync as existsSync8, readFileSync as readFileSync12, readdirSync as readdirSync6, realpathSync as realpathSync7, statSync as statSync7 } from "fs";
15009
- import { join as join15, sep as sep5 } from "path";
15094
+ import { existsSync as existsSync8, readFileSync as readFileSync12, readdirSync as readdirSync6, realpathSync as realpathSync8, statSync as statSync7 } from "fs";
15095
+ import { join as join16, sep as sep6 } from "path";
15010
15096
  var isObj6 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
15011
15097
  var nonEmpty = (v) => !(v === undefined || v === null || v === "" || Array.isArray(v) && v.length === 0 || isObj6(v) && Object.keys(v).length === 0);
15012
15098
  var focus = { dir: null };
@@ -15037,7 +15123,7 @@ function jsonFiles(dir, cap = Infinity) {
15037
15123
  for (const e of ents.sort((a, b) => a.name < b.name ? -1 : 1)) {
15038
15124
  if (out2.length >= cap)
15039
15125
  return;
15040
- const p = join15(d, e.name);
15126
+ const p = join16(d, e.name);
15041
15127
  if (e.isDirectory())
15042
15128
  walk(p);
15043
15129
  else if (e.isFile() && e.name.endsWith(".json"))
@@ -15058,24 +15144,24 @@ function setFocus(repoDir, raw2, registry = registryFile()) {
15058
15144
  return { ok: false, error: "project_dir must be an absolute path without .. segments" };
15059
15145
  let real2 = "";
15060
15146
  try {
15061
- real2 = realpathSync7(s);
15147
+ real2 = realpathSync8(s);
15062
15148
  if (!statSync7(real2).isDirectory())
15063
15149
  throw new Error("not a dir");
15064
15150
  } catch {
15065
15151
  return { ok: false, error: `Directory does not exist: ${s}` };
15066
15152
  }
15067
- if (!existsSync8(join15(real2, ".loki")) || !statSync7(join15(real2, ".loki")).isDirectory())
15153
+ if (!existsSync8(join16(real2, ".loki")) || !statSync7(join16(real2, ".loki")).isDirectory())
15068
15154
  return { ok: false, error: `Directory does not contain a .loki/ subdirectory: ${s}` };
15069
15155
  const allowed = new Set;
15070
15156
  try {
15071
- allowed.add(realpathSync7(repoDir));
15157
+ allowed.add(realpathSync8(repoDir));
15072
15158
  } catch {}
15073
15159
  const reg = readJson(registry);
15074
15160
  if (isObj6(reg) && isObj6(reg.projects))
15075
15161
  for (const p of Object.values(reg.projects)) {
15076
15162
  if (isObj6(p) && typeof p.path === "string") {
15077
15163
  try {
15078
- allowed.add(realpathSync7(p.path));
15164
+ allowed.add(realpathSync8(p.path));
15079
15165
  } catch {}
15080
15166
  }
15081
15167
  }
@@ -15089,7 +15175,7 @@ var QUEUES = [["pending.json", "pending"], ["in-progress.json", "in_progress"],
15089
15175
  var RANK = { done: 3, in_progress: 2, review: 1, pending: 0 };
15090
15176
  function tasksList(loki, status) {
15091
15177
  const all = [];
15092
- const st = readJson(join15(loki, "dashboard-state.json"));
15178
+ const st = readJson(join16(loki, "dashboard-state.json"));
15093
15179
  const groups = isObj6(st) && isObj6(st.tasks) ? st.tasks : {};
15094
15180
  for (const [key, mapped] of GROUPS) {
15095
15181
  const g = groups[key];
@@ -15111,7 +15197,7 @@ function tasksList(loki, status) {
15111
15197
  });
15112
15198
  }
15113
15199
  for (const [file, qs] of QUEUES) {
15114
- const raw2 = readJson(join15(loki, "queue", file));
15200
+ const raw2 = readJson(join16(loki, "queue", file));
15115
15201
  const items = isObj6(raw2) ? raw2.tasks : raw2;
15116
15202
  if (!Array.isArray(items))
15117
15203
  continue;
@@ -15165,7 +15251,7 @@ function tasksList(loki, status) {
15165
15251
  }
15166
15252
  return status ? out2.filter((t) => t.status === status) : out2;
15167
15253
  }
15168
- var memDir = (loki, sub) => join15(loki, "memory", sub);
15254
+ var memDir = (loki, sub) => join16(loki, "memory", sub);
15169
15255
  function memoryEpisodes(loki, limit = 50) {
15170
15256
  const eps = jsonFiles(memDir(loki, "episodic")).map(readJson).filter(isObj6);
15171
15257
  eps.sort((a, b) => String(b.timestamp ?? "").localeCompare(String(a.timestamp ?? "")));
@@ -15174,19 +15260,19 @@ function memoryEpisodes(loki, limit = 50) {
15174
15260
  function memoryEpisode(loki, id) {
15175
15261
  for (const f of jsonFiles(memDir(loki, "episodic"))) {
15176
15262
  const d = readJson(f);
15177
- if (isObj6(d) && (d.id === id || f.split(sep5).pop() === `${id}.json`))
15263
+ if (isObj6(d) && (d.id === id || f.split(sep6).pop() === `${id}.json`))
15178
15264
  return d;
15179
15265
  }
15180
15266
  return null;
15181
15267
  }
15182
15268
  function memoryPatterns(loki) {
15183
- const d = readJson(join15(memDir(loki, "semantic"), "patterns.json"));
15269
+ const d = readJson(join16(memDir(loki, "semantic"), "patterns.json"));
15184
15270
  const list = Array.isArray(d) ? d : isObj6(d) && Array.isArray(d.patterns) ? d.patterns : [];
15185
15271
  return list.filter(isObj6);
15186
15272
  }
15187
15273
  var memorySkills = (loki) => jsonFiles(memDir(loki, "skills")).map(readJson).filter(isObj6);
15188
15274
  function memoryEconomics(loki) {
15189
- const rawV = readJson(join15(loki, "memory", "token_economics.json"));
15275
+ const rawV = readJson(join16(loki, "memory", "token_economics.json"));
15190
15276
  const raw2 = isObj6(rawV) ? rawV : {};
15191
15277
  const m = isObj6(raw2.metrics) ? raw2.metrics : {};
15192
15278
  const n2 = (v) => typeof v === "number" && Number.isFinite(v) ? Math.trunc(v) : 0;
@@ -15222,7 +15308,7 @@ function memoryEconomics(loki) {
15222
15308
  };
15223
15309
  }
15224
15310
  function contextTracking(loki) {
15225
- const f = join15(loki, "context", "tracking.json");
15311
+ const f = join16(loki, "context", "tracking.json");
15226
15312
  if (!existsSync8(f)) {
15227
15313
  return { status: 200, body: {
15228
15314
  session_id: null,
@@ -15284,7 +15370,7 @@ function mount10(ctx) {
15284
15370
 
15285
15371
  // src/server/routes/metrics.ts
15286
15372
  import { readdirSync as readdirSync7, readFileSync as readFileSync13, statSync as statSync8 } from "fs";
15287
- import { join as join16 } from "path";
15373
+ import { join as join17 } from "path";
15288
15374
  var isObj7 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
15289
15375
  var arr = (v) => Array.isArray(v) ? v : [];
15290
15376
  function buildMetricsText(repoDir, now = Date.now()) {
@@ -15301,7 +15387,7 @@ function buildMetricsText(repoDir, now = Date.now()) {
15301
15387
  L.push(...samples);
15302
15388
  L.push("");
15303
15389
  };
15304
- const stRaw = readJson(join16(loki, "dashboard-state.json"));
15390
+ const stRaw = readJson(join17(loki, "dashboard-state.json"));
15305
15391
  const state = isObj7(stRaw) ? stRaw : null;
15306
15392
  let status = 0;
15307
15393
  if (state?.mode === "paused")
@@ -15310,7 +15396,7 @@ function buildMetricsText(repoDir, now = Date.now()) {
15310
15396
  status = 1;
15311
15397
  else {
15312
15398
  try {
15313
- if (pidAlive(parseInt(readFileSync13(join16(loki, "loki.pid"), "utf8").trim(), 10)))
15399
+ if (pidAlive(parseInt(readFileSync13(join17(loki, "loki.pid"), "utf8").trim(), 10)))
15314
15400
  status = 1;
15315
15401
  } catch {}
15316
15402
  }
@@ -15326,7 +15412,7 @@ function buildMetricsText(repoDir, now = Date.now()) {
15326
15412
  `loki_tasks_total{status="failed"} ${arr(tasks.failed).length}`
15327
15413
  ] : null);
15328
15414
  let agents = null;
15329
- const af = readJson(join16(loki, "state", "agents.json"));
15415
+ const af = readJson(join17(loki, "state", "agents.json"));
15330
15416
  if (Array.isArray(af) && af.length)
15331
15417
  agents = af;
15332
15418
  else if (state && Array.isArray(state.agents) && state.agents.length)
@@ -15338,7 +15424,7 @@ function buildMetricsText(repoDir, now = Date.now()) {
15338
15424
  gauge("loki_cost_usd", "Estimated total cost in USD", "gauge", cost.estimated_cost_usd !== null ? [`loki_cost_usd ${cost.estimated_cost_usd}`] : null);
15339
15425
  let events2 = null;
15340
15426
  try {
15341
- events2 = readFileSync13(join16(loki, "events.jsonl"), "utf8").split(`
15427
+ events2 = readFileSync13(join17(loki, "events.jsonl"), "utf8").split(`
15342
15428
  `).filter((l) => l.trim()).length;
15343
15429
  } catch {}
15344
15430
  gauge("loki_events_total", "Total number of events recorded", "counter", events2 !== null ? [`loki_events_total ${events2}`] : null);
@@ -15350,19 +15436,19 @@ function buildMetricsText(repoDir, now = Date.now()) {
15350
15436
  }
15351
15437
  gauge("loki_uptime_seconds", "Seconds since session started", "gauge", uptime !== null ? [`loki_uptime_seconds ${uptime}`] : null);
15352
15438
  let total = 0, ver = 0, gaps = 0, notver = 0, unk = 0, att = 0, sig = 0;
15353
- const pdir = join16(loki, "proofs");
15439
+ const pdir = join17(loki, "proofs");
15354
15440
  let entries = [];
15355
15441
  try {
15356
15442
  entries = readdirSync7(pdir).sort();
15357
15443
  } catch {}
15358
15444
  for (const e of entries) {
15359
15445
  try {
15360
- if (!statSync8(join16(pdir, e)).isDirectory())
15446
+ if (!statSync8(join17(pdir, e)).isDirectory())
15361
15447
  continue;
15362
15448
  } catch {
15363
15449
  continue;
15364
15450
  }
15365
- const d = readJson(join16(pdir, e, "proof.json"));
15451
+ const d = readJson(join17(pdir, e, "proof.json"));
15366
15452
  if (!isObj7(d))
15367
15453
  continue;
15368
15454
  total++;
@@ -15407,7 +15493,7 @@ function mount11(ctx) {
15407
15493
 
15408
15494
  // src/server/routes/session_control.ts
15409
15495
  import { existsSync as existsSync9, mkdirSync as mkdirSync4, readdirSync as readdirSync8, readFileSync as readFileSync14, unlinkSync as unlinkSync2, writeFileSync as writeFileSync5 } from "fs";
15410
- import { dirname as dirname3, join as join17 } from "path";
15496
+ import { dirname as dirname3, join as join18 } from "path";
15411
15497
  var isObj8 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
15412
15498
  var ACTIONS = ["pause", "resume", "stop", "council-review"];
15413
15499
  var isAction = (v) => ACTIONS.includes(v);
@@ -15432,7 +15518,7 @@ var readJson2 = (p) => {
15432
15518
  };
15433
15519
  function runnerPid(loki) {
15434
15520
  try {
15435
- const pid = parseInt(readFileSync14(join17(loki, "loki.pid"), "utf8").trim(), 10);
15521
+ const pid = parseInt(readFileSync14(join18(loki, "loki.pid"), "utf8").trim(), 10);
15436
15522
  return pidAlive(pid) ? pid : null;
15437
15523
  } catch {
15438
15524
  return null;
@@ -15442,18 +15528,18 @@ function applyControl(repoDir, action) {
15442
15528
  const loki = lokiDirOf(repoDir);
15443
15529
  const running = runnerPid(loki) !== null;
15444
15530
  const put = (rel) => {
15445
- const f = join17(loki, rel);
15531
+ const f = join18(loki, rel);
15446
15532
  mkdirSync4(dirname3(f), { recursive: true });
15447
15533
  writeFileSync5(f, new Date().toISOString());
15448
15534
  };
15449
15535
  if (action === "council-review") {
15450
- put(join17("signals", "COUNCIL_REVIEW_REQUESTED"));
15536
+ put(join18("signals", "COUNCIL_REVIEW_REQUESTED"));
15451
15537
  return { status: 200, body: { success: true, message: "Council review requested" } };
15452
15538
  }
15453
15539
  if (action === "resume") {
15454
15540
  for (const n2 of ["PAUSE", "STOP"]) {
15455
15541
  try {
15456
- unlinkSync2(join17(loki, n2));
15542
+ unlinkSync2(join18(loki, n2));
15457
15543
  } catch {}
15458
15544
  }
15459
15545
  return running ? { status: 200, body: { success: true, message: "Session resumed", process_verified: true } } : { status: 503, body: { success: false, message: "Session process is not running; the signal files were cleared" } };
@@ -15473,16 +15559,16 @@ function runControl(db, repoDir, action, c, via) {
15473
15559
  return c.json(r.body, r.status);
15474
15560
  }
15475
15561
  function councilState(loki) {
15476
- const f = join17(loki, "council", "state.json");
15562
+ const f = join18(loki, "council", "state.json");
15477
15563
  if (!existsSync9(f))
15478
15564
  return { enabled: false, total_votes: 0, verdicts: [] };
15479
15565
  const d = readJson2(f);
15480
15566
  return isObj8(d) ? d : { enabled: null, total_votes: null, verdicts: null, error: "unreadable_state" };
15481
15567
  }
15482
15568
  function councilVerdicts(loki, limit = 20) {
15483
- const st = readJson2(join17(loki, "council", "state.json"));
15569
+ const st = readJson2(join18(loki, "council", "state.json"));
15484
15570
  const verdicts = isObj8(st) && Array.isArray(st.verdicts) ? st.verdicts : [];
15485
- const votes = join17(loki, "council", "votes");
15571
+ const votes = join18(loki, "council", "votes");
15486
15572
  const details = [];
15487
15573
  let names = [];
15488
15574
  try {
@@ -15496,8 +15582,8 @@ function councilVerdicts(loki, limit = 20) {
15496
15582
  }
15497
15583
  };
15498
15584
  for (const n2 of names.slice(0, limit)) {
15499
- const dir = join17(votes, n2), d = { iteration: n2 };
15500
- const ev = text2(join17(dir, "evidence.md"));
15585
+ const dir = join18(votes, n2), d = { iteration: n2 };
15586
+ const ev = text2(join18(dir, "evidence.md"));
15501
15587
  if (ev !== null)
15502
15588
  d.evidence_preview = ev.slice(0, 500);
15503
15589
  let files = [];
@@ -15505,10 +15591,10 @@ function councilVerdicts(loki, limit = 20) {
15505
15591
  files = readdirSync8(dir).filter((f) => /^member-.*\.txt$/.test(f)).sort();
15506
15592
  } catch {}
15507
15593
  d.members = files.flatMap((f) => {
15508
- const t = text2(join17(dir, f));
15594
+ const t = text2(join18(dir, f));
15509
15595
  return t === null ? [] : [{ member: f.replace(/\.txt$/, ""), content: t.trim() }];
15510
15596
  });
15511
- const con = text2(join17(dir, "contrarian.txt"));
15597
+ const con = text2(join18(dir, "contrarian.txt"));
15512
15598
  if (con !== null)
15513
15599
  d.contrarian = con.trim();
15514
15600
  details.push(d);
@@ -15519,7 +15605,7 @@ function councilConvergence(loki) {
15519
15605
  const dataPoints = [];
15520
15606
  let raw2 = "";
15521
15607
  try {
15522
- raw2 = readFileSync14(join17(loki, "council", "convergence.log"), "utf8");
15608
+ raw2 = readFileSync14(join18(loki, "council", "convergence.log"), "utf8");
15523
15609
  } catch {}
15524
15610
  for (const line2 of raw2.trim().split(`
15525
15611
  `)) {
@@ -15535,7 +15621,7 @@ function councilConvergence(loki) {
15535
15621
  }
15536
15622
  function councilReport(loki) {
15537
15623
  try {
15538
- return { report: readFileSync14(join17(loki, "council", "report.md"), "utf8") };
15624
+ return { report: readFileSync14(join18(loki, "council", "report.md"), "utf8") };
15539
15625
  } catch {
15540
15626
  return { report: null };
15541
15627
  }
@@ -15548,7 +15634,7 @@ function councilTranscripts(loki, q) {
15548
15634
  if (Number.isNaN(since))
15549
15635
  return { error: "Invalid 'since' timestamp format; expected ISO8601" };
15550
15636
  }
15551
- const dir = join17(loki, "council", "transcripts");
15637
+ const dir = join18(loki, "council", "transcripts");
15552
15638
  let names = [];
15553
15639
  try {
15554
15640
  names = readdirSync8(dir).filter((f) => /^iter-.*\.json$/.test(f)).sort().reverse();
@@ -15557,7 +15643,7 @@ function councilTranscripts(loki, q) {
15557
15643
  }
15558
15644
  const out2 = [];
15559
15645
  for (const n2 of names) {
15560
- const rec = readJson2(join17(dir, n2));
15646
+ const rec = readJson2(join18(dir, n2));
15561
15647
  if (!isObj8(rec) || typeof rec.iteration_id !== "string")
15562
15648
  continue;
15563
15649
  if (since !== null) {
@@ -15610,11 +15696,11 @@ function mount12(ctx) {
15610
15696
  // src/server/routes/integrations.ts
15611
15697
  import { spawn as spawn2 } from "child_process";
15612
15698
  import { existsSync as existsSync10, readFileSync as readFileSync15, statSync as statSync9 } from "fs";
15613
- import { delimiter, join as join18 } from "path";
15699
+ import { delimiter, join as join19 } from "path";
15614
15700
  var GH_TIMEOUT_MS = 3000;
15615
15701
  var isSet = (n2) => !!n2 && (process.env[n2] ?? "") !== "";
15616
15702
  function ghAuthed(pathVar = process.env.PATH ?? "", timeoutMs = GH_TIMEOUT_MS) {
15617
- const exe = pathVar.split(delimiter).filter(Boolean).map((d) => join18(d, "gh")).find((p) => existsSync10(p));
15703
+ const exe = pathVar.split(delimiter).filter(Boolean).map((d) => join19(d, "gh")).find((p) => existsSync10(p));
15618
15704
  if (!exe)
15619
15705
  return Promise.resolve(null);
15620
15706
  return new Promise((resolve4) => {
@@ -15659,7 +15745,7 @@ function configuredName(repoDir, path) {
15659
15745
  }
15660
15746
  function mcpServers(repoDir) {
15661
15747
  try {
15662
- const p = join18(repoDir, ".mcp.json");
15748
+ const p = join19(repoDir, ".mcp.json");
15663
15749
  if (!existsSync10(p) || statSync9(p).size > 1e6)
15664
15750
  return null;
15665
15751
  const j = JSON.parse(readFileSync15(p, "utf8"));
@@ -15740,7 +15826,7 @@ function mount13(ctx) {
15740
15826
 
15741
15827
  // src/server/routes/merge_risk.ts
15742
15828
  import { spawn as spawn3 } from "child_process";
15743
- import { realpathSync as realpathSync8 } from "fs";
15829
+ import { realpathSync as realpathSync9 } from "fs";
15744
15830
  import { basename as basename5, resolve as resolve4 } from "path";
15745
15831
  var PR = /^[0-9]{1,7}$/;
15746
15832
  var REF = /^[A-Za-z0-9._/][A-Za-z0-9._/-]{0,99}$/;
@@ -15839,7 +15925,7 @@ function mount14(ctx) {
15839
15925
  const active = { run: 0, queue: 0, risk: 0 };
15840
15926
  const real2 = (p) => {
15841
15927
  try {
15842
- return realpathSync8(p);
15928
+ return realpathSync9(p);
15843
15929
  } catch {
15844
15930
  return null;
15845
15931
  }
@@ -16051,7 +16137,7 @@ function mount15(ctx) {
16051
16137
  // src/server/routes/providers.ts
16052
16138
  import { spawn as spawn4 } from "child_process";
16053
16139
  import { existsSync as existsSync11, readFileSync as readFileSync16 } from "fs";
16054
- import { delimiter as delimiter2, join as join19 } from "path";
16140
+ import { delimiter as delimiter2, join as join20 } from "path";
16055
16141
  var ORDER = ["claude", "codex", "cline", "aider", "opencode"];
16056
16142
  var DEPRECATED = ["gemini"];
16057
16143
  var AUTH_ENV = {
@@ -16067,7 +16153,7 @@ function findOnPath(bin, pathVar) {
16067
16153
  for (const d of pathVar.split(delimiter2)) {
16068
16154
  if (!d)
16069
16155
  continue;
16070
- const p = join19(d, bin);
16156
+ const p = join20(d, bin);
16071
16157
  if (existsSync11(p))
16072
16158
  return p;
16073
16159
  }
@@ -16112,14 +16198,14 @@ function probeCli(bin, timeoutMs, pathVar = process.env.PATH ?? "") {
16112
16198
  });
16113
16199
  }
16114
16200
  function providersDir() {
16115
- const cands = [process.env.LOKI_PROVIDERS_DIR, join19(import.meta.dir, "../../../../../providers"), join19(import.meta.dir, "../../../providers")];
16116
- return cands.find((d) => !!d && existsSync11(join19(d, "model_catalog.json"))) ?? null;
16201
+ const cands = [process.env.LOKI_PROVIDERS_DIR, join20(import.meta.dir, "../../../../../providers"), join20(import.meta.dir, "../../../providers")];
16202
+ return cands.find((d) => !!d && existsSync11(join20(d, "model_catalog.json"))) ?? null;
16117
16203
  }
16118
16204
  function loadCatalog(dir) {
16119
16205
  if (!dir)
16120
16206
  return { providers: {}, updated: null };
16121
16207
  try {
16122
- const j = JSON.parse(readFileSync16(join19(dir, "model_catalog.json"), "utf8"));
16208
+ const j = JSON.parse(readFileSync16(join20(dir, "model_catalog.json"), "utf8"));
16123
16209
  return { providers: j.providers ?? {}, updated: typeof j.updated === "string" ? j.updated : null };
16124
16210
  } catch {
16125
16211
  return { providers: {}, updated: null };
@@ -16165,7 +16251,7 @@ function mount16(ctx) {
16165
16251
  function computeStats(db, since) {
16166
16252
  const all = db.select().from(runs).all().filter((r) => !since || r.startedAt !== null && r.startedAt >= since);
16167
16253
  const by = {};
16168
- let finished = 0, running = 0, blocked = 0, withVerdict = 0, verified = 0, unchecked = 0;
16254
+ let finished = 0, running = 0, blocked = 0, verified = 0, unchecked = 0, failed = 0, satisfied = 0, other = 0;
16169
16255
  let mUsd = 0, mRuns = 0, pUsd = 0, pRuns = 0;
16170
16256
  for (const r of all) {
16171
16257
  if (r.endedAt)
@@ -16176,11 +16262,16 @@ function computeStats(db, since) {
16176
16262
  if (verdict) {
16177
16263
  by[verdict] = (by[verdict] ?? 0) + 1;
16178
16264
  if (r.endedAt) {
16179
- withVerdict++;
16180
16265
  if (verdict === "VERIFIED")
16181
16266
  verified++;
16182
16267
  else if (verdict === "VERIFIED (signature not checked)")
16183
16268
  unchecked++;
16269
+ else if (verdict === "ALREADY_SATISFIED" || verdict === "ALREADY_SATISFIED (signature not checked)")
16270
+ satisfied++;
16271
+ else if (verdict === "FAILED" || verdict === "PARTIAL" || verdict === "TAMPERED")
16272
+ failed++;
16273
+ else
16274
+ other++;
16184
16275
  }
16185
16276
  }
16186
16277
  if (r.verdict === "SPEC_CONFLICT")
@@ -16210,9 +16301,11 @@ function computeStats(db, since) {
16210
16301
  runs_running: running,
16211
16302
  by_verdict: by,
16212
16303
  blocked_waiting: blocked,
16213
- verified_rate: withVerdict ? verified / withVerdict : null,
16304
+ verified_rate: verified + unchecked + failed ? verified / (verified + unchecked + failed) : null,
16305
+ breakdown: { verified, failed, already_satisfied: satisfied, other },
16214
16306
  verified_unchecked: unchecked,
16215
16307
  keys_configured: pubkeysFromEnv().configured === true,
16308
+ ...pubkeysFromEnv().reason ? { keys_reason: pubkeysFromEnv().reason } : {},
16216
16309
  cost: {
16217
16310
  measured_usd: mRuns ? r62(mUsd) : null,
16218
16311
  measured_runs: mRuns,
@@ -16355,13 +16448,13 @@ data: ${JSON.stringify({ source: k.slice(0, i), run: k.slice(i + 1) })}
16355
16448
  }
16356
16449
 
16357
16450
  // src/server/routes/verify.ts
16358
- import { readFileSync as readFileSync25, realpathSync as realpathSync11 } from "fs";
16451
+ import { readFileSync as readFileSync26, realpathSync as realpathSync13 } from "fs";
16359
16452
  import { createPublicKey as createPublicKey5 } from "crypto";
16360
- import { join as join28, sep as sep9 } from "path";
16453
+ import { join as join31, sep as sep11 } from "path";
16361
16454
 
16362
16455
  // ../../loki-ts/src/engine10/verify_cmd.ts
16363
- import { existsSync as existsSync17, readdirSync as readdirSync12, readFileSync as readFileSync24, statSync as statSync12 } from "fs";
16364
- import { dirname as dirname12, join as join27 } from "path";
16456
+ import { existsSync as existsSync19, readdirSync as readdirSync12, readFileSync as readFileSync25, statSync as statSync13 } from "fs";
16457
+ import { dirname as dirname12, join as join30 } from "path";
16365
16458
  import { createHash as createHash10, createPublicKey as createPublicKey4, verify as verify3 } from "crypto";
16366
16459
 
16367
16460
  // ../../loki-ts/src/util/paths.ts
@@ -16386,15 +16479,15 @@ var REPO_ROOT = findRepoRoot();
16386
16479
 
16387
16480
  // ../../loki-ts/src/engine10/stages/seal.ts
16388
16481
  import { createHash as createHash9, randomBytes as randomBytes3, createPrivateKey, createPublicKey as createPublicKey2, generateKeyPairSync, sign } from "crypto";
16389
- import { chmodSync as chmodSync4, existsSync as existsSync16, readdirSync as readdirSync11, linkSync, mkdirSync as mkdirSync8, readFileSync as readFileSync22, statSync as statSync11, unlinkSync as unlinkSync4, writeFileSync as writeFileSync9 } from "fs";
16390
- import { homedir as homedir5 } from "os";
16391
- import { basename as basename9, dirname as dirname11, join as join26 } from "path";
16482
+ import { chmodSync as chmodSync4, existsSync as existsSync18, readdirSync as readdirSync11, linkSync, mkdirSync as mkdirSync9, readFileSync as readFileSync23, statSync as statSync12, unlinkSync as unlinkSync4, writeFileSync as writeFileSync10 } from "fs";
16483
+ import { homedir as homedir6 } from "os";
16484
+ import { basename as basename9, dirname as dirname11, join as join29 } from "path";
16392
16485
 
16393
16486
  // ../../loki-ts/src/e10ext/assert_delta.ts
16394
16487
  import { execFileSync as execFileSync3 } from "child_process";
16395
16488
  import { mkdtempSync, readFileSync as readFileSync17, rmSync as rmSync3 } from "fs";
16396
- import { tmpdir } from "os";
16397
- import { join as join20 } from "path";
16489
+ import { tmpdir as tmpdir2 } from "os";
16490
+ import { join as join21 } from "path";
16398
16491
  import { basename as basename6 } from "path";
16399
16492
  var WEAKENED = { verdict: "weakened", changes: [] };
16400
16493
  var PY = String.raw`
@@ -16553,7 +16646,7 @@ function classifyAssertDelta(inp) {
16553
16646
  return WEAKENED;
16554
16647
  let cwd = "";
16555
16648
  try {
16556
- cwd = mkdtempSync(join20(tmpdir(), "loki-ad-"));
16649
+ cwd = mkdtempSync(join21(tmpdir2(), "loki-ad-"));
16557
16650
  const raw2 = execFileSync3("python3", ["-I", "-c", PY], {
16558
16651
  input: JSON.stringify({ path: inp.path, base: inp.base, head: inp.head, task: inp.task }),
16559
16652
  cwd,
@@ -16584,7 +16677,7 @@ function assertDeltaNotes(repoDir, baseSha, headRef, path, task, baseCounts, hea
16584
16677
  return null;
16585
16678
  try {
16586
16679
  const show = (ref) => execFileSync3("git", ["show", `${ref}:${path}`], { cwd: repoDir, encoding: "utf8", env: process.env, stdio: ["ignore", "pipe", "ignore"] });
16587
- const head = headRef ? show(headRef) : readFileSync17(join20(repoDir, path), "utf8");
16680
+ const head = headRef ? show(headRef) : readFileSync17(join21(repoDir, path), "utf8");
16588
16681
  const r = classifyTestEdit(path, show(baseSha), head, task, baseCounts, headCounts);
16589
16682
  return r[0]?.kind === "literal-only" ? r.map((e) => `assertion value changed (not shown to be required by the task): ${e.file}:${e.line} ${e.old} -> ${e.new}`) : null;
16590
16683
  } catch {
@@ -16601,15 +16694,15 @@ function classifyTestEdit(file, oldText, newText, taskText, baseCounts, headCoun
16601
16694
 
16602
16695
  // ../../loki-ts/src/e10ext/discard.ts
16603
16696
  import { execFileSync as execFileSync4 } from "child_process";
16604
- import { chmodSync as chmodSync3, closeSync as closeSync4, lstatSync as lstatSync3, mkdirSync as mkdirSync5, openSync as openSync4, readFileSync as readFileSync18, realpathSync as realpathSync9, rmdirSync, unlinkSync as unlinkSync3, writeFileSync as writeFileSync6 } from "fs";
16605
- import { dirname as dirname5, join as join21, relative, sep as sep6 } from "path";
16697
+ import { chmodSync as chmodSync3, closeSync as closeSync4, lstatSync as lstatSync3, mkdirSync as mkdirSync5, openSync as openSync4, readFileSync as readFileSync18, realpathSync as realpathSync10, rmdirSync, unlinkSync as unlinkSync3, writeFileSync as writeFileSync6 } from "fs";
16698
+ import { dirname as dirname5, join as join22, relative, sep as sep7 } from "path";
16606
16699
  function safeRestore(repoDir, f, want, mode) {
16607
- const root = realpathSync9(repoDir), fp = join21(root, f), rel = relative(root, dirname5(fp));
16700
+ const root = realpathSync10(repoDir), fp = join22(root, f), rel = relative(root, dirname5(fp));
16608
16701
  if (rel.startsWith(".."))
16609
16702
  throw new Error("outside repo");
16610
16703
  let cur = root;
16611
- for (const part of rel === "" ? [] : rel.split(sep6)) {
16612
- cur = join21(cur, part);
16704
+ for (const part of rel === "" ? [] : rel.split(sep7)) {
16705
+ cur = join22(cur, part);
16613
16706
  let st2;
16614
16707
  try {
16615
16708
  st2 = lstatSync3(cur);
@@ -16658,7 +16751,7 @@ async function discardIfSatisfied(git, base, o, staged, keep, repoDir) {
16658
16751
  return { status: "failed", data: {}, reason: "already-satisfied discard failed" };
16659
16752
  const notProven = [];
16660
16753
  for (const [f, v] of Object.entries(o.intake?.preexisting_untracked_blobs ?? {})) {
16661
- const fp = join21(repoDir, f);
16754
+ const fp = join22(repoDir, f);
16662
16755
  try {
16663
16756
  if (v.startsWith("!")) {
16664
16757
  const st = lstatSync3(fp);
@@ -16677,7 +16770,7 @@ async function discardIfSatisfied(git, base, o, staged, keep, repoDir) {
16677
16770
  try {
16678
16771
  let m = 420;
16679
16772
  try {
16680
- const l = lstatSync3(join21(repoDir, f));
16773
+ const l = lstatSync3(join22(repoDir, f));
16681
16774
  if (l.isFile())
16682
16775
  m = l.mode & 511;
16683
16776
  } catch {}
@@ -16717,7 +16810,7 @@ function dropSet(repoDir, staged, preexistingDirty, env = process.env) {
16717
16810
  import { basename as basename8, dirname as dirname7 } from "path";
16718
16811
 
16719
16812
  // ../../loki-ts/src/engine10/testmap.ts
16720
- import { basename as basename7, extname, join as join22, relative as relative2, sep as sep7 } from "path";
16813
+ import { basename as basename7, extname, join as join23, relative as relative2, sep as sep8 } from "path";
16721
16814
  var SKIP_DIRS = new Set([
16722
16815
  ".git",
16723
16816
  "node_modules",
@@ -16742,7 +16835,7 @@ function isTestFile(rel) {
16742
16835
  }
16743
16836
 
16744
16837
  // ../../loki-ts/src/features/speed/unit_mode.ts
16745
- import { closeSync as closeSync5, constants, fstatSync as fstatSync3, lstatSync as lstatSync4, openSync as openSync5, readSync as readSync3, realpathSync as realpathSync10 } from "fs";
16838
+ import { closeSync as closeSync5, constants, fstatSync as fstatSync3, lstatSync as lstatSync4, openSync as openSync5, readSync as readSync3, realpathSync as realpathSync11 } from "fs";
16746
16839
  var MAX_SPEC_BYTES = 1024 * 1024;
16747
16840
  var MAX_TOKEN_BUDGET = 1e9;
16748
16841
  var norm2 = (p) => p.replace(/^\.\//, "");
@@ -16911,10 +17004,257 @@ async function run(argv, opts = {}) {
16911
17004
 
16912
17005
  // ../../loki-ts/src/features/visual_evidence.ts
16913
17006
  import { spawn as spawn5 } from "child_process";
17007
+
17008
+ // ../../loki-ts/src/project_model/discover.ts
17009
+ import { existsSync as existsSync14, mkdirSync as mkdirSync6, readFileSync as readFileSync19, rmSync as rmSync4, writeFileSync as writeFileSync7 } from "fs";
17010
+ import { join as join25 } from "path";
17011
+
17012
+ // ../../loki-ts/src/engine10/types.ts
17013
+ var STAGE_BUDGETS = {
17014
+ intake: { targetS: 15, limitS: 60 },
17015
+ plan: { targetS: 45, limitS: 90 },
17016
+ wall: { targetS: 45, limitS: 90 },
17017
+ implement: { targetS: 180, limitS: 480 },
17018
+ verify: { targetS: 60, limitS: 120 },
17019
+ fix: { targetS: 90, limitS: 180 },
17020
+ commit: { targetS: 5, limitS: 30 },
17021
+ seal: { targetS: 15, limitS: 60 },
17022
+ pr: { targetS: 15, limitS: 60 },
17023
+ deep: { targetS: null, limitS: 2700 }
17024
+ };
17025
+
17026
+ // ../../loki-ts/src/project_model/schema.ts
17027
+ import { existsSync as existsSync13, lstatSync as lstatSync5, realpathSync as realpathSync12, statSync as statSync10 } from "fs";
17028
+ import { isAbsolute, join as join24, normalize, sep as sep9 } from "path";
17029
+ var PROJECT_MODEL_SCHEMA = "loki.v10.project/1";
17030
+ var COMMAND_KINDS = ["test", "lint", "build", "start"];
17031
+ var isRec = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
17032
+ var isStr = (v) => typeof v === "string" && v.trim() !== "";
17033
+ function safeRel(p) {
17034
+ const n2 = normalize(p.trim()).split(sep9).join("/").replace(/\/$/, "");
17035
+ if (n2 === "" || isAbsolute(n2) || n2 === ".." || n2.startsWith("../"))
17036
+ return null;
17037
+ return n2;
17038
+ }
17039
+ function insideRepo(repoDir, rel) {
17040
+ try {
17041
+ const real2 = realpathSync12(join24(repoDir, rel));
17042
+ const base = realpathSync12(repoDir);
17043
+ return real2 === base || real2.startsWith(base + sep9);
17044
+ } catch {
17045
+ return false;
17046
+ }
17047
+ }
17048
+ function checkCitation(repoDir, raw2) {
17049
+ if (!isStr(raw2))
17050
+ return "citation is not a non-empty string";
17051
+ const rel = safeRel(raw2.replace(/:\d+(?:-\d+)?$/, ""));
17052
+ if (rel === null)
17053
+ return `citation "${raw2}" is not a repo-relative path`;
17054
+ const abs = join24(repoDir, rel);
17055
+ if (!existsSync13(abs) || !statSync10(abs).isFile() || !insideRepo(repoDir, rel))
17056
+ return `citation "${raw2}" names a file that does not exist`;
17057
+ return null;
17058
+ }
17059
+ function checkFingerprint(repoDir, raw2) {
17060
+ if (!isStr(raw2))
17061
+ return "fingerprint entry is not a non-empty string";
17062
+ const rel = safeRel(raw2.replace(/:\d+(?:-\d+)?$/, ""));
17063
+ if (rel === null)
17064
+ return `fingerprint "${raw2}" is not a repo-relative path`;
17065
+ try {
17066
+ if (!lstatSync5(join24(repoDir, rel)).isFile() || !insideRepo(repoDir, rel))
17067
+ return `fingerprint "${raw2}" is not a regular file inside the repo`;
17068
+ } catch {
17069
+ return `fingerprint "${raw2}" names a file that does not exist`;
17070
+ }
17071
+ return null;
17072
+ }
17073
+ function checkCites(repoDir, v, where, errs) {
17074
+ if (!Array.isArray(v) || v.length === 0) {
17075
+ errs.push(`${where}: needs at least one citation (a repo-relative file path)`);
17076
+ return [];
17077
+ }
17078
+ const out2 = [];
17079
+ for (const c of v) {
17080
+ const e = checkCitation(repoDir, c);
17081
+ if (e)
17082
+ errs.push(`${where}: ${e}`);
17083
+ else
17084
+ out2.push(String(c));
17085
+ }
17086
+ return out2;
17087
+ }
17088
+ function checkDir(repoDir, v, where, errs) {
17089
+ const rel = isStr(v) ? safeRel(v) : null;
17090
+ if (rel === null) {
17091
+ errs.push(`${where}: must be a repo-relative directory ("." for the repo root)`);
17092
+ return ".";
17093
+ }
17094
+ const abs = join24(repoDir, rel);
17095
+ if (!existsSync13(abs) || !statSync10(abs).isDirectory() || !insideRepo(repoDir, rel))
17096
+ errs.push(`${where}: directory "${rel}" does not exist`);
17097
+ return rel;
17098
+ }
17099
+ function checkCommand(repoDir, v, where, errs) {
17100
+ if (v === null || v === undefined)
17101
+ return null;
17102
+ if (!isRec(v) || !isStr(v.cmd)) {
17103
+ errs.push(`${where}: must be null or {cmd, cwd, cite}`);
17104
+ return null;
17105
+ }
17106
+ const cwd = checkDir(repoDir, v.cwd, `${where}.cwd`, errs);
17107
+ return { cmd: v.cmd.trim(), cwd, cite: checkCites(repoDir, v.cite, `${where}.cite`, errs) };
17108
+ }
17109
+ function validateAnswer(repoDir, raw2) {
17110
+ const errs = [];
17111
+ if (!isRec(raw2))
17112
+ return { ok: false, errors: ["answer must be a JSON object"] };
17113
+ if (!isStr(raw2.workspaceKind))
17114
+ errs.push("workspaceKind: required non-empty string");
17115
+ const workspaceCite = checkCites(repoDir, raw2.workspaceCite, "workspaceCite", errs);
17116
+ if (!Array.isArray(raw2.packages))
17117
+ errs.push("packages: required array");
17118
+ const packages = [];
17119
+ for (const [i, p] of (Array.isArray(raw2.packages) ? raw2.packages : []).entries()) {
17120
+ const w = `packages[${i}]`;
17121
+ if (!isRec(p) || !isStr(p.name)) {
17122
+ errs.push(`${w}: must be an object with a name`);
17123
+ continue;
17124
+ }
17125
+ const root = checkDir(repoDir, p.root, `${w}.root`, errs);
17126
+ const cmds = isRec(p.commands) ? p.commands : {};
17127
+ if (!isRec(p.commands))
17128
+ errs.push(`${w}.commands: required object with keys ${COMMAND_KINDS.join(", ")} (each null or a command)`);
17129
+ const commands = {};
17130
+ for (const k of COMMAND_KINDS)
17131
+ commands[k] = checkCommand(repoDir, cmds[k], `${w}.commands.${k}`, errs);
17132
+ const ui = isRec(p.ui) ? p.ui : {};
17133
+ if (!isRec(p.ui) || typeof ui.present !== "boolean")
17134
+ errs.push(`${w}.ui: required {present: boolean, boot: command|null, cite}`);
17135
+ packages.push({
17136
+ name: p.name.trim(),
17137
+ root,
17138
+ runner: isStr(p.runner) ? p.runner.trim() : null,
17139
+ commands,
17140
+ ui: { present: ui.present === true, boot: checkCommand(repoDir, ui.boot, `${w}.ui.boot`, errs), cite: checkCites(repoDir, ui.cite, `${w}.ui.cite`, errs) },
17141
+ cite: checkCites(repoDir, p.cite, `${w}.cite`, errs)
17142
+ });
17143
+ }
17144
+ if (!Array.isArray(raw2.fingerprintFiles))
17145
+ errs.push("fingerprintFiles: required array of the manifest and lockfile paths");
17146
+ const fingerprintFiles = (Array.isArray(raw2.fingerprintFiles) ? raw2.fingerprintFiles : []).filter((f) => {
17147
+ const e = checkFingerprint(repoDir, f);
17148
+ if (e)
17149
+ errs.push(`fingerprintFiles: ${e}`);
17150
+ return e === null;
17151
+ }).map((f) => String(f).replace(/:\d+(?:-\d+)?$/, ""));
17152
+ if (errs.length > 0)
17153
+ return { ok: false, errors: errs };
17154
+ return { ok: true, model: { schema: PROJECT_MODEL_SCHEMA, status: "ok", workspaceKind: String(raw2.workspaceKind).trim(), workspaceCite, packages, fingerprintFiles } };
17155
+ }
17156
+ function unknownModel(key, reason, expiresAt) {
17157
+ return { schema: PROJECT_MODEL_SCHEMA, status: "unknown", key, workspaceKind: "unknown", workspaceCite: [], packages: [], fingerprintFiles: [], reason, ...expiresAt === undefined ? {} : { expiresAt } };
17158
+ }
17159
+ function parseCached(repoDir, raw2, now) {
17160
+ if (!isRec(raw2) || raw2.schema !== PROJECT_MODEL_SCHEMA || typeof raw2.key !== "string")
17161
+ return null;
17162
+ if (raw2.status === "unknown")
17163
+ return typeof raw2.expiresAt === "number" && raw2.expiresAt > now ? unknownModel(raw2.key, typeof raw2.reason === "string" ? raw2.reason : "cached unknown", raw2.expiresAt) : null;
17164
+ if (raw2.status !== "ok")
17165
+ return null;
17166
+ const v = validateAnswer(repoDir, raw2);
17167
+ return v.ok ? { ...v.model, key: raw2.key } : null;
17168
+ }
17169
+
17170
+ // ../../loki-ts/src/project_model/gather.ts
17171
+ var GATHER_CAPS = {
17172
+ maxListFiles: 20000,
17173
+ maxDepth: 3,
17174
+ maxFileBytes: 16 * 1024,
17175
+ maxFiles: 60,
17176
+ maxTotalBytes: 96 * 1024,
17177
+ maxTreeLines: 300,
17178
+ maxHashBytes: 16 * 1024 * 1024
17179
+ };
17180
+
17181
+ // ../../loki-ts/src/project_model/discover.ts
17182
+ function projectModelEnabled(env = process.env) {
17183
+ return env["LOKI_E10_PROJECT_MODEL"] !== "0";
17184
+ }
17185
+ var PROJECT_FILE = ".loki/project.json";
17186
+ var UNKNOWN_TTL_MS = 60 * 60 * 1000;
17187
+ function loadCached(repoDir, now = Date.now()) {
17188
+ try {
17189
+ return parseCached(repoDir, JSON.parse(readFileSync19(join25(repoDir, PROJECT_FILE), "utf8")), now);
17190
+ } catch {
17191
+ return null;
17192
+ }
17193
+ }
17194
+
17195
+ // ../../loki-ts/src/project_model/api.ts
17196
+ import { posix } from "path";
17197
+ var clean = (p) => typeof p === "string" ? posix.normalize(p.replace(/\\/g, "/")).replace(/^\.\//, "") : "";
17198
+ var isRec2 = (v) => typeof v === "object" && v !== null;
17199
+ var wellFormed = (p) => isRec2(p) && typeof p.root === "string" && typeof p.name === "string";
17200
+ function projectApi(model) {
17201
+ const known = isRec2(model) && model.status === "ok";
17202
+ const packages = known && Array.isArray(model.packages) ? model.packages.filter(wellFormed) : [];
17203
+ const packageOf = (file) => {
17204
+ const f = clean(file);
17205
+ if (f === "")
17206
+ return null;
17207
+ let best = null;
17208
+ for (const p of packages) {
17209
+ const hit = p.root === "." || f === p.root || f.startsWith(`${p.root}/`);
17210
+ if (hit && (best === null || p.root !== "." && (best.root === "." || p.root.length > best.root.length)))
17211
+ best = p;
17212
+ }
17213
+ return best;
17214
+ };
17215
+ return {
17216
+ model,
17217
+ known: () => known,
17218
+ packages: () => packages,
17219
+ workspaceKind: () => isRec2(model) && typeof model.workspaceKind === "string" ? model.workspaceKind : "unknown",
17220
+ packageRootOf: (file) => packageOf(file)?.root ?? null,
17221
+ packageOf,
17222
+ relativeToRoot: (file) => {
17223
+ const p = packageOf(file);
17224
+ return p ? p.root === "." ? clean(file) : clean(file).slice(p.root.length + 1) : null;
17225
+ },
17226
+ commandFor: (pkg, kind) => (packages.find((p) => p.root === clean(pkg) || p.name === pkg) ?? null)?.commands?.[kind] ?? null,
17227
+ hasUI: () => packages.some((p) => isRec2(p.ui) && p.ui.present === true),
17228
+ uiBoot: () => {
17229
+ for (const p of packages)
17230
+ if (isRec2(p.ui) && p.ui.present === true && isRec2(p.ui.boot))
17231
+ return { pkg: p, boot: p.ui.boot };
17232
+ return null;
17233
+ }
17234
+ };
17235
+ }
17236
+
17237
+ // ../../loki-ts/src/project_model/resolve.ts
17238
+ function loadProjectApi(repoDir, env = process.env) {
17239
+ if (!projectModelEnabled(env))
17240
+ return null;
17241
+ try {
17242
+ const model = loadCached(repoDir);
17243
+ const api = model ? projectApi(model) : null;
17244
+ return api && api.known() ? api : null;
17245
+ } catch {
17246
+ return null;
17247
+ }
17248
+ }
17249
+ function isMultiRoot(api) {
17250
+ return !!api && api.known() && api.packages().some((p) => p.root !== ".");
17251
+ }
17252
+
17253
+ // ../../loki-ts/src/features/visual_evidence.ts
16914
17254
  import { createHash as createHash6, randomBytes as randomBytes2 } from "crypto";
16915
- import { existsSync as existsSync13, lstatSync as lstatSync5, mkdirSync as mkdirSync6, readdirSync as readdirSync9, readFileSync as readFileSync19, writeFileSync as writeFileSync7 } from "fs";
17255
+ import { existsSync as existsSync15, lstatSync as lstatSync6, mkdirSync as mkdirSync7, readdirSync as readdirSync9, readFileSync as readFileSync20, writeFileSync as writeFileSync8 } from "fs";
16916
17256
  import { createServer } from "net";
16917
- import { dirname as dirname8, isAbsolute, join as join23, normalize, relative as relative3, sep as sep8 } from "path";
17257
+ import { dirname as dirname8, isAbsolute as isAbsolute2, join as join26, normalize as normalize2, relative as relative3, sep as sep10 } from "path";
16918
17258
  var visualEvidenceEnabled = (env = process.env) => env["LOKI_VISUAL_EVIDENCE"] !== "0";
16919
17259
  var PAGE_RE = /^(?:.*\/)?(?:app|pages|src|public)\/.*\.(?:html|jsx|tsx|vue|svelte)$/;
16920
17260
  var isPageFile = (p) => PAGE_RE.test(p);
@@ -16924,12 +17264,12 @@ function routeFor(file) {
16924
17264
  return `/${r}`.replace(/\/+/g, "/");
16925
17265
  }
16926
17266
  var screenName = (route) => (route === "/" ? "index" : route.slice(1).replace(/[^A-Za-z0-9._-]+/g, "_")) || "index";
16927
- var sha256File = (p) => createHash6("sha256").update(readFileSync19(p)).digest("hex");
17267
+ var sha256File = (p) => createHash6("sha256").update(readFileSync20(p)).digest("hex");
16928
17268
  function hashScreens(runDir, rels) {
16929
17269
  const out2 = [];
16930
17270
  for (const rel of rels) {
16931
17271
  try {
16932
- out2.push({ path: rel, sha256: sha256File(join23(runDir, rel)) });
17272
+ out2.push({ path: rel, sha256: sha256File(join26(runDir, rel)) });
16933
17273
  } catch {}
16934
17274
  }
16935
17275
  return out2;
@@ -16940,16 +17280,16 @@ function checkScreens(runDir, screens) {
16940
17280
  for (const s of screens) {
16941
17281
  if (typeof s?.path !== "string" || typeof s.sha256 !== "string")
16942
17282
  return "evidence_screens entry is malformed";
16943
- if (isAbsolute(s.path) || normalize(s.path).startsWith(".."))
17283
+ if (isAbsolute2(s.path) || normalize2(s.path).startsWith(".."))
16944
17284
  return `evidence screenshot path escapes the evidence dir: ${s.path}`;
16945
- const file = join23(runDir, s.path);
17285
+ const file = join26(runDir, s.path);
16946
17286
  let cur = runDir;
16947
- for (const seg of normalize(s.path).split(sep8)) {
16948
- cur = join23(cur, seg);
17287
+ for (const seg of normalize2(s.path).split(sep10)) {
17288
+ cur = join26(cur, seg);
16949
17289
  if (isSymlink(cur))
16950
17290
  return `evidence screenshot path is a symlink: ${s.path}`;
16951
17291
  }
16952
- if (!existsSync13(file))
17292
+ if (!existsSync15(file))
16953
17293
  return `evidence screenshot is missing: ${s.path}`;
16954
17294
  if (!isRegularFile(file))
16955
17295
  return `evidence screenshot is not a regular file: ${s.path}`;
@@ -16991,16 +17331,16 @@ async function waitUp(url, ms, signal) {
16991
17331
  return false;
16992
17332
  }
16993
17333
  var playwrightBin = (repoDir) => {
16994
- const bin = join23(repoDir, "node_modules", ".bin", "playwright");
16995
- return existsSync13(bin) ? bin : null;
17334
+ const bin = join26(repoDir, "node_modules", ".bin", "playwright");
17335
+ return existsSync15(bin) ? bin : null;
16996
17336
  };
16997
17337
  function openapiPaths(repoDir) {
16998
17338
  for (const f of ["openapi.json", "openapi.yaml", "openapi.yml", "swagger.json"]) {
16999
- const p = join23(repoDir, f);
17000
- if (!existsSync13(p))
17339
+ const p = join26(repoDir, f);
17340
+ if (!existsSync15(p))
17001
17341
  continue;
17002
17342
  try {
17003
- const txt = readFileSync19(p, "utf8");
17343
+ const txt = readFileSync20(p, "utf8");
17004
17344
  const paths = f.endsWith(".json") ? Object.keys(JSON.parse(txt).paths ?? {}) : [...txt.matchAll(/^ {2}(\/[^\s:]*):\s*$/gm)].map((m) => m[1]);
17005
17345
  return paths.filter((x) => !x.includes("{")).slice(0, 10);
17006
17346
  } catch {
@@ -17010,8 +17350,8 @@ function openapiPaths(repoDir) {
17010
17350
  return [];
17011
17351
  }
17012
17352
  var playwrightTestPkg = (repoDir) => {
17013
- const d = join23(repoDir, "node_modules", "@playwright", "test");
17014
- return existsSync13(join23(d, "package.json")) ? d : null;
17353
+ const d = join26(repoDir, "node_modules", "@playwright", "test");
17354
+ return existsSync15(join26(d, "package.json")) ? d : null;
17015
17355
  };
17016
17356
  var e2eSpecSource = (pkgDir, base, routes) => `const { test } = require(${JSON.stringify(pkgDir)});
17017
17357
  test("loki e2e walkthrough", async ({ page }) => {
@@ -17023,24 +17363,24 @@ async function recordE2eMedia(repoDir, runDir, relDir, base, routes, ms, signal)
17023
17363
  if (!pw || !pkg)
17024
17364
  return { rels: [], skipped: "playwright e2e video and trace skipped: @playwright/test is not installed in the repo" };
17025
17365
  try {
17026
- const dir = join23(runDir, relDir, "e2e");
17027
- mkdirSync6(dir, { recursive: true });
17028
- writeFileSync7(join23(dir, "walk.spec.cjs"), e2eSpecSource(pkg, base, routes));
17029
- writeFileSync7(join23(dir, "playwright.config.cjs"), `module.exports = { testDir: ".", testMatch: "walk.spec.cjs", outputDir: "./out", reporter: "null", use: { video: "on", trace: "on" } };
17366
+ const dir = join26(runDir, relDir, "e2e");
17367
+ mkdirSync7(dir, { recursive: true });
17368
+ writeFileSync8(join26(dir, "walk.spec.cjs"), e2eSpecSource(pkg, base, routes));
17369
+ writeFileSync8(join26(dir, "playwright.config.cjs"), `module.exports = { testDir: ".", testMatch: "walk.spec.cjs", outputDir: "./out", reporter: "null", use: { video: "on", trace: "on" } };
17030
17370
  `);
17031
- if (!await shoot(pw, ["test", "--config", join23(dir, "playwright.config.cjs")], repoDir, ms, signal))
17371
+ if (!await shoot(pw, ["test", "--config", join26(dir, "playwright.config.cjs")], repoDir, ms, signal))
17032
17372
  return { rels: [], skipped: "playwright e2e video and trace skipped: the run failed or timed out" };
17033
17373
  const rels = [];
17034
17374
  const walk = (d) => {
17035
17375
  for (const e of readdirSync9(d, { withFileTypes: true })) {
17036
- const full = join23(d, e.name);
17376
+ const full = join26(d, e.name);
17037
17377
  if (e.isDirectory())
17038
17378
  walk(full);
17039
17379
  else if (e.isFile() && /\.(webm|zip)$/.test(e.name))
17040
- rels.push(join23(relDir, "e2e", relative3(dir, full)));
17380
+ rels.push(join26(relDir, "e2e", relative3(dir, full)));
17041
17381
  }
17042
17382
  };
17043
- walk(join23(dir, "out"));
17383
+ walk(join26(dir, "out"));
17044
17384
  return rels.length > 0 ? { rels, skipped: null } : { rels, skipped: "playwright e2e video and trace skipped: no video or trace was produced" };
17045
17385
  } catch (e) {
17046
17386
  return { rels: [], skipped: `playwright e2e video and trace skipped: ${String(e?.message ?? e).slice(0, 120)}` };
@@ -17078,14 +17418,14 @@ function shoot(pw, args, cwd, ms, signal) {
17078
17418
  }
17079
17419
  var isSymlink = (p) => {
17080
17420
  try {
17081
- return lstatSync5(p).isSymbolicLink();
17421
+ return lstatSync6(p).isSymbolicLink();
17082
17422
  } catch {
17083
17423
  return false;
17084
17424
  }
17085
17425
  };
17086
17426
  var isRegularFile = (p) => {
17087
17427
  try {
17088
- return lstatSync5(p).isFile();
17428
+ return lstatSync6(p).isFile();
17089
17429
  } catch {
17090
17430
  return false;
17091
17431
  }
@@ -17133,24 +17473,30 @@ async function captureVisualEvidence(repoDir, runDir, changed, opts = {}) {
17133
17473
  try {
17134
17474
  if (!visualEvidenceEnabled(opts.env))
17135
17475
  return skip("LOKI_VISUAL_EVIDENCE is 0");
17136
- const pkgPath = join23(repoDir, "package.json");
17137
- if (!existsSync13(pkgPath))
17138
- return skip("no package.json");
17139
- const script = pickScript(JSON.parse(readFileSync19(pkgPath, "utf8")));
17140
- if (!script)
17141
- return skip("no dev, preview or start script");
17476
+ const api = loadProjectApi(repoDir), boot = isMultiRoot(api) ? api.uiBoot() : null;
17477
+ const appDir = boot ? join26(repoDir, boot.boot.cwd) : repoDir;
17478
+ const pkgPath = join26(appDir, "package.json");
17479
+ let script = null;
17480
+ if (!boot) {
17481
+ if (!existsSync15(pkgPath))
17482
+ return skip("no package.json");
17483
+ script = pickScript(JSON.parse(readFileSync20(pkgPath, "utf8")));
17484
+ if (!script)
17485
+ return skip("no dev, preview or start script");
17486
+ }
17142
17487
  const pages = changed.filter(isPageFile);
17143
17488
  const explicitOn = (opts.env ?? process.env)["LOKI_VISUAL_EVIDENCE"] === "1";
17144
17489
  const apiPaths = pages.length === 0 && explicitOn ? openapiPaths(repoDir) : [];
17145
17490
  if (pages.length === 0 && apiPaths.length === 0)
17146
17491
  return skip(explicitOn ? "no changed page files and no openapi routes" : "no changed page files (set LOKI_VISUAL_EVIDENCE=1 for the API transcript)");
17147
- const pw = pages.length > 0 ? playwrightBin(repoDir) : null;
17492
+ const pw = pages.length > 0 ? playwrightBin(appDir) ?? playwrightBin(repoDir) : null;
17148
17493
  if (pages.length > 0 && !pw)
17149
17494
  return skip("playwright not resolvable in the repo");
17150
17495
  const budget = opts.budgetMs ?? DEFAULT_BUDGET_MS, end = Date.now() + budget, left = () => end - Date.now();
17151
17496
  const aborted = () => signal?.aborted === true;
17152
17497
  const port = await freePort(), base = `http://127.0.0.1:${port}`;
17153
- child = spawn5("npm", ["run", script, "--silent"], { cwd: repoDir, detached: true, env: { ...opts.env ?? process.env, PORT: String(port), HOST: "127.0.0.1", BROWSER: "none" }, stdio: "ignore" });
17498
+ const [bc, ba] = boot ? ["bash", ["-c", boot.boot.cmd]] : ["npm", ["run", script, "--silent"]];
17499
+ child = spawn5(bc, [...ba], { cwd: appDir, detached: true, env: { ...opts.env ?? process.env, PORT: String(port), HOST: "127.0.0.1", BROWSER: "none" }, stdio: "ignore" });
17154
17500
  child.on("error", () => {
17155
17501
  return;
17156
17502
  });
@@ -17161,26 +17507,26 @@ async function captureVisualEvidence(repoDir, runDir, changed, opts = {}) {
17161
17507
  }
17162
17508
  if (!await waitUp(base, left(), signal))
17163
17509
  return skip(aborted() ? "aborted" : `${script} server did not answer within ${Math.round(budget / 1000)}s`);
17164
- const evRoot = join23(runDir, "evidence");
17510
+ const evRoot = join26(runDir, "evidence");
17165
17511
  if (isSymlink(evRoot))
17166
17512
  return skip("evidence dir is a symlink");
17167
- mkdirSync6(evRoot, { recursive: true });
17513
+ mkdirSync7(evRoot, { recursive: true });
17168
17514
  if (pages.length > 0) {
17169
- const rel0 = join23("evidence", randomBytes2(6).toString("hex"));
17170
- mkdirSync6(join23(runDir, rel0));
17515
+ const rel0 = join26("evidence", randomBytes2(6).toString("hex"));
17516
+ mkdirSync7(join26(runDir, rel0));
17171
17517
  const rels = [];
17172
17518
  for (const route of [...new Set(pages.map(routeFor))].slice(0, opts.maxRoutes ?? MAX_ROUTES)) {
17173
17519
  if (aborted() || left() <= 0)
17174
17520
  break;
17175
- const rel = join23(rel0, `${screenName(route)}.png`);
17176
- const ok = await shoot(pw, ["screenshot", `${base}${route}`, join23(runDir, rel)], repoDir, left(), signal);
17177
- if (ok && !aborted() && !isSymlink(join23(runDir, rel)) && existsSync13(join23(runDir, rel)))
17521
+ const rel = join26(rel0, `${screenName(route)}.png`);
17522
+ const ok = await shoot(pw, ["screenshot", `${base}${route}`, join26(runDir, rel)], appDir, left(), signal);
17523
+ if (ok && !aborted() && !isSymlink(join26(runDir, rel)) && existsSync15(join26(runDir, rel)))
17178
17524
  rels.push(rel);
17179
17525
  }
17180
17526
  if (aborted())
17181
17527
  return skip("aborted");
17182
17528
  const routes = [...new Set(pages.map(routeFor))].slice(0, opts.maxRoutes ?? MAX_ROUTES);
17183
- const media = rels.length > 0 && left() > 0 ? await recordE2eMedia(repoDir, runDir, rel0, base, routes, left(), signal) : { rels: [], skipped: null };
17529
+ const media = rels.length > 0 && left() > 0 ? await recordE2eMedia(appDir, runDir, rel0, base, routes, left(), signal) : { rels: [], skipped: null };
17184
17530
  if (aborted())
17185
17531
  return skip("aborted");
17186
17532
  const screens = hashScreens(runDir, [...rels, ...media.rels]);
@@ -17199,9 +17545,9 @@ async function captureVisualEvidence(repoDir, runDir, changed, opts = {}) {
17199
17545
  }
17200
17546
  if (aborted())
17201
17547
  return skip("aborted");
17202
- const hdir = join23(evRoot, randomBytes2(6).toString("hex"));
17203
- mkdirSync6(hdir);
17204
- writeFileSync7(join23(hdir, "http.json"), JSON.stringify(rows, null, 2) + `
17548
+ const hdir = join26(evRoot, randomBytes2(6).toString("hex"));
17549
+ mkdirSync7(hdir);
17550
+ writeFileSync8(join26(hdir, "http.json"), JSON.stringify(rows, null, 2) + `
17205
17551
  `);
17206
17552
  return { screens: [], http: true, skipped: null };
17207
17553
  } catch (e) {
@@ -17227,23 +17573,9 @@ function receiptScreensProblem(receiptPath, receipt) {
17227
17573
  return receipt["evidence_screens"] === undefined ? null : checkScreens(dirname8(receiptPath), receipt["evidence_screens"]);
17228
17574
  }
17229
17575
 
17230
- // ../../loki-ts/src/engine10/types.ts
17231
- var STAGE_BUDGETS = {
17232
- intake: { targetS: 15, limitS: 60 },
17233
- plan: { targetS: 45, limitS: 90 },
17234
- wall: { targetS: 45, limitS: 90 },
17235
- implement: { targetS: 180, limitS: 480 },
17236
- verify: { targetS: 60, limitS: 120 },
17237
- fix: { targetS: 90, limitS: 180 },
17238
- commit: { targetS: 5, limitS: 30 },
17239
- seal: { targetS: 15, limitS: 60 },
17240
- pr: { targetS: 15, limitS: 60 },
17241
- deep: { targetS: null, limitS: 2700 }
17242
- };
17243
-
17244
17576
  // ../../loki-ts/src/features/contract.ts
17245
- import { existsSync as existsSync14, lstatSync as lstatSync6, mkdirSync as mkdirSync7, readFileSync as readFileSync20, writeFileSync as writeFileSync8 } from "fs";
17246
- import { dirname as dirname9, join as join24 } from "path";
17577
+ import { existsSync as existsSync16, lstatSync as lstatSync7, mkdirSync as mkdirSync8, readFileSync as readFileSync21, writeFileSync as writeFileSync9 } from "fs";
17578
+ import { dirname as dirname9, join as join27 } from "path";
17247
17579
  import { createHash as createHash7 } from "crypto";
17248
17580
  var MAX_CRITERIA = 50;
17249
17581
  var MAX_TEXT = 500;
@@ -17278,14 +17610,14 @@ function untracedLines(trace) {
17278
17610
  return trace.criteria.filter((c) => c.status === "no_match").map((c) => `contract ${sanitizeCriterion(c.id)} untraced (no keyword match in changed files): ${sanitizeCriterion(c.text)}`);
17279
17611
  }
17280
17612
  function contractPath(repoDir) {
17281
- return join24(repoDir, ".loki", "contract.json");
17613
+ return join27(repoDir, ".loki", "contract.json");
17282
17614
  }
17283
17615
  var MAX_CONTRACT_BYTES = 1024 * 1024;
17284
17616
  function readContract(repoDir) {
17285
17617
  const p = contractPath(repoDir);
17286
17618
  let st;
17287
17619
  try {
17288
- st = lstatSync6(p);
17620
+ st = lstatSync7(p);
17289
17621
  } catch {
17290
17622
  return { contract: null, notes: [], sha256: null };
17291
17623
  }
@@ -17295,7 +17627,7 @@ function readContract(repoDir) {
17295
17627
  return { contract: null, notes: ["contract unreadable: too large"], sha256: null };
17296
17628
  let raw2;
17297
17629
  try {
17298
- raw2 = readFileSync20(p);
17630
+ raw2 = readFileSync21(p);
17299
17631
  } catch {
17300
17632
  return { contract: null, notes: ["contract unreadable: read failed"], sha256: null };
17301
17633
  }
@@ -17330,7 +17662,7 @@ function sealContract(repoDir, body, rawDiff, checks, env = process.env, snap) {
17330
17662
  const live = readContract(repoDir);
17331
17663
  let exists2 = true;
17332
17664
  try {
17333
- lstatSync6(contractPath(repoDir));
17665
+ lstatSync7(contractPath(repoDir));
17334
17666
  } catch {
17335
17667
  exists2 = false;
17336
17668
  }
@@ -17359,18 +17691,18 @@ function sealContract(repoDir, body, rawDiff, checks, env = process.env, snap) {
17359
17691
 
17360
17692
  // ../../loki-ts/src/features/speed/seal_group.ts
17361
17693
  import { createHash as createHash8 } from "crypto";
17362
- import { existsSync as existsSync15, readdirSync as readdirSync10, readFileSync as readFileSync21, statSync as statSync10 } from "fs";
17363
- import { dirname as dirname10, join as join25 } from "path";
17694
+ import { existsSync as existsSync17, readdirSync as readdirSync10, readFileSync as readFileSync22, statSync as statSync11 } from "fs";
17695
+ import { dirname as dirname10, join as join28 } from "path";
17364
17696
  var ID_RE = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;
17365
17697
  var PASSING = new Set(["VERIFIED", "ALREADY_SATISFIED"]);
17366
17698
  var sha2 = (b) => createHash8("sha256").update(b).digest("hex");
17367
17699
  var isObj9 = (v) => v !== null && typeof v === "object" && !Array.isArray(v);
17368
17700
  var text2 = (v, cap) => typeof v === "string" ? v.replace(/[\x00-\x1f\x7f]+/g, " ").trim().slice(0, cap) : "";
17369
17701
  var num2 = (v) => typeof v === "number" && Number.isFinite(v) && v >= 0 ? v : 0;
17370
- var unitsDir = (runDir) => join25(runDir, "group", "units");
17702
+ var unitsDir = (runDir) => join28(runDir, "group", "units");
17371
17703
  var readJson3 = (p) => {
17372
17704
  try {
17373
- return JSON.parse(readFileSync21(p, "utf8"));
17705
+ return JSON.parse(readFileSync22(p, "utf8"));
17374
17706
  } catch {
17375
17707
  return;
17376
17708
  }
@@ -17383,8 +17715,8 @@ var dirNames = (d) => {
17383
17715
  }
17384
17716
  };
17385
17717
  function sealGroup(runDir, rh) {
17386
- const manifestPath = join25(runDir, "group", "manifest.json");
17387
- if (!existsSync15(manifestPath))
17718
+ const manifestPath = join28(runDir, "group", "manifest.json");
17719
+ if (!existsSync17(manifestPath))
17388
17720
  return { section: undefined, notProven: [], problems: 0, allUnitsPass: true };
17389
17721
  const notProven = [];
17390
17722
  let problems = 0;
@@ -17408,10 +17740,10 @@ function sealGroup(runDir, rh) {
17408
17740
  return fail(`unit ${index2} has an invalid, duplicate or case-colliding id`);
17409
17741
  seen.add(id);
17410
17742
  seenLower.add(id.toLowerCase());
17411
- const dir = join25(unitsDir(runDir), id), rp = join25(dir, "receipt.json"), ep = join25(dir, "events.jsonl");
17743
+ const dir = join28(unitsDir(runDir), id), rp = join28(dir, "receipt.json"), ep = join28(dir, "events.jsonl");
17412
17744
  let bytes;
17413
17745
  try {
17414
- bytes = readFileSync21(rp);
17746
+ bytes = readFileSync22(rp);
17415
17747
  } catch {
17416
17748
  return fail(`unit ${id} sub-receipt missing`);
17417
17749
  }
@@ -17428,7 +17760,7 @@ function sealGroup(runDir, rh) {
17428
17760
  unit_id: id,
17429
17761
  sub_receipt_sha256: sha2(bytes),
17430
17762
  receipt_sha256: sub["receipt_sha256"],
17431
- events_file_sha256: existsSync15(ep) ? sha2(readFileSync21(ep)) : null,
17763
+ events_file_sha256: existsSync17(ep) ? sha2(readFileSync22(ep)) : null,
17432
17764
  verdict,
17433
17765
  goal: text2(u["goal"], 200),
17434
17766
  files: (Array.isArray(u["files"]) ? u["files"] : []).map((f) => text2(f, 200)).filter(Boolean).slice(0, 50),
@@ -17453,7 +17785,7 @@ async function verifyGroup(receiptPath, receipt, rh, verifyOne, deps = {}) {
17453
17785
  const bad = (reason) => ({ verdict: "TAMPERED", reason: `group: ${reason}` });
17454
17786
  const g = receipt["group"];
17455
17787
  if (g === undefined)
17456
- return existsSync15(join25(runDir, "group", "manifest.json")) ? bad("group directory present but the receipt has no group section") : null;
17788
+ return existsSync17(join28(runDir, "group", "manifest.json")) ? bad("group directory present but the receipt has no group section") : null;
17457
17789
  if (!isObj9(g) || !ID_RE.test(String(g["group_id"] ?? "")) || !Array.isArray(g["units"]) || g["units"].length === 0)
17458
17790
  return bad("group section malformed");
17459
17791
  const entries = g["units"];
@@ -17465,20 +17797,20 @@ async function verifyGroup(receiptPath, receipt, rh, verifyOne, deps = {}) {
17465
17797
  return bad("unit directories do not match the receipt (missing or extra unit)");
17466
17798
  let unchecked = null;
17467
17799
  for (let i = 0;i < entries.length; i++) {
17468
- const e = entries[i], id = ids[i], dir = join25(unitsDir(runDir), id), rp = join25(dir, "receipt.json"), ep = join25(dir, "events.jsonl");
17800
+ const e = entries[i], id = ids[i], dir = join28(unitsDir(runDir), id), rp = join28(dir, "receipt.json"), ep = join28(dir, "events.jsonl");
17469
17801
  if (e["index"] !== i)
17470
17802
  return bad(`unit ${id} is out of order (index ${String(e["index"])} at position ${i})`);
17471
17803
  let bytes;
17472
17804
  try {
17473
- if (!statSync10(dir).isDirectory())
17805
+ if (!statSync11(dir).isDirectory())
17474
17806
  return bad(`unit ${id} is not a directory`);
17475
- bytes = readFileSync21(rp);
17807
+ bytes = readFileSync22(rp);
17476
17808
  } catch {
17477
17809
  return bad(`unit ${id} sub-receipt missing`);
17478
17810
  }
17479
17811
  if (sha2(bytes) !== e["sub_receipt_sha256"])
17480
17812
  return bad(`unit ${id} sub-receipt does not match its recorded hash`);
17481
- const evSha = existsSync15(ep) ? sha2(readFileSync21(ep)) : null;
17813
+ const evSha = existsSync17(ep) ? sha2(readFileSync22(ep)) : null;
17482
17814
  if (evSha !== (e["events_file_sha256"] ?? null))
17483
17815
  return bad(`unit ${id} events.jsonl does not match its recorded hash`);
17484
17816
  const sub = readJson3(rp);
@@ -17533,21 +17865,21 @@ function loadSigningKey(generate = true) {
17533
17865
  let pem = inline ?? "";
17534
17866
  if (!inline) {
17535
17867
  const given = process.env["LOKI_RECEIPT_SIGNING_KEY_FILE"]?.trim();
17536
- const file = given || join26(process.env["HOME"] || homedir5(), ".loki", "keys", RECEIPT_SIGNER_BASENAME);
17868
+ const file = given || join29(process.env["HOME"] || homedir6(), ".loki", "keys", RECEIPT_SIGNER_BASENAME);
17537
17869
  try {
17538
- pem = readFileSync22(file);
17870
+ pem = readFileSync23(file);
17539
17871
  if (!given) {
17540
17872
  for (const [f, m] of [[file, 384], [dirname11(file), 448]])
17541
- if (statSync11(f).mode & 63)
17873
+ if (statSync12(f).mode & 63)
17542
17874
  chmodSync4(f, m);
17543
17875
  }
17544
17876
  } catch (e) {
17545
17877
  if (e.code !== "ENOENT" || !generate)
17546
17878
  return null;
17547
- mkdirSync8(dirname11(dirname11(file)), { recursive: true, mode: 448 });
17548
- mkdirSync8(dirname11(file), { recursive: true, mode: 448 });
17879
+ mkdirSync9(dirname11(dirname11(file)), { recursive: true, mode: 448 });
17880
+ mkdirSync9(dirname11(file), { recursive: true, mode: 448 });
17549
17881
  const tmp = `${file}.${randomBytes3(6).toString("hex")}.tmp`;
17550
- writeFileSync9(tmp, generateKeyPairSync("ed25519").privateKey.export({ type: "pkcs8", format: "pem" }), { flag: "wx", mode: 384 });
17882
+ writeFileSync10(tmp, generateKeyPairSync("ed25519").privateKey.export({ type: "pkcs8", format: "pem" }), { flag: "wx", mode: 384 });
17551
17883
  try {
17552
17884
  linkSync(tmp, file);
17553
17885
  } catch (l) {
@@ -17556,7 +17888,7 @@ function loadSigningKey(generate = true) {
17556
17888
  } finally {
17557
17889
  unlinkSync4(tmp);
17558
17890
  }
17559
- pem = readFileSync22(file);
17891
+ pem = readFileSync23(file);
17560
17892
  }
17561
17893
  }
17562
17894
  const k = createPrivateKey(pem);
@@ -17683,7 +18015,7 @@ var sealStage = {
17683
18015
  if (wallNotRun > 0) {
17684
18016
  try {
17685
18017
  const kept = new Set((Array.isArray(o.wall?.files) ? o.wall.files : []).map((f) => basename9(String(f.path))));
17686
- for (const n2 of readdirSync11(join26(ctx.runDir, "wall")).sort())
18018
+ for (const n2 of readdirSync11(join29(ctx.runDir, "wall")).sort())
17687
18019
  if (!kept.has(n2))
17688
18020
  notProven.add(`wall test discarded: ${n2} (not_run)`);
17689
18021
  } catch {}
@@ -17743,7 +18075,7 @@ var sealStage = {
17743
18075
  const cost = ctx.cost.read(ctx.repoDir, iterIds);
17744
18076
  if (cost.unmetered)
17745
18077
  notProven.add("cost unmetered (CLI invoker; recorded as 0)");
17746
- const eventsPath = join26(ctx.runDir, "events.jsonl");
18078
+ const eventsPath = join29(ctx.runDir, "events.jsonl");
17747
18079
  const wallFiles = Array.isArray(o.wall?.files) ? o.wall.files : [];
17748
18080
  const wallPassed = typeof o.verify?.wall_passed === "boolean" ? o.verify.wall_passed : null;
17749
18081
  if (wallFiles.length > 0 && wallPassed === null)
@@ -17779,7 +18111,7 @@ var sealStage = {
17779
18111
  provider: ctx.provider,
17780
18112
  model: ctx.model,
17781
18113
  resumed: o.intake?.resumed === true,
17782
- events_sha256: sha2562(existsSync16(eventsPath) ? readFileSync22(eventsPath) : ""),
18114
+ events_sha256: sha2562(existsSync18(eventsPath) ? readFileSync23(eventsPath) : ""),
17783
18115
  ...await sealEvidence(ctx.repoDir, ctx.runDir, o, notProven, signal, ctx.emit),
17784
18116
  log_seal: true
17785
18117
  };
@@ -17793,11 +18125,11 @@ var sealStage = {
17793
18125
  hash = receiptSha256(body);
17794
18126
  }
17795
18127
  const receipt = { ...body, receipt_sha256: hash, verification: { jwt: sig.jwt, kid: sig.kid } };
17796
- mkdirSync8(ctx.runDir, { recursive: true });
17797
- const path = join26(ctx.runDir, "receipt.json");
17798
- writeFileSync9(path, JSON.stringify(receipt, null, 2) + `
18128
+ mkdirSync9(ctx.runDir, { recursive: true });
18129
+ const path = join29(ctx.runDir, "receipt.json");
18130
+ writeFileSync10(path, JSON.stringify(receipt, null, 2) + `
17799
18131
  `);
17800
- writeFileSync9(join26(ctx.runDir, "receipt.md"), renderReceiptMd(receipt));
18132
+ writeFileSync10(join29(ctx.runDir, "receipt.md"), renderReceiptMd(receipt));
17801
18133
  const signed = sig.jwt !== null;
17802
18134
  const data = { receipt_path: path, receipt_sha256: hash, signed, kid: sig.kid, verdict, not_proven: receipt.not_proven };
17803
18135
  ctx.emit("receipt.sealed", "seal", { path, receipt_sha256: hash, signed, kid: sig.kid, verdict, not_proven: receipt.not_proven });
@@ -17807,7 +18139,7 @@ var sealStage = {
17807
18139
 
17808
18140
  // ../../loki-ts/src/features/receipt_dsse.ts
17809
18141
  import { createPublicKey as createPublicKey3, sign as sign2, verify as verify2 } from "crypto";
17810
- import { readFileSync as readFileSync23 } from "fs";
18142
+ import { readFileSync as readFileSync24 } from "fs";
17811
18143
  var DSSE_PAYLOAD_TYPE = "application/vnd.in-toto+json";
17812
18144
  var STATEMENT_TYPE = "https://in-toto.io/Statement/v1";
17813
18145
  var PREDICATE_TYPE = "https://autonomi.dev/loki/receipt/v10";
@@ -17888,7 +18220,7 @@ function verifyDsseReceipt(env, d) {
17888
18220
  }
17889
18221
  function runIdGuard(receiptPath, runId) {
17890
18222
  try {
17891
- return envelopeRunIdProblem(JSON.parse(readFileSync23(receiptPath, "utf8")), runId);
18223
+ return envelopeRunIdProblem(JSON.parse(readFileSync24(receiptPath, "utf8")), runId);
17892
18224
  } catch {
17893
18225
  return null;
17894
18226
  }
@@ -17902,7 +18234,7 @@ function pubFor(kid, given) {
17902
18234
  const active = loadSigningKey(false);
17903
18235
  const pubs = (process.env["LOKI_RECEIPT_RETIRED_PUBKEYS"] ?? "").split(":").map((f) => f.trim()).filter(Boolean).flatMap((f) => {
17904
18236
  try {
17905
- return [createPublicKey4(readFileSync24(f))];
18237
+ return [createPublicKey4(readFileSync25(f))];
17906
18238
  } catch {
17907
18239
  return [];
17908
18240
  }
@@ -17934,10 +18266,10 @@ function checkEventLog(receiptPath, receipt) {
17934
18266
  const bound = receipt["events_sha256"];
17935
18267
  if (typeof bound !== "string" || bound === createHash10("sha256").digest("hex"))
17936
18268
  return null;
17937
- const path = join27(dirname12(receiptPath), "events.jsonl");
17938
- if (!existsSync17(path))
18269
+ const path = join30(dirname12(receiptPath), "events.jsonl");
18270
+ if (!existsSync19(path))
17939
18271
  return "events.jsonl is missing";
17940
- const raw2 = readFileSync24(path, "utf8"), events2 = readEvents(path), lines = raw2.split(`
18272
+ const raw2 = readFileSync25(path, "utf8"), events2 = readEvents(path), lines = raw2.split(`
17941
18273
  `).filter((l) => l.trim() !== "");
17942
18274
  if (events2.length !== lines.length || events2.some((e, i) => e.seq !== i))
17943
18275
  return "events.jsonl has a forged, edited or missing line";
@@ -17953,7 +18285,7 @@ function checkEventLog(receiptPath, receipt) {
17953
18285
  return h.digest("hex") === bound ? null : "events.jsonl does not match the hash recorded at seal";
17954
18286
  }
17955
18287
  function checkLogSeal(receiptPath, hash, kid, given) {
17956
- const path = join27(dirname12(receiptPath), "events.jsonl"), events2 = readEvents(path), done = events2.findIndex((e) => e.type === "run.completed");
18288
+ const path = join30(dirname12(receiptPath), "events.jsonl"), events2 = readEvents(path), done = events2.findIndex((e) => e.type === "run.completed");
17957
18289
  if (!events2.some((e) => e.type === "receipt.sealed" && e.data["receipt_sha256"] === hash))
17958
18290
  return { verdict: "TAMPERED", reason: "events.jsonl has no receipt.sealed event for this receipt" };
17959
18291
  if (done < 0)
@@ -17961,7 +18293,7 @@ function checkLogSeal(receiptPath, hash, kid, given) {
17961
18293
  const seal = events2[done + 1], d = seal?.type === "log.sealed" ? seal.data : null;
17962
18294
  if (!d)
17963
18295
  return { verdict: "TAMPERED", reason: "events.jsonl has no signed log.sealed line after run.completed" };
17964
- const lines = readFileSync24(path, "utf8").split(`
18296
+ const lines = readFileSync25(path, "utf8").split(`
17965
18297
  `).filter((l) => l.trim() !== ""), sha3 = createHash10("sha256").update(lines.slice(0, done + 1).join(`
17966
18298
  `) + `
17967
18299
  `).digest("hex");
@@ -17971,11 +18303,11 @@ function checkLogSeal(receiptPath, hash, kid, given) {
17971
18303
  return null;
17972
18304
  }
17973
18305
  async function verifyReceipt(receiptPath, deps = {}) {
17974
- if (!existsSync17(receiptPath))
18306
+ if (!existsSync19(receiptPath))
17975
18307
  return { verdict: "UNCHECKED", reasons: [`receipt not found: ${receiptPath}`] };
17976
18308
  let receipt;
17977
18309
  try {
17978
- receipt = JSON.parse((deps.read ?? ((p) => readFileSync24(p, "utf8")))(receiptPath));
18310
+ receipt = JSON.parse((deps.read ?? ((p) => readFileSync25(p, "utf8")))(receiptPath));
17979
18311
  } catch {
17980
18312
  return { verdict: "UNCHECKED", reasons: ["receipt.json is not valid JSON"] };
17981
18313
  }
@@ -18008,6 +18340,9 @@ async function verifyReceipt(receiptPath, deps = {}) {
18008
18340
  return { verdict: "UNCHECKED", reasons: [outcome.reason ?? "attestation not checked"] };
18009
18341
  if (outcome.status === "tampered")
18010
18342
  return { verdict: "TAMPERED", reasons: [outcome.reason ?? "attestation invalid"] };
18343
+ const shownKid = verification.kid, signedKid = JSON.parse(Buffer.from(jwt.split(".")[0], "base64url").toString()).kid;
18344
+ if (shownKid !== undefined && shownKid !== signedKid)
18345
+ return { verdict: "TAMPERED", reasons: ["verification.kid differs from the signing kid in the attestation header (metadata outside the signed digest was edited)"] };
18011
18346
  const seal = receipt["log_seal"] !== true ? null : checkLogSeal(receiptPath, computed, JSON.parse(Buffer.from(jwt.split(".")[0], "base64url").toString()).kid, deps.pubkey);
18012
18347
  if (seal)
18013
18348
  return { verdict: seal.verdict, reasons: [seal.reason] };
@@ -18040,15 +18375,15 @@ function mount19(ctx) {
18040
18375
  return done(404, "receipt not available", "not_found");
18041
18376
  let runDir, file;
18042
18377
  try {
18043
- const root = realpathSync11(join28(repo.p, ".loki", "runs"));
18044
- runDir = realpathSync11(join28(root, run2));
18045
- if (runDir !== join28(root, run2))
18378
+ const root = realpathSync13(join31(repo.p, ".loki", "runs"));
18379
+ runDir = realpathSync13(join31(root, run2));
18380
+ if (runDir !== join31(root, run2))
18046
18381
  return done(404, "receipt not found", "not_found");
18047
- file = realpathSync11(join28(runDir, "receipt.json"));
18382
+ file = realpathSync13(join31(runDir, "receipt.json"));
18048
18383
  } catch {
18049
18384
  return done(404, "receipt not found", "not_found");
18050
18385
  }
18051
- if (!file.startsWith(runDir + sep9))
18386
+ if (!file.startsWith(runDir + sep11))
18052
18387
  return done(404, "receipt not found", "not_found");
18053
18388
  let integrity, reasons, sha3 = null, outcome = "UNREADABLE";
18054
18389
  try {
@@ -18063,7 +18398,7 @@ function mount19(ctx) {
18063
18398
  sha3 = r.receiptSha256 ?? null;
18064
18399
  }
18065
18400
  try {
18066
- outcome = outcomeOf(JSON.parse(readFileSync25(file, "utf8")));
18401
+ outcome = outcomeOf(JSON.parse(readFileSync26(file, "utf8")));
18067
18402
  } catch {
18068
18403
  outcome = "UNREADABLE";
18069
18404
  }
@@ -18125,8 +18460,8 @@ function ttlCache(ttlMs = PROBE_CACHE_MS, now = Date.now) {
18125
18460
  }
18126
18461
 
18127
18462
  // src/server/legacy/shim.ts
18128
- import { existsSync as existsSync18, readFileSync as readFileSync26 } from "fs";
18129
- import { join as join29 } from "path";
18463
+ import { existsSync as existsSync20, readFileSync as readFileSync27 } from "fs";
18464
+ import { join as join32 } from "path";
18130
18465
 
18131
18466
  // src/server/legacy/routes.ts
18132
18467
  var LEGACY_ROUTES = [
@@ -18498,10 +18833,10 @@ var logged = new Set;
18498
18833
  var toHono = (p) => p.replace(/\{([A-Za-z_]+):path\}/g, ":$1{.+}").replace(/\{([A-Za-z_]+)\}/g, ":$1");
18499
18834
  var NOT_SUPPORTED = "not yet supported by the Control Plane";
18500
18835
  var repoVersion = () => {
18501
- for (const f of [join29(import.meta.dir, "../../../../../VERSION")]) {
18836
+ for (const f of [join32(import.meta.dir, "../../../../../VERSION")]) {
18502
18837
  try {
18503
- if (existsSync18(f))
18504
- return readFileSync26(f, "utf8").trim();
18838
+ if (existsSync20(f))
18839
+ return readFileSync27(f, "utf8").trim();
18505
18840
  } catch {}
18506
18841
  }
18507
18842
  return "unknown";
@@ -18515,8 +18850,8 @@ var intParam = (v, def, min, max) => {
18515
18850
  return n2 >= min && n2 <= max ? n2 : null;
18516
18851
  };
18517
18852
  function queryAudit(db, q) {
18518
- const where = and(q.action ? eq(audit.action, q.action) : undefined, q.start_date ? gte(audit.ts, q.start_date) : undefined, q.end_date ? lt(audit.ts, `${q.end_date}\uFFFF`) : undefined);
18519
- return db.select().from(audit).where(where).orderBy(desc(audit.id)).limit(q.limit).offset(q.offset).all().map((r) => ({ id: r.id, timestamp: r.ts, action: r.action, actor: r.actor, details: r.detail }));
18853
+ const where = and(q.action ? eq(audit2.action, q.action) : undefined, q.start_date ? gte(audit2.ts, q.start_date) : undefined, q.end_date ? lt(audit2.ts, `${q.end_date}\uFFFF`) : undefined);
18854
+ return db.select().from(audit2).where(where).orderBy(desc(audit2.id)).limit(q.limit).offset(q.offset).all().map((r) => ({ id: r.id, timestamp: r.ts, action: r.action, actor: r.actor, details: r.detail }));
18520
18855
  }
18521
18856
  function legacyShim(opts) {
18522
18857
  const { db } = opts;
@@ -18683,10 +19018,11 @@ function legacyShim(opts) {
18683
19018
 
18684
19019
  // src/server/app.ts
18685
19020
  var MAX_BODY2 = 1e6;
18686
- var defaultUiDir = () => [join30(import.meta.dir, "../../ui/dist"), join30(import.meta.dir, "../ui/dist")].find((d) => existsSync19(join30(d, "index.html")));
19021
+ var defaultUiDir = () => [join33(import.meta.dir, "../../ui/dist"), join33(import.meta.dir, "../ui/dist")].find((d) => existsSync21(join33(d, "index.html")));
18687
19022
  function createApp(opts) {
18688
19023
  const uiDir = opts.uiDir ?? defaultUiDir();
18689
19024
  const { db, sqlite } = openDb(opts.dbPath);
19025
+ cleanupLeakedFixtures(sqlite);
18690
19026
  recomputeLegacy(db);
18691
19027
  let ready = true;
18692
19028
  const answerDir = opts.answerDir ?? defaultAnswerDir();
@@ -18808,11 +19144,11 @@ function createApp(opts) {
18808
19144
  return c.json({ error: "not found" }, 404);
18809
19145
  const root = resolve6(uiDir);
18810
19146
  const f = resolve6(root, `.${p}`);
18811
- if (f.startsWith(root + sep10) && existsSync19(f) && statSync13(f).isFile())
19147
+ if (f.startsWith(root + sep12) && existsSync21(f) && statSync14(f).isFile())
18812
19148
  return new Response(Bun.file(f));
18813
19149
  if (/\.[a-z0-9]+$/i.test(p))
18814
19150
  return c.json({ error: "not found" }, 404);
18815
- return new Response(Bun.file(join30(root, "index.html")));
19151
+ return new Response(Bun.file(join33(root, "index.html")));
18816
19152
  });
18817
19153
  return { app, db, close: () => sqlite.close() };
18818
19154
  }