loki-mode 11.8.1 → 11.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/SKILL.md +2 -2
  2. package/VERSION +1 -1
  3. package/autonomy/lib/backlog.py +15 -3
  4. package/autonomy/lib/workspace.py +95 -4
  5. package/autonomy/loki +189 -108
  6. package/autonomy/trigger-server.py +302 -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 +25 -1
  21. package/loki-ts/dist/loki.js +1016 -946
  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 +457 -279
  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-D3PgG7SH.js} +1 -1
  33. package/web-app/dist/assets/{Avatar-0BTVsQ2A.js → Avatar-Ct77hvlG.js} +1 -1
  34. package/web-app/dist/assets/{Badge-Bbq8CXgn.js → Badge-Cg_gZaJF.js} +1 -1
  35. package/web-app/dist/assets/{Button-CKD4n0ld.js → Button-i9r2G84n.js} +1 -1
  36. package/web-app/dist/assets/{CockpitPage-DJhLNLAz.js → CockpitPage-C3UyLXyp.js} +1 -1
  37. package/web-app/dist/assets/{ComparePage-Di2akuII.js → ComparePage-B1dXHga_.js} +1 -1
  38. package/web-app/dist/assets/{ErrorBoundary-9lhsYG-z.js → ErrorBoundary-CQ_xREw0.js} +1 -1
  39. package/web-app/dist/assets/{EvidenceReceiptPanel-CQDGXvDr.js → EvidenceReceiptPanel-DwUxd_Xi.js} +1 -1
  40. package/web-app/dist/assets/{GitHubIssuesPanel-Y_w2k9Ej.js → GitHubIssuesPanel-C0jAnuAd.js} +1 -1
  41. package/web-app/dist/assets/{GitHubPRsPanel-pMSQedCv.js → GitHubPRsPanel-B4pNXJMz.js} +1 -1
  42. package/web-app/dist/assets/{HomePage-CIAkiGdt.js → HomePage-BC9UTS1l.js} +2 -2
  43. package/web-app/dist/assets/{LoginPage-BES4Pk6k.js → LoginPage-CnpdwK8F.js} +1 -1
  44. package/web-app/dist/assets/{MagicPage-B154fMhi.js → MagicPage-Bj0lZuNp.js} +1 -1
  45. package/web-app/dist/assets/{MetricsPage-BcNyaiUO.js → MetricsPage-yyw3VnfX.js} +1 -1
  46. package/web-app/dist/assets/{NotFoundPage-D28FqYxG.js → NotFoundPage-LMxkJP15.js} +1 -1
  47. package/web-app/dist/assets/{ProjectPage-C_O3u8Jx.js → ProjectPage-2x0MRkNu.js} +3 -3
  48. package/web-app/dist/assets/{ProjectsPage-uqdahuPA.js → ProjectsPage-A8J8o1gj.js} +1 -1
  49. package/web-app/dist/assets/{SettingsPage-DLvVA9OZ.js → SettingsPage-fwvOVcRb.js} +1 -1
  50. package/web-app/dist/assets/{ShowcasePage-BAZP8vsm.js → ShowcasePage-DqI4sUwk.js} +1 -1
  51. package/web-app/dist/assets/{SystemSettingsPage-DPYZkLWh.js → SystemSettingsPage-CyytfNo_.js} +1 -1
  52. package/web-app/dist/assets/{TeamsPage-BgSyb5Pd.js → TeamsPage-BTTe76W1.js} +1 -1
  53. package/web-app/dist/assets/{TemplatesPage-D_Jh5VU1.js → TemplatesPage-CIp3OTlL.js} +1 -1
  54. package/web-app/dist/assets/{TerminalOutput-4v8poDKb.js → TerminalOutput-dX5L4ky-.js} +1 -1
  55. package/web-app/dist/assets/{activity-CE2b6kdw.js → activity-uHXr2AaF.js} +1 -1
  56. package/web-app/dist/assets/{bell-CxJDlLSR.js → bell-D6RbxRMo.js} +1 -1
  57. package/web-app/dist/assets/{bot-C2WuK4zw.js → bot-D31w2Mu6.js} +1 -1
  58. package/web-app/dist/assets/{check-81epVldw.js → check-CoKJksbr.js} +1 -1
  59. package/web-app/dist/assets/{chevron-left-B6EXh_3C.js → chevron-left-Bjx7oX7s.js} +1 -1
  60. package/web-app/dist/assets/{circle-alert-0iqoo-iy.js → circle-alert-DOAh8Jmi.js} +1 -1
  61. package/web-app/dist/assets/{clock-CFrBpNUp.js → clock-DgEXPNWH.js} +1 -1
  62. package/web-app/dist/assets/{cloud-BBSFhUT_.js → cloud-0dXJFzBv.js} +1 -1
  63. package/web-app/dist/assets/{code-xml-BCU2V1rq.js → code-xml-DytQ_bq7.js} +1 -1
  64. package/web-app/dist/assets/{database-D2hvO52K.js → database-Qyjryb72.js} +1 -1
  65. package/web-app/dist/assets/{dollar-sign-CXFA9b6T.js → dollar-sign-BzhTVy1e.js} +1 -1
  66. package/web-app/dist/assets/{file-code-corner-CqTCEEfS.js → file-code-corner-DTdSOALw.js} +1 -1
  67. package/web-app/dist/assets/{file-plus-DYFbkGbs.js → file-plus-VdjOG1RK.js} +1 -1
  68. package/web-app/dist/assets/{globe-DpYHLobk.js → globe-BhxNOfyM.js} +1 -1
  69. package/web-app/dist/assets/{hammer-CZ97SEYo.js → hammer-MQzrw8ee.js} +1 -1
  70. package/web-app/dist/assets/{index-DzswgpRo.js → index-DaxrMbHf.js} +3 -3
  71. package/web-app/dist/assets/{layers-2Tq0edx4.js → layers-C7nusTP-.js} +1 -1
  72. package/web-app/dist/assets/{loader-circle-BFP4kUX-.js → loader-circle-B1qBkgOY.js} +1 -1
  73. package/web-app/dist/assets/{lock-DkKdZH4d.js → lock-BdkOxK2O.js} +1 -1
  74. package/web-app/dist/assets/{package-ChOGD8yf.js → package-RVyK96Zn.js} +1 -1
  75. package/web-app/dist/assets/{plus-hggB1o6b.js → plus-ntmwFRrp.js} +1 -1
  76. package/web-app/dist/assets/{refresh-cw-DYwVLOeF.js → refresh-cw-BJ39b_PP.js} +1 -1
  77. package/web-app/dist/assets/{rotate-ccw-CUdY506q.js → rotate-ccw-uPx_-3vz.js} +1 -1
  78. package/web-app/dist/assets/{scroll-text-Wvidz5uN.js → scroll-text-D9qvXkj-.js} +1 -1
  79. package/web-app/dist/assets/{server-O11QmkCN.js → server-gs4xmD8Y.js} +1 -1
  80. package/web-app/dist/assets/{shield-alert-DXXx27uV.js → shield-alert-O2_YffGI.js} +1 -1
  81. package/web-app/dist/assets/{thumbs-up-DF8ivs3A.js → thumbs-up-B3RVBhQx.js} +1 -1
  82. package/web-app/dist/assets/{trash-2-LA6PW0Nf.js → trash-2-DEUt7qhq.js} +1 -1
  83. package/web-app/dist/assets/{trending-down-Bw_vLKHT.js → trending-down-oyTns4UZ.js} +1 -1
  84. package/web-app/dist/assets/{trending-up-tX8motha.js → trending-up-CdpZK0Oc.js} +1 -1
  85. package/web-app/dist/assets/{upload-CxtTWJd2.js → upload-D9sbjjC8.js} +1 -1
  86. package/web-app/dist/assets/{usePolling-D-P5Iofq.js → usePolling-6ognrnyV.js} +1 -1
  87. package/web-app/dist/assets/{user-dKTcuUFq.js → user-CBKc8QoI.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,12 @@ 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;
10512
+ }
10513
+ function summaryPassFailCount(out2) {
10514
+ return ["pass", "fail"].reduce((t, k) => t + +([...out2.matchAll(new RegExp(`^(?:#|\\u2139) ${k} (\\d+)$`, "gm"))].pop()?.[1] ?? 0), 0);
10424
10515
  }
10425
10516
  function skipped(raw2) {
10426
10517
  return stripAnsi(raw2).split(`
@@ -10488,7 +10579,7 @@ function markExecuted(a, executed) {
10488
10579
  return { ...a, checks_deferred, summary: summaryOf({ ...a, checks_deferred }) };
10489
10580
  }
10490
10581
  function classifyRisk(files, env = process.env) {
10491
- const lines = files.reduce((n2, f) => n2 + f.added + f.deleted, 0);
10582
+ const lines2 = files.reduce((n2, f) => n2 + f.added + f.deleted, 0);
10492
10583
  const sensitive = files.flatMap((f) => {
10493
10584
  const hit = SENSITIVE.find(([, re]) => re.test(f.path));
10494
10585
  return hit && !isTestFile(f.path) ? [`${f.path} (${hit[0]})`] : [];
@@ -10506,14 +10597,14 @@ function classifyRisk(files, env = process.env) {
10506
10597
  high.push(`public API surface: ${api.slice(0, 3).join(", ")}`);
10507
10598
  if (files.length >= FILES_HIGH)
10508
10599
  high.push(`${files.length} files >= ${FILES_HIGH}`);
10509
- if (lines >= LINES_HIGH)
10510
- high.push(`${lines} changed lines >= ${LINES_HIGH}`);
10600
+ if (lines2 >= LINES_HIGH)
10601
+ high.push(`${lines2} changed lines >= ${LINES_HIGH}`);
10511
10602
  if (testsModified.length)
10512
10603
  med.push(`existing tests or test config modified: ${testsModified.slice(0, 3).join(", ")}`);
10513
10604
  if (files.length >= FILES_MEDIUM)
10514
10605
  med.push(`${files.length} files >= ${FILES_MEDIUM}`);
10515
- if (lines >= LINES_MEDIUM)
10516
- med.push(`${lines} changed lines >= ${LINES_MEDIUM}`);
10606
+ if (lines2 >= LINES_MEDIUM)
10607
+ med.push(`${lines2} changed lines >= ${LINES_MEDIUM}`);
10517
10608
  const computed = high.length ? "high" : med.length ? "medium" : "low";
10518
10609
  const reasons = high.length ? [...high, ...med] : med.length ? med : [`${files.length} file${files.length === 1 ? "" : "s"}, no sensitive paths`];
10519
10610
  let tier = computed, override, ignored;
@@ -10536,7 +10627,7 @@ function classifyRisk(files, env = process.env) {
10536
10627
  tier,
10537
10628
  computed,
10538
10629
  reasons,
10539
- stats: { files: files.length, lines, sensitive, tests_modified: testsModified, tests_deleted: testsDeleted, api },
10630
+ stats: { files: files.length, lines: lines2, sensitive, tests_modified: testsModified, tests_deleted: testsDeleted, api },
10540
10631
  checks_selected: plan.selected,
10541
10632
  checks_deferred,
10542
10633
  ...override ? { override } : {},
@@ -10571,7 +10662,7 @@ function diffFiles(repoDir, baseSha) {
10571
10662
  return f;
10572
10663
  let n2 = 0;
10573
10664
  try {
10574
- n2 = readFileSync28(join39(repoDir, path), "utf8").split(`
10665
+ n2 = readFileSync29(join40(repoDir, path), "utf8").split(`
10575
10666
  `).length;
10576
10667
  } catch {}
10577
10668
  return { path, added: n2, deleted: 0, status: "added" };
@@ -10579,11 +10670,11 @@ function diffFiles(repoDir, baseSha) {
10579
10670
  }
10580
10671
  function recordRisk(repoDir, a) {
10581
10672
  try {
10582
- const dir = join39(repoDir, ".loki", "state"), p = join39(dir, "risk-tier.json");
10673
+ const dir = join40(repoDir, ".loki", "state"), p = join40(dir, "risk-tier.json");
10583
10674
  mkdirSync12(dir, { recursive: true });
10584
10675
  writeFileSync15(`${p}.tmp`, JSON.stringify(a, null, 2) + `
10585
10676
  `);
10586
- renameSync5(`${p}.tmp`, p);
10677
+ renameSync6(`${p}.tmp`, p);
10587
10678
  return p;
10588
10679
  } catch {
10589
10680
  return null;
@@ -10624,10 +10715,10 @@ var init_check_result = __esm(() => {
10624
10715
  import { spawnSync as spawnSync3 } from "child_process";
10625
10716
  import { mkdtempSync as mkdtempSync5, realpathSync as realpathSync16, rmSync as rmSync9 } from "fs";
10626
10717
  import { tmpdir as tmpdir7 } from "os";
10627
- import { basename as basename14, extname as extname2, join as join40 } from "path";
10718
+ import { basename as basename14, extname as extname2, join as join41 } from "path";
10628
10719
  function changedFilesNamed(out2, repoDir, changed) {
10629
10720
  return changed.filter((f) => {
10630
- if (out2.includes(f) || out2.includes(join40(repoDir, f)))
10721
+ if (out2.includes(f) || out2.includes(join41(repoDir, f)))
10631
10722
  return true;
10632
10723
  const segs = f.split("/");
10633
10724
  if (suffixes(segs, 2).some((x) => x !== f && bounded(out2, x, "(?![\\w-])")))
@@ -10738,7 +10829,7 @@ async function loadErrorIsHarnessOwned(i) {
10738
10829
  return false;
10739
10830
  if (i.args.some((a) => changed.includes(a.replace(/^\.\//, ""))))
10740
10831
  return false;
10741
- const dir = mkdtempSync5(join40(tmpdir7(), "e10-loadown-"));
10832
+ const dir = mkdtempSync5(join41(tmpdir7(), "e10-loadown-"));
10742
10833
  const git3 = (args) => {
10743
10834
  safeGit(i.repoDir, args, { stdio: "ignore" });
10744
10835
  };
@@ -10770,7 +10861,7 @@ var init_load_owner = __esm(() => {
10770
10861
  });
10771
10862
 
10772
10863
  // ../../loki-ts/src/util/wall_owned.ts
10773
- import { isAbsolute as isAbsolute4, join as join41, relative as relative4 } from "path";
10864
+ import { isAbsolute as isAbsolute4, join as join42, relative as relative4 } from "path";
10774
10865
  function failingLocations(raw2, cwd, repoDir) {
10775
10866
  const out2 = raw2.replace(ANSI2, "").replace(/\r\n?/g, `
10776
10867
  `), files = new Set;
@@ -10802,7 +10893,7 @@ function failingLocations(raw2, cwd, repoDir) {
10802
10893
  }
10803
10894
  if (errors2 === 0 || located !== errors2)
10804
10895
  return null;
10805
- return [...files].map((f) => relative4(repoDir, isAbsolute4(f) ? f : join41(cwd, f)));
10896
+ return [...files].map((f) => relative4(repoDir, isAbsolute4(f) ? f : join42(cwd, f)));
10806
10897
  }
10807
10898
  function wallOwnedFailure(raw2, cwd, repoDir, wall) {
10808
10899
  const locs = failingLocations(raw2, cwd, repoDir);
@@ -10831,11 +10922,11 @@ var init_state = __esm(() => {
10831
10922
  });
10832
10923
 
10833
10924
  // ../../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";
10925
+ import { existsSync as existsSync22, readFileSync as readFileSync30, mkdirSync as mkdirSync13, readdirSync as readdirSync12, renameSync as renameSync7, rmSync as rmSync10, writeFileSync as writeFileSync16 } from "fs";
10926
+ import { basename as basename15, join as join43, resolve as resolve11, isAbsolute as isAbsolute5 } from "path";
10836
10927
  function loadProfile(repoDir) {
10837
10928
  try {
10838
- const p = JSON.parse(readFileSync29(commandProfilePath(repoDir), "utf8"));
10929
+ const p = JSON.parse(readFileSync30(commandProfilePath(repoDir), "utf8"));
10839
10930
  return p?.version === 1 && Array.isArray(p.commands) && p.baseline ? p : null;
10840
10931
  } catch {
10841
10932
  return null;
@@ -10866,7 +10957,7 @@ function attributeFullSuite(repoDir, baseSha, full, blocked, changed) {
10866
10957
  const block = new Set(blocked);
10867
10958
  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
10959
  }
10869
- var commandProfilePath = (repoDir) => join42(repoDir, ".loki", "state", "repo-profile.json");
10960
+ var commandProfilePath = (repoDir) => join43(repoDir, ".loki", "state", "repo-profile.json");
10870
10961
  var init_repo_profile = __esm(() => {
10871
10962
  init_failures();
10872
10963
  init_check_result();
@@ -10879,20 +10970,20 @@ var init_repo_profile = __esm(() => {
10879
10970
  import { createHash as createHash10 } from "crypto";
10880
10971
  import {
10881
10972
  existsSync as existsSync23,
10882
- readdirSync as readdirSync12,
10883
- readFileSync as readFileSync30,
10973
+ readdirSync as readdirSync13,
10974
+ readFileSync as readFileSync31,
10884
10975
  statSync as statSync16,
10885
10976
  writeFileSync as writeFileSync17,
10886
10977
  mkdirSync as mkdirSync14
10887
10978
  } from "fs";
10888
- import { basename as basename16, dirname as dirname13, join as join43, resolve as resolve12 } from "path";
10979
+ import { basename as basename16, dirname as dirname13, join as join44, resolve as resolve12 } from "path";
10889
10980
  function readManifest(path) {
10890
10981
  if (!existsSync23(path)) {
10891
10982
  return null;
10892
10983
  }
10893
10984
  let raw2;
10894
10985
  try {
10895
- raw2 = readFileSync30(path, "utf8");
10986
+ raw2 = readFileSync31(path, "utf8");
10896
10987
  } catch {
10897
10988
  return null;
10898
10989
  }
@@ -10929,7 +11020,7 @@ function resolveMembers(rootDir, declared, selfBasename) {
10929
11020
  const seen = new Set;
10930
11021
  let children;
10931
11022
  try {
10932
- children = readdirSync12(rootDir);
11023
+ children = readdirSync13(rootDir);
10933
11024
  } catch {
10934
11025
  children = [];
10935
11026
  }
@@ -10944,7 +11035,7 @@ function resolveMembers(rootDir, declared, selfBasename) {
10944
11035
  for (const child of children) {
10945
11036
  if (!matchPattern(name, child))
10946
11037
  continue;
10947
- const full = join43(rootDir, child);
11038
+ const full = join44(rootDir, child);
10948
11039
  if (seen.has(full))
10949
11040
  continue;
10950
11041
  let st;
@@ -10961,7 +11052,7 @@ function resolveMembers(rootDir, declared, selfBasename) {
10961
11052
  }
10962
11053
  if (declared.length === 0) {
10963
11054
  for (const child of children) {
10964
- const full = join43(rootDir, child);
11055
+ const full = join44(rootDir, child);
10965
11056
  if (seen.has(full))
10966
11057
  continue;
10967
11058
  let st;
@@ -10972,7 +11063,7 @@ function resolveMembers(rootDir, declared, selfBasename) {
10972
11063
  }
10973
11064
  if (!st.isDirectory())
10974
11065
  continue;
10975
- if (!existsSync23(join43(full, ".loki", "app.json")))
11066
+ if (!existsSync23(join44(full, ".loki", "app.json")))
10976
11067
  continue;
10977
11068
  seen.add(full);
10978
11069
  out2.push(full);
@@ -10993,17 +11084,17 @@ function cacheKey(manifestPaths) {
10993
11084
  }
10994
11085
  function writeCache(targetDir, key, result) {
10995
11086
  try {
10996
- const cacheDir = join43(targetDir, ".loki", "state");
11087
+ const cacheDir = join44(targetDir, ".loki", "state");
10997
11088
  mkdirSync14(cacheDir, { recursive: true });
10998
- const cachePath = join43(cacheDir, "project-graph.json");
11089
+ const cachePath = join44(cacheDir, "project-graph.json");
10999
11090
  writeFileSync17(cachePath, JSON.stringify({ key, result }, null, 2), "utf8");
11000
11091
  } catch {}
11001
11092
  }
11002
11093
  function discoverProjectGraph(targetDir) {
11003
11094
  const absTarget = resolve12(targetDir);
11004
- const targetManifest = join43(absTarget, ".loki", "app.json");
11095
+ const targetManifest = join44(absTarget, ".loki", "app.json");
11005
11096
  const parentDir = dirname13(absTarget);
11006
- const parentManifest = join43(parentDir, ".loki", "app.json");
11097
+ const parentManifest = join44(parentDir, ".loki", "app.json");
11007
11098
  const examined = [];
11008
11099
  let chosenManifest = null;
11009
11100
  let chosenRoot = "";
@@ -11023,7 +11114,7 @@ function discoverProjectGraph(targetDir) {
11023
11114
  const members = resolveMembers(chosenRoot, declared, basename16(absTarget));
11024
11115
  const filtered = [];
11025
11116
  for (const m of members) {
11026
- const sibManifestPath = join43(m, ".loki", "app.json");
11117
+ const sibManifestPath = join44(m, ".loki", "app.json");
11027
11118
  if (existsSync23(sibManifestPath)) {
11028
11119
  examined.push(sibManifestPath);
11029
11120
  const sibManifest = readManifest(sibManifestPath);
@@ -11096,15 +11187,15 @@ var init_steer = __esm(() => {
11096
11187
  });
11097
11188
 
11098
11189
  // ../../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";
11190
+ import { existsSync as existsSync24, mkdirSync as mkdirSync15, readFileSync as readFileSync32 } from "fs";
11191
+ import { dirname as dirname14, join as join45 } from "path";
11101
11192
  import { createHash as createHash11 } from "crypto";
11102
11193
  function loadLessons(repoDir) {
11103
11194
  const p = lessonsPath(repoDir);
11104
11195
  if (!existsSync24(p))
11105
11196
  return [];
11106
11197
  try {
11107
- const j = JSON.parse(readFileSync31(p, "utf-8"));
11198
+ const j = JSON.parse(readFileSync32(p, "utf-8"));
11108
11199
  return Array.isArray(j.lessons) ? j.lessons : [];
11109
11200
  } catch {
11110
11201
  return [];
@@ -11146,7 +11237,7 @@ function recordRunVerdict(repoDir, runId, verdict) {
11146
11237
  if (dirty)
11147
11238
  save(repoDir, lessons);
11148
11239
  }
11149
- var lessonsPath = (repoDir) => join44(repoDir, ".loki", "memory", "semantic", "pr-lessons.json"), KINDS;
11240
+ var lessonsPath = (repoDir) => join45(repoDir, ".loki", "memory", "semantic", "pr-lessons.json"), KINDS;
11150
11241
  var init_pr_lessons = __esm(() => {
11151
11242
  init_atomic();
11152
11243
  KINDS = new Set(["pr", "receipt", "ticket", "rejected"]);
@@ -11158,11 +11249,30 @@ var init_model_downgrades = __esm(() => {
11158
11249
  init_decision();
11159
11250
  });
11160
11251
 
11252
+ // ../../loki-ts/src/e10ext/select.ts
11253
+ var PYTEST_COLLECTION_EXITS;
11254
+ var init_select = __esm(() => {
11255
+ PYTEST_COLLECTION_EXITS = new Set([2, 3, 4, 5]);
11256
+ });
11257
+
11258
+ // ../../loki-ts/src/e10ext/treeswap.ts
11259
+ var init_treeswap = __esm(() => {
11260
+ init_safe_git();
11261
+ });
11262
+
11263
+ // ../../loki-ts/src/features/attempts.ts
11264
+ var init_attempts = __esm(() => {
11265
+ init_credential_env();
11266
+ init_safe_git();
11267
+ init_select();
11268
+ init_treeswap();
11269
+ });
11270
+
11161
11271
  // ../../loki-ts/src/providers/mcp_config.ts
11162
11272
  var init_mcp_config = () => {};
11163
11273
 
11164
11274
  // ../../loki-ts/src/providers/claude_flags.ts
11165
- import { existsSync as existsSync25, readFileSync as readFileSync32 } from "fs";
11275
+ import { existsSync as existsSync25, readFileSync as readFileSync33 } from "fs";
11166
11276
  import { resolve as resolvePath, dirname as dirname15 } from "path";
11167
11277
  import { fileURLToPath as fileURLToPath3 } from "url";
11168
11278
  function designArchetypesText() {
@@ -11174,7 +11284,7 @@ function designArchetypesText() {
11174
11284
  ]) {
11175
11285
  const p = resolvePath(moduleDir, ...rel);
11176
11286
  if (existsSync25(p))
11177
- return readFileSync32(p, "utf8");
11287
+ return readFileSync33(p, "utf8");
11178
11288
  }
11179
11289
  } catch {}
11180
11290
  return "";
@@ -11252,14 +11362,14 @@ var init_providers = __esm(() => {
11252
11362
  });
11253
11363
 
11254
11364
  // ../../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";
11365
+ import { existsSync as existsSync26, lstatSync as lstatSync9, mkdirSync as mkdirSync16, readFileSync as readFileSync34, writeFileSync as writeFileSync18 } from "fs";
11366
+ import { join as join46 } from "path";
11257
11367
  function loadRouteRecord(runDir) {
11258
11368
  try {
11259
- const p = join45(runDir, ROUTE_RECORD_FILE);
11369
+ const p = join46(runDir, ROUTE_RECORD_FILE);
11260
11370
  if (!existsSync26(p) || !lstatSync9(p).isFile())
11261
11371
  return;
11262
- const v = JSON.parse(readFileSync33(p, "utf8"));
11372
+ const v = JSON.parse(readFileSync34(p, "utf8"));
11263
11373
  return typeof v === "object" && v !== null && !Array.isArray(v) ? v : undefined;
11264
11374
  } catch {
11265
11375
  return;
@@ -11269,7 +11379,7 @@ var ROUTE_RECORD_FILE = "route.json";
11269
11379
  var init_route_record = () => {};
11270
11380
 
11271
11381
  // ../../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";
11382
+ import { existsSync as existsSync27, mkdirSync as mkdirSync17, readFileSync as readFileSync35, renameSync as renameSync8, rmSync as rmSync11, writeFileSync as writeFileSync19 } from "fs";
11273
11383
  import { resolve as resolve13 } from "path";
11274
11384
  function validRun(v) {
11275
11385
  if (typeof v !== "object" || v === null)
@@ -11285,7 +11395,7 @@ function readRunHistory(key, cacheRoot) {
11285
11395
  if (!existsSync27(path))
11286
11396
  return [];
11287
11397
  try {
11288
- const parsed = JSON.parse(readFileSync34(path, "utf8"));
11398
+ const parsed = JSON.parse(readFileSync35(path, "utf8"));
11289
11399
  const runs2 = parsed?.runs;
11290
11400
  return Array.isArray(runs2) ? runs2.filter(validRun) : [];
11291
11401
  } catch {
@@ -11300,7 +11410,7 @@ function appendRunOutcome(key, outcome, cacheRoot) {
11300
11410
  const tmp = `${target}.${process.pid}.tmp`;
11301
11411
  try {
11302
11412
  writeFileSync19(tmp, JSON.stringify({ runs: runs2 }));
11303
- renameSync7(tmp, target);
11413
+ renameSync8(tmp, target);
11304
11414
  } catch (e) {
11305
11415
  rmSync11(tmp, { force: true, recursive: false });
11306
11416
  throw e;
@@ -11319,7 +11429,7 @@ var init_history_store = __esm(() => {
11319
11429
  });
11320
11430
 
11321
11431
  // ../../loki-ts/src/runner/router/history.ts
11322
- import { dirname as dirname16, join as join46, resolve as resolve14 } from "path";
11432
+ import { dirname as dirname16, join as join47, resolve as resolve14 } from "path";
11323
11433
  import { fileURLToPath as fileURLToPath4 } from "url";
11324
11434
  function shapeKey(model) {
11325
11435
  if (!model || model.status !== "ok")
@@ -11420,6 +11530,7 @@ var init_implement = __esm(() => {
11420
11530
  init_context();
11421
11531
  init_model_downgrades();
11422
11532
  init_sizing();
11533
+ init_attempts();
11423
11534
  init_plan();
11424
11535
  init_verify();
11425
11536
  init_resolve();
@@ -11479,7 +11590,7 @@ var init_worker = __esm(() => {
11479
11590
  });
11480
11591
 
11481
11592
  // ../../loki-ts/src/project_model/package_suite.ts
11482
- import { join as join47 } from "path";
11593
+ import { join as join48 } from "path";
11483
11594
  async function runPackageSuites(repoDir, signal, opts, checks, notProven) {
11484
11595
  const api = loadProjectApi(repoDir);
11485
11596
  if (!isMultiRoot(api))
@@ -11490,7 +11601,7 @@ async function runPackageSuites(repoDir, signal, opts, checks, notProven) {
11490
11601
  for (const pkg of withTests) {
11491
11602
  const tc = pkg.commands.test, name = `full suite: ${pkg.name}`, cmd = `(cd ${tc.cwd} && ${tc.cmd})`;
11492
11603
  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 } } : {} });
11604
+ const r = await run(["bash", "-c", tc.cmd], { cwd: join48(repoDir, tc.cwd), timeoutMs: opts.timeoutMs, ...opts.path ? { env: { PATH: opts.path } } : {} });
11494
11605
  const duration_s = (Date.now() - started) / 1000;
11495
11606
  if (signal.aborted) {
11496
11607
  checks.push({ name, cmd, result: "not_run", duration_s });
@@ -11499,7 +11610,7 @@ async function runPackageSuites(repoDir, signal, opts, checks, notProven) {
11499
11610
  }
11500
11611
  const out2 = `${r.stdout}
11501
11612
  ${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 } } : {} });
11613
+ 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
11614
  if (lr) {
11504
11615
  checks.push({ name, cmd, result: "not_run", duration_s });
11505
11616
  notProven.add(`${lr} (${name}; harness-owned)`);
@@ -11533,7 +11644,7 @@ __export(exports_deep, {
11533
11644
  DEFAULT_PUSH_SH: () => DEFAULT_PUSH_SH
11534
11645
  });
11535
11646
  import { existsSync as existsSync28, mkdirSync as mkdirSync18, writeFileSync as writeFileSync20 } from "fs";
11536
- import { join as join48 } from "path";
11647
+ import { join as join49 } from "path";
11537
11648
  async function runFullSuite(ctx, signal, opts, checks, notProven) {
11538
11649
  if (await runPackageSuites(ctx.repoDir, signal, { path: opts.path, timeoutMs: opts.fullSuiteTimeoutMs ?? FULL_SUITE_TIMEOUT_MS, baseSha: ctx.baseSha }, checks, notProven))
11539
11650
  return;
@@ -11630,7 +11741,7 @@ async function runSecretScan(ctx, changed, opts, checks, notProven) {
11630
11741
  }
11631
11742
  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
11743
  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 });
11744
+ const r = await run(["bash", "-c", inline, "_", scriptPath, ...changed.map((f) => join49(ctx.repoDir, f))], { cwd: ctx.repoDir, timeoutMs: 30000 });
11634
11745
  const durationS = (Date.now() - started) / 1000;
11635
11746
  if (r.exitCode === 3) {
11636
11747
  checks.push({ name, cmd: "secret-scan.sh", result: "not_run", duration_s: durationS });
@@ -11651,10 +11762,10 @@ function commentArgv(prNumber, bodyFile) {
11651
11762
  return ["comment", prNumber, bodyFile];
11652
11763
  }
11653
11764
  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(`
11765
+ const lines2 = [`## Loki deep verify: ${statusState}`, "", "### Checks"];
11766
+ lines2.push(...checks.length ? checks.map((c) => `- ${c.result}: ${c.name}${c.reason ? ` (${c.reason})` : ""}`) : ["- none"]);
11767
+ lines2.push("", "### NOT PROVEN", ...notProven.length ? notProven.map((n2) => `- ${n2}`) : ["- none"]);
11768
+ return `${lines2.join(`
11658
11769
  `)}
11659
11770
  `;
11660
11771
  }
@@ -11671,7 +11782,7 @@ async function postResults(ctx, opts, checks, notProven, statusState) {
11671
11782
  const prNumber = prUrl ? PR_NUMBER_RE.exec(prUrl)?.[1] : undefined;
11672
11783
  if (prNumber) {
11673
11784
  mkdirSync18(ctx.runDir, { recursive: true });
11674
- const bodyFile = join48(ctx.runDir, "deep-comment.md");
11785
+ const bodyFile = join49(ctx.runDir, "deep-comment.md");
11675
11786
  writeFileSync20(bodyFile, buildCommentBody(checks, notProven, statusState), "utf8");
11676
11787
  const commentResult = await run(["bash", scriptPath, ...commentArgv(prNumber, bodyFile)], { cwd: ctx.repoDir, env, timeoutMs: 30000 });
11677
11788
  if (commentResult.exitCode !== 0)
@@ -11725,7 +11836,7 @@ async function runDeep(ctx, signal, opts = {}) {
11725
11836
  const sig = signReceipt(`${ctx.runId}-deep`, addendumSha256);
11726
11837
  const addendum = { ...addendumBody, addendum_sha256: addendumSha256, verification: { jwt: sig.jwt, kid: sig.kid } };
11727
11838
  mkdirSync18(ctx.runDir, { recursive: true });
11728
- writeFileSync20(join48(ctx.runDir, "receipt-addendum-1.json"), JSON.stringify(addendum, null, 2) + `
11839
+ writeFileSync20(join49(ctx.runDir, "receipt-addendum-1.json"), JSON.stringify(addendum, null, 2) + `
11729
11840
  `);
11730
11841
  const pushNotProven = await postResults(ctx, opts, checks, notProvenList, statusState);
11731
11842
  const allNotProven = [...notProvenList, ...pushNotProven];
@@ -11770,7 +11881,7 @@ var init_deep = __esm(() => {
11770
11881
  // ../../loki-ts/src/engine10/stages/verify.ts
11771
11882
  import { existsSync as existsSync29, mkdtempSync as mkdtempSync6, realpathSync as realpathSync17, rmSync as rmSync12 } from "fs";
11772
11883
  import { tmpdir as tmpdir8 } from "os";
11773
- import { dirname as dirname17, isAbsolute as isAbsolute6, join as join49, relative as relative5 } from "path";
11884
+ import { dirname as dirname17, isAbsolute as isAbsolute6, join as join50, relative as relative5 } from "path";
11774
11885
  function resolveTool(repoDir, bin, fallback, lastResort = fallback) {
11775
11886
  const inRepo = (p) => {
11776
11887
  try {
@@ -11781,7 +11892,7 @@ function resolveTool(repoDir, bin, fallback, lastResort = fallback) {
11781
11892
  }
11782
11893
  };
11783
11894
  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);
11895
+ const project = [join50(repoDir, ".venv", "bin", bin), join50(repoDir, "venv", "bin", bin), ...venvEnv && inRepo(venvEnv) ? [join50(venvEnv, "bin", bin)] : []].find(existsSync29);
11785
11896
  return project ? [project, "project"] : [Bun.which(fallback) ? fallback : lastResort, "system"];
11786
11897
  }
11787
11898
  function runnerCmd(t, repoDir) {
@@ -11811,13 +11922,6 @@ function commandFor(t, repoDir, api) {
11811
11922
  const [cmd, args, interpreter] = runnerCmd(site ? { runner: t.runner, path: site.file } : t, repoDir);
11812
11923
  return { cwd: site?.cwd ?? repoDir, cmd, args, argv: [cmd, ...args], ...interpreter ? { interpreter } : {}, pkgRoot: site?.root ?? "." };
11813
11924
  }
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
11925
  async function runOnce(cmd, args, cwd, signal, opts) {
11822
11926
  if (!Bun.which(cmd, { PATH: opts.path ?? process.env["PATH"] ?? "" }))
11823
11927
  return { ok: false, missing: true, cut: false, out: "" };
@@ -11856,8 +11960,8 @@ function firstError(out2) {
11856
11960
  return l2;
11857
11961
  }
11858
11962
  };
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)) ?? "";
11963
+ const lines2 = out2.slice(-65536).split(`
11964
+ `).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
11965
  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
11966
  }
11863
11967
  async function runCheck(ctx, name, cmd, args, signal, checks, opts = {}) {
@@ -11899,7 +12003,7 @@ async function subtractBase(ctx, api, checks, tests, changed, wall, rel, signal)
11899
12003
  const sus = pairs.filter(({ c }) => rel.has(c.name) && c.result === "pass");
11900
12004
  if (!(un.some(({ c }) => c.result === "fail" && c.ids?.length) || sus.length) || signal.aborted)
11901
12005
  return out2;
11902
- const dir = mkdtempSync6(join49(tmpdir8(), "e10-base-"));
12006
+ const dir = mkdtempSync6(join50(tmpdir8(), "e10-base-"));
11903
12007
  const git3 = (args) => {
11904
12008
  safeGit(ctx.repoDir, args, { stdio: "ignore", repoDrivers: true });
11905
12009
  };
@@ -11907,12 +12011,15 @@ async function subtractBase(ctx, api, checks, tests, changed, wall, rel, signal)
11907
12011
  git3(["-c", "core.hooksPath=/dev/null", "worktree", "add", "--detach", dir, ctx.baseSha]);
11908
12012
  const base = new Map;
11909
12013
  for (const { c, t } of new Set([...un, ...sus])) {
11910
- const bc = commandFor(t, dir, api), b = await runOnce(bc.cmd, bc.args, bc.cwd, signal, {});
11911
- base.set(c, { red: !b.ok && !b.cut ? failIds(b.out) : [], n: ran(b.out) ?? ["pass", "fail"].reduce((t2, k) => t2 + +([...b.out.matchAll(new RegExp(`^(?:#|\\u2139) ${k} (\\d+)$`, "gm"))].pop()?.[1] ?? 0), 0), sk: skipped(b.out) });
12014
+ const bc = commandFor(t, dir, api);
12015
+ const b = await runOnce(bc.cmd, bc.args, bc.cwd, signal, {});
12016
+ const red = !b.ok && !b.cut ? failIds(b.out) : [];
12017
+ base.set(c, { red, n: ran(b.out) ?? summaryPassFailCount(b.out), sk: skipped(b.out) });
11912
12018
  }
11913
12019
  out2.weak = sus.filter(({ c }) => (c.sk ?? 0) > base.get(c).sk || (c.n ?? 0) < base.get(c).n).map(({ c }) => c.name);
11914
- for (const { c } of sus)
12020
+ for (const { c } of sus) {
11915
12021
  out2.cnt[c.name] = { b: { run: base.get(c).n, skipped: base.get(c).sk }, h: { run: c.n ?? 0, skipped: c.sk ?? 0 } };
12022
+ }
11916
12023
  const progress = checks.some((c) => wall.has(c.name) && c.result === "pass") || sus.some(({ c }) => !out2.weak.includes(c.name) && base.get(c).red.length > 0 && (c.n ?? 0) >= base.get(c).n);
11917
12024
  if (progress) {
11918
12025
  for (const { c } of un) {
@@ -11934,7 +12041,7 @@ async function subtractBase(ctx, api, checks, tests, changed, wall, rel, signal)
11934
12041
  return out2;
11935
12042
  }
11936
12043
  function isLokiModeRepo(repoDir) {
11937
- return existsSync29(join49(repoDir, "scripts", "select-tests.sh"));
12044
+ return existsSync29(join50(repoDir, "scripts", "select-tests.sh"));
11938
12045
  }
11939
12046
  async function runLintChecks(ctx, changed, signal, checks, opts = {}) {
11940
12047
  const { api, ...runOpts } = opts;
@@ -11951,15 +12058,16 @@ async function runLintChecks(ctx, changed, signal, checks, opts = {}) {
11951
12058
  }
11952
12059
  for (const g of groupByPackage(api, ctx.repoDir, changed.filter((f) => /\.(ts|tsx|js|jsx)$/.test(f)))) {
11953
12060
  const sfx = g.root === "." ? "" : `:${g.root}`, co = g.root === "." ? opts : { ...opts, cwd: g.cwd };
11954
- if (existsSync29(join49(g.cwd, "tsconfig.json")))
12061
+ if (existsSync29(join50(g.cwd, "tsconfig.json")))
11955
12062
  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))))
12063
+ if (ESLINT_CONFIGS.some((f) => existsSync29(join50(g.cwd, f))))
11957
12064
  await runCheck(ctx, `lint:eslint${sfx}`, "npx", ["eslint", ...g.rel], signal, checks, { ...co, kind: "static" });
11958
12065
  }
11959
12066
  }
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;
12067
+ var CHECK_TIMEOUT_MS = 60000, dedupeTests = (tests) => [...new Map(tests.map((t) => [`${t.runner}:${t.path}`, t])).values()], CODE_EXT, ESLINT_CONFIGS, verifyStage;
11961
12068
  var init_verify = __esm(() => {
11962
12069
  init_scope();
12070
+ init_runner_config();
11963
12071
  init_assert_delta();
11964
12072
  init_failures();
11965
12073
  init_load_owner();
@@ -11973,10 +12081,8 @@ var init_verify = __esm(() => {
11973
12081
  init_repo_profile();
11974
12082
  init_check_result();
11975
12083
  init_check_result();
12084
+ init_runner_config();
11976
12085
  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
12086
  ESLINT_CONFIGS = [".eslintrc", ".eslintrc.json", ".eslintrc.js", ".eslintrc.cjs", "eslint.config.js", "eslint.config.mjs", "eslint.config.cjs"];
11981
12087
  verifyStage = {
11982
12088
  name: "verify",
@@ -12013,8 +12119,8 @@ var init_verify = __esm(() => {
12013
12119
  const wall = ctx.outputs().wall ?? {};
12014
12120
  const wallPaths = new Set((wall.files ?? []).map((f) => isAbsolute6(f.path) ? relative5(ctx.repoDir, f.path) : f.path));
12015
12121
  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]);
12122
+ const intake = ctx.outputs().intake, relevant2 = ctx.tests.impacted(map, namedFiles(intake?.task ?? "", loadRepoMap(intake?.repomap_ref)));
12123
+ const tests = dedupeTests([...impacted, ...changedTestFiles, ...wallTests, ...relevant2]);
12018
12124
  const scopedOut = scopedOutOf(map);
12019
12125
  if (scopedOut.length)
12020
12126
  ctx.emit("test.scoped_out", "verify", { tests: scopedOut });
@@ -12022,10 +12128,10 @@ var init_verify = __esm(() => {
12022
12128
  if (signal.aborted)
12023
12129
  break;
12024
12130
  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 } : {} });
12131
+ 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
12132
  }
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);
12133
+ const targetNames = new Set([...wallTests, ...relevant2].map((t) => `${t.runner}:${t.path}`));
12134
+ 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
12135
  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
12136
  preRed.names.push(...attr.preExistingChecks);
12031
12137
  preRed.ids.push(...attr.preExistingIds);
@@ -12071,7 +12177,7 @@ var init_verify = __esm(() => {
12071
12177
  }
12072
12178
  };
12073
12179
  const testCounts = {};
12074
- const modifiedRel = relevant.filter((t) => changed.includes(t.path) && inBase(t.path)).flatMap((t) => {
12180
+ const modifiedRel = relevant2.filter((t) => changed.includes(t.path) && inBase(t.path)).flatMap((t) => {
12075
12181
  const k = preRed.cnt[`${t.runner}:${t.path}`];
12076
12182
  if (k)
12077
12183
  testCounts[t.path] = k;
@@ -12085,8 +12191,8 @@ var init_verify = __esm(() => {
12085
12191
  });
12086
12192
 
12087
12193
  // ../../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";
12194
+ import { existsSync as existsSync30, readdirSync as readdirSync14, readFileSync as readFileSync36 } from "fs";
12195
+ import { dirname as dirname18, join as join51 } from "path";
12090
12196
  function parseJsonc(text2) {
12091
12197
  let out2 = "", i = 0;
12092
12198
  while (i < text2.length) {
@@ -12121,25 +12227,25 @@ function moduleOf(file, depth = 0) {
12121
12227
  const ext = typeof j.extends === "string" ? j.extends : null;
12122
12228
  if (!ext || !ext.startsWith("."))
12123
12229
  return null;
12124
- const next = join50(dirname18(file), ext.endsWith(".json") ? ext : `${ext}.json`);
12230
+ const next = join51(dirname18(file), ext.endsWith(".json") ? ext : `${ext}.json`);
12125
12231
  return existsSync30(next) ? moduleOf(next, depth + 1) : null;
12126
12232
  }
12127
12233
  function readPackageConventions(pkgDir, runner = null) {
12128
12234
  let names = [];
12129
12235
  try {
12130
- names = readdirSync13(pkgDir).filter((f) => f.endsWith(".json")).sort((x, y) => x.length - y.length || x.localeCompare(y));
12236
+ names = readdirSync14(pkgDir).filter((f) => f.endsWith(".json")).sort((x, y) => x.length - y.length || x.localeCompare(y));
12131
12237
  } catch {
12132
12238
  return null;
12133
12239
  }
12134
12240
  let packageType = null, tsModule = null;
12135
12241
  for (const n2 of names) {
12136
- const j = readJson4(join50(pkgDir, n2));
12242
+ const j = readJson4(join51(pkgDir, n2));
12137
12243
  if (!j)
12138
12244
  continue;
12139
12245
  if (packageType === null && typeof j.type === "string" && typeof j.name === "string")
12140
12246
  packageType = j.type;
12141
12247
  if (tsModule === null && j.compilerOptions !== undefined)
12142
- tsModule = moduleOf(join50(pkgDir, n2));
12248
+ tsModule = moduleOf(join51(pkgDir, n2));
12143
12249
  }
12144
12250
  if (packageType === null && tsModule === null)
12145
12251
  return null;
@@ -12161,7 +12267,7 @@ function conventionViolation(c, fileName, content) {
12161
12267
  }
12162
12268
  var readJson4 = (p) => {
12163
12269
  try {
12164
- const v = parseJsonc(readFileSync35(p, "utf8"));
12270
+ const v = parseJsonc(readFileSync36(p, "utf8"));
12165
12271
  return v && typeof v === "object" && !Array.isArray(v) ? v : null;
12166
12272
  } catch {
12167
12273
  return null;
@@ -12171,15 +12277,15 @@ var init_conventions = () => {};
12171
12277
 
12172
12278
  // ../../loki-ts/src/engine10/stages/wall.ts
12173
12279
  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";
12280
+ 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
12281
  import { tmpdir as tmpdir9 } from "os";
12176
- import { dirname as dirname19, isAbsolute as isAbsolute7, join as join51, relative as relative6 } from "path";
12282
+ import { dirname as dirname19, isAbsolute as isAbsolute7, join as join52, relative as relative6 } from "path";
12177
12283
  function pytestCollectionIsRed(output, repoDir) {
12178
12284
  if (/ModuleNotFoundError/.test(output))
12179
12285
  return false;
12180
12286
  const real2 = (p) => {
12181
12287
  try {
12182
- return realpathSync18(isAbsolute7(p) ? p : join51(repoDir, p));
12288
+ return realpathSync18(isAbsolute7(p) ? p : join52(repoDir, p));
12183
12289
  } catch {
12184
12290
  return null;
12185
12291
  }
@@ -12216,7 +12322,7 @@ function pytestCollectionIsRed(output, repoDir) {
12216
12322
  }
12217
12323
  function moduleUnderRepo(repoDir, name) {
12218
12324
  const rel = name.replace(/\./g, "/");
12219
- return existsSync31(join51(repoDir, `${rel}.py`)) || existsSync31(join51(repoDir, rel, "__init__.py"));
12325
+ return existsSync31(join52(repoDir, `${rel}.py`)) || existsSync31(join52(repoDir, rel, "__init__.py"));
12220
12326
  }
12221
12327
  function pytestExit1IsRed(output) {
12222
12328
  if (/^!+ _pytest\.outcomes\.Exit\b/m.test(output))
@@ -12314,10 +12420,10 @@ ${acceptance.map((a) => `Acceptance: ${a}`).join(`
12314
12420
  function loadTaskText(ctx, fromPrior) {
12315
12421
  if (fromPrior)
12316
12422
  return fromPrior;
12317
- const issueJsonPath = process.env.LOKI_E10_ISSUE_JSON ?? join51(ctx.runDir, "issue.json");
12423
+ const issueJsonPath = process.env.LOKI_E10_ISSUE_JSON ?? join52(ctx.runDir, "issue.json");
12318
12424
  if (existsSync31(issueJsonPath)) {
12319
12425
  try {
12320
- const issue = JSON.parse(readFileSync36(issueJsonPath, "utf8"));
12426
+ const issue = JSON.parse(readFileSync37(issueJsonPath, "utf8"));
12321
12427
  const text2 = [issue.title, issue.body].filter((s) => typeof s === "string" && s.length > 0).join(`
12322
12428
 
12323
12429
  `);
@@ -12329,7 +12435,7 @@ function loadTaskText(ctx, fromPrior) {
12329
12435
  }
12330
12436
  function wallTargetDir(repoDir, existingTests) {
12331
12437
  const first = existingTests[0];
12332
- return first ? join51(repoDir, dirname19(first.path)) : join51(repoDir, "tests");
12438
+ return first ? join52(repoDir, dirname19(first.path)) : join52(repoDir, "tests");
12333
12439
  }
12334
12440
  function guessRunner(fileName, runners) {
12335
12441
  if (fileName.endsWith(".py"))
@@ -12360,11 +12466,11 @@ async function wallAuthor(ctx, signal) {
12360
12466
  const tree = prior.intake?.tree;
12361
12467
  const repomapText = repoMapText(ctx.repoDir, tree, repomapRef, WALL_MAP_MAX_LINES);
12362
12468
  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-"));
12469
+ const conv2 = readPackageConventions(join52(ctx.repoDir, pkg?.root ?? "."), pkg?.runner ?? null);
12470
+ const cwd = mkdtempSync7(join52(tmpdir9(), "loki-e15-wall-"));
12365
12471
  const wm = wallManifestFor(ctx.repoDir, tree, task);
12366
12472
  for (const [n2, c] of [["task.md", task], ["repomap.txt", repomapText], ...wm ? [["wall_manifest.txt", wm.text]] : []])
12367
- writeFileSync21(join51(cwd, n2), c, "utf8");
12473
+ writeFileSync21(join52(cwd, n2), c, "utf8");
12368
12474
  const session = await ctx.sessions.run({
12369
12475
  stage: "wall",
12370
12476
  brief: buildWallBrief(task, repomapText, runners, !!wm, conventionsBrief(conv2), unitSpec()?.acceptance ?? []),
@@ -12379,15 +12485,15 @@ async function wallAuthor(ctx, signal) {
12379
12485
  rmSync13(cwd, { recursive: true, force: true });
12380
12486
  return done({ status: "failed", data: {}, reason: "wall session aborted, killed, or timed out", killed: true });
12381
12487
  }
12382
- const generated = readdirSync14(cwd).filter((f) => f.startsWith(WALL_PREFIX));
12383
- const sealedDir = join51(ctx.runDir, "wall");
12488
+ const generated = readdirSync15(cwd).filter((f) => f.startsWith(WALL_PREFIX));
12489
+ const sealedDir = join52(ctx.runDir, "wall");
12384
12490
  if (generated.length > 0)
12385
12491
  mkdirSync19(sealedDir, { recursive: true });
12386
12492
  const contents = new Map;
12387
12493
  const discarded = [];
12388
12494
  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");
12495
+ const content = readFileSync37(join52(cwd, name), "utf8"), bad = conventionViolation(conv2, name, content);
12496
+ writeFileSync21(join52(sealedDir, name), content, "utf8");
12391
12497
  if (bad)
12392
12498
  discarded.push({ file: name, reason: `${bad}; wall test did not compile under the package config` });
12393
12499
  else
@@ -12407,7 +12513,7 @@ function installWall(ctx, a, baseDir, opts = {}) {
12407
12513
  const content = contents.get(name);
12408
12514
  if (content === undefined)
12409
12515
  continue;
12410
- const dest = join51(targetDir, name), runner = guessRunner(name, runners);
12516
+ const dest = join52(targetDir, name), runner = guessRunner(name, runners);
12411
12517
  writeFileSync21(dest, content, "utf8");
12412
12518
  sealedFiles.push({ path: dest, sha256: sha2562(content) });
12413
12519
  readOnlyFiles.push({ path: dest, content });
@@ -12417,7 +12523,7 @@ function installWall(ctx, a, baseDir, opts = {}) {
12417
12523
  ctx.emit("wall.sealed", "wall", { files: sealedFiles, ...a.manifestSha256 ? { manifest_sha256: a.manifestSha256 } : {} });
12418
12524
  const baseRunner = opts.baseRunner ?? new RealBaseTestRunner, baseRun = { pass: 0, fail: 0, not_run: discarded.length }, notRunFiles = [];
12419
12525
  for (const t of wallTests) {
12420
- const r = baseRunner.run(baseDir, [t]), abs = join51(ctx.repoDir, t.path);
12526
+ const r = baseRunner.run(baseDir, [t]), abs = join52(ctx.repoDir, t.path);
12421
12527
  baseRun.pass += r.pass;
12422
12528
  baseRun.fail += r.fail;
12423
12529
  baseRun.not_run += r.not_run ?? 0;
@@ -12469,15 +12575,15 @@ var init_wall = __esm(() => {
12469
12575
  });
12470
12576
 
12471
12577
  // ../../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";
12578
+ 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";
12579
+ import { dirname as dirname20, join as join53, relative as relative7, sep as sep11 } from "path";
12474
12580
  function safeRestore(repoDir, f, want, mode) {
12475
- const root = realpathSync19(repoDir), fp = join52(root, f), rel = relative7(root, dirname20(fp));
12581
+ const root = realpathSync19(repoDir), fp = join53(root, f), rel = relative7(root, dirname20(fp));
12476
12582
  if (rel.startsWith(".."))
12477
12583
  throw new Error("outside repo");
12478
12584
  let cur = root;
12479
12585
  for (const part of rel === "" ? [] : rel.split(sep11)) {
12480
- cur = join52(cur, part);
12586
+ cur = join53(cur, part);
12481
12587
  let st2;
12482
12588
  try {
12483
12589
  st2 = lstatSync10(cur);
@@ -12494,7 +12600,7 @@ function safeRestore(repoDir, f, want, mode) {
12494
12600
  } catch {
12495
12601
  st = null;
12496
12602
  }
12497
- if (st?.isFile() && readFileSync37(fp).equals(want)) {
12603
+ if (st?.isFile() && readFileSync38(fp).equals(want)) {
12498
12604
  chmodSync5(fp, mode);
12499
12605
  return;
12500
12606
  }
@@ -12532,7 +12638,7 @@ async function discardIfSatisfied(git3, base, o, staged, keep, repoDir) {
12532
12638
  return { status: "failed", data: {}, reason: "already-satisfied discard failed" };
12533
12639
  const notProven = [];
12534
12640
  for (const [f, v] of Object.entries(o.intake?.preexisting_untracked_blobs ?? {})) {
12535
- const fp = join52(repoDir, f);
12641
+ const fp = join53(repoDir, f);
12536
12642
  try {
12537
12643
  if (v.startsWith("!")) {
12538
12644
  const st = lstatSync10(fp);
@@ -12551,7 +12657,7 @@ async function discardIfSatisfied(git3, base, o, staged, keep, repoDir) {
12551
12657
  try {
12552
12658
  let m = 420;
12553
12659
  try {
12554
- const l = lstatSync10(join52(repoDir, f));
12660
+ const l = lstatSync10(join53(repoDir, f));
12555
12661
  if (l.isFile())
12556
12662
  m = l.mode & 511;
12557
12663
  } catch {}
@@ -12627,9 +12733,9 @@ var init_scope2 = __esm(() => {
12627
12733
  // ../../loki-ts/src/features/visual_evidence.ts
12628
12734
  import { spawn as spawn7 } from "child_process";
12629
12735
  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";
12736
+ import { existsSync as existsSync32, lstatSync as lstatSync11, mkdirSync as mkdirSync21, readdirSync as readdirSync16, readFileSync as readFileSync39, writeFileSync as writeFileSync23 } from "fs";
12631
12737
  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";
12738
+ import { dirname as dirname23, isAbsolute as isAbsolute8, join as join54, normalize as normalize2, relative as relative8, sep as sep12 } from "path";
12633
12739
  function routeFor(file) {
12634
12740
  let r = file.replace(/^(?:.*?\/)?(?:app|pages|src|public)\//, "").replace(/^(?:routes|pages)\//, "");
12635
12741
  r = r.replace(/\.(?:html|jsx|tsx|vue|svelte)$/, "").replace(/\/?(?:page|index|\+page)$/, "");
@@ -12639,7 +12745,7 @@ function hashScreens(runDir, rels) {
12639
12745
  const out2 = [];
12640
12746
  for (const rel of rels) {
12641
12747
  try {
12642
- out2.push({ path: rel, sha256: sha256File(join53(runDir, rel)) });
12748
+ out2.push({ path: rel, sha256: sha256File(join54(runDir, rel)) });
12643
12749
  } catch {}
12644
12750
  }
12645
12751
  return out2;
@@ -12652,10 +12758,10 @@ function checkScreens(runDir, screens) {
12652
12758
  return "evidence_screens entry is malformed";
12653
12759
  if (isAbsolute8(s.path) || normalize2(s.path).startsWith(".."))
12654
12760
  return `evidence screenshot path escapes the evidence dir: ${s.path}`;
12655
- const file = join53(runDir, s.path);
12761
+ const file = join54(runDir, s.path);
12656
12762
  let cur = runDir;
12657
12763
  for (const seg of normalize2(s.path).split(sep12)) {
12658
- cur = join53(cur, seg);
12764
+ cur = join54(cur, seg);
12659
12765
  if (isSymlink(cur))
12660
12766
  return `evidence screenshot path is a symlink: ${s.path}`;
12661
12767
  }
@@ -12702,11 +12808,11 @@ async function waitUp(url, ms, signal) {
12702
12808
  }
12703
12809
  function openapiPaths(repoDir) {
12704
12810
  for (const f of ["openapi.json", "openapi.yaml", "openapi.yml", "swagger.json"]) {
12705
- const p = join53(repoDir, f);
12811
+ const p = join54(repoDir, f);
12706
12812
  if (!existsSync32(p))
12707
12813
  continue;
12708
12814
  try {
12709
- const txt = readFileSync38(p, "utf8");
12815
+ const txt = readFileSync39(p, "utf8");
12710
12816
  const paths = f.endsWith(".json") ? Object.keys(JSON.parse(txt).paths ?? {}) : [...txt.matchAll(/^ {2}(\/[^\s:]*):\s*$/gm)].map((m) => m[1]);
12711
12817
  return paths.filter((x) => !x.includes("{")).slice(0, 10);
12712
12818
  } catch {
@@ -12720,24 +12826,24 @@ async function recordE2eMedia(repoDir, runDir, relDir, base, routes, ms, signal)
12720
12826
  if (!pw || !pkg)
12721
12827
  return { rels: [], skipped: "playwright e2e video and trace skipped: @playwright/test is not installed in the repo" };
12722
12828
  try {
12723
- const dir = join53(runDir, relDir, "e2e");
12829
+ const dir = join54(runDir, relDir, "e2e");
12724
12830
  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" } };
12831
+ writeFileSync23(join54(dir, "walk.spec.cjs"), e2eSpecSource(pkg, base, routes));
12832
+ writeFileSync23(join54(dir, "playwright.config.cjs"), `module.exports = { testDir: ".", testMatch: "walk.spec.cjs", outputDir: "./out", reporter: "null", use: { video: "on", trace: "on" } };
12727
12833
  `);
12728
- if (!await shoot(pw, ["test", "--config", join53(dir, "playwright.config.cjs")], repoDir, ms, signal))
12834
+ if (!await shoot(pw, ["test", "--config", join54(dir, "playwright.config.cjs")], repoDir, ms, signal))
12729
12835
  return { rels: [], skipped: "playwright e2e video and trace skipped: the run failed or timed out" };
12730
12836
  const rels = [];
12731
12837
  const walk = (d) => {
12732
- for (const e of readdirSync15(d, { withFileTypes: true })) {
12733
- const full = join53(d, e.name);
12838
+ for (const e of readdirSync16(d, { withFileTypes: true })) {
12839
+ const full = join54(d, e.name);
12734
12840
  if (e.isDirectory())
12735
12841
  walk(full);
12736
12842
  else if (e.isFile() && /\.(webm|zip)$/.test(e.name))
12737
- rels.push(join53(relDir, "e2e", relative8(dir, full)));
12843
+ rels.push(join54(relDir, "e2e", relative8(dir, full)));
12738
12844
  }
12739
12845
  };
12740
- walk(join53(dir, "out"));
12846
+ walk(join54(dir, "out"));
12741
12847
  return rels.length > 0 ? { rels, skipped: null } : { rels, skipped: "playwright e2e video and trace skipped: no video or trace was produced" };
12742
12848
  } catch (e) {
12743
12849
  return { rels: [], skipped: `playwright e2e video and trace skipped: ${String(e?.message ?? e).slice(0, 120)}` };
@@ -12797,6 +12903,16 @@ async function stopServer(c, graceMs, signal) {
12797
12903
  for (let i = 0;i < 10 && groupAlive(pid); i++)
12798
12904
  await new Promise((r) => setTimeout(r, 50));
12799
12905
  }
12906
+ function noRootReason(subs, lacking) {
12907
+ const list = (xs) => xs.map((d) => `${d}/`).join(" or ");
12908
+ if (subs.length === 0)
12909
+ return "no package.json";
12910
+ if (lacking.length === subs.length)
12911
+ return `no dev, preview or start script in ${list(lacking)} (no root package.json)`;
12912
+ if (lacking.length === 0)
12913
+ return `no root package.json (${list(subs)} have a dev, preview or start script but no UI boot was resolved)`;
12914
+ return `no root package.json; no dev, preview or start script in ${list(lacking)} (${list(subs.filter((d) => !lacking.includes(d)))} has one)`;
12915
+ }
12800
12916
  async function captureVisualEvidence(repoDir, runDir, changed, opts = {}) {
12801
12917
  const skip = (why) => ({ screens: [], http: false, skipped: why });
12802
12918
  const signal = opts.signal;
@@ -12806,13 +12922,22 @@ async function captureVisualEvidence(repoDir, runDir, changed, opts = {}) {
12806
12922
  if (!visualEvidenceEnabled(opts.env))
12807
12923
  return skip("LOKI_VISUAL_EVIDENCE is 0");
12808
12924
  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");
12925
+ const appDir = boot ? join54(repoDir, boot.boot.cwd) : repoDir;
12926
+ const pkgPath = join54(appDir, "package.json");
12811
12927
  let script = null;
12812
12928
  if (!boot) {
12813
- if (!existsSync32(pkgPath))
12814
- return skip("no package.json");
12815
- script = pickScript(JSON.parse(readFileSync38(pkgPath, "utf8")));
12929
+ if (!existsSync32(pkgPath)) {
12930
+ const subs = ["backend", "frontend"].filter((d) => existsSync32(join54(repoDir, d, "package.json")));
12931
+ const has = (d) => {
12932
+ try {
12933
+ return pickScript(JSON.parse(readFileSync39(join54(repoDir, d, "package.json"), "utf8"))) !== null;
12934
+ } catch {
12935
+ return false;
12936
+ }
12937
+ };
12938
+ return skip(noRootReason(subs, subs.filter((d) => !has(d))));
12939
+ }
12940
+ script = pickScript(JSON.parse(readFileSync39(pkgPath, "utf8")));
12816
12941
  if (!script)
12817
12942
  return skip("no dev, preview or start script");
12818
12943
  }
@@ -12839,20 +12964,20 @@ async function captureVisualEvidence(repoDir, runDir, changed, opts = {}) {
12839
12964
  }
12840
12965
  if (!await waitUp(base, left(), signal))
12841
12966
  return skip(aborted() ? "aborted" : `${script} server did not answer within ${Math.round(budget / 1000)}s`);
12842
- const evRoot = join53(runDir, "evidence");
12967
+ const evRoot = join54(runDir, "evidence");
12843
12968
  if (isSymlink(evRoot))
12844
12969
  return skip("evidence dir is a symlink");
12845
12970
  mkdirSync21(evRoot, { recursive: true });
12846
12971
  if (pages.length > 0) {
12847
- const rel0 = join53("evidence", randomBytes4(6).toString("hex"));
12848
- mkdirSync21(join53(runDir, rel0));
12972
+ const rel0 = join54("evidence", randomBytes4(6).toString("hex"));
12973
+ mkdirSync21(join54(runDir, rel0));
12849
12974
  const rels = [];
12850
12975
  for (const route of [...new Set(pages.map(routeFor))].slice(0, opts.maxRoutes ?? MAX_ROUTES)) {
12851
12976
  if (aborted() || left() <= 0)
12852
12977
  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)))
12978
+ const rel = join54(rel0, `${screenName(route)}.png`);
12979
+ const ok = await shoot(pw, ["screenshot", `${base}${route}`, join54(runDir, rel)], appDir, left(), signal);
12980
+ if (ok && !aborted() && !isSymlink(join54(runDir, rel)) && existsSync32(join54(runDir, rel)))
12856
12981
  rels.push(rel);
12857
12982
  }
12858
12983
  if (aborted())
@@ -12877,9 +13002,9 @@ async function captureVisualEvidence(repoDir, runDir, changed, opts = {}) {
12877
13002
  }
12878
13003
  if (aborted())
12879
13004
  return skip("aborted");
12880
- const hdir = join53(evRoot, randomBytes4(6).toString("hex"));
13005
+ const hdir = join54(evRoot, randomBytes4(6).toString("hex"));
12881
13006
  mkdirSync21(hdir);
12882
- writeFileSync23(join53(hdir, "http.json"), JSON.stringify(rows, null, 2) + `
13007
+ writeFileSync23(join54(hdir, "http.json"), JSON.stringify(rows, null, 2) + `
12883
13008
  `);
12884
13009
  return { screens: [], http: true, skipped: null };
12885
13010
  } catch (e) {
@@ -12904,12 +13029,12 @@ async function sealEvidence(repoDir, runDir, o, notProven, signal, emit) {
12904
13029
  function receiptScreensProblem(receiptPath, receipt) {
12905
13030
  return receipt["evidence_screens"] === undefined ? null : checkScreens(dirname23(receiptPath), receipt["evidence_screens"]);
12906
13031
  }
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");
13032
+ 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) => {
13033
+ const bin = join54(repoDir, "node_modules", ".bin", "playwright");
12909
13034
  return existsSync32(bin) ? bin : null;
12910
13035
  }, playwrightTestPkg = (repoDir) => {
12911
- const d = join53(repoDir, "node_modules", "@playwright", "test");
12912
- return existsSync32(join53(d, "package.json")) ? d : null;
13036
+ const d = join54(repoDir, "node_modules", "@playwright", "test");
13037
+ return existsSync32(join54(d, "package.json")) ? d : null;
12913
13038
  }, e2eSpecSource = (pkgDir, base, routes) => `const { test } = require(${JSON.stringify(pkgDir)});
12914
13039
  test("loki e2e walkthrough", async ({ page }) => {
12915
13040
  for (const r of ${JSON.stringify(routes)}) { await page.goto(${JSON.stringify(base)} + r); await page.waitForLoadState("load"); }
@@ -12940,14 +13065,14 @@ var init_visual_evidence = __esm(() => {
12940
13065
  });
12941
13066
 
12942
13067
  // ../../loki-ts/src/council/role_agents.ts
12943
- import { existsSync as existsSync33, readdirSync as readdirSync16, readFileSync as readFileSync39 } from "fs";
13068
+ import { existsSync as existsSync33, readdirSync as readdirSync17, readFileSync as readFileSync40 } from "fs";
12944
13069
  import { homedir as homedir9 } from "os";
12945
- import { join as join54 } from "path";
13070
+ import { join as join55 } from "path";
12946
13071
  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")];
13072
+ 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
13073
  }
12949
13074
  function parseAgent(file, stem2) {
12950
- const m = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/.exec(readFileSync39(file, "utf8"));
13075
+ const m = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/.exec(readFileSync40(file, "utf8"));
12951
13076
  if (!m)
12952
13077
  return null;
12953
13078
  const body = m[2].trim();
@@ -12965,14 +13090,14 @@ function loadRoleAgent(repoDir, role, env = process.env) {
12965
13090
  continue;
12966
13091
  let names;
12967
13092
  try {
12968
- names = readdirSync16(d).filter((n2) => n2.endsWith(".md") && !n2.startsWith(".")).sort();
13093
+ names = readdirSync17(d).filter((n2) => n2.endsWith(".md") && !n2.startsWith(".")).sort();
12969
13094
  } catch {
12970
13095
  continue;
12971
13096
  }
12972
13097
  const ordered = [...names.filter((n2) => n2 === `${role}.md`), ...names.filter((n2) => n2 !== `${role}.md`)];
12973
13098
  for (const n2 of ordered) {
12974
13099
  try {
12975
- const a = parseAgent(join54(d, n2), n2.slice(0, -3));
13100
+ const a = parseAgent(join55(d, n2), n2.slice(0, -3));
12976
13101
  if (a && a.role === role)
12977
13102
  return a;
12978
13103
  } catch {}
@@ -12989,8 +13114,8 @@ var init_role_agents = __esm(() => {
12989
13114
  });
12990
13115
 
12991
13116
  // ../../loki-ts/src/engine10/stages/xreview.ts
12992
- import { existsSync as existsSync34, readFileSync as readFileSync40 } from "fs";
12993
- import { join as join55 } from "path";
13117
+ import { existsSync as existsSync34, readFileSync as readFileSync41 } from "fs";
13118
+ import { join as join56 } from "path";
12994
13119
  function minVerdict(verdict, review) {
12995
13120
  const ceiling = review?.level === "block" ? "PARTIAL" : verdict;
12996
13121
  return RANK3[ceiling] < RANK3[verdict] ? ceiling : verdict;
@@ -13003,11 +13128,11 @@ function explicitReview(repoDir, env) {
13003
13128
  if (flag && isOff(ev))
13004
13129
  return "off";
13005
13130
  for (const f of ["loki.yaml", "loki.yml"]) {
13006
- const p = join55(repoDir, f);
13131
+ const p = join56(repoDir, f);
13007
13132
  if (!existsSync34(p))
13008
13133
  continue;
13009
13134
  try {
13010
- const m = /^review:[ \t]*["']?([A-Za-z]+)["']?[ \t]*(?:#.*)?$/m.exec(readFileSync40(p, "utf8"));
13135
+ const m = /^review:[ \t]*["']?([A-Za-z]+)["']?[ \t]*(?:#.*)?$/m.exec(readFileSync41(p, "utf8"));
13011
13136
  const v = asProvider(m?.[1]);
13012
13137
  if (v)
13013
13138
  return v;
@@ -13217,16 +13342,41 @@ function recordRun(env, repoDir, model, verdict, usd, unmetered, wallS, cacheRoo
13217
13342
  return false;
13218
13343
  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
13344
  }
13220
- var NOT_RECORDED = "NOT RECORDED", previewOn = (env) => env["LOKI_COST_PREVIEW"] !== "0";
13345
+ var MIN_RUNS = 3, NOT_RECORDED = "NOT RECORDED", previewOn = (env) => env["LOKI_COST_PREVIEW"] !== "0", ESTIMATE_HELP;
13221
13346
  var init_cost_preview = __esm(() => {
13222
13347
  init_cache();
13348
+ init_session();
13223
13349
  init_history();
13224
13350
  init_engine_origin();
13351
+ ESTIMATE_HELP = `loki estimate - Show what a run is likely to cost
13352
+
13353
+ Usage: loki estimate [--model M] [--provider P] [--json]
13354
+
13355
+ Prints the same estimate line "loki start" prints before a run: the dollar and time
13356
+ range of this repo's prior verified runs of the same project shape. With fewer than
13357
+ ${MIN_RUNS} such runs it prints a rough per-model prior, or NOT AVAILABLE (reason).
13358
+ It never estimates from task text and never prints a number it did not measure.
13359
+
13360
+ Options:
13361
+ --model M Model whose rough prior to use when there is no history (default: the start model)
13362
+ --provider P Provider whose default model is used (default: LOKI_PROVIDER or claude)
13363
+ --json Machine-readable output (the estimate result object)
13364
+ --help, -h Show this help
13365
+
13366
+ Changed: --iterations is removed. The old iteration-based projection (tools/estimate-run.py)
13367
+ is replaced by this single estimator. LOKI_COST_PREVIEW=0 turns the estimate off.
13368
+
13369
+ Examples:
13370
+ loki estimate
13371
+ loki estimate --json
13372
+
13373
+ See also: loki cost --detail (what a run ALREADY spent)
13374
+ `;
13225
13375
  });
13226
13376
 
13227
13377
  // ../../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";
13378
+ import { existsSync as existsSync35, lstatSync as lstatSync12, mkdirSync as mkdirSync22, readFileSync as readFileSync42, rmSync as rmSync14, writeFileSync as writeFileSync24 } from "fs";
13379
+ import { dirname as dirname24, join as join57 } from "path";
13230
13380
  import { createHash as createHash13 } from "crypto";
13231
13381
  function contractEnabled(env = process.env) {
13232
13382
  return env["LOKI_CONTRACT"] !== "0";
@@ -13258,7 +13408,7 @@ function untracedLines(trace) {
13258
13408
  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
13409
  }
13260
13410
  function contractPath(repoDir) {
13261
- return join56(repoDir, ".loki", "contract.json");
13411
+ return join57(repoDir, ".loki", "contract.json");
13262
13412
  }
13263
13413
  function readContract(repoDir) {
13264
13414
  const p = contractPath(repoDir);
@@ -13274,7 +13424,7 @@ function readContract(repoDir) {
13274
13424
  return { contract: null, notes: ["contract unreadable: too large"], sha256: null };
13275
13425
  let raw2;
13276
13426
  try {
13277
- raw2 = readFileSync41(p);
13427
+ raw2 = readFileSync42(p);
13278
13428
  } catch {
13279
13429
  return { contract: null, notes: ["contract unreadable: read failed"], sha256: null };
13280
13430
  }
@@ -13343,16 +13493,16 @@ var init_contract = __esm(() => {
13343
13493
  init_safe_git();
13344
13494
  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
13495
  MAX_CONTRACT_BYTES = 1024 * 1024;
13346
- REQ_MAP_REL = join56(".loki", "state", "requirements-map.json");
13496
+ REQ_MAP_REL = join57(".loki", "state", "requirements-map.json");
13347
13497
  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
13498
  });
13349
13499
 
13350
13500
  // ../../loki-ts/src/features/speed/seal_group.ts
13351
13501
  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";
13502
+ import { existsSync as existsSync36, mkdirSync as mkdirSync23, readdirSync as readdirSync18, readFileSync as readFileSync43, renameSync as renameSync9, statSync as statSync17, writeFileSync as writeFileSync25 } from "fs";
13503
+ import { basename as basename19, dirname as dirname25, join as join58 } from "path";
13354
13504
  function sealGroup(runDir, rh) {
13355
- const manifestPath = join57(runDir, "group", "manifest.json");
13505
+ const manifestPath = join58(runDir, "group", "manifest.json");
13356
13506
  if (!existsSync36(manifestPath))
13357
13507
  return { section: undefined, notProven: [], problems: 0, allUnitsPass: true };
13358
13508
  const notProven = [];
@@ -13377,10 +13527,10 @@ function sealGroup(runDir, rh) {
13377
13527
  return fail(`unit ${index2} has an invalid, duplicate or case-colliding id`);
13378
13528
  seen.add(id);
13379
13529
  seenLower.add(id.toLowerCase());
13380
- const dir = join57(unitsDir(runDir), id), rp = join57(dir, "receipt.json"), ep = join57(dir, "events.jsonl");
13530
+ const dir = join58(unitsDir(runDir), id), rp = join58(dir, "receipt.json"), ep = join58(dir, "events.jsonl");
13381
13531
  let bytes;
13382
13532
  try {
13383
- bytes = readFileSync42(rp);
13533
+ bytes = readFileSync43(rp);
13384
13534
  } catch {
13385
13535
  return fail(`unit ${id} sub-receipt missing`);
13386
13536
  }
@@ -13397,7 +13547,7 @@ function sealGroup(runDir, rh) {
13397
13547
  unit_id: id,
13398
13548
  sub_receipt_sha256: sha2(bytes),
13399
13549
  receipt_sha256: sub["receipt_sha256"],
13400
- events_file_sha256: existsSync36(ep) ? sha2(readFileSync42(ep)) : null,
13550
+ events_file_sha256: existsSync36(ep) ? sha2(readFileSync43(ep)) : null,
13401
13551
  verdict,
13402
13552
  goal: text2(u["goal"], 200),
13403
13553
  files: (Array.isArray(u["files"]) ? u["files"] : []).map((f) => text2(f, 200)).filter(Boolean).slice(0, 50),
@@ -13422,7 +13572,7 @@ async function verifyGroup(receiptPath, receipt, rh, verifyOne, deps = {}) {
13422
13572
  const bad = (reason) => ({ verdict: "TAMPERED", reason: `group: ${reason}` });
13423
13573
  const g = receipt["group"];
13424
13574
  if (g === undefined)
13425
- return existsSync36(join57(runDir, "group", "manifest.json")) ? bad("group directory present but the receipt has no group section") : null;
13575
+ return existsSync36(join58(runDir, "group", "manifest.json")) ? bad("group directory present but the receipt has no group section") : null;
13426
13576
  if (!isObj10(g) || !ID_RE.test(String(g["group_id"] ?? "")) || !Array.isArray(g["units"]) || g["units"].length === 0)
13427
13577
  return bad("group section malformed");
13428
13578
  const entries = g["units"];
@@ -13434,20 +13584,20 @@ async function verifyGroup(receiptPath, receipt, rh, verifyOne, deps = {}) {
13434
13584
  return bad("unit directories do not match the receipt (missing or extra unit)");
13435
13585
  let unchecked = null;
13436
13586
  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");
13587
+ const e = entries[i], id = ids[i], dir = join58(unitsDir(runDir), id), rp = join58(dir, "receipt.json"), ep = join58(dir, "events.jsonl");
13438
13588
  if (e["index"] !== i)
13439
13589
  return bad(`unit ${id} is out of order (index ${String(e["index"])} at position ${i})`);
13440
13590
  let bytes;
13441
13591
  try {
13442
13592
  if (!statSync17(dir).isDirectory())
13443
13593
  return bad(`unit ${id} is not a directory`);
13444
- bytes = readFileSync42(rp);
13594
+ bytes = readFileSync43(rp);
13445
13595
  } catch {
13446
13596
  return bad(`unit ${id} sub-receipt missing`);
13447
13597
  }
13448
13598
  if (sha2(bytes) !== e["sub_receipt_sha256"])
13449
13599
  return bad(`unit ${id} sub-receipt does not match its recorded hash`);
13450
- const evSha = existsSync36(ep) ? sha2(readFileSync42(ep)) : null;
13600
+ const evSha = existsSync36(ep) ? sha2(readFileSync43(ep)) : null;
13451
13601
  if (evSha !== (e["events_file_sha256"] ?? null))
13452
13602
  return bad(`unit ${id} events.jsonl does not match its recorded hash`);
13453
13603
  const sub = readJson5(rp);
@@ -13467,8 +13617,8 @@ async function verifyGroup(receiptPath, receipt, rh, verifyOne, deps = {}) {
13467
13617
  }
13468
13618
  function compositeVerdict(childVerdicts) {
13469
13619
  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)) });
13620
+ const failed2 = childVerdicts.filter((v) => v === "FAILED" || !["VERIFIED", "ALREADY_SATISFIED", "PARTIAL", "SPEC_CONFLICT"].includes(v)).length;
13621
+ return capGroupVerdict(base, { section: undefined, notProven: [], problems: failed2, allUnitsPass: childVerdicts.every((v) => PASSING.has(v)) });
13472
13622
  }
13473
13623
  async function verifyComposite(receiptPath, receipt, rh, verifyOne, deps = {}) {
13474
13624
  const runDir = dirname25(receiptPath);
@@ -13479,13 +13629,13 @@ async function verifyComposite(receiptPath, receipt, rh, verifyOne, deps = {}) {
13479
13629
  return bad("body malformed");
13480
13630
  let ibytes;
13481
13631
  try {
13482
- ibytes = readFileSync42(join57(runDir, "integration.json"));
13632
+ ibytes = readFileSync43(join58(runDir, "integration.json"));
13483
13633
  } catch {
13484
13634
  return bad("integration.json missing");
13485
13635
  }
13486
13636
  if (sha2(ibytes) !== receipt["integration_sha256"])
13487
13637
  return bad("integration.json does not match its recorded hash");
13488
- const integ = readJson5(join57(runDir, "integration.json"));
13638
+ const integ = readJson5(join58(runDir, "integration.json"));
13489
13639
  if (!isObj10(integ) || !isObj10(integ["outcomes"]))
13490
13640
  return bad("integration.json has no outcomes");
13491
13641
  const heads = isObj10(integ["heads"]) ? integ["heads"] : {}, outcomes = integ["outcomes"];
@@ -13503,10 +13653,10 @@ async function verifyComposite(receiptPath, receipt, rh, verifyOne, deps = {}) {
13503
13653
  return bad(`child ${text2(repo, 80) || "?"} has an invalid receipt path`);
13504
13654
  if (outcomes[repo] !== "ok")
13505
13655
  return bad(`child ${text2(repo, 80)} did not finish ok in integration.json`);
13506
- const rp = join57(runDir, rel);
13656
+ const rp = join58(runDir, rel);
13507
13657
  let bytes;
13508
13658
  try {
13509
- bytes = readFileSync42(rp);
13659
+ bytes = readFileSync43(rp);
13510
13660
  } catch {
13511
13661
  return bad(`child ${repo} receipt missing`);
13512
13662
  }
@@ -13541,15 +13691,15 @@ function compositeHash(r) {
13541
13691
  const { composite_sha256: _c, verification: _v, ...body } = r;
13542
13692
  return sha2(canonicalJson(body));
13543
13693
  }
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) => {
13694
+ 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
13695
  try {
13546
- return JSON.parse(readFileSync42(p, "utf8"));
13696
+ return JSON.parse(readFileSync43(p, "utf8"));
13547
13697
  } catch {
13548
13698
  return;
13549
13699
  }
13550
13700
  }, dirNames = (d) => {
13551
13701
  try {
13552
- return readdirSync17(d).filter((n2) => !n2.startsWith(".")).sort();
13702
+ return readdirSync18(d).filter((n2) => !n2.startsWith(".")).sort();
13553
13703
  } catch {
13554
13704
  return null;
13555
13705
  }
@@ -13561,8 +13711,8 @@ var init_seal_group = __esm(() => {
13561
13711
  });
13562
13712
 
13563
13713
  // ../../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";
13714
+ import { existsSync as existsSync37, readFileSync as readFileSync44, statSync as statSync18 } from "fs";
13715
+ import { basename as basename20, join as join59 } from "path";
13566
13716
  function supplyEnabled(env) {
13567
13717
  return env["LOKI_SUPPLY_GUARD"] !== "0";
13568
13718
  }
@@ -13577,13 +13727,13 @@ function isManifestPath(path) {
13577
13727
  return MANIFESTS.test(basename20(path));
13578
13728
  }
13579
13729
  function readDeclared(repoDir) {
13580
- const p = join58(repoDir, ".loki", DECLARED_FILE);
13730
+ const p = join59(repoDir, ".loki", DECLARED_FILE);
13581
13731
  if (!existsSync37(p))
13582
13732
  return { deps: [], problem: null };
13583
13733
  try {
13584
13734
  if (statSync18(p).size > MAX_FILE_BYTES)
13585
13735
  return { deps: [], problem: "declaration file too large" };
13586
- const j = JSON.parse(readFileSync43(p, "utf8"));
13736
+ const j = JSON.parse(readFileSync44(p, "utf8"));
13587
13737
  if (!Array.isArray(j))
13588
13738
  return { deps: [], problem: "declaration is not a JSON array" };
13589
13739
  const deps = [];
@@ -13603,11 +13753,11 @@ function readDeclared(repoDir) {
13603
13753
  }
13604
13754
  }
13605
13755
  function readAllowlist(repoDir) {
13606
- const p = join58(repoDir, ".loki", "supply-allowlist");
13756
+ const p = join59(repoDir, ".loki", "supply-allowlist");
13607
13757
  if (!existsSync37(p))
13608
13758
  return new Set;
13609
13759
  try {
13610
- return new Set(readFileSync43(p, "utf8").split(`
13760
+ return new Set(readFileSync44(p, "utf8").split(`
13611
13761
  `).map((l) => l.replace(/#.*$/, "").trim()).filter(Boolean));
13612
13762
  } catch {
13613
13763
  return new Set;
@@ -13648,7 +13798,7 @@ function userConfigPrivate(eco, repoDir, env, goEnv = {}, name = "") {
13648
13798
  if (eco === "npm") {
13649
13799
  if (privateUrlList([env["npm_config_registry"] ?? "", env["NPM_CONFIG_REGISTRY"] ?? ""]))
13650
13800
  return true;
13651
- return files(join58(repoDir, ".npmrc"), home ? join58(home, ".npmrc") : "").some((t) => t.split(`
13801
+ return files(join59(repoDir, ".npmrc"), home ? join59(home, ".npmrc") : "").some((t) => t.split(`
13652
13802
  `).some((l) => {
13653
13803
  const m = /^\s*(?:(@[^:\s]+):)?registry\s*=\s*(\S+)/.exec(l);
13654
13804
  return !!m && privateUrl(m[2]) && (m[1] === undefined || name.startsWith(`${m[1]}/`));
@@ -13657,7 +13807,7 @@ function userConfigPrivate(eco, repoDir, env, goEnv = {}, name = "") {
13657
13807
  if (eco === "pypi") {
13658
13808
  if (privateUrlList([env["PIP_INDEX_URL"] ?? "", env["PIP_EXTRA_INDEX_URL"] ?? ""]))
13659
13809
  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")];
13810
+ const cfg = [env["PIP_CONFIG_FILE"] ?? "", home ? join59(home, ".config", "pip", "pip.conf") : "", home ? join59(home, ".pip", "pip.conf") : "", join59(repoDir, "pip.conf")];
13661
13811
  return files(...cfg).some((t) => t.split(`
13662
13812
  `).some((l) => {
13663
13813
  const m = /^\s*(?:extra-)?index-url\s*[=:]\s*(.+)$/.exec(l);
@@ -13667,7 +13817,7 @@ function userConfigPrivate(eco, repoDir, env, goEnv = {}, name = "") {
13667
13817
  if (eco === "cargo") {
13668
13818
  if (Object.keys(env).some((k) => /^CARGO_REGISTRIES_.+_INDEX$/.test(k) || /^CARGO_SOURCE_/.test(k)))
13669
13819
  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));
13820
+ 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
13821
  }
13672
13822
  if (eco === "go") {
13673
13823
  const gp = goEnv["GOPROXY"] ?? env["GOPROXY"] ?? "";
@@ -13790,7 +13940,7 @@ var MIN_AGE_DAYS = 7, DECLARED_FILE = "supply-declared.json", MAX_CHECKED = 50,
13790
13940
  }
13791
13941
  }, 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
13942
  try {
13793
- return statSync18(p).size <= MAX_FILE_BYTES ? readFileSync43(p, "utf8") : "";
13943
+ return statSync18(p).size <= MAX_FILE_BYTES ? readFileSync44(p, "utf8") : "";
13794
13944
  } catch {
13795
13945
  return "";
13796
13946
  }
@@ -13806,8 +13956,8 @@ var init_supply_guard = __esm(() => {
13806
13956
 
13807
13957
  // ../../loki-ts/src/engine10/stages/seal.ts
13808
13958
  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";
13959
+ import { existsSync as existsSync38, readdirSync as readdirSync19, mkdirSync as mkdirSync24, readFileSync as readFileSync45, writeFileSync as writeFileSync26 } from "fs";
13960
+ import { basename as basename21, isAbsolute as isAbsolute9, join as join60, relative as relative9 } from "path";
13811
13961
  function sanitizeReason(s) {
13812
13962
  const collapsed = s.replace(/[\x00-\x1f\x7f]+/g, " ").trim();
13813
13963
  const capped = collapsed.length > 500 ? `${collapsed.slice(0, 500)}...` : collapsed;
@@ -13882,7 +14032,7 @@ function renderReceiptMd(r) {
13882
14032
  `);
13883
14033
  }
13884
14034
  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"));
14035
+ const r = relative9(ctx.repoDir, join60(ctx.runDir, "receipt.json"));
13886
14036
  return r.startsWith("..") || isAbsolute9(r) ? `.loki/runs/${ctx.runId}/receipt.json` : r;
13887
14037
  }, commitStage, sealStage;
13888
14038
  var init_seal = __esm(() => {
@@ -13985,7 +14135,7 @@ var init_seal = __esm(() => {
13985
14135
  if (wallNotRun > 0) {
13986
14136
  try {
13987
14137
  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())
14138
+ for (const n2 of readdirSync19(join60(ctx.runDir, "wall")).sort())
13989
14139
  if (!kept.has(n2))
13990
14140
  notProven.add(`wall test discarded: ${n2} (not_run)`);
13991
14141
  } catch {}
@@ -14046,19 +14196,19 @@ var init_seal = __esm(() => {
14046
14196
  for (const [s, d] of Object.entries(o))
14047
14197
  if (typeof d?.duration_s === "number")
14048
14198
  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;
14199
+ 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
14200
  const iterIds = Object.values(o).flatMap((d) => [...strs2(d?.iteration_ids), ...strs2([d?.iteration_id])]);
14051
14201
  if (iterIds.length === 0)
14052
14202
  notProven.add("cost not measured (no iteration ids recorded)");
14053
14203
  const cost = ctx.cost.read(ctx.repoDir, iterIds);
14054
14204
  if (cost.unmetered)
14055
14205
  notProven.add("cost unmetered (CLI invoker; recorded as 0)");
14056
- const eventsPath = join59(ctx.runDir, "events.jsonl");
14206
+ const eventsPath = join60(ctx.runDir, "events.jsonl");
14057
14207
  const wallFiles = Array.isArray(o.wall?.files) ? o.wall.files : [];
14058
14208
  const wallPassed = typeof o.verify?.wall_passed === "boolean" ? o.verify.wall_passed : null;
14059
14209
  if (wallFiles.length > 0 && wallPassed === null)
14060
14210
  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);
14211
+ 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
14212
  if (routeBlock)
14063
14213
  for (const l of routeNotProven(routeBlock))
14064
14214
  notProven.add(l);
@@ -14100,7 +14250,7 @@ var init_seal = __esm(() => {
14100
14250
  provider: ctx.provider,
14101
14251
  model: ctx.model,
14102
14252
  resumed: o.intake?.resumed === true,
14103
- events_sha256: sha2562(existsSync38(eventsPath) ? readFileSync44(eventsPath) : ""),
14253
+ events_sha256: sha2562(existsSync38(eventsPath) ? readFileSync45(eventsPath) : ""),
14104
14254
  ...await sealEvidence(ctx.repoDir, ctx.runDir, o, notProven, signal, ctx.emit),
14105
14255
  log_seal: true,
14106
14256
  ...receiptBlock(process.env, cost.usd, cost.unmetered === true, totalS),
@@ -14121,10 +14271,10 @@ var init_seal = __esm(() => {
14121
14271
  }
14122
14272
  const receipt = { ...body, receipt_sha256: hash, verification: { jwt: sig.jwt, kid: sig.kid } };
14123
14273
  mkdirSync24(ctx.runDir, { recursive: true });
14124
- const path = join59(ctx.runDir, "receipt.json");
14274
+ const path = join60(ctx.runDir, "receipt.json");
14125
14275
  writeFileSync26(path, JSON.stringify(receipt, null, 2) + `
14126
14276
  `);
14127
- writeFileSync26(join59(ctx.runDir, "receipt.md"), renderReceiptMd(receipt));
14277
+ writeFileSync26(join60(ctx.runDir, "receipt.md"), renderReceiptMd(receipt));
14128
14278
  recordRun(process.env, ctx.repoDir, ctx.model, verdict, cost.usd, cost.unmetered === true, totalS);
14129
14279
  const signed = sig.jwt !== null;
14130
14280
  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 +14293,8 @@ var init_seal = __esm(() => {
14143
14293
  });
14144
14294
 
14145
14295
  // ../../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";
14296
+ import { createPublicKey as createPublicKey3, sign as sign2, verify as verify3 } from "crypto";
14297
+ import { readFileSync as readFileSync46 } from "fs";
14148
14298
  function pae(payloadType, body) {
14149
14299
  const t = Buffer.from(payloadType, "utf8");
14150
14300
  return Buffer.concat([Buffer.from(`DSSEv1 ${t.length} `), t, Buffer.from(` ${body.length} `), body]);
@@ -14163,7 +14313,7 @@ function verifyEnvelope(env, pubFor) {
14163
14313
  continue;
14164
14314
  }
14165
14315
  known++;
14166
- if (verify2(null, pae(env.payloadType, body), pub, Buffer.from(s.sig, "base64"))) {
14316
+ if (verify3(null, pae(env.payloadType, body), pub, Buffer.from(s.sig, "base64"))) {
14167
14317
  kid = s.keyid;
14168
14318
  break;
14169
14319
  }
@@ -14221,7 +14371,7 @@ function verifyDsseReceipt(env, d) {
14221
14371
  }
14222
14372
  function runIdGuard(receiptPath, runId) {
14223
14373
  try {
14224
- return envelopeRunIdProblem(JSON.parse(readFileSync45(receiptPath, "utf8")), runId);
14374
+ return envelopeRunIdProblem(JSON.parse(readFileSync46(receiptPath, "utf8")), runId);
14225
14375
  } catch {
14226
14376
  return null;
14227
14377
  }
@@ -14235,16 +14385,16 @@ var init_keys_cmd = __esm(() => {
14235
14385
  });
14236
14386
 
14237
14387
  // ../../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";
14388
+ import { existsSync as existsSync39, readFileSync as readFileSync47, renameSync as renameSync10, statSync as statSync20, writeFileSync as writeFileSync27 } from "fs";
14389
+ import { basename as basename22, dirname as dirname27, join as join61 } from "path";
14390
+ import { createHash as createHash16, createPublicKey as createPublicKey4, verify as verify4 } from "crypto";
14241
14391
  function pubFor(kid, given) {
14242
14392
  if (given)
14243
14393
  return kidOf2(given) === kid ? given : undefined;
14244
14394
  const active = loadSigningKey(false);
14245
14395
  const pubs = (process.env["LOKI_RECEIPT_RETIRED_PUBKEYS"] ?? "").split(":").map((f) => f.trim()).filter(Boolean).flatMap((f) => {
14246
14396
  try {
14247
- return [createPublicKey4(readFileSync46(f))];
14397
+ return [createPublicKey4(readFileSync47(f))];
14248
14398
  } catch {
14249
14399
  return [];
14250
14400
  }
@@ -14268,7 +14418,7 @@ function checkAttestation(jwt, expectedHash, given) {
14268
14418
  const pub = pubFor(header.kid, given);
14269
14419
  if (!pub)
14270
14420
  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")))
14421
+ if (!verify4(null, Buffer.from(`${h}.${p}`), pub, Buffer.from(s, "base64url")))
14272
14422
  return bad("signature does not verify");
14273
14423
  return payload?.receipt_sha256 === expectedHash ? { status: "verified", reason: null } : bad("attestation binds a different receipt hash");
14274
14424
  }
@@ -14276,17 +14426,17 @@ function checkEventLog(receiptPath, receipt) {
14276
14426
  const bound = receipt["events_sha256"];
14277
14427
  if (typeof bound !== "string" || bound === createHash16("sha256").digest("hex"))
14278
14428
  return null;
14279
- const path = join60(dirname27(receiptPath), "events.jsonl");
14429
+ const path = join61(dirname27(receiptPath), "events.jsonl");
14280
14430
  if (!existsSync39(path))
14281
14431
  return "events.jsonl is missing";
14282
- const raw2 = readFileSync46(path, "utf8"), events2 = readEvents(path), lines = raw2.split(`
14432
+ const raw2 = readFileSync47(path, "utf8"), events2 = readEvents(path), lines2 = raw2.split(`
14283
14433
  `).filter((l) => l.trim() !== "");
14284
- if (events2.length !== lines.length || events2.some((e, i) => e.seq !== i))
14434
+ if (events2.length !== lines2.length || events2.some((e, i) => e.seq !== i))
14285
14435
  return "events.jsonl has a forged, edited or missing line";
14286
14436
  if (events2.some((e) => e.type === "tamper.detected"))
14287
14437
  return "the supervisor detected events.jsonl being modified during the run";
14288
14438
  const h = createHash16("sha256");
14289
- for (const l of lines) {
14439
+ for (const l of lines2) {
14290
14440
  if (h.copy().digest("hex") === bound)
14291
14441
  return null;
14292
14442
  h.update(l + `
@@ -14295,7 +14445,7 @@ function checkEventLog(receiptPath, receipt) {
14295
14445
  return h.digest("hex") === bound ? null : "events.jsonl does not match the hash recorded at seal";
14296
14446
  }
14297
14447
  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");
14448
+ const path = join61(dirname27(receiptPath), "events.jsonl"), events2 = readEvents(path), done = events2.findIndex((e) => e.type === "run.completed");
14299
14449
  if (!events2.some((e) => e.type === "receipt.sealed" && e.data["receipt_sha256"] === hash))
14300
14450
  return { verdict: "TAMPERED", reason: "events.jsonl has no receipt.sealed event for this receipt" };
14301
14451
  if (done < 0)
@@ -14303,12 +14453,12 @@ function checkLogSeal(receiptPath, hash, kid, given) {
14303
14453
  const seal = events2[done + 1], d = seal?.type === "log.sealed" ? seal.data : null;
14304
14454
  if (!d)
14305
14455
  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(`
14456
+ const lines2 = readFileSync47(path, "utf8").split(`
14457
+ `).filter((l) => l.trim() !== ""), sha3 = createHash16("sha256").update(lines2.slice(0, done + 1).join(`
14308
14458
  `) + `
14309
14459
  `).digest("hex");
14310
14460
  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")))
14461
+ 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
14462
  return { verdict: "TAMPERED", reason: "log.sealed line is invalid, forged, or records a tamper" };
14313
14463
  return null;
14314
14464
  }
@@ -14317,7 +14467,7 @@ async function verifyReceipt(receiptPath, deps = {}) {
14317
14467
  return { verdict: "UNCHECKED", reasons: [`receipt not found: ${receiptPath}`] };
14318
14468
  let receipt;
14319
14469
  try {
14320
- receipt = JSON.parse((deps.read ?? ((p) => readFileSync46(p, "utf8")))(receiptPath));
14470
+ receipt = JSON.parse((deps.read ?? ((p) => readFileSync47(p, "utf8")))(receiptPath));
14321
14471
  } catch {
14322
14472
  return { verdict: "UNCHECKED", reasons: ["receipt.json is not valid JSON"] };
14323
14473
  }
@@ -14399,8 +14549,8 @@ var init_verify_cmd = __esm(() => {
14399
14549
  });
14400
14550
 
14401
14551
  // 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";
14552
+ import { existsSync as existsSync41, readFileSync as readFileSync50, statSync as statSync21 } from "fs";
14553
+ import { join as join64, resolve as resolve15, sep as sep14 } from "path";
14404
14554
 
14405
14555
  // node_modules/hono/dist/request/constants.js
14406
14556
  var GET_MATCH_RESULT = Symbol();
@@ -20172,6 +20322,9 @@ var SHA = /^[0-9a-f]{40,64}$/;
20172
20322
  var MAX_RECEIPT_BYTES = 1024 * 1024;
20173
20323
  var GIT_TIMEOUT_MS = 5000;
20174
20324
  var cache = new Map;
20325
+ var FAIL_TTL_MS = 60000;
20326
+ var failed = new Map;
20327
+ var diffStatGitRuns = { n: 0 };
20175
20328
  var gitEnv = () => ({ PATH: process.env.PATH ?? "", HOME: process.env.HOME ?? "", LC_ALL: "C", GIT_TERMINAL_PROMPT: "0", GIT_OPTIONAL_LOCKS: "0" });
20176
20329
  function parseNumstat(out) {
20177
20330
  const files = [];
@@ -20229,13 +20382,19 @@ function diffStatFor(db, source, run) {
20229
20382
  const base = typeof r?.base_sha === "string" ? r.base_sha : "", head = typeof r?.head_sha === "string" ? r.head_sha : "";
20230
20383
  if (!SHA.test(base) || !SHA.test(head))
20231
20384
  return null;
20385
+ const failKey = `${key}:${base}:${head}`;
20386
+ const failedAt = failed.get(failKey);
20387
+ if (failedAt !== undefined && Date.now() - failedAt < FAIL_TTL_MS)
20388
+ return null;
20232
20389
  try {
20390
+ diffStatGitRuns.n++;
20233
20391
  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
20392
  const files = parseNumstat(out);
20235
20393
  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
20394
  cache.set(key, stat);
20237
20395
  return stat;
20238
20396
  } catch {
20397
+ failed.set(failKey, Date.now());
20239
20398
  return null;
20240
20399
  }
20241
20400
  }
@@ -20604,10 +20763,10 @@ function stopReason(evs, verdict) {
20604
20763
  return { reason: /[.!?]$/.test(reason) ? reason : `${reason}.`, ownRules: OWN_RULES.test(text2) };
20605
20764
  }
20606
20765
  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 };
20766
+ const failed2 = [...evs].reverse().find((e) => e.type === "stage.failed");
20767
+ const why = typeof failed2?.data?.reason === "string" ? oneLine(failed2.data.reason, 240) : "";
20768
+ if (failed2 && failed2.stage)
20769
+ return { reason: `The run failed in the ${failed2.stage} stage${why ? `: ${why}` : ""}.`, ownRules: false };
20611
20770
  const bad = evs.filter((e) => e.type === "test.result" && e.data.result === "fail").length;
20612
20771
  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
20772
  }
@@ -22197,6 +22356,7 @@ var isObj4 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
22197
22356
  var str2 = (v) => typeof v === "string" && v !== "" ? v : null;
22198
22357
  var SAFE_ID = /^[A-Za-z0-9_-]{1,128}$/;
22199
22358
  var MAX_CHECKPOINTS = 50;
22359
+ var ROLLBACK_ALLOW = new Set(["session.json", "dashboard-state.json", "CONTINUITY.md", "autonomy-state.json", "state", "queue"]);
22200
22360
  var validCheckpointId = (id) => SAFE_ID.test(id);
22201
22361
  var storeOf = (repoDir) => join17(repoDir, ".loki", "state", "checkpoints");
22202
22362
  var lokiOf = (repoDir) => join17(repoDir, ".loki");
@@ -22336,16 +22496,33 @@ function rollbackCheckpoint(repoDir, id, now2 = new Date) {
22336
22496
  `);
22337
22497
  let restored = 0;
22338
22498
  const errors = [];
22499
+ const skipped = (src) => src === join17(real2, "state", "checkpoints");
22339
22500
  for (const e of readdirSync4(real2, { withFileTypes: true })) {
22340
22501
  if (e.name === "metadata.json" || e.name === "worktree-snapshot.txt")
22341
22502
  continue;
22342
- const src = join17(real2, e.name);
22503
+ if (!ROLLBACK_ALLOW.has(e.name)) {
22504
+ errors.push(`${e.name}: not in the rollback allowlist, skipped`);
22505
+ continue;
22506
+ }
22507
+ const src = join17(real2, e.name), dst = join17(loki, e.name);
22343
22508
  try {
22344
22509
  if (lstatSync2(src).isSymbolicLink()) {
22345
22510
  errors.push(`${e.name}: symlink skipped`);
22346
22511
  continue;
22347
22512
  }
22348
- cpSync(src, join17(loki, e.name), { recursive: true, filter: (s) => s !== join17(real2, "state", "checkpoints") });
22513
+ if (existsSync8(dst) && lstatSync2(dst).isSymbolicLink()) {
22514
+ errors.push(`${e.name}: destination is a symlink, skipped`);
22515
+ continue;
22516
+ }
22517
+ cpSync(src, dst, { recursive: true, filter: (s) => {
22518
+ if (skipped(s))
22519
+ return false;
22520
+ if (lstatSync2(s).isSymbolicLink()) {
22521
+ errors.push(`${s.slice(real2.length + 1)}: nested symlink skipped`);
22522
+ return false;
22523
+ }
22524
+ return true;
22525
+ } });
22349
22526
  restored++;
22350
22527
  } catch (err) {
22351
22528
  errors.push(`${e.name}: ${err.message}`);
@@ -23567,8 +23744,8 @@ function tasksList(loki, status) {
23567
23744
  const out2 = order2.map((o) => ("ref" in o) ? byId.get(o.ref) : o);
23568
23745
  for (const t of out2) {
23569
23746
  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;
23747
+ const ec = t.exitCode, failed2 = t._terminal_outcome === "failed";
23748
+ 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
23749
  if (o)
23573
23750
  t.description = `Iteration ${o}${t.provider ? `, built by ${t.provider}` : ""}.`;
23574
23751
  }
@@ -24575,7 +24752,7 @@ function mount19(ctx) {
24575
24752
  function computeStats(db, since) {
24576
24753
  const all = db.select().from(runs).all().filter((r) => !since || r.startedAt !== null && r.startedAt >= since);
24577
24754
  const by = {};
24578
- let finished = 0, running = 0, blocked = 0, verified = 0, unchecked = 0, failed = 0, satisfied = 0, other = 0;
24755
+ let finished = 0, running = 0, blocked = 0, verified = 0, unchecked = 0, failed2 = 0, satisfied = 0, other = 0;
24579
24756
  let mUsd = 0, mRuns = 0, pUsd = 0, pRuns = 0;
24580
24757
  for (const r of all) {
24581
24758
  if (r.endedAt)
@@ -24593,7 +24770,7 @@ function computeStats(db, since) {
24593
24770
  else if (verdict === "ALREADY_SATISFIED" || verdict === "ALREADY_SATISFIED (signature not checked)")
24594
24771
  satisfied++;
24595
24772
  else if (verdict === "FAILED" || verdict === "PARTIAL" || verdict === "TAMPERED")
24596
- failed++;
24773
+ failed2++;
24597
24774
  else
24598
24775
  other++;
24599
24776
  }
@@ -24625,8 +24802,8 @@ function computeStats(db, since) {
24625
24802
  runs_running: running,
24626
24803
  by_verdict: by,
24627
24804
  blocked_waiting: blocked,
24628
- verified_rate: verified + unchecked + failed ? verified / (verified + unchecked + failed) : null,
24629
- breakdown: { verified, failed, already_satisfied: satisfied, other },
24805
+ verified_rate: verified + unchecked + failed2 ? verified / (verified + unchecked + failed2) : null,
24806
+ breakdown: { verified, failed: failed2, already_satisfied: satisfied, other },
24630
24807
  verified_unchecked: unchecked,
24631
24808
  keys_configured: pubkeysFromEnv().configured === true,
24632
24809
  ...pubkeysFromEnv().reason ? { keys_reason: pubkeysFromEnv().reason } : {},
@@ -24650,9 +24827,9 @@ function mount20(ctx) {
24650
24827
  }
24651
24828
 
24652
24829
  // src/server/routes/verify.ts
24653
- import { readFileSync as readFileSync47, realpathSync as realpathSync20 } from "fs";
24830
+ import { readFileSync as readFileSync48, realpathSync as realpathSync20 } from "fs";
24654
24831
  import { createPublicKey as createPublicKey5 } from "crypto";
24655
- import { join as join61, sep as sep13 } from "path";
24832
+ import { join as join62, sep as sep13 } from "path";
24656
24833
  init_verify_cmd();
24657
24834
  init_seal();
24658
24835
  init_receipt_dsse();
@@ -24679,11 +24856,11 @@ function mount21(ctx) {
24679
24856
  return done(404, "receipt not available", "not_found");
24680
24857
  let runDir, file;
24681
24858
  try {
24682
- const root = realpathSync20(join61(repo.p, ".loki", "runs"));
24683
- runDir = realpathSync20(join61(root, run2));
24684
- if (runDir !== join61(root, run2))
24859
+ const root = realpathSync20(join62(repo.p, ".loki", "runs"));
24860
+ runDir = realpathSync20(join62(root, run2));
24861
+ if (runDir !== join62(root, run2))
24685
24862
  return done(404, "receipt not found", "not_found");
24686
- file = realpathSync20(join61(runDir, "receipt.json"));
24863
+ file = realpathSync20(join62(runDir, "receipt.json"));
24687
24864
  } catch {
24688
24865
  return done(404, "receipt not found", "not_found");
24689
24866
  }
@@ -24702,7 +24879,7 @@ function mount21(ctx) {
24702
24879
  sha3 = r.receiptSha256 ?? null;
24703
24880
  }
24704
24881
  try {
24705
- outcome = outcomeOf(JSON.parse(readFileSync47(file, "utf8")));
24882
+ outcome = outcomeOf(JSON.parse(readFileSync48(file, "utf8")));
24706
24883
  } catch {
24707
24884
  outcome = "UNREADABLE";
24708
24885
  }
@@ -24764,8 +24941,8 @@ function ttlCache(ttlMs = PROBE_CACHE_MS, now2 = Date.now) {
24764
24941
  }
24765
24942
 
24766
24943
  // src/server/legacy/shim.ts
24767
- import { existsSync as existsSync40, readFileSync as readFileSync48 } from "fs";
24768
- import { join as join62 } from "path";
24944
+ import { existsSync as existsSync40, readFileSync as readFileSync49 } from "fs";
24945
+ import { join as join63 } from "path";
24769
24946
 
24770
24947
  // src/server/legacy/routes.ts
24771
24948
  var LEGACY_ROUTES = [
@@ -25108,11 +25285,12 @@ function metricsMapped(repoDir) {
25108
25285
  // src/server/legacy/routes-control.ts
25109
25286
  function controlMapped(db, repoDir) {
25110
25287
  const loki = () => lokiDirOf(repoDir);
25288
+ const ctl = (action) => (c) => peerIsLoopback(c) && isLoopbackHost(c.req.header("host")) ? runControl(db, repoDir, action, c, "legacy") : c.json({ error: "loopback only" }, 403);
25111
25289
  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"),
25290
+ "POST /api/control/pause": ctl("pause"),
25291
+ "POST /api/control/resume": ctl("resume"),
25292
+ "POST /api/control/stop": ctl("stop"),
25293
+ "POST /api/council/force-review": ctl("council-review"),
25116
25294
  "GET /api/council/state": (c) => c.json(councilState(loki())),
25117
25295
  "GET /api/council/verdicts": (c) => {
25118
25296
  const n2 = intQ(c.req.query("limit"), 1000);
@@ -25137,10 +25315,10 @@ var logged = new Set;
25137
25315
  var toHono = (p) => p.replace(/\{([A-Za-z_]+):path\}/g, ":$1{.+}").replace(/\{([A-Za-z_]+)\}/g, ":$1");
25138
25316
  var NOT_SUPPORTED = "not yet supported by the Control Plane";
25139
25317
  var repoVersion = () => {
25140
- for (const f of [join62(import.meta.dir, "../../../../../VERSION")]) {
25318
+ for (const f of [join63(import.meta.dir, "../../../../../VERSION")]) {
25141
25319
  try {
25142
25320
  if (existsSync40(f))
25143
- return readFileSync48(f, "utf8").trim();
25321
+ return readFileSync49(f, "utf8").trim();
25144
25322
  } catch {}
25145
25323
  }
25146
25324
  return "unknown";
@@ -25322,7 +25500,7 @@ function legacyShim(opts) {
25322
25500
 
25323
25501
  // src/server/app.ts
25324
25502
  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")));
25503
+ var defaultUiDir = () => [join64(import.meta.dir, "../../ui/dist"), join64(import.meta.dir, "../ui/dist")].find((d) => existsSync41(join64(d, "index.html")));
25326
25504
  function createApp(opts) {
25327
25505
  const uiDir = opts.uiDir ?? defaultUiDir();
25328
25506
  const { db, sqlite } = openDb(opts.dbPath);
@@ -25351,10 +25529,10 @@ function createApp(opts) {
25351
25529
  await next();
25352
25530
  });
25353
25531
  const readInstalled = () => {
25354
- for (const f of [process.env.LOKI_VERSION_FILE, join63(import.meta.dir, "../../../../VERSION")]) {
25532
+ for (const f of [process.env.LOKI_VERSION_FILE, join64(import.meta.dir, "../../../../VERSION")]) {
25355
25533
  try {
25356
25534
  if (f)
25357
- return readFileSync49(f, "utf8").trim() || "unknown";
25535
+ return readFileSync50(f, "utf8").trim() || "unknown";
25358
25536
  } catch {}
25359
25537
  }
25360
25538
  return "unknown";
@@ -25469,7 +25647,7 @@ function createApp(opts) {
25469
25647
  return new Response(Bun.file(f));
25470
25648
  if (/\.[a-z0-9]+$/i.test(p))
25471
25649
  return c.json({ error: "not found" }, 404);
25472
- return new Response(Bun.file(join63(root, "index.html")));
25650
+ return new Response(Bun.file(join64(root, "index.html")));
25473
25651
  });
25474
25652
  return { app, db, close: () => {
25475
25653
  clearInterval(reconciler);