loki-mode 11.17.45 → 11.17.46

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 (74) hide show
  1. package/SKILL.md +2 -2
  2. package/VERSION +1 -1
  3. package/autonomy/lib/secret-scan.sh +4 -0
  4. package/autonomy/loki +25 -6
  5. package/completions/_loki +6 -0
  6. package/completions/loki.bash +8 -0
  7. package/dashboard/__init__.py +1 -1
  8. package/docs/CLI-REFERENCE.md +1 -1
  9. package/docs/INSTALLATION.md +1 -1
  10. package/docs/v10/BOARD.md +30 -30
  11. package/docs/v10/FAILURE-CLASSES.md +8 -0
  12. package/docs/v10/PROGRESS.md +3 -0
  13. package/loki-ts/dist/loki.js +1267 -1252
  14. package/mcp/__init__.py +1 -1
  15. package/package.json +2 -2
  16. package/packages/control-plane/dist/server.js +1742 -1717
  17. package/plugins/loki-mode/.claude-plugin/plugin.json +1 -1
  18. package/web-app/dist/assets/{AdminPage-BCGIu6vt.js → AdminPage-DOQbb9S2.js} +1 -1
  19. package/web-app/dist/assets/{Avatar-FGro9b68.js → Avatar-DlCHpaY9.js} +1 -1
  20. package/web-app/dist/assets/{Badge-DZ0OOxdx.js → Badge-CsGRidkQ.js} +1 -1
  21. package/web-app/dist/assets/{Button-C702FTxi.js → Button-CaNiRTkv.js} +1 -1
  22. package/web-app/dist/assets/{CockpitPage-B1CZaaXx.js → CockpitPage-B8sezC-W.js} +1 -1
  23. package/web-app/dist/assets/{ComparePage-DRd-w9yO.js → ComparePage-B36mEWLR.js} +1 -1
  24. package/web-app/dist/assets/{ErrorBoundary-B9T1V8_Q.js → ErrorBoundary-BFGjCcE5.js} +1 -1
  25. package/web-app/dist/assets/{EvidenceReceiptPanel-CScPICN7.js → EvidenceReceiptPanel-mFXX7T0e.js} +1 -1
  26. package/web-app/dist/assets/{GitHubIssuesPanel-BL8nzIoH.js → GitHubIssuesPanel-BjUOWEuY.js} +1 -1
  27. package/web-app/dist/assets/{GitHubPRsPanel-W0FX5DSl.js → GitHubPRsPanel-CcLMzJZk.js} +1 -1
  28. package/web-app/dist/assets/{HomePage-_3jq9WUA.js → HomePage-9j8CciDG.js} +2 -2
  29. package/web-app/dist/assets/{LoginPage-DsPxoAOJ.js → LoginPage-BXEJEipC.js} +1 -1
  30. package/web-app/dist/assets/{MagicPage-nOER2-ZE.js → MagicPage-JJhHAzUo.js} +1 -1
  31. package/web-app/dist/assets/{MetricsPage-CoiAlCTC.js → MetricsPage-g2murSvE.js} +1 -1
  32. package/web-app/dist/assets/{NotFoundPage-l46SOx1i.js → NotFoundPage-D3fshofn.js} +1 -1
  33. package/web-app/dist/assets/{ProjectPage-DxO8dkf_.js → ProjectPage-B91c8gqI.js} +3 -3
  34. package/web-app/dist/assets/{ProjectsPage-DtNSAVm9.js → ProjectsPage-f-AqCLnm.js} +1 -1
  35. package/web-app/dist/assets/{SettingsPage-DbNetRfY.js → SettingsPage-xmKoxIhv.js} +1 -1
  36. package/web-app/dist/assets/{ShowcasePage-BSzhjOYS.js → ShowcasePage-DxPIEO67.js} +1 -1
  37. package/web-app/dist/assets/{SystemSettingsPage-DWrN72dn.js → SystemSettingsPage-Cm9Dy2-W.js} +1 -1
  38. package/web-app/dist/assets/{TeamsPage-CVFEOL9r.js → TeamsPage-xUmbM2Q6.js} +1 -1
  39. package/web-app/dist/assets/{TemplatesPage-IpcsZhEh.js → TemplatesPage-BF0atSP9.js} +1 -1
  40. package/web-app/dist/assets/{TerminalOutput-Bv9Zgwyj.js → TerminalOutput-BRR-hjM0.js} +1 -1
  41. package/web-app/dist/assets/{activity-CHgtGwwW.js → activity-bs7r0Zgu.js} +1 -1
  42. package/web-app/dist/assets/{bell-Ym0Mx2Xd.js → bell-BSBQU6oH.js} +1 -1
  43. package/web-app/dist/assets/{bot-C-6tA3JT.js → bot-IY1WHsy_.js} +1 -1
  44. package/web-app/dist/assets/{check-CyFyZ4Qi.js → check-BbLOs82D.js} +1 -1
  45. package/web-app/dist/assets/{chevron-left-B3SPNdcz.js → chevron-left-Dr_YJex2.js} +1 -1
  46. package/web-app/dist/assets/{circle-alert-DcKKeKmd.js → circle-alert-CdPabs5h.js} +1 -1
  47. package/web-app/dist/assets/{clock-CHLwYBFX.js → clock-DSSC0-0Q.js} +1 -1
  48. package/web-app/dist/assets/{cloud-CBE7j7iG.js → cloud-D_gWiz8h.js} +1 -1
  49. package/web-app/dist/assets/{code-xml-Cu-b65vO.js → code-xml-BeK0mcX9.js} +1 -1
  50. package/web-app/dist/assets/{database-DSJlQYxs.js → database-CcJkBSXN.js} +1 -1
  51. package/web-app/dist/assets/{dollar-sign-BcPFsZq0.js → dollar-sign-B0KTo6gY.js} +1 -1
  52. package/web-app/dist/assets/{file-code-corner-Dl_LDpv_.js → file-code-corner-Br9i1QUK.js} +1 -1
  53. package/web-app/dist/assets/{file-plus-BoLEmKCc.js → file-plus-Cntlx-H3.js} +1 -1
  54. package/web-app/dist/assets/{globe-4-KwsIUo.js → globe-BzTQwBst.js} +1 -1
  55. package/web-app/dist/assets/{hammer-CaG_crBz.js → hammer-DY5E-S_s.js} +1 -1
  56. package/web-app/dist/assets/{index-DD7FC7yd.js → index-D8iQaeY_.js} +3 -3
  57. package/web-app/dist/assets/{layers-BDQEO970.js → layers-fichWK2q.js} +1 -1
  58. package/web-app/dist/assets/{loader-circle-ENh48A-3.js → loader-circle-H4GEC-nt.js} +1 -1
  59. package/web-app/dist/assets/{lock-CcI4ltMK.js → lock-L2gybTp0.js} +1 -1
  60. package/web-app/dist/assets/{package-DA_Bt7Mx.js → package-C6GhNvrY.js} +1 -1
  61. package/web-app/dist/assets/{plus-r_jqdmwV.js → plus-C76f-H0b.js} +1 -1
  62. package/web-app/dist/assets/{refresh-cw-B0o3B073.js → refresh-cw-CYrIM5Gt.js} +1 -1
  63. package/web-app/dist/assets/{rotate-ccw-D5EyaAC-.js → rotate-ccw-DuVzPMTc.js} +1 -1
  64. package/web-app/dist/assets/{scroll-text-DKrZwpKk.js → scroll-text-Ou40WwYD.js} +1 -1
  65. package/web-app/dist/assets/{server-FMLinzaf.js → server-CY2Y_apg.js} +1 -1
  66. package/web-app/dist/assets/{shield-alert-C5YWhIfX.js → shield-alert-CNf-jmi3.js} +1 -1
  67. package/web-app/dist/assets/{thumbs-up-lWteH19H.js → thumbs-up-DNRc_NvO.js} +1 -1
  68. package/web-app/dist/assets/{trash-2-CTWsJujI.js → trash-2-CdzowWF_.js} +1 -1
  69. package/web-app/dist/assets/{trending-down-n969VpQb.js → trending-down-DtZEAOFe.js} +1 -1
  70. package/web-app/dist/assets/{trending-up-hNpwNjdL.js → trending-up-BwiPR3rB.js} +1 -1
  71. package/web-app/dist/assets/{upload-DmcM8inI.js → upload-BI_6o8oU.js} +1 -1
  72. package/web-app/dist/assets/{usePolling-CQCRI-UO.js → usePolling-TefZLSUZ.js} +1 -1
  73. package/web-app/dist/assets/{user-BCspLAnr.js → user-BzczgZi4.js} +1 -1
  74. package/web-app/dist/index.html +1 -1
@@ -7594,201 +7594,13 @@ var init_paths = __esm(() => {
7594
7594
  REPO_ROOT = findRepoRoot();
7595
7595
  });
7596
7596
 
7597
- // ../../loki-ts/src/util/receipt_signer.ts
7598
- import { createHash as createHash6, randomBytes as randomBytes3, createPrivateKey, createPublicKey as createPublicKey2, generateKeyPairSync, sign, verify as verify2 } from "crypto";
7599
- import { chmodSync as chmodSync4, linkSync, mkdirSync as mkdirSync5, readdirSync as readdirSync9, readFileSync as readFileSync18, renameSync as renameSync4, statSync as statSync12, unlinkSync as unlinkSync3, writeFileSync as writeFileSync8 } from "fs";
7600
- import { homedir as homedir8 } from "os";
7601
- import { dirname as dirname7, join as join27 } from "path";
7602
- function canonicalJson(x) {
7603
- const walk = (v) => {
7604
- if (Array.isArray(v))
7605
- return `[${v.map(walk).join(",")}]`;
7606
- if (v !== null && typeof v === "object") {
7607
- const o = v;
7608
- return `{${Object.keys(o).filter((k) => o[k] !== undefined).sort().map((k) => `${JSON.stringify(k)}:${walk(o[k])}`).join(",")}}`;
7609
- }
7610
- return JSON.stringify(v ?? null);
7611
- };
7612
- return walk(x).replace(/[\u0080-\uFFFF]/g, (c) => `\\u${c.charCodeAt(0).toString(16).padStart(4, "0")}`);
7613
- }
7614
- function loadSigningKey(generate = true) {
7615
- try {
7616
- const inline = process.env["LOKI_RECEIPT_SIGNING_KEY"]?.trim();
7617
- let pem = inline ?? "";
7618
- if (!inline) {
7619
- const given = process.env["LOKI_RECEIPT_SIGNING_KEY_FILE"]?.trim();
7620
- const file = given || join27(process.env["HOME"] || homedir8(), ".loki", "keys", RECEIPT_SIGNER_BASENAME);
7621
- try {
7622
- pem = readFileSync18(file);
7623
- if (!given) {
7624
- for (const [f, m] of [[file, 384], [dirname7(file), 448]])
7625
- if (statSync12(f).mode & 63)
7626
- chmodSync4(f, m);
7627
- }
7628
- } catch (e) {
7629
- if (e.code !== "ENOENT" || !generate)
7630
- return null;
7631
- mkdirSync5(dirname7(dirname7(file)), { recursive: true, mode: 448 });
7632
- mkdirSync5(dirname7(file), { recursive: true, mode: 448 });
7633
- const tmp = `${file}.${randomBytes3(6).toString("hex")}.tmp`;
7634
- writeFileSync8(tmp, generateKeyPairSync("ed25519").privateKey.export({ type: "pkcs8", format: "pem" }), { flag: "wx", mode: 384 });
7635
- try {
7636
- linkSync(tmp, file);
7637
- } catch (l) {
7638
- if (l.code !== "EEXIST")
7639
- throw l;
7640
- } finally {
7641
- unlinkSync3(tmp);
7642
- }
7643
- pem = readFileSync18(file);
7644
- }
7645
- }
7646
- const k = createPrivateKey(pem);
7647
- return k.asymmetricKeyType === "ed25519" ? k : null;
7648
- } catch {
7649
- return null;
7650
- }
7651
- }
7652
- function signReceipt(runId, hash) {
7653
- const key = loadSigningKey();
7654
- if (!key)
7655
- return { jwt: null, kid: null };
7656
- const kid = kidOf2(createPublicKey2(key));
7657
- const input = `${b64u(canonicalJson({ alg: "EdDSA", typ: "JWT", kid }))}.${b64u(canonicalJson({ job_id: runId, run_id: runId, receipt_sha256: hash, iat: Math.floor(Date.now() / 1000) }))}`;
7658
- return { jwt: `${input}.${b64u(sign(null, Buffer.from(input), key))}`, kid };
7659
- }
7660
- var RECEIPT_SIGNER_BASENAME = "receipt-ed25519.pem", b64u = (b) => Buffer.from(b).toString("base64url"), kidOf2 = (pub) => b64u(createHash6("sha256").update(`{"crv":"Ed25519","kty":"OKP","x":"${pub.export({ format: "jwk" }).x}"}`).digest());
7661
- var init_receipt_signer = () => {};
7662
-
7663
- // ../../loki-ts/src/engine10/registry.ts
7664
- var init_registry = () => {};
7665
-
7666
- // ../../loki-ts/src/engine10/types.ts
7667
- function pushArgv(a) {
7668
- switch (a.cmd) {
7669
- case "push-pr":
7670
- return ["push-pr", a.repoDir, a.branch, a.title, a.bodyFile, a.draft ? "1" : "0"];
7671
- case "comment":
7672
- return ["comment", a.runId, a.prUrl, a.file];
7673
- case "status":
7674
- return ["status", a.sha, a.state, a.description];
7675
- case "issue-comment":
7676
- return a.update ? ["issue-comment", "--update", a.issueRef, a.bodyFile] : ["issue-comment", a.issueRef, a.bodyFile];
7677
- }
7678
- }
7679
- function taskBlock(task) {
7680
- return ["Task (untrusted, quoted verbatim):", "<<<TASK", task, "TASK"];
7681
- }
7682
- var STAGE_BUDGETS;
7683
- var init_types = __esm(() => {
7684
- STAGE_BUDGETS = {
7685
- intake: { targetS: 15, limitS: 60 },
7686
- plan: { targetS: 45, limitS: 90 },
7687
- wall: { targetS: 45, limitS: 90 },
7688
- implement: { targetS: 180, limitS: 480 },
7689
- verify: { targetS: 60, limitS: 120 },
7690
- fix: { targetS: 90, limitS: 180 },
7691
- commit: { targetS: 5, limitS: 30 },
7692
- seal: { targetS: 15, limitS: 60 },
7693
- pr: { targetS: 15, limitS: 60 },
7694
- deep: { targetS: null, limitS: 2700 }
7695
- };
7696
- });
7697
-
7698
- // ../../loki-ts/src/engine10/output.ts
7699
- var init_output = __esm(() => {
7700
- init_registry();
7701
- init_redact();
7702
- init_types();
7703
- });
7704
-
7705
- // ../../loki-ts/src/util/atomic.ts
7706
- import {
7707
- closeSync as closeSync5,
7708
- fstatSync as fstatSync4,
7709
- fsyncSync as fsyncSync2,
7710
- lstatSync as lstatSync5,
7711
- mkdirSync as mkdirSync6,
7712
- openSync as openSync5,
7713
- readSync as readSync4,
7714
- renameSync as renameSync5,
7715
- rmSync as rmSync5,
7716
- statSync as statSync13,
7717
- unlinkSync as unlinkSync4,
7718
- writeFileSync as writeFileSync9,
7719
- writeSync as writeSync3
7720
- } from "fs";
7721
- import { dirname as dirname8 } from "path";
7722
- function durableWriteThenRename(target, body) {
7723
- mkdirSync6(dirname8(target), { recursive: true });
7724
- const tmp = tmpPathFor(target);
7725
- const fd = openSync5(tmp, "w");
7726
- try {
7727
- writeSync3(fd, body);
7728
- fsyncSync2(fd);
7729
- } finally {
7730
- closeSync5(fd);
7731
- }
7732
- renameSync5(tmp, target);
7733
- fsyncParentDir(target);
7734
- }
7735
- function fsyncParentDir(target) {
7736
- let dfd = null;
7737
- try {
7738
- dfd = openSync5(dirname8(target), "r");
7739
- fsyncSync2(dfd);
7740
- } catch {} finally {
7741
- if (dfd !== null) {
7742
- try {
7743
- closeSync5(dfd);
7744
- } catch {}
7745
- }
7746
- }
7747
- }
7748
- function atomicWriteJson(target, data) {
7749
- durableWriteThenRename(target, `${JSON.stringify(data, null, 2)}
7750
- `);
7751
- }
7752
- function tmpPathFor(target) {
7753
- return `${target}.tmp.${process.pid}.${++_tmpCounter}`;
7754
- }
7755
- var _tmpCounter = 0, _appendChains;
7756
- var init_atomic = __esm(() => {
7757
- _appendChains = new Map;
7758
- });
7759
-
7760
- // ../../loki-ts/src/util/engine_origin.ts
7761
- function readOriginUrl(repoDir) {
7762
- try {
7763
- const url = safeGit(repoDir, ["config", "--get", "remote.origin.url"]).trim();
7764
- return url || null;
7765
- } catch {
7766
- return null;
7767
- }
7768
- }
7769
- var init_engine_origin = __esm(() => {
7770
- init_atomic();
7771
- init_safe_git();
7772
- });
7773
-
7774
- // ../../loki-ts/src/e10ext/stop_restore.ts
7775
- var init_stop_restore = __esm(() => {
7776
- init_safe_git();
7777
- });
7778
-
7779
- // ../../loki-ts/src/util/base_guard.ts
7780
- var init_base_guard = __esm(() => {
7781
- init_run_status();
7782
- init_safe_git();
7783
- });
7784
-
7785
7597
  // ../../loki-ts/src/engine10/cache.ts
7786
- import { createHash as createHash7 } from "crypto";
7787
- import { existsSync as existsSync15, mkdirSync as mkdirSync7, readFileSync as readFileSync19, writeFileSync as writeFileSync10 } from "fs";
7598
+ import { createHash as createHash6 } from "crypto";
7599
+ import { existsSync as existsSync15, mkdirSync as mkdirSync5, readFileSync as readFileSync18, writeFileSync as writeFileSync8 } from "fs";
7788
7600
  import { resolve as resolve8 } from "path";
7789
7601
  function repoKey(originUrl, repoDir) {
7790
7602
  const basis = originUrl && originUrl.trim() !== "" ? originUrl : resolve8(repoDir);
7791
- return createHash7("sha256").update(basis).digest("hex");
7603
+ return createHash6("sha256").update(basis).digest("hex");
7792
7604
  }
7793
7605
  function defaultCacheRoot() {
7794
7606
  return resolve8(homeLokiDir(), "cache", "v10");
@@ -7800,7 +7612,7 @@ function readJson3(path, isValid = (_v) => true) {
7800
7612
  if (!existsSync15(path))
7801
7613
  return null;
7802
7614
  try {
7803
- const parsed = JSON.parse(readFileSync19(path, "utf8"));
7615
+ const parsed = JSON.parse(readFileSync18(path, "utf8"));
7804
7616
  return isValid(parsed) ? parsed : null;
7805
7617
  } catch {
7806
7618
  return null;
@@ -7924,12 +7736,12 @@ var init_lean_select = __esm(() => {
7924
7736
  });
7925
7737
 
7926
7738
  // ../../loki-ts/src/util/claude_model.ts
7927
- import { readFileSync as readFileSync20 } from "fs";
7739
+ import { readFileSync as readFileSync19 } from "fs";
7928
7740
  import { resolve as resolve9 } from "path";
7929
7741
  function resolveClaudeModel(want) {
7930
7742
  try {
7931
7743
  const path = process.env["LOKI_MODEL_CATALOG"] || resolve9(REPO_ROOT, "providers/model_catalog.json");
7932
- return JSON.parse(readFileSync20(path, "utf8")).providers?.claude?.cli_aliases?.[want] ?? want;
7744
+ return JSON.parse(readFileSync19(path, "utf8")).providers?.claude?.cli_aliases?.[want] ?? want;
7933
7745
  } catch {
7934
7746
  return want;
7935
7747
  }
@@ -7939,6 +7751,13 @@ var init_claude_model = __esm(() => {
7939
7751
  init_paths();
7940
7752
  FAMILY_ALIASES = new Set(["haiku", "sonnet", "opus", "fable"]);
7941
7753
  });
7754
+
7755
+ // ../../loki-ts/src/runner/watchdog.ts
7756
+ var warned;
7757
+ var init_watchdog = __esm(() => {
7758
+ warned = new Set;
7759
+ });
7760
+
7942
7761
  // ../../loki-ts/src/providers/price_table.ts
7943
7762
  var PRICE_TABLE;
7944
7763
  var init_price_table = __esm(() => {
@@ -7961,25 +7780,25 @@ var init_price_table = __esm(() => {
7961
7780
  });
7962
7781
 
7963
7782
  // ../../loki-ts/src/providers/provider_cost.ts
7964
- import { existsSync as existsSync16, mkdirSync as mkdirSync8, readdirSync as readdirSync10, readFileSync as readFileSync21, renameSync as renameSync6, writeFileSync as writeFileSync11 } from "fs";
7965
- import { dirname as dirname9, join as join28 } from "path";
7783
+ import { existsSync as existsSync16, mkdirSync as mkdirSync6, readdirSync as readdirSync9, readFileSync as readFileSync20, renameSync as renameSync4, writeFileSync as writeFileSync9 } from "fs";
7784
+ import { dirname as dirname7, join as join27 } from "path";
7966
7785
  function withArchivedCosts(lokiRoot, ids, keep = () => true) {
7967
7786
  let files = [];
7968
7787
  try {
7969
- files = readdirSync10(join28(lokiRoot, "metrics"));
7788
+ files = readdirSync9(join27(lokiRoot, "metrics"));
7970
7789
  } catch {}
7971
7790
  return [...new Set([...ids, ...ids.flatMap((id) => files.filter((f) => f.startsWith(`result-cost-${id}-q`) && f.endsWith(".json")).map((f) => f.slice("result-cost-".length, -".json".length)).filter(keep))])];
7972
7791
  }
7973
7792
  function neverRan(path, unmeteredMarker) {
7974
7793
  try {
7975
- const r = JSON.parse(readFileSync21(path, "utf8")), n2 = (v) => typeof v === "number" && Number.isFinite(v) ? v : 0;
7794
+ const r = JSON.parse(readFileSync20(path, "utf8")), n2 = (v) => typeof v === "number" && Number.isFinite(v) ? v : 0;
7976
7795
  return r["total_cost_usd"] === 0 && r["source"] !== unmeteredMarker && ["input_tokens", "output_tokens", "cache_read_tokens", "cache_creation_tokens"].every((k) => n2(r[k]) === 0);
7977
7796
  } catch {
7978
7797
  return false;
7979
7798
  }
7980
7799
  }
7981
7800
  function liveArchivedCosts(lokiRoot, ids, unmeteredMarker) {
7982
- return withArchivedCosts(lokiRoot, ids, (id) => !neverRan(join28(lokiRoot, "metrics", `result-cost-${id}.json`), unmeteredMarker));
7801
+ return withArchivedCosts(lokiRoot, ids, (id) => !neverRan(join27(lokiRoot, "metrics", `result-cost-${id}.json`), unmeteredMarker));
7983
7802
  }
7984
7803
  var ESTIMATED_PROVIDERS;
7985
7804
  var init_provider_cost = __esm(() => {
@@ -8031,8 +7850,8 @@ function routerMode(env = process.env) {
8031
7850
  var routerEnabled = (env = process.env) => routerMode(env) === "on";
8032
7851
 
8033
7852
  // ../../loki-ts/src/engine10/cost.ts
8034
- import { readdirSync as readdirSync11, readFileSync as readFileSync22 } from "fs";
8035
- import { join as join29 } from "path";
7853
+ import { readdirSync as readdirSync10, readFileSync as readFileSync21 } from "fs";
7854
+ import { join as join28 } from "path";
8036
7855
  function routerTelemetryOn(env = process.env) {
8037
7856
  return routerEnabled(env);
8038
7857
  }
@@ -8040,7 +7859,7 @@ function num3(v) {
8040
7859
  return typeof v === "number" && Number.isFinite(v) ? v : 0;
8041
7860
  }
8042
7861
  function resultCostPath(lokiRoot, iteration) {
8043
- return join29(lokiRoot, "metrics", `result-cost-${iteration}.json`);
7862
+ return join28(lokiRoot, "metrics", `result-cost-${iteration}.json`);
8044
7863
  }
8045
7864
  function sumResultCosts(lokiRoot, iterations, context = []) {
8046
7865
  const out2 = {
@@ -8071,7 +7890,7 @@ function sumResultCosts(lokiRoot, iterations, context = []) {
8071
7890
  const parsed = new Map;
8072
7891
  for (const iter of iterations) {
8073
7892
  try {
8074
- parsed.set(iter, JSON.parse(readFileSync22(resultCostPath(lokiRoot, iter), "utf8")));
7893
+ parsed.set(iter, JSON.parse(readFileSync21(resultCostPath(lokiRoot, iter), "utf8")));
8075
7894
  } catch {}
8076
7895
  }
8077
7896
  const own = new Set(iterations);
@@ -8080,7 +7899,7 @@ function sumResultCosts(lokiRoot, iterations, context = []) {
8080
7899
  if (own.has(iter))
8081
7900
  continue;
8082
7901
  try {
8083
- lookup.push({ iter, rec: JSON.parse(readFileSync22(resultCostPath(lokiRoot, iter), "utf8")) });
7902
+ lookup.push({ iter, rec: JSON.parse(readFileSync21(resultCostPath(lokiRoot, iter), "utf8")) });
8084
7903
  } catch {}
8085
7904
  }
8086
7905
  const verdicts = hooks.costRecords?.resumeVerdicts(lookup) ?? { ambiguous: [], separate: [] };
@@ -8181,28 +8000,88 @@ var init_cost = __esm(() => {
8181
8000
  init_provider_cost();
8182
8001
  });
8183
8002
 
8003
+ // ../../loki-ts/src/util/atomic.ts
8004
+ import {
8005
+ closeSync as closeSync5,
8006
+ fstatSync as fstatSync4,
8007
+ fsyncSync as fsyncSync2,
8008
+ lstatSync as lstatSync5,
8009
+ mkdirSync as mkdirSync8,
8010
+ openSync as openSync5,
8011
+ readSync as readSync4,
8012
+ renameSync as renameSync5,
8013
+ rmSync as rmSync5,
8014
+ statSync as statSync12,
8015
+ unlinkSync as unlinkSync3,
8016
+ writeFileSync as writeFileSync11,
8017
+ writeSync as writeSync3
8018
+ } from "fs";
8019
+ import { dirname as dirname8 } from "path";
8020
+ function durableWriteThenRename(target, body) {
8021
+ mkdirSync8(dirname8(target), { recursive: true });
8022
+ const tmp = tmpPathFor(target);
8023
+ const fd = openSync5(tmp, "w");
8024
+ try {
8025
+ writeSync3(fd, body);
8026
+ fsyncSync2(fd);
8027
+ } finally {
8028
+ closeSync5(fd);
8029
+ }
8030
+ renameSync5(tmp, target);
8031
+ fsyncParentDir(target);
8032
+ }
8033
+ function fsyncParentDir(target) {
8034
+ let dfd = null;
8035
+ try {
8036
+ dfd = openSync5(dirname8(target), "r");
8037
+ fsyncSync2(dfd);
8038
+ } catch {} finally {
8039
+ if (dfd !== null) {
8040
+ try {
8041
+ closeSync5(dfd);
8042
+ } catch {}
8043
+ }
8044
+ }
8045
+ }
8046
+ function atomicWriteJson(target, data) {
8047
+ durableWriteThenRename(target, `${JSON.stringify(data, null, 2)}
8048
+ `);
8049
+ }
8050
+ function tmpPathFor(target) {
8051
+ return `${target}.tmp.${process.pid}.${++_tmpCounter}`;
8052
+ }
8053
+ var _tmpCounter = 0, _appendChains;
8054
+ var init_atomic = __esm(() => {
8055
+ _appendChains = new Map;
8056
+ });
8057
+
8184
8058
  // ../../loki-ts/src/runner/budget.ts
8185
- import { existsSync as existsSync17, mkdirSync as mkdirSync10, readdirSync as readdirSync12, readFileSync as readFileSync23, writeFileSync as writeFileSync13 } from "fs";
8186
- import { dirname as dirname10, join as join30, resolve as resolve10 } from "path";
8059
+ import { existsSync as existsSync17, mkdirSync as mkdirSync9, readdirSync as readdirSync11, readFileSync as readFileSync22, writeFileSync as writeFileSync12 } from "fs";
8060
+ import { dirname as dirname9, join as join29, resolve as resolve10 } from "path";
8187
8061
  import { fileURLToPath as fileURLToPath2 } from "url";
8188
- function loadDefaultMaxCostUsd() {
8189
- const here = dirname10(fileURLToPath2(import.meta.url));
8190
- for (const rel of [["..", "..", "data"], ["..", "data"]]) {
8062
+ function loadDefaultMaxCostUsd(dataDirs = [["..", "..", "data"], ["..", "data"]]) {
8063
+ const here = dirname9(fileURLToPath2(import.meta.url));
8064
+ for (const rel of dataDirs) {
8191
8065
  try {
8192
- const j = JSON.parse(readFileSync23(resolve10(here, ...rel, "model-pricing.json"), "utf8"));
8066
+ const j = JSON.parse(readFileSync22(resolve10(here, ...rel, "model-pricing.json"), "utf8"));
8193
8067
  if (typeof j.default_max_cost_usd === "number" && Number.isFinite(j.default_max_cost_usd) && j.default_max_cost_usd > 0)
8194
8068
  return j.default_max_cost_usd;
8195
8069
  } catch {}
8196
8070
  }
8071
+ if (!pricingFallbackWarned) {
8072
+ pricingFallbackWarned = true;
8073
+ process.stderr.write(`loki: warning: model-pricing.json is missing or unreadable (looked in ${dataDirs.map((r) => resolve10(here, ...r)).join(", ")}); using fallback default max cost $${DEFAULT_FALLBACK_USD}
8074
+ `);
8075
+ }
8197
8076
  return DEFAULT_FALLBACK_USD;
8198
8077
  }
8199
8078
  function _loadPricing() {
8200
8079
  try {
8201
- const moduleDir = dirname10(fileURLToPath2(import.meta.url));
8080
+ const moduleDir = dirname9(fileURLToPath2(import.meta.url));
8202
8081
  const jsonPath = resolve10(moduleDir, "..", "..", "data", "model-pricing.json");
8203
8082
  if (!existsSync17(jsonPath))
8204
8083
  return _FALLBACK_PRICING;
8205
- const raw2 = JSON.parse(readFileSync23(jsonPath, "utf8"));
8084
+ const raw2 = JSON.parse(readFileSync22(jsonPath, "utf8"));
8206
8085
  const pricing2 = raw2["pricing"];
8207
8086
  if (!pricing2 || typeof pricing2 !== "object")
8208
8087
  return _FALLBACK_PRICING;
@@ -8267,7 +8146,7 @@ function calculateCostFromRecords(records) {
8267
8146
  }
8268
8147
  return round4(total);
8269
8148
  }
8270
- var DEFAULT_FALLBACK_USD = 100, _FALLBACK_PRICING, PRICING, DEFAULT_PRICING_KEY = "sonnet";
8149
+ var DEFAULT_FALLBACK_USD = 100, pricingFallbackWarned = false, _FALLBACK_PRICING, PRICING, DEFAULT_PRICING_KEY = "sonnet", warnedCorruptBudget;
8271
8150
  var init_budget = __esm(() => {
8272
8151
  init_atomic();
8273
8152
  init_paths();
@@ -8296,37 +8175,70 @@ var init_budget = __esm(() => {
8296
8175
  "gpt-5.3-codex": { input: 1.75, output: 14, cache_read: 0.175, cache_write: 2.1875 }
8297
8176
  };
8298
8177
  PRICING = Object.freeze(_loadPricing());
8178
+ warnedCorruptBudget = new Set;
8299
8179
  });
8300
8180
 
8301
- // ../../loki-ts/src/runner/router/decision.ts
8302
- var init_decision = () => {};
8303
-
8304
- // ../../loki-ts/src/project_model/schema.ts
8305
- import { existsSync as existsSync18, lstatSync as lstatSync6, realpathSync as realpathSync14, statSync as statSync14 } from "fs";
8306
- import { isAbsolute, join as join31, normalize, sep as sep9 } from "path";
8307
- function safeRel(p) {
8308
- const n2 = normalize(p.trim()).split(sep9).join("/").replace(/\/$/, "");
8309
- if (n2 === "" || isAbsolute(n2) || n2 === ".." || n2.startsWith("../"))
8310
- return null;
8311
- return n2;
8312
- }
8313
- function insideRepo(repoDir, rel) {
8314
- try {
8315
- const real2 = realpathSync14(join31(repoDir, rel));
8316
- const base = realpathSync14(repoDir);
8317
- return real2 === base || real2.startsWith(base + sep9);
8318
- } catch {
8319
- return false;
8181
+ // ../../loki-ts/src/engine10/types.ts
8182
+ function pushArgv(a) {
8183
+ switch (a.cmd) {
8184
+ case "push-pr":
8185
+ return ["push-pr", a.repoDir, a.branch, a.title, a.bodyFile, a.draft ? "1" : "0"];
8186
+ case "comment":
8187
+ return ["comment", a.runId, a.prUrl, a.file];
8188
+ case "status":
8189
+ return ["status", a.sha, a.state, a.description];
8190
+ case "issue-comment":
8191
+ return a.update ? ["issue-comment", "--update", a.issueRef, a.bodyFile] : ["issue-comment", a.issueRef, a.bodyFile];
8320
8192
  }
8321
8193
  }
8322
- function checkCitation(repoDir, raw2) {
8323
- if (!isStr(raw2))
8194
+ function taskBlock(task) {
8195
+ return ["Task (untrusted, quoted verbatim):", "<<<TASK", task, "TASK"];
8196
+ }
8197
+ var STAGE_BUDGETS;
8198
+ var init_types = __esm(() => {
8199
+ STAGE_BUDGETS = {
8200
+ intake: { targetS: 15, limitS: 60 },
8201
+ plan: { targetS: 45, limitS: 90 },
8202
+ wall: { targetS: 45, limitS: 90 },
8203
+ implement: { targetS: 180, limitS: 480 },
8204
+ verify: { targetS: 60, limitS: 120 },
8205
+ fix: { targetS: 90, limitS: 180 },
8206
+ commit: { targetS: 5, limitS: 30 },
8207
+ seal: { targetS: 15, limitS: 60 },
8208
+ pr: { targetS: 15, limitS: 60 },
8209
+ deep: { targetS: null, limitS: 2700 }
8210
+ };
8211
+ });
8212
+
8213
+ // ../../loki-ts/src/runner/router/decision.ts
8214
+ var init_decision = () => {};
8215
+
8216
+ // ../../loki-ts/src/project_model/schema.ts
8217
+ import { existsSync as existsSync18, lstatSync as lstatSync6, realpathSync as realpathSync14, statSync as statSync13 } from "fs";
8218
+ import { isAbsolute, join as join30, normalize, sep as sep9 } from "path";
8219
+ function safeRel(p) {
8220
+ const n2 = normalize(p.trim()).split(sep9).join("/").replace(/\/$/, "");
8221
+ if (n2 === "" || isAbsolute(n2) || n2 === ".." || n2.startsWith("../"))
8222
+ return null;
8223
+ return n2;
8224
+ }
8225
+ function insideRepo(repoDir, rel) {
8226
+ try {
8227
+ const real2 = realpathSync14(join30(repoDir, rel));
8228
+ const base = realpathSync14(repoDir);
8229
+ return real2 === base || real2.startsWith(base + sep9);
8230
+ } catch {
8231
+ return false;
8232
+ }
8233
+ }
8234
+ function checkCitation(repoDir, raw2) {
8235
+ if (!isStr(raw2))
8324
8236
  return "citation is not a non-empty string";
8325
8237
  const rel = safeRel(raw2.replace(/:\d+(?:-\d+)?$/, ""));
8326
8238
  if (rel === null)
8327
8239
  return `citation "${raw2}" is not a repo-relative path`;
8328
- const abs = join31(repoDir, rel);
8329
- if (!existsSync18(abs) || !statSync14(abs).isFile() || !insideRepo(repoDir, rel))
8240
+ const abs = join30(repoDir, rel);
8241
+ if (!existsSync18(abs) || !statSync13(abs).isFile() || !insideRepo(repoDir, rel))
8330
8242
  return `citation "${raw2}" names a file that does not exist`;
8331
8243
  return null;
8332
8244
  }
@@ -8337,7 +8249,7 @@ function checkFingerprint(repoDir, raw2) {
8337
8249
  if (rel === null)
8338
8250
  return `fingerprint "${raw2}" is not a repo-relative path`;
8339
8251
  try {
8340
- if (!lstatSync6(join31(repoDir, rel)).isFile() || !insideRepo(repoDir, rel))
8252
+ if (!lstatSync6(join30(repoDir, rel)).isFile() || !insideRepo(repoDir, rel))
8341
8253
  return `fingerprint "${raw2}" is not a regular file inside the repo`;
8342
8254
  } catch {
8343
8255
  return `fingerprint "${raw2}" names a file that does not exist`;
@@ -8365,8 +8277,8 @@ function checkDir(repoDir, v, where, errs) {
8365
8277
  errs.push(`${where}: must be a repo-relative directory ("." for the repo root)`);
8366
8278
  return ".";
8367
8279
  }
8368
- const abs = join31(repoDir, rel);
8369
- if (!existsSync18(abs) || !statSync14(abs).isDirectory() || !insideRepo(repoDir, rel))
8280
+ const abs = join30(repoDir, rel);
8281
+ if (!existsSync18(abs) || !statSync13(abs).isDirectory() || !insideRepo(repoDir, rel))
8370
8282
  errs.push(`${where}: directory "${rel}" does not exist`);
8371
8283
  return rel;
8372
8284
  }
@@ -8468,7 +8380,7 @@ var init_schema = __esm(() => {
8468
8380
  });
8469
8381
 
8470
8382
  // ../../loki-ts/src/runner/router/history_store.ts
8471
- import { existsSync as existsSync19, mkdirSync as mkdirSync11, readFileSync as readFileSync24, renameSync as renameSync7, rmSync as rmSync6, writeFileSync as writeFileSync14 } from "fs";
8383
+ import { existsSync as existsSync19, mkdirSync as mkdirSync10, readFileSync as readFileSync23, renameSync as renameSync6, rmSync as rmSync6, writeFileSync as writeFileSync13 } from "fs";
8472
8384
  import { resolve as resolve11 } from "path";
8473
8385
  function validRun(v) {
8474
8386
  if (typeof v !== "object" || v === null)
@@ -8484,7 +8396,7 @@ function readRunHistory(key, cacheRoot) {
8484
8396
  if (!existsSync19(path))
8485
8397
  return [];
8486
8398
  try {
8487
- const parsed = JSON.parse(readFileSync24(path, "utf8"));
8399
+ const parsed = JSON.parse(readFileSync23(path, "utf8"));
8488
8400
  const runs2 = parsed?.runs;
8489
8401
  return Array.isArray(runs2) ? runs2.filter(validRun) : [];
8490
8402
  } catch {
@@ -8494,12 +8406,12 @@ function readRunHistory(key, cacheRoot) {
8494
8406
  function appendRunOutcome(key, outcome, cacheRoot) {
8495
8407
  try {
8496
8408
  const runs2 = [...readRunHistory(key, cacheRoot), outcome].slice(-HISTORY_CAP);
8497
- mkdirSync11(repoCacheDir(key, cacheRoot), { recursive: true });
8409
+ mkdirSync10(repoCacheDir(key, cacheRoot), { recursive: true });
8498
8410
  const target = historyPath(key, cacheRoot);
8499
8411
  const tmp = `${target}.${process.pid}.tmp`;
8500
8412
  try {
8501
- writeFileSync14(tmp, JSON.stringify({ runs: runs2 }));
8502
- renameSync7(tmp, target);
8413
+ writeFileSync13(tmp, JSON.stringify({ runs: runs2 }));
8414
+ renameSync6(tmp, target);
8503
8415
  } catch (e) {
8504
8416
  rmSync6(tmp, { force: true, recursive: false });
8505
8417
  throw e;
@@ -8517,18 +8429,10 @@ var init_history_store = __esm(() => {
8517
8429
  OWNERS = ["code", "harness", "env", "provider"];
8518
8430
  });
8519
8431
 
8520
- // ../../loki-ts/src/runner/model_downgrades.ts
8521
- var init_model_downgrades = __esm(() => {
8522
- init_sizing();
8523
- init_model_rank();
8524
- init_claude_model();
8525
- init_decision();
8526
- });
8527
-
8528
8432
  // ../../loki-ts/src/project_model/gather.ts
8529
- import { createHash as createHash8 } from "crypto";
8530
- import { closeSync as closeSync6, lstatSync as lstatSync7, openSync as openSync6, readFileSync as readFileSync25, readSync as readSync5 } from "fs";
8531
- import { dirname as dirname11, join as join32 } from "path";
8433
+ import { createHash as createHash7 } from "crypto";
8434
+ import { closeSync as closeSync6, lstatSync as lstatSync7, openSync as openSync6, readFileSync as readFileSync24, readSync as readSync5 } from "fs";
8435
+ import { dirname as dirname10, join as join31 } from "path";
8532
8436
  function listShallow(repoDir) {
8533
8437
  try {
8534
8438
  const out2 = safeGit(repoDir, ["ls-files", "-z"], { maxBuffer: 256 * 1024 * 1024 });
@@ -8538,10 +8442,10 @@ function listShallow(repoDir) {
8538
8442
  }
8539
8443
  }
8540
8444
  function shallowDirs(repoDir) {
8541
- return [...new Set(listShallow(repoDir).map((p) => dirname11(p)))].sort();
8445
+ return [...new Set(listShallow(repoDir).map((p) => dirname10(p)))].sort();
8542
8446
  }
8543
8447
  function computeKey(repoDir, fingerprintFiles, dirs, committedHash = null) {
8544
- const h = createHash8("sha256");
8448
+ const h = createHash7("sha256");
8545
8449
  h.update(`${MODEL_REV}
8546
8450
  `);
8547
8451
  if (committedHash !== null)
@@ -8554,9 +8458,9 @@ function computeKey(repoDir, fingerprintFiles, dirs, committedHash = null) {
8554
8458
  if (checkFingerprint(repoDir, f) !== null)
8555
8459
  h.update("invalid");
8556
8460
  else {
8557
- const abs = join32(repoDir, f);
8461
+ const abs = join31(repoDir, f);
8558
8462
  const size = lstatSync7(abs).size;
8559
- h.update(size <= GATHER_CAPS.maxHashBytes ? readFileSync25(abs) : `too-large:${size}`);
8463
+ h.update(size <= GATHER_CAPS.maxHashBytes ? readFileSync24(abs) : `too-large:${size}`);
8560
8464
  }
8561
8465
  } catch {
8562
8466
  h.update("missing");
@@ -8581,7 +8485,7 @@ function committedModelHash(raw2) {
8581
8485
  if (typeof raw2 !== "object" || raw2 === null || Array.isArray(raw2))
8582
8486
  return null;
8583
8487
  const { key: _key, expiresAt: _exp, ...rest } = raw2;
8584
- return createHash8("sha256").update(JSON.stringify(rest)).digest("hex");
8488
+ return createHash7("sha256").update(JSON.stringify(rest)).digest("hex");
8585
8489
  }
8586
8490
  var GATHER_CAPS, depthOf = (p) => p.split("/").length, MODEL_REV = "model-rev:2";
8587
8491
  var init_gather = __esm(() => {
@@ -8598,55 +8502,22 @@ var init_gather = __esm(() => {
8598
8502
  };
8599
8503
  });
8600
8504
 
8601
- // ../../loki-ts/src/project_model/discover.ts
8602
- import { existsSync as existsSync20, mkdirSync as mkdirSync12, readFileSync as readFileSync26, rmSync as rmSync7, writeFileSync as writeFileSync15 } from "fs";
8603
- import { join as join33 } from "path";
8604
- function projectModelEnabled(env = process.env) {
8605
- return env["LOKI_E10_PROJECT_MODEL"] !== "0";
8606
- }
8607
- function loadCached(repoDir, now2 = Date.now()) {
8505
+ // ../../loki-ts/src/util/engine_origin.ts
8506
+ function readOriginUrl(repoDir) {
8608
8507
  try {
8609
- return parseCached(repoDir, JSON.parse(readFileSync26(join33(repoDir, PROJECT_FILE), "utf8")), now2);
8508
+ const url = safeGit(repoDir, ["config", "--get", "remote.origin.url"]).trim();
8509
+ return url || null;
8610
8510
  } catch {
8611
8511
  return null;
8612
8512
  }
8613
8513
  }
8614
- function loadCommitted(repoDir) {
8615
- if (!isGitTracked(repoDir, PROJECT_FILE))
8616
- return null;
8617
- const ignore = (why) => {
8618
- process.stderr.write(`loki: ignoring committed ${PROJECT_FILE}: ${why}; falling back to discovery
8619
- `);
8620
- return null;
8621
- };
8622
- let raw2;
8623
- try {
8624
- raw2 = JSON.parse(readFileSync26(join33(repoDir, PROJECT_FILE), "utf8"));
8625
- } catch (e) {
8626
- return ignore(`unreadable JSON (${e instanceof Error ? e.message : String(e)})`);
8627
- }
8628
- const hash = committedModelHash(raw2);
8629
- if (hash === null)
8630
- return ignore("not a JSON object");
8631
- const r = raw2;
8632
- if (r["schema"] !== PROJECT_MODEL_SCHEMA || r["status"] !== "ok")
8633
- return ignore(`needs schema "${PROJECT_MODEL_SCHEMA}" and status "ok"`);
8634
- const v = validateAnswer(repoDir, raw2);
8635
- if (!v.ok)
8636
- return ignore(v.errors.slice(0, 3).join("; "));
8637
- return { model: { ...v.model, key: "" }, hash };
8638
- }
8639
- var PROJECT_FILE = ".loki/project.json", UNKNOWN_TTL_MS;
8640
- var init_discover2 = __esm(() => {
8641
- init_model_downgrades();
8642
- init_types();
8643
- init_gather();
8644
- init_schema();
8645
- UNKNOWN_TTL_MS = 60 * 60 * 1000;
8514
+ var init_engine_origin = __esm(() => {
8515
+ init_atomic();
8516
+ init_safe_git();
8646
8517
  });
8647
8518
 
8648
8519
  // ../../loki-ts/src/runner/router/history.ts
8649
- import { dirname as dirname12, join as join34, resolve as resolve12 } from "path";
8520
+ import { dirname as dirname11, join as join32, resolve as resolve12 } from "path";
8650
8521
  import { fileURLToPath as fileURLToPath3 } from "url";
8651
8522
  function shapeKey(model) {
8652
8523
  if (!model || model.status !== "ok")
@@ -8678,7 +8549,7 @@ var init_history = __esm(() => {
8678
8549
  init_cache();
8679
8550
  init_engine_origin();
8680
8551
  init_history_store();
8681
- SHAPE_DEFAULTS_FILE = resolve12(dirname12(fileURLToPath3(import.meta.url)), "..", "..", "..", "data", "router-shape-defaults.json");
8552
+ SHAPE_DEFAULTS_FILE = resolve12(dirname11(fileURLToPath3(import.meta.url)), "..", "..", "..", "data", "router-shape-defaults.json");
8682
8553
  });
8683
8554
 
8684
8555
  // ../../loki-ts/src/runner/router/unit_model.ts
@@ -8740,6 +8611,7 @@ var init_budget_cap = __esm(() => {
8740
8611
  // ../../loki-ts/src/engine10/session.ts
8741
8612
  var STDERR_TAIL_BYTES, ADVISOR_MARKED_STAGES;
8742
8613
  var init_session = __esm(() => {
8614
+ init_watchdog();
8743
8615
  init_run_pid();
8744
8616
  init_provider_cost();
8745
8617
  init_model_routes();
@@ -8764,7 +8636,7 @@ var init_model_rank = __esm(() => {
8764
8636
  });
8765
8637
 
8766
8638
  // ../../loki-ts/src/engine10/sizing.ts
8767
- import { existsSync as existsSync21, readFileSync as readFileSync27 } from "fs";
8639
+ import { existsSync as existsSync20, readFileSync as readFileSync25 } from "fs";
8768
8640
  import { basename as basename8 } from "path";
8769
8641
  function namedFiles(task, map) {
8770
8642
  return (map?.files ?? []).filter((f) => task.toLowerCase().includes(basename8(f).toLowerCase()));
@@ -8786,7 +8658,7 @@ function sizeTask(task, map, tests) {
8786
8658
  }
8787
8659
  function loadRepoMap(repomapRef) {
8788
8660
  try {
8789
- return repomapRef && existsSync21(repomapRef) ? JSON.parse(readFileSync27(repomapRef, "utf8")) : null;
8661
+ return repomapRef && existsSync20(repomapRef) ? JSON.parse(readFileSync25(repomapRef, "utf8")) : null;
8790
8662
  } catch {
8791
8663
  return null;
8792
8664
  }
@@ -8820,1536 +8692,1690 @@ var init_sizing = __esm(() => {
8820
8692
  resolveModelAlias = resolveClaudeModel;
8821
8693
  });
8822
8694
 
8823
- // ../../loki-ts/src/features/speed/unit_mode.ts
8824
- import { closeSync as closeSync7, constants, fstatSync as fstatSync5, lstatSync as lstatSync8, openSync as openSync7, readSync as readSync6, realpathSync as realpathSync15 } from "fs";
8825
- function readBounded(path) {
8826
- const l = lstatSync8(path);
8827
- if (l.isSymbolicLink() || !l.isFile() || l.size > MAX_SPEC_BYTES)
8828
- return null;
8829
- const fd = openSync7(path, constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK);
8695
+ // ../../loki-ts/src/runner/model_downgrades.ts
8696
+ var init_model_downgrades = __esm(() => {
8697
+ init_sizing();
8698
+ init_model_rank();
8699
+ init_claude_model();
8700
+ init_decision();
8701
+ });
8702
+
8703
+ // ../../loki-ts/src/project_model/discover.ts
8704
+ import { existsSync as existsSync21, mkdirSync as mkdirSync11, readFileSync as readFileSync26, rmSync as rmSync7, writeFileSync as writeFileSync14 } from "fs";
8705
+ import { join as join34 } from "path";
8706
+ function projectModelEnabled(env = process.env) {
8707
+ return env["LOKI_E10_PROJECT_MODEL"] !== "0";
8708
+ }
8709
+ function loadCached(repoDir, now2 = Date.now()) {
8830
8710
  try {
8831
- const st = fstatSync5(fd);
8832
- if (!st.isFile() || st.size > MAX_SPEC_BYTES)
8833
- return null;
8834
- const buf = Buffer.alloc(st.size + 1);
8835
- let n2 = 0;
8836
- for (;; ) {
8837
- const r = readSync6(fd, buf, n2, buf.length - n2, null);
8838
- if (r <= 0)
8839
- break;
8840
- n2 += r;
8841
- if (n2 >= buf.length)
8842
- return null;
8843
- }
8844
- return buf.subarray(0, n2).toString("utf8");
8845
- } finally {
8846
- closeSync7(fd);
8711
+ return parseCached(repoDir, JSON.parse(readFileSync26(join34(repoDir, PROJECT_FILE), "utf8")), now2);
8712
+ } catch {
8713
+ return null;
8847
8714
  }
8848
8715
  }
8849
- function parseText(txt) {
8716
+ function loadCommitted(repoDir) {
8717
+ if (!isGitTracked(repoDir, PROJECT_FILE))
8718
+ return null;
8719
+ const ignore = (why) => {
8720
+ process.stderr.write(`loki: ignoring committed ${PROJECT_FILE}: ${why}; falling back to discovery
8721
+ `);
8722
+ return null;
8723
+ };
8724
+ let raw2;
8850
8725
  try {
8851
- const j = JSON.parse(txt), ws = strs(j.writeSet), pack = strs(j.pack), tb = j.tokenBudget;
8852
- if (typeof j.id !== "string" || !j.id || !ws || ws.length === 0 || !pack || typeof tb !== "number" || !Number.isFinite(tb) || !(tb > 0) || tb > MAX_TOKEN_BUDGET)
8726
+ raw2 = JSON.parse(readFileSync26(join34(repoDir, PROJECT_FILE), "utf8"));
8727
+ } catch (e) {
8728
+ return ignore(`unreadable JSON (${e instanceof Error ? e.message : String(e)})`);
8729
+ }
8730
+ const hash = committedModelHash(raw2);
8731
+ if (hash === null)
8732
+ return ignore("not a JSON object");
8733
+ const r = raw2;
8734
+ if (r["schema"] !== PROJECT_MODEL_SCHEMA || r["status"] !== "ok")
8735
+ return ignore(`needs schema "${PROJECT_MODEL_SCHEMA}" and status "ok"`);
8736
+ const v = validateAnswer(repoDir, raw2);
8737
+ if (!v.ok)
8738
+ return ignore(v.errors.slice(0, 3).join("; "));
8739
+ return { model: { ...v.model, key: "" }, hash };
8740
+ }
8741
+ var PROJECT_FILE = ".loki/project.json", UNKNOWN_TTL_MS;
8742
+ var init_discover2 = __esm(() => {
8743
+ init_model_downgrades();
8744
+ init_types();
8745
+ init_gather();
8746
+ init_schema();
8747
+ UNKNOWN_TTL_MS = 60 * 60 * 1000;
8748
+ });
8749
+
8750
+ // ../../loki-ts/src/project_model/api.ts
8751
+ import { posix } from "path";
8752
+ function projectApi(model) {
8753
+ const known = isRec2(model) && model.status === "ok";
8754
+ const packages = known && Array.isArray(model.packages) ? model.packages.filter(wellFormed) : [];
8755
+ const packageOf = (file) => {
8756
+ const f = clean(file);
8757
+ if (f === "")
8853
8758
  return null;
8854
- const acc = j.acceptance === undefined ? [] : strs(j.acceptance);
8855
- if (acc === null)
8759
+ let best = null;
8760
+ for (const p of packages) {
8761
+ const hit = p.root === "." || f === p.root || f.startsWith(`${p.root}/`);
8762
+ if (hit && (best === null || p.root !== "." && (best.root === "." || p.root.length > best.root.length)))
8763
+ best = p;
8764
+ }
8765
+ return best;
8766
+ };
8767
+ return {
8768
+ model,
8769
+ known: () => known,
8770
+ packages: () => packages,
8771
+ workspaceKind: () => isRec2(model) && typeof model.workspaceKind === "string" ? model.workspaceKind : "unknown",
8772
+ packageRootOf: (file) => packageOf(file)?.root ?? null,
8773
+ packageOf,
8774
+ relativeToRoot: (file) => {
8775
+ const p = packageOf(file);
8776
+ return p ? p.root === "." ? clean(file) : clean(file).slice(p.root.length + 1) : null;
8777
+ },
8778
+ commandFor: (pkg, kind) => (packages.find((p) => p.root === clean(pkg) || p.name === pkg) ?? null)?.commands?.[kind] ?? null,
8779
+ dependsOn: (pkg) => {
8780
+ const d = (packages.find((p) => p.root === clean(pkg) || p.name === pkg) ?? null)?.dependsOn;
8781
+ return Array.isArray(d) ? d.filter((x) => typeof x === "string") : null;
8782
+ },
8783
+ installFor: (pkg) => {
8784
+ const i = (packages.find((p) => p.root === clean(pkg) || p.name === pkg) ?? null)?.install;
8785
+ return isRec2(i) && typeof i.cmd === "string" ? i : null;
8786
+ },
8787
+ hasUI: () => packages.some((p) => isRec2(p.ui) && p.ui.present === true),
8788
+ uiBoot: () => {
8789
+ for (const p of packages)
8790
+ if (isRec2(p.ui) && p.ui.present === true && isRec2(p.ui.boot))
8791
+ return { pkg: p, boot: p.ui.boot };
8856
8792
  return null;
8857
- return { id: j.id, writeSet: ws, pack, tokenBudget: tb, ...acc.length ? { acceptance: acc.map((a) => a.slice(0, 500)).slice(0, 50) } : {} };
8858
- } catch {
8859
- return null;
8860
- }
8793
+ }
8794
+ };
8861
8795
  }
8862
- function parseSpec(path) {
8796
+ var clean = (p) => typeof p === "string" ? posix.normalize(p.replace(/\\/g, "/")).replace(/^\.\//, "") : "", isRec2 = (v) => typeof v === "object" && v !== null, wellFormed = (p) => isRec2(p) && typeof p.root === "string" && typeof p.name === "string";
8797
+ var init_api = () => {};
8798
+
8799
+ // ../../loki-ts/src/project_model/resolve.ts
8800
+ import { join as join35 } from "path";
8801
+ function loadProjectApi(repoDir, env = process.env) {
8802
+ if (!projectModelEnabled(env))
8803
+ return null;
8863
8804
  try {
8864
- const txt = readBounded(path);
8865
- return txt === null ? null : parseText(txt);
8805
+ const model = loadCached(repoDir);
8806
+ const api = model ? projectApi(model) : null;
8807
+ return api && api.known() ? api : null;
8866
8808
  } catch {
8867
8809
  return null;
8868
8810
  }
8869
8811
  }
8870
- function unitSpec(env = process.env) {
8871
- const path = env["LOKI_UNIT_SPEC"], frozen = env["LOKI_UNIT_SPEC_FROZEN"];
8872
- if (env["LOKI_SPEED"] === "0" || !path)
8812
+ function isMultiRoot(api) {
8813
+ return !!api && api.known() && api.packages().some((p) => p.root !== ".");
8814
+ }
8815
+ function siteFor(api, repoDir, file) {
8816
+ if (!isMultiRoot(api))
8873
8817
  return null;
8874
- if (frozen !== undefined)
8875
- return frozen.length <= MAX_SPEC_BYTES ? parseText(frozen) : null;
8876
- if (!memo.has(path))
8877
- memo.set(path, parseSpec(path));
8878
- return memo.get(path) ?? null;
8818
+ const pkg = api.packageOf(file);
8819
+ const rel = api.relativeToRoot(file);
8820
+ if (!pkg || pkg.root === "." || rel === null)
8821
+ return null;
8822
+ return { pkg, root: pkg.root, cwd: join35(repoDir, pkg.root), file: rel };
8879
8823
  }
8880
- function unitFlags(staged, env = process.env, pre = {}) {
8881
- const s = unitSpec(env);
8882
- return s ? staged.filter(({ f }) => !inWriteSet(s, f) && !Object.hasOwn(pre, f)).map(({ f }) => f) : null;
8824
+ function groupByPackage(api, repoDir, files) {
8825
+ const out2 = new Map;
8826
+ for (const f of files) {
8827
+ const s = siteFor(api, repoDir, f);
8828
+ const root = s?.root ?? ".";
8829
+ const g = out2.get(root) ?? { root, cwd: s?.cwd ?? repoDir, files: [], rel: [] };
8830
+ g.files.push(f);
8831
+ g.rel.push(s?.file ?? f);
8832
+ out2.set(root, g);
8833
+ }
8834
+ return [...out2.values()];
8883
8835
  }
8884
- var MAX_SPEC_BYTES, MAX_TOKEN_BUDGET = 1e9, norm2 = (p) => p.replace(/^\.\//, ""), CTRL, strs = (v) => Array.isArray(v) && v.every((x) => typeof x === "string" && !CTRL.test(x)) ? v.map(norm2) : null, memo, inWriteSet = (spec, f) => spec.writeSet.some((w) => w.endsWith("/") ? f.startsWith(w) : f === w);
8885
- var init_unit_mode = __esm(() => {
8886
- MAX_SPEC_BYTES = 1024 * 1024;
8887
- CTRL = /[\x00-\x1f\x7f]/;
8888
- memo = new Map;
8836
+ var init_resolve = __esm(() => {
8837
+ init_discover2();
8838
+ init_api();
8889
8839
  });
8890
-
8891
- // ../../loki-ts/src/util/ts_signatures.ts
8892
- function templateEnd(s, i) {
8893
- for (let j = i + 1;j < s.length; ) {
8894
- const c = s[j];
8895
- if (c === "\\")
8896
- j += 2;
8897
- else if (c === "`")
8898
- return j + 1;
8899
- else if (c === "$" && s[j + 1] === "{") {
8900
- let depth = 1;
8901
- j += 2;
8902
- while (j < s.length && depth > 0) {
8903
- const k = skipLiteral(s, j);
8904
- if (k !== j) {
8905
- j = k;
8906
- continue;
8907
- }
8908
- if (s[j] === "{")
8909
- depth++;
8910
- else if (s[j] === "}")
8911
- depth--;
8912
- j++;
8913
- }
8914
- } else
8915
- j++;
8916
- }
8917
- return s.length;
8840
+ // ../../loki-ts/src/project_model/scope.ts
8841
+ function scopedOutOf(map) {
8842
+ return [...dropped.get(map) ?? []].sort();
8918
8843
  }
8919
- function commentBefore(s, i) {
8920
- let p = i - 1;
8921
- while (p >= 0 && /\s/.test(s[p]))
8922
- p--;
8923
- if (p < 1)
8924
- return false;
8925
- if (s[p] === "/" && s[p - 1] === "*")
8926
- return true;
8927
- return s.slice(s.lastIndexOf(`
8928
- `, p) + 1, p + 1).includes("//");
8844
+ var apis, imports, dropped;
8845
+ var init_scope = __esm(() => {
8846
+ init_resolve();
8847
+ apis = new WeakMap;
8848
+ imports = new WeakMap;
8849
+ dropped = new WeakMap;
8850
+ });
8851
+
8852
+ // ../../loki-ts/src/util/test_file_text.ts
8853
+ import { basename as basename9 } from "path";
8854
+ function isTestFile(rel) {
8855
+ const name = basename9(rel);
8856
+ return JS_TEST_RE.test(name) || PY_TEST_RE.test(name) || GO_TEST_RE.test(name);
8929
8857
  }
8930
- function regexEnd(s, i) {
8931
- if (commentBefore(s, i))
8932
- return i;
8933
- let p = i - 1;
8934
- while (p >= 0 && /\s/.test(s[p]))
8935
- p--;
8936
- if (p >= 1 && (s[p] === "+" || s[p] === "-") && s[p - 1] === s[p])
8937
- return i;
8938
- if (p >= 0 && !REGEX_PREV.includes(s[p]) && !/\b(return|typeof|case|in|of|void|delete|throw)$/.test(s.slice(Math.max(0, p - 7), p + 1)))
8939
- return i;
8940
- let inClass = false;
8941
- for (let j = i + 1;j < s.length; j++) {
8942
- const c = s[j];
8943
- if (c === `
8944
- `)
8945
- return i;
8946
- if (c === "\\")
8947
- j++;
8948
- else if (c === "[")
8949
- inClass = true;
8950
- else if (c === "]")
8951
- inClass = false;
8952
- else if (c === "/" && !inClass) {
8953
- j++;
8954
- while (j < s.length && /[a-z]/i.test(s[j]))
8955
- j++;
8956
- return j;
8858
+ var JS_TEST_RE, PY_TEST_RE, GO_TEST_RE;
8859
+ var init_test_file_text = __esm(() => {
8860
+ JS_TEST_RE = /\.(test|spec)\.[cm]?[jt]sx?$/;
8861
+ PY_TEST_RE = /^(test_.+|.+_test)\.py$/;
8862
+ GO_TEST_RE = /_test\.go$/;
8863
+ });
8864
+
8865
+ // ../../loki-ts/src/engine10/testmap.ts
8866
+ import { existsSync as existsSync22, readdirSync as readdirSync12, readFileSync as readFileSync27, realpathSync as realpathSync15 } from "fs";
8867
+ import { basename as basename10, extname, isAbsolute as isAbsolute2, join as join36, relative, sep as sep10 } from "path";
8868
+ function projectInterpreter(root) {
8869
+ const ve = process.env["VIRTUAL_ENV"], inRepo = (p) => {
8870
+ try {
8871
+ const r = relative(realpathSync15(root), realpathSync15(p));
8872
+ return r !== "" && !r.startsWith("..") && !isAbsolute2(r);
8873
+ } catch {
8874
+ return false;
8957
8875
  }
8958
- }
8959
- return i;
8876
+ };
8877
+ return [join36(root, ".venv", "bin", "python"), join36(root, "venv", "bin", "python"), ...ve && inRepo(ve) ? [join36(ve, "bin", "python")] : []].some(existsSync22) ? "project" : "system";
8960
8878
  }
8961
- function stripComments(t) {
8962
- let out2 = "";
8963
- for (let i = 0;i < t.length; ) {
8964
- if (isComment(t, i)) {
8965
- const e = t[i + 1] === "/" ? t.indexOf(`
8966
- `, i) < 0 ? t.length : t.indexOf(`
8967
- `, i) : t.indexOf("*/", i + 2) < 0 ? t.length : t.indexOf("*/", i + 2) + 2;
8968
- out2 += t[i + 1] === "/" ? "" : " ";
8969
- i = e;
8970
- continue;
8971
- }
8972
- const c = t[i];
8973
- const k = c === '"' || c === "'" || c === "`" ? skipLiteral(t, i) : i;
8974
- if (k !== i) {
8975
- out2 += t.slice(i, k);
8976
- i = k;
8977
- continue;
8978
- }
8979
- out2 += c;
8980
- i++;
8981
- }
8982
- return out2;
8879
+ var SKIP_DIRS;
8880
+ var init_testmap = __esm(() => {
8881
+ init_resolve();
8882
+ init_scope();
8883
+ init_test_file_text();
8884
+ SKIP_DIRS = new Set([
8885
+ ".git",
8886
+ "node_modules",
8887
+ "dist",
8888
+ "build",
8889
+ "target",
8890
+ "coverage",
8891
+ ".venv",
8892
+ "venv",
8893
+ "__pycache__",
8894
+ ".tox",
8895
+ ".pytest_cache",
8896
+ ".loki",
8897
+ ".next"
8898
+ ]);
8899
+ });
8900
+
8901
+ // ../../loki-ts/src/util/check_result.ts
8902
+ import { existsSync as existsSync23, mkdirSync as mkdirSync12, readFileSync as readFileSync28, realpathSync as realpathSync16, renameSync as renameSync7, writeFileSync as writeFileSync15 } from "fs";
8903
+ import { basename as basename11, isAbsolute as isAbsolute3, join as join37 } from "path";
8904
+ function plainTestEnv(base = process.env) {
8905
+ const e = {};
8906
+ for (const [k, v] of Object.entries(tokenFreeEnv(base)))
8907
+ if (typeof v === "string")
8908
+ e[k] = v;
8909
+ e["FORCE_COLOR"] = "0";
8910
+ e["NO_COLOR"] = "1";
8911
+ delete e["CLICOLOR_FORCE"];
8912
+ return e;
8983
8913
  }
8984
- function skipLiteral(s, i) {
8985
- const c = s[i];
8986
- if (c === "/" && s[i + 1] === "/") {
8987
- const n2 = s.indexOf(`
8988
- `, i);
8989
- return n2 < 0 ? s.length : n2;
8914
+ function ran(raw2, path, ok) {
8915
+ const out2 = stripAnsi(raw2);
8916
+ const n2 = (s, re) => +(s.match(re)?.[1] ?? 0);
8917
+ const blk = out2.trimEnd().match(/(?:^|\n)((?:(?:#|\u2139) \w+ [\d.]+(?:\n|$)){5,})$/)?.[1];
8918
+ if (blk) {
8919
+ const c = n2(blk, /(?:#|\u2139) pass (\d+)/) + n2(blk, /(?:#|\u2139) fail (\d+)/);
8920
+ const nm = out2.match(/^(?:ok \d+ - |\u2714 )(\S+\.[cm]?[jt]s)(?: \(|$)/m)?.[1];
8921
+ return c === 1 && nm && (!path || basename11(nm) === basename11(path)) ? 0 : c;
8990
8922
  }
8991
- if (c === "/" && s[i + 1] === "*") {
8992
- const n2 = s.indexOf("*/", i + 2);
8993
- return n2 < 0 ? s.length : n2 + 2;
8923
+ const cg = cargoTrailer(out2);
8924
+ if (cg.length)
8925
+ return cg.reduce((t, l2) => t + n2(l2, /(\d+) passed/) + n2(l2, /(\d+) failed/), 0);
8926
+ const g = goCount(out2, ok);
8927
+ if (g !== undefined)
8928
+ return g;
8929
+ const tail = tailLines(out2);
8930
+ const l = tail.filter((x) => /^(?:=+ )?(?:\d+ \w+.*|no tests ran) in [\d.]+s|^\s*Tests?:?\s+\d|^No tests found/.test(x) && (!/^\s*Tests?:?\s+\d/.test(x) || tail.some((y) => /^\s*Test (?:Files|Suites):?\s+\d/.test(y)))).pop();
8931
+ if (!l)
8932
+ return null;
8933
+ return /^(?:=+ )?no tests (?:ran|found)|^No tests found|skipped/i.test(l) || /\d+ (?:passed|failed|errors?)/.test(l) ? n2(l, /(\d+) passed/) + n2(l, /(\d+) failed/) + n2(l, /(\d+) errors?/) : null;
8934
+ }
8935
+ function cargoTrailer(out2) {
8936
+ const ls = out2.split(`
8937
+ `).filter((x) => x.trim());
8938
+ while (ls.length && /^error: /.test(ls[ls.length - 1]))
8939
+ ls.pop();
8940
+ const segs = [];
8941
+ for (const l of ls) {
8942
+ if (/^running \d+ tests?\b/.test(l))
8943
+ segs.push([]);
8944
+ else if (segs.length && !/^\s*(?:Running |Doc-tests |Doctest)/.test(l))
8945
+ segs[segs.length - 1].push(l);
8994
8946
  }
8995
- if (c === "/")
8996
- return regexEnd(s, i);
8997
- if (c === "`")
8998
- return templateEnd(s, i);
8999
- if (c !== '"' && c !== "'")
9000
- return i;
9001
- for (let j = i + 1;j < s.length; j++) {
9002
- if (s[j] === "\\")
9003
- j++;
9004
- else if (s[j] === c)
9005
- return j + 1;
9006
- else if (s[j] === `
9007
- `)
9008
- return i;
8947
+ const lastOf = (g) => g[g.length - 1]?.startsWith("test result: ") ? g[g.length - 1] : undefined;
8948
+ if (!segs.length) {
8949
+ const g = [];
8950
+ for (let k = ls.length - 1;k >= 0 && ls[k].startsWith("test result: "); k--)
8951
+ g.push(ls[k]);
8952
+ return g;
9009
8953
  }
9010
- return s.length;
8954
+ if (!lastOf(segs[segs.length - 1]))
8955
+ return [];
8956
+ return segs.flatMap((g) => lastOf(g) ?? []);
9011
8957
  }
9012
- function matchingBrace(s, open) {
9013
- let depth = 0;
9014
- for (let i = open;i < s.length; ) {
9015
- const j = skipLiteral(s, i);
9016
- if (j !== i) {
9017
- i = j;
9018
- continue;
9019
- }
9020
- if (s[i] === "{")
9021
- depth++;
9022
- else if (s[i] === "}" && --depth === 0)
9023
- return i;
9024
- i++;
9025
- }
9026
- return s.length;
8958
+ function bunCount(out2) {
8959
+ const ls = out2.split(`
8960
+ `).filter((x) => x.trim());
8961
+ if (!/^Ran \d+ tests? across \d+ files?\./.test(ls[ls.length - 1] ?? ""))
8962
+ return null;
8963
+ const blk = ls.slice(-9, -1);
8964
+ const last = (k) => +(blk.map((x) => x.match(new RegExp(`^\\s*(\\d+) ${k}\\s*$`))?.[1]).filter(Boolean).pop() ?? 0);
8965
+ return last("pass") + last("fail");
9027
8966
  }
9028
- function items(src) {
9029
- const out2 = [];
9030
- let start = 0, head = "", p = 0, i = 0;
9031
- const push = (end, body, stop, balanced = true) => {
9032
- if (head.trim())
9033
- out2.push({ head, raw: src.slice(start, stop).trim(), end, body, balanced });
9034
- head = "";
9035
- p = 0;
9036
- start = stop;
9037
- };
9038
- while (i < src.length) {
9039
- const j = skipLiteral(src, i);
9040
- if (j !== i) {
9041
- if (!isComment(src, i))
9042
- head += src.slice(i, j);
9043
- i = j;
9044
- continue;
9045
- }
9046
- const c = src[i];
9047
- if (c === "(" || c === "[")
9048
- p++;
9049
- else if (c === ")" || c === "]")
9050
- p--;
9051
- if (c === "{" && p === 0) {
9052
- const close = matchingBrace(src, i);
9053
- const h = head.trim();
9054
- const declaration = /^(export\s+)?(declare\s+)?(type|interface|enum)\b/.test(h);
9055
- if (declaration || h === "" || /^export(\s+type)?$/.test(h) || /[:|&]$/.test(h)) {
9056
- head += stripComments(src.slice(i, close + 1));
9057
- i = close + 1;
9058
- if (declaration && !/^(export\s+)?(declare\s+)?type\b/.test(h))
9059
- push("", null, i);
9060
- continue;
9061
- }
9062
- const body = src.slice(i + 1, close);
9063
- const balanced = close < src.length;
9064
- i = close + 1;
9065
- if (src[i] === ";")
9066
- i++;
9067
- push("{", body, i, balanced);
9068
- continue;
9069
- }
9070
- if (c === ";" && p === 0) {
9071
- i++;
9072
- push(";", null, i);
9073
- continue;
9074
- }
9075
- if (c === `
9076
- ` && p === 0 && head.trim() && /^\s*(export|import)\b/.test(src.slice(i + 1, i + 40))) {
9077
- push("", null, i);
9078
- i++;
9079
- start = i;
9080
- continue;
9081
- }
9082
- head += c;
9083
- i++;
9084
- }
9085
- push("", null, src.length);
9086
- return out2;
8967
+ function unittestCount(out2, ok) {
8968
+ const ls = out2.split(`
8969
+ `).filter((x) => x.trim()), v = ls[ls.length - 1] ?? "", r = ls[ls.length - 2]?.match(/^Ran (\d+) tests? in [\d.]+s$/);
8970
+ if (!r || !/^(?:OK|FAILED)\b/.test(v))
8971
+ return null;
8972
+ if (ls.filter((x) => /^Ran \d+ tests? in [\d.]+s$/.test(x)).length > 1)
8973
+ return null;
8974
+ if (ok === true && v.startsWith("FAILED") || ok === false && v.startsWith("OK"))
8975
+ return null;
8976
+ return Math.max(0, +r[1] - +(v.match(/skipped=(\d+)/)?.[1] ?? 0) - +(v.match(/expected failures=(\d+)/)?.[1] ?? 0));
9087
8977
  }
9088
- function arrowHeadLen(s) {
9089
- const n2 = s.length;
9090
- let i = 0;
9091
- const ws = () => {
9092
- while (i < n2 && /\s/.test(s[i]))
9093
- i++;
9094
- };
9095
- if (/^async\s/.test(s)) {
9096
- i = 5;
9097
- ws();
9098
- }
9099
- if (s[i] === "<") {
9100
- let d2 = 0;
9101
- for (;i < n2; i++) {
9102
- if (s[i] === "=" && s[i + 1] === ">")
9103
- i++;
9104
- else if (s[i] === "<")
9105
- d2++;
9106
- else if (s[i] === ">" && --d2 === 0) {
9107
- i++;
9108
- break;
9109
- }
8978
+ function goCount(out2, ok) {
8979
+ const lines = out2.split(`
8980
+ `);
8981
+ if (!lines.some((x) => GO_PKG_RE.test(x)))
8982
+ return;
8983
+ 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));
8984
+ for (const x of lines) {
8985
+ const r = x.match(/^=== RUN\s+(\S+)/), m = x.match(/^--- (PASS|FAIL|SKIP): (\S+)/);
8986
+ if (r)
8987
+ seen.add(r[1]);
8988
+ else if (m && (seen.has(m[2]) || m[1] === "FAIL" && failPkg)) {
8989
+ last.set(m[2], m[1]);
8990
+ if (m[1] === "SKIP")
8991
+ skip.add(m[2]);
9110
8992
  }
9111
- if (d2 !== 0)
9112
- return -1;
9113
- ws();
9114
8993
  }
9115
- if (s[i] === "(") {
9116
- let d2 = 0;
9117
- for (;i < n2; ) {
9118
- const k = skipLiteral(s, i);
9119
- if (k !== i) {
9120
- i = k;
8994
+ for (const k of skip)
8995
+ last.set(k, "SKIP");
8996
+ if (ok === true && [...last.values()].includes("FAIL"))
8997
+ return null;
8998
+ const t = [...last.values()].filter((v) => v !== "SKIP").length;
8999
+ if (t > 0)
9000
+ return t;
9001
+ if (lines.some((x) => /^(?:=== RUN|PASS$|FAIL$|testing: warning: no tests to run)/.test(x)))
9002
+ return 0;
9003
+ return lines.filter((x) => GO_PKG_RE.test(x)).every((x) => /\[no tests? (?:files|to run)\]/.test(x)) ? 0 : null;
9004
+ }
9005
+ function goRunner(cmd, args) {
9006
+ const segs = args ? [[cmd, ...args]] : cmd.split(/&&|\|\||[;|\n]/).map((x) => x.trim().split(/\s+/).filter(Boolean));
9007
+ for (const t0 of segs) {
9008
+ const t = t0.map((x) => x.replace(/^["']|["']$/g, ""));
9009
+ let k = 0;
9010
+ for (;k < t.length; k++) {
9011
+ const x = t[k];
9012
+ if (/^[A-Za-z_]\w*=/.test(x) || /^(?:env|command|exec|time|nohup)$/.test(basename11(x)))
9013
+ continue;
9014
+ if (/^-/.test(x) && k > 0 && basename11(t[k - 1]) === "env") {
9015
+ if (x === "-u" || x === "-C" || x === "-S")
9016
+ k++;
9121
9017
  continue;
9122
9018
  }
9123
- if (s[i] === "(")
9124
- d2++;
9125
- else if (s[i] === ")" && --d2 === 0) {
9126
- i++;
9127
- break;
9128
- }
9129
- i++;
9019
+ break;
9130
9020
  }
9131
- if (d2 !== 0)
9132
- return -1;
9133
- } else {
9134
- const st = i;
9135
- while (i < n2 && /[\w$]/.test(s[i]))
9136
- i++;
9137
- if (i === st)
9138
- return -1;
9021
+ if (t[k] !== undefined && basename11(t[k]) === "go")
9022
+ return { runner: "go" };
9139
9023
  }
9140
- ws();
9141
- if (s[i] === "=" && s[i + 1] === ">")
9142
- return i + 2;
9143
- if (s[i] !== ":")
9144
- return -1;
9145
- let d = 0;
9146
- for (i++;i < n2; ) {
9147
- const k = skipLiteral(s, i);
9148
- if (k !== i) {
9149
- i = k;
9024
+ return {};
9025
+ }
9026
+ function goJson(out2) {
9027
+ let any = false, failed2 = false;
9028
+ for (const x of out2.split(`
9029
+ `)) {
9030
+ if (!x.trim() || /^go: /.test(x))
9150
9031
  continue;
9032
+ let e;
9033
+ try {
9034
+ e = JSON.parse(x);
9035
+ } catch {
9036
+ return null;
9151
9037
  }
9152
- const c = s[i];
9153
- if ("([{".includes(c))
9154
- d++;
9155
- else if (")]}".includes(c))
9156
- d--;
9157
- else if (d === 0 && c === "=")
9158
- return s[i + 1] === ">" ? i + 2 : -1;
9159
- else if (d === 0 && c === ";")
9160
- return -1;
9161
- i++;
9038
+ if (!e || typeof e !== "object" || typeof e.Action !== "string")
9039
+ return null;
9040
+ any = true;
9041
+ if (e.Action === "fail")
9042
+ failed2 = true;
9162
9043
  }
9163
- return -1;
9044
+ return any ? { failed: failed2 } : null;
9164
9045
  }
9165
- function memberSig(h) {
9166
- if (!h || /^(private|protected|#)/.test(h))
9167
- return null;
9168
- let depth = 0, angle = 0;
9169
- for (let i = 0;i < h.length; i++) {
9170
- const c = h[i];
9171
- if ("([{".includes(c))
9172
- depth++;
9173
- else if (")]}".includes(c))
9174
- depth--;
9175
- else if (depth === 0 && c === "<")
9176
- angle++;
9177
- else if (depth === 0 && c === ">" && h[i - 1] !== "=" && angle > 0)
9178
- angle--;
9179
- else if (c === "=" && depth === 0 && angle === 0 && !"=!<".includes(h[i - 1] ?? " ") && h[i + 1] !== "=" && h[i + 1] !== ">") {
9180
- const rhs = h.slice(i + 1).trim();
9181
- if (rhs.endsWith("=>") || /^(async\s+)?function\b/.test(rhs))
9182
- return h;
9183
- const len = arrowHeadLen(rhs);
9184
- return `${h.slice(0, i).trim()}${len > 0 ? ` = ${rhs.slice(0, len)}` : ""}`;
9185
- }
9186
- }
9187
- return h;
9046
+ function summaryPassFailCount(out2) {
9047
+ return ["pass", "fail"].reduce((t, k) => t + +([...out2.matchAll(new RegExp(`^(?:#|\\u2139) ${k} (\\d+)$`, "gm"))].pop()?.[1] ?? 0), 0);
9188
9048
  }
9189
- function splitDeclarators(h) {
9190
- const parts = [];
9191
- let depth = 0, angle = 0, init = false, from = 0;
9192
- for (let i = 0;i < h.length; ) {
9193
- const k = skipLiteral(h, i);
9194
- if (k !== i) {
9195
- i = k;
9196
- continue;
9197
- }
9198
- const c = h[i];
9199
- if ("([{".includes(c))
9200
- depth++;
9201
- else if (")]}".includes(c))
9202
- depth--;
9203
- else if (depth === 0 && !init && c === "<")
9204
- angle++;
9205
- else if (depth === 0 && !init && c === ">" && h[i - 1] !== "=")
9206
- angle = Math.max(0, angle - 1);
9207
- else if (depth === 0 && angle === 0 && !init && c === "=" && h[i + 1] !== "=" && h[i + 1] !== ">" && !"=!<".includes(h[i - 1] ?? " ")) {
9208
- init = true;
9209
- const rest = h.slice(i + 1);
9210
- const len = arrowHeadLen(rest.trimStart());
9211
- if (len > 0) {
9212
- i += 1 + (rest.length - rest.trimStart().length) + len;
9213
- continue;
9214
- }
9215
- } else if (depth === 0 && angle === 0 && c === ",") {
9216
- parts.push(h.slice(from, i));
9217
- from = i + 1;
9218
- init = false;
9219
- }
9220
- i++;
9221
- }
9222
- parts.push(h.slice(from));
9223
- return parts;
9049
+ function skipped(raw2) {
9050
+ return stripAnsi(raw2).split(`
9051
+ `).filter((l) => /^(?:=+ )?\d+ \w+.* in [\d.]+s|^\s*Tests?:?\s+\d|^(?:#|\u2139) skipped \d/.test(l.trim())).reduce((t, l) => t + [...l.matchAll(/(\d+) (?:skipped|deselected|xfailed)|skipped (\d+)/g)].reduce((u, m) => u + +(m[1] ?? m[2]), 0), 0);
9224
9052
  }
9225
- function exportHead(h, end) {
9226
- let depth = 0, last = -1, nn = -1;
9227
- const star = /^export\s*(type\s*)?\*/.test(h.slice(0, 40));
9228
- for (let i = 0;i < h.length; ) {
9229
- const k = skipLiteral(h, i);
9230
- if (k !== i) {
9231
- last = k - 1;
9232
- i = k;
9233
- continue;
9234
- }
9235
- const c = h[i];
9236
- if ("([{".includes(c))
9237
- depth++;
9238
- else if (")]}".includes(c))
9239
- depth--;
9240
- else if (depth === 0 && c === ";")
9241
- return h.slice(0, i + 1);
9242
- else if (depth === 0 && c === `
9243
- ` && last >= 0) {
9244
- if (nn < i) {
9245
- nn = i;
9246
- while (nn < h.length && /\s/.test(h[nn]))
9247
- nn++;
9248
- }
9249
- const tail = h.slice(Math.max(0, last - 300), last + 1);
9250
- const next = h.slice(nn, nn + 12);
9251
- const open = /[=|&,<(:?.+\-*/]$|=>$|\b(from|as|extends|keyof|typeof|type)$/.test(tail) || /^(from\b|extends\b|as\b|[|&?:.,=])/.test(next);
9252
- if (!open && (!star || /\bfrom\s*(["'])[^"']*\1$/.test(tail)))
9253
- return h.slice(0, last + 1);
9254
- }
9255
- if (!/\s/.test(c))
9256
- last = i;
9257
- i++;
9053
+ function testCount(raw2, path, ok) {
9054
+ const out2 = stripAnsi(raw2);
9055
+ const bu = bunCount(out2), ut = unittestCount(out2, ok);
9056
+ if (bu !== null)
9057
+ return bu;
9058
+ if (ut !== null)
9059
+ return ut;
9060
+ if (/^\s*(?:Test Files\s+0\b|No test files found)/m.test(out2) && !/^\s*Tests?\s+\d/m.test(out2))
9061
+ return 0;
9062
+ const r = ran(out2, path, ok);
9063
+ if (r !== null)
9064
+ return r;
9065
+ const tl = tailLines(out2);
9066
+ const pw = tl.filter((x) => /^\s*\d+ (?:passed|failed|flaky)\b/.test(x));
9067
+ if (pw.length && tl.some((x) => /^\s*\d+ passed \([\d.]+m?s\)/.test(x) || /^\s*\d+ failed$/.test(x)))
9068
+ return pw.reduce((t, x) => t + +x.trim().split(" ")[0], 0);
9069
+ const lastNum = (re) => {
9070
+ const m = [...out2.matchAll(re)].pop();
9071
+ return m ? +m[1] : null;
9072
+ };
9073
+ const mp = lastNum(/^\s*(\d+) passing\b/gm), mf = lastNum(/^\s*(\d+) failing\b/gm);
9074
+ if (mp !== null || mf !== null)
9075
+ return (mp ?? 0) + (mf ?? 0);
9076
+ return null;
9077
+ }
9078
+ function classifyCheck(i) {
9079
+ if (i.missing)
9080
+ return { result: "not_run", reason: "tool not found on PATH" };
9081
+ if (i.cut)
9082
+ return { result: "not_run", reason: "timed out or aborted" };
9083
+ if (i.kind === "static")
9084
+ return { result: i.ok ? "pass" : "fail" };
9085
+ const text2 = stripAnsi(i.out);
9086
+ if (i.runner === "go") {
9087
+ if (i.ok)
9088
+ return { result: "not_run", reason: GO_EXIT0_REASON };
9089
+ const j = goJson(text2);
9090
+ if (j)
9091
+ return j.failed ? { result: "fail" } : { result: "not_run", reason: UNCONFIRMED_REASON };
9092
+ return FAIL_EVIDENCE.test(text2) || GO_FAIL_EVIDENCE.test(text2) ? { result: "fail" } : { result: "not_run", reason: UNCONFIRMED_REASON };
9258
9093
  }
9259
- return h.trimEnd() + (end === ";" ? ";" : "");
9094
+ const n2 = testCount(i.out, i.path, i.ok);
9095
+ if (!i.ok && FAIL_EVIDENCE.test(text2))
9096
+ return { result: "fail", ...n2 !== null ? { n: n2 } : {} };
9097
+ if (n2 === 0)
9098
+ return { result: "not_run", n: 0, reason: `${NO_TESTS_REASON} (ran 0 tests, empty or all skipped)` };
9099
+ if (!i.ok)
9100
+ return { result: "fail", ...n2 !== null ? { n: n2 } : {} };
9101
+ if (n2 === null)
9102
+ return { result: "not_run", reason: UNMEASURED_REASON };
9103
+ return { result: "pass", n: n2 };
9260
9104
  }
9261
- function hasTopSemi(line2) {
9262
- let depth = 0;
9263
- for (let i = 0;i < line2.length; ) {
9264
- const k = skipLiteral(line2, i);
9265
- if (k !== i) {
9266
- i = k;
9267
- continue;
9268
- }
9269
- const c = line2[i];
9270
- if ("([{".includes(c))
9271
- depth++;
9272
- else if (")]}".includes(c))
9273
- depth--;
9274
- else if (c === ";" && depth <= 0 && line2.slice(i + 1).trim())
9275
- return true;
9276
- i++;
9105
+ function checkProvenance(name, ctx) {
9106
+ const head = name.split(":")[0];
9107
+ if (LLM_JUDGES.has(head))
9108
+ return "llm_judge";
9109
+ if (head === "holdout")
9110
+ return "independent";
9111
+ if (!NON_RUNNER.has(head) && name.includes(":")) {
9112
+ const p = normPath(name.slice(name.indexOf(":") + 1));
9113
+ if (ctx.wallFiles.some((w) => normPath(w) === p))
9114
+ return "independent";
9115
+ if (ctx.addedFiles.some((a) => normPath(a) === p))
9116
+ return "agent_authored";
9117
+ if (ctx.modifiedFiles?.some((m) => normPath(m) === p))
9118
+ return "agent_authored";
9277
9119
  }
9278
- return false;
9120
+ return "exogenous";
9279
9121
  }
9280
- function strictMember(m) {
9281
- const h = m.head.trim();
9282
- if (!MEMBER_GRAMMAR.test(h) || NOT_MEMBER.test(h))
9283
- return false;
9284
- if (m.end !== "{" && /^[^=:<]*\(/.test(h) && !/^(?:(?:public|protected|private)\s+)?constructor\b/.test(h) && !/\)\s*:/.test(h))
9285
- return false;
9286
- return true;
9122
+ function summaryOf(a) {
9123
+ const why = a.tier === a.computed ? a.reasons.slice(0, 2).join("; ") : `computed ${a.computed}; override ${a.override?.from} to ${a.override?.to}: ${a.override?.reason}`;
9124
+ const def = a.checks_deferred;
9125
+ return `risk: ${a.tier} (${why}), ${def.length ? def.join(", ") : "no deeper checks"} not run`;
9287
9126
  }
9288
- function tsAmbiguous(s, jsx) {
9289
- const n2 = s.length, stack = [];
9290
- const tpl = (from) => {
9291
- for (let j = from;j < n2; ) {
9292
- const c = s[j];
9293
- if (c === "\\")
9294
- j += 2;
9295
- else if (c === "`")
9296
- return j + 1;
9297
- else if (c === "$" && s[j + 1] === "{") {
9298
- stack.push("$");
9299
- return j + 2;
9300
- } else
9301
- j++;
9302
- }
9303
- return -1;
9304
- };
9305
- let prev = "", prev2 = "";
9306
- const set = (c) => {
9307
- prev2 = prev;
9308
- prev = c;
9309
- };
9310
- for (let i = 0;i < n2; ) {
9311
- const c = s[i];
9312
- if (/\s/.test(c)) {
9313
- i++;
9314
- continue;
9315
- }
9316
- if (c === "/" && s[i + 1] === "/") {
9317
- const e = s.indexOf(`
9318
- `, i);
9319
- if (e < 0)
9320
- break;
9321
- i = e;
9322
- continue;
9323
- }
9324
- if (c === "/" && s[i + 1] === "*") {
9325
- const e = s.indexOf("*/", i + 2);
9326
- if (e < 0)
9327
- return true;
9328
- i = e + 2;
9329
- continue;
9330
- }
9331
- if (c === "`" || c === "}" && stack[stack.length - 1] === "$") {
9332
- if (c === "`" && jsx)
9333
- return true;
9334
- if (c === "}")
9335
- stack.pop();
9336
- const e = tpl(i + 1);
9337
- if (e < 0)
9338
- return true;
9339
- i = e;
9340
- set("a");
9341
- continue;
9342
- }
9343
- if (c === '"' || c === "'") {
9344
- let j = i + 1;
9345
- for (;j < n2 && s[j] !== c; j++) {
9346
- if (s[j] === "\\")
9347
- j++;
9348
- else if (s[j] === `
9349
- `)
9350
- return true;
9351
- }
9352
- if (j >= n2)
9353
- return true;
9354
- i = j + 1;
9355
- set("a");
9356
- continue;
9357
- }
9358
- if (c === "/") {
9359
- if (jsx && prev === "<") {
9360
- i++;
9361
- set("/");
9362
- continue;
9363
- }
9364
- if (commentBefore(s, i))
9365
- return true;
9366
- if (prev && !"(,=:[!&|?{;+-*%~^".includes(prev))
9367
- return true;
9368
- if ((prev === "+" || prev === "-") && prev2 === prev)
9369
- return true;
9370
- let j = i + 1, cls = false, closed = false;
9371
- for (;j < n2; j++) {
9372
- const d = s[j];
9373
- if (d === `
9374
- `)
9375
- return true;
9376
- if (d === "\\")
9377
- j++;
9378
- else if (d === "[")
9379
- cls = true;
9380
- else if (d === "]")
9381
- cls = false;
9382
- else if (d === "/" && !cls) {
9383
- closed = true;
9384
- break;
9385
- }
9386
- }
9387
- if (!closed)
9388
- return true;
9389
- i = j + 1;
9390
- while (i < n2 && /[a-z]/i.test(s[i]))
9391
- i++;
9392
- set("a");
9393
- continue;
9394
- }
9395
- if (c === "(" || c === "[" || c === "{")
9396
- stack.push(c);
9397
- else if (c === ")" || c === "]" || c === "}") {
9398
- const o = stack.pop();
9399
- if (o !== (c === ")" ? "(" : c === "]" ? "[" : "{"))
9400
- return true;
9401
- }
9402
- set(c);
9403
- i++;
9404
- }
9405
- return stack.length > 0;
9127
+ function markExecuted(a, executed) {
9128
+ const checks_deferred = deferredOf(a.tier, a.checks_selected, executed);
9129
+ return { ...a, checks_deferred, summary: summaryOf({ ...a, checks_deferred }) + (a.advisories?.length ? `; advisory: ${a.advisories.join(", ")}` : "") };
9406
9130
  }
9407
- function maskDefaults(sig) {
9408
- let out2 = "", pd = 0;
9409
- for (let i = 0;i < sig.length; ) {
9410
- const k = skipLiteral(sig, i);
9411
- if (k !== i) {
9412
- out2 += sig.slice(i, k);
9413
- i = k;
9414
- continue;
9415
- }
9416
- const c = sig[i];
9417
- if (c === "(")
9418
- pd++;
9419
- else if (c === ")")
9420
- pd--;
9421
- else if (c === "=" && pd > 0 && sig[i + 1] !== "=" && sig[i + 1] !== ">" && !"=!<".includes(sig[i - 1] ?? " ")) {
9422
- out2 = out2.trimEnd() + " = ...";
9423
- let d = 0, angle = 0, j = i + 1;
9424
- for (;j < sig.length; ) {
9425
- const l = skipLiteral(sig, j);
9426
- if (l !== j) {
9427
- j = l;
9428
- continue;
9429
- }
9430
- const e = sig[j];
9431
- if ("([{".includes(e))
9432
- d++;
9433
- else if (")]}".includes(e)) {
9434
- if (d === 0)
9435
- break;
9436
- d--;
9437
- } else if (e === "<" && /[\w$]/.test(sig[j - 1] ?? ""))
9438
- angle++;
9439
- else if (e === ">" && sig[j - 1] !== "=" && angle > 0)
9440
- angle--;
9441
- else if (e === "," && d === 0 && angle === 0)
9442
- break;
9443
- j++;
9444
- }
9445
- i = j;
9446
- continue;
9131
+ function manifestDepsChanged(path, oldText, newText, patch) {
9132
+ if (/(^|\/)package\.json$/.test(path)) {
9133
+ try {
9134
+ const o = oldText.trim() ? JSON.parse(oldText) : {}, n2 = newText.trim() ? JSON.parse(newText) : {};
9135
+ return PKG_DEP_KEYS.some((k) => JSON.stringify(o?.[k] ?? {}) !== JSON.stringify(n2?.[k] ?? {}));
9136
+ } catch {
9137
+ return true;
9447
9138
  }
9448
- out2 += c;
9449
- i++;
9450
9139
  }
9451
- return out2;
9140
+ const changed = patch.split(`
9141
+ `).filter((l) => /^[+-]/.test(l) && !/^(\+\+\+|---)/.test(l));
9142
+ if (/(^|\/)go\.mod$/.test(path))
9143
+ return changed.some((l) => {
9144
+ const b = l.slice(1).trim();
9145
+ return b !== "" && !b.startsWith("//") && !/^(module|go|toolchain)\b/.test(b);
9146
+ });
9147
+ return changed.some((l) => {
9148
+ const b = l.slice(1).trim();
9149
+ if (/^"[A-Za-z][\w.-]*(\[[\w,.-]+\])?\s*([<>=~!]|;|@|"\s*,?\s*$)/.test(b))
9150
+ return true;
9151
+ const m = /^([A-Za-z][\w.-]*)\s*=\s*("[\^~<>=!*\d]|\{[^}]*version)/.exec(b);
9152
+ return !!m && !PYPROJECT_NONDEP.test(m[1]);
9153
+ });
9452
9154
  }
9453
- function maskDecorators(sig) {
9454
- let out2 = "";
9455
- const badNext = (at) => {
9456
- const r = sig.slice(at).trimStart();
9457
- return !/[A-Za-z_$@]/.test(r[0] ?? "") || RESERVED_AFTER_DECORATOR.test(r);
9155
+ function manifestsWithoutLock(files) {
9156
+ return files.filter((f) => f.deps && f.status !== "deleted").flatMap((f) => {
9157
+ const pair = MANIFEST_LOCKS.find(([m]) => m.test(f.path));
9158
+ if (!pair)
9159
+ return [];
9160
+ const d = dirOf(f.path);
9161
+ const locked = files.some((g) => pair[1].test(g.path) && (dirOf(g.path) === "" || d === dirOf(g.path) || d.startsWith(dirOf(g.path) + "/")));
9162
+ return locked ? [] : [f.path];
9163
+ });
9164
+ }
9165
+ function classifyRisk(files, env = process.env) {
9166
+ const lines = files.reduce((n2, f) => n2 + f.added + f.deleted, 0);
9167
+ const sensitive = files.flatMap((f) => {
9168
+ const hit = SENSITIVE.find(([, re]) => re.test(f.path));
9169
+ return hit && !isTestFile(f.path) ? [`${f.path} (${hit[0]})`] : [];
9170
+ });
9171
+ const testFiles = files.filter((f) => isTestFile(f.path));
9172
+ const testsDeleted = testFiles.filter((f) => f.status === "deleted").map((f) => f.path);
9173
+ const testsModified = [...testFiles.filter((f) => f.status !== "added" && f.status !== "deleted").map((f) => f.path), ...files.filter((f) => TEST_CFG.test(f.path)).map((f) => f.path)];
9174
+ const api = files.filter((f) => API.test(f.path) && !isTestFile(f.path)).map((f) => f.path);
9175
+ const high = [], med = [];
9176
+ if (sensitive.length)
9177
+ high.push(`sensitive paths: ${sensitive.slice(0, 5).join(", ")}${sensitive.length > 5 ? ` (+${sensitive.length - 5})` : ""}`);
9178
+ if (testsDeleted.length)
9179
+ high.push(`test files deleted: ${testsDeleted.join(", ")}`);
9180
+ if (api.length)
9181
+ high.push(`public API surface: ${api.slice(0, 3).join(", ")}`);
9182
+ if (files.length >= FILES_HIGH)
9183
+ high.push(`${files.length} files >= ${FILES_HIGH}`);
9184
+ if (lines >= LINES_HIGH)
9185
+ high.push(`${lines} changed lines >= ${LINES_HIGH}`);
9186
+ if (testsModified.length)
9187
+ med.push(`existing tests or test config modified: ${testsModified.slice(0, 3).join(", ")}`);
9188
+ if (files.length >= FILES_MEDIUM)
9189
+ med.push(`${files.length} files >= ${FILES_MEDIUM}`);
9190
+ if (lines >= LINES_MEDIUM)
9191
+ med.push(`${lines} changed lines >= ${LINES_MEDIUM}`);
9192
+ const computed = high.length ? "high" : med.length ? "medium" : "low";
9193
+ const reasons = high.length ? [...high, ...med] : med.length ? med : [`${files.length} file${files.length === 1 ? "" : "s"}, no sensitive paths`];
9194
+ let tier = computed, override, ignored;
9195
+ const asked = (env["LOKI_RISK_TIER"] ?? "").trim().toLowerCase();
9196
+ if (asked in RANK2 && asked !== computed) {
9197
+ const reason = (env["LOKI_RISK_OVERRIDE_REASON"] ?? "").trim();
9198
+ if (RANK2[asked] > RANK2[computed]) {
9199
+ tier = asked;
9200
+ override = { from: computed, to: asked, reason: reason || "raised by LOKI_RISK_TIER" };
9201
+ } else if (reason) {
9202
+ tier = asked;
9203
+ override = { from: computed, to: asked, reason };
9204
+ } else
9205
+ ignored = `LOKI_RISK_TIER=${asked} ignored: lowering from ${computed} needs LOKI_RISK_OVERRIDE_REASON`;
9206
+ }
9207
+ const plan = PLAN[tier], checks_deferred = deferredOf(tier, plan.selected, []);
9208
+ const advisories = manifestsWithoutLock(files).map((p) => `manifest changed without lockfile: ${p}`);
9209
+ const summary = summaryOf({ tier, computed, reasons, ...override ? { override } : {}, checks_deferred }) + (advisories.length ? `; advisory: ${advisories.join(", ")}` : "");
9210
+ return {
9211
+ version: 1,
9212
+ tier,
9213
+ computed,
9214
+ reasons,
9215
+ stats: { files: files.length, lines, sensitive, tests_modified: testsModified, tests_deleted: testsDeleted, api },
9216
+ checks_selected: plan.selected,
9217
+ checks_deferred,
9218
+ ...override ? { override } : {},
9219
+ ...ignored ? { override_ignored: ignored } : {},
9220
+ ...advisories.length ? { advisories } : {},
9221
+ summary
9458
9222
  };
9459
- for (let i = 0;i < sig.length; ) {
9460
- const k = skipLiteral(sig, i);
9461
- if (k !== i) {
9462
- out2 += sig.slice(i, k);
9463
- i = k;
9464
- continue;
9465
- }
9466
- if (sig[i] !== "@") {
9467
- out2 += sig[i];
9468
- i++;
9223
+ }
9224
+ function changedFiles(repoDir, baseSha) {
9225
+ const run = (args) => safeGit(repoDir, args).split(`
9226
+ `).map((l) => l.trim()).filter(Boolean);
9227
+ const tracked = run(["diff", "--name-only", baseSha]);
9228
+ const untracked = run(["ls-files", "--others", "--exclude-standard"]);
9229
+ return [...new Set([...tracked, ...untracked])].filter((f) => !f.startsWith(".loki/"));
9230
+ }
9231
+ function diffFiles(repoDir, baseSha) {
9232
+ const paths = changedFiles(repoDir, baseSha), stat = new Map;
9233
+ for (const l of safeGit(repoDir, ["diff", "--numstat", baseSha]).split(`
9234
+ `)) {
9235
+ const [a, d, ...p] = l.split("\t"), path = p.join("\t");
9236
+ if (path)
9237
+ stat.set(path, { path, added: Number(a) || 0, deleted: Number(d) || 0 });
9238
+ }
9239
+ for (const l of safeGit(repoDir, ["diff", "--name-status", baseSha]).split(`
9240
+ `)) {
9241
+ const [s, ...p] = l.split("\t"), f = stat.get(p[p.length - 1] ?? "");
9242
+ if (f)
9243
+ f.status = s?.startsWith("A") ? "added" : s?.startsWith("D") ? "deleted" : "modified";
9244
+ }
9245
+ const out2 = paths.map((path) => {
9246
+ const f = stat.get(path);
9247
+ if (f)
9248
+ return f;
9249
+ let n2 = 0;
9250
+ try {
9251
+ n2 = readFileSync28(join37(repoDir, path), "utf8").split(`
9252
+ `).length;
9253
+ } catch {}
9254
+ return { path, added: n2, deleted: 0, status: "added" };
9255
+ });
9256
+ for (const f of out2) {
9257
+ if (f.status === "deleted" || !MANIFEST_LOCKS.some(([m]) => m.test(f.path)))
9469
9258
  continue;
9470
- }
9471
- const m = /^@[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)*/.exec(sig.slice(i, i + 200));
9472
- if (!m)
9473
- return null;
9474
- out2 += m[0];
9475
- i += m[0].length;
9476
- if (sig[i] === "(") {
9477
- out2 += "(...)";
9478
- i = skipBalanced(sig, i);
9479
- }
9480
- if (badNext(i))
9481
- return null;
9259
+ try {
9260
+ const oldText = f.status === "added" ? "" : safeGit(repoDir, ["show", `${baseSha}:${f.path}`]);
9261
+ const newText = readFileSync28(join37(repoDir, f.path), "utf8");
9262
+ const patch = f.status === "added" ? newText.split(`
9263
+ `).map((l) => `+${l}`).join(`
9264
+ `) : safeGit(repoDir, ["diff", "-U0", baseSha, "--", f.path]);
9265
+ if (manifestDepsChanged(f.path, oldText, newText, patch))
9266
+ f.deps = true;
9267
+ } catch {}
9482
9268
  }
9483
9269
  return out2;
9484
9270
  }
9485
- function skipBalanced(s, i) {
9486
- let d = 0;
9487
- while (i < s.length) {
9488
- const k = skipLiteral(s, i);
9489
- if (k !== i) {
9490
- i = k;
9491
- continue;
9492
- }
9493
- if ("([{".includes(s[i]))
9494
- d++;
9495
- else if (")]}".includes(s[i]) && --d === 0)
9496
- return i + 1;
9497
- i++;
9271
+ function recordRisk(repoDir, a) {
9272
+ try {
9273
+ const dir = join37(repoDir, ".loki", "state"), p = join37(dir, "risk-tier.json");
9274
+ mkdirSync12(dir, { recursive: true });
9275
+ writeFileSync15(`${p}.tmp`, JSON.stringify(a, null, 2) + `
9276
+ `);
9277
+ renameSync7(`${p}.tmp`, p);
9278
+ return p;
9279
+ } catch {
9280
+ return null;
9498
9281
  }
9499
- return s.length;
9500
9282
  }
9501
- function maskExtends(head) {
9502
- let angle = 0, depth = 0;
9503
- for (let i = 0;i < head.length; ) {
9504
- const k = skipLiteral(head, i);
9505
- if (k !== i) {
9506
- i = k;
9507
- continue;
9508
- }
9509
- const c = head[i];
9510
- if ("([{".includes(c))
9511
- depth++;
9512
- else if (")]}".includes(c))
9513
- depth--;
9514
- else if (c === "<")
9515
- angle++;
9516
- else if (c === ">" && head[i - 1] !== "=")
9517
- angle = Math.max(0, angle - 1);
9518
- else if (depth === 0 && angle === 0 && /^extends\s/.test(head.slice(i)) && /\W/.test(head[i - 1] ?? " ")) {
9519
- const from = i + 7;
9520
- const im = /\simplements\s/.exec(head.slice(from));
9521
- const end = im ? from + im.index : head.length;
9522
- const expr = head.slice(from, end).trim();
9523
- const ok = /^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)*(?:<[^()]*>)?$/.test(expr);
9524
- const tail = head.slice(end).trim();
9525
- return `${head.slice(0, i)}extends ${ok ? expr : "..."}${tail ? ` ${tail}` : ""}`;
9283
+ function truncateBytes(s, cap) {
9284
+ const buf = Buffer.from(s, "utf8");
9285
+ return buf.length <= cap ? s : buf.subarray(0, Math.max(0, cap)).toString("utf8").replace(/\uFFFD$/, "");
9286
+ }
9287
+ function failureExcerpt(out2, repoDir = "", cap = EXCERPT_GROUP_CAP) {
9288
+ let t = stripAnsi(out2.slice(-65536)).replace(/\r/g, "");
9289
+ if (repoDir)
9290
+ t = t.split(repoDir.replace(/\/+$/, "") + "/").join("");
9291
+ const lines = t.split(`
9292
+ `).map((l) => l.replace(/\s+$/, ""));
9293
+ const pick = new Set;
9294
+ const take = (i, n2 = 1) => {
9295
+ for (let k = 0;k < n2 && i + k < lines.length; k++)
9296
+ if (lines[i + k].trim())
9297
+ pick.add(i + k);
9298
+ };
9299
+ lines.forEach((l, i) => {
9300
+ if (/^E\s/.test(l))
9301
+ take(i);
9302
+ else if (/^\s*(Expected|Received)\b/.test(l) || /^\s*expect\(.*\)\.\w+/.test(l))
9303
+ take(i);
9304
+ else if (/^[\w./@-]+\.py:\d+: /.test(l))
9305
+ take(i);
9306
+ else if (/AssertionError|^\s*(expected|actual)\b.*[:=]|^\s*(not ok|FAILED)\b/.test(l))
9307
+ take(i, 3);
9308
+ else if (/^\s*--- FAIL/.test(l))
9309
+ take(i, 4);
9310
+ else if (/^[\w./@-]+\.\w+(\(\d+,\d+\)|:\d+(:\d+)?)[:\s].*\b(error|TS\d+|[A-Z]{1,3}\d{3,4})\b/.test(l))
9311
+ take(i);
9312
+ });
9313
+ const lastFrame = lines.map((l, i) => /^[\w./@-]+\.py:\d+: /.test(l) ? i : -1).filter((i) => i >= 0).pop();
9314
+ if (lastFrame !== undefined && [...pick].some((i) => /^E\s/.test(lines[i])))
9315
+ take(lastFrame);
9316
+ let body = [...pick].sort((a, b) => a - b).map((i) => lines[i]).join(`
9317
+ `);
9318
+ if (!body)
9319
+ body = lines.filter((l) => l.trim()).slice(-6).join(`
9320
+ `);
9321
+ body = redactSecrets(body);
9322
+ return truncateBytes(body, cap);
9323
+ }
9324
+ function pytestCollectionIsRed(output, repoDir) {
9325
+ if (/ModuleNotFoundError/.test(output))
9326
+ return false;
9327
+ const real2 = (p) => {
9328
+ try {
9329
+ return realpathSync16(isAbsolute3(p) ? p : join37(repoDir, p));
9330
+ } catch {
9331
+ return null;
9526
9332
  }
9527
- i++;
9333
+ };
9334
+ const repoReal = real2(repoDir);
9335
+ const under = (p) => {
9336
+ const r = real2(p);
9337
+ return !!repoReal && !!r && (r === repoReal || r.startsWith(`${repoReal}/`));
9338
+ };
9339
+ const imp = /ImportError: cannot import name .* from ['"][\w.]+['"] \(([^)]+)\)/.exec(output);
9340
+ if (imp)
9341
+ return under(imp[1]);
9342
+ const hdr = [...output.matchAll(/^=+ short test summary info =+$/gm)].pop();
9343
+ if (!hdr)
9344
+ return false;
9345
+ const cls = [...output.slice(hdr.index).matchAll(/^ERROR \S+ - (\w+Error): /gm)].pop()?.[1];
9346
+ if (cls !== "AttributeError" && cls !== "NameError")
9347
+ return false;
9348
+ const start = output.search(/^_+ ERROR collecting /m);
9349
+ if (start < 0 || start > hdr.index)
9350
+ return false;
9351
+ const block = output.slice(start, hdr.index).split(/^-+ Captured .*$/m)[0];
9352
+ const eLines = [...block.matchAll(new RegExp(`^E {3}${cls}: (.*)$`, "gm"))];
9353
+ const eLine = eLines[eLines.length - 1];
9354
+ if (!eLine)
9355
+ return false;
9356
+ if (cls === "AttributeError") {
9357
+ const m = /^module ['"]([\w.]+)['"] has no attribute/.exec(eLine[1]);
9358
+ return !!m && moduleUnderRepo(repoDir, m[1]);
9528
9359
  }
9529
- return head;
9360
+ const frames = [...block.slice(0, eLine.index).matchAll(/^(.+?):\d+: in \S+\n\s*(.*)$/gm)];
9361
+ const last = frames[frames.length - 1];
9362
+ return !!last && under(last[1]) && !/^\s*(import\s|from\s\S+\s+import\b)/.test(last[2] ?? "");
9530
9363
  }
9531
- function enumSig(h) {
9532
- const open = h.indexOf("{");
9533
- if (open < 0)
9534
- return null;
9535
- const close = matchingBrace(h, open);
9536
- if (close >= h.length)
9537
- return null;
9538
- const names = [];
9539
- let depth = 0, from = open + 1;
9540
- const take = (to) => {
9541
- const nm = h.slice(from, to).split("=")[0].trim();
9542
- if (nm)
9543
- names.push(nm);
9364
+ function moduleUnderRepo(repoDir, name) {
9365
+ const rel = name.replace(/\./g, "/");
9366
+ return existsSync23(join37(repoDir, `${rel}.py`)) || existsSync23(join37(repoDir, rel, "__init__.py"));
9367
+ }
9368
+ function pytestExit1IsRed(output) {
9369
+ if (/^!+ _pytest\.outcomes\.Exit\b/m.test(output))
9370
+ return false;
9371
+ if (/^=*\s*(\d+ \w+(, )?)*\d+ failed\b.* in [\d.]+s/.test(output.trimEnd().split(`
9372
+ `).pop() ?? ""))
9373
+ return true;
9374
+ const hdr = [...output.matchAll(/^=+ short test summary info =+$/gm)].pop();
9375
+ return !!hdr && /^FAILED \S+/m.test(output.slice(hdr.index));
9376
+ }
9377
+ function parsedFailCount(runner, output) {
9378
+ const m = runner === "bun" ? /^\s*(\d+)\s+fail\s*$/m.exec(output) : runner === "jest" ? /Tests:\s*(\d+)\s+failed/i.exec(output) : runner === "vitest" ? /Tests\s+(\d+)\s+failed/i.exec(output) : null;
9379
+ return m ? Number(m[1]) : 0;
9380
+ }
9381
+ function nodeIsRed(f, raw2) {
9382
+ const o = raw2.replace(/^# (?!(?:tests|suites|pass|fail|cancelled|skipped|todo|duration_ms|Subtest)\b)/gm, "");
9383
+ const mod = /^Error(?: \[\w+\])?: Cannot find (?:module|package) '([^']+)'(?:(?!\u2716)[\s\S])*?\nNode\.js v/m.exec(o), top = /^(\S+):\d+\n.*\n.*\n\n?(?:ReferenceError|SyntaxError)\b/m.exec(o);
9384
+ const named = [...o.matchAll(/^(?:\u2716|\s*not ok \d+ -) (?!failing tests:)(.+?)(?: \(|$)/gm)].some((m) => !f.path.endsWith(m[1]) && !m[1].endsWith(f.path)), blk = o.split("failing tests:")[1] ?? "", esm = /^SyntaxError: The requested module '([^']+)' does not provide an export named/m.exec(o), errs = [...blk.matchAll(/^ {2}(\w+Error): .*\n\s+at (?:.*\()?([^\s()]+?):\d+:\d+/gm), ...o.matchAll(/^ +name: '(\w+Error)'\n(?:.*\n)*? +stack: \|-\n\s+(?:.*\()?([^\s()]+?):\d+:\d+/gm)];
9385
+ return /^(?:#|\u2139) fail [1-9]/m.test(raw2) && (/^ {2}AssertionError\b/m.test(blk) || /^ +name: 'AssertionError'/m.test(o) || (mod && !named ? /^[./]/.test(mod[1]) : esm ? /^[./]/.test(esm[1]) : top ? !top[1].endsWith(f.path) : !errs.length || errs.some((e) => !/^(?:Reference|Syntax)Error$/.test(e[1]) || !(e[2].startsWith("node:") || e[2].endsWith(f.path)))));
9386
+ }
9387
+ function notRunReason(status, interpreter) {
9388
+ if (interpreter === "system")
9389
+ return "ran under the system interpreter, not the project's, so the result proves nothing";
9390
+ if (status === null)
9391
+ return "the test runner did not start or timed out";
9392
+ if (status === 126 || status === 127)
9393
+ return `the test runner command was not found or not executable (exit ${status})`;
9394
+ return status === 0 ? "exit 0 but no executed test count could be confirmed" : `exit ${status} but the runner output showed no readable failing test`;
9395
+ }
9396
+ function classify(f, status, output, repoDir, interpreter) {
9397
+ if (interpreter === "system")
9398
+ return "not_run";
9399
+ output = stripAnsi(output);
9400
+ if (status === null || status === 126 || status === 127)
9401
+ return "not_run";
9402
+ if (status === 0)
9403
+ return classifyCheck({ kind: "test", ok: true, out: output, path: f.path, ...f.runner === "go" ? { runner: "go" } : {} }).result === "pass" ? "pass" : "not_run";
9404
+ if (f.runner === "pytest")
9405
+ return status === 1 ? pytestExit1IsRed(output) ? "fail" : "not_run" : status === 2 ? pytestCollectionIsRed(output, repoDir) ? "fail" : "not_run" : "not_run";
9406
+ if (f.runner === "jest" || f.runner === "vitest" || f.runner === "bun")
9407
+ return parsedFailCount(f.runner, output) > 0 ? "fail" : "not_run";
9408
+ return f.runner === "node" && nodeIsRed(f, output) ? "fail" : "not_run";
9409
+ }
9410
+ var NO_TESTS_REASON = "no tests executed", GO_EXIT0_REASON = "test count could not be confirmed: go test output is produced by the code under test and cannot confirm execution (a Go exit 0 is never a pass)", UNCONFIRMED_REASON = "test count could not be confirmed", FAIL_EVIDENCE, GO_FAIL_EVIDENCE, UNMEASURED_REASON = "executed count unmeasured (Loki could not parse the runner summary, harness-owned)", stripAnsi = (s) => s.replace(/\u001b\][^\u0007\u001b]*(?:\u0007|\u001b\\)/g, "").replace(/(?:\u001b\[|\u009b)[0-9;?]*[ -\/]*[@-~]/g, "").replace(/\r\n?/g, `
9411
+ `), tailLines = (out2) => out2.split(`
9412
+ `).filter((x) => x.trim()).slice(-12), GO_PKG_RE, isExecutedProof = (c) => c.result === "pass" && typeof c.n === "number" && c.n > 0, hasExecutedProof = (checks) => checks.some(isExecutedProof), LLM_JUDGES, NON_RUNNER, normPath = (p) => p.replace(/^(?:\.\/)+/, ""), hasExogenousProof = (checks, ctx) => checks.some((c) => isExecutedProof(c) && ((p) => p !== "agent_authored" && p !== "llm_judge")(checkProvenance(c.name ?? "", ctx))), SENSITIVE, API, TEST_CFG, RANK2, FILES_MEDIUM = 4, FILES_HIGH = 15, LINES_MEDIUM = 100, LINES_HIGH = 500, RAN_ALWAYS, PLAN, LOW_SKIPPED, deferredOf = (tier, selected, executed) => (tier === "low" ? LOW_SKIPPED : selected).filter((c) => !RAN_ALWAYS.includes(c) && !executed.includes(c)), MANIFEST_LOCKS, dirOf = (p) => p.includes("/") ? p.slice(0, p.lastIndexOf("/")) : "", PKG_DEP_KEYS, PYPROJECT_NONDEP, EXCERPT_GROUP_CAP = 1500, EXCERPT_TOTAL_CAP = 6144;
9413
+ var init_check_result = __esm(() => {
9414
+ init_testmap();
9415
+ init_safe_git();
9416
+ init_redact();
9417
+ FAIL_EVIDENCE = /\[(?:build|setup) failed\]|^--- FAIL|^[\w./-]+\.go:\d+:\d+: \S|\b[1-9]\d* (?:failed|errors?)\b/m;
9418
+ GO_FAIL_EVIDENCE = /^FAIL\t\S+\t[\d.]+s$|^panic: /m;
9419
+ GO_PKG_RE = /^(?:ok|FAIL|\?)\s+\S+\s+(?:\(cached\)|[\d.]+s\b|\[(?:no test files|build failed|setup failed)\])/;
9420
+ LLM_JUDGES = new Set(["council", "xreview", "code_review", "devils_advocate", "magic_debate"]);
9421
+ NON_RUNNER = new Set(["lint", "typecheck", "holdout", "select-tests"]);
9422
+ SENSITIVE = [
9423
+ ["auth", /(^|\/)(auth|oauth|sso|session|login|permission|rbac|acl)[\w.-]*(\/|\.|$)/i],
9424
+ ["crypto", /(^|\/)(crypt|crypto|jwt|signing|keys?|secrets?|tokens?|credential|password)[\w.-]*(\/|\.|$)/i],
9425
+ ["payments", /(^|\/)(payments?|billing|checkout|stripe|invoice)[\w.-]*(\/|\.|$)/i],
9426
+ ["migration", /(^|\/)(migrations?|alembic)\/|\.sql$/i],
9427
+ ["ci", /(^|\/)(\.github\/(workflows|actions)|\.gitlab-ci\.yml|\.circleci|Jenkinsfile|azure-pipelines)/i],
9428
+ ["lockfile", /(^|\/)(package-lock\.json|pnpm-lock\.yaml|bun\.lockb?|go\.sum|[\w-]+\.lock)$/],
9429
+ ["infra", /(^|\/)(Dockerfile[\w.-]*|docker-compose[\w.-]*\.ya?ml|terraform|helm|k8s|kubernetes|infra|deploy)(\/|\.|$)|\.tf$/i],
9430
+ ["env", /(^|\/)\.env[\w.]*$/]
9431
+ ];
9432
+ API = /(^|\/)(openapi|swagger)[\w.-]*\.(ya?ml|json)$|\.proto$|\.d\.ts$|(^|\/)(api|routes|public)\/|(^|\/)index\.(ts|js|py)$/i;
9433
+ TEST_CFG = /(^|\/)(conftest\.py|\.?pytest\.(ini|toml)|tox\.ini|jest\.config\.[\w.]+|vitest\.config\.[\w.]+)$/;
9434
+ RANK2 = { low: 0, medium: 1, high: 2 };
9435
+ RAN_ALWAYS = ["targeted tests", "lint of changed files"];
9436
+ PLAN = {
9437
+ low: { selected: [...RAN_ALWAYS] },
9438
+ medium: { selected: [...RAN_ALWAYS, "full test command", "one reviewer"] },
9439
+ high: { selected: [...RAN_ALWAYS, "full test command", "full suite", "council", "security scan"] }
9544
9440
  };
9545
- for (let i = open + 1;i < close; ) {
9546
- const k = skipLiteral(h, i);
9547
- if (k !== i) {
9548
- i = k;
9549
- continue;
9441
+ LOW_SKIPPED = ["full test command", "reviewer", "council", "security scan"];
9442
+ MANIFEST_LOCKS = [
9443
+ [/(^|\/)package\.json$/, /(^|\/)(package-lock\.json|npm-shrinkwrap\.json|pnpm-lock\.yaml|yarn\.lock|bun\.lockb?)$/],
9444
+ [/(^|\/)pyproject\.toml$/, /(^|\/)(poetry\.lock|uv\.lock|pdm\.lock|Pipfile\.lock)$/],
9445
+ [/(^|\/)go\.mod$/, /(^|\/)go\.sum$/]
9446
+ ];
9447
+ PKG_DEP_KEYS = ["dependencies", "devDependencies", "peerDependencies", "optionalDependencies"];
9448
+ PYPROJECT_NONDEP = /^(name|version|description|readme|license|requires-python|python|authors|maintainers|keywords|classifiers|homepage|repository|documentation|packages|include|exclude)$/;
9449
+ });
9450
+
9451
+ // ../../loki-ts/src/util/receipt_signer.ts
9452
+ import { createHash as createHash8, randomBytes as randomBytes3, createPrivateKey, createPublicKey as createPublicKey2, generateKeyPairSync, sign, verify as verify2 } from "crypto";
9453
+ import { chmodSync as chmodSync4, linkSync, mkdirSync as mkdirSync13, readdirSync as readdirSync13, readFileSync as readFileSync29, renameSync as renameSync8, statSync as statSync14, unlinkSync as unlinkSync4, writeFileSync as writeFileSync16 } from "fs";
9454
+ import { homedir as homedir8 } from "os";
9455
+ import { spawnSync as spawnSync2 } from "child_process";
9456
+ import { dirname as dirname12, join as join38 } from "path";
9457
+ function canonicalJson(x) {
9458
+ const walk = (v) => {
9459
+ if (Array.isArray(v))
9460
+ return `[${v.map(walk).join(",")}]`;
9461
+ if (v !== null && typeof v === "object") {
9462
+ const o = v;
9463
+ return `{${Object.keys(o).filter((k) => o[k] !== undefined).sort().map((k) => `${JSON.stringify(k)}:${walk(o[k])}`).join(",")}}`;
9550
9464
  }
9551
- const c = h[i];
9552
- if ("([{".includes(c))
9553
- depth++;
9554
- else if (")]}".includes(c))
9555
- depth--;
9556
- else if (c === "," && depth === 0) {
9557
- take(i);
9558
- from = i + 1;
9465
+ return JSON.stringify(v ?? null);
9466
+ };
9467
+ return walk(x).replace(/[\u0080-\uFFFF]/g, (c) => `\\u${c.charCodeAt(0).toString(16).padStart(4, "0")}`);
9468
+ }
9469
+ function loadSigningKey(generate = true) {
9470
+ try {
9471
+ const inline = process.env["LOKI_RECEIPT_SIGNING_KEY"]?.trim();
9472
+ let pem = inline ?? "";
9473
+ if (!inline) {
9474
+ const given = process.env["LOKI_RECEIPT_SIGNING_KEY_FILE"]?.trim();
9475
+ const file = given || join38(process.env["HOME"] || homedir8(), ".loki", "keys", RECEIPT_SIGNER_BASENAME);
9476
+ try {
9477
+ pem = readFileSync29(file);
9478
+ if (!given) {
9479
+ for (const [f, m] of [[file, 384], [dirname12(file), 448]])
9480
+ if (statSync14(f).mode & 63)
9481
+ chmodSync4(f, m);
9482
+ }
9483
+ } catch (e) {
9484
+ if (e.code !== "ENOENT" || !generate)
9485
+ return null;
9486
+ mkdirSync13(dirname12(dirname12(file)), { recursive: true, mode: 448 });
9487
+ mkdirSync13(dirname12(file), { recursive: true, mode: 448 });
9488
+ const tmp = `${file}.${randomBytes3(6).toString("hex")}.tmp`;
9489
+ writeFileSync16(tmp, generateKeyPairSync("ed25519").privateKey.export({ type: "pkcs8", format: "pem" }), { flag: "wx", mode: 384 });
9490
+ try {
9491
+ linkSync(tmp, file);
9492
+ } catch (l) {
9493
+ if (l.code !== "EEXIST")
9494
+ throw l;
9495
+ } finally {
9496
+ unlinkSync4(tmp);
9497
+ }
9498
+ pem = readFileSync29(file);
9499
+ }
9559
9500
  }
9560
- i++;
9501
+ const k = createPrivateKey(pem);
9502
+ return k.asymmetricKeyType === "ed25519" ? k : null;
9503
+ } catch {
9504
+ return null;
9561
9505
  }
9562
- take(close);
9563
- return `${h.slice(0, open).trim()} { ${names.join(", ")} }`;
9564
9506
  }
9565
- function tsSignatures(src, jsx) {
9566
- const out2 = [];
9567
- for (const it of items(src)) {
9568
- const h = it.head.trim();
9569
- if (!/^export\b/.test(h))
9570
- continue;
9571
- const lines = [];
9572
- if (/^export\s+(declare\s+)?enum\b/.test(h)) {
9573
- lines.push(enumSig(h) ?? exportHead(h, it.end));
9574
- } else if (/^export\s+(declare\s+)?(type|interface)\b|^export\s*(type\s*)?[{*]/.test(h)) {
9575
- lines.push(exportHead(h, it.end));
9576
- } else if (/^export\s+(default\s+)?(abstract\s+)?class\b/.test(h)) {
9577
- const ms = jsx || !it.balanced ? [] : items(it.body ?? "");
9578
- const sigs = ms.map((m) => strictMember(m) ? memberSig(m.head.trim()) : null).map((m) => m === null ? null : maskDefaults(m));
9579
- const dropped = ms.some((m) => !strictMember(m)) || sigs.some((m) => m !== null && (hasTopSemi(m) || NOT_MEMBER.test(m)));
9580
- const keep = dropped ? [] : sigs.filter((m) => !!m);
9581
- const headM = maskDecorators(exportHead(h, ""));
9582
- const memM = keep.map(maskDecorators);
9583
- if (headM !== null && !memM.includes(null))
9584
- lines.push(`${maskExtends(headM)} {`, ...memM.map((m) => ` ${m};`), "}");
9585
- } else if (/^export\s+(default\s+)?@/.test(h)) {
9586
- const dm = maskDecorators(h);
9587
- if (dm !== null)
9588
- lines.push(maskExtends(dm));
9589
- } else if (/^export\s+default\s/.test(h)) {
9590
- const rest = h.replace(/^export\s+default\s+/, "");
9591
- const len = arrowHeadLen(rest);
9592
- if (len > 0)
9593
- lines.push(`export default ${rest.slice(0, len)}`);
9594
- else if (/^(async\s+)?function\b/.test(rest))
9595
- lines.push(memberSig(h) ?? h);
9596
- else
9597
- lines.push(/^[\w$.]+$/.test(rest) ? h : "export default ...");
9598
- } else if (/^export\s+(const|let|var)\b/.test(h)) {
9599
- lines.push(splitDeclarators(h).map((p) => memberSig(p.trim()) ?? p.trim()).join(", "));
9600
- } else {
9601
- lines.push(memberSig(h) ?? h);
9602
- }
9603
- if (!lines.length)
9604
- continue;
9605
- const masked = /^export\s+(declare\s+)?(type|interface|enum)\b|^export\s*(type\s*)?[{*]|^export\s+(default\s+)?(abstract\s+)?class\b/.test(h) ? lines : lines.map((l) => {
9606
- const d = maskDecorators(l);
9607
- return d === null ? null : maskDefaults(d);
9608
- });
9609
- if (masked.includes(null))
9610
- continue;
9611
- const done = masked;
9612
- if (!done.some(hasTopSemi))
9613
- out2.push(...done);
9507
+ function signReceipt(runId, hash) {
9508
+ const key = loadSigningKey();
9509
+ if (!key)
9510
+ return { jwt: null, kid: null };
9511
+ const kid = kidOf2(createPublicKey2(key));
9512
+ const input = `${b64u(canonicalJson({ alg: "EdDSA", typ: "JWT", kid }))}.${b64u(canonicalJson({ job_id: runId, run_id: runId, receipt_sha256: hash, iat: Math.floor(Date.now() / 1000) }))}`;
9513
+ return { jwt: `${input}.${b64u(sign(null, Buffer.from(input), key))}`, kid };
9514
+ }
9515
+ function toolVersion(bin) {
9516
+ try {
9517
+ const r = spawnSync2(bin, ["--version"], { env: plainTestEnv(), encoding: "utf8", timeout: 5000, killSignal: "SIGKILL", stdio: ["ignore", "pipe", "ignore"] });
9518
+ return r.status === 0 ? /\d+(\.\d+)+\S*/.exec(String(r.stdout))?.[0] ?? null : null;
9519
+ } catch {
9520
+ return null;
9614
9521
  }
9615
- return out2;
9616
9522
  }
9617
- var REGEX_PREV = "(,=:[!&|?{;+-*%~^", isComment = (s, i) => s[i] === "/" && (s[i + 1] === "/" || s[i + 1] === "*"), MODS = "(?:(?:public|private|protected|static|readonly|abstract|async|get|set|declare|override)\\s+)*", MEMBER_GRAMMAR, NOT_MEMBER, RESERVED_AFTER_DECORATOR;
9618
- var init_ts_signatures = __esm(() => {
9619
- MEMBER_GRAMMAR = new RegExp(`^(?:@[\\w$.]+(?:\\([^)]*\\))?\\s*)*${MODS}\\*?\\s*(?:[A-Za-z_$][\\w$]*|#[A-Za-z_$][\\w$]*|\\[[^\\]]*\\])\\s*[?!]?\\s*(?:[(<:=]|$)`);
9620
- NOT_MEMBER = /^(return|const|let|var|if|else|for|while|do|switch|case|default|throw|try|catch|finally|new|await|yield|break|continue|import|export|function|delete|typeof|void)\b/;
9621
- RESERVED_AFTER_DECORATOR = /^(?:if|else|for|while|do|switch|case|try|catch|finally|return|throw|with|var|let|const|function|new|delete|typeof|void|yield|await|break|continue|import|debugger|instanceof|this|super|null|true|false)(?![\w$])/;
9523
+ var RECEIPT_SIGNER_BASENAME = "receipt-ed25519.pem", b64u = (b) => Buffer.from(b).toString("base64url"), kidOf2 = (pub) => b64u(createHash8("sha256").update(`{"crv":"Ed25519","kty":"OKP","x":"${pub.export({ format: "jwk" }).x}"}`).digest()), TOOLCHAIN_KEYS, collectToolchain = (probe = toolVersion) => ({ bun: process.versions["bun"] ?? probe("bun"), node: probe("node"), python3: probe("python3"), git: probe("git") });
9524
+ var init_receipt_signer = __esm(() => {
9525
+ init_check_result();
9526
+ TOOLCHAIN_KEYS = ["bun", "node", "python3", "git"];
9622
9527
  });
9623
9528
 
9624
- // ../../loki-ts/src/util/failure_memory.ts
9625
- var init_failure_memory = __esm(() => {
9626
- init_cache();
9627
- init_safe_git();
9529
+ // ../../loki-ts/src/engine10/registry.ts
9530
+ var init_registry = () => {};
9531
+
9532
+ // ../../loki-ts/src/engine10/output.ts
9533
+ var init_output = __esm(() => {
9534
+ init_registry();
9535
+ init_redact();
9536
+ init_types();
9628
9537
  });
9629
9538
 
9630
- // ../../loki-ts/src/e10ext/repomemory.ts
9631
- var init_repomemory = __esm(() => {
9632
- init_cache();
9633
- init_failure_memory();
9539
+ // ../../loki-ts/src/e10ext/stop_restore.ts
9540
+ var init_stop_restore = __esm(() => {
9541
+ init_safe_git();
9634
9542
  });
9635
9543
 
9636
- // ../../loki-ts/src/engine10/failures.ts
9637
- function failCount(out2) {
9638
- const n2 = (s, re) => +(s.match(re)?.[1] ?? 0);
9639
- const l = out2.split(`
9640
- `).reverse().map((x) => x.trim()).find((x) => /^(?:=+ )?\d+ \w+.* in [\d.]+s|^Tests?:?\s+\d/.test(x)), m = /^(?:#|\u2139) fail (\d+)$/m.exec(out2);
9641
- return l ? n2(l, /(\d+) failed/) + n2(l, /(\d+) errors?/) : m ? +m[1] : null;
9642
- }
9643
- function failIds(output) {
9644
- const ids = [...new Set(output.split(`
9645
- `).map((l) => ID_LINE.exec(l.trim())).flatMap((m) => m ? [(m.slice(1).find(Boolean) ?? "").trim()] : []).filter(Boolean))];
9646
- const c = failCount(output);
9647
- return c && ids.length >= c ? ids : [];
9648
- }
9649
- var ID_LINE;
9650
- var init_failures = __esm(() => {
9651
- ID_LINE = /^(?:(?:FAILED|ERROR)\s+(\S+::\S*)(?:\s.*)?|\u25cf\s+(?!Console|Test suite failed)(.+)|not ok \d+ - (.+?)(?:\s+#.*)?|\u2716\s+(?!failing tests:)(.+?)(?:\s+\([\d.]+ms\))?|(?:FAIL|\u00d7)\s+(.+))$/;
9544
+ // ../../loki-ts/src/util/base_guard.ts
9545
+ var init_base_guard = __esm(() => {
9546
+ init_run_status();
9547
+ init_safe_git();
9652
9548
  });
9653
9549
 
9654
- // ../../loki-ts/src/project_model/api.ts
9655
- import { posix } from "path";
9656
- function projectApi(model) {
9657
- const known = isRec2(model) && model.status === "ok";
9658
- const packages = known && Array.isArray(model.packages) ? model.packages.filter(wellFormed) : [];
9659
- const packageOf = (file) => {
9660
- const f = clean(file);
9661
- if (f === "")
9662
- return null;
9663
- let best = null;
9664
- for (const p of packages) {
9665
- const hit = p.root === "." || f === p.root || f.startsWith(`${p.root}/`);
9666
- if (hit && (best === null || p.root !== "." && (best.root === "." || p.root.length > best.root.length)))
9667
- best = p;
9668
- }
9669
- return best;
9670
- };
9671
- return {
9672
- model,
9673
- known: () => known,
9674
- packages: () => packages,
9675
- workspaceKind: () => isRec2(model) && typeof model.workspaceKind === "string" ? model.workspaceKind : "unknown",
9676
- packageRootOf: (file) => packageOf(file)?.root ?? null,
9677
- packageOf,
9678
- relativeToRoot: (file) => {
9679
- const p = packageOf(file);
9680
- return p ? p.root === "." ? clean(file) : clean(file).slice(p.root.length + 1) : null;
9681
- },
9682
- commandFor: (pkg, kind) => (packages.find((p) => p.root === clean(pkg) || p.name === pkg) ?? null)?.commands?.[kind] ?? null,
9683
- dependsOn: (pkg) => {
9684
- const d = (packages.find((p) => p.root === clean(pkg) || p.name === pkg) ?? null)?.dependsOn;
9685
- return Array.isArray(d) ? d.filter((x) => typeof x === "string") : null;
9686
- },
9687
- installFor: (pkg) => {
9688
- const i = (packages.find((p) => p.root === clean(pkg) || p.name === pkg) ?? null)?.install;
9689
- return isRec2(i) && typeof i.cmd === "string" ? i : null;
9690
- },
9691
- hasUI: () => packages.some((p) => isRec2(p.ui) && p.ui.present === true),
9692
- uiBoot: () => {
9693
- for (const p of packages)
9694
- if (isRec2(p.ui) && p.ui.present === true && isRec2(p.ui.boot))
9695
- return { pkg: p, boot: p.ui.boot };
9550
+ // ../../loki-ts/src/features/speed/unit_mode.ts
9551
+ import { closeSync as closeSync7, constants, fstatSync as fstatSync5, lstatSync as lstatSync8, openSync as openSync7, readSync as readSync6, realpathSync as realpathSync17 } from "fs";
9552
+ function readBounded(path) {
9553
+ const l = lstatSync8(path);
9554
+ if (l.isSymbolicLink() || !l.isFile() || l.size > MAX_SPEC_BYTES)
9555
+ return null;
9556
+ const fd = openSync7(path, constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK);
9557
+ try {
9558
+ const st = fstatSync5(fd);
9559
+ if (!st.isFile() || st.size > MAX_SPEC_BYTES)
9696
9560
  return null;
9561
+ const buf = Buffer.alloc(st.size + 1);
9562
+ let n2 = 0;
9563
+ for (;; ) {
9564
+ const r = readSync6(fd, buf, n2, buf.length - n2, null);
9565
+ if (r <= 0)
9566
+ break;
9567
+ n2 += r;
9568
+ if (n2 >= buf.length)
9569
+ return null;
9697
9570
  }
9698
- };
9571
+ return buf.subarray(0, n2).toString("utf8");
9572
+ } finally {
9573
+ closeSync7(fd);
9574
+ }
9699
9575
  }
9700
- var clean = (p) => typeof p === "string" ? posix.normalize(p.replace(/\\/g, "/")).replace(/^\.\//, "") : "", isRec2 = (v) => typeof v === "object" && v !== null, wellFormed = (p) => isRec2(p) && typeof p.root === "string" && typeof p.name === "string";
9701
- var init_api = () => {};
9702
-
9703
- // ../../loki-ts/src/project_model/resolve.ts
9704
- import { join as join36 } from "path";
9705
- function loadProjectApi(repoDir, env = process.env) {
9706
- if (!projectModelEnabled(env))
9707
- return null;
9576
+ function parseText(txt) {
9708
9577
  try {
9709
- const model = loadCached(repoDir);
9710
- const api = model ? projectApi(model) : null;
9711
- return api && api.known() ? api : null;
9578
+ const j = JSON.parse(txt), ws = strs(j.writeSet), pack = strs(j.pack), tb = j.tokenBudget;
9579
+ if (typeof j.id !== "string" || !j.id || !ws || ws.length === 0 || !pack || typeof tb !== "number" || !Number.isFinite(tb) || !(tb > 0) || tb > MAX_TOKEN_BUDGET)
9580
+ return null;
9581
+ const acc = j.acceptance === undefined ? [] : strs(j.acceptance);
9582
+ if (acc === null)
9583
+ return null;
9584
+ return { id: j.id, writeSet: ws, pack, tokenBudget: tb, ...acc.length ? { acceptance: acc.map((a) => a.slice(0, 500)).slice(0, 50) } : {} };
9712
9585
  } catch {
9713
9586
  return null;
9714
9587
  }
9715
9588
  }
9716
- function isMultiRoot(api) {
9717
- return !!api && api.known() && api.packages().some((p) => p.root !== ".");
9718
- }
9719
- function siteFor(api, repoDir, file) {
9720
- if (!isMultiRoot(api))
9721
- return null;
9722
- const pkg = api.packageOf(file);
9723
- const rel = api.relativeToRoot(file);
9724
- if (!pkg || pkg.root === "." || rel === null)
9589
+ function parseSpec(path) {
9590
+ try {
9591
+ const txt = readBounded(path);
9592
+ return txt === null ? null : parseText(txt);
9593
+ } catch {
9725
9594
  return null;
9726
- return { pkg, root: pkg.root, cwd: join36(repoDir, pkg.root), file: rel };
9727
- }
9728
- function groupByPackage(api, repoDir, files) {
9729
- const out2 = new Map;
9730
- for (const f of files) {
9731
- const s = siteFor(api, repoDir, f);
9732
- const root = s?.root ?? ".";
9733
- const g = out2.get(root) ?? { root, cwd: s?.cwd ?? repoDir, files: [], rel: [] };
9734
- g.files.push(f);
9735
- g.rel.push(s?.file ?? f);
9736
- out2.set(root, g);
9737
9595
  }
9738
- return [...out2.values()];
9739
9596
  }
9740
- var init_resolve = __esm(() => {
9741
- init_discover2();
9742
- init_api();
9743
- });
9744
- // ../../loki-ts/src/project_model/scope.ts
9745
- function scopedOutOf(map) {
9746
- return [...dropped.get(map) ?? []].sort();
9597
+ function unitSpec(env = process.env) {
9598
+ const path = env["LOKI_UNIT_SPEC"], frozen = env["LOKI_UNIT_SPEC_FROZEN"];
9599
+ if (env["LOKI_SPEED"] === "0" || !path)
9600
+ return null;
9601
+ if (frozen !== undefined)
9602
+ return frozen.length <= MAX_SPEC_BYTES ? parseText(frozen) : null;
9603
+ if (!memo.has(path))
9604
+ memo.set(path, parseSpec(path));
9605
+ return memo.get(path) ?? null;
9747
9606
  }
9748
- var apis, imports, dropped;
9749
- var init_scope = __esm(() => {
9750
- init_resolve();
9751
- apis = new WeakMap;
9752
- imports = new WeakMap;
9753
- dropped = new WeakMap;
9754
- });
9755
-
9756
- // ../../loki-ts/src/util/test_file_text.ts
9757
- import { basename as basename9 } from "path";
9758
- function isTestFile(rel) {
9759
- const name = basename9(rel);
9760
- return JS_TEST_RE.test(name) || PY_TEST_RE.test(name) || GO_TEST_RE.test(name);
9607
+ function unitFlags(staged, env = process.env, pre = {}) {
9608
+ const s = unitSpec(env);
9609
+ return s ? staged.filter(({ f }) => !inWriteSet(s, f) && !Object.hasOwn(pre, f)).map(({ f }) => f) : null;
9761
9610
  }
9762
- var JS_TEST_RE, PY_TEST_RE, GO_TEST_RE;
9763
- var init_test_file_text = __esm(() => {
9764
- JS_TEST_RE = /\.(test|spec)\.[cm]?[jt]sx?$/;
9765
- PY_TEST_RE = /^(test_.+|.+_test)\.py$/;
9766
- GO_TEST_RE = /_test\.go$/;
9611
+ var MAX_SPEC_BYTES, MAX_TOKEN_BUDGET = 1e9, norm2 = (p) => p.replace(/^\.\//, ""), CTRL, strs = (v) => Array.isArray(v) && v.every((x) => typeof x === "string" && !CTRL.test(x)) ? v.map(norm2) : null, memo, inWriteSet = (spec, f) => spec.writeSet.some((w) => w.endsWith("/") ? f.startsWith(w) : f === w);
9612
+ var init_unit_mode = __esm(() => {
9613
+ MAX_SPEC_BYTES = 1024 * 1024;
9614
+ CTRL = /[\x00-\x1f\x7f]/;
9615
+ memo = new Map;
9767
9616
  });
9768
9617
 
9769
- // ../../loki-ts/src/engine10/testmap.ts
9770
- import { existsSync as existsSync22, readdirSync as readdirSync13, readFileSync as readFileSync28, realpathSync as realpathSync16 } from "fs";
9771
- import { basename as basename10, extname, isAbsolute as isAbsolute2, join as join37, relative, sep as sep10 } from "path";
9772
- function projectInterpreter(root) {
9773
- const ve = process.env["VIRTUAL_ENV"], inRepo = (p) => {
9774
- try {
9775
- const r = relative(realpathSync16(root), realpathSync16(p));
9776
- return r !== "" && !r.startsWith("..") && !isAbsolute2(r);
9777
- } catch {
9778
- return false;
9779
- }
9780
- };
9781
- return [join37(root, ".venv", "bin", "python"), join37(root, "venv", "bin", "python"), ...ve && inRepo(ve) ? [join37(ve, "bin", "python")] : []].some(existsSync22) ? "project" : "system";
9618
+ // ../../loki-ts/src/util/ts_signatures.ts
9619
+ function templateEnd(s, i) {
9620
+ for (let j = i + 1;j < s.length; ) {
9621
+ const c = s[j];
9622
+ if (c === "\\")
9623
+ j += 2;
9624
+ else if (c === "`")
9625
+ return j + 1;
9626
+ else if (c === "$" && s[j + 1] === "{") {
9627
+ let depth = 1;
9628
+ j += 2;
9629
+ while (j < s.length && depth > 0) {
9630
+ const k = skipLiteral(s, j);
9631
+ if (k !== j) {
9632
+ j = k;
9633
+ continue;
9634
+ }
9635
+ if (s[j] === "{")
9636
+ depth++;
9637
+ else if (s[j] === "}")
9638
+ depth--;
9639
+ j++;
9640
+ }
9641
+ } else
9642
+ j++;
9643
+ }
9644
+ return s.length;
9782
9645
  }
9783
- var SKIP_DIRS;
9784
- var init_testmap = __esm(() => {
9785
- init_resolve();
9786
- init_scope();
9787
- init_test_file_text();
9788
- SKIP_DIRS = new Set([
9789
- ".git",
9790
- "node_modules",
9791
- "dist",
9792
- "build",
9793
- "target",
9794
- "coverage",
9795
- ".venv",
9796
- "venv",
9797
- "__pycache__",
9798
- ".tox",
9799
- ".pytest_cache",
9800
- ".loki",
9801
- ".next"
9802
- ]);
9803
- });
9804
-
9805
- // ../../loki-ts/src/util/check_result.ts
9806
- import { existsSync as existsSync23, mkdirSync as mkdirSync13, readFileSync as readFileSync29, realpathSync as realpathSync17, renameSync as renameSync8, writeFileSync as writeFileSync16 } from "fs";
9807
- import { basename as basename11, isAbsolute as isAbsolute3, join as join38 } from "path";
9808
- function plainTestEnv(base = process.env) {
9809
- const e = {};
9810
- for (const [k, v] of Object.entries(tokenFreeEnv(base)))
9811
- if (typeof v === "string")
9812
- e[k] = v;
9813
- e["FORCE_COLOR"] = "0";
9814
- e["NO_COLOR"] = "1";
9815
- delete e["CLICOLOR_FORCE"];
9816
- return e;
9646
+ function commentBefore(s, i) {
9647
+ let p = i - 1;
9648
+ while (p >= 0 && /\s/.test(s[p]))
9649
+ p--;
9650
+ if (p < 1)
9651
+ return false;
9652
+ if (s[p] === "/" && s[p - 1] === "*")
9653
+ return true;
9654
+ return s.slice(s.lastIndexOf(`
9655
+ `, p) + 1, p + 1).includes("//");
9817
9656
  }
9818
- function ran(raw2, path, ok) {
9819
- const out2 = stripAnsi(raw2);
9820
- const n2 = (s, re) => +(s.match(re)?.[1] ?? 0);
9821
- const blk = out2.trimEnd().match(/(?:^|\n)((?:(?:#|\u2139) \w+ [\d.]+(?:\n|$)){5,})$/)?.[1];
9822
- if (blk) {
9823
- const c = n2(blk, /(?:#|\u2139) pass (\d+)/) + n2(blk, /(?:#|\u2139) fail (\d+)/);
9824
- const nm = out2.match(/^(?:ok \d+ - |\u2714 )(\S+\.[cm]?[jt]s)(?: \(|$)/m)?.[1];
9825
- return c === 1 && nm && (!path || basename11(nm) === basename11(path)) ? 0 : c;
9657
+ function regexEnd(s, i) {
9658
+ if (commentBefore(s, i))
9659
+ return i;
9660
+ let p = i - 1;
9661
+ while (p >= 0 && /\s/.test(s[p]))
9662
+ p--;
9663
+ if (p >= 1 && (s[p] === "+" || s[p] === "-") && s[p - 1] === s[p])
9664
+ return i;
9665
+ if (p >= 0 && !REGEX_PREV.includes(s[p]) && !/\b(return|typeof|case|in|of|void|delete|throw)$/.test(s.slice(Math.max(0, p - 7), p + 1)))
9666
+ return i;
9667
+ let inClass = false;
9668
+ for (let j = i + 1;j < s.length; j++) {
9669
+ const c = s[j];
9670
+ if (c === `
9671
+ `)
9672
+ return i;
9673
+ if (c === "\\")
9674
+ j++;
9675
+ else if (c === "[")
9676
+ inClass = true;
9677
+ else if (c === "]")
9678
+ inClass = false;
9679
+ else if (c === "/" && !inClass) {
9680
+ j++;
9681
+ while (j < s.length && /[a-z]/i.test(s[j]))
9682
+ j++;
9683
+ return j;
9684
+ }
9826
9685
  }
9827
- const cg = cargoTrailer(out2);
9828
- if (cg.length)
9829
- return cg.reduce((t, l2) => t + n2(l2, /(\d+) passed/) + n2(l2, /(\d+) failed/), 0);
9830
- const g = goCount(out2, ok);
9831
- if (g !== undefined)
9832
- return g;
9833
- const tail = tailLines(out2);
9834
- const l = tail.filter((x) => /^(?:=+ )?(?:\d+ \w+.*|no tests ran) in [\d.]+s|^\s*Tests?:?\s+\d|^No tests found/.test(x) && (!/^\s*Tests?:?\s+\d/.test(x) || tail.some((y) => /^\s*Test (?:Files|Suites):?\s+\d/.test(y)))).pop();
9835
- if (!l)
9836
- return null;
9837
- return /^(?:=+ )?no tests (?:ran|found)|^No tests found|skipped/i.test(l) || /\d+ (?:passed|failed|errors?)/.test(l) ? n2(l, /(\d+) passed/) + n2(l, /(\d+) failed/) + n2(l, /(\d+) errors?/) : null;
9686
+ return i;
9838
9687
  }
9839
- function cargoTrailer(out2) {
9840
- const ls = out2.split(`
9841
- `).filter((x) => x.trim());
9842
- while (ls.length && /^error: /.test(ls[ls.length - 1]))
9843
- ls.pop();
9844
- const segs = [];
9845
- for (const l of ls) {
9846
- if (/^running \d+ tests?\b/.test(l))
9847
- segs.push([]);
9848
- else if (segs.length && !/^\s*(?:Running |Doc-tests |Doctest)/.test(l))
9849
- segs[segs.length - 1].push(l);
9850
- }
9851
- const lastOf = (g) => g[g.length - 1]?.startsWith("test result: ") ? g[g.length - 1] : undefined;
9852
- if (!segs.length) {
9853
- const g = [];
9854
- for (let k = ls.length - 1;k >= 0 && ls[k].startsWith("test result: "); k--)
9855
- g.push(ls[k]);
9856
- return g;
9688
+ function stripComments(t) {
9689
+ let out2 = "";
9690
+ for (let i = 0;i < t.length; ) {
9691
+ if (isComment(t, i)) {
9692
+ const e = t[i + 1] === "/" ? t.indexOf(`
9693
+ `, i) < 0 ? t.length : t.indexOf(`
9694
+ `, i) : t.indexOf("*/", i + 2) < 0 ? t.length : t.indexOf("*/", i + 2) + 2;
9695
+ out2 += t[i + 1] === "/" ? "" : " ";
9696
+ i = e;
9697
+ continue;
9698
+ }
9699
+ const c = t[i];
9700
+ const k = c === '"' || c === "'" || c === "`" ? skipLiteral(t, i) : i;
9701
+ if (k !== i) {
9702
+ out2 += t.slice(i, k);
9703
+ i = k;
9704
+ continue;
9705
+ }
9706
+ out2 += c;
9707
+ i++;
9857
9708
  }
9858
- if (!lastOf(segs[segs.length - 1]))
9859
- return [];
9860
- return segs.flatMap((g) => lastOf(g) ?? []);
9861
- }
9862
- function bunCount(out2) {
9863
- const ls = out2.split(`
9864
- `).filter((x) => x.trim());
9865
- if (!/^Ran \d+ tests? across \d+ files?\./.test(ls[ls.length - 1] ?? ""))
9866
- return null;
9867
- const blk = ls.slice(-9, -1);
9868
- const last = (k) => +(blk.map((x) => x.match(new RegExp(`^\\s*(\\d+) ${k}\\s*$`))?.[1]).filter(Boolean).pop() ?? 0);
9869
- return last("pass") + last("fail");
9709
+ return out2;
9870
9710
  }
9871
- function unittestCount(out2, ok) {
9872
- const ls = out2.split(`
9873
- `).filter((x) => x.trim()), v = ls[ls.length - 1] ?? "", r = ls[ls.length - 2]?.match(/^Ran (\d+) tests? in [\d.]+s$/);
9874
- if (!r || !/^(?:OK|FAILED)\b/.test(v))
9875
- return null;
9876
- if (ls.filter((x) => /^Ran \d+ tests? in [\d.]+s$/.test(x)).length > 1)
9877
- return null;
9878
- if (ok === true && v.startsWith("FAILED") || ok === false && v.startsWith("OK"))
9879
- return null;
9880
- return Math.max(0, +r[1] - +(v.match(/skipped=(\d+)/)?.[1] ?? 0) - +(v.match(/expected failures=(\d+)/)?.[1] ?? 0));
9711
+ function skipLiteral(s, i) {
9712
+ const c = s[i];
9713
+ if (c === "/" && s[i + 1] === "/") {
9714
+ const n2 = s.indexOf(`
9715
+ `, i);
9716
+ return n2 < 0 ? s.length : n2;
9717
+ }
9718
+ if (c === "/" && s[i + 1] === "*") {
9719
+ const n2 = s.indexOf("*/", i + 2);
9720
+ return n2 < 0 ? s.length : n2 + 2;
9721
+ }
9722
+ if (c === "/")
9723
+ return regexEnd(s, i);
9724
+ if (c === "`")
9725
+ return templateEnd(s, i);
9726
+ if (c !== '"' && c !== "'")
9727
+ return i;
9728
+ for (let j = i + 1;j < s.length; j++) {
9729
+ if (s[j] === "\\")
9730
+ j++;
9731
+ else if (s[j] === c)
9732
+ return j + 1;
9733
+ else if (s[j] === `
9734
+ `)
9735
+ return i;
9736
+ }
9737
+ return s.length;
9881
9738
  }
9882
- function goCount(out2, ok) {
9883
- const lines = out2.split(`
9884
- `);
9885
- if (!lines.some((x) => GO_PKG_RE.test(x)))
9886
- return;
9887
- 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));
9888
- for (const x of lines) {
9889
- const r = x.match(/^=== RUN\s+(\S+)/), m = x.match(/^--- (PASS|FAIL|SKIP): (\S+)/);
9890
- if (r)
9891
- seen.add(r[1]);
9892
- else if (m && (seen.has(m[2]) || m[1] === "FAIL" && failPkg)) {
9893
- last.set(m[2], m[1]);
9894
- if (m[1] === "SKIP")
9895
- skip.add(m[2]);
9739
+ function matchingBrace(s, open) {
9740
+ let depth = 0;
9741
+ for (let i = open;i < s.length; ) {
9742
+ const j = skipLiteral(s, i);
9743
+ if (j !== i) {
9744
+ i = j;
9745
+ continue;
9896
9746
  }
9747
+ if (s[i] === "{")
9748
+ depth++;
9749
+ else if (s[i] === "}" && --depth === 0)
9750
+ return i;
9751
+ i++;
9897
9752
  }
9898
- for (const k of skip)
9899
- last.set(k, "SKIP");
9900
- if (ok === true && [...last.values()].includes("FAIL"))
9901
- return null;
9902
- const t = [...last.values()].filter((v) => v !== "SKIP").length;
9903
- if (t > 0)
9904
- return t;
9905
- if (lines.some((x) => /^(?:=== RUN|PASS$|FAIL$|testing: warning: no tests to run)/.test(x)))
9906
- return 0;
9907
- return lines.filter((x) => GO_PKG_RE.test(x)).every((x) => /\[no tests? (?:files|to run)\]/.test(x)) ? 0 : null;
9753
+ return s.length;
9908
9754
  }
9909
- function goRunner(cmd, args) {
9910
- const segs = args ? [[cmd, ...args]] : cmd.split(/&&|\|\||[;|\n]/).map((x) => x.trim().split(/\s+/).filter(Boolean));
9911
- for (const t0 of segs) {
9912
- const t = t0.map((x) => x.replace(/^["']|["']$/g, ""));
9913
- let k = 0;
9914
- for (;k < t.length; k++) {
9915
- const x = t[k];
9916
- if (/^[A-Za-z_]\w*=/.test(x) || /^(?:env|command|exec|time|nohup)$/.test(basename11(x)))
9917
- continue;
9918
- if (/^-/.test(x) && k > 0 && basename11(t[k - 1]) === "env") {
9919
- if (x === "-u" || x === "-C" || x === "-S")
9920
- k++;
9755
+ function items(src) {
9756
+ const out2 = [];
9757
+ let start = 0, head = "", p = 0, i = 0;
9758
+ const push = (end, body, stop, balanced = true) => {
9759
+ if (head.trim())
9760
+ out2.push({ head, raw: src.slice(start, stop).trim(), end, body, balanced });
9761
+ head = "";
9762
+ p = 0;
9763
+ start = stop;
9764
+ };
9765
+ while (i < src.length) {
9766
+ const j = skipLiteral(src, i);
9767
+ if (j !== i) {
9768
+ if (!isComment(src, i))
9769
+ head += src.slice(i, j);
9770
+ i = j;
9771
+ continue;
9772
+ }
9773
+ const c = src[i];
9774
+ if (c === "(" || c === "[")
9775
+ p++;
9776
+ else if (c === ")" || c === "]")
9777
+ p--;
9778
+ if (c === "{" && p === 0) {
9779
+ const close = matchingBrace(src, i);
9780
+ const h = head.trim();
9781
+ const declaration = /^(export\s+)?(declare\s+)?(type|interface|enum)\b/.test(h);
9782
+ if (declaration || h === "" || /^export(\s+type)?$/.test(h) || /[:|&]$/.test(h)) {
9783
+ head += stripComments(src.slice(i, close + 1));
9784
+ i = close + 1;
9785
+ if (declaration && !/^(export\s+)?(declare\s+)?type\b/.test(h))
9786
+ push("", null, i);
9921
9787
  continue;
9922
9788
  }
9923
- break;
9789
+ const body = src.slice(i + 1, close);
9790
+ const balanced = close < src.length;
9791
+ i = close + 1;
9792
+ if (src[i] === ";")
9793
+ i++;
9794
+ push("{", body, i, balanced);
9795
+ continue;
9924
9796
  }
9925
- if (t[k] !== undefined && basename11(t[k]) === "go")
9926
- return { runner: "go" };
9927
- }
9928
- return {};
9929
- }
9930
- function goJson(out2) {
9931
- let any = false, failed2 = false;
9932
- for (const x of out2.split(`
9933
- `)) {
9934
- if (!x.trim() || /^go: /.test(x))
9797
+ if (c === ";" && p === 0) {
9798
+ i++;
9799
+ push(";", null, i);
9935
9800
  continue;
9936
- let e;
9937
- try {
9938
- e = JSON.parse(x);
9939
- } catch {
9940
- return null;
9941
9801
  }
9942
- if (!e || typeof e !== "object" || typeof e.Action !== "string")
9943
- return null;
9944
- any = true;
9945
- if (e.Action === "fail")
9946
- failed2 = true;
9802
+ if (c === `
9803
+ ` && p === 0 && head.trim() && /^\s*(export|import)\b/.test(src.slice(i + 1, i + 40))) {
9804
+ push("", null, i);
9805
+ i++;
9806
+ start = i;
9807
+ continue;
9808
+ }
9809
+ head += c;
9810
+ i++;
9947
9811
  }
9948
- return any ? { failed: failed2 } : null;
9949
- }
9950
- function summaryPassFailCount(out2) {
9951
- return ["pass", "fail"].reduce((t, k) => t + +([...out2.matchAll(new RegExp(`^(?:#|\\u2139) ${k} (\\d+)$`, "gm"))].pop()?.[1] ?? 0), 0);
9952
- }
9953
- function skipped(raw2) {
9954
- return stripAnsi(raw2).split(`
9955
- `).filter((l) => /^(?:=+ )?\d+ \w+.* in [\d.]+s|^\s*Tests?:?\s+\d|^(?:#|\u2139) skipped \d/.test(l.trim())).reduce((t, l) => t + [...l.matchAll(/(\d+) (?:skipped|deselected|xfailed)|skipped (\d+)/g)].reduce((u, m) => u + +(m[1] ?? m[2]), 0), 0);
9812
+ push("", null, src.length);
9813
+ return out2;
9956
9814
  }
9957
- function testCount(raw2, path, ok) {
9958
- const out2 = stripAnsi(raw2);
9959
- const bu = bunCount(out2), ut = unittestCount(out2, ok);
9960
- if (bu !== null)
9961
- return bu;
9962
- if (ut !== null)
9963
- return ut;
9964
- if (/^\s*(?:Test Files\s+0\b|No test files found)/m.test(out2) && !/^\s*Tests?\s+\d/m.test(out2))
9965
- return 0;
9966
- const r = ran(out2, path, ok);
9967
- if (r !== null)
9968
- return r;
9969
- const tl = tailLines(out2);
9970
- const pw = tl.filter((x) => /^\s*\d+ (?:passed|failed|flaky)\b/.test(x));
9971
- if (pw.length && tl.some((x) => /^\s*\d+ passed \([\d.]+m?s\)/.test(x) || /^\s*\d+ failed$/.test(x)))
9972
- return pw.reduce((t, x) => t + +x.trim().split(" ")[0], 0);
9973
- const lastNum = (re) => {
9974
- const m = [...out2.matchAll(re)].pop();
9975
- return m ? +m[1] : null;
9815
+ function arrowHeadLen(s) {
9816
+ const n2 = s.length;
9817
+ let i = 0;
9818
+ const ws = () => {
9819
+ while (i < n2 && /\s/.test(s[i]))
9820
+ i++;
9976
9821
  };
9977
- const mp = lastNum(/^\s*(\d+) passing\b/gm), mf = lastNum(/^\s*(\d+) failing\b/gm);
9978
- if (mp !== null || mf !== null)
9979
- return (mp ?? 0) + (mf ?? 0);
9980
- return null;
9981
- }
9982
- function classifyCheck(i) {
9983
- if (i.missing)
9984
- return { result: "not_run", reason: "tool not found on PATH" };
9985
- if (i.cut)
9986
- return { result: "not_run", reason: "timed out or aborted" };
9987
- if (i.kind === "static")
9988
- return { result: i.ok ? "pass" : "fail" };
9989
- const text2 = stripAnsi(i.out);
9990
- if (i.runner === "go") {
9991
- if (i.ok)
9992
- return { result: "not_run", reason: GO_EXIT0_REASON };
9993
- const j = goJson(text2);
9994
- if (j)
9995
- return j.failed ? { result: "fail" } : { result: "not_run", reason: UNCONFIRMED_REASON };
9996
- return FAIL_EVIDENCE.test(text2) || GO_FAIL_EVIDENCE.test(text2) ? { result: "fail" } : { result: "not_run", reason: UNCONFIRMED_REASON };
9822
+ if (/^async\s/.test(s)) {
9823
+ i = 5;
9824
+ ws();
9997
9825
  }
9998
- const n2 = testCount(i.out, i.path, i.ok);
9999
- if (!i.ok && FAIL_EVIDENCE.test(text2))
10000
- return { result: "fail", ...n2 !== null ? { n: n2 } : {} };
10001
- if (n2 === 0)
10002
- return { result: "not_run", n: 0, reason: `${NO_TESTS_REASON} (ran 0 tests, empty or all skipped)` };
10003
- if (!i.ok)
10004
- return { result: "fail", ...n2 !== null ? { n: n2 } : {} };
10005
- if (n2 === null)
10006
- return { result: "not_run", reason: UNMEASURED_REASON };
10007
- return { result: "pass", n: n2 };
9826
+ if (s[i] === "<") {
9827
+ let d2 = 0;
9828
+ for (;i < n2; i++) {
9829
+ if (s[i] === "=" && s[i + 1] === ">")
9830
+ i++;
9831
+ else if (s[i] === "<")
9832
+ d2++;
9833
+ else if (s[i] === ">" && --d2 === 0) {
9834
+ i++;
9835
+ break;
9836
+ }
9837
+ }
9838
+ if (d2 !== 0)
9839
+ return -1;
9840
+ ws();
9841
+ }
9842
+ if (s[i] === "(") {
9843
+ let d2 = 0;
9844
+ for (;i < n2; ) {
9845
+ const k = skipLiteral(s, i);
9846
+ if (k !== i) {
9847
+ i = k;
9848
+ continue;
9849
+ }
9850
+ if (s[i] === "(")
9851
+ d2++;
9852
+ else if (s[i] === ")" && --d2 === 0) {
9853
+ i++;
9854
+ break;
9855
+ }
9856
+ i++;
9857
+ }
9858
+ if (d2 !== 0)
9859
+ return -1;
9860
+ } else {
9861
+ const st = i;
9862
+ while (i < n2 && /[\w$]/.test(s[i]))
9863
+ i++;
9864
+ if (i === st)
9865
+ return -1;
9866
+ }
9867
+ ws();
9868
+ if (s[i] === "=" && s[i + 1] === ">")
9869
+ return i + 2;
9870
+ if (s[i] !== ":")
9871
+ return -1;
9872
+ let d = 0;
9873
+ for (i++;i < n2; ) {
9874
+ const k = skipLiteral(s, i);
9875
+ if (k !== i) {
9876
+ i = k;
9877
+ continue;
9878
+ }
9879
+ const c = s[i];
9880
+ if ("([{".includes(c))
9881
+ d++;
9882
+ else if (")]}".includes(c))
9883
+ d--;
9884
+ else if (d === 0 && c === "=")
9885
+ return s[i + 1] === ">" ? i + 2 : -1;
9886
+ else if (d === 0 && c === ";")
9887
+ return -1;
9888
+ i++;
9889
+ }
9890
+ return -1;
10008
9891
  }
10009
- function checkProvenance(name, ctx) {
10010
- const head = name.split(":")[0];
10011
- if (LLM_JUDGES.has(head))
10012
- return "llm_judge";
10013
- if (head === "holdout")
10014
- return "independent";
10015
- if (!NON_RUNNER.has(head) && name.includes(":")) {
10016
- const p = normPath(name.slice(name.indexOf(":") + 1));
10017
- if (ctx.wallFiles.some((w) => normPath(w) === p))
10018
- return "independent";
10019
- if (ctx.addedFiles.some((a) => normPath(a) === p))
10020
- return "agent_authored";
10021
- if (ctx.modifiedFiles?.some((m) => normPath(m) === p))
10022
- return "agent_authored";
9892
+ function memberSig(h) {
9893
+ if (!h || /^(private|protected|#)/.test(h))
9894
+ return null;
9895
+ let depth = 0, angle = 0;
9896
+ for (let i = 0;i < h.length; i++) {
9897
+ const c = h[i];
9898
+ if ("([{".includes(c))
9899
+ depth++;
9900
+ else if (")]}".includes(c))
9901
+ depth--;
9902
+ else if (depth === 0 && c === "<")
9903
+ angle++;
9904
+ else if (depth === 0 && c === ">" && h[i - 1] !== "=" && angle > 0)
9905
+ angle--;
9906
+ else if (c === "=" && depth === 0 && angle === 0 && !"=!<".includes(h[i - 1] ?? " ") && h[i + 1] !== "=" && h[i + 1] !== ">") {
9907
+ const rhs = h.slice(i + 1).trim();
9908
+ if (rhs.endsWith("=>") || /^(async\s+)?function\b/.test(rhs))
9909
+ return h;
9910
+ const len = arrowHeadLen(rhs);
9911
+ return `${h.slice(0, i).trim()}${len > 0 ? ` = ${rhs.slice(0, len)}` : ""}`;
9912
+ }
10023
9913
  }
10024
- return "exogenous";
10025
- }
10026
- function summaryOf(a) {
10027
- const why = a.tier === a.computed ? a.reasons.slice(0, 2).join("; ") : `computed ${a.computed}; override ${a.override?.from} to ${a.override?.to}: ${a.override?.reason}`;
10028
- const def = a.checks_deferred;
10029
- return `risk: ${a.tier} (${why}), ${def.length ? def.join(", ") : "no deeper checks"} not run`;
9914
+ return h;
10030
9915
  }
10031
- function markExecuted(a, executed) {
10032
- const checks_deferred = deferredOf(a.tier, a.checks_selected, executed);
10033
- return { ...a, checks_deferred, summary: summaryOf({ ...a, checks_deferred }) + (a.advisories?.length ? `; advisory: ${a.advisories.join(", ")}` : "") };
9916
+ function splitDeclarators(h) {
9917
+ const parts = [];
9918
+ let depth = 0, angle = 0, init = false, from = 0;
9919
+ for (let i = 0;i < h.length; ) {
9920
+ const k = skipLiteral(h, i);
9921
+ if (k !== i) {
9922
+ i = k;
9923
+ continue;
9924
+ }
9925
+ const c = h[i];
9926
+ if ("([{".includes(c))
9927
+ depth++;
9928
+ else if (")]}".includes(c))
9929
+ depth--;
9930
+ else if (depth === 0 && !init && c === "<")
9931
+ angle++;
9932
+ else if (depth === 0 && !init && c === ">" && h[i - 1] !== "=")
9933
+ angle = Math.max(0, angle - 1);
9934
+ else if (depth === 0 && angle === 0 && !init && c === "=" && h[i + 1] !== "=" && h[i + 1] !== ">" && !"=!<".includes(h[i - 1] ?? " ")) {
9935
+ init = true;
9936
+ const rest = h.slice(i + 1);
9937
+ const len = arrowHeadLen(rest.trimStart());
9938
+ if (len > 0) {
9939
+ i += 1 + (rest.length - rest.trimStart().length) + len;
9940
+ continue;
9941
+ }
9942
+ } else if (depth === 0 && angle === 0 && c === ",") {
9943
+ parts.push(h.slice(from, i));
9944
+ from = i + 1;
9945
+ init = false;
9946
+ }
9947
+ i++;
9948
+ }
9949
+ parts.push(h.slice(from));
9950
+ return parts;
10034
9951
  }
10035
- function manifestDepsChanged(path, oldText, newText, patch) {
10036
- if (/(^|\/)package\.json$/.test(path)) {
10037
- try {
10038
- const o = oldText.trim() ? JSON.parse(oldText) : {}, n2 = newText.trim() ? JSON.parse(newText) : {};
10039
- return PKG_DEP_KEYS.some((k) => JSON.stringify(o?.[k] ?? {}) !== JSON.stringify(n2?.[k] ?? {}));
10040
- } catch {
10041
- return true;
9952
+ function exportHead(h, end) {
9953
+ let depth = 0, last = -1, nn = -1;
9954
+ const star = /^export\s*(type\s*)?\*/.test(h.slice(0, 40));
9955
+ for (let i = 0;i < h.length; ) {
9956
+ const k = skipLiteral(h, i);
9957
+ if (k !== i) {
9958
+ last = k - 1;
9959
+ i = k;
9960
+ continue;
9961
+ }
9962
+ const c = h[i];
9963
+ if ("([{".includes(c))
9964
+ depth++;
9965
+ else if (")]}".includes(c))
9966
+ depth--;
9967
+ else if (depth === 0 && c === ";")
9968
+ return h.slice(0, i + 1);
9969
+ else if (depth === 0 && c === `
9970
+ ` && last >= 0) {
9971
+ if (nn < i) {
9972
+ nn = i;
9973
+ while (nn < h.length && /\s/.test(h[nn]))
9974
+ nn++;
9975
+ }
9976
+ const tail = h.slice(Math.max(0, last - 300), last + 1);
9977
+ const next = h.slice(nn, nn + 12);
9978
+ const open = /[=|&,<(:?.+\-*/]$|=>$|\b(from|as|extends|keyof|typeof|type)$/.test(tail) || /^(from\b|extends\b|as\b|[|&?:.,=])/.test(next);
9979
+ if (!open && (!star || /\bfrom\s*(["'])[^"']*\1$/.test(tail)))
9980
+ return h.slice(0, last + 1);
10042
9981
  }
9982
+ if (!/\s/.test(c))
9983
+ last = i;
9984
+ i++;
10043
9985
  }
10044
- const changed = patch.split(`
10045
- `).filter((l) => /^[+-]/.test(l) && !/^(\+\+\+|---)/.test(l));
10046
- if (/(^|\/)go\.mod$/.test(path))
10047
- return changed.some((l) => {
10048
- const b = l.slice(1).trim();
10049
- return b !== "" && !b.startsWith("//") && !/^(module|go|toolchain)\b/.test(b);
10050
- });
10051
- return changed.some((l) => {
10052
- const b = l.slice(1).trim();
10053
- if (/^"[A-Za-z][\w.-]*(\[[\w,.-]+\])?\s*([<>=~!]|;|@|"\s*,?\s*$)/.test(b))
9986
+ return h.trimEnd() + (end === ";" ? ";" : "");
9987
+ }
9988
+ function hasTopSemi(line2) {
9989
+ let depth = 0;
9990
+ for (let i = 0;i < line2.length; ) {
9991
+ const k = skipLiteral(line2, i);
9992
+ if (k !== i) {
9993
+ i = k;
9994
+ continue;
9995
+ }
9996
+ const c = line2[i];
9997
+ if ("([{".includes(c))
9998
+ depth++;
9999
+ else if (")]}".includes(c))
10000
+ depth--;
10001
+ else if (c === ";" && depth <= 0 && line2.slice(i + 1).trim())
10054
10002
  return true;
10055
- const m = /^([A-Za-z][\w.-]*)\s*=\s*("[\^~<>=!*\d]|\{[^}]*version)/.exec(b);
10056
- return !!m && !PYPROJECT_NONDEP.test(m[1]);
10057
- });
10003
+ i++;
10004
+ }
10005
+ return false;
10058
10006
  }
10059
- function manifestsWithoutLock(files) {
10060
- return files.filter((f) => f.deps && f.status !== "deleted").flatMap((f) => {
10061
- const pair = MANIFEST_LOCKS.find(([m]) => m.test(f.path));
10062
- if (!pair)
10063
- return [];
10064
- const d = dirOf(f.path);
10065
- const locked = files.some((g) => pair[1].test(g.path) && (dirOf(g.path) === "" || d === dirOf(g.path) || d.startsWith(dirOf(g.path) + "/")));
10066
- return locked ? [] : [f.path];
10067
- });
10007
+ function strictMember(m) {
10008
+ const h = m.head.trim();
10009
+ if (!MEMBER_GRAMMAR.test(h) || NOT_MEMBER.test(h))
10010
+ return false;
10011
+ if (m.end !== "{" && /^[^=:<]*\(/.test(h) && !/^(?:(?:public|protected|private)\s+)?constructor\b/.test(h) && !/\)\s*:/.test(h))
10012
+ return false;
10013
+ return true;
10068
10014
  }
10069
- function classifyRisk(files, env = process.env) {
10070
- const lines = files.reduce((n2, f) => n2 + f.added + f.deleted, 0);
10071
- const sensitive = files.flatMap((f) => {
10072
- const hit = SENSITIVE.find(([, re]) => re.test(f.path));
10073
- return hit && !isTestFile(f.path) ? [`${f.path} (${hit[0]})`] : [];
10074
- });
10075
- const testFiles = files.filter((f) => isTestFile(f.path));
10076
- const testsDeleted = testFiles.filter((f) => f.status === "deleted").map((f) => f.path);
10077
- const testsModified = [...testFiles.filter((f) => f.status !== "added" && f.status !== "deleted").map((f) => f.path), ...files.filter((f) => TEST_CFG.test(f.path)).map((f) => f.path)];
10078
- const api = files.filter((f) => API.test(f.path) && !isTestFile(f.path)).map((f) => f.path);
10079
- const high = [], med = [];
10080
- if (sensitive.length)
10081
- high.push(`sensitive paths: ${sensitive.slice(0, 5).join(", ")}${sensitive.length > 5 ? ` (+${sensitive.length - 5})` : ""}`);
10082
- if (testsDeleted.length)
10083
- high.push(`test files deleted: ${testsDeleted.join(", ")}`);
10084
- if (api.length)
10085
- high.push(`public API surface: ${api.slice(0, 3).join(", ")}`);
10086
- if (files.length >= FILES_HIGH)
10087
- high.push(`${files.length} files >= ${FILES_HIGH}`);
10088
- if (lines >= LINES_HIGH)
10089
- high.push(`${lines} changed lines >= ${LINES_HIGH}`);
10090
- if (testsModified.length)
10091
- med.push(`existing tests or test config modified: ${testsModified.slice(0, 3).join(", ")}`);
10092
- if (files.length >= FILES_MEDIUM)
10093
- med.push(`${files.length} files >= ${FILES_MEDIUM}`);
10094
- if (lines >= LINES_MEDIUM)
10095
- med.push(`${lines} changed lines >= ${LINES_MEDIUM}`);
10096
- const computed = high.length ? "high" : med.length ? "medium" : "low";
10097
- const reasons = high.length ? [...high, ...med] : med.length ? med : [`${files.length} file${files.length === 1 ? "" : "s"}, no sensitive paths`];
10098
- let tier = computed, override, ignored;
10099
- const asked = (env["LOKI_RISK_TIER"] ?? "").trim().toLowerCase();
10100
- if (asked in RANK2 && asked !== computed) {
10101
- const reason = (env["LOKI_RISK_OVERRIDE_REASON"] ?? "").trim();
10102
- if (RANK2[asked] > RANK2[computed]) {
10103
- tier = asked;
10104
- override = { from: computed, to: asked, reason: reason || "raised by LOKI_RISK_TIER" };
10105
- } else if (reason) {
10106
- tier = asked;
10107
- override = { from: computed, to: asked, reason };
10108
- } else
10109
- ignored = `LOKI_RISK_TIER=${asked} ignored: lowering from ${computed} needs LOKI_RISK_OVERRIDE_REASON`;
10015
+ function tsAmbiguous(s, jsx) {
10016
+ const n2 = s.length, stack = [];
10017
+ const tpl = (from) => {
10018
+ for (let j = from;j < n2; ) {
10019
+ const c = s[j];
10020
+ if (c === "\\")
10021
+ j += 2;
10022
+ else if (c === "`")
10023
+ return j + 1;
10024
+ else if (c === "$" && s[j + 1] === "{") {
10025
+ stack.push("$");
10026
+ return j + 2;
10027
+ } else
10028
+ j++;
10029
+ }
10030
+ return -1;
10031
+ };
10032
+ let prev = "", prev2 = "";
10033
+ const set = (c) => {
10034
+ prev2 = prev;
10035
+ prev = c;
10036
+ };
10037
+ for (let i = 0;i < n2; ) {
10038
+ const c = s[i];
10039
+ if (/\s/.test(c)) {
10040
+ i++;
10041
+ continue;
10042
+ }
10043
+ if (c === "/" && s[i + 1] === "/") {
10044
+ const e = s.indexOf(`
10045
+ `, i);
10046
+ if (e < 0)
10047
+ break;
10048
+ i = e;
10049
+ continue;
10050
+ }
10051
+ if (c === "/" && s[i + 1] === "*") {
10052
+ const e = s.indexOf("*/", i + 2);
10053
+ if (e < 0)
10054
+ return true;
10055
+ i = e + 2;
10056
+ continue;
10057
+ }
10058
+ if (c === "`" || c === "}" && stack[stack.length - 1] === "$") {
10059
+ if (c === "`" && jsx)
10060
+ return true;
10061
+ if (c === "}")
10062
+ stack.pop();
10063
+ const e = tpl(i + 1);
10064
+ if (e < 0)
10065
+ return true;
10066
+ i = e;
10067
+ set("a");
10068
+ continue;
10069
+ }
10070
+ if (c === '"' || c === "'") {
10071
+ let j = i + 1;
10072
+ for (;j < n2 && s[j] !== c; j++) {
10073
+ if (s[j] === "\\")
10074
+ j++;
10075
+ else if (s[j] === `
10076
+ `)
10077
+ return true;
10078
+ }
10079
+ if (j >= n2)
10080
+ return true;
10081
+ i = j + 1;
10082
+ set("a");
10083
+ continue;
10084
+ }
10085
+ if (c === "/") {
10086
+ if (jsx && prev === "<") {
10087
+ i++;
10088
+ set("/");
10089
+ continue;
10090
+ }
10091
+ if (commentBefore(s, i))
10092
+ return true;
10093
+ if (prev && !"(,=:[!&|?{;+-*%~^".includes(prev))
10094
+ return true;
10095
+ if ((prev === "+" || prev === "-") && prev2 === prev)
10096
+ return true;
10097
+ let j = i + 1, cls = false, closed = false;
10098
+ for (;j < n2; j++) {
10099
+ const d = s[j];
10100
+ if (d === `
10101
+ `)
10102
+ return true;
10103
+ if (d === "\\")
10104
+ j++;
10105
+ else if (d === "[")
10106
+ cls = true;
10107
+ else if (d === "]")
10108
+ cls = false;
10109
+ else if (d === "/" && !cls) {
10110
+ closed = true;
10111
+ break;
10112
+ }
10113
+ }
10114
+ if (!closed)
10115
+ return true;
10116
+ i = j + 1;
10117
+ while (i < n2 && /[a-z]/i.test(s[i]))
10118
+ i++;
10119
+ set("a");
10120
+ continue;
10121
+ }
10122
+ if (c === "(" || c === "[" || c === "{")
10123
+ stack.push(c);
10124
+ else if (c === ")" || c === "]" || c === "}") {
10125
+ const o = stack.pop();
10126
+ if (o !== (c === ")" ? "(" : c === "]" ? "[" : "{"))
10127
+ return true;
10128
+ }
10129
+ set(c);
10130
+ i++;
10110
10131
  }
10111
- const plan = PLAN[tier], checks_deferred = deferredOf(tier, plan.selected, []);
10112
- const advisories = manifestsWithoutLock(files).map((p) => `manifest changed without lockfile: ${p}`);
10113
- const summary = summaryOf({ tier, computed, reasons, ...override ? { override } : {}, checks_deferred }) + (advisories.length ? `; advisory: ${advisories.join(", ")}` : "");
10114
- return {
10115
- version: 1,
10116
- tier,
10117
- computed,
10118
- reasons,
10119
- stats: { files: files.length, lines, sensitive, tests_modified: testsModified, tests_deleted: testsDeleted, api },
10120
- checks_selected: plan.selected,
10121
- checks_deferred,
10122
- ...override ? { override } : {},
10123
- ...ignored ? { override_ignored: ignored } : {},
10124
- ...advisories.length ? { advisories } : {},
10125
- summary
10126
- };
10127
- }
10128
- function changedFiles(repoDir, baseSha) {
10129
- const run = (args) => safeGit(repoDir, args).split(`
10130
- `).map((l) => l.trim()).filter(Boolean);
10131
- const tracked = run(["diff", "--name-only", baseSha]);
10132
- const untracked = run(["ls-files", "--others", "--exclude-standard"]);
10133
- return [...new Set([...tracked, ...untracked])].filter((f) => !f.startsWith(".loki/"));
10132
+ return stack.length > 0;
10134
10133
  }
10135
- function diffFiles(repoDir, baseSha) {
10136
- const paths = changedFiles(repoDir, baseSha), stat = new Map;
10137
- for (const l of safeGit(repoDir, ["diff", "--numstat", baseSha]).split(`
10138
- `)) {
10139
- const [a, d, ...p] = l.split("\t"), path = p.join("\t");
10140
- if (path)
10141
- stat.set(path, { path, added: Number(a) || 0, deleted: Number(d) || 0 });
10142
- }
10143
- for (const l of safeGit(repoDir, ["diff", "--name-status", baseSha]).split(`
10144
- `)) {
10145
- const [s, ...p] = l.split("\t"), f = stat.get(p[p.length - 1] ?? "");
10146
- if (f)
10147
- f.status = s?.startsWith("A") ? "added" : s?.startsWith("D") ? "deleted" : "modified";
10148
- }
10149
- const out2 = paths.map((path) => {
10150
- const f = stat.get(path);
10151
- if (f)
10152
- return f;
10153
- let n2 = 0;
10154
- try {
10155
- n2 = readFileSync29(join38(repoDir, path), "utf8").split(`
10156
- `).length;
10157
- } catch {}
10158
- return { path, added: n2, deleted: 0, status: "added" };
10159
- });
10160
- for (const f of out2) {
10161
- if (f.status === "deleted" || !MANIFEST_LOCKS.some(([m]) => m.test(f.path)))
10134
+ function maskDefaults(sig) {
10135
+ let out2 = "", pd = 0;
10136
+ for (let i = 0;i < sig.length; ) {
10137
+ const k = skipLiteral(sig, i);
10138
+ if (k !== i) {
10139
+ out2 += sig.slice(i, k);
10140
+ i = k;
10162
10141
  continue;
10163
- try {
10164
- const oldText = f.status === "added" ? "" : safeGit(repoDir, ["show", `${baseSha}:${f.path}`]);
10165
- const newText = readFileSync29(join38(repoDir, f.path), "utf8");
10166
- const patch = f.status === "added" ? newText.split(`
10167
- `).map((l) => `+${l}`).join(`
10168
- `) : safeGit(repoDir, ["diff", "-U0", baseSha, "--", f.path]);
10169
- if (manifestDepsChanged(f.path, oldText, newText, patch))
10170
- f.deps = true;
10171
- } catch {}
10142
+ }
10143
+ const c = sig[i];
10144
+ if (c === "(")
10145
+ pd++;
10146
+ else if (c === ")")
10147
+ pd--;
10148
+ else if (c === "=" && pd > 0 && sig[i + 1] !== "=" && sig[i + 1] !== ">" && !"=!<".includes(sig[i - 1] ?? " ")) {
10149
+ out2 = out2.trimEnd() + " = ...";
10150
+ let d = 0, angle = 0, j = i + 1;
10151
+ for (;j < sig.length; ) {
10152
+ const l = skipLiteral(sig, j);
10153
+ if (l !== j) {
10154
+ j = l;
10155
+ continue;
10156
+ }
10157
+ const e = sig[j];
10158
+ if ("([{".includes(e))
10159
+ d++;
10160
+ else if (")]}".includes(e)) {
10161
+ if (d === 0)
10162
+ break;
10163
+ d--;
10164
+ } else if (e === "<" && /[\w$]/.test(sig[j - 1] ?? ""))
10165
+ angle++;
10166
+ else if (e === ">" && sig[j - 1] !== "=" && angle > 0)
10167
+ angle--;
10168
+ else if (e === "," && d === 0 && angle === 0)
10169
+ break;
10170
+ j++;
10171
+ }
10172
+ i = j;
10173
+ continue;
10174
+ }
10175
+ out2 += c;
10176
+ i++;
10172
10177
  }
10173
10178
  return out2;
10174
10179
  }
10175
- function recordRisk(repoDir, a) {
10176
- try {
10177
- const dir = join38(repoDir, ".loki", "state"), p = join38(dir, "risk-tier.json");
10178
- mkdirSync13(dir, { recursive: true });
10179
- writeFileSync16(`${p}.tmp`, JSON.stringify(a, null, 2) + `
10180
- `);
10181
- renameSync8(`${p}.tmp`, p);
10182
- return p;
10183
- } catch {
10184
- return null;
10185
- }
10186
- }
10187
- function truncateBytes(s, cap) {
10188
- const buf = Buffer.from(s, "utf8");
10189
- return buf.length <= cap ? s : buf.subarray(0, Math.max(0, cap)).toString("utf8").replace(/\uFFFD$/, "");
10190
- }
10191
- function failureExcerpt(out2, repoDir = "", cap = EXCERPT_GROUP_CAP) {
10192
- let t = stripAnsi(out2.slice(-65536)).replace(/\r/g, "");
10193
- if (repoDir)
10194
- t = t.split(repoDir.replace(/\/+$/, "") + "/").join("");
10195
- const lines = t.split(`
10196
- `).map((l) => l.replace(/\s+$/, ""));
10197
- const pick = new Set;
10198
- const take = (i, n2 = 1) => {
10199
- for (let k = 0;k < n2 && i + k < lines.length; k++)
10200
- if (lines[i + k].trim())
10201
- pick.add(i + k);
10180
+ function maskDecorators(sig) {
10181
+ let out2 = "";
10182
+ const badNext = (at) => {
10183
+ const r = sig.slice(at).trimStart();
10184
+ return !/[A-Za-z_$@]/.test(r[0] ?? "") || RESERVED_AFTER_DECORATOR.test(r);
10202
10185
  };
10203
- lines.forEach((l, i) => {
10204
- if (/^E\s/.test(l))
10205
- take(i);
10206
- else if (/^\s*(Expected|Received)\b/.test(l) || /^\s*expect\(.*\)\.\w+/.test(l))
10207
- take(i);
10208
- else if (/^[\w./@-]+\.py:\d+: /.test(l))
10209
- take(i);
10210
- else if (/AssertionError|^\s*(expected|actual)\b.*[:=]|^\s*(not ok|FAILED)\b/.test(l))
10211
- take(i, 3);
10212
- else if (/^\s*--- FAIL/.test(l))
10213
- take(i, 4);
10214
- else if (/^[\w./@-]+\.\w+(\(\d+,\d+\)|:\d+(:\d+)?)[:\s].*\b(error|TS\d+|[A-Z]{1,3}\d{3,4})\b/.test(l))
10215
- take(i);
10216
- });
10217
- const lastFrame = lines.map((l, i) => /^[\w./@-]+\.py:\d+: /.test(l) ? i : -1).filter((i) => i >= 0).pop();
10218
- if (lastFrame !== undefined && [...pick].some((i) => /^E\s/.test(lines[i])))
10219
- take(lastFrame);
10220
- let body = [...pick].sort((a, b) => a - b).map((i) => lines[i]).join(`
10221
- `);
10222
- if (!body)
10223
- body = lines.filter((l) => l.trim()).slice(-6).join(`
10224
- `);
10225
- body = redactSecrets(body);
10226
- return truncateBytes(body, cap);
10227
- }
10228
- function pytestCollectionIsRed(output, repoDir) {
10229
- if (/ModuleNotFoundError/.test(output))
10230
- return false;
10231
- const real2 = (p) => {
10232
- try {
10233
- return realpathSync17(isAbsolute3(p) ? p : join38(repoDir, p));
10234
- } catch {
10235
- return null;
10186
+ for (let i = 0;i < sig.length; ) {
10187
+ const k = skipLiteral(sig, i);
10188
+ if (k !== i) {
10189
+ out2 += sig.slice(i, k);
10190
+ i = k;
10191
+ continue;
10236
10192
  }
10237
- };
10238
- const repoReal = real2(repoDir);
10239
- const under = (p) => {
10240
- const r = real2(p);
10241
- return !!repoReal && !!r && (r === repoReal || r.startsWith(`${repoReal}/`));
10242
- };
10243
- const imp = /ImportError: cannot import name .* from ['"][\w.]+['"] \(([^)]+)\)/.exec(output);
10244
- if (imp)
10245
- return under(imp[1]);
10246
- const hdr = [...output.matchAll(/^=+ short test summary info =+$/gm)].pop();
10247
- if (!hdr)
10248
- return false;
10249
- const cls = [...output.slice(hdr.index).matchAll(/^ERROR \S+ - (\w+Error): /gm)].pop()?.[1];
10250
- if (cls !== "AttributeError" && cls !== "NameError")
10251
- return false;
10252
- const start = output.search(/^_+ ERROR collecting /m);
10253
- if (start < 0 || start > hdr.index)
10254
- return false;
10255
- const block = output.slice(start, hdr.index).split(/^-+ Captured .*$/m)[0];
10256
- const eLines = [...block.matchAll(new RegExp(`^E {3}${cls}: (.*)$`, "gm"))];
10257
- const eLine = eLines[eLines.length - 1];
10258
- if (!eLine)
10259
- return false;
10260
- if (cls === "AttributeError") {
10261
- const m = /^module ['"]([\w.]+)['"] has no attribute/.exec(eLine[1]);
10262
- return !!m && moduleUnderRepo(repoDir, m[1]);
10193
+ if (sig[i] !== "@") {
10194
+ out2 += sig[i];
10195
+ i++;
10196
+ continue;
10197
+ }
10198
+ const m = /^@[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)*/.exec(sig.slice(i, i + 200));
10199
+ if (!m)
10200
+ return null;
10201
+ out2 += m[0];
10202
+ i += m[0].length;
10203
+ if (sig[i] === "(") {
10204
+ out2 += "(...)";
10205
+ i = skipBalanced(sig, i);
10206
+ }
10207
+ if (badNext(i))
10208
+ return null;
10263
10209
  }
10264
- const frames = [...block.slice(0, eLine.index).matchAll(/^(.+?):\d+: in \S+\n\s*(.*)$/gm)];
10265
- const last = frames[frames.length - 1];
10266
- return !!last && under(last[1]) && !/^\s*(import\s|from\s\S+\s+import\b)/.test(last[2] ?? "");
10210
+ return out2;
10267
10211
  }
10268
- function moduleUnderRepo(repoDir, name) {
10269
- const rel = name.replace(/\./g, "/");
10270
- return existsSync23(join38(repoDir, `${rel}.py`)) || existsSync23(join38(repoDir, rel, "__init__.py"));
10212
+ function skipBalanced(s, i) {
10213
+ let d = 0;
10214
+ while (i < s.length) {
10215
+ const k = skipLiteral(s, i);
10216
+ if (k !== i) {
10217
+ i = k;
10218
+ continue;
10219
+ }
10220
+ if ("([{".includes(s[i]))
10221
+ d++;
10222
+ else if (")]}".includes(s[i]) && --d === 0)
10223
+ return i + 1;
10224
+ i++;
10225
+ }
10226
+ return s.length;
10271
10227
  }
10272
- function pytestExit1IsRed(output) {
10273
- if (/^!+ _pytest\.outcomes\.Exit\b/m.test(output))
10274
- return false;
10275
- if (/^=*\s*(\d+ \w+(, )?)*\d+ failed\b.* in [\d.]+s/.test(output.trimEnd().split(`
10276
- `).pop() ?? ""))
10277
- return true;
10278
- const hdr = [...output.matchAll(/^=+ short test summary info =+$/gm)].pop();
10279
- return !!hdr && /^FAILED \S+/m.test(output.slice(hdr.index));
10228
+ function maskExtends(head) {
10229
+ let angle = 0, depth = 0;
10230
+ for (let i = 0;i < head.length; ) {
10231
+ const k = skipLiteral(head, i);
10232
+ if (k !== i) {
10233
+ i = k;
10234
+ continue;
10235
+ }
10236
+ const c = head[i];
10237
+ if ("([{".includes(c))
10238
+ depth++;
10239
+ else if (")]}".includes(c))
10240
+ depth--;
10241
+ else if (c === "<")
10242
+ angle++;
10243
+ else if (c === ">" && head[i - 1] !== "=")
10244
+ angle = Math.max(0, angle - 1);
10245
+ else if (depth === 0 && angle === 0 && /^extends\s/.test(head.slice(i)) && /\W/.test(head[i - 1] ?? " ")) {
10246
+ const from = i + 7;
10247
+ const im = /\simplements\s/.exec(head.slice(from));
10248
+ const end = im ? from + im.index : head.length;
10249
+ const expr = head.slice(from, end).trim();
10250
+ const ok = /^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)*(?:<[^()]*>)?$/.test(expr);
10251
+ const tail = head.slice(end).trim();
10252
+ return `${head.slice(0, i)}extends ${ok ? expr : "..."}${tail ? ` ${tail}` : ""}`;
10253
+ }
10254
+ i++;
10255
+ }
10256
+ return head;
10280
10257
  }
10281
- function parsedFailCount(runner, output) {
10282
- const m = runner === "bun" ? /^\s*(\d+)\s+fail\s*$/m.exec(output) : runner === "jest" ? /Tests:\s*(\d+)\s+failed/i.exec(output) : runner === "vitest" ? /Tests\s+(\d+)\s+failed/i.exec(output) : null;
10283
- return m ? Number(m[1]) : 0;
10258
+ function enumSig(h) {
10259
+ const open = h.indexOf("{");
10260
+ if (open < 0)
10261
+ return null;
10262
+ const close = matchingBrace(h, open);
10263
+ if (close >= h.length)
10264
+ return null;
10265
+ const names = [];
10266
+ let depth = 0, from = open + 1;
10267
+ const take = (to) => {
10268
+ const nm = h.slice(from, to).split("=")[0].trim();
10269
+ if (nm)
10270
+ names.push(nm);
10271
+ };
10272
+ for (let i = open + 1;i < close; ) {
10273
+ const k = skipLiteral(h, i);
10274
+ if (k !== i) {
10275
+ i = k;
10276
+ continue;
10277
+ }
10278
+ const c = h[i];
10279
+ if ("([{".includes(c))
10280
+ depth++;
10281
+ else if (")]}".includes(c))
10282
+ depth--;
10283
+ else if (c === "," && depth === 0) {
10284
+ take(i);
10285
+ from = i + 1;
10286
+ }
10287
+ i++;
10288
+ }
10289
+ take(close);
10290
+ return `${h.slice(0, open).trim()} { ${names.join(", ")} }`;
10284
10291
  }
10285
- function nodeIsRed(f, raw2) {
10286
- const o = raw2.replace(/^# (?!(?:tests|suites|pass|fail|cancelled|skipped|todo|duration_ms|Subtest)\b)/gm, "");
10287
- const mod = /^Error(?: \[\w+\])?: Cannot find (?:module|package) '([^']+)'(?:(?!\u2716)[\s\S])*?\nNode\.js v/m.exec(o), top = /^(\S+):\d+\n.*\n.*\n\n?(?:ReferenceError|SyntaxError)\b/m.exec(o);
10288
- const named = [...o.matchAll(/^(?:\u2716|\s*not ok \d+ -) (?!failing tests:)(.+?)(?: \(|$)/gm)].some((m) => !f.path.endsWith(m[1]) && !m[1].endsWith(f.path)), blk = o.split("failing tests:")[1] ?? "", esm = /^SyntaxError: The requested module '([^']+)' does not provide an export named/m.exec(o), errs = [...blk.matchAll(/^ {2}(\w+Error): .*\n\s+at (?:.*\()?([^\s()]+?):\d+:\d+/gm), ...o.matchAll(/^ +name: '(\w+Error)'\n(?:.*\n)*? +stack: \|-\n\s+(?:.*\()?([^\s()]+?):\d+:\d+/gm)];
10289
- return /^(?:#|\u2139) fail [1-9]/m.test(raw2) && (/^ {2}AssertionError\b/m.test(blk) || /^ +name: 'AssertionError'/m.test(o) || (mod && !named ? /^[./]/.test(mod[1]) : esm ? /^[./]/.test(esm[1]) : top ? !top[1].endsWith(f.path) : !errs.length || errs.some((e) => !/^(?:Reference|Syntax)Error$/.test(e[1]) || !(e[2].startsWith("node:") || e[2].endsWith(f.path)))));
10292
+ function tsSignatures(src, jsx) {
10293
+ const out2 = [];
10294
+ for (const it of items(src)) {
10295
+ const h = it.head.trim();
10296
+ if (!/^export\b/.test(h))
10297
+ continue;
10298
+ const lines = [];
10299
+ if (/^export\s+(declare\s+)?enum\b/.test(h)) {
10300
+ lines.push(enumSig(h) ?? exportHead(h, it.end));
10301
+ } else if (/^export\s+(declare\s+)?(type|interface)\b|^export\s*(type\s*)?[{*]/.test(h)) {
10302
+ lines.push(exportHead(h, it.end));
10303
+ } else if (/^export\s+(default\s+)?(abstract\s+)?class\b/.test(h)) {
10304
+ const ms = jsx || !it.balanced ? [] : items(it.body ?? "");
10305
+ const sigs = ms.map((m) => strictMember(m) ? memberSig(m.head.trim()) : null).map((m) => m === null ? null : maskDefaults(m));
10306
+ const dropped2 = ms.some((m) => !strictMember(m)) || sigs.some((m) => m !== null && (hasTopSemi(m) || NOT_MEMBER.test(m)));
10307
+ const keep = dropped2 ? [] : sigs.filter((m) => !!m);
10308
+ const headM = maskDecorators(exportHead(h, ""));
10309
+ const memM = keep.map(maskDecorators);
10310
+ if (headM !== null && !memM.includes(null))
10311
+ lines.push(`${maskExtends(headM)} {`, ...memM.map((m) => ` ${m};`), "}");
10312
+ } else if (/^export\s+(default\s+)?@/.test(h)) {
10313
+ const dm = maskDecorators(h);
10314
+ if (dm !== null)
10315
+ lines.push(maskExtends(dm));
10316
+ } else if (/^export\s+default\s/.test(h)) {
10317
+ const rest = h.replace(/^export\s+default\s+/, "");
10318
+ const len = arrowHeadLen(rest);
10319
+ if (len > 0)
10320
+ lines.push(`export default ${rest.slice(0, len)}`);
10321
+ else if (/^(async\s+)?function\b/.test(rest))
10322
+ lines.push(memberSig(h) ?? h);
10323
+ else
10324
+ lines.push(/^[\w$.]+$/.test(rest) ? h : "export default ...");
10325
+ } else if (/^export\s+(const|let|var)\b/.test(h)) {
10326
+ lines.push(splitDeclarators(h).map((p) => memberSig(p.trim()) ?? p.trim()).join(", "));
10327
+ } else {
10328
+ lines.push(memberSig(h) ?? h);
10329
+ }
10330
+ if (!lines.length)
10331
+ continue;
10332
+ const masked = /^export\s+(declare\s+)?(type|interface|enum)\b|^export\s*(type\s*)?[{*]|^export\s+(default\s+)?(abstract\s+)?class\b/.test(h) ? lines : lines.map((l) => {
10333
+ const d = maskDecorators(l);
10334
+ return d === null ? null : maskDefaults(d);
10335
+ });
10336
+ if (masked.includes(null))
10337
+ continue;
10338
+ const done = masked;
10339
+ if (!done.some(hasTopSemi))
10340
+ out2.push(...done);
10341
+ }
10342
+ return out2;
10290
10343
  }
10291
- function notRunReason(status, interpreter) {
10292
- if (interpreter === "system")
10293
- return "ran under the system interpreter, not the project's, so the result proves nothing";
10294
- if (status === null)
10295
- return "the test runner did not start or timed out";
10296
- if (status === 126 || status === 127)
10297
- return `the test runner command was not found or not executable (exit ${status})`;
10298
- return status === 0 ? "exit 0 but no executed test count could be confirmed" : `exit ${status} but the runner output showed no readable failing test`;
10344
+ var REGEX_PREV = "(,=:[!&|?{;+-*%~^", isComment = (s, i) => s[i] === "/" && (s[i + 1] === "/" || s[i + 1] === "*"), MODS = "(?:(?:public|private|protected|static|readonly|abstract|async|get|set|declare|override)\\s+)*", MEMBER_GRAMMAR, NOT_MEMBER, RESERVED_AFTER_DECORATOR;
10345
+ var init_ts_signatures = __esm(() => {
10346
+ MEMBER_GRAMMAR = new RegExp(`^(?:@[\\w$.]+(?:\\([^)]*\\))?\\s*)*${MODS}\\*?\\s*(?:[A-Za-z_$][\\w$]*|#[A-Za-z_$][\\w$]*|\\[[^\\]]*\\])\\s*[?!]?\\s*(?:[(<:=]|$)`);
10347
+ NOT_MEMBER = /^(return|const|let|var|if|else|for|while|do|switch|case|default|throw|try|catch|finally|new|await|yield|break|continue|import|export|function|delete|typeof|void)\b/;
10348
+ RESERVED_AFTER_DECORATOR = /^(?:if|else|for|while|do|switch|case|try|catch|finally|return|throw|with|var|let|const|function|new|delete|typeof|void|yield|await|break|continue|import|debugger|instanceof|this|super|null|true|false)(?![\w$])/;
10349
+ });
10350
+
10351
+ // ../../loki-ts/src/util/failure_memory.ts
10352
+ var init_failure_memory = __esm(() => {
10353
+ init_cache();
10354
+ init_safe_git();
10355
+ });
10356
+
10357
+ // ../../loki-ts/src/e10ext/repomemory.ts
10358
+ var init_repomemory = __esm(() => {
10359
+ init_cache();
10360
+ init_failure_memory();
10361
+ });
10362
+
10363
+ // ../../loki-ts/src/engine10/failures.ts
10364
+ function failCount(out2) {
10365
+ const n2 = (s, re) => +(s.match(re)?.[1] ?? 0);
10366
+ const l = out2.split(`
10367
+ `).reverse().map((x) => x.trim()).find((x) => /^(?:=+ )?\d+ \w+.* in [\d.]+s|^Tests?:?\s+\d/.test(x)), m = /^(?:#|\u2139) fail (\d+)$/m.exec(out2);
10368
+ return l ? n2(l, /(\d+) failed/) + n2(l, /(\d+) errors?/) : m ? +m[1] : null;
10299
10369
  }
10300
- function classify(f, status, output, repoDir, interpreter) {
10301
- if (interpreter === "system")
10302
- return "not_run";
10303
- output = stripAnsi(output);
10304
- if (status === null || status === 126 || status === 127)
10305
- return "not_run";
10306
- if (status === 0)
10307
- return classifyCheck({ kind: "test", ok: true, out: output, path: f.path, ...f.runner === "go" ? { runner: "go" } : {} }).result === "pass" ? "pass" : "not_run";
10308
- if (f.runner === "pytest")
10309
- return status === 1 ? pytestExit1IsRed(output) ? "fail" : "not_run" : status === 2 ? pytestCollectionIsRed(output, repoDir) ? "fail" : "not_run" : "not_run";
10310
- if (f.runner === "jest" || f.runner === "vitest" || f.runner === "bun")
10311
- return parsedFailCount(f.runner, output) > 0 ? "fail" : "not_run";
10312
- return f.runner === "node" && nodeIsRed(f, output) ? "fail" : "not_run";
10370
+ function failIds(output) {
10371
+ const ids = [...new Set(output.split(`
10372
+ `).map((l) => ID_LINE.exec(l.trim())).flatMap((m) => m ? [(m.slice(1).find(Boolean) ?? "").trim()] : []).filter(Boolean))];
10373
+ const c = failCount(output);
10374
+ return c && ids.length >= c ? ids : [];
10313
10375
  }
10314
- var NO_TESTS_REASON = "no tests executed", GO_EXIT0_REASON = "test count could not be confirmed: go test output is produced by the code under test and cannot confirm execution (a Go exit 0 is never a pass)", UNCONFIRMED_REASON = "test count could not be confirmed", FAIL_EVIDENCE, GO_FAIL_EVIDENCE, UNMEASURED_REASON = "executed count unmeasured (Loki could not parse the runner summary, harness-owned)", stripAnsi = (s) => s.replace(/\u001b\][^\u0007\u001b]*(?:\u0007|\u001b\\)/g, "").replace(/(?:\u001b\[|\u009b)[0-9;?]*[ -\/]*[@-~]/g, "").replace(/\r\n?/g, `
10315
- `), tailLines = (out2) => out2.split(`
10316
- `).filter((x) => x.trim()).slice(-12), GO_PKG_RE, isExecutedProof = (c) => c.result === "pass" && typeof c.n === "number" && c.n > 0, hasExecutedProof = (checks) => checks.some(isExecutedProof), LLM_JUDGES, NON_RUNNER, normPath = (p) => p.replace(/^(?:\.\/)+/, ""), hasExogenousProof = (checks, ctx) => checks.some((c) => isExecutedProof(c) && ((p) => p !== "agent_authored" && p !== "llm_judge")(checkProvenance(c.name ?? "", ctx))), SENSITIVE, API, TEST_CFG, RANK2, FILES_MEDIUM = 4, FILES_HIGH = 15, LINES_MEDIUM = 100, LINES_HIGH = 500, RAN_ALWAYS, PLAN, LOW_SKIPPED, deferredOf = (tier, selected, executed) => (tier === "low" ? LOW_SKIPPED : selected).filter((c) => !RAN_ALWAYS.includes(c) && !executed.includes(c)), MANIFEST_LOCKS, dirOf = (p) => p.includes("/") ? p.slice(0, p.lastIndexOf("/")) : "", PKG_DEP_KEYS, PYPROJECT_NONDEP, EXCERPT_GROUP_CAP = 1500, EXCERPT_TOTAL_CAP = 6144;
10317
- var init_check_result = __esm(() => {
10318
- init_testmap();
10319
- init_safe_git();
10320
- init_redact();
10321
- FAIL_EVIDENCE = /\[(?:build|setup) failed\]|^--- FAIL|^[\w./-]+\.go:\d+:\d+: \S|\b[1-9]\d* (?:failed|errors?)\b/m;
10322
- GO_FAIL_EVIDENCE = /^FAIL\t\S+\t[\d.]+s$|^panic: /m;
10323
- GO_PKG_RE = /^(?:ok|FAIL|\?)\s+\S+\s+(?:\(cached\)|[\d.]+s\b|\[(?:no test files|build failed|setup failed)\])/;
10324
- LLM_JUDGES = new Set(["council", "xreview", "code_review", "devils_advocate", "magic_debate"]);
10325
- NON_RUNNER = new Set(["lint", "typecheck", "holdout", "select-tests"]);
10326
- SENSITIVE = [
10327
- ["auth", /(^|\/)(auth|oauth|sso|session|login|permission|rbac|acl)[\w.-]*(\/|\.|$)/i],
10328
- ["crypto", /(^|\/)(crypt|crypto|jwt|signing|keys?|secrets?|tokens?|credential|password)[\w.-]*(\/|\.|$)/i],
10329
- ["payments", /(^|\/)(payments?|billing|checkout|stripe|invoice)[\w.-]*(\/|\.|$)/i],
10330
- ["migration", /(^|\/)(migrations?|alembic)\/|\.sql$/i],
10331
- ["ci", /(^|\/)(\.github\/(workflows|actions)|\.gitlab-ci\.yml|\.circleci|Jenkinsfile|azure-pipelines)/i],
10332
- ["lockfile", /(^|\/)(package-lock\.json|pnpm-lock\.yaml|bun\.lockb?|go\.sum|[\w-]+\.lock)$/],
10333
- ["infra", /(^|\/)(Dockerfile[\w.-]*|docker-compose[\w.-]*\.ya?ml|terraform|helm|k8s|kubernetes|infra|deploy)(\/|\.|$)|\.tf$/i],
10334
- ["env", /(^|\/)\.env[\w.]*$/]
10335
- ];
10336
- API = /(^|\/)(openapi|swagger)[\w.-]*\.(ya?ml|json)$|\.proto$|\.d\.ts$|(^|\/)(api|routes|public)\/|(^|\/)index\.(ts|js|py)$/i;
10337
- TEST_CFG = /(^|\/)(conftest\.py|\.?pytest\.(ini|toml)|tox\.ini|jest\.config\.[\w.]+|vitest\.config\.[\w.]+)$/;
10338
- RANK2 = { low: 0, medium: 1, high: 2 };
10339
- RAN_ALWAYS = ["targeted tests", "lint of changed files"];
10340
- PLAN = {
10341
- low: { selected: [...RAN_ALWAYS] },
10342
- medium: { selected: [...RAN_ALWAYS, "full test command", "one reviewer"] },
10343
- high: { selected: [...RAN_ALWAYS, "full test command", "full suite", "council", "security scan"] }
10344
- };
10345
- LOW_SKIPPED = ["full test command", "reviewer", "council", "security scan"];
10346
- MANIFEST_LOCKS = [
10347
- [/(^|\/)package\.json$/, /(^|\/)(package-lock\.json|npm-shrinkwrap\.json|pnpm-lock\.yaml|yarn\.lock|bun\.lockb?)$/],
10348
- [/(^|\/)pyproject\.toml$/, /(^|\/)(poetry\.lock|uv\.lock|pdm\.lock|Pipfile\.lock)$/],
10349
- [/(^|\/)go\.mod$/, /(^|\/)go\.sum$/]
10350
- ];
10351
- PKG_DEP_KEYS = ["dependencies", "devDependencies", "peerDependencies", "optionalDependencies"];
10352
- PYPROJECT_NONDEP = /^(name|version|description|readme|license|requires-python|python|authors|maintainers|keywords|classifiers|homepage|repository|documentation|packages|include|exclude)$/;
10376
+ var ID_LINE;
10377
+ var init_failures = __esm(() => {
10378
+ ID_LINE = /^(?:(?:FAILED|ERROR)\s+(\S+::\S*)(?:\s.*)?|\u25cf\s+(?!Console|Test suite failed)(.+)|not ok \d+ - (.+?)(?:\s+#.*)?|\u2716\s+(?!failing tests:)(.+?)(?:\s+\([\d.]+ms\))?|(?:FAIL|\u00d7)\s+(.+))$/;
10353
10379
  });
10354
10380
 
10355
10381
  // ../../loki-ts/src/runner/state.ts
@@ -10370,7 +10396,7 @@ var init_state = __esm(() => {
10370
10396
  });
10371
10397
 
10372
10398
  // ../../loki-ts/src/runner/repo_profile.ts
10373
- import { spawnSync as spawnSync2 } from "child_process";
10399
+ import { spawnSync as spawnSync3 } from "child_process";
10374
10400
  import { existsSync as existsSync24, lstatSync as lstatSync9, readFileSync as readFileSync30, mkdirSync as mkdirSync14, readdirSync as readdirSync14, renameSync as renameSync9, rmSync as rmSync8, writeFileSync as writeFileSync17 } from "fs";
10375
10401
  import { basename as basename12, join as join39, resolve as resolve13, isAbsolute as isAbsolute4 } from "path";
10376
10402
  function detectFormatters(repoDir) {
@@ -10405,7 +10431,7 @@ function runFormatters(repoDir, files, env) {
10405
10431
  }) : [];
10406
10432
  return fmts.flatMap((fm) => {
10407
10433
  const own = mine.filter((f) => fm.exts.test(f));
10408
- const r = own.length ? spawnSync2(fm.argv[0], [...fm.argv.slice(1), ...own.map((f) => `./${f}`)], { cwd: repoDir, env, stdio: "ignore", timeout: 30000 }) : null;
10434
+ const r = own.length ? spawnSync3(fm.argv[0], [...fm.argv.slice(1), ...own.map((f) => `./${f}`)], { cwd: repoDir, env, stdio: "ignore", timeout: 30000 }) : null;
10409
10435
  return r && !r.error && r.status === 0 ? own : [];
10410
10436
  });
10411
10437
  }
@@ -11213,7 +11239,7 @@ var init_shell = __esm(() => {
11213
11239
 
11214
11240
  // ../../loki-ts/src/util/python.ts
11215
11241
  import { accessSync, constants as constants3, existsSync as existsSync27, mkdtempSync as mkdtempSync4, readFileSync as readFileSync34, realpathSync as realpathSync18, rmSync as rmSync11, statSync as statSync16 } from "fs";
11216
- import { spawnSync as spawnSync3 } from "child_process";
11242
+ import { spawnSync as spawnSync4 } from "child_process";
11217
11243
  import { tmpdir as tmpdir5 } from "os";
11218
11244
  async function findPython3() {
11219
11245
  if (_pythonCache !== undefined)
@@ -11250,7 +11276,7 @@ function pySignatures(src) {
11250
11276
  const py = resolvePython3();
11251
11277
  if (!py)
11252
11278
  return [];
11253
- const r = spawnSync3(py, ["-I", "-S", "-c", PY_SCRIPT], {
11279
+ const r = spawnSync4(py, ["-I", "-S", "-c", PY_SCRIPT], {
11254
11280
  input: src.replace(/\r\n?/g, `
11255
11281
  `),
11256
11282
  cwd: tmpdir5(),
@@ -12402,7 +12428,7 @@ var init_runner_errors = __esm(() => {
12402
12428
  });
12403
12429
 
12404
12430
  // ../../loki-ts/src/runner/load_owner.ts
12405
- import { spawnSync as spawnSync4 } from "child_process";
12431
+ import { spawnSync as spawnSync5 } from "child_process";
12406
12432
  import { mkdtempSync as mkdtempSync6, realpathSync as realpathSync19, rmSync as rmSync14 } from "fs";
12407
12433
  import { tmpdir as tmpdir8 } from "os";
12408
12434
  import { basename as basename18, extname as extname2, join as join46 } from "path";
@@ -12447,7 +12473,7 @@ function sameLoadError(head, base, headRoots, baseRoots) {
12447
12473
  function killTree(pid) {
12448
12474
  const all = [pid];
12449
12475
  for (let k = 0;k < all.length; k++) {
12450
- const kids = spawnSync4("pgrep", ["-P", String(all[k])], { encoding: "utf8", env: process.env }).stdout.split(/\s+/).filter(Boolean).map(Number);
12476
+ const kids = spawnSync5("pgrep", ["-P", String(all[k])], { encoding: "utf8", env: process.env }).stdout.split(/\s+/).filter(Boolean).map(Number);
12451
12477
  all.push(...kids.filter((x) => Number.isInteger(x) && !all.includes(x)));
12452
12478
  }
12453
12479
  try {
@@ -12595,7 +12621,7 @@ var init_wall_owned = __esm(() => {
12595
12621
  });
12596
12622
 
12597
12623
  // ../../loki-ts/src/util/app_probe.ts
12598
- import { spawnSync as spawnSync5 } from "child_process";
12624
+ import { spawnSync as spawnSync6 } from "child_process";
12599
12625
  import { existsSync as existsSync29, mkdirSync as mkdirSync18, readFileSync as readFileSync38, renameSync as renameSync11, writeFileSync as writeFileSync21 } from "fs";
12600
12626
  import { join as join48 } from "path";
12601
12627
  function readJson4(p) {
@@ -12610,7 +12636,7 @@ function stopApp(repoDir, opts = {}) {
12610
12636
  const runner = opts.runnerPath ?? join48(REPO_ROOT, "autonomy", "app-runner.sh");
12611
12637
  if (!existsSync29(runner) || !existsSync29(join48(repoDir, ".loki", "app-runner", "app.pid")))
12612
12638
  return;
12613
- spawnSync5("bash", ["-c", STOP, "app-probe", repoDir, runner], { cwd: repoDir, env: plainTestEnv(), timeout: opts.timeoutMs ?? 60000, killSignal: "SIGKILL", stdio: "ignore" });
12639
+ spawnSync6("bash", ["-c", STOP, "app-probe", repoDir, runner], { cwd: repoDir, env: plainTestEnv(), timeout: opts.timeoutMs ?? 60000, killSignal: "SIGKILL", stdio: "ignore" });
12614
12640
  }
12615
12641
  async function appProbe(repoDir, lokiDir2, opts = {}) {
12616
12642
  const timeoutMs = opts.timeoutMs ?? 60000, runner = opts.runnerPath ?? join48(REPO_ROOT, "autonomy", "app-runner.sh");
@@ -12618,7 +12644,7 @@ async function appProbe(repoDir, lokiDir2, opts = {}) {
12618
12644
  const t0 = Date.now();
12619
12645
  const mk2 = (name, result, reason) => ({ name, cmd: "autonomy/app-runner.sh", result, duration_s: (Date.now() - t0) / 1000, reason });
12620
12646
  const sh = (script) => {
12621
- const r = spawnSync5("bash", ["-c", script, "app-probe", repoDir, runner], { cwd: repoDir, env: { ...plainTestEnv(), ...opts.env ?? {} }, encoding: "utf8", timeout: timeoutMs, killSignal: "SIGKILL", stdio: ["ignore", "pipe", "ignore"] });
12647
+ const r = spawnSync6("bash", ["-c", script, "app-probe", repoDir, runner], { cwd: repoDir, env: { ...plainTestEnv(), ...opts.env ?? {} }, encoding: "utf8", timeout: timeoutMs, killSignal: "SIGKILL", stdio: ["ignore", "pipe", "ignore"] });
12622
12648
  return { out: r.stdout ?? "", status: r.error ? null : r.status };
12623
12649
  };
12624
12650
  let pid = null, url = null;
@@ -12663,7 +12689,7 @@ async function functionalVerify(opts, url, dir, lokiDir2, timeoutMs) {
12663
12689
  return res("not_run", "python3 or functional-verify.py unavailable");
12664
12690
  const specFile = join48(dir, "spec.txt");
12665
12691
  writeFileSync21(specFile, opts.specText ?? "");
12666
- const r = spawnSync5(py, ["-I", script, specFile, url], { env: plainTestEnv(), encoding: "utf8", timeout: timeoutMs, killSignal: "SIGKILL", stdio: ["ignore", "pipe", "ignore"] });
12692
+ const r = spawnSync6(py, ["-I", script, specFile, url], { env: plainTestEnv(), encoding: "utf8", timeout: timeoutMs, killSignal: "SIGKILL", stdio: ["ignore", "pipe", "ignore"] });
12667
12693
  let out2 = null;
12668
12694
  try {
12669
12695
  const v = JSON.parse(r.stdout ?? "");
@@ -14569,7 +14595,7 @@ var readJson5 = (p) => {
14569
14595
  var init_conventions = () => {};
14570
14596
 
14571
14597
  // ../../loki-ts/src/engine10/stages/wall.ts
14572
- import { spawnSync as spawnSync6 } from "child_process";
14598
+ import { spawnSync as spawnSync7 } from "child_process";
14573
14599
  import { existsSync as existsSync38, mkdirSync as mkdirSync24, mkdtempSync as mkdtempSync11, readFileSync as readFileSync45, readdirSync as readdirSync18, rmSync as rmSync19, writeFileSync as writeFileSync27 } from "fs";
14574
14600
  import { tmpdir as tmpdir12 } from "os";
14575
14601
  import { dirname as dirname23, join as join58, relative as relative8 } from "path";
@@ -14589,7 +14615,7 @@ class RealBaseTestRunner {
14589
14615
  const api = this.api === undefined ? loadProjectApi(repoDir) : this.api;
14590
14616
  for (const f of files) {
14591
14617
  const { cmd, args, interpreter, cwd } = commandFor(f, repoDir, api);
14592
- const r = spawnSync6(cmd, f.runner === "pytest" ? [...args, "-rfE"] : args, { cwd, encoding: "utf8", timeout: Math.max(1000, this.timeoutMs), env: plainTestEnv() });
14618
+ const r = spawnSync7(cmd, f.runner === "pytest" ? [...args, "-rfE"] : args, { cwd, encoding: "utf8", timeout: Math.max(1000, this.timeoutMs), env: plainTestEnv() });
14593
14619
  const status = r.error ? null : r.status;
14594
14620
  const result = classify(f, status, stripAnsi(`${r.stdout ?? ""}
14595
14621
  ${r.stderr ?? ""}`), repoDir, interpreter);
@@ -16136,6 +16162,7 @@ var init_seal = __esm(() => {
16136
16162
  events_sha256: sha2562(existsSync45(eventsPath) ? readFileSync53(eventsPath) : ""),
16137
16163
  ...await sealEvidence(ctx.repoDir, ctx.runDir, o, notProven, signal, ctx.emit),
16138
16164
  log_seal: true,
16165
+ toolchain: collectToolchain(),
16139
16166
  ...receiptBlock(process.env, cost.usd, cost.unmetered === true, totalS),
16140
16167
  ...reviewReceipt(ctx.provider, xr).review ? { review: reviewReceipt(ctx.provider, xr).review } : {},
16141
16168
  ...mp ? { mutation_proof: mp.line, mutation_outcome: mp.outcome } : {},
@@ -16294,12 +16321,10 @@ function compositeHash(r) {
16294
16321
  const { composite_sha256: _c, verification: _v, ...body } = r;
16295
16322
  return sha3(canonicalJson(body));
16296
16323
  }
16297
- var COMPOSITE_SCHEMA = "loki.v10.composite/1", TOOLCHAIN_KEYS, toolchainProblem = (t) => isObj10(t) && Object.keys(t).length === TOOLCHAIN_KEYS.length && TOOLCHAIN_KEYS.every((k) => t[k] === null || typeof t[k] === "string") ? null : "toolchain section is malformed", slugOf = (repo) => repo.replace(/\//g, "__"), isComposite = (r) => r["schema"] === COMPOSITE_SCHEMA, relOk = (p) => p !== "" && !p.startsWith("/") && !p.split("/").includes("..");
16324
+ var COMPOSITE_SCHEMA = "loki.v10.composite/1", toolchainProblem = (t) => isObj10(t) && Object.keys(t).length === TOOLCHAIN_KEYS.length && TOOLCHAIN_KEYS.every((k) => t[k] === null || typeof t[k] === "string") ? null : "toolchain section is malformed", slugOf = (repo) => repo.replace(/\//g, "__"), isComposite = (r) => r["schema"] === COMPOSITE_SCHEMA, relOk = (p) => p !== "" && !p.startsWith("/") && !p.split("/").includes("..");
16298
16325
  var init_composite_seal = __esm(() => {
16299
16326
  init_receipt_signer();
16300
- init_check_result();
16301
16327
  init_seal_group();
16302
- TOOLCHAIN_KEYS = ["bun", "node", "python3", "git"];
16303
16328
  });
16304
16329
 
16305
16330
  // ../../loki-ts/src/engine10/keys_cmd.ts