loki-mode 10.9.1 → 10.10.1

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 (107) 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 +18 -0
  32. package/docs/v10/LEGACY-REMOVAL.md +334 -25
  33. package/docs/v10/PROGRESS.md +15 -0
  34. package/loki-ts/dist/loki.js +1570 -1790
  35. package/mcp/__init__.py +1 -1
  36. package/package.json +3 -2
  37. package/packages/control-plane/dist/server.js +424 -327
  38. package/packages/control-plane/src/db/fixture-cleanup.ts +47 -0
  39. package/packages/control-plane/src/server/app.ts +2 -0
  40. package/packages/control-plane/src/server/integrity.ts +48 -9
  41. package/packages/control-plane/src/server/routes/stats.ts +16 -5
  42. package/packages/control-plane/src/server/runs.ts +16 -4
  43. package/packages/control-plane/ui/dist/assets/index-NAMkcA9N.js +165 -0
  44. package/packages/control-plane/ui/dist/index.html +1 -1
  45. package/plugins/loki-mode/.claude-plugin/plugin.json +1 -1
  46. package/schemas/status-result.schema.json +13 -22
  47. package/skills/release-cadence.md +5 -5
  48. package/web-app/dist/assets/{AdminPage-DqMnfZrH.js → AdminPage-B8RjG3sy.js} +1 -1
  49. package/web-app/dist/assets/{Avatar-BEof29du.js → Avatar-Dttmvqc-.js} +1 -1
  50. package/web-app/dist/assets/{Badge-WD53OL5m.js → Badge-Bvyp8VrD.js} +1 -1
  51. package/web-app/dist/assets/{Button-Rzk5flhU.js → Button-DtY5ZrZw.js} +1 -1
  52. package/web-app/dist/assets/{CockpitPage-Bqz3PCMh.js → CockpitPage-B2gbOwRp.js} +1 -1
  53. package/web-app/dist/assets/{ComparePage-Byo2NzAZ.js → ComparePage-CHi5nZTU.js} +1 -1
  54. package/web-app/dist/assets/{ErrorBoundary-BuE0kH3W.js → ErrorBoundary-BUuTj-OQ.js} +1 -1
  55. package/web-app/dist/assets/{EvidenceReceiptPanel-CKFHOPBx.js → EvidenceReceiptPanel-C5aOn4ES.js} +1 -1
  56. package/web-app/dist/assets/{GitHubIssuesPanel-Df_kTMqI.js → GitHubIssuesPanel-isVxxHXb.js} +1 -1
  57. package/web-app/dist/assets/{GitHubPRsPanel-CouvcKrZ.js → GitHubPRsPanel-DtDpxyQl.js} +1 -1
  58. package/web-app/dist/assets/{HomePage-dEpLzj0x.js → HomePage-Casf-LUB.js} +2 -2
  59. package/web-app/dist/assets/{LoginPage-6DVjUND5.js → LoginPage-DvB78691.js} +1 -1
  60. package/web-app/dist/assets/{MagicPage-BDQ9MOfY.js → MagicPage-DU6hmbnC.js} +1 -1
  61. package/web-app/dist/assets/{MetricsPage-CXiJ04G_.js → MetricsPage-DcPwzoS8.js} +1 -1
  62. package/web-app/dist/assets/{NotFoundPage-BaXhZvyj.js → NotFoundPage-Bs6WxAZ8.js} +1 -1
  63. package/web-app/dist/assets/{ProjectPage-BzE4V7Ti.js → ProjectPage-RBIqC-DJ.js} +3 -3
  64. package/web-app/dist/assets/{ProjectsPage-ipf6TLMX.js → ProjectsPage-CogXD7jM.js} +1 -1
  65. package/web-app/dist/assets/{SettingsPage-BDxkUKN6.js → SettingsPage-pAyJAfPt.js} +1 -1
  66. package/web-app/dist/assets/{ShowcasePage-YQXD72SM.js → ShowcasePage-D6bT2bDd.js} +1 -1
  67. package/web-app/dist/assets/{SystemSettingsPage-Dou7zHS0.js → SystemSettingsPage-CE8xiJRh.js} +1 -1
  68. package/web-app/dist/assets/{TeamsPage-CQarfcYr.js → TeamsPage-wMS55TDT.js} +1 -1
  69. package/web-app/dist/assets/{TemplatesPage-CPi1R8Xj.js → TemplatesPage-COJNVQqu.js} +1 -1
  70. package/web-app/dist/assets/{TerminalOutput-WiGhWdYD.js → TerminalOutput-Cc2XASxd.js} +1 -1
  71. package/web-app/dist/assets/{activity-DOa7ejQA.js → activity-C6eHFDjL.js} +1 -1
  72. package/web-app/dist/assets/{bell-CX-VAlYN.js → bell-BJpVYj4n.js} +1 -1
  73. package/web-app/dist/assets/{bot-EDXaZmHc.js → bot-BwRVVUQL.js} +1 -1
  74. package/web-app/dist/assets/{check-CnBdKk3L.js → check-CGDwoKy8.js} +1 -1
  75. package/web-app/dist/assets/{chevron-left-BJ_ydRQW.js → chevron-left-CF2Nilj6.js} +1 -1
  76. package/web-app/dist/assets/{circle-alert-tz8ufhc_.js → circle-alert-D0RyRec1.js} +1 -1
  77. package/web-app/dist/assets/{clock-DFBZPSsH.js → clock-xeF3gW0W.js} +1 -1
  78. package/web-app/dist/assets/{cloud-uy3p_5Q7.js → cloud-D5Sy69ZZ.js} +1 -1
  79. package/web-app/dist/assets/{code-xml-3CbYD9YL.js → code-xml-C6-5Ql9I.js} +1 -1
  80. package/web-app/dist/assets/{database-YiNBXc9c.js → database-BSjG1_z0.js} +1 -1
  81. package/web-app/dist/assets/{dollar-sign-D0wBa4Cv.js → dollar-sign-FnDR2QUu.js} +1 -1
  82. package/web-app/dist/assets/{file-code-corner-DDdZVZ5v.js → file-code-corner-BiOK9vR6.js} +1 -1
  83. package/web-app/dist/assets/{file-plus-D09LTBqt.js → file-plus-3WALQDEE.js} +1 -1
  84. package/web-app/dist/assets/{globe-BirTPwff.js → globe-vUdUrQqp.js} +1 -1
  85. package/web-app/dist/assets/{hammer-DaE5qgnd.js → hammer-0VwHUEpa.js} +1 -1
  86. package/web-app/dist/assets/{index-D8t7KnQf.js → index-BMKIwteZ.js} +3 -3
  87. package/web-app/dist/assets/{layers-DVVrckRY.js → layers-C6q8kzST.js} +1 -1
  88. package/web-app/dist/assets/{loader-circle-2-nMGmcb.js → loader-circle-B4Tz724d.js} +1 -1
  89. package/web-app/dist/assets/{lock-D0Xj43pC.js → lock-yxvEA65n.js} +1 -1
  90. package/web-app/dist/assets/{package-BB7S3egm.js → package-BpKD0Ot0.js} +1 -1
  91. package/web-app/dist/assets/{plus-BLhoMrn3.js → plus-Ibqx_Vo9.js} +1 -1
  92. package/web-app/dist/assets/{refresh-cw-DMc2uPLw.js → refresh-cw-C9auyvi-.js} +1 -1
  93. package/web-app/dist/assets/{rotate-ccw-DM3IP9x7.js → rotate-ccw-2cEiXm6O.js} +1 -1
  94. package/web-app/dist/assets/{scroll-text-lH2uW7rB.js → scroll-text-BPAWML-w.js} +1 -1
  95. package/web-app/dist/assets/{server-BkerZcdR.js → server-CZh6B1zx.js} +1 -1
  96. package/web-app/dist/assets/{shield-alert-B8ohcUfu.js → shield-alert-CivolUIx.js} +1 -1
  97. package/web-app/dist/assets/{thumbs-up-CTIxLzPD.js → thumbs-up-BP9covEH.js} +1 -1
  98. package/web-app/dist/assets/{trash-2-C3zIpsH5.js → trash-2-BCDfTcNx.js} +1 -1
  99. package/web-app/dist/assets/{trending-down-BTT7wV4Y.js → trending-down-5LypivXh.js} +1 -1
  100. package/web-app/dist/assets/{trending-up-BzHdCDvF.js → trending-up-DNt4SFdk.js} +1 -1
  101. package/web-app/dist/assets/{upload-HxR34-A8.js → upload-DBmHbY6y.js} +1 -1
  102. package/web-app/dist/assets/{usePolling-DDj6Fupy.js → usePolling-DOPZcbUL.js} +1 -1
  103. package/web-app/dist/assets/{user-qH5y9yNy.js → user-BQTJJDCG.js} +1 -1
  104. package/web-app/dist/index.html +1 -1
  105. package/docs/BRANCH-LIFECYCLE-PLAN.md +0 -354
  106. package/docs/EXECUTION-COCKPIT-PLAN.md +0 -289
  107. 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;
@@ -6960,7 +6960,7 @@ var require_public_api = __commonJS((exports) => {
6960
6960
 
6961
6961
  // src/server/app.ts
6962
6962
  import { existsSync as existsSync19, statSync as statSync13 } from "fs";
6963
- import { join as join30, resolve as resolve6, sep as sep10 } from "path";
6963
+ import { join as join31, resolve as resolve6, sep as sep11 } 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 readFileSync25, realpathSync as realpathSync12 } from "fs";
16359
16452
  import { createPublicKey as createPublicKey5 } from "crypto";
16360
- import { join as join28, sep as sep9 } from "path";
16453
+ import { join as join29, sep as sep10 } from "path";
16361
16454
 
16362
16455
  // ../../loki-ts/src/engine10/verify_cmd.ts
16363
16456
  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";
16457
+ import { dirname as dirname12, join as join28 } 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
@@ -16387,14 +16480,14 @@ var REPO_ROOT = findRepoRoot();
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
16482
  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";
16483
+ import { homedir as homedir6 } from "os";
16484
+ import { basename as basename9, dirname as dirname11, join as join27 } 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(/^\.\//, "");
@@ -16914,7 +17007,7 @@ import { spawn as spawn5 } from "child_process";
16914
17007
  import { createHash as createHash6, randomBytes as randomBytes2 } from "crypto";
16915
17008
  import { existsSync as existsSync13, lstatSync as lstatSync5, mkdirSync as mkdirSync6, readdirSync as readdirSync9, readFileSync as readFileSync19, writeFileSync as writeFileSync7 } from "fs";
16916
17009
  import { createServer } from "net";
16917
- import { dirname as dirname8, isAbsolute, join as join23, normalize, relative as relative3, sep as sep8 } from "path";
17010
+ import { dirname as dirname8, isAbsolute, join as join24, normalize, relative as relative3, sep as sep9 } from "path";
16918
17011
  var visualEvidenceEnabled = (env = process.env) => env["LOKI_VISUAL_EVIDENCE"] !== "0";
16919
17012
  var PAGE_RE = /^(?:.*\/)?(?:app|pages|src|public)\/.*\.(?:html|jsx|tsx|vue|svelte)$/;
16920
17013
  var isPageFile = (p) => PAGE_RE.test(p);
@@ -16929,7 +17022,7 @@ function hashScreens(runDir, rels) {
16929
17022
  const out2 = [];
16930
17023
  for (const rel of rels) {
16931
17024
  try {
16932
- out2.push({ path: rel, sha256: sha256File(join23(runDir, rel)) });
17025
+ out2.push({ path: rel, sha256: sha256File(join24(runDir, rel)) });
16933
17026
  } catch {}
16934
17027
  }
16935
17028
  return out2;
@@ -16942,10 +17035,10 @@ function checkScreens(runDir, screens) {
16942
17035
  return "evidence_screens entry is malformed";
16943
17036
  if (isAbsolute(s.path) || normalize(s.path).startsWith(".."))
16944
17037
  return `evidence screenshot path escapes the evidence dir: ${s.path}`;
16945
- const file = join23(runDir, s.path);
17038
+ const file = join24(runDir, s.path);
16946
17039
  let cur = runDir;
16947
- for (const seg of normalize(s.path).split(sep8)) {
16948
- cur = join23(cur, seg);
17040
+ for (const seg of normalize(s.path).split(sep9)) {
17041
+ cur = join24(cur, seg);
16949
17042
  if (isSymlink(cur))
16950
17043
  return `evidence screenshot path is a symlink: ${s.path}`;
16951
17044
  }
@@ -16991,12 +17084,12 @@ async function waitUp(url, ms, signal) {
16991
17084
  return false;
16992
17085
  }
16993
17086
  var playwrightBin = (repoDir) => {
16994
- const bin = join23(repoDir, "node_modules", ".bin", "playwright");
17087
+ const bin = join24(repoDir, "node_modules", ".bin", "playwright");
16995
17088
  return existsSync13(bin) ? bin : null;
16996
17089
  };
16997
17090
  function openapiPaths(repoDir) {
16998
17091
  for (const f of ["openapi.json", "openapi.yaml", "openapi.yml", "swagger.json"]) {
16999
- const p = join23(repoDir, f);
17092
+ const p = join24(repoDir, f);
17000
17093
  if (!existsSync13(p))
17001
17094
  continue;
17002
17095
  try {
@@ -17010,8 +17103,8 @@ function openapiPaths(repoDir) {
17010
17103
  return [];
17011
17104
  }
17012
17105
  var playwrightTestPkg = (repoDir) => {
17013
- const d = join23(repoDir, "node_modules", "@playwright", "test");
17014
- return existsSync13(join23(d, "package.json")) ? d : null;
17106
+ const d = join24(repoDir, "node_modules", "@playwright", "test");
17107
+ return existsSync13(join24(d, "package.json")) ? d : null;
17015
17108
  };
17016
17109
  var e2eSpecSource = (pkgDir, base, routes) => `const { test } = require(${JSON.stringify(pkgDir)});
17017
17110
  test("loki e2e walkthrough", async ({ page }) => {
@@ -17023,24 +17116,24 @@ async function recordE2eMedia(repoDir, runDir, relDir, base, routes, ms, signal)
17023
17116
  if (!pw || !pkg)
17024
17117
  return { rels: [], skipped: "playwright e2e video and trace skipped: @playwright/test is not installed in the repo" };
17025
17118
  try {
17026
- const dir = join23(runDir, relDir, "e2e");
17119
+ const dir = join24(runDir, relDir, "e2e");
17027
17120
  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" } };
17121
+ writeFileSync7(join24(dir, "walk.spec.cjs"), e2eSpecSource(pkg, base, routes));
17122
+ writeFileSync7(join24(dir, "playwright.config.cjs"), `module.exports = { testDir: ".", testMatch: "walk.spec.cjs", outputDir: "./out", reporter: "null", use: { video: "on", trace: "on" } };
17030
17123
  `);
17031
- if (!await shoot(pw, ["test", "--config", join23(dir, "playwright.config.cjs")], repoDir, ms, signal))
17124
+ if (!await shoot(pw, ["test", "--config", join24(dir, "playwright.config.cjs")], repoDir, ms, signal))
17032
17125
  return { rels: [], skipped: "playwright e2e video and trace skipped: the run failed or timed out" };
17033
17126
  const rels = [];
17034
17127
  const walk = (d) => {
17035
17128
  for (const e of readdirSync9(d, { withFileTypes: true })) {
17036
- const full = join23(d, e.name);
17129
+ const full = join24(d, e.name);
17037
17130
  if (e.isDirectory())
17038
17131
  walk(full);
17039
17132
  else if (e.isFile() && /\.(webm|zip)$/.test(e.name))
17040
- rels.push(join23(relDir, "e2e", relative3(dir, full)));
17133
+ rels.push(join24(relDir, "e2e", relative3(dir, full)));
17041
17134
  }
17042
17135
  };
17043
- walk(join23(dir, "out"));
17136
+ walk(join24(dir, "out"));
17044
17137
  return rels.length > 0 ? { rels, skipped: null } : { rels, skipped: "playwright e2e video and trace skipped: no video or trace was produced" };
17045
17138
  } catch (e) {
17046
17139
  return { rels: [], skipped: `playwright e2e video and trace skipped: ${String(e?.message ?? e).slice(0, 120)}` };
@@ -17133,7 +17226,7 @@ async function captureVisualEvidence(repoDir, runDir, changed, opts = {}) {
17133
17226
  try {
17134
17227
  if (!visualEvidenceEnabled(opts.env))
17135
17228
  return skip("LOKI_VISUAL_EVIDENCE is 0");
17136
- const pkgPath = join23(repoDir, "package.json");
17229
+ const pkgPath = join24(repoDir, "package.json");
17137
17230
  if (!existsSync13(pkgPath))
17138
17231
  return skip("no package.json");
17139
17232
  const script = pickScript(JSON.parse(readFileSync19(pkgPath, "utf8")));
@@ -17161,20 +17254,20 @@ async function captureVisualEvidence(repoDir, runDir, changed, opts = {}) {
17161
17254
  }
17162
17255
  if (!await waitUp(base, left(), signal))
17163
17256
  return skip(aborted() ? "aborted" : `${script} server did not answer within ${Math.round(budget / 1000)}s`);
17164
- const evRoot = join23(runDir, "evidence");
17257
+ const evRoot = join24(runDir, "evidence");
17165
17258
  if (isSymlink(evRoot))
17166
17259
  return skip("evidence dir is a symlink");
17167
17260
  mkdirSync6(evRoot, { recursive: true });
17168
17261
  if (pages.length > 0) {
17169
- const rel0 = join23("evidence", randomBytes2(6).toString("hex"));
17170
- mkdirSync6(join23(runDir, rel0));
17262
+ const rel0 = join24("evidence", randomBytes2(6).toString("hex"));
17263
+ mkdirSync6(join24(runDir, rel0));
17171
17264
  const rels = [];
17172
17265
  for (const route of [...new Set(pages.map(routeFor))].slice(0, opts.maxRoutes ?? MAX_ROUTES)) {
17173
17266
  if (aborted() || left() <= 0)
17174
17267
  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)))
17268
+ const rel = join24(rel0, `${screenName(route)}.png`);
17269
+ const ok = await shoot(pw, ["screenshot", `${base}${route}`, join24(runDir, rel)], repoDir, left(), signal);
17270
+ if (ok && !aborted() && !isSymlink(join24(runDir, rel)) && existsSync13(join24(runDir, rel)))
17178
17271
  rels.push(rel);
17179
17272
  }
17180
17273
  if (aborted())
@@ -17199,9 +17292,9 @@ async function captureVisualEvidence(repoDir, runDir, changed, opts = {}) {
17199
17292
  }
17200
17293
  if (aborted())
17201
17294
  return skip("aborted");
17202
- const hdir = join23(evRoot, randomBytes2(6).toString("hex"));
17295
+ const hdir = join24(evRoot, randomBytes2(6).toString("hex"));
17203
17296
  mkdirSync6(hdir);
17204
- writeFileSync7(join23(hdir, "http.json"), JSON.stringify(rows, null, 2) + `
17297
+ writeFileSync7(join24(hdir, "http.json"), JSON.stringify(rows, null, 2) + `
17205
17298
  `);
17206
17299
  return { screens: [], http: true, skipped: null };
17207
17300
  } catch (e) {
@@ -17243,7 +17336,7 @@ var STAGE_BUDGETS = {
17243
17336
 
17244
17337
  // ../../loki-ts/src/features/contract.ts
17245
17338
  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";
17339
+ import { dirname as dirname9, join as join25 } from "path";
17247
17340
  import { createHash as createHash7 } from "crypto";
17248
17341
  var MAX_CRITERIA = 50;
17249
17342
  var MAX_TEXT = 500;
@@ -17278,7 +17371,7 @@ function untracedLines(trace) {
17278
17371
  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
17372
  }
17280
17373
  function contractPath(repoDir) {
17281
- return join24(repoDir, ".loki", "contract.json");
17374
+ return join25(repoDir, ".loki", "contract.json");
17282
17375
  }
17283
17376
  var MAX_CONTRACT_BYTES = 1024 * 1024;
17284
17377
  function readContract(repoDir) {
@@ -17360,14 +17453,14 @@ function sealContract(repoDir, body, rawDiff, checks, env = process.env, snap) {
17360
17453
  // ../../loki-ts/src/features/speed/seal_group.ts
17361
17454
  import { createHash as createHash8 } from "crypto";
17362
17455
  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";
17456
+ import { dirname as dirname10, join as join26 } from "path";
17364
17457
  var ID_RE = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;
17365
17458
  var PASSING = new Set(["VERIFIED", "ALREADY_SATISFIED"]);
17366
17459
  var sha2 = (b) => createHash8("sha256").update(b).digest("hex");
17367
17460
  var isObj9 = (v) => v !== null && typeof v === "object" && !Array.isArray(v);
17368
17461
  var text2 = (v, cap) => typeof v === "string" ? v.replace(/[\x00-\x1f\x7f]+/g, " ").trim().slice(0, cap) : "";
17369
17462
  var num2 = (v) => typeof v === "number" && Number.isFinite(v) && v >= 0 ? v : 0;
17370
- var unitsDir = (runDir) => join25(runDir, "group", "units");
17463
+ var unitsDir = (runDir) => join26(runDir, "group", "units");
17371
17464
  var readJson3 = (p) => {
17372
17465
  try {
17373
17466
  return JSON.parse(readFileSync21(p, "utf8"));
@@ -17383,7 +17476,7 @@ var dirNames = (d) => {
17383
17476
  }
17384
17477
  };
17385
17478
  function sealGroup(runDir, rh) {
17386
- const manifestPath = join25(runDir, "group", "manifest.json");
17479
+ const manifestPath = join26(runDir, "group", "manifest.json");
17387
17480
  if (!existsSync15(manifestPath))
17388
17481
  return { section: undefined, notProven: [], problems: 0, allUnitsPass: true };
17389
17482
  const notProven = [];
@@ -17408,7 +17501,7 @@ function sealGroup(runDir, rh) {
17408
17501
  return fail(`unit ${index2} has an invalid, duplicate or case-colliding id`);
17409
17502
  seen.add(id);
17410
17503
  seenLower.add(id.toLowerCase());
17411
- const dir = join25(unitsDir(runDir), id), rp = join25(dir, "receipt.json"), ep = join25(dir, "events.jsonl");
17504
+ const dir = join26(unitsDir(runDir), id), rp = join26(dir, "receipt.json"), ep = join26(dir, "events.jsonl");
17412
17505
  let bytes;
17413
17506
  try {
17414
17507
  bytes = readFileSync21(rp);
@@ -17453,7 +17546,7 @@ async function verifyGroup(receiptPath, receipt, rh, verifyOne, deps = {}) {
17453
17546
  const bad = (reason) => ({ verdict: "TAMPERED", reason: `group: ${reason}` });
17454
17547
  const g = receipt["group"];
17455
17548
  if (g === undefined)
17456
- return existsSync15(join25(runDir, "group", "manifest.json")) ? bad("group directory present but the receipt has no group section") : null;
17549
+ return existsSync15(join26(runDir, "group", "manifest.json")) ? bad("group directory present but the receipt has no group section") : null;
17457
17550
  if (!isObj9(g) || !ID_RE.test(String(g["group_id"] ?? "")) || !Array.isArray(g["units"]) || g["units"].length === 0)
17458
17551
  return bad("group section malformed");
17459
17552
  const entries = g["units"];
@@ -17465,7 +17558,7 @@ async function verifyGroup(receiptPath, receipt, rh, verifyOne, deps = {}) {
17465
17558
  return bad("unit directories do not match the receipt (missing or extra unit)");
17466
17559
  let unchecked = null;
17467
17560
  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");
17561
+ const e = entries[i], id = ids[i], dir = join26(unitsDir(runDir), id), rp = join26(dir, "receipt.json"), ep = join26(dir, "events.jsonl");
17469
17562
  if (e["index"] !== i)
17470
17563
  return bad(`unit ${id} is out of order (index ${String(e["index"])} at position ${i})`);
17471
17564
  let bytes;
@@ -17533,7 +17626,7 @@ function loadSigningKey(generate = true) {
17533
17626
  let pem = inline ?? "";
17534
17627
  if (!inline) {
17535
17628
  const given = process.env["LOKI_RECEIPT_SIGNING_KEY_FILE"]?.trim();
17536
- const file = given || join26(process.env["HOME"] || homedir5(), ".loki", "keys", RECEIPT_SIGNER_BASENAME);
17629
+ const file = given || join27(process.env["HOME"] || homedir6(), ".loki", "keys", RECEIPT_SIGNER_BASENAME);
17537
17630
  try {
17538
17631
  pem = readFileSync22(file);
17539
17632
  if (!given) {
@@ -17683,7 +17776,7 @@ var sealStage = {
17683
17776
  if (wallNotRun > 0) {
17684
17777
  try {
17685
17778
  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())
17779
+ for (const n2 of readdirSync11(join27(ctx.runDir, "wall")).sort())
17687
17780
  if (!kept.has(n2))
17688
17781
  notProven.add(`wall test discarded: ${n2} (not_run)`);
17689
17782
  } catch {}
@@ -17743,7 +17836,7 @@ var sealStage = {
17743
17836
  const cost = ctx.cost.read(ctx.repoDir, iterIds);
17744
17837
  if (cost.unmetered)
17745
17838
  notProven.add("cost unmetered (CLI invoker; recorded as 0)");
17746
- const eventsPath = join26(ctx.runDir, "events.jsonl");
17839
+ const eventsPath = join27(ctx.runDir, "events.jsonl");
17747
17840
  const wallFiles = Array.isArray(o.wall?.files) ? o.wall.files : [];
17748
17841
  const wallPassed = typeof o.verify?.wall_passed === "boolean" ? o.verify.wall_passed : null;
17749
17842
  if (wallFiles.length > 0 && wallPassed === null)
@@ -17794,10 +17887,10 @@ var sealStage = {
17794
17887
  }
17795
17888
  const receipt = { ...body, receipt_sha256: hash, verification: { jwt: sig.jwt, kid: sig.kid } };
17796
17889
  mkdirSync8(ctx.runDir, { recursive: true });
17797
- const path = join26(ctx.runDir, "receipt.json");
17890
+ const path = join27(ctx.runDir, "receipt.json");
17798
17891
  writeFileSync9(path, JSON.stringify(receipt, null, 2) + `
17799
17892
  `);
17800
- writeFileSync9(join26(ctx.runDir, "receipt.md"), renderReceiptMd(receipt));
17893
+ writeFileSync9(join27(ctx.runDir, "receipt.md"), renderReceiptMd(receipt));
17801
17894
  const signed = sig.jwt !== null;
17802
17895
  const data = { receipt_path: path, receipt_sha256: hash, signed, kid: sig.kid, verdict, not_proven: receipt.not_proven };
17803
17896
  ctx.emit("receipt.sealed", "seal", { path, receipt_sha256: hash, signed, kid: sig.kid, verdict, not_proven: receipt.not_proven });
@@ -17934,7 +18027,7 @@ function checkEventLog(receiptPath, receipt) {
17934
18027
  const bound = receipt["events_sha256"];
17935
18028
  if (typeof bound !== "string" || bound === createHash10("sha256").digest("hex"))
17936
18029
  return null;
17937
- const path = join27(dirname12(receiptPath), "events.jsonl");
18030
+ const path = join28(dirname12(receiptPath), "events.jsonl");
17938
18031
  if (!existsSync17(path))
17939
18032
  return "events.jsonl is missing";
17940
18033
  const raw2 = readFileSync24(path, "utf8"), events2 = readEvents(path), lines = raw2.split(`
@@ -17953,7 +18046,7 @@ function checkEventLog(receiptPath, receipt) {
17953
18046
  return h.digest("hex") === bound ? null : "events.jsonl does not match the hash recorded at seal";
17954
18047
  }
17955
18048
  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");
18049
+ const path = join28(dirname12(receiptPath), "events.jsonl"), events2 = readEvents(path), done = events2.findIndex((e) => e.type === "run.completed");
17957
18050
  if (!events2.some((e) => e.type === "receipt.sealed" && e.data["receipt_sha256"] === hash))
17958
18051
  return { verdict: "TAMPERED", reason: "events.jsonl has no receipt.sealed event for this receipt" };
17959
18052
  if (done < 0)
@@ -18008,6 +18101,9 @@ async function verifyReceipt(receiptPath, deps = {}) {
18008
18101
  return { verdict: "UNCHECKED", reasons: [outcome.reason ?? "attestation not checked"] };
18009
18102
  if (outcome.status === "tampered")
18010
18103
  return { verdict: "TAMPERED", reasons: [outcome.reason ?? "attestation invalid"] };
18104
+ const shownKid = verification.kid, signedKid = JSON.parse(Buffer.from(jwt.split(".")[0], "base64url").toString()).kid;
18105
+ if (shownKid !== undefined && shownKid !== signedKid)
18106
+ return { verdict: "TAMPERED", reasons: ["verification.kid differs from the signing kid in the attestation header (metadata outside the signed digest was edited)"] };
18011
18107
  const seal = receipt["log_seal"] !== true ? null : checkLogSeal(receiptPath, computed, JSON.parse(Buffer.from(jwt.split(".")[0], "base64url").toString()).kid, deps.pubkey);
18012
18108
  if (seal)
18013
18109
  return { verdict: seal.verdict, reasons: [seal.reason] };
@@ -18040,15 +18136,15 @@ function mount19(ctx) {
18040
18136
  return done(404, "receipt not available", "not_found");
18041
18137
  let runDir, file;
18042
18138
  try {
18043
- const root = realpathSync11(join28(repo.p, ".loki", "runs"));
18044
- runDir = realpathSync11(join28(root, run2));
18045
- if (runDir !== join28(root, run2))
18139
+ const root = realpathSync12(join29(repo.p, ".loki", "runs"));
18140
+ runDir = realpathSync12(join29(root, run2));
18141
+ if (runDir !== join29(root, run2))
18046
18142
  return done(404, "receipt not found", "not_found");
18047
- file = realpathSync11(join28(runDir, "receipt.json"));
18143
+ file = realpathSync12(join29(runDir, "receipt.json"));
18048
18144
  } catch {
18049
18145
  return done(404, "receipt not found", "not_found");
18050
18146
  }
18051
- if (!file.startsWith(runDir + sep9))
18147
+ if (!file.startsWith(runDir + sep10))
18052
18148
  return done(404, "receipt not found", "not_found");
18053
18149
  let integrity, reasons, sha3 = null, outcome = "UNREADABLE";
18054
18150
  try {
@@ -18126,7 +18222,7 @@ function ttlCache(ttlMs = PROBE_CACHE_MS, now = Date.now) {
18126
18222
 
18127
18223
  // src/server/legacy/shim.ts
18128
18224
  import { existsSync as existsSync18, readFileSync as readFileSync26 } from "fs";
18129
- import { join as join29 } from "path";
18225
+ import { join as join30 } from "path";
18130
18226
 
18131
18227
  // src/server/legacy/routes.ts
18132
18228
  var LEGACY_ROUTES = [
@@ -18498,7 +18594,7 @@ var logged = new Set;
18498
18594
  var toHono = (p) => p.replace(/\{([A-Za-z_]+):path\}/g, ":$1{.+}").replace(/\{([A-Za-z_]+)\}/g, ":$1");
18499
18595
  var NOT_SUPPORTED = "not yet supported by the Control Plane";
18500
18596
  var repoVersion = () => {
18501
- for (const f of [join29(import.meta.dir, "../../../../../VERSION")]) {
18597
+ for (const f of [join30(import.meta.dir, "../../../../../VERSION")]) {
18502
18598
  try {
18503
18599
  if (existsSync18(f))
18504
18600
  return readFileSync26(f, "utf8").trim();
@@ -18515,8 +18611,8 @@ var intParam = (v, def, min, max) => {
18515
18611
  return n2 >= min && n2 <= max ? n2 : null;
18516
18612
  };
18517
18613
  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 }));
18614
+ 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);
18615
+ 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
18616
  }
18521
18617
  function legacyShim(opts) {
18522
18618
  const { db } = opts;
@@ -18683,10 +18779,11 @@ function legacyShim(opts) {
18683
18779
 
18684
18780
  // src/server/app.ts
18685
18781
  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")));
18782
+ var defaultUiDir = () => [join31(import.meta.dir, "../../ui/dist"), join31(import.meta.dir, "../ui/dist")].find((d) => existsSync19(join31(d, "index.html")));
18687
18783
  function createApp(opts) {
18688
18784
  const uiDir = opts.uiDir ?? defaultUiDir();
18689
18785
  const { db, sqlite } = openDb(opts.dbPath);
18786
+ cleanupLeakedFixtures(sqlite);
18690
18787
  recomputeLegacy(db);
18691
18788
  let ready = true;
18692
18789
  const answerDir = opts.answerDir ?? defaultAnswerDir();
@@ -18808,11 +18905,11 @@ function createApp(opts) {
18808
18905
  return c.json({ error: "not found" }, 404);
18809
18906
  const root = resolve6(uiDir);
18810
18907
  const f = resolve6(root, `.${p}`);
18811
- if (f.startsWith(root + sep10) && existsSync19(f) && statSync13(f).isFile())
18908
+ if (f.startsWith(root + sep11) && existsSync19(f) && statSync13(f).isFile())
18812
18909
  return new Response(Bun.file(f));
18813
18910
  if (/\.[a-z0-9]+$/i.test(p))
18814
18911
  return c.json({ error: "not found" }, 404);
18815
- return new Response(Bun.file(join30(root, "index.html")));
18912
+ return new Response(Bun.file(join31(root, "index.html")));
18816
18913
  });
18817
18914
  return { app, db, close: () => sqlite.close() };
18818
18915
  }