loki-mode 11.8.1 → 11.9.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 (90) hide show
  1. package/SKILL.md +2 -2
  2. package/VERSION +1 -1
  3. package/autonomy/lib/backlog.py +4 -3
  4. package/autonomy/lib/workspace.py +95 -4
  5. package/autonomy/loki +185 -106
  6. package/autonomy/trigger-server.py +282 -4
  7. package/bin/loki +1 -1
  8. package/completions/_loki +34 -1
  9. package/completions/loki.bash +10 -3
  10. package/dashboard/__init__.py +1 -1
  11. package/docs/ARCHITECTURE.md +0 -1
  12. package/docs/CLI-REFERENCE.md +1 -1
  13. package/docs/COMPONENTS.md +1 -1
  14. package/docs/DEPLOY-PLAN.md +13 -3
  15. package/docs/INSTALLATION.md +1 -1
  16. package/docs/WORKSPACES.md +1 -1
  17. package/docs/dev/architecture-reference.md +1 -1
  18. package/docs/sentry-intake.md +15 -1
  19. package/docs/v10/D61-SPEED.md +2 -0
  20. package/docs/v10/PROGRESS.md +13 -1
  21. package/loki-ts/dist/loki.js +947 -881
  22. package/mcp/__init__.py +1 -1
  23. package/package.json +2 -2
  24. package/packages/control-plane/dist/ask-tools-server.js +13 -4
  25. package/packages/control-plane/dist/server.js +428 -276
  26. package/packages/control-plane/src/server/diffstat.ts +10 -2
  27. package/packages/control-plane/src/server/legacy/routes-control.ts +8 -4
  28. package/packages/control-plane/src/server/routes/checkpoints.ts +11 -2
  29. package/packages/control-plane/ui/dist/assets/index-u8cd6TRQ.js +190 -0
  30. package/packages/control-plane/ui/dist/index.html +1 -1
  31. package/plugins/loki-mode/.claude-plugin/plugin.json +1 -1
  32. package/web-app/dist/assets/{AdminPage-BnIg9ilA.js → AdminPage-BdH9iL2I.js} +1 -1
  33. package/web-app/dist/assets/{Avatar-0BTVsQ2A.js → Avatar-C-WixxR6.js} +1 -1
  34. package/web-app/dist/assets/{Badge-Bbq8CXgn.js → Badge-BFWpRd_L.js} +1 -1
  35. package/web-app/dist/assets/{Button-CKD4n0ld.js → Button-CE5vG66Y.js} +1 -1
  36. package/web-app/dist/assets/{CockpitPage-DJhLNLAz.js → CockpitPage-BAVn10bY.js} +1 -1
  37. package/web-app/dist/assets/{ComparePage-Di2akuII.js → ComparePage-DOecIGfM.js} +1 -1
  38. package/web-app/dist/assets/{ErrorBoundary-9lhsYG-z.js → ErrorBoundary-1rNA5v7f.js} +1 -1
  39. package/web-app/dist/assets/{EvidenceReceiptPanel-CQDGXvDr.js → EvidenceReceiptPanel-DjOaWfdH.js} +1 -1
  40. package/web-app/dist/assets/{GitHubIssuesPanel-Y_w2k9Ej.js → GitHubIssuesPanel-NNPBuRPD.js} +1 -1
  41. package/web-app/dist/assets/{GitHubPRsPanel-pMSQedCv.js → GitHubPRsPanel-CL1b--tV.js} +1 -1
  42. package/web-app/dist/assets/{HomePage-CIAkiGdt.js → HomePage-BsLDN6MW.js} +2 -2
  43. package/web-app/dist/assets/{LoginPage-BES4Pk6k.js → LoginPage-CVIv5bCS.js} +1 -1
  44. package/web-app/dist/assets/{MagicPage-B154fMhi.js → MagicPage-BgMBB9lK.js} +1 -1
  45. package/web-app/dist/assets/{MetricsPage-BcNyaiUO.js → MetricsPage-BHeJlqPC.js} +1 -1
  46. package/web-app/dist/assets/{NotFoundPage-D28FqYxG.js → NotFoundPage-DDzwfyDK.js} +1 -1
  47. package/web-app/dist/assets/{ProjectPage-C_O3u8Jx.js → ProjectPage-CuQWEYBF.js} +3 -3
  48. package/web-app/dist/assets/{ProjectsPage-uqdahuPA.js → ProjectsPage-OoD0RFDZ.js} +1 -1
  49. package/web-app/dist/assets/{SettingsPage-DLvVA9OZ.js → SettingsPage-tMFMKkK5.js} +1 -1
  50. package/web-app/dist/assets/{ShowcasePage-BAZP8vsm.js → ShowcasePage-DVfLoWdz.js} +1 -1
  51. package/web-app/dist/assets/{SystemSettingsPage-DPYZkLWh.js → SystemSettingsPage-CThV9N4l.js} +1 -1
  52. package/web-app/dist/assets/{TeamsPage-BgSyb5Pd.js → TeamsPage-CV0nvXI8.js} +1 -1
  53. package/web-app/dist/assets/{TemplatesPage-D_Jh5VU1.js → TemplatesPage-D81EI1L8.js} +1 -1
  54. package/web-app/dist/assets/{TerminalOutput-4v8poDKb.js → TerminalOutput-Bj7uKRIe.js} +1 -1
  55. package/web-app/dist/assets/{activity-CE2b6kdw.js → activity-DoNfNkOL.js} +1 -1
  56. package/web-app/dist/assets/{bell-CxJDlLSR.js → bell-BWO3OU50.js} +1 -1
  57. package/web-app/dist/assets/{bot-C2WuK4zw.js → bot-DcvWv_zD.js} +1 -1
  58. package/web-app/dist/assets/{check-81epVldw.js → check-CfG09Z18.js} +1 -1
  59. package/web-app/dist/assets/{chevron-left-B6EXh_3C.js → chevron-left-z0fh4x2h.js} +1 -1
  60. package/web-app/dist/assets/{circle-alert-0iqoo-iy.js → circle-alert-C_7GlYul.js} +1 -1
  61. package/web-app/dist/assets/{clock-CFrBpNUp.js → clock-BsYUp070.js} +1 -1
  62. package/web-app/dist/assets/{cloud-BBSFhUT_.js → cloud-BVWAJ7Rv.js} +1 -1
  63. package/web-app/dist/assets/{code-xml-BCU2V1rq.js → code-xml-CdZT-6zu.js} +1 -1
  64. package/web-app/dist/assets/{database-D2hvO52K.js → database-Dbkm9t2D.js} +1 -1
  65. package/web-app/dist/assets/{dollar-sign-CXFA9b6T.js → dollar-sign-CfIUM5Pd.js} +1 -1
  66. package/web-app/dist/assets/{file-code-corner-CqTCEEfS.js → file-code-corner-CrUKhnj-.js} +1 -1
  67. package/web-app/dist/assets/{file-plus-DYFbkGbs.js → file-plus-Cf6u0xHY.js} +1 -1
  68. package/web-app/dist/assets/{globe-DpYHLobk.js → globe-DsIqaI7w.js} +1 -1
  69. package/web-app/dist/assets/{hammer-CZ97SEYo.js → hammer-CA14BeMp.js} +1 -1
  70. package/web-app/dist/assets/{index-DzswgpRo.js → index-DpJ_GJuZ.js} +3 -3
  71. package/web-app/dist/assets/{layers-2Tq0edx4.js → layers-Dh5Oqhrj.js} +1 -1
  72. package/web-app/dist/assets/{loader-circle-BFP4kUX-.js → loader-circle-B0WHIGqt.js} +1 -1
  73. package/web-app/dist/assets/{lock-DkKdZH4d.js → lock-DkgfEGYF.js} +1 -1
  74. package/web-app/dist/assets/{package-ChOGD8yf.js → package-LzjmwD1S.js} +1 -1
  75. package/web-app/dist/assets/{plus-hggB1o6b.js → plus-BNjA0KSV.js} +1 -1
  76. package/web-app/dist/assets/{refresh-cw-DYwVLOeF.js → refresh-cw-k3IacuDX.js} +1 -1
  77. package/web-app/dist/assets/{rotate-ccw-CUdY506q.js → rotate-ccw-BFON_xv-.js} +1 -1
  78. package/web-app/dist/assets/{scroll-text-Wvidz5uN.js → scroll-text-CaIlSPZB.js} +1 -1
  79. package/web-app/dist/assets/{server-O11QmkCN.js → server-CKFUccCG.js} +1 -1
  80. package/web-app/dist/assets/{shield-alert-DXXx27uV.js → shield-alert-Q4-rPo5r.js} +1 -1
  81. package/web-app/dist/assets/{thumbs-up-DF8ivs3A.js → thumbs-up-BYMeh8oV.js} +1 -1
  82. package/web-app/dist/assets/{trash-2-LA6PW0Nf.js → trash-2-BHSUEIHw.js} +1 -1
  83. package/web-app/dist/assets/{trending-down-Bw_vLKHT.js → trending-down-BC_FR2VT.js} +1 -1
  84. package/web-app/dist/assets/{trending-up-tX8motha.js → trending-up-CmA-WmLW.js} +1 -1
  85. package/web-app/dist/assets/{upload-CxtTWJd2.js → upload-BxLgBa1B.js} +1 -1
  86. package/web-app/dist/assets/{usePolling-D-P5Iofq.js → usePolling-DKNgwt5n.js} +1 -1
  87. package/web-app/dist/assets/{user-dKTcuUFq.js → user-CA2DlmLc.js} +1 -1
  88. package/web-app/dist/index.html +1 -1
  89. package/memory/rag_injector.py +0 -161
  90. package/packages/control-plane/ui/dist/assets/index-DbLzr87u.js +0 -190
@@ -248,7 +248,7 @@ async function backfill(o) {
248
248
  }
249
249
  const source = sourceId(o.repoDir);
250
250
  let runs2 = 0, sent = 0;
251
- const failed = [];
251
+ const failed2 = [];
252
252
  for (const id of ids) {
253
253
  const runDir = join7(root, id);
254
254
  if (!hasSpool(runDir))
@@ -264,9 +264,9 @@ async function backfill(o) {
264
264
  await sleep(backoffMs(a));
265
265
  }
266
266
  if (!res.ok)
267
- failed.push(id);
267
+ failed2.push(id);
268
268
  }
269
- return { runs: runs2, sent, failed };
269
+ return { runs: runs2, sent, failed: failed2 };
270
270
  }
271
271
  var init_backfill = __esm(() => {
272
272
  init_ship();
@@ -7326,8 +7326,8 @@ var init_paths = __esm(() => {
7326
7326
  });
7327
7327
 
7328
7328
  // ../../loki-ts/src/util/receipt_signer.ts
7329
- import { createHash as createHash6, randomBytes as randomBytes2, createPrivateKey, createPublicKey as createPublicKey2, generateKeyPairSync, sign } from "crypto";
7330
- import { chmodSync as chmodSync4, linkSync, mkdirSync as mkdirSync5, readFileSync as readFileSync18, statSync as statSync11, unlinkSync as unlinkSync3, writeFileSync as writeFileSync8 } from "fs";
7329
+ import { createHash as createHash6, randomBytes as randomBytes2, createPrivateKey, createPublicKey as createPublicKey2, generateKeyPairSync, sign, verify as verify2 } from "crypto";
7330
+ import { chmodSync as chmodSync4, linkSync, mkdirSync as mkdirSync5, readdirSync as readdirSync9, readFileSync as readFileSync18, renameSync as renameSync4, statSync as statSync11, unlinkSync as unlinkSync3, writeFileSync as writeFileSync8 } from "fs";
7331
7331
  import { homedir as homedir8 } from "os";
7332
7332
  import { dirname as dirname7, join as join26 } from "path";
7333
7333
  function canonicalJson(x) {
@@ -9185,7 +9185,7 @@ function routerMode(env = process.env) {
9185
9185
  var routerEnabled = (env = process.env) => routerMode(env) === "on";
9186
9186
 
9187
9187
  // ../../loki-ts/src/engine10/cost.ts
9188
- import { readdirSync as readdirSync9, readFileSync as readFileSync22 } from "fs";
9188
+ import { readdirSync as readdirSync10, readFileSync as readFileSync22 } from "fs";
9189
9189
  import { join as join30 } from "path";
9190
9190
  function routerTelemetryOn(env = process.env) {
9191
9191
  return routerEnabled(env);
@@ -9342,7 +9342,7 @@ import {
9342
9342
  mkdirSync as mkdirSync9,
9343
9343
  openSync as openSync6,
9344
9344
  readSync as readSync5,
9345
- renameSync as renameSync4,
9345
+ renameSync as renameSync5,
9346
9346
  rmSync as rmSync6,
9347
9347
  statSync as statSync14,
9348
9348
  unlinkSync as unlinkSync4,
@@ -9360,7 +9360,7 @@ function durableWriteThenRename(target, body) {
9360
9360
  } finally {
9361
9361
  closeSync6(fd);
9362
9362
  }
9363
- renameSync4(tmp, target);
9363
+ renameSync5(tmp, target);
9364
9364
  fsyncParentDir(target);
9365
9365
  }
9366
9366
  function fsyncParentDir(target) {
@@ -9389,7 +9389,7 @@ var init_atomic = __esm(() => {
9389
9389
  });
9390
9390
 
9391
9391
  // ../../loki-ts/src/runner/budget.ts
9392
- import { existsSync as existsSync18, mkdirSync as mkdirSync10, readdirSync as readdirSync10, readFileSync as readFileSync23, writeFileSync as writeFileSync13 } from "fs";
9392
+ import { existsSync as existsSync18, mkdirSync as mkdirSync10, readdirSync as readdirSync11, readFileSync as readFileSync23, writeFileSync as writeFileSync13 } from "fs";
9393
9393
  import { dirname as dirname11, join as join31, resolve as resolve10 } from "path";
9394
9394
  import { fileURLToPath as fileURLToPath2 } from "url";
9395
9395
  function loadDefaultMaxCostUsd() {
@@ -9979,11 +9979,99 @@ var init_scope = __esm(() => {
9979
9979
  dropped = new WeakMap;
9980
9980
  });
9981
9981
 
9982
+ // ../../loki-ts/src/features/runner_config.ts
9983
+ import { readFileSync as readFileSync27 } from "fs";
9984
+ import { join as join37 } from "path";
9985
+ function relevant(shared, text2) {
9986
+ if (shared === "go") {
9987
+ let blk = false;
9988
+ return lines(text2).filter((l) => {
9989
+ if (blk) {
9990
+ if (l === ")")
9991
+ blk = false;
9992
+ return true;
9993
+ }
9994
+ if (/^replace\s*\($/.test(l)) {
9995
+ blk = true;
9996
+ return true;
9997
+ }
9998
+ return /^replace\s/.test(l);
9999
+ }).join(`
10000
+ `);
10001
+ }
10002
+ if (shared === "tsconfig") {
10003
+ const j = jsonc(text2);
10004
+ const o = j.compilerOptions ?? {};
10005
+ return JSON.stringify(["extends", "baseUrl", "paths", "rootDirs", "moduleSuffixes", "customConditions"].map((k) => k === "extends" ? j["extends"] : o[k]));
10006
+ }
10007
+ if (Array.isArray(shared)) {
10008
+ const j = JSON.parse(text2);
10009
+ return JSON.stringify([shared.map((k) => j[k]), depGuard(j)]);
10010
+ }
10011
+ let on = false;
10012
+ return lines(text2).filter((l) => {
10013
+ if (/^\[[^\]]+\]$/.test(l))
10014
+ on = shared.test(l);
10015
+ return on || KW.test(l);
10016
+ }).join(`
10017
+ `);
10018
+ }
10019
+ function testConfigChanged(repoDir, baseSha, changed) {
10020
+ return changed.filter((f) => {
10021
+ const spec = RUNNER_CONFIGS.find((s) => s.re.test(f));
10022
+ if (!spec)
10023
+ return false;
10024
+ if (!spec.shared)
10025
+ return true;
10026
+ const b = baseText(repoDir, baseSha, f), h = headText(repoDir, f);
10027
+ if (b === h)
10028
+ return false;
10029
+ try {
10030
+ return relevant(spec.shared, b || "{}") !== relevant(spec.shared, h || "{}");
10031
+ } catch {
10032
+ return true;
10033
+ }
10034
+ });
10035
+ }
10036
+ var PKG_KEYS, RUNNER_CONFIGS, DEP_MAPS, RUNNER_DEP, LOCAL_SPEC, depGuard = (j) => DEP_MAPS.map((m) => Object.entries(j[m] ?? {}).filter(([k, v]) => RUNNER_DEP.test(k) || LOCAL_SPEC.test(String(v)))), KW, lines = (t) => t.split(`
10037
+ `).map((l) => l.trim()).filter((l) => l && !l.startsWith("#")), jsonc = (t) => JSON.parse(t.replace(/("(?:\\.|[^"\\])*")|\/\/[^\n]*|\/\*[\s\S]*?\*\//g, "$1").replace(/,(\s*[}\]])/g, "$1")), baseText = (dir, base, f) => {
10038
+ try {
10039
+ return safeGit(dir, ["show", `${base}:./${f}`], { timeout: 20000, maxBuffer: 64 << 20 });
10040
+ } catch {
10041
+ return "";
10042
+ }
10043
+ }, headText = (dir, f) => {
10044
+ try {
10045
+ return readFileSync27(join37(dir, f), "utf8");
10046
+ } catch {
10047
+ return "";
10048
+ }
10049
+ };
10050
+ var init_runner_config = __esm(() => {
10051
+ init_safe_git();
10052
+ PKG_KEYS = ["scripts", "jest", "overrides", "resolutions", "imports", "pnpm", "mocha", "vitest", "ava", "c8", "nyc", "babel", "bun"];
10053
+ RUNNER_CONFIGS = [
10054
+ { re: /(^|\/)(conftest\.py|tox\.ini|bunfig\.toml|\.?pytest\.(ini|toml)|\.mocharc[\w.]*)$/ },
10055
+ { re: /(^|\/)(vitest|vite|jest)\.(config|workspace)\.[\w.]+$/ },
10056
+ { re: /(^|\/)\.cargo\/config(\.toml)?$/ },
10057
+ { re: /(^|\/)package\.json$/, shared: PKG_KEYS },
10058
+ { re: /(^|\/)pyproject\.toml$/, shared: /^\[tool\.pytest/ },
10059
+ { re: /(^|\/)setup\.cfg$/, shared: /^\[tool:pytest\]/ },
10060
+ { re: /(^|\/)go\.mod$/, shared: "go" },
10061
+ { re: /(^|\/)Cargo\.toml$/, shared: /^\[(patch|replace)\b/ },
10062
+ { re: /(^|\/)[jt]sconfig(\.[\w-]+)?\.json$/, shared: "tsconfig" }
10063
+ ];
10064
+ DEP_MAPS = ["dependencies", "devDependencies", "optionalDependencies", "peerDependencies"];
10065
+ RUNNER_DEP = /^(jest|vitest|mocha|ava|c8|nyc|pytest[\w-]*|@jest\/.*|ts-jest|babel-jest)$/;
10066
+ LOCAL_SPEC = /^(file|link|npm|workspace):/;
10067
+ KW = /pytest|jest|mocha|vitest|addopts|testpaths/i;
10068
+ });
10069
+
9982
10070
  // ../../loki-ts/src/e10ext/assert_delta.ts
9983
10071
  import { execFileSync as execFileSync5 } from "child_process";
9984
- import { mkdtempSync as mkdtempSync4, readFileSync as readFileSync27, rmSync as rmSync8 } from "fs";
10072
+ import { mkdtempSync as mkdtempSync4, readFileSync as readFileSync28, rmSync as rmSync8 } from "fs";
9985
10073
  import { tmpdir as tmpdir6 } from "os";
9986
- import { join as join37 } from "path";
10074
+ import { join as join38 } from "path";
9987
10075
  import { basename as basename11 } from "path";
9988
10076
  function classifyAssertDelta(inp) {
9989
10077
  const f = basename11(inp.path);
@@ -9993,7 +10081,7 @@ function classifyAssertDelta(inp) {
9993
10081
  return WEAKENED;
9994
10082
  let cwd = "";
9995
10083
  try {
9996
- cwd = mkdtempSync4(join37(tmpdir6(), "loki-ad-"));
10084
+ cwd = mkdtempSync4(join38(tmpdir6(), "loki-ad-"));
9997
10085
  const raw2 = execFileSync5("python3", ["-I", "-c", PY], {
9998
10086
  input: JSON.stringify({ path: inp.path, base: inp.base, head: inp.head, task: inp.task }),
9999
10087
  cwd,
@@ -10024,7 +10112,7 @@ function assertDeltaNotes(repoDir, baseSha, headRef, path, task, baseCounts, hea
10024
10112
  return null;
10025
10113
  try {
10026
10114
  const show = (ref) => safeGit(repoDir, ["show", `${ref}:${path}`]);
10027
- const head = headRef ? show(headRef) : readFileSync27(join37(repoDir, path), "utf8");
10115
+ const head = headRef ? show(headRef) : readFileSync28(join38(repoDir, path), "utf8");
10028
10116
  const r = classifyTestEdit(path, show(baseSha), head, task, baseCounts, headCounts);
10029
10117
  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;
10030
10118
  } catch {
@@ -10214,9 +10302,9 @@ function hasRealTestFailure(out2) {
10214
10302
  return /^\s*Tests:?\s+.*\b[1-9]\d* failed\b/m.test(out2) || /^(?:=+ )?.*\b[1-9]\d* failed\b.* in [\d.]+s/m.test(out2) || /^FAILED \S+::/m.test(out2) || /^(?:#|\u2139) fail [1-9]/m.test(out2) || /^\s*[1-9]\d*\s+fail\s*$/m.test(out2) || /^\s*(?:FAIL|\u00D7)\s+\S.*\s>\s/m.test(out2) || /\bAssertionError\b/.test(out2);
10215
10303
  }
10216
10304
  function firstLoadLine(out2) {
10217
- const lines = out2.split(`
10305
+ const lines2 = out2.split(`
10218
10306
  `).map((l2) => l2.trim()).filter(Boolean);
10219
- const l = lines.find((x) => /^(?:E\s+)?(?:Error|\w*Error|ERROR|Failed|FAIL)\b|Cannot find|Interrupted:|No test files found/.test(x)) ?? lines[0] ?? "";
10307
+ const l = lines2.find((x) => /^(?:E\s+)?(?:Error|\w*Error|ERROR|Failed|FAIL)\b|Cannot find|Interrupted:|No test files found/.test(x)) ?? lines2[0] ?? "";
10220
10308
  return l.replace(/\s+/g, " ").slice(0, 160);
10221
10309
  }
10222
10310
  function classifyRunnerOutput(out2) {
@@ -10248,7 +10336,7 @@ var init_runner_errors = __esm(() => {
10248
10336
  });
10249
10337
 
10250
10338
  // ../../loki-ts/src/engine10/testmap.ts
10251
- import { basename as basename12, extname, join as join38, relative as relative3, sep as sep10 } from "path";
10339
+ import { basename as basename12, extname, join as join39, relative as relative3, sep as sep10 } from "path";
10252
10340
  function isTestFile(rel) {
10253
10341
  const name = basename12(rel);
10254
10342
  return JS_TEST_RE.test(name) || PY_TEST_RE.test(name) || GO_TEST_RE.test(name);
@@ -10278,8 +10366,8 @@ var init_testmap = __esm(() => {
10278
10366
  });
10279
10367
 
10280
10368
  // ../../loki-ts/src/util/check_result.ts
10281
- import { mkdirSync as mkdirSync12, readFileSync as readFileSync28, renameSync as renameSync5, writeFileSync as writeFileSync15 } from "fs";
10282
- import { basename as basename13, join as join39 } from "path";
10369
+ import { mkdirSync as mkdirSync12, readFileSync as readFileSync29, renameSync as renameSync6, writeFileSync as writeFileSync15 } from "fs";
10370
+ import { basename as basename13, join as join40 } from "path";
10283
10371
  function plainTestEnv(base = process.env) {
10284
10372
  const e = {};
10285
10373
  for (const [k, v] of Object.entries(tokenFreeEnv(base)))
@@ -10355,12 +10443,12 @@ function unittestCount(out2, ok) {
10355
10443
  return Math.max(0, +r[1] - +(v.match(/skipped=(\d+)/)?.[1] ?? 0) - +(v.match(/expected failures=(\d+)/)?.[1] ?? 0));
10356
10444
  }
10357
10445
  function goCount(out2, ok) {
10358
- const lines = out2.split(`
10446
+ const lines2 = out2.split(`
10359
10447
  `);
10360
- if (!lines.some((x) => GO_PKG_RE.test(x)))
10448
+ if (!lines2.some((x) => GO_PKG_RE.test(x)))
10361
10449
  return;
10362
- const last = new Map, seen = new Set, skip = new Set, failPkg = ok !== true && lines.some((x) => /^FAIL\s+\S+\s+[\d.]+s\b/.test(x));
10363
- for (const x of lines) {
10450
+ const last = new Map, seen = new Set, skip = new Set, failPkg = ok !== true && lines2.some((x) => /^FAIL\s+\S+\s+[\d.]+s\b/.test(x));
10451
+ for (const x of lines2) {
10364
10452
  const r = x.match(/^=== RUN\s+(\S+)/), m = x.match(/^--- (PASS|FAIL|SKIP): (\S+)/);
10365
10453
  if (r)
10366
10454
  seen.add(r[1]);
@@ -10377,9 +10465,9 @@ function goCount(out2, ok) {
10377
10465
  const t = [...last.values()].filter((v) => v !== "SKIP").length;
10378
10466
  if (t > 0)
10379
10467
  return t;
10380
- if (lines.some((x) => /^(?:=== RUN|PASS$|FAIL$|testing: warning: no tests to run)/.test(x)))
10468
+ if (lines2.some((x) => /^(?:=== RUN|PASS$|FAIL$|testing: warning: no tests to run)/.test(x)))
10381
10469
  return 0;
10382
- return lines.filter((x) => GO_PKG_RE.test(x)).every((x) => /\[no tests? (?:files|to run)\]/.test(x)) ? 0 : null;
10470
+ return lines2.filter((x) => GO_PKG_RE.test(x)).every((x) => /\[no tests? (?:files|to run)\]/.test(x)) ? 0 : null;
10383
10471
  }
10384
10472
  function goRunner(cmd, args) {
10385
10473
  const segs = args ? [[cmd, ...args]] : cmd.split(/&&|\|\||[;|\n]/).map((x) => x.trim().split(/\s+/).filter(Boolean));
@@ -10403,7 +10491,7 @@ function goRunner(cmd, args) {
10403
10491
  return {};
10404
10492
  }
10405
10493
  function goJson(out2) {
10406
- let any = false, failed = false;
10494
+ let any = false, failed2 = false;
10407
10495
  for (const x of out2.split(`
10408
10496
  `)) {
10409
10497
  if (!x.trim() || /^go: /.test(x))
@@ -10418,9 +10506,9 @@ function goJson(out2) {
10418
10506
  return null;
10419
10507
  any = true;
10420
10508
  if (e.Action === "fail")
10421
- failed = true;
10509
+ failed2 = true;
10422
10510
  }
10423
- return any ? { failed } : null;
10511
+ return any ? { failed: failed2 } : null;
10424
10512
  }
10425
10513
  function skipped(raw2) {
10426
10514
  return stripAnsi(raw2).split(`
@@ -10488,7 +10576,7 @@ function markExecuted(a, executed) {
10488
10576
  return { ...a, checks_deferred, summary: summaryOf({ ...a, checks_deferred }) };
10489
10577
  }
10490
10578
  function classifyRisk(files, env = process.env) {
10491
- const lines = files.reduce((n2, f) => n2 + f.added + f.deleted, 0);
10579
+ const lines2 = files.reduce((n2, f) => n2 + f.added + f.deleted, 0);
10492
10580
  const sensitive = files.flatMap((f) => {
10493
10581
  const hit = SENSITIVE.find(([, re]) => re.test(f.path));
10494
10582
  return hit && !isTestFile(f.path) ? [`${f.path} (${hit[0]})`] : [];
@@ -10506,14 +10594,14 @@ function classifyRisk(files, env = process.env) {
10506
10594
  high.push(`public API surface: ${api.slice(0, 3).join(", ")}`);
10507
10595
  if (files.length >= FILES_HIGH)
10508
10596
  high.push(`${files.length} files >= ${FILES_HIGH}`);
10509
- if (lines >= LINES_HIGH)
10510
- high.push(`${lines} changed lines >= ${LINES_HIGH}`);
10597
+ if (lines2 >= LINES_HIGH)
10598
+ high.push(`${lines2} changed lines >= ${LINES_HIGH}`);
10511
10599
  if (testsModified.length)
10512
10600
  med.push(`existing tests or test config modified: ${testsModified.slice(0, 3).join(", ")}`);
10513
10601
  if (files.length >= FILES_MEDIUM)
10514
10602
  med.push(`${files.length} files >= ${FILES_MEDIUM}`);
10515
- if (lines >= LINES_MEDIUM)
10516
- med.push(`${lines} changed lines >= ${LINES_MEDIUM}`);
10603
+ if (lines2 >= LINES_MEDIUM)
10604
+ med.push(`${lines2} changed lines >= ${LINES_MEDIUM}`);
10517
10605
  const computed = high.length ? "high" : med.length ? "medium" : "low";
10518
10606
  const reasons = high.length ? [...high, ...med] : med.length ? med : [`${files.length} file${files.length === 1 ? "" : "s"}, no sensitive paths`];
10519
10607
  let tier = computed, override, ignored;
@@ -10536,7 +10624,7 @@ function classifyRisk(files, env = process.env) {
10536
10624
  tier,
10537
10625
  computed,
10538
10626
  reasons,
10539
- stats: { files: files.length, lines, sensitive, tests_modified: testsModified, tests_deleted: testsDeleted, api },
10627
+ stats: { files: files.length, lines: lines2, sensitive, tests_modified: testsModified, tests_deleted: testsDeleted, api },
10540
10628
  checks_selected: plan.selected,
10541
10629
  checks_deferred,
10542
10630
  ...override ? { override } : {},
@@ -10571,7 +10659,7 @@ function diffFiles(repoDir, baseSha) {
10571
10659
  return f;
10572
10660
  let n2 = 0;
10573
10661
  try {
10574
- n2 = readFileSync28(join39(repoDir, path), "utf8").split(`
10662
+ n2 = readFileSync29(join40(repoDir, path), "utf8").split(`
10575
10663
  `).length;
10576
10664
  } catch {}
10577
10665
  return { path, added: n2, deleted: 0, status: "added" };
@@ -10579,11 +10667,11 @@ function diffFiles(repoDir, baseSha) {
10579
10667
  }
10580
10668
  function recordRisk(repoDir, a) {
10581
10669
  try {
10582
- const dir = join39(repoDir, ".loki", "state"), p = join39(dir, "risk-tier.json");
10670
+ const dir = join40(repoDir, ".loki", "state"), p = join40(dir, "risk-tier.json");
10583
10671
  mkdirSync12(dir, { recursive: true });
10584
10672
  writeFileSync15(`${p}.tmp`, JSON.stringify(a, null, 2) + `
10585
10673
  `);
10586
- renameSync5(`${p}.tmp`, p);
10674
+ renameSync6(`${p}.tmp`, p);
10587
10675
  return p;
10588
10676
  } catch {
10589
10677
  return null;
@@ -10624,10 +10712,10 @@ var init_check_result = __esm(() => {
10624
10712
  import { spawnSync as spawnSync3 } from "child_process";
10625
10713
  import { mkdtempSync as mkdtempSync5, realpathSync as realpathSync16, rmSync as rmSync9 } from "fs";
10626
10714
  import { tmpdir as tmpdir7 } from "os";
10627
- import { basename as basename14, extname as extname2, join as join40 } from "path";
10715
+ import { basename as basename14, extname as extname2, join as join41 } from "path";
10628
10716
  function changedFilesNamed(out2, repoDir, changed) {
10629
10717
  return changed.filter((f) => {
10630
- if (out2.includes(f) || out2.includes(join40(repoDir, f)))
10718
+ if (out2.includes(f) || out2.includes(join41(repoDir, f)))
10631
10719
  return true;
10632
10720
  const segs = f.split("/");
10633
10721
  if (suffixes(segs, 2).some((x) => x !== f && bounded(out2, x, "(?![\\w-])")))
@@ -10738,7 +10826,7 @@ async function loadErrorIsHarnessOwned(i) {
10738
10826
  return false;
10739
10827
  if (i.args.some((a) => changed.includes(a.replace(/^\.\//, ""))))
10740
10828
  return false;
10741
- const dir = mkdtempSync5(join40(tmpdir7(), "e10-loadown-"));
10829
+ const dir = mkdtempSync5(join41(tmpdir7(), "e10-loadown-"));
10742
10830
  const git3 = (args) => {
10743
10831
  safeGit(i.repoDir, args, { stdio: "ignore" });
10744
10832
  };
@@ -10770,7 +10858,7 @@ var init_load_owner = __esm(() => {
10770
10858
  });
10771
10859
 
10772
10860
  // ../../loki-ts/src/util/wall_owned.ts
10773
- import { isAbsolute as isAbsolute4, join as join41, relative as relative4 } from "path";
10861
+ import { isAbsolute as isAbsolute4, join as join42, relative as relative4 } from "path";
10774
10862
  function failingLocations(raw2, cwd, repoDir) {
10775
10863
  const out2 = raw2.replace(ANSI2, "").replace(/\r\n?/g, `
10776
10864
  `), files = new Set;
@@ -10802,7 +10890,7 @@ function failingLocations(raw2, cwd, repoDir) {
10802
10890
  }
10803
10891
  if (errors2 === 0 || located !== errors2)
10804
10892
  return null;
10805
- return [...files].map((f) => relative4(repoDir, isAbsolute4(f) ? f : join41(cwd, f)));
10893
+ return [...files].map((f) => relative4(repoDir, isAbsolute4(f) ? f : join42(cwd, f)));
10806
10894
  }
10807
10895
  function wallOwnedFailure(raw2, cwd, repoDir, wall) {
10808
10896
  const locs = failingLocations(raw2, cwd, repoDir);
@@ -10831,11 +10919,11 @@ var init_state = __esm(() => {
10831
10919
  });
10832
10920
 
10833
10921
  // ../../loki-ts/src/runner/repo_profile.ts
10834
- import { existsSync as existsSync22, readFileSync as readFileSync29, mkdirSync as mkdirSync13, readdirSync as readdirSync11, renameSync as renameSync6, rmSync as rmSync10, writeFileSync as writeFileSync16 } from "fs";
10835
- import { basename as basename15, join as join42, resolve as resolve11, isAbsolute as isAbsolute5 } from "path";
10922
+ import { existsSync as existsSync22, readFileSync as readFileSync30, mkdirSync as mkdirSync13, readdirSync as readdirSync12, renameSync as renameSync7, rmSync as rmSync10, writeFileSync as writeFileSync16 } from "fs";
10923
+ import { basename as basename15, join as join43, resolve as resolve11, isAbsolute as isAbsolute5 } from "path";
10836
10924
  function loadProfile(repoDir) {
10837
10925
  try {
10838
- const p = JSON.parse(readFileSync29(commandProfilePath(repoDir), "utf8"));
10926
+ const p = JSON.parse(readFileSync30(commandProfilePath(repoDir), "utf8"));
10839
10927
  return p?.version === 1 && Array.isArray(p.commands) && p.baseline ? p : null;
10840
10928
  } catch {
10841
10929
  return null;
@@ -10866,7 +10954,7 @@ function attributeFullSuite(repoDir, baseSha, full, blocked, changed) {
10866
10954
  const block = new Set(blocked);
10867
10955
  return attribute(repoDir, baseSha, bad.map((c) => ({ name: c.name, ...c.ids ? { ids: c.ids } : {}, eligible: (c.ids ?? []).every((i) => !block.has(i) && !changed.some((f) => i.includes(f))) })));
10868
10956
  }
10869
- var commandProfilePath = (repoDir) => join42(repoDir, ".loki", "state", "repo-profile.json");
10957
+ var commandProfilePath = (repoDir) => join43(repoDir, ".loki", "state", "repo-profile.json");
10870
10958
  var init_repo_profile = __esm(() => {
10871
10959
  init_failures();
10872
10960
  init_check_result();
@@ -10879,20 +10967,20 @@ var init_repo_profile = __esm(() => {
10879
10967
  import { createHash as createHash10 } from "crypto";
10880
10968
  import {
10881
10969
  existsSync as existsSync23,
10882
- readdirSync as readdirSync12,
10883
- readFileSync as readFileSync30,
10970
+ readdirSync as readdirSync13,
10971
+ readFileSync as readFileSync31,
10884
10972
  statSync as statSync16,
10885
10973
  writeFileSync as writeFileSync17,
10886
10974
  mkdirSync as mkdirSync14
10887
10975
  } from "fs";
10888
- import { basename as basename16, dirname as dirname13, join as join43, resolve as resolve12 } from "path";
10976
+ import { basename as basename16, dirname as dirname13, join as join44, resolve as resolve12 } from "path";
10889
10977
  function readManifest(path) {
10890
10978
  if (!existsSync23(path)) {
10891
10979
  return null;
10892
10980
  }
10893
10981
  let raw2;
10894
10982
  try {
10895
- raw2 = readFileSync30(path, "utf8");
10983
+ raw2 = readFileSync31(path, "utf8");
10896
10984
  } catch {
10897
10985
  return null;
10898
10986
  }
@@ -10929,7 +11017,7 @@ function resolveMembers(rootDir, declared, selfBasename) {
10929
11017
  const seen = new Set;
10930
11018
  let children;
10931
11019
  try {
10932
- children = readdirSync12(rootDir);
11020
+ children = readdirSync13(rootDir);
10933
11021
  } catch {
10934
11022
  children = [];
10935
11023
  }
@@ -10944,7 +11032,7 @@ function resolveMembers(rootDir, declared, selfBasename) {
10944
11032
  for (const child of children) {
10945
11033
  if (!matchPattern(name, child))
10946
11034
  continue;
10947
- const full = join43(rootDir, child);
11035
+ const full = join44(rootDir, child);
10948
11036
  if (seen.has(full))
10949
11037
  continue;
10950
11038
  let st;
@@ -10961,7 +11049,7 @@ function resolveMembers(rootDir, declared, selfBasename) {
10961
11049
  }
10962
11050
  if (declared.length === 0) {
10963
11051
  for (const child of children) {
10964
- const full = join43(rootDir, child);
11052
+ const full = join44(rootDir, child);
10965
11053
  if (seen.has(full))
10966
11054
  continue;
10967
11055
  let st;
@@ -10972,7 +11060,7 @@ function resolveMembers(rootDir, declared, selfBasename) {
10972
11060
  }
10973
11061
  if (!st.isDirectory())
10974
11062
  continue;
10975
- if (!existsSync23(join43(full, ".loki", "app.json")))
11063
+ if (!existsSync23(join44(full, ".loki", "app.json")))
10976
11064
  continue;
10977
11065
  seen.add(full);
10978
11066
  out2.push(full);
@@ -10993,17 +11081,17 @@ function cacheKey(manifestPaths) {
10993
11081
  }
10994
11082
  function writeCache(targetDir, key, result) {
10995
11083
  try {
10996
- const cacheDir = join43(targetDir, ".loki", "state");
11084
+ const cacheDir = join44(targetDir, ".loki", "state");
10997
11085
  mkdirSync14(cacheDir, { recursive: true });
10998
- const cachePath = join43(cacheDir, "project-graph.json");
11086
+ const cachePath = join44(cacheDir, "project-graph.json");
10999
11087
  writeFileSync17(cachePath, JSON.stringify({ key, result }, null, 2), "utf8");
11000
11088
  } catch {}
11001
11089
  }
11002
11090
  function discoverProjectGraph(targetDir) {
11003
11091
  const absTarget = resolve12(targetDir);
11004
- const targetManifest = join43(absTarget, ".loki", "app.json");
11092
+ const targetManifest = join44(absTarget, ".loki", "app.json");
11005
11093
  const parentDir = dirname13(absTarget);
11006
- const parentManifest = join43(parentDir, ".loki", "app.json");
11094
+ const parentManifest = join44(parentDir, ".loki", "app.json");
11007
11095
  const examined = [];
11008
11096
  let chosenManifest = null;
11009
11097
  let chosenRoot = "";
@@ -11023,7 +11111,7 @@ function discoverProjectGraph(targetDir) {
11023
11111
  const members = resolveMembers(chosenRoot, declared, basename16(absTarget));
11024
11112
  const filtered = [];
11025
11113
  for (const m of members) {
11026
- const sibManifestPath = join43(m, ".loki", "app.json");
11114
+ const sibManifestPath = join44(m, ".loki", "app.json");
11027
11115
  if (existsSync23(sibManifestPath)) {
11028
11116
  examined.push(sibManifestPath);
11029
11117
  const sibManifest = readManifest(sibManifestPath);
@@ -11096,15 +11184,15 @@ var init_steer = __esm(() => {
11096
11184
  });
11097
11185
 
11098
11186
  // ../../loki-ts/src/util/pr_lessons.ts
11099
- import { existsSync as existsSync24, mkdirSync as mkdirSync15, readFileSync as readFileSync31 } from "fs";
11100
- import { dirname as dirname14, join as join44 } from "path";
11187
+ import { existsSync as existsSync24, mkdirSync as mkdirSync15, readFileSync as readFileSync32 } from "fs";
11188
+ import { dirname as dirname14, join as join45 } from "path";
11101
11189
  import { createHash as createHash11 } from "crypto";
11102
11190
  function loadLessons(repoDir) {
11103
11191
  const p = lessonsPath(repoDir);
11104
11192
  if (!existsSync24(p))
11105
11193
  return [];
11106
11194
  try {
11107
- const j = JSON.parse(readFileSync31(p, "utf-8"));
11195
+ const j = JSON.parse(readFileSync32(p, "utf-8"));
11108
11196
  return Array.isArray(j.lessons) ? j.lessons : [];
11109
11197
  } catch {
11110
11198
  return [];
@@ -11146,7 +11234,7 @@ function recordRunVerdict(repoDir, runId, verdict) {
11146
11234
  if (dirty)
11147
11235
  save(repoDir, lessons);
11148
11236
  }
11149
- var lessonsPath = (repoDir) => join44(repoDir, ".loki", "memory", "semantic", "pr-lessons.json"), KINDS;
11237
+ var lessonsPath = (repoDir) => join45(repoDir, ".loki", "memory", "semantic", "pr-lessons.json"), KINDS;
11150
11238
  var init_pr_lessons = __esm(() => {
11151
11239
  init_atomic();
11152
11240
  KINDS = new Set(["pr", "receipt", "ticket", "rejected"]);
@@ -11162,7 +11250,7 @@ var init_model_downgrades = __esm(() => {
11162
11250
  var init_mcp_config = () => {};
11163
11251
 
11164
11252
  // ../../loki-ts/src/providers/claude_flags.ts
11165
- import { existsSync as existsSync25, readFileSync as readFileSync32 } from "fs";
11253
+ import { existsSync as existsSync25, readFileSync as readFileSync33 } from "fs";
11166
11254
  import { resolve as resolvePath, dirname as dirname15 } from "path";
11167
11255
  import { fileURLToPath as fileURLToPath3 } from "url";
11168
11256
  function designArchetypesText() {
@@ -11174,7 +11262,7 @@ function designArchetypesText() {
11174
11262
  ]) {
11175
11263
  const p = resolvePath(moduleDir, ...rel);
11176
11264
  if (existsSync25(p))
11177
- return readFileSync32(p, "utf8");
11265
+ return readFileSync33(p, "utf8");
11178
11266
  }
11179
11267
  } catch {}
11180
11268
  return "";
@@ -11252,14 +11340,14 @@ var init_providers = __esm(() => {
11252
11340
  });
11253
11341
 
11254
11342
  // ../../loki-ts/src/runner/router/route_record.ts
11255
- import { existsSync as existsSync26, lstatSync as lstatSync9, mkdirSync as mkdirSync16, readFileSync as readFileSync33, writeFileSync as writeFileSync18 } from "fs";
11256
- import { join as join45 } from "path";
11343
+ import { existsSync as existsSync26, lstatSync as lstatSync9, mkdirSync as mkdirSync16, readFileSync as readFileSync34, writeFileSync as writeFileSync18 } from "fs";
11344
+ import { join as join46 } from "path";
11257
11345
  function loadRouteRecord(runDir) {
11258
11346
  try {
11259
- const p = join45(runDir, ROUTE_RECORD_FILE);
11347
+ const p = join46(runDir, ROUTE_RECORD_FILE);
11260
11348
  if (!existsSync26(p) || !lstatSync9(p).isFile())
11261
11349
  return;
11262
- const v = JSON.parse(readFileSync33(p, "utf8"));
11350
+ const v = JSON.parse(readFileSync34(p, "utf8"));
11263
11351
  return typeof v === "object" && v !== null && !Array.isArray(v) ? v : undefined;
11264
11352
  } catch {
11265
11353
  return;
@@ -11269,7 +11357,7 @@ var ROUTE_RECORD_FILE = "route.json";
11269
11357
  var init_route_record = () => {};
11270
11358
 
11271
11359
  // ../../loki-ts/src/runner/router/history_store.ts
11272
- import { existsSync as existsSync27, mkdirSync as mkdirSync17, readFileSync as readFileSync34, renameSync as renameSync7, rmSync as rmSync11, writeFileSync as writeFileSync19 } from "fs";
11360
+ import { existsSync as existsSync27, mkdirSync as mkdirSync17, readFileSync as readFileSync35, renameSync as renameSync8, rmSync as rmSync11, writeFileSync as writeFileSync19 } from "fs";
11273
11361
  import { resolve as resolve13 } from "path";
11274
11362
  function validRun(v) {
11275
11363
  if (typeof v !== "object" || v === null)
@@ -11285,7 +11373,7 @@ function readRunHistory(key, cacheRoot) {
11285
11373
  if (!existsSync27(path))
11286
11374
  return [];
11287
11375
  try {
11288
- const parsed = JSON.parse(readFileSync34(path, "utf8"));
11376
+ const parsed = JSON.parse(readFileSync35(path, "utf8"));
11289
11377
  const runs2 = parsed?.runs;
11290
11378
  return Array.isArray(runs2) ? runs2.filter(validRun) : [];
11291
11379
  } catch {
@@ -11300,7 +11388,7 @@ function appendRunOutcome(key, outcome, cacheRoot) {
11300
11388
  const tmp = `${target}.${process.pid}.tmp`;
11301
11389
  try {
11302
11390
  writeFileSync19(tmp, JSON.stringify({ runs: runs2 }));
11303
- renameSync7(tmp, target);
11391
+ renameSync8(tmp, target);
11304
11392
  } catch (e) {
11305
11393
  rmSync11(tmp, { force: true, recursive: false });
11306
11394
  throw e;
@@ -11319,7 +11407,7 @@ var init_history_store = __esm(() => {
11319
11407
  });
11320
11408
 
11321
11409
  // ../../loki-ts/src/runner/router/history.ts
11322
- import { dirname as dirname16, join as join46, resolve as resolve14 } from "path";
11410
+ import { dirname as dirname16, join as join47, resolve as resolve14 } from "path";
11323
11411
  import { fileURLToPath as fileURLToPath4 } from "url";
11324
11412
  function shapeKey(model) {
11325
11413
  if (!model || model.status !== "ok")
@@ -11479,7 +11567,7 @@ var init_worker = __esm(() => {
11479
11567
  });
11480
11568
 
11481
11569
  // ../../loki-ts/src/project_model/package_suite.ts
11482
- import { join as join47 } from "path";
11570
+ import { join as join48 } from "path";
11483
11571
  async function runPackageSuites(repoDir, signal, opts, checks, notProven) {
11484
11572
  const api = loadProjectApi(repoDir);
11485
11573
  if (!isMultiRoot(api))
@@ -11490,7 +11578,7 @@ async function runPackageSuites(repoDir, signal, opts, checks, notProven) {
11490
11578
  for (const pkg of withTests) {
11491
11579
  const tc = pkg.commands.test, name = `full suite: ${pkg.name}`, cmd = `(cd ${tc.cwd} && ${tc.cmd})`;
11492
11580
  const started = Date.now();
11493
- const r = await run(["bash", "-c", tc.cmd], { cwd: join47(repoDir, tc.cwd), timeoutMs: opts.timeoutMs, ...opts.path ? { env: { PATH: opts.path } } : {} });
11581
+ const r = await run(["bash", "-c", tc.cmd], { cwd: join48(repoDir, tc.cwd), timeoutMs: opts.timeoutMs, ...opts.path ? { env: { PATH: opts.path } } : {} });
11494
11582
  const duration_s = (Date.now() - started) / 1000;
11495
11583
  if (signal.aborted) {
11496
11584
  checks.push({ name, cmd, result: "not_run", duration_s });
@@ -11499,7 +11587,7 @@ async function runPackageSuites(repoDir, signal, opts, checks, notProven) {
11499
11587
  }
11500
11588
  const out2 = `${r.stdout}
11501
11589
  ${r.stderr}`;
11502
- const lr = r.exitCode === 0 || !opts.baseSha ? undefined : await harnessLoadReason({ repoDir, baseSha: opts.baseSha, out: out2, cmd: "bash", args: ["-c", tc.cmd], signal, cwd: join47(repoDir, tc.cwd), ...opts.path ? { env: { PATH: opts.path } } : {} });
11590
+ const lr = r.exitCode === 0 || !opts.baseSha ? undefined : await harnessLoadReason({ repoDir, baseSha: opts.baseSha, out: out2, cmd: "bash", args: ["-c", tc.cmd], signal, cwd: join48(repoDir, tc.cwd), ...opts.path ? { env: { PATH: opts.path } } : {} });
11503
11591
  if (lr) {
11504
11592
  checks.push({ name, cmd, result: "not_run", duration_s });
11505
11593
  notProven.add(`${lr} (${name}; harness-owned)`);
@@ -11533,7 +11621,7 @@ __export(exports_deep, {
11533
11621
  DEFAULT_PUSH_SH: () => DEFAULT_PUSH_SH
11534
11622
  });
11535
11623
  import { existsSync as existsSync28, mkdirSync as mkdirSync18, writeFileSync as writeFileSync20 } from "fs";
11536
- import { join as join48 } from "path";
11624
+ import { join as join49 } from "path";
11537
11625
  async function runFullSuite(ctx, signal, opts, checks, notProven) {
11538
11626
  if (await runPackageSuites(ctx.repoDir, signal, { path: opts.path, timeoutMs: opts.fullSuiteTimeoutMs ?? FULL_SUITE_TIMEOUT_MS, baseSha: ctx.baseSha }, checks, notProven))
11539
11627
  return;
@@ -11630,7 +11718,7 @@ async function runSecretScan(ctx, changed, opts, checks, notProven) {
11630
11718
  }
11631
11719
  const inline = 'script="$1"; shift; . "$script" || exit 3; hit=0; for f in "$@"; do if _commit_scan_secret_file "$f" || _commit_path_looks_secret "$f"; then printf "%s\\n" "$f"; hit=1; fi; done; exit "$hit"';
11632
11720
  const started = Date.now();
11633
- const r = await run(["bash", "-c", inline, "_", scriptPath, ...changed.map((f) => join48(ctx.repoDir, f))], { cwd: ctx.repoDir, timeoutMs: 30000 });
11721
+ const r = await run(["bash", "-c", inline, "_", scriptPath, ...changed.map((f) => join49(ctx.repoDir, f))], { cwd: ctx.repoDir, timeoutMs: 30000 });
11634
11722
  const durationS = (Date.now() - started) / 1000;
11635
11723
  if (r.exitCode === 3) {
11636
11724
  checks.push({ name, cmd: "secret-scan.sh", result: "not_run", duration_s: durationS });
@@ -11651,10 +11739,10 @@ function commentArgv(prNumber, bodyFile) {
11651
11739
  return ["comment", prNumber, bodyFile];
11652
11740
  }
11653
11741
  function buildCommentBody(checks, notProven, statusState) {
11654
- const lines = [`## Loki deep verify: ${statusState}`, "", "### Checks"];
11655
- lines.push(...checks.length ? checks.map((c) => `- ${c.result}: ${c.name}${c.reason ? ` (${c.reason})` : ""}`) : ["- none"]);
11656
- lines.push("", "### NOT PROVEN", ...notProven.length ? notProven.map((n2) => `- ${n2}`) : ["- none"]);
11657
- return `${lines.join(`
11742
+ const lines2 = [`## Loki deep verify: ${statusState}`, "", "### Checks"];
11743
+ lines2.push(...checks.length ? checks.map((c) => `- ${c.result}: ${c.name}${c.reason ? ` (${c.reason})` : ""}`) : ["- none"]);
11744
+ lines2.push("", "### NOT PROVEN", ...notProven.length ? notProven.map((n2) => `- ${n2}`) : ["- none"]);
11745
+ return `${lines2.join(`
11658
11746
  `)}
11659
11747
  `;
11660
11748
  }
@@ -11671,7 +11759,7 @@ async function postResults(ctx, opts, checks, notProven, statusState) {
11671
11759
  const prNumber = prUrl ? PR_NUMBER_RE.exec(prUrl)?.[1] : undefined;
11672
11760
  if (prNumber) {
11673
11761
  mkdirSync18(ctx.runDir, { recursive: true });
11674
- const bodyFile = join48(ctx.runDir, "deep-comment.md");
11762
+ const bodyFile = join49(ctx.runDir, "deep-comment.md");
11675
11763
  writeFileSync20(bodyFile, buildCommentBody(checks, notProven, statusState), "utf8");
11676
11764
  const commentResult = await run(["bash", scriptPath, ...commentArgv(prNumber, bodyFile)], { cwd: ctx.repoDir, env, timeoutMs: 30000 });
11677
11765
  if (commentResult.exitCode !== 0)
@@ -11725,7 +11813,7 @@ async function runDeep(ctx, signal, opts = {}) {
11725
11813
  const sig = signReceipt(`${ctx.runId}-deep`, addendumSha256);
11726
11814
  const addendum = { ...addendumBody, addendum_sha256: addendumSha256, verification: { jwt: sig.jwt, kid: sig.kid } };
11727
11815
  mkdirSync18(ctx.runDir, { recursive: true });
11728
- writeFileSync20(join48(ctx.runDir, "receipt-addendum-1.json"), JSON.stringify(addendum, null, 2) + `
11816
+ writeFileSync20(join49(ctx.runDir, "receipt-addendum-1.json"), JSON.stringify(addendum, null, 2) + `
11729
11817
  `);
11730
11818
  const pushNotProven = await postResults(ctx, opts, checks, notProvenList, statusState);
11731
11819
  const allNotProven = [...notProvenList, ...pushNotProven];
@@ -11770,7 +11858,7 @@ var init_deep = __esm(() => {
11770
11858
  // ../../loki-ts/src/engine10/stages/verify.ts
11771
11859
  import { existsSync as existsSync29, mkdtempSync as mkdtempSync6, realpathSync as realpathSync17, rmSync as rmSync12 } from "fs";
11772
11860
  import { tmpdir as tmpdir8 } from "os";
11773
- import { dirname as dirname17, isAbsolute as isAbsolute6, join as join49, relative as relative5 } from "path";
11861
+ import { dirname as dirname17, isAbsolute as isAbsolute6, join as join50, relative as relative5 } from "path";
11774
11862
  function resolveTool(repoDir, bin, fallback, lastResort = fallback) {
11775
11863
  const inRepo = (p) => {
11776
11864
  try {
@@ -11781,7 +11869,7 @@ function resolveTool(repoDir, bin, fallback, lastResort = fallback) {
11781
11869
  }
11782
11870
  };
11783
11871
  const venvEnv = process.env["VIRTUAL_ENV"];
11784
- const project = [join49(repoDir, ".venv", "bin", bin), join49(repoDir, "venv", "bin", bin), ...venvEnv && inRepo(venvEnv) ? [join49(venvEnv, "bin", bin)] : []].find(existsSync29);
11872
+ const project = [join50(repoDir, ".venv", "bin", bin), join50(repoDir, "venv", "bin", bin), ...venvEnv && inRepo(venvEnv) ? [join50(venvEnv, "bin", bin)] : []].find(existsSync29);
11785
11873
  return project ? [project, "project"] : [Bun.which(fallback) ? fallback : lastResort, "system"];
11786
11874
  }
11787
11875
  function runnerCmd(t, repoDir) {
@@ -11811,13 +11899,6 @@ function commandFor(t, repoDir, api) {
11811
11899
  const [cmd, args, interpreter] = runnerCmd(site ? { runner: t.runner, path: site.file } : t, repoDir);
11812
11900
  return { cwd: site?.cwd ?? repoDir, cmd, args, argv: [cmd, ...args], ...interpreter ? { interpreter } : {}, pkgRoot: site?.root ?? "." };
11813
11901
  }
11814
- function testConfigChanged(repoDir, baseSha, changed) {
11815
- const diff = (f) => safeGit(repoDir, ["diff", "-U0", baseSha, "--", f]);
11816
- return changed.filter((f) => CFG_ALWAYS.test(f) || CFG_SHARED.test(f) && (() => {
11817
- const d = diff(f);
11818
- return !d || CFG_LINE.test(d);
11819
- })());
11820
- }
11821
11902
  async function runOnce(cmd, args, cwd, signal, opts) {
11822
11903
  if (!Bun.which(cmd, { PATH: opts.path ?? process.env["PATH"] ?? "" }))
11823
11904
  return { ok: false, missing: true, cut: false, out: "" };
@@ -11856,8 +11937,8 @@ function firstError(out2) {
11856
11937
  return l2;
11857
11938
  }
11858
11939
  };
11859
- const lines = out2.slice(-65536).split(`
11860
- `).map((l2) => (l2.startsWith("{") ? goOut(l2) : l2).trim()).filter(Boolean), l = lines.find((x) => /^(FAILED\s|\u25cf\s.*\u203a|not ok\s|_{3,}\s.+\s_{3,}$)/.test(x)) ?? lines.find((x) => /^[\w./-]+\.go:\d+:\d+: \S/.test(x)) ?? lines.find((x) => /fail|error/i.test(x) && !/^(=|\u2713|ok\b|PASS)/.test(x)) ?? "";
11940
+ const lines2 = out2.slice(-65536).split(`
11941
+ `).map((l2) => (l2.startsWith("{") ? goOut(l2) : l2).trim()).filter(Boolean), l = lines2.find((x) => /^(FAILED\s|\u25cf\s.*\u203a|not ok\s|_{3,}\s.+\s_{3,}$)/.test(x)) ?? lines2.find((x) => /^[\w./-]+\.go:\d+:\d+: \S/.test(x)) ?? lines2.find((x) => /fail|error/i.test(x) && !/^(=|\u2713|ok\b|PASS)/.test(x)) ?? "";
11861
11942
  return l.replace(/\d{4}-\d\d-\d\dT[\d:.]+Z?/g, "").replace(/(^|\s)\/(?:[\w.@-]+\/)*[\w.@-]+/g, "$1<path>").replace(/:\d+(?::\d+)?/g, "").replace(/\[?\d+(?:\.\d+)?m?s\]?/g, "").replace(/\s+/g, " ").slice(0, 160);
11862
11943
  }
11863
11944
  async function runCheck(ctx, name, cmd, args, signal, checks, opts = {}) {
@@ -11899,7 +11980,7 @@ async function subtractBase(ctx, api, checks, tests, changed, wall, rel, signal)
11899
11980
  const sus = pairs.filter(({ c }) => rel.has(c.name) && c.result === "pass");
11900
11981
  if (!(un.some(({ c }) => c.result === "fail" && c.ids?.length) || sus.length) || signal.aborted)
11901
11982
  return out2;
11902
- const dir = mkdtempSync6(join49(tmpdir8(), "e10-base-"));
11983
+ const dir = mkdtempSync6(join50(tmpdir8(), "e10-base-"));
11903
11984
  const git3 = (args) => {
11904
11985
  safeGit(ctx.repoDir, args, { stdio: "ignore", repoDrivers: true });
11905
11986
  };
@@ -11934,7 +12015,7 @@ async function subtractBase(ctx, api, checks, tests, changed, wall, rel, signal)
11934
12015
  return out2;
11935
12016
  }
11936
12017
  function isLokiModeRepo(repoDir) {
11937
- return existsSync29(join49(repoDir, "scripts", "select-tests.sh"));
12018
+ return existsSync29(join50(repoDir, "scripts", "select-tests.sh"));
11938
12019
  }
11939
12020
  async function runLintChecks(ctx, changed, signal, checks, opts = {}) {
11940
12021
  const { api, ...runOpts } = opts;
@@ -11951,15 +12032,16 @@ async function runLintChecks(ctx, changed, signal, checks, opts = {}) {
11951
12032
  }
11952
12033
  for (const g of groupByPackage(api, ctx.repoDir, changed.filter((f) => /\.(ts|tsx|js|jsx)$/.test(f)))) {
11953
12034
  const sfx = g.root === "." ? "" : `:${g.root}`, co = g.root === "." ? opts : { ...opts, cwd: g.cwd };
11954
- if (existsSync29(join49(g.cwd, "tsconfig.json")))
12035
+ if (existsSync29(join50(g.cwd, "tsconfig.json")))
11955
12036
  await runCheck(ctx, `lint:tsc${sfx}`, "npx", ["tsc", "--noEmit", "-p", "."], signal, checks, { ...co, kind: "static" });
11956
- if (ESLINT_CONFIGS.some((f) => existsSync29(join49(g.cwd, f))))
12037
+ if (ESLINT_CONFIGS.some((f) => existsSync29(join50(g.cwd, f))))
11957
12038
  await runCheck(ctx, `lint:eslint${sfx}`, "npx", ["eslint", ...g.rel], signal, checks, { ...co, kind: "static" });
11958
12039
  }
11959
12040
  }
11960
- var CHECK_TIMEOUT_MS = 60000, dedupeTests = (tests) => [...new Map(tests.map((t) => [`${t.runner}:${t.path}`, t])).values()], CODE_EXT, CFG_ALWAYS, CFG_SHARED, CFG_LINE, ESLINT_CONFIGS, verifyStage;
12041
+ var CHECK_TIMEOUT_MS = 60000, dedupeTests = (tests) => [...new Map(tests.map((t) => [`${t.runner}:${t.path}`, t])).values()], CODE_EXT, ESLINT_CONFIGS, verifyStage;
11961
12042
  var init_verify = __esm(() => {
11962
12043
  init_scope();
12044
+ init_runner_config();
11963
12045
  init_assert_delta();
11964
12046
  init_failures();
11965
12047
  init_load_owner();
@@ -11973,10 +12055,8 @@ var init_verify = __esm(() => {
11973
12055
  init_repo_profile();
11974
12056
  init_check_result();
11975
12057
  init_check_result();
12058
+ init_runner_config();
11976
12059
  CODE_EXT = /\.(?:[cm]?[jt]sx?|py|go)$/;
11977
- CFG_ALWAYS = /(^|\/)(conftest\.py|\.?pytest\.(?:ini|toml)|tox\.ini|jest\.config\.[\w.]+|vitest\.config\.[\w.]+|\.mocharc[\w.]*)$/;
11978
- CFG_SHARED = /(^|\/)(setup\.cfg|pyproject\.toml|package\.json)$/;
11979
- CFG_LINE = /^[+-].*(pytest|jest|mocha|vitest|"test"\s*:|addopts|testpaths)/im;
11980
12060
  ESLINT_CONFIGS = [".eslintrc", ".eslintrc.json", ".eslintrc.js", ".eslintrc.cjs", "eslint.config.js", "eslint.config.mjs", "eslint.config.cjs"];
11981
12061
  verifyStage = {
11982
12062
  name: "verify",
@@ -12013,8 +12093,8 @@ var init_verify = __esm(() => {
12013
12093
  const wall = ctx.outputs().wall ?? {};
12014
12094
  const wallPaths = new Set((wall.files ?? []).map((f) => isAbsolute6(f.path) ? relative5(ctx.repoDir, f.path) : f.path));
12015
12095
  const wallTests = map.tests.filter((t) => wallPaths.has(t.path));
12016
- const intake = ctx.outputs().intake, relevant = ctx.tests.impacted(map, namedFiles(intake?.task ?? "", loadRepoMap(intake?.repomap_ref)));
12017
- const tests = dedupeTests([...impacted, ...changedTestFiles, ...wallTests, ...relevant]);
12096
+ const intake = ctx.outputs().intake, relevant2 = ctx.tests.impacted(map, namedFiles(intake?.task ?? "", loadRepoMap(intake?.repomap_ref)));
12097
+ const tests = dedupeTests([...impacted, ...changedTestFiles, ...wallTests, ...relevant2]);
12018
12098
  const scopedOut = scopedOutOf(map);
12019
12099
  if (scopedOut.length)
12020
12100
  ctx.emit("test.scoped_out", "verify", { tests: scopedOut });
@@ -12022,10 +12102,10 @@ var init_verify = __esm(() => {
12022
12102
  if (signal.aborted)
12023
12103
  break;
12024
12104
  const tc = commandFor(t, ctx.repoDir, api);
12025
- await runCheck(ctx, `${t.runner}:${t.path}`, tc.cmd, tc.args, signal, checks, { ...tc.interpreter ? { interpreter: tc.interpreter } : {}, protect: [...wallTests, ...relevant].includes(t), ...tc.pkgRoot !== "." ? { cwd: tc.cwd } : {} });
12105
+ await runCheck(ctx, `${t.runner}:${t.path}`, tc.cmd, tc.args, signal, checks, { ...tc.interpreter ? { interpreter: tc.interpreter } : {}, protect: [...wallTests, ...relevant2].includes(t), ...tc.pkgRoot !== "." ? { cwd: tc.cwd } : {} });
12026
12106
  }
12027
- const targetNames = new Set([...wallTests, ...relevant].map((t) => `${t.runner}:${t.path}`));
12028
- const preRed = await subtractBase(ctx, api, checks, tests, changed, new Set(wallTests.map((t) => `${t.runner}:${t.path}`)), new Set(relevant.map((t) => `${t.runner}:${t.path}`)), signal);
12107
+ const targetNames = new Set([...wallTests, ...relevant2].map((t) => `${t.runner}:${t.path}`));
12108
+ const preRed = await subtractBase(ctx, api, checks, tests, changed, new Set(wallTests.map((t) => `${t.runner}:${t.path}`)), new Set(relevant2.map((t) => `${t.runner}:${t.path}`)), signal);
12029
12109
  const attr = attribute(ctx.repoDir, ctx.baseSha, checks.filter((c) => c.result === "fail" && !preRed.names.includes(c.name)).map((c) => ({ name: c.name, ids: c.ids, eligible: !targetNames.has(c.name) && !changed.includes(c.name.slice(c.name.indexOf(":") + 1)) })));
12030
12110
  preRed.names.push(...attr.preExistingChecks);
12031
12111
  preRed.ids.push(...attr.preExistingIds);
@@ -12071,7 +12151,7 @@ var init_verify = __esm(() => {
12071
12151
  }
12072
12152
  };
12073
12153
  const testCounts = {};
12074
- const modifiedRel = relevant.filter((t) => changed.includes(t.path) && inBase(t.path)).flatMap((t) => {
12154
+ const modifiedRel = relevant2.filter((t) => changed.includes(t.path) && inBase(t.path)).flatMap((t) => {
12075
12155
  const k = preRed.cnt[`${t.runner}:${t.path}`];
12076
12156
  if (k)
12077
12157
  testCounts[t.path] = k;
@@ -12085,8 +12165,8 @@ var init_verify = __esm(() => {
12085
12165
  });
12086
12166
 
12087
12167
  // ../../loki-ts/src/project_model/conventions.ts
12088
- import { existsSync as existsSync30, readdirSync as readdirSync13, readFileSync as readFileSync35 } from "fs";
12089
- import { dirname as dirname18, join as join50 } from "path";
12168
+ import { existsSync as existsSync30, readdirSync as readdirSync14, readFileSync as readFileSync36 } from "fs";
12169
+ import { dirname as dirname18, join as join51 } from "path";
12090
12170
  function parseJsonc(text2) {
12091
12171
  let out2 = "", i = 0;
12092
12172
  while (i < text2.length) {
@@ -12121,25 +12201,25 @@ function moduleOf(file, depth = 0) {
12121
12201
  const ext = typeof j.extends === "string" ? j.extends : null;
12122
12202
  if (!ext || !ext.startsWith("."))
12123
12203
  return null;
12124
- const next = join50(dirname18(file), ext.endsWith(".json") ? ext : `${ext}.json`);
12204
+ const next = join51(dirname18(file), ext.endsWith(".json") ? ext : `${ext}.json`);
12125
12205
  return existsSync30(next) ? moduleOf(next, depth + 1) : null;
12126
12206
  }
12127
12207
  function readPackageConventions(pkgDir, runner = null) {
12128
12208
  let names = [];
12129
12209
  try {
12130
- names = readdirSync13(pkgDir).filter((f) => f.endsWith(".json")).sort((x, y) => x.length - y.length || x.localeCompare(y));
12210
+ names = readdirSync14(pkgDir).filter((f) => f.endsWith(".json")).sort((x, y) => x.length - y.length || x.localeCompare(y));
12131
12211
  } catch {
12132
12212
  return null;
12133
12213
  }
12134
12214
  let packageType = null, tsModule = null;
12135
12215
  for (const n2 of names) {
12136
- const j = readJson4(join50(pkgDir, n2));
12216
+ const j = readJson4(join51(pkgDir, n2));
12137
12217
  if (!j)
12138
12218
  continue;
12139
12219
  if (packageType === null && typeof j.type === "string" && typeof j.name === "string")
12140
12220
  packageType = j.type;
12141
12221
  if (tsModule === null && j.compilerOptions !== undefined)
12142
- tsModule = moduleOf(join50(pkgDir, n2));
12222
+ tsModule = moduleOf(join51(pkgDir, n2));
12143
12223
  }
12144
12224
  if (packageType === null && tsModule === null)
12145
12225
  return null;
@@ -12161,7 +12241,7 @@ function conventionViolation(c, fileName, content) {
12161
12241
  }
12162
12242
  var readJson4 = (p) => {
12163
12243
  try {
12164
- const v = parseJsonc(readFileSync35(p, "utf8"));
12244
+ const v = parseJsonc(readFileSync36(p, "utf8"));
12165
12245
  return v && typeof v === "object" && !Array.isArray(v) ? v : null;
12166
12246
  } catch {
12167
12247
  return null;
@@ -12171,15 +12251,15 @@ var init_conventions = () => {};
12171
12251
 
12172
12252
  // ../../loki-ts/src/engine10/stages/wall.ts
12173
12253
  import { spawnSync as spawnSync4 } from "child_process";
12174
- import { existsSync as existsSync31, mkdirSync as mkdirSync19, mkdtempSync as mkdtempSync7, readFileSync as readFileSync36, readdirSync as readdirSync14, realpathSync as realpathSync18, rmSync as rmSync13, writeFileSync as writeFileSync21 } from "fs";
12254
+ import { existsSync as existsSync31, mkdirSync as mkdirSync19, mkdtempSync as mkdtempSync7, readFileSync as readFileSync37, readdirSync as readdirSync15, realpathSync as realpathSync18, rmSync as rmSync13, writeFileSync as writeFileSync21 } from "fs";
12175
12255
  import { tmpdir as tmpdir9 } from "os";
12176
- import { dirname as dirname19, isAbsolute as isAbsolute7, join as join51, relative as relative6 } from "path";
12256
+ import { dirname as dirname19, isAbsolute as isAbsolute7, join as join52, relative as relative6 } from "path";
12177
12257
  function pytestCollectionIsRed(output, repoDir) {
12178
12258
  if (/ModuleNotFoundError/.test(output))
12179
12259
  return false;
12180
12260
  const real2 = (p) => {
12181
12261
  try {
12182
- return realpathSync18(isAbsolute7(p) ? p : join51(repoDir, p));
12262
+ return realpathSync18(isAbsolute7(p) ? p : join52(repoDir, p));
12183
12263
  } catch {
12184
12264
  return null;
12185
12265
  }
@@ -12216,7 +12296,7 @@ function pytestCollectionIsRed(output, repoDir) {
12216
12296
  }
12217
12297
  function moduleUnderRepo(repoDir, name) {
12218
12298
  const rel = name.replace(/\./g, "/");
12219
- return existsSync31(join51(repoDir, `${rel}.py`)) || existsSync31(join51(repoDir, rel, "__init__.py"));
12299
+ return existsSync31(join52(repoDir, `${rel}.py`)) || existsSync31(join52(repoDir, rel, "__init__.py"));
12220
12300
  }
12221
12301
  function pytestExit1IsRed(output) {
12222
12302
  if (/^!+ _pytest\.outcomes\.Exit\b/m.test(output))
@@ -12314,10 +12394,10 @@ ${acceptance.map((a) => `Acceptance: ${a}`).join(`
12314
12394
  function loadTaskText(ctx, fromPrior) {
12315
12395
  if (fromPrior)
12316
12396
  return fromPrior;
12317
- const issueJsonPath = process.env.LOKI_E10_ISSUE_JSON ?? join51(ctx.runDir, "issue.json");
12397
+ const issueJsonPath = process.env.LOKI_E10_ISSUE_JSON ?? join52(ctx.runDir, "issue.json");
12318
12398
  if (existsSync31(issueJsonPath)) {
12319
12399
  try {
12320
- const issue = JSON.parse(readFileSync36(issueJsonPath, "utf8"));
12400
+ const issue = JSON.parse(readFileSync37(issueJsonPath, "utf8"));
12321
12401
  const text2 = [issue.title, issue.body].filter((s) => typeof s === "string" && s.length > 0).join(`
12322
12402
 
12323
12403
  `);
@@ -12329,7 +12409,7 @@ function loadTaskText(ctx, fromPrior) {
12329
12409
  }
12330
12410
  function wallTargetDir(repoDir, existingTests) {
12331
12411
  const first = existingTests[0];
12332
- return first ? join51(repoDir, dirname19(first.path)) : join51(repoDir, "tests");
12412
+ return first ? join52(repoDir, dirname19(first.path)) : join52(repoDir, "tests");
12333
12413
  }
12334
12414
  function guessRunner(fileName, runners) {
12335
12415
  if (fileName.endsWith(".py"))
@@ -12360,11 +12440,11 @@ async function wallAuthor(ctx, signal) {
12360
12440
  const tree = prior.intake?.tree;
12361
12441
  const repomapText = repoMapText(ctx.repoDir, tree, repomapRef, WALL_MAP_MAX_LINES);
12362
12442
  const targetDir = wallTargetDir(ctx.repoDir, existingTests), pkg = loadProjectApi(ctx.repoDir)?.packageOf(`${relative6(ctx.repoDir, targetDir)}/x`);
12363
- const conv2 = readPackageConventions(join51(ctx.repoDir, pkg?.root ?? "."), pkg?.runner ?? null);
12364
- const cwd = mkdtempSync7(join51(tmpdir9(), "loki-e15-wall-"));
12443
+ const conv2 = readPackageConventions(join52(ctx.repoDir, pkg?.root ?? "."), pkg?.runner ?? null);
12444
+ const cwd = mkdtempSync7(join52(tmpdir9(), "loki-e15-wall-"));
12365
12445
  const wm = wallManifestFor(ctx.repoDir, tree, task);
12366
12446
  for (const [n2, c] of [["task.md", task], ["repomap.txt", repomapText], ...wm ? [["wall_manifest.txt", wm.text]] : []])
12367
- writeFileSync21(join51(cwd, n2), c, "utf8");
12447
+ writeFileSync21(join52(cwd, n2), c, "utf8");
12368
12448
  const session = await ctx.sessions.run({
12369
12449
  stage: "wall",
12370
12450
  brief: buildWallBrief(task, repomapText, runners, !!wm, conventionsBrief(conv2), unitSpec()?.acceptance ?? []),
@@ -12379,15 +12459,15 @@ async function wallAuthor(ctx, signal) {
12379
12459
  rmSync13(cwd, { recursive: true, force: true });
12380
12460
  return done({ status: "failed", data: {}, reason: "wall session aborted, killed, or timed out", killed: true });
12381
12461
  }
12382
- const generated = readdirSync14(cwd).filter((f) => f.startsWith(WALL_PREFIX));
12383
- const sealedDir = join51(ctx.runDir, "wall");
12462
+ const generated = readdirSync15(cwd).filter((f) => f.startsWith(WALL_PREFIX));
12463
+ const sealedDir = join52(ctx.runDir, "wall");
12384
12464
  if (generated.length > 0)
12385
12465
  mkdirSync19(sealedDir, { recursive: true });
12386
12466
  const contents = new Map;
12387
12467
  const discarded = [];
12388
12468
  for (const name of generated) {
12389
- const content = readFileSync36(join51(cwd, name), "utf8"), bad = conventionViolation(conv2, name, content);
12390
- writeFileSync21(join51(sealedDir, name), content, "utf8");
12469
+ const content = readFileSync37(join52(cwd, name), "utf8"), bad = conventionViolation(conv2, name, content);
12470
+ writeFileSync21(join52(sealedDir, name), content, "utf8");
12391
12471
  if (bad)
12392
12472
  discarded.push({ file: name, reason: `${bad}; wall test did not compile under the package config` });
12393
12473
  else
@@ -12407,7 +12487,7 @@ function installWall(ctx, a, baseDir, opts = {}) {
12407
12487
  const content = contents.get(name);
12408
12488
  if (content === undefined)
12409
12489
  continue;
12410
- const dest = join51(targetDir, name), runner = guessRunner(name, runners);
12490
+ const dest = join52(targetDir, name), runner = guessRunner(name, runners);
12411
12491
  writeFileSync21(dest, content, "utf8");
12412
12492
  sealedFiles.push({ path: dest, sha256: sha2562(content) });
12413
12493
  readOnlyFiles.push({ path: dest, content });
@@ -12417,7 +12497,7 @@ function installWall(ctx, a, baseDir, opts = {}) {
12417
12497
  ctx.emit("wall.sealed", "wall", { files: sealedFiles, ...a.manifestSha256 ? { manifest_sha256: a.manifestSha256 } : {} });
12418
12498
  const baseRunner = opts.baseRunner ?? new RealBaseTestRunner, baseRun = { pass: 0, fail: 0, not_run: discarded.length }, notRunFiles = [];
12419
12499
  for (const t of wallTests) {
12420
- const r = baseRunner.run(baseDir, [t]), abs = join51(ctx.repoDir, t.path);
12500
+ const r = baseRunner.run(baseDir, [t]), abs = join52(ctx.repoDir, t.path);
12421
12501
  baseRun.pass += r.pass;
12422
12502
  baseRun.fail += r.fail;
12423
12503
  baseRun.not_run += r.not_run ?? 0;
@@ -12469,15 +12549,15 @@ var init_wall = __esm(() => {
12469
12549
  });
12470
12550
 
12471
12551
  // ../../loki-ts/src/e10ext/discard.ts
12472
- import { chmodSync as chmodSync5, closeSync as closeSync8, lstatSync as lstatSync10, mkdirSync as mkdirSync20, openSync as openSync8, readFileSync as readFileSync37, realpathSync as realpathSync19, rmdirSync, unlinkSync as unlinkSync5, writeFileSync as writeFileSync22 } from "fs";
12473
- import { dirname as dirname20, join as join52, relative as relative7, sep as sep11 } from "path";
12552
+ import { chmodSync as chmodSync5, closeSync as closeSync8, lstatSync as lstatSync10, mkdirSync as mkdirSync20, openSync as openSync8, readFileSync as readFileSync38, realpathSync as realpathSync19, rmdirSync, unlinkSync as unlinkSync5, writeFileSync as writeFileSync22 } from "fs";
12553
+ import { dirname as dirname20, join as join53, relative as relative7, sep as sep11 } from "path";
12474
12554
  function safeRestore(repoDir, f, want, mode) {
12475
- const root = realpathSync19(repoDir), fp = join52(root, f), rel = relative7(root, dirname20(fp));
12555
+ const root = realpathSync19(repoDir), fp = join53(root, f), rel = relative7(root, dirname20(fp));
12476
12556
  if (rel.startsWith(".."))
12477
12557
  throw new Error("outside repo");
12478
12558
  let cur = root;
12479
12559
  for (const part of rel === "" ? [] : rel.split(sep11)) {
12480
- cur = join52(cur, part);
12560
+ cur = join53(cur, part);
12481
12561
  let st2;
12482
12562
  try {
12483
12563
  st2 = lstatSync10(cur);
@@ -12494,7 +12574,7 @@ function safeRestore(repoDir, f, want, mode) {
12494
12574
  } catch {
12495
12575
  st = null;
12496
12576
  }
12497
- if (st?.isFile() && readFileSync37(fp).equals(want)) {
12577
+ if (st?.isFile() && readFileSync38(fp).equals(want)) {
12498
12578
  chmodSync5(fp, mode);
12499
12579
  return;
12500
12580
  }
@@ -12532,7 +12612,7 @@ async function discardIfSatisfied(git3, base, o, staged, keep, repoDir) {
12532
12612
  return { status: "failed", data: {}, reason: "already-satisfied discard failed" };
12533
12613
  const notProven = [];
12534
12614
  for (const [f, v] of Object.entries(o.intake?.preexisting_untracked_blobs ?? {})) {
12535
- const fp = join52(repoDir, f);
12615
+ const fp = join53(repoDir, f);
12536
12616
  try {
12537
12617
  if (v.startsWith("!")) {
12538
12618
  const st = lstatSync10(fp);
@@ -12551,7 +12631,7 @@ async function discardIfSatisfied(git3, base, o, staged, keep, repoDir) {
12551
12631
  try {
12552
12632
  let m = 420;
12553
12633
  try {
12554
- const l = lstatSync10(join52(repoDir, f));
12634
+ const l = lstatSync10(join53(repoDir, f));
12555
12635
  if (l.isFile())
12556
12636
  m = l.mode & 511;
12557
12637
  } catch {}
@@ -12627,9 +12707,9 @@ var init_scope2 = __esm(() => {
12627
12707
  // ../../loki-ts/src/features/visual_evidence.ts
12628
12708
  import { spawn as spawn7 } from "child_process";
12629
12709
  import { createHash as createHash12, randomBytes as randomBytes4 } from "crypto";
12630
- import { existsSync as existsSync32, lstatSync as lstatSync11, mkdirSync as mkdirSync21, readdirSync as readdirSync15, readFileSync as readFileSync38, writeFileSync as writeFileSync23 } from "fs";
12710
+ import { existsSync as existsSync32, lstatSync as lstatSync11, mkdirSync as mkdirSync21, readdirSync as readdirSync16, readFileSync as readFileSync39, writeFileSync as writeFileSync23 } from "fs";
12631
12711
  import { createServer } from "net";
12632
- import { dirname as dirname23, isAbsolute as isAbsolute8, join as join53, normalize as normalize2, relative as relative8, sep as sep12 } from "path";
12712
+ import { dirname as dirname23, isAbsolute as isAbsolute8, join as join54, normalize as normalize2, relative as relative8, sep as sep12 } from "path";
12633
12713
  function routeFor(file) {
12634
12714
  let r = file.replace(/^(?:.*?\/)?(?:app|pages|src|public)\//, "").replace(/^(?:routes|pages)\//, "");
12635
12715
  r = r.replace(/\.(?:html|jsx|tsx|vue|svelte)$/, "").replace(/\/?(?:page|index|\+page)$/, "");
@@ -12639,7 +12719,7 @@ function hashScreens(runDir, rels) {
12639
12719
  const out2 = [];
12640
12720
  for (const rel of rels) {
12641
12721
  try {
12642
- out2.push({ path: rel, sha256: sha256File(join53(runDir, rel)) });
12722
+ out2.push({ path: rel, sha256: sha256File(join54(runDir, rel)) });
12643
12723
  } catch {}
12644
12724
  }
12645
12725
  return out2;
@@ -12652,10 +12732,10 @@ function checkScreens(runDir, screens) {
12652
12732
  return "evidence_screens entry is malformed";
12653
12733
  if (isAbsolute8(s.path) || normalize2(s.path).startsWith(".."))
12654
12734
  return `evidence screenshot path escapes the evidence dir: ${s.path}`;
12655
- const file = join53(runDir, s.path);
12735
+ const file = join54(runDir, s.path);
12656
12736
  let cur = runDir;
12657
12737
  for (const seg of normalize2(s.path).split(sep12)) {
12658
- cur = join53(cur, seg);
12738
+ cur = join54(cur, seg);
12659
12739
  if (isSymlink(cur))
12660
12740
  return `evidence screenshot path is a symlink: ${s.path}`;
12661
12741
  }
@@ -12702,11 +12782,11 @@ async function waitUp(url, ms, signal) {
12702
12782
  }
12703
12783
  function openapiPaths(repoDir) {
12704
12784
  for (const f of ["openapi.json", "openapi.yaml", "openapi.yml", "swagger.json"]) {
12705
- const p = join53(repoDir, f);
12785
+ const p = join54(repoDir, f);
12706
12786
  if (!existsSync32(p))
12707
12787
  continue;
12708
12788
  try {
12709
- const txt = readFileSync38(p, "utf8");
12789
+ const txt = readFileSync39(p, "utf8");
12710
12790
  const paths = f.endsWith(".json") ? Object.keys(JSON.parse(txt).paths ?? {}) : [...txt.matchAll(/^ {2}(\/[^\s:]*):\s*$/gm)].map((m) => m[1]);
12711
12791
  return paths.filter((x) => !x.includes("{")).slice(0, 10);
12712
12792
  } catch {
@@ -12720,24 +12800,24 @@ async function recordE2eMedia(repoDir, runDir, relDir, base, routes, ms, signal)
12720
12800
  if (!pw || !pkg)
12721
12801
  return { rels: [], skipped: "playwright e2e video and trace skipped: @playwright/test is not installed in the repo" };
12722
12802
  try {
12723
- const dir = join53(runDir, relDir, "e2e");
12803
+ const dir = join54(runDir, relDir, "e2e");
12724
12804
  mkdirSync21(dir, { recursive: true });
12725
- writeFileSync23(join53(dir, "walk.spec.cjs"), e2eSpecSource(pkg, base, routes));
12726
- writeFileSync23(join53(dir, "playwright.config.cjs"), `module.exports = { testDir: ".", testMatch: "walk.spec.cjs", outputDir: "./out", reporter: "null", use: { video: "on", trace: "on" } };
12805
+ writeFileSync23(join54(dir, "walk.spec.cjs"), e2eSpecSource(pkg, base, routes));
12806
+ writeFileSync23(join54(dir, "playwright.config.cjs"), `module.exports = { testDir: ".", testMatch: "walk.spec.cjs", outputDir: "./out", reporter: "null", use: { video: "on", trace: "on" } };
12727
12807
  `);
12728
- if (!await shoot(pw, ["test", "--config", join53(dir, "playwright.config.cjs")], repoDir, ms, signal))
12808
+ if (!await shoot(pw, ["test", "--config", join54(dir, "playwright.config.cjs")], repoDir, ms, signal))
12729
12809
  return { rels: [], skipped: "playwright e2e video and trace skipped: the run failed or timed out" };
12730
12810
  const rels = [];
12731
12811
  const walk = (d) => {
12732
- for (const e of readdirSync15(d, { withFileTypes: true })) {
12733
- const full = join53(d, e.name);
12812
+ for (const e of readdirSync16(d, { withFileTypes: true })) {
12813
+ const full = join54(d, e.name);
12734
12814
  if (e.isDirectory())
12735
12815
  walk(full);
12736
12816
  else if (e.isFile() && /\.(webm|zip)$/.test(e.name))
12737
- rels.push(join53(relDir, "e2e", relative8(dir, full)));
12817
+ rels.push(join54(relDir, "e2e", relative8(dir, full)));
12738
12818
  }
12739
12819
  };
12740
- walk(join53(dir, "out"));
12820
+ walk(join54(dir, "out"));
12741
12821
  return rels.length > 0 ? { rels, skipped: null } : { rels, skipped: "playwright e2e video and trace skipped: no video or trace was produced" };
12742
12822
  } catch (e) {
12743
12823
  return { rels: [], skipped: `playwright e2e video and trace skipped: ${String(e?.message ?? e).slice(0, 120)}` };
@@ -12797,6 +12877,16 @@ async function stopServer(c, graceMs, signal) {
12797
12877
  for (let i = 0;i < 10 && groupAlive(pid); i++)
12798
12878
  await new Promise((r) => setTimeout(r, 50));
12799
12879
  }
12880
+ function noRootReason(subs, lacking) {
12881
+ const list = (xs) => xs.map((d) => `${d}/`).join(" or ");
12882
+ if (subs.length === 0)
12883
+ return "no package.json";
12884
+ if (lacking.length === subs.length)
12885
+ return `no dev, preview or start script in ${list(lacking)} (no root package.json)`;
12886
+ if (lacking.length === 0)
12887
+ return `no root package.json (${list(subs)} have a dev, preview or start script but no UI boot was resolved)`;
12888
+ return `no root package.json; no dev, preview or start script in ${list(lacking)} (${list(subs.filter((d) => !lacking.includes(d)))} has one)`;
12889
+ }
12800
12890
  async function captureVisualEvidence(repoDir, runDir, changed, opts = {}) {
12801
12891
  const skip = (why) => ({ screens: [], http: false, skipped: why });
12802
12892
  const signal = opts.signal;
@@ -12806,13 +12896,22 @@ async function captureVisualEvidence(repoDir, runDir, changed, opts = {}) {
12806
12896
  if (!visualEvidenceEnabled(opts.env))
12807
12897
  return skip("LOKI_VISUAL_EVIDENCE is 0");
12808
12898
  const api = loadProjectApi(repoDir), boot = isMultiRoot(api) ? api.uiBoot() : null;
12809
- const appDir = boot ? join53(repoDir, boot.boot.cwd) : repoDir;
12810
- const pkgPath = join53(appDir, "package.json");
12899
+ const appDir = boot ? join54(repoDir, boot.boot.cwd) : repoDir;
12900
+ const pkgPath = join54(appDir, "package.json");
12811
12901
  let script = null;
12812
12902
  if (!boot) {
12813
- if (!existsSync32(pkgPath))
12814
- return skip("no package.json");
12815
- script = pickScript(JSON.parse(readFileSync38(pkgPath, "utf8")));
12903
+ if (!existsSync32(pkgPath)) {
12904
+ const subs = ["backend", "frontend"].filter((d) => existsSync32(join54(repoDir, d, "package.json")));
12905
+ const has = (d) => {
12906
+ try {
12907
+ return pickScript(JSON.parse(readFileSync39(join54(repoDir, d, "package.json"), "utf8"))) !== null;
12908
+ } catch {
12909
+ return false;
12910
+ }
12911
+ };
12912
+ return skip(noRootReason(subs, subs.filter((d) => !has(d))));
12913
+ }
12914
+ script = pickScript(JSON.parse(readFileSync39(pkgPath, "utf8")));
12816
12915
  if (!script)
12817
12916
  return skip("no dev, preview or start script");
12818
12917
  }
@@ -12839,20 +12938,20 @@ async function captureVisualEvidence(repoDir, runDir, changed, opts = {}) {
12839
12938
  }
12840
12939
  if (!await waitUp(base, left(), signal))
12841
12940
  return skip(aborted() ? "aborted" : `${script} server did not answer within ${Math.round(budget / 1000)}s`);
12842
- const evRoot = join53(runDir, "evidence");
12941
+ const evRoot = join54(runDir, "evidence");
12843
12942
  if (isSymlink(evRoot))
12844
12943
  return skip("evidence dir is a symlink");
12845
12944
  mkdirSync21(evRoot, { recursive: true });
12846
12945
  if (pages.length > 0) {
12847
- const rel0 = join53("evidence", randomBytes4(6).toString("hex"));
12848
- mkdirSync21(join53(runDir, rel0));
12946
+ const rel0 = join54("evidence", randomBytes4(6).toString("hex"));
12947
+ mkdirSync21(join54(runDir, rel0));
12849
12948
  const rels = [];
12850
12949
  for (const route of [...new Set(pages.map(routeFor))].slice(0, opts.maxRoutes ?? MAX_ROUTES)) {
12851
12950
  if (aborted() || left() <= 0)
12852
12951
  break;
12853
- const rel = join53(rel0, `${screenName(route)}.png`);
12854
- const ok = await shoot(pw, ["screenshot", `${base}${route}`, join53(runDir, rel)], appDir, left(), signal);
12855
- if (ok && !aborted() && !isSymlink(join53(runDir, rel)) && existsSync32(join53(runDir, rel)))
12952
+ const rel = join54(rel0, `${screenName(route)}.png`);
12953
+ const ok = await shoot(pw, ["screenshot", `${base}${route}`, join54(runDir, rel)], appDir, left(), signal);
12954
+ if (ok && !aborted() && !isSymlink(join54(runDir, rel)) && existsSync32(join54(runDir, rel)))
12856
12955
  rels.push(rel);
12857
12956
  }
12858
12957
  if (aborted())
@@ -12877,9 +12976,9 @@ async function captureVisualEvidence(repoDir, runDir, changed, opts = {}) {
12877
12976
  }
12878
12977
  if (aborted())
12879
12978
  return skip("aborted");
12880
- const hdir = join53(evRoot, randomBytes4(6).toString("hex"));
12979
+ const hdir = join54(evRoot, randomBytes4(6).toString("hex"));
12881
12980
  mkdirSync21(hdir);
12882
- writeFileSync23(join53(hdir, "http.json"), JSON.stringify(rows, null, 2) + `
12981
+ writeFileSync23(join54(hdir, "http.json"), JSON.stringify(rows, null, 2) + `
12883
12982
  `);
12884
12983
  return { screens: [], http: true, skipped: null };
12885
12984
  } catch (e) {
@@ -12904,12 +13003,12 @@ async function sealEvidence(repoDir, runDir, o, notProven, signal, emit) {
12904
13003
  function receiptScreensProblem(receiptPath, receipt) {
12905
13004
  return receipt["evidence_screens"] === undefined ? null : checkScreens(dirname23(receiptPath), receipt["evidence_screens"]);
12906
13005
  }
12907
- var visualEvidenceEnabled = (env = process.env) => env["LOKI_VISUAL_EVIDENCE"] !== "0", PAGE_RE, isPageFile = (p) => PAGE_RE.test(p), screenName = (route) => (route === "/" ? "index" : route.slice(1).replace(/[^A-Za-z0-9._-]+/g, "_")) || "index", sha256File = (p) => createHash12("sha256").update(readFileSync38(p)).digest("hex"), playwrightBin = (repoDir) => {
12908
- const bin = join53(repoDir, "node_modules", ".bin", "playwright");
13006
+ var visualEvidenceEnabled = (env = process.env) => env["LOKI_VISUAL_EVIDENCE"] !== "0", PAGE_RE, isPageFile = (p) => PAGE_RE.test(p), screenName = (route) => (route === "/" ? "index" : route.slice(1).replace(/[^A-Za-z0-9._-]+/g, "_")) || "index", sha256File = (p) => createHash12("sha256").update(readFileSync39(p)).digest("hex"), playwrightBin = (repoDir) => {
13007
+ const bin = join54(repoDir, "node_modules", ".bin", "playwright");
12909
13008
  return existsSync32(bin) ? bin : null;
12910
13009
  }, playwrightTestPkg = (repoDir) => {
12911
- const d = join53(repoDir, "node_modules", "@playwright", "test");
12912
- return existsSync32(join53(d, "package.json")) ? d : null;
13010
+ const d = join54(repoDir, "node_modules", "@playwright", "test");
13011
+ return existsSync32(join54(d, "package.json")) ? d : null;
12913
13012
  }, e2eSpecSource = (pkgDir, base, routes) => `const { test } = require(${JSON.stringify(pkgDir)});
12914
13013
  test("loki e2e walkthrough", async ({ page }) => {
12915
13014
  for (const r of ${JSON.stringify(routes)}) { await page.goto(${JSON.stringify(base)} + r); await page.waitForLoadState("load"); }
@@ -12940,14 +13039,14 @@ var init_visual_evidence = __esm(() => {
12940
13039
  });
12941
13040
 
12942
13041
  // ../../loki-ts/src/council/role_agents.ts
12943
- import { existsSync as existsSync33, readdirSync as readdirSync16, readFileSync as readFileSync39 } from "fs";
13042
+ import { existsSync as existsSync33, readdirSync as readdirSync17, readFileSync as readFileSync40 } from "fs";
12944
13043
  import { homedir as homedir9 } from "os";
12945
- import { join as join54 } from "path";
13044
+ import { join as join55 } from "path";
12946
13045
  function agentDirs(repoDir, env = process.env) {
12947
- return [env["LOKI_AGENTS_PROJECT_DIR"] || join54(repoDir, ".loki", "agents"), env["LOKI_AGENTS_USER_DIR"] || join54(env["XDG_CONFIG_HOME"] || join54(homedir9(), ".config"), "loki", "agents")];
13046
+ return [env["LOKI_AGENTS_PROJECT_DIR"] || join55(repoDir, ".loki", "agents"), env["LOKI_AGENTS_USER_DIR"] || join55(env["XDG_CONFIG_HOME"] || join55(homedir9(), ".config"), "loki", "agents")];
12948
13047
  }
12949
13048
  function parseAgent(file, stem2) {
12950
- const m = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/.exec(readFileSync39(file, "utf8"));
13049
+ const m = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/.exec(readFileSync40(file, "utf8"));
12951
13050
  if (!m)
12952
13051
  return null;
12953
13052
  const body = m[2].trim();
@@ -12965,14 +13064,14 @@ function loadRoleAgent(repoDir, role, env = process.env) {
12965
13064
  continue;
12966
13065
  let names;
12967
13066
  try {
12968
- names = readdirSync16(d).filter((n2) => n2.endsWith(".md") && !n2.startsWith(".")).sort();
13067
+ names = readdirSync17(d).filter((n2) => n2.endsWith(".md") && !n2.startsWith(".")).sort();
12969
13068
  } catch {
12970
13069
  continue;
12971
13070
  }
12972
13071
  const ordered = [...names.filter((n2) => n2 === `${role}.md`), ...names.filter((n2) => n2 !== `${role}.md`)];
12973
13072
  for (const n2 of ordered) {
12974
13073
  try {
12975
- const a = parseAgent(join54(d, n2), n2.slice(0, -3));
13074
+ const a = parseAgent(join55(d, n2), n2.slice(0, -3));
12976
13075
  if (a && a.role === role)
12977
13076
  return a;
12978
13077
  } catch {}
@@ -12989,8 +13088,8 @@ var init_role_agents = __esm(() => {
12989
13088
  });
12990
13089
 
12991
13090
  // ../../loki-ts/src/engine10/stages/xreview.ts
12992
- import { existsSync as existsSync34, readFileSync as readFileSync40 } from "fs";
12993
- import { join as join55 } from "path";
13091
+ import { existsSync as existsSync34, readFileSync as readFileSync41 } from "fs";
13092
+ import { join as join56 } from "path";
12994
13093
  function minVerdict(verdict, review) {
12995
13094
  const ceiling = review?.level === "block" ? "PARTIAL" : verdict;
12996
13095
  return RANK3[ceiling] < RANK3[verdict] ? ceiling : verdict;
@@ -13003,11 +13102,11 @@ function explicitReview(repoDir, env) {
13003
13102
  if (flag && isOff(ev))
13004
13103
  return "off";
13005
13104
  for (const f of ["loki.yaml", "loki.yml"]) {
13006
- const p = join55(repoDir, f);
13105
+ const p = join56(repoDir, f);
13007
13106
  if (!existsSync34(p))
13008
13107
  continue;
13009
13108
  try {
13010
- const m = /^review:[ \t]*["']?([A-Za-z]+)["']?[ \t]*(?:#.*)?$/m.exec(readFileSync40(p, "utf8"));
13109
+ const m = /^review:[ \t]*["']?([A-Za-z]+)["']?[ \t]*(?:#.*)?$/m.exec(readFileSync41(p, "utf8"));
13011
13110
  const v = asProvider(m?.[1]);
13012
13111
  if (v)
13013
13112
  return v;
@@ -13217,16 +13316,41 @@ function recordRun(env, repoDir, model, verdict, usd, unmetered, wallS, cacheRoo
13217
13316
  return false;
13218
13317
  return appendRunOutcome(repoKey(readOriginUrl(repoDir), repoDir), { shape, executor: /haiku/i.test(model) ? "haiku" : "sonnet", verdict: "pass", owner: null, escalated: false, usd, wallS, wallKind: WALL_KIND }, cacheRoot);
13219
13318
  }
13220
- var NOT_RECORDED = "NOT RECORDED", previewOn = (env) => env["LOKI_COST_PREVIEW"] !== "0";
13319
+ var MIN_RUNS = 3, NOT_RECORDED = "NOT RECORDED", previewOn = (env) => env["LOKI_COST_PREVIEW"] !== "0", ESTIMATE_HELP;
13221
13320
  var init_cost_preview = __esm(() => {
13222
13321
  init_cache();
13322
+ init_session();
13223
13323
  init_history();
13224
13324
  init_engine_origin();
13325
+ ESTIMATE_HELP = `loki estimate - Show what a run is likely to cost
13326
+
13327
+ Usage: loki estimate [--model M] [--provider P] [--json]
13328
+
13329
+ Prints the same estimate line "loki start" prints before a run: the dollar and time
13330
+ range of this repo's prior verified runs of the same project shape. With fewer than
13331
+ ${MIN_RUNS} such runs it prints a rough per-model prior, or NOT AVAILABLE (reason).
13332
+ It never estimates from task text and never prints a number it did not measure.
13333
+
13334
+ Options:
13335
+ --model M Model whose rough prior to use when there is no history (default: the start model)
13336
+ --provider P Provider whose default model is used (default: LOKI_PROVIDER or claude)
13337
+ --json Machine-readable output (the estimate result object)
13338
+ --help, -h Show this help
13339
+
13340
+ Changed: --iterations is removed. The old iteration-based projection (tools/estimate-run.py)
13341
+ is replaced by this single estimator. LOKI_COST_PREVIEW=0 turns the estimate off.
13342
+
13343
+ Examples:
13344
+ loki estimate
13345
+ loki estimate --json
13346
+
13347
+ See also: loki cost --detail (what a run ALREADY spent)
13348
+ `;
13225
13349
  });
13226
13350
 
13227
13351
  // ../../loki-ts/src/features/contract.ts
13228
- import { existsSync as existsSync35, lstatSync as lstatSync12, mkdirSync as mkdirSync22, readFileSync as readFileSync41, rmSync as rmSync14, writeFileSync as writeFileSync24 } from "fs";
13229
- import { dirname as dirname24, join as join56 } from "path";
13352
+ import { existsSync as existsSync35, lstatSync as lstatSync12, mkdirSync as mkdirSync22, readFileSync as readFileSync42, rmSync as rmSync14, writeFileSync as writeFileSync24 } from "fs";
13353
+ import { dirname as dirname24, join as join57 } from "path";
13230
13354
  import { createHash as createHash13 } from "crypto";
13231
13355
  function contractEnabled(env = process.env) {
13232
13356
  return env["LOKI_CONTRACT"] !== "0";
@@ -13258,7 +13382,7 @@ function untracedLines(trace) {
13258
13382
  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)}`);
13259
13383
  }
13260
13384
  function contractPath(repoDir) {
13261
- return join56(repoDir, ".loki", "contract.json");
13385
+ return join57(repoDir, ".loki", "contract.json");
13262
13386
  }
13263
13387
  function readContract(repoDir) {
13264
13388
  const p = contractPath(repoDir);
@@ -13274,7 +13398,7 @@ function readContract(repoDir) {
13274
13398
  return { contract: null, notes: ["contract unreadable: too large"], sha256: null };
13275
13399
  let raw2;
13276
13400
  try {
13277
- raw2 = readFileSync41(p);
13401
+ raw2 = readFileSync42(p);
13278
13402
  } catch {
13279
13403
  return { contract: null, notes: ["contract unreadable: read failed"], sha256: null };
13280
13404
  }
@@ -13343,16 +13467,16 @@ var init_contract = __esm(() => {
13343
13467
  init_safe_git();
13344
13468
  STOP2 = new Set(["the", "and", "for", "with", "that", "this", "must", "should", "shall", "when", "then", "are", "was", "all", "any", "can", "not", "has", "have", "from", "into", "each", "user", "users"]);
13345
13469
  MAX_CONTRACT_BYTES = 1024 * 1024;
13346
- REQ_MAP_REL = join56(".loki", "state", "requirements-map.json");
13470
+ REQ_MAP_REL = join57(".loki", "state", "requirements-map.json");
13347
13471
  REQ_STOP = new Set(["with", "that", "this", "from", "have", "will", "should", "must", "when", "then", "into", "each", "every", "user", "users", "page", "able", "allow", "support", "ensure", "using", "their", "there", "which", "shall"]);
13348
13472
  });
13349
13473
 
13350
13474
  // ../../loki-ts/src/features/speed/seal_group.ts
13351
13475
  import { createHash as createHash14 } from "crypto";
13352
- import { existsSync as existsSync36, mkdirSync as mkdirSync23, readdirSync as readdirSync17, readFileSync as readFileSync42, renameSync as renameSync8, statSync as statSync17, writeFileSync as writeFileSync25 } from "fs";
13353
- import { basename as basename19, dirname as dirname25, join as join57 } from "path";
13476
+ import { existsSync as existsSync36, mkdirSync as mkdirSync23, readdirSync as readdirSync18, readFileSync as readFileSync43, renameSync as renameSync9, statSync as statSync17, writeFileSync as writeFileSync25 } from "fs";
13477
+ import { basename as basename19, dirname as dirname25, join as join58 } from "path";
13354
13478
  function sealGroup(runDir, rh) {
13355
- const manifestPath = join57(runDir, "group", "manifest.json");
13479
+ const manifestPath = join58(runDir, "group", "manifest.json");
13356
13480
  if (!existsSync36(manifestPath))
13357
13481
  return { section: undefined, notProven: [], problems: 0, allUnitsPass: true };
13358
13482
  const notProven = [];
@@ -13377,10 +13501,10 @@ function sealGroup(runDir, rh) {
13377
13501
  return fail(`unit ${index2} has an invalid, duplicate or case-colliding id`);
13378
13502
  seen.add(id);
13379
13503
  seenLower.add(id.toLowerCase());
13380
- const dir = join57(unitsDir(runDir), id), rp = join57(dir, "receipt.json"), ep = join57(dir, "events.jsonl");
13504
+ const dir = join58(unitsDir(runDir), id), rp = join58(dir, "receipt.json"), ep = join58(dir, "events.jsonl");
13381
13505
  let bytes;
13382
13506
  try {
13383
- bytes = readFileSync42(rp);
13507
+ bytes = readFileSync43(rp);
13384
13508
  } catch {
13385
13509
  return fail(`unit ${id} sub-receipt missing`);
13386
13510
  }
@@ -13397,7 +13521,7 @@ function sealGroup(runDir, rh) {
13397
13521
  unit_id: id,
13398
13522
  sub_receipt_sha256: sha2(bytes),
13399
13523
  receipt_sha256: sub["receipt_sha256"],
13400
- events_file_sha256: existsSync36(ep) ? sha2(readFileSync42(ep)) : null,
13524
+ events_file_sha256: existsSync36(ep) ? sha2(readFileSync43(ep)) : null,
13401
13525
  verdict,
13402
13526
  goal: text2(u["goal"], 200),
13403
13527
  files: (Array.isArray(u["files"]) ? u["files"] : []).map((f) => text2(f, 200)).filter(Boolean).slice(0, 50),
@@ -13422,7 +13546,7 @@ async function verifyGroup(receiptPath, receipt, rh, verifyOne, deps = {}) {
13422
13546
  const bad = (reason) => ({ verdict: "TAMPERED", reason: `group: ${reason}` });
13423
13547
  const g = receipt["group"];
13424
13548
  if (g === undefined)
13425
- return existsSync36(join57(runDir, "group", "manifest.json")) ? bad("group directory present but the receipt has no group section") : null;
13549
+ return existsSync36(join58(runDir, "group", "manifest.json")) ? bad("group directory present but the receipt has no group section") : null;
13426
13550
  if (!isObj10(g) || !ID_RE.test(String(g["group_id"] ?? "")) || !Array.isArray(g["units"]) || g["units"].length === 0)
13427
13551
  return bad("group section malformed");
13428
13552
  const entries = g["units"];
@@ -13434,20 +13558,20 @@ async function verifyGroup(receiptPath, receipt, rh, verifyOne, deps = {}) {
13434
13558
  return bad("unit directories do not match the receipt (missing or extra unit)");
13435
13559
  let unchecked = null;
13436
13560
  for (let i = 0;i < entries.length; i++) {
13437
- const e = entries[i], id = ids[i], dir = join57(unitsDir(runDir), id), rp = join57(dir, "receipt.json"), ep = join57(dir, "events.jsonl");
13561
+ const e = entries[i], id = ids[i], dir = join58(unitsDir(runDir), id), rp = join58(dir, "receipt.json"), ep = join58(dir, "events.jsonl");
13438
13562
  if (e["index"] !== i)
13439
13563
  return bad(`unit ${id} is out of order (index ${String(e["index"])} at position ${i})`);
13440
13564
  let bytes;
13441
13565
  try {
13442
13566
  if (!statSync17(dir).isDirectory())
13443
13567
  return bad(`unit ${id} is not a directory`);
13444
- bytes = readFileSync42(rp);
13568
+ bytes = readFileSync43(rp);
13445
13569
  } catch {
13446
13570
  return bad(`unit ${id} sub-receipt missing`);
13447
13571
  }
13448
13572
  if (sha2(bytes) !== e["sub_receipt_sha256"])
13449
13573
  return bad(`unit ${id} sub-receipt does not match its recorded hash`);
13450
- const evSha = existsSync36(ep) ? sha2(readFileSync42(ep)) : null;
13574
+ const evSha = existsSync36(ep) ? sha2(readFileSync43(ep)) : null;
13451
13575
  if (evSha !== (e["events_file_sha256"] ?? null))
13452
13576
  return bad(`unit ${id} events.jsonl does not match its recorded hash`);
13453
13577
  const sub = readJson5(rp);
@@ -13467,8 +13591,8 @@ async function verifyGroup(receiptPath, receipt, rh, verifyOne, deps = {}) {
13467
13591
  }
13468
13592
  function compositeVerdict(childVerdicts) {
13469
13593
  const base = childVerdicts.includes("SPEC_CONFLICT") ? "SPEC_CONFLICT" : childVerdicts.length > 0 && childVerdicts.every((v) => v === "ALREADY_SATISFIED") ? "ALREADY_SATISFIED" : "VERIFIED";
13470
- const failed = childVerdicts.filter((v) => v === "FAILED" || !["VERIFIED", "ALREADY_SATISFIED", "PARTIAL", "SPEC_CONFLICT"].includes(v)).length;
13471
- return capGroupVerdict(base, { section: undefined, notProven: [], problems: failed, allUnitsPass: childVerdicts.every((v) => PASSING.has(v)) });
13594
+ const failed2 = childVerdicts.filter((v) => v === "FAILED" || !["VERIFIED", "ALREADY_SATISFIED", "PARTIAL", "SPEC_CONFLICT"].includes(v)).length;
13595
+ return capGroupVerdict(base, { section: undefined, notProven: [], problems: failed2, allUnitsPass: childVerdicts.every((v) => PASSING.has(v)) });
13472
13596
  }
13473
13597
  async function verifyComposite(receiptPath, receipt, rh, verifyOne, deps = {}) {
13474
13598
  const runDir = dirname25(receiptPath);
@@ -13479,13 +13603,13 @@ async function verifyComposite(receiptPath, receipt, rh, verifyOne, deps = {}) {
13479
13603
  return bad("body malformed");
13480
13604
  let ibytes;
13481
13605
  try {
13482
- ibytes = readFileSync42(join57(runDir, "integration.json"));
13606
+ ibytes = readFileSync43(join58(runDir, "integration.json"));
13483
13607
  } catch {
13484
13608
  return bad("integration.json missing");
13485
13609
  }
13486
13610
  if (sha2(ibytes) !== receipt["integration_sha256"])
13487
13611
  return bad("integration.json does not match its recorded hash");
13488
- const integ = readJson5(join57(runDir, "integration.json"));
13612
+ const integ = readJson5(join58(runDir, "integration.json"));
13489
13613
  if (!isObj10(integ) || !isObj10(integ["outcomes"]))
13490
13614
  return bad("integration.json has no outcomes");
13491
13615
  const heads = isObj10(integ["heads"]) ? integ["heads"] : {}, outcomes = integ["outcomes"];
@@ -13503,10 +13627,10 @@ async function verifyComposite(receiptPath, receipt, rh, verifyOne, deps = {}) {
13503
13627
  return bad(`child ${text2(repo, 80) || "?"} has an invalid receipt path`);
13504
13628
  if (outcomes[repo] !== "ok")
13505
13629
  return bad(`child ${text2(repo, 80)} did not finish ok in integration.json`);
13506
- const rp = join57(runDir, rel);
13630
+ const rp = join58(runDir, rel);
13507
13631
  let bytes;
13508
13632
  try {
13509
- bytes = readFileSync42(rp);
13633
+ bytes = readFileSync43(rp);
13510
13634
  } catch {
13511
13635
  return bad(`child ${repo} receipt missing`);
13512
13636
  }
@@ -13541,15 +13665,15 @@ function compositeHash(r) {
13541
13665
  const { composite_sha256: _c, verification: _v, ...body } = r;
13542
13666
  return sha2(canonicalJson(body));
13543
13667
  }
13544
- var ID_RE, PASSING, sha2 = (b) => createHash14("sha256").update(b).digest("hex"), isObj10 = (v) => v !== null && typeof v === "object" && !Array.isArray(v), text2 = (v, cap) => typeof v === "string" ? v.replace(/[\x00-\x1f\x7f]+/g, " ").trim().slice(0, cap) : "", num4 = (v) => typeof v === "number" && Number.isFinite(v) && v >= 0 ? v : 0, unitsDir = (runDir) => join57(runDir, "group", "units"), readJson5 = (p) => {
13668
+ var ID_RE, PASSING, sha2 = (b) => createHash14("sha256").update(b).digest("hex"), isObj10 = (v) => v !== null && typeof v === "object" && !Array.isArray(v), text2 = (v, cap) => typeof v === "string" ? v.replace(/[\x00-\x1f\x7f]+/g, " ").trim().slice(0, cap) : "", num4 = (v) => typeof v === "number" && Number.isFinite(v) && v >= 0 ? v : 0, unitsDir = (runDir) => join58(runDir, "group", "units"), readJson5 = (p) => {
13545
13669
  try {
13546
- return JSON.parse(readFileSync42(p, "utf8"));
13670
+ return JSON.parse(readFileSync43(p, "utf8"));
13547
13671
  } catch {
13548
13672
  return;
13549
13673
  }
13550
13674
  }, dirNames = (d) => {
13551
13675
  try {
13552
- return readdirSync17(d).filter((n2) => !n2.startsWith(".")).sort();
13676
+ return readdirSync18(d).filter((n2) => !n2.startsWith(".")).sort();
13553
13677
  } catch {
13554
13678
  return null;
13555
13679
  }
@@ -13561,8 +13685,8 @@ var init_seal_group = __esm(() => {
13561
13685
  });
13562
13686
 
13563
13687
  // ../../loki-ts/src/supply/supply_guard.ts
13564
- import { existsSync as existsSync37, readFileSync as readFileSync43, statSync as statSync18 } from "fs";
13565
- import { basename as basename20, join as join58 } from "path";
13688
+ import { existsSync as existsSync37, readFileSync as readFileSync44, statSync as statSync18 } from "fs";
13689
+ import { basename as basename20, join as join59 } from "path";
13566
13690
  function supplyEnabled(env) {
13567
13691
  return env["LOKI_SUPPLY_GUARD"] !== "0";
13568
13692
  }
@@ -13577,13 +13701,13 @@ function isManifestPath(path) {
13577
13701
  return MANIFESTS.test(basename20(path));
13578
13702
  }
13579
13703
  function readDeclared(repoDir) {
13580
- const p = join58(repoDir, ".loki", DECLARED_FILE);
13704
+ const p = join59(repoDir, ".loki", DECLARED_FILE);
13581
13705
  if (!existsSync37(p))
13582
13706
  return { deps: [], problem: null };
13583
13707
  try {
13584
13708
  if (statSync18(p).size > MAX_FILE_BYTES)
13585
13709
  return { deps: [], problem: "declaration file too large" };
13586
- const j = JSON.parse(readFileSync43(p, "utf8"));
13710
+ const j = JSON.parse(readFileSync44(p, "utf8"));
13587
13711
  if (!Array.isArray(j))
13588
13712
  return { deps: [], problem: "declaration is not a JSON array" };
13589
13713
  const deps = [];
@@ -13603,11 +13727,11 @@ function readDeclared(repoDir) {
13603
13727
  }
13604
13728
  }
13605
13729
  function readAllowlist(repoDir) {
13606
- const p = join58(repoDir, ".loki", "supply-allowlist");
13730
+ const p = join59(repoDir, ".loki", "supply-allowlist");
13607
13731
  if (!existsSync37(p))
13608
13732
  return new Set;
13609
13733
  try {
13610
- return new Set(readFileSync43(p, "utf8").split(`
13734
+ return new Set(readFileSync44(p, "utf8").split(`
13611
13735
  `).map((l) => l.replace(/#.*$/, "").trim()).filter(Boolean));
13612
13736
  } catch {
13613
13737
  return new Set;
@@ -13648,7 +13772,7 @@ function userConfigPrivate(eco, repoDir, env, goEnv = {}, name = "") {
13648
13772
  if (eco === "npm") {
13649
13773
  if (privateUrlList([env["npm_config_registry"] ?? "", env["NPM_CONFIG_REGISTRY"] ?? ""]))
13650
13774
  return true;
13651
- return files(join58(repoDir, ".npmrc"), home ? join58(home, ".npmrc") : "").some((t) => t.split(`
13775
+ return files(join59(repoDir, ".npmrc"), home ? join59(home, ".npmrc") : "").some((t) => t.split(`
13652
13776
  `).some((l) => {
13653
13777
  const m = /^\s*(?:(@[^:\s]+):)?registry\s*=\s*(\S+)/.exec(l);
13654
13778
  return !!m && privateUrl(m[2]) && (m[1] === undefined || name.startsWith(`${m[1]}/`));
@@ -13657,7 +13781,7 @@ function userConfigPrivate(eco, repoDir, env, goEnv = {}, name = "") {
13657
13781
  if (eco === "pypi") {
13658
13782
  if (privateUrlList([env["PIP_INDEX_URL"] ?? "", env["PIP_EXTRA_INDEX_URL"] ?? ""]))
13659
13783
  return true;
13660
- const cfg = [env["PIP_CONFIG_FILE"] ?? "", home ? join58(home, ".config", "pip", "pip.conf") : "", home ? join58(home, ".pip", "pip.conf") : "", join58(repoDir, "pip.conf")];
13784
+ const cfg = [env["PIP_CONFIG_FILE"] ?? "", home ? join59(home, ".config", "pip", "pip.conf") : "", home ? join59(home, ".pip", "pip.conf") : "", join59(repoDir, "pip.conf")];
13661
13785
  return files(...cfg).some((t) => t.split(`
13662
13786
  `).some((l) => {
13663
13787
  const m = /^\s*(?:extra-)?index-url\s*[=:]\s*(.+)$/.exec(l);
@@ -13667,7 +13791,7 @@ function userConfigPrivate(eco, repoDir, env, goEnv = {}, name = "") {
13667
13791
  if (eco === "cargo") {
13668
13792
  if (Object.keys(env).some((k) => /^CARGO_REGISTRIES_.+_INDEX$/.test(k) || /^CARGO_SOURCE_/.test(k)))
13669
13793
  return true;
13670
- return files(join58(repoDir, ".cargo", "config.toml"), join58(repoDir, ".cargo", "config"), home ? join58(home, ".cargo", "config.toml") : "", home ? join58(home, ".cargo", "config") : "").some((t) => /^\s*\[(registries\.|source\.)/m.test(t) || /replace-with\s*=/.test(t));
13794
+ return files(join59(repoDir, ".cargo", "config.toml"), join59(repoDir, ".cargo", "config"), home ? join59(home, ".cargo", "config.toml") : "", home ? join59(home, ".cargo", "config") : "").some((t) => /^\s*\[(registries\.|source\.)/m.test(t) || /replace-with\s*=/.test(t));
13671
13795
  }
13672
13796
  if (eco === "go") {
13673
13797
  const gp = goEnv["GOPROXY"] ?? env["GOPROXY"] ?? "";
@@ -13790,7 +13914,7 @@ var MIN_AGE_DAYS = 7, DECLARED_FILE = "supply-declared.json", MAX_CHECKED = 50,
13790
13914
  }
13791
13915
  }, PUBLIC_HOSTS, hostOf = (u) => u.trim().toLowerCase().replace(/^[a-z][a-z0-9+.-]*:\/\//, "").replace(/^www\./, "").split(/[/?#\s]/)[0].replace(/^[^@]*@/, "").replace(/:\d+$/, ""), privateUrl = (u) => /[a-z0-9]/i.test(u) && !PUBLIC_HOSTS.has(hostOf(u)), readSmall = (p) => {
13792
13916
  try {
13793
- return statSync18(p).size <= MAX_FILE_BYTES ? readFileSync43(p, "utf8") : "";
13917
+ return statSync18(p).size <= MAX_FILE_BYTES ? readFileSync44(p, "utf8") : "";
13794
13918
  } catch {
13795
13919
  return "";
13796
13920
  }
@@ -13806,8 +13930,8 @@ var init_supply_guard = __esm(() => {
13806
13930
 
13807
13931
  // ../../loki-ts/src/engine10/stages/seal.ts
13808
13932
  import { createHash as createHash15 } from "crypto";
13809
- import { existsSync as existsSync38, readdirSync as readdirSync18, mkdirSync as mkdirSync24, readFileSync as readFileSync44, writeFileSync as writeFileSync26 } from "fs";
13810
- import { basename as basename21, isAbsolute as isAbsolute9, join as join59, relative as relative9 } from "path";
13933
+ import { existsSync as existsSync38, readdirSync as readdirSync19, mkdirSync as mkdirSync24, readFileSync as readFileSync45, writeFileSync as writeFileSync26 } from "fs";
13934
+ import { basename as basename21, isAbsolute as isAbsolute9, join as join60, relative as relative9 } from "path";
13811
13935
  function sanitizeReason(s) {
13812
13936
  const collapsed = s.replace(/[\x00-\x1f\x7f]+/g, " ").trim();
13813
13937
  const capped = collapsed.length > 500 ? `${collapsed.slice(0, 500)}...` : collapsed;
@@ -13882,7 +14006,7 @@ function renderReceiptMd(r) {
13882
14006
  `);
13883
14007
  }
13884
14008
  var DEEP_NOT_PROVEN, SIGNING_UNAVAILABLE = "receipt signing unavailable (no usable signing key: invalid key or unwritable ~/.loki/keys)", EXCLUDE_LOKI = ":(exclude).loki", sha2562 = (s) => createHash15("sha256").update(s).digest("hex"), str3 = (v) => typeof v === "string" && v !== "" ? v : null, strs2 = (v) => Array.isArray(v) ? v.filter((x) => typeof x === "string" && x !== "") : [], receiptRel = (ctx) => {
13885
- const r = relative9(ctx.repoDir, join59(ctx.runDir, "receipt.json"));
14009
+ const r = relative9(ctx.repoDir, join60(ctx.runDir, "receipt.json"));
13886
14010
  return r.startsWith("..") || isAbsolute9(r) ? `.loki/runs/${ctx.runId}/receipt.json` : r;
13887
14011
  }, commitStage, sealStage;
13888
14012
  var init_seal = __esm(() => {
@@ -13985,7 +14109,7 @@ var init_seal = __esm(() => {
13985
14109
  if (wallNotRun > 0) {
13986
14110
  try {
13987
14111
  const kept = new Set((Array.isArray(o.wall?.files) ? o.wall.files : []).map((f) => basename21(String(f.path))));
13988
- for (const n2 of readdirSync18(join59(ctx.runDir, "wall")).sort())
14112
+ for (const n2 of readdirSync19(join60(ctx.runDir, "wall")).sort())
13989
14113
  if (!kept.has(n2))
13990
14114
  notProven.add(`wall test discarded: ${n2} (not_run)`);
13991
14115
  } catch {}
@@ -14046,19 +14170,19 @@ var init_seal = __esm(() => {
14046
14170
  for (const [s, d] of Object.entries(o))
14047
14171
  if (typeof d?.duration_s === "number")
14048
14172
  stages[s] = d.duration_s;
14049
- const T = hooks.time, time = T ? T.build(ctx, stages, T.firstEventMs(join59(ctx.runDir, "events.jsonl"))) : { wall_s: Object.values(stages).reduce((a, b) => a + (b ?? 0), 0), stages }, totalS = T?.reconciled(time) ?? null;
14173
+ const T = hooks.time, time = T ? T.build(ctx, stages, T.firstEventMs(join60(ctx.runDir, "events.jsonl"))) : { wall_s: Object.values(stages).reduce((a, b) => a + (b ?? 0), 0), stages }, totalS = T?.reconciled(time) ?? null;
14050
14174
  const iterIds = Object.values(o).flatMap((d) => [...strs2(d?.iteration_ids), ...strs2([d?.iteration_id])]);
14051
14175
  if (iterIds.length === 0)
14052
14176
  notProven.add("cost not measured (no iteration ids recorded)");
14053
14177
  const cost = ctx.cost.read(ctx.repoDir, iterIds);
14054
14178
  if (cost.unmetered)
14055
14179
  notProven.add("cost unmetered (CLI invoker; recorded as 0)");
14056
- const eventsPath = join59(ctx.runDir, "events.jsonl");
14180
+ const eventsPath = join60(ctx.runDir, "events.jsonl");
14057
14181
  const wallFiles = Array.isArray(o.wall?.files) ? o.wall.files : [];
14058
14182
  const wallPassed = typeof o.verify?.wall_passed === "boolean" ? o.verify.wall_passed : null;
14059
14183
  if (wallFiles.length > 0 && wallPassed === null)
14060
14184
  notProven.add("wall result not recorded by verify");
14061
- const routeBlock = buildRouteBlock(process.env, ctx.provider, o.implement?.route ?? o.plan?.route_record ?? (routerEnabled(process.env) ? loadRouteRecord(ctx.runDir) : undefined), cost.router ?? (routerEnabled(process.env) ? sumResultCosts(join59(ctx.repoDir, ".loki"), iterIds).router : undefined), ctx.model);
14185
+ const routeBlock = buildRouteBlock(process.env, ctx.provider, o.implement?.route ?? o.plan?.route_record ?? (routerEnabled(process.env) ? loadRouteRecord(ctx.runDir) : undefined), cost.router ?? (routerEnabled(process.env) ? sumResultCosts(join60(ctx.repoDir, ".loki"), iterIds).router : undefined), ctx.model);
14062
14186
  if (routeBlock)
14063
14187
  for (const l of routeNotProven(routeBlock))
14064
14188
  notProven.add(l);
@@ -14100,7 +14224,7 @@ var init_seal = __esm(() => {
14100
14224
  provider: ctx.provider,
14101
14225
  model: ctx.model,
14102
14226
  resumed: o.intake?.resumed === true,
14103
- events_sha256: sha2562(existsSync38(eventsPath) ? readFileSync44(eventsPath) : ""),
14227
+ events_sha256: sha2562(existsSync38(eventsPath) ? readFileSync45(eventsPath) : ""),
14104
14228
  ...await sealEvidence(ctx.repoDir, ctx.runDir, o, notProven, signal, ctx.emit),
14105
14229
  log_seal: true,
14106
14230
  ...receiptBlock(process.env, cost.usd, cost.unmetered === true, totalS),
@@ -14121,10 +14245,10 @@ var init_seal = __esm(() => {
14121
14245
  }
14122
14246
  const receipt = { ...body, receipt_sha256: hash, verification: { jwt: sig.jwt, kid: sig.kid } };
14123
14247
  mkdirSync24(ctx.runDir, { recursive: true });
14124
- const path = join59(ctx.runDir, "receipt.json");
14248
+ const path = join60(ctx.runDir, "receipt.json");
14125
14249
  writeFileSync26(path, JSON.stringify(receipt, null, 2) + `
14126
14250
  `);
14127
- writeFileSync26(join59(ctx.runDir, "receipt.md"), renderReceiptMd(receipt));
14251
+ writeFileSync26(join60(ctx.runDir, "receipt.md"), renderReceiptMd(receipt));
14128
14252
  recordRun(process.env, ctx.repoDir, ctx.model, verdict, cost.usd, cost.unmetered === true, totalS);
14129
14253
  const signed = sig.jwt !== null;
14130
14254
  const data = { receipt_path: path, receipt_sha256: hash, signed, kid: sig.kid, verdict, not_proven: receipt.not_proven, ...mp ? { mutation_line: mp.line } : {}, ...routeBlock ? { route_line: routePrLine(routeBlock) } : {} };
@@ -14143,8 +14267,8 @@ var init_seal = __esm(() => {
14143
14267
  });
14144
14268
 
14145
14269
  // ../../loki-ts/src/features/receipt_dsse.ts
14146
- import { createPublicKey as createPublicKey3, sign as sign2, verify as verify2 } from "crypto";
14147
- import { readFileSync as readFileSync45 } from "fs";
14270
+ import { createPublicKey as createPublicKey3, sign as sign2, verify as verify3 } from "crypto";
14271
+ import { readFileSync as readFileSync46 } from "fs";
14148
14272
  function pae(payloadType, body) {
14149
14273
  const t = Buffer.from(payloadType, "utf8");
14150
14274
  return Buffer.concat([Buffer.from(`DSSEv1 ${t.length} `), t, Buffer.from(` ${body.length} `), body]);
@@ -14163,7 +14287,7 @@ function verifyEnvelope(env, pubFor) {
14163
14287
  continue;
14164
14288
  }
14165
14289
  known++;
14166
- if (verify2(null, pae(env.payloadType, body), pub, Buffer.from(s.sig, "base64"))) {
14290
+ if (verify3(null, pae(env.payloadType, body), pub, Buffer.from(s.sig, "base64"))) {
14167
14291
  kid = s.keyid;
14168
14292
  break;
14169
14293
  }
@@ -14221,7 +14345,7 @@ function verifyDsseReceipt(env, d) {
14221
14345
  }
14222
14346
  function runIdGuard(receiptPath, runId) {
14223
14347
  try {
14224
- return envelopeRunIdProblem(JSON.parse(readFileSync45(receiptPath, "utf8")), runId);
14348
+ return envelopeRunIdProblem(JSON.parse(readFileSync46(receiptPath, "utf8")), runId);
14225
14349
  } catch {
14226
14350
  return null;
14227
14351
  }
@@ -14235,16 +14359,16 @@ var init_keys_cmd = __esm(() => {
14235
14359
  });
14236
14360
 
14237
14361
  // ../../loki-ts/src/engine10/verify_cmd.ts
14238
- import { existsSync as existsSync39, readFileSync as readFileSync46, renameSync as renameSync9, statSync as statSync20, writeFileSync as writeFileSync27 } from "fs";
14239
- import { basename as basename22, dirname as dirname27, join as join60 } from "path";
14240
- import { createHash as createHash16, createPublicKey as createPublicKey4, verify as verify3 } from "crypto";
14362
+ import { existsSync as existsSync39, readFileSync as readFileSync47, renameSync as renameSync10, statSync as statSync20, writeFileSync as writeFileSync27 } from "fs";
14363
+ import { basename as basename22, dirname as dirname27, join as join61 } from "path";
14364
+ import { createHash as createHash16, createPublicKey as createPublicKey4, verify as verify4 } from "crypto";
14241
14365
  function pubFor(kid, given) {
14242
14366
  if (given)
14243
14367
  return kidOf2(given) === kid ? given : undefined;
14244
14368
  const active = loadSigningKey(false);
14245
14369
  const pubs = (process.env["LOKI_RECEIPT_RETIRED_PUBKEYS"] ?? "").split(":").map((f) => f.trim()).filter(Boolean).flatMap((f) => {
14246
14370
  try {
14247
- return [createPublicKey4(readFileSync46(f))];
14371
+ return [createPublicKey4(readFileSync47(f))];
14248
14372
  } catch {
14249
14373
  return [];
14250
14374
  }
@@ -14268,7 +14392,7 @@ function checkAttestation(jwt, expectedHash, given) {
14268
14392
  const pub = pubFor(header.kid, given);
14269
14393
  if (!pub)
14270
14394
  return { status: "unchecked", reason: `no key for kid ${String(header.kid)} on this machine (set LOKI_RECEIPT_SIGNING_KEY_FILE or LOKI_RECEIPT_RETIRED_PUBKEYS)` };
14271
- if (!verify3(null, Buffer.from(`${h}.${p}`), pub, Buffer.from(s, "base64url")))
14395
+ if (!verify4(null, Buffer.from(`${h}.${p}`), pub, Buffer.from(s, "base64url")))
14272
14396
  return bad("signature does not verify");
14273
14397
  return payload?.receipt_sha256 === expectedHash ? { status: "verified", reason: null } : bad("attestation binds a different receipt hash");
14274
14398
  }
@@ -14276,17 +14400,17 @@ function checkEventLog(receiptPath, receipt) {
14276
14400
  const bound = receipt["events_sha256"];
14277
14401
  if (typeof bound !== "string" || bound === createHash16("sha256").digest("hex"))
14278
14402
  return null;
14279
- const path = join60(dirname27(receiptPath), "events.jsonl");
14403
+ const path = join61(dirname27(receiptPath), "events.jsonl");
14280
14404
  if (!existsSync39(path))
14281
14405
  return "events.jsonl is missing";
14282
- const raw2 = readFileSync46(path, "utf8"), events2 = readEvents(path), lines = raw2.split(`
14406
+ const raw2 = readFileSync47(path, "utf8"), events2 = readEvents(path), lines2 = raw2.split(`
14283
14407
  `).filter((l) => l.trim() !== "");
14284
- if (events2.length !== lines.length || events2.some((e, i) => e.seq !== i))
14408
+ if (events2.length !== lines2.length || events2.some((e, i) => e.seq !== i))
14285
14409
  return "events.jsonl has a forged, edited or missing line";
14286
14410
  if (events2.some((e) => e.type === "tamper.detected"))
14287
14411
  return "the supervisor detected events.jsonl being modified during the run";
14288
14412
  const h = createHash16("sha256");
14289
- for (const l of lines) {
14413
+ for (const l of lines2) {
14290
14414
  if (h.copy().digest("hex") === bound)
14291
14415
  return null;
14292
14416
  h.update(l + `
@@ -14295,7 +14419,7 @@ function checkEventLog(receiptPath, receipt) {
14295
14419
  return h.digest("hex") === bound ? null : "events.jsonl does not match the hash recorded at seal";
14296
14420
  }
14297
14421
  function checkLogSeal(receiptPath, hash, kid, given) {
14298
- const path = join60(dirname27(receiptPath), "events.jsonl"), events2 = readEvents(path), done = events2.findIndex((e) => e.type === "run.completed");
14422
+ const path = join61(dirname27(receiptPath), "events.jsonl"), events2 = readEvents(path), done = events2.findIndex((e) => e.type === "run.completed");
14299
14423
  if (!events2.some((e) => e.type === "receipt.sealed" && e.data["receipt_sha256"] === hash))
14300
14424
  return { verdict: "TAMPERED", reason: "events.jsonl has no receipt.sealed event for this receipt" };
14301
14425
  if (done < 0)
@@ -14303,12 +14427,12 @@ function checkLogSeal(receiptPath, hash, kid, given) {
14303
14427
  const seal = events2[done + 1], d = seal?.type === "log.sealed" ? seal.data : null;
14304
14428
  if (!d)
14305
14429
  return { verdict: "TAMPERED", reason: "events.jsonl has no signed log.sealed line after run.completed" };
14306
- const lines = readFileSync46(path, "utf8").split(`
14307
- `).filter((l) => l.trim() !== ""), sha3 = createHash16("sha256").update(lines.slice(0, done + 1).join(`
14430
+ const lines2 = readFileSync47(path, "utf8").split(`
14431
+ `).filter((l) => l.trim() !== ""), sha3 = createHash16("sha256").update(lines2.slice(0, done + 1).join(`
14308
14432
  `) + `
14309
14433
  `).digest("hex");
14310
14434
  const pub = d["kid"] === kid ? pubFor(kid, given) : undefined;
14311
- if (!pub || d["events_sha256"] !== sha3 || d["tampered"] !== false || typeof d["sig"] !== "string" || !verify3(null, Buffer.from(`${sha3}:false`), pub, Buffer.from(d["sig"], "base64url")))
14435
+ if (!pub || d["events_sha256"] !== sha3 || d["tampered"] !== false || typeof d["sig"] !== "string" || !verify4(null, Buffer.from(`${sha3}:false`), pub, Buffer.from(d["sig"], "base64url")))
14312
14436
  return { verdict: "TAMPERED", reason: "log.sealed line is invalid, forged, or records a tamper" };
14313
14437
  return null;
14314
14438
  }
@@ -14317,7 +14441,7 @@ async function verifyReceipt(receiptPath, deps = {}) {
14317
14441
  return { verdict: "UNCHECKED", reasons: [`receipt not found: ${receiptPath}`] };
14318
14442
  let receipt;
14319
14443
  try {
14320
- receipt = JSON.parse((deps.read ?? ((p) => readFileSync46(p, "utf8")))(receiptPath));
14444
+ receipt = JSON.parse((deps.read ?? ((p) => readFileSync47(p, "utf8")))(receiptPath));
14321
14445
  } catch {
14322
14446
  return { verdict: "UNCHECKED", reasons: ["receipt.json is not valid JSON"] };
14323
14447
  }
@@ -14399,8 +14523,8 @@ var init_verify_cmd = __esm(() => {
14399
14523
  });
14400
14524
 
14401
14525
  // src/server/app.ts
14402
- import { existsSync as existsSync41, readFileSync as readFileSync49, statSync as statSync21 } from "fs";
14403
- import { join as join63, resolve as resolve15, sep as sep14 } from "path";
14526
+ import { existsSync as existsSync41, readFileSync as readFileSync50, statSync as statSync21 } from "fs";
14527
+ import { join as join64, resolve as resolve15, sep as sep14 } from "path";
14404
14528
 
14405
14529
  // node_modules/hono/dist/request/constants.js
14406
14530
  var GET_MATCH_RESULT = Symbol();
@@ -20172,6 +20296,9 @@ var SHA = /^[0-9a-f]{40,64}$/;
20172
20296
  var MAX_RECEIPT_BYTES = 1024 * 1024;
20173
20297
  var GIT_TIMEOUT_MS = 5000;
20174
20298
  var cache = new Map;
20299
+ var FAIL_TTL_MS = 60000;
20300
+ var failed = new Map;
20301
+ var diffStatGitRuns = { n: 0 };
20175
20302
  var gitEnv = () => ({ PATH: process.env.PATH ?? "", HOME: process.env.HOME ?? "", LC_ALL: "C", GIT_TERMINAL_PROMPT: "0", GIT_OPTIONAL_LOCKS: "0" });
20176
20303
  function parseNumstat(out) {
20177
20304
  const files = [];
@@ -20229,13 +20356,19 @@ function diffStatFor(db, source, run) {
20229
20356
  const base = typeof r?.base_sha === "string" ? r.base_sha : "", head = typeof r?.head_sha === "string" ? r.head_sha : "";
20230
20357
  if (!SHA.test(base) || !SHA.test(head))
20231
20358
  return null;
20359
+ const failKey = `${key}:${base}:${head}`;
20360
+ const failedAt = failed.get(failKey);
20361
+ if (failedAt !== undefined && Date.now() - failedAt < FAIL_TTL_MS)
20362
+ return null;
20232
20363
  try {
20364
+ diffStatGitRuns.n++;
20233
20365
  const out = execFileSync("git", ["-C", repoReal, "-c", "core.quotePath=false", "diff", "--numstat", "--no-renames", `${base}..${head}`, "--"], { env: gitEnv(), timeout: GIT_TIMEOUT_MS, maxBuffer: 8 * 1024 * 1024, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] });
20234
20366
  const files = parseNumstat(out);
20235
20367
  const stat = { base, head, files, added: files.reduce((n2, f) => n2 + (f.added ?? 0), 0), removed: files.reduce((n2, f) => n2 + (f.removed ?? 0), 0), source: "git" };
20236
20368
  cache.set(key, stat);
20237
20369
  return stat;
20238
20370
  } catch {
20371
+ failed.set(failKey, Date.now());
20239
20372
  return null;
20240
20373
  }
20241
20374
  }
@@ -20604,10 +20737,10 @@ function stopReason(evs, verdict) {
20604
20737
  return { reason: /[.!?]$/.test(reason) ? reason : `${reason}.`, ownRules: OWN_RULES.test(text2) };
20605
20738
  }
20606
20739
  if (verdict === "FAILED") {
20607
- const failed = [...evs].reverse().find((e) => e.type === "stage.failed");
20608
- const why = typeof failed?.data?.reason === "string" ? oneLine(failed.data.reason, 240) : "";
20609
- if (failed && failed.stage)
20610
- return { reason: `The run failed in the ${failed.stage} stage${why ? `: ${why}` : ""}.`, ownRules: false };
20740
+ const failed2 = [...evs].reverse().find((e) => e.type === "stage.failed");
20741
+ const why = typeof failed2?.data?.reason === "string" ? oneLine(failed2.data.reason, 240) : "";
20742
+ if (failed2 && failed2.stage)
20743
+ return { reason: `The run failed in the ${failed2.stage} stage${why ? `: ${why}` : ""}.`, ownRules: false };
20611
20744
  const bad = evs.filter((e) => e.type === "test.result" && e.data.result === "fail").length;
20612
20745
  return { reason: bad ? `The run failed: ${bad} check${bad === 1 ? "" : "s"} did not pass.` : "The run failed and did not record a reason.", ownRules: false };
20613
20746
  }
@@ -22197,6 +22330,7 @@ var isObj4 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
22197
22330
  var str2 = (v) => typeof v === "string" && v !== "" ? v : null;
22198
22331
  var SAFE_ID = /^[A-Za-z0-9_-]{1,128}$/;
22199
22332
  var MAX_CHECKPOINTS = 50;
22333
+ var ROLLBACK_ALLOW = new Set(["session.json", "dashboard-state.json", "CONTINUITY.md", "autonomy-state.json", "state", "queue"]);
22200
22334
  var validCheckpointId = (id) => SAFE_ID.test(id);
22201
22335
  var storeOf = (repoDir) => join17(repoDir, ".loki", "state", "checkpoints");
22202
22336
  var lokiOf = (repoDir) => join17(repoDir, ".loki");
@@ -22336,16 +22470,33 @@ function rollbackCheckpoint(repoDir, id, now2 = new Date) {
22336
22470
  `);
22337
22471
  let restored = 0;
22338
22472
  const errors = [];
22473
+ const skipped = (src) => src === join17(real2, "state", "checkpoints");
22339
22474
  for (const e of readdirSync4(real2, { withFileTypes: true })) {
22340
22475
  if (e.name === "metadata.json" || e.name === "worktree-snapshot.txt")
22341
22476
  continue;
22342
- const src = join17(real2, e.name);
22477
+ if (!ROLLBACK_ALLOW.has(e.name)) {
22478
+ errors.push(`${e.name}: not in the rollback allowlist, skipped`);
22479
+ continue;
22480
+ }
22481
+ const src = join17(real2, e.name), dst = join17(loki, e.name);
22343
22482
  try {
22344
22483
  if (lstatSync2(src).isSymbolicLink()) {
22345
22484
  errors.push(`${e.name}: symlink skipped`);
22346
22485
  continue;
22347
22486
  }
22348
- cpSync(src, join17(loki, e.name), { recursive: true, filter: (s) => s !== join17(real2, "state", "checkpoints") });
22487
+ if (existsSync8(dst) && lstatSync2(dst).isSymbolicLink()) {
22488
+ errors.push(`${e.name}: destination is a symlink, skipped`);
22489
+ continue;
22490
+ }
22491
+ cpSync(src, dst, { recursive: true, filter: (s) => {
22492
+ if (skipped(s))
22493
+ return false;
22494
+ if (lstatSync2(s).isSymbolicLink()) {
22495
+ errors.push(`${s.slice(real2.length + 1)}: nested symlink skipped`);
22496
+ return false;
22497
+ }
22498
+ return true;
22499
+ } });
22349
22500
  restored++;
22350
22501
  } catch (err) {
22351
22502
  errors.push(`${e.name}: ${err.message}`);
@@ -23567,8 +23718,8 @@ function tasksList(loki, status) {
23567
23718
  const out2 = order2.map((o) => ("ref" in o) ? byId.get(o.ref) : o);
23568
23719
  for (const t of out2) {
23569
23720
  if (t.type === "iteration" && !t.description) {
23570
- const ec = t.exitCode, failed = t._terminal_outcome === "failed";
23571
- const o = Number.isInteger(ec) && ec === 0 && !failed ? "completed cleanly (exit 0)" : Number.isInteger(ec) && ec !== 0 ? `failed (exit ${ec})` : failed ? "failed" : t._terminal_outcome === "completed" ? "completed" : t.status === "done" ? "finished" : null;
23721
+ const ec = t.exitCode, failed2 = t._terminal_outcome === "failed";
23722
+ const o = Number.isInteger(ec) && ec === 0 && !failed2 ? "completed cleanly (exit 0)" : Number.isInteger(ec) && ec !== 0 ? `failed (exit ${ec})` : failed2 ? "failed" : t._terminal_outcome === "completed" ? "completed" : t.status === "done" ? "finished" : null;
23572
23723
  if (o)
23573
23724
  t.description = `Iteration ${o}${t.provider ? `, built by ${t.provider}` : ""}.`;
23574
23725
  }
@@ -24575,7 +24726,7 @@ function mount19(ctx) {
24575
24726
  function computeStats(db, since) {
24576
24727
  const all = db.select().from(runs).all().filter((r) => !since || r.startedAt !== null && r.startedAt >= since);
24577
24728
  const by = {};
24578
- let finished = 0, running = 0, blocked = 0, verified = 0, unchecked = 0, failed = 0, satisfied = 0, other = 0;
24729
+ let finished = 0, running = 0, blocked = 0, verified = 0, unchecked = 0, failed2 = 0, satisfied = 0, other = 0;
24579
24730
  let mUsd = 0, mRuns = 0, pUsd = 0, pRuns = 0;
24580
24731
  for (const r of all) {
24581
24732
  if (r.endedAt)
@@ -24593,7 +24744,7 @@ function computeStats(db, since) {
24593
24744
  else if (verdict === "ALREADY_SATISFIED" || verdict === "ALREADY_SATISFIED (signature not checked)")
24594
24745
  satisfied++;
24595
24746
  else if (verdict === "FAILED" || verdict === "PARTIAL" || verdict === "TAMPERED")
24596
- failed++;
24747
+ failed2++;
24597
24748
  else
24598
24749
  other++;
24599
24750
  }
@@ -24625,8 +24776,8 @@ function computeStats(db, since) {
24625
24776
  runs_running: running,
24626
24777
  by_verdict: by,
24627
24778
  blocked_waiting: blocked,
24628
- verified_rate: verified + unchecked + failed ? verified / (verified + unchecked + failed) : null,
24629
- breakdown: { verified, failed, already_satisfied: satisfied, other },
24779
+ verified_rate: verified + unchecked + failed2 ? verified / (verified + unchecked + failed2) : null,
24780
+ breakdown: { verified, failed: failed2, already_satisfied: satisfied, other },
24630
24781
  verified_unchecked: unchecked,
24631
24782
  keys_configured: pubkeysFromEnv().configured === true,
24632
24783
  ...pubkeysFromEnv().reason ? { keys_reason: pubkeysFromEnv().reason } : {},
@@ -24650,9 +24801,9 @@ function mount20(ctx) {
24650
24801
  }
24651
24802
 
24652
24803
  // src/server/routes/verify.ts
24653
- import { readFileSync as readFileSync47, realpathSync as realpathSync20 } from "fs";
24804
+ import { readFileSync as readFileSync48, realpathSync as realpathSync20 } from "fs";
24654
24805
  import { createPublicKey as createPublicKey5 } from "crypto";
24655
- import { join as join61, sep as sep13 } from "path";
24806
+ import { join as join62, sep as sep13 } from "path";
24656
24807
  init_verify_cmd();
24657
24808
  init_seal();
24658
24809
  init_receipt_dsse();
@@ -24679,11 +24830,11 @@ function mount21(ctx) {
24679
24830
  return done(404, "receipt not available", "not_found");
24680
24831
  let runDir, file;
24681
24832
  try {
24682
- const root = realpathSync20(join61(repo.p, ".loki", "runs"));
24683
- runDir = realpathSync20(join61(root, run2));
24684
- if (runDir !== join61(root, run2))
24833
+ const root = realpathSync20(join62(repo.p, ".loki", "runs"));
24834
+ runDir = realpathSync20(join62(root, run2));
24835
+ if (runDir !== join62(root, run2))
24685
24836
  return done(404, "receipt not found", "not_found");
24686
- file = realpathSync20(join61(runDir, "receipt.json"));
24837
+ file = realpathSync20(join62(runDir, "receipt.json"));
24687
24838
  } catch {
24688
24839
  return done(404, "receipt not found", "not_found");
24689
24840
  }
@@ -24702,7 +24853,7 @@ function mount21(ctx) {
24702
24853
  sha3 = r.receiptSha256 ?? null;
24703
24854
  }
24704
24855
  try {
24705
- outcome = outcomeOf(JSON.parse(readFileSync47(file, "utf8")));
24856
+ outcome = outcomeOf(JSON.parse(readFileSync48(file, "utf8")));
24706
24857
  } catch {
24707
24858
  outcome = "UNREADABLE";
24708
24859
  }
@@ -24764,8 +24915,8 @@ function ttlCache(ttlMs = PROBE_CACHE_MS, now2 = Date.now) {
24764
24915
  }
24765
24916
 
24766
24917
  // src/server/legacy/shim.ts
24767
- import { existsSync as existsSync40, readFileSync as readFileSync48 } from "fs";
24768
- import { join as join62 } from "path";
24918
+ import { existsSync as existsSync40, readFileSync as readFileSync49 } from "fs";
24919
+ import { join as join63 } from "path";
24769
24920
 
24770
24921
  // src/server/legacy/routes.ts
24771
24922
  var LEGACY_ROUTES = [
@@ -25108,11 +25259,12 @@ function metricsMapped(repoDir) {
25108
25259
  // src/server/legacy/routes-control.ts
25109
25260
  function controlMapped(db, repoDir) {
25110
25261
  const loki = () => lokiDirOf(repoDir);
25262
+ const ctl = (action) => (c) => peerIsLoopback(c) && isLoopbackHost(c.req.header("host")) ? runControl(db, repoDir, action, c, "legacy") : c.json({ error: "loopback only" }, 403);
25111
25263
  return {
25112
- "POST /api/control/pause": (c) => runControl(db, repoDir, "pause", c, "legacy"),
25113
- "POST /api/control/resume": (c) => runControl(db, repoDir, "resume", c, "legacy"),
25114
- "POST /api/control/stop": (c) => runControl(db, repoDir, "stop", c, "legacy"),
25115
- "POST /api/council/force-review": (c) => runControl(db, repoDir, "council-review", c, "legacy"),
25264
+ "POST /api/control/pause": ctl("pause"),
25265
+ "POST /api/control/resume": ctl("resume"),
25266
+ "POST /api/control/stop": ctl("stop"),
25267
+ "POST /api/council/force-review": ctl("council-review"),
25116
25268
  "GET /api/council/state": (c) => c.json(councilState(loki())),
25117
25269
  "GET /api/council/verdicts": (c) => {
25118
25270
  const n2 = intQ(c.req.query("limit"), 1000);
@@ -25137,10 +25289,10 @@ var logged = new Set;
25137
25289
  var toHono = (p) => p.replace(/\{([A-Za-z_]+):path\}/g, ":$1{.+}").replace(/\{([A-Za-z_]+)\}/g, ":$1");
25138
25290
  var NOT_SUPPORTED = "not yet supported by the Control Plane";
25139
25291
  var repoVersion = () => {
25140
- for (const f of [join62(import.meta.dir, "../../../../../VERSION")]) {
25292
+ for (const f of [join63(import.meta.dir, "../../../../../VERSION")]) {
25141
25293
  try {
25142
25294
  if (existsSync40(f))
25143
- return readFileSync48(f, "utf8").trim();
25295
+ return readFileSync49(f, "utf8").trim();
25144
25296
  } catch {}
25145
25297
  }
25146
25298
  return "unknown";
@@ -25322,7 +25474,7 @@ function legacyShim(opts) {
25322
25474
 
25323
25475
  // src/server/app.ts
25324
25476
  var MAX_BODY2 = 1e6;
25325
- var defaultUiDir = () => [join63(import.meta.dir, "../../ui/dist"), join63(import.meta.dir, "../ui/dist")].find((d) => existsSync41(join63(d, "index.html")));
25477
+ var defaultUiDir = () => [join64(import.meta.dir, "../../ui/dist"), join64(import.meta.dir, "../ui/dist")].find((d) => existsSync41(join64(d, "index.html")));
25326
25478
  function createApp(opts) {
25327
25479
  const uiDir = opts.uiDir ?? defaultUiDir();
25328
25480
  const { db, sqlite } = openDb(opts.dbPath);
@@ -25351,10 +25503,10 @@ function createApp(opts) {
25351
25503
  await next();
25352
25504
  });
25353
25505
  const readInstalled = () => {
25354
- for (const f of [process.env.LOKI_VERSION_FILE, join63(import.meta.dir, "../../../../VERSION")]) {
25506
+ for (const f of [process.env.LOKI_VERSION_FILE, join64(import.meta.dir, "../../../../VERSION")]) {
25355
25507
  try {
25356
25508
  if (f)
25357
- return readFileSync49(f, "utf8").trim() || "unknown";
25509
+ return readFileSync50(f, "utf8").trim() || "unknown";
25358
25510
  } catch {}
25359
25511
  }
25360
25512
  return "unknown";
@@ -25469,7 +25621,7 @@ function createApp(opts) {
25469
25621
  return new Response(Bun.file(f));
25470
25622
  if (/\.[a-z0-9]+$/i.test(p))
25471
25623
  return c.json({ error: "not found" }, 404);
25472
- return new Response(Bun.file(join63(root, "index.html")));
25624
+ return new Response(Bun.file(join64(root, "index.html")));
25473
25625
  });
25474
25626
  return { app, db, close: () => {
25475
25627
  clearInterval(reconciler);