loki-mode 11.6.0 → 11.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/README.md +19 -2
  2. package/SKILL.md +2 -2
  3. package/VERSION +1 -1
  4. package/autonomy/lib/backlog.py +48 -4
  5. package/autonomy/lib/chat.sh +60 -3
  6. package/autonomy/lib/loki_yaml.py +8 -2
  7. package/autonomy/lib/workspace.py +267 -11
  8. package/autonomy/loki +315 -21
  9. package/autonomy/trigger-server.py +454 -4
  10. package/autonomy/verify.sh +9 -0
  11. package/completions/_loki +4 -0
  12. package/completions/loki.bash +20 -2
  13. package/dashboard/__init__.py +1 -1
  14. package/docs/CLI-REFERENCE.md +2 -1
  15. package/docs/DEPLOY-PLAN.md +17 -8
  16. package/docs/EVALUATING.md +8 -0
  17. package/docs/INSTALLATION.md +1 -1
  18. package/docs/WORKSPACES.md +15 -2
  19. package/docs/dev/architecture-reference.md +8 -0
  20. package/docs/sentry-intake.md +33 -0
  21. package/docs/v10/CLI-MODERN.md +1 -1
  22. package/docs/v10/D61-SPEED.md +12 -0
  23. package/docs/v10/PROGRESS.md +10 -0
  24. package/loki-ts/dist/loki.js +1055 -1004
  25. package/mcp/__init__.py +1 -1
  26. package/package.json +2 -2
  27. package/packages/control-plane/dist/server.js +789 -634
  28. package/plugins/loki-mode/.claude-plugin/plugin.json +1 -1
  29. package/schemas/loki-yaml.schema.json +5 -2
  30. package/src/policies/engine.js +41 -2
  31. package/src/policies/types.js +49 -0
  32. package/web-app/dist/assets/{AdminPage-DhXdJNQk.js → AdminPage-BnIg9ilA.js} +1 -1
  33. package/web-app/dist/assets/{Avatar-ixVj76fM.js → Avatar-0BTVsQ2A.js} +1 -1
  34. package/web-app/dist/assets/{Badge-BGMSp22s.js → Badge-Bbq8CXgn.js} +1 -1
  35. package/web-app/dist/assets/{Button-pYJc32tG.js → Button-CKD4n0ld.js} +1 -1
  36. package/web-app/dist/assets/{CockpitPage-C1JBBouA.js → CockpitPage-DJhLNLAz.js} +1 -1
  37. package/web-app/dist/assets/{ComparePage-qD0NV4RK.js → ComparePage-Di2akuII.js} +1 -1
  38. package/web-app/dist/assets/{ErrorBoundary-C-yxO58j.js → ErrorBoundary-9lhsYG-z.js} +1 -1
  39. package/web-app/dist/assets/{EvidenceReceiptPanel-ByPzuE1S.js → EvidenceReceiptPanel-CQDGXvDr.js} +1 -1
  40. package/web-app/dist/assets/{GitHubIssuesPanel-BNv_2BEJ.js → GitHubIssuesPanel-Y_w2k9Ej.js} +1 -1
  41. package/web-app/dist/assets/{GitHubPRsPanel-CuLZQ9CS.js → GitHubPRsPanel-pMSQedCv.js} +1 -1
  42. package/web-app/dist/assets/{HomePage-CS_FW8Je.js → HomePage-CIAkiGdt.js} +2 -2
  43. package/web-app/dist/assets/{LoginPage-DwiNf1Wm.js → LoginPage-BES4Pk6k.js} +1 -1
  44. package/web-app/dist/assets/{MagicPage-CmQybLPZ.js → MagicPage-B154fMhi.js} +1 -1
  45. package/web-app/dist/assets/{MetricsPage-D9ERdpv8.js → MetricsPage-BcNyaiUO.js} +1 -1
  46. package/web-app/dist/assets/{NotFoundPage-D9ccgdbF.js → NotFoundPage-D28FqYxG.js} +1 -1
  47. package/web-app/dist/assets/{ProjectPage-B9Jslmgv.js → ProjectPage-C_O3u8Jx.js} +3 -3
  48. package/web-app/dist/assets/{ProjectsPage-DnU99Go2.js → ProjectsPage-uqdahuPA.js} +1 -1
  49. package/web-app/dist/assets/{SettingsPage-DcTyx6wo.js → SettingsPage-DLvVA9OZ.js} +1 -1
  50. package/web-app/dist/assets/{ShowcasePage-jEY9ghSr.js → ShowcasePage-BAZP8vsm.js} +1 -1
  51. package/web-app/dist/assets/{SystemSettingsPage-CSGltW-8.js → SystemSettingsPage-DPYZkLWh.js} +1 -1
  52. package/web-app/dist/assets/{TeamsPage-gUuJXb9l.js → TeamsPage-BgSyb5Pd.js} +1 -1
  53. package/web-app/dist/assets/{TemplatesPage-37Zhe--v.js → TemplatesPage-D_Jh5VU1.js} +1 -1
  54. package/web-app/dist/assets/{TerminalOutput-B09hRwaR.js → TerminalOutput-4v8poDKb.js} +1 -1
  55. package/web-app/dist/assets/{activity-q_4MY9J0.js → activity-CE2b6kdw.js} +1 -1
  56. package/web-app/dist/assets/{bell-X8OC_PFP.js → bell-CxJDlLSR.js} +1 -1
  57. package/web-app/dist/assets/{bot-C-N1PUOD.js → bot-C2WuK4zw.js} +1 -1
  58. package/web-app/dist/assets/{check-B2c76Jmp.js → check-81epVldw.js} +1 -1
  59. package/web-app/dist/assets/{chevron-left-BF76YLNw.js → chevron-left-B6EXh_3C.js} +1 -1
  60. package/web-app/dist/assets/{circle-alert-AeFKBD8h.js → circle-alert-0iqoo-iy.js} +1 -1
  61. package/web-app/dist/assets/{clock-CiXRf-FH.js → clock-CFrBpNUp.js} +1 -1
  62. package/web-app/dist/assets/{cloud-DKQ8SRJ5.js → cloud-BBSFhUT_.js} +1 -1
  63. package/web-app/dist/assets/{code-xml-DEb6jXWP.js → code-xml-BCU2V1rq.js} +1 -1
  64. package/web-app/dist/assets/{database-6W7e-E9c.js → database-D2hvO52K.js} +1 -1
  65. package/web-app/dist/assets/{dollar-sign-Ds2GOuyW.js → dollar-sign-CXFA9b6T.js} +1 -1
  66. package/web-app/dist/assets/{file-code-corner-CZU0jg52.js → file-code-corner-CqTCEEfS.js} +1 -1
  67. package/web-app/dist/assets/{file-plus-DTVYnXCN.js → file-plus-DYFbkGbs.js} +1 -1
  68. package/web-app/dist/assets/{globe-0mUzKYDe.js → globe-DpYHLobk.js} +1 -1
  69. package/web-app/dist/assets/{hammer-BMHZg3YX.js → hammer-CZ97SEYo.js} +1 -1
  70. package/web-app/dist/assets/{index-BvOTvjax.js → index-DzswgpRo.js} +3 -3
  71. package/web-app/dist/assets/{layers-Df7TdcWB.js → layers-2Tq0edx4.js} +1 -1
  72. package/web-app/dist/assets/{loader-circle-kPsVfrSY.js → loader-circle-BFP4kUX-.js} +1 -1
  73. package/web-app/dist/assets/{lock-CXYcbEc5.js → lock-DkKdZH4d.js} +1 -1
  74. package/web-app/dist/assets/{package-Cqhua1ID.js → package-ChOGD8yf.js} +1 -1
  75. package/web-app/dist/assets/{plus-lseXhsn-.js → plus-hggB1o6b.js} +1 -1
  76. package/web-app/dist/assets/{refresh-cw-OZriI6gD.js → refresh-cw-DYwVLOeF.js} +1 -1
  77. package/web-app/dist/assets/{rotate-ccw-CKCfjPg8.js → rotate-ccw-CUdY506q.js} +1 -1
  78. package/web-app/dist/assets/{scroll-text-DoVyyfKb.js → scroll-text-Wvidz5uN.js} +1 -1
  79. package/web-app/dist/assets/{server-DGy7TflW.js → server-O11QmkCN.js} +1 -1
  80. package/web-app/dist/assets/{shield-alert-BCzRcMXi.js → shield-alert-DXXx27uV.js} +1 -1
  81. package/web-app/dist/assets/{thumbs-up-C_znJd5g.js → thumbs-up-DF8ivs3A.js} +1 -1
  82. package/web-app/dist/assets/{trash-2-BosCbeeA.js → trash-2-LA6PW0Nf.js} +1 -1
  83. package/web-app/dist/assets/{trending-down-CkR5zDkV.js → trending-down-Bw_vLKHT.js} +1 -1
  84. package/web-app/dist/assets/{trending-up-DpUIfOH4.js → trending-up-tX8motha.js} +1 -1
  85. package/web-app/dist/assets/{upload-CH9aKc8L.js → upload-CxtTWJd2.js} +1 -1
  86. package/web-app/dist/assets/{usePolling-pzxrHJeg.js → usePolling-D-P5Iofq.js} +1 -1
  87. package/web-app/dist/assets/{user-B7OHa-VQ.js → user-dKTcuUFq.js} +1 -1
  88. package/web-app/dist/index.html +1 -1
@@ -7325,6 +7325,72 @@ var init_paths = __esm(() => {
7325
7325
  REPO_ROOT = findRepoRoot();
7326
7326
  });
7327
7327
 
7328
+ // ../../loki-ts/src/util/receipt_signer.ts
7329
+ import { createHash as createHash6, randomBytes as randomBytes2, createPrivateKey, createPublicKey as createPublicKey2, generateKeyPairSync, sign } from "crypto";
7330
+ import { chmodSync as chmodSync4, linkSync, mkdirSync as mkdirSync5, readFileSync as readFileSync18, statSync as statSync11, unlinkSync as unlinkSync3, writeFileSync as writeFileSync8 } from "fs";
7331
+ import { homedir as homedir8 } from "os";
7332
+ import { dirname as dirname7, join as join26 } from "path";
7333
+ function canonicalJson(x) {
7334
+ const walk = (v) => {
7335
+ if (Array.isArray(v))
7336
+ return `[${v.map(walk).join(",")}]`;
7337
+ if (v !== null && typeof v === "object") {
7338
+ const o = v;
7339
+ return `{${Object.keys(o).filter((k) => o[k] !== undefined).sort().map((k) => `${JSON.stringify(k)}:${walk(o[k])}`).join(",")}}`;
7340
+ }
7341
+ return JSON.stringify(v ?? null);
7342
+ };
7343
+ return walk(x).replace(/[\u0080-\uFFFF]/g, (c) => `\\u${c.charCodeAt(0).toString(16).padStart(4, "0")}`);
7344
+ }
7345
+ function loadSigningKey(generate = true) {
7346
+ try {
7347
+ const inline = process.env["LOKI_RECEIPT_SIGNING_KEY"]?.trim();
7348
+ let pem = inline ?? "";
7349
+ if (!inline) {
7350
+ const given = process.env["LOKI_RECEIPT_SIGNING_KEY_FILE"]?.trim();
7351
+ const file = given || join26(process.env["HOME"] || homedir8(), ".loki", "keys", RECEIPT_SIGNER_BASENAME);
7352
+ try {
7353
+ pem = readFileSync18(file);
7354
+ if (!given) {
7355
+ for (const [f, m] of [[file, 384], [dirname7(file), 448]])
7356
+ if (statSync11(f).mode & 63)
7357
+ chmodSync4(f, m);
7358
+ }
7359
+ } catch (e) {
7360
+ if (e.code !== "ENOENT" || !generate)
7361
+ return null;
7362
+ mkdirSync5(dirname7(dirname7(file)), { recursive: true, mode: 448 });
7363
+ mkdirSync5(dirname7(file), { recursive: true, mode: 448 });
7364
+ const tmp = `${file}.${randomBytes2(6).toString("hex")}.tmp`;
7365
+ writeFileSync8(tmp, generateKeyPairSync("ed25519").privateKey.export({ type: "pkcs8", format: "pem" }), { flag: "wx", mode: 384 });
7366
+ try {
7367
+ linkSync(tmp, file);
7368
+ } catch (l) {
7369
+ if (l.code !== "EEXIST")
7370
+ throw l;
7371
+ } finally {
7372
+ unlinkSync3(tmp);
7373
+ }
7374
+ pem = readFileSync18(file);
7375
+ }
7376
+ }
7377
+ const k = createPrivateKey(pem);
7378
+ return k.asymmetricKeyType === "ed25519" ? k : null;
7379
+ } catch {
7380
+ return null;
7381
+ }
7382
+ }
7383
+ function signReceipt(runId, hash) {
7384
+ const key = loadSigningKey();
7385
+ if (!key)
7386
+ return { jwt: null, kid: null };
7387
+ const kid = kidOf2(createPublicKey2(key));
7388
+ 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) }))}`;
7389
+ return { jwt: `${input}.${b64u(sign(null, Buffer.from(input), key))}`, kid };
7390
+ }
7391
+ 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());
7392
+ var init_receipt_signer = () => {};
7393
+
7328
7394
  // ../../loki-ts/src/engine10/registry.ts
7329
7395
  var init_registry = () => {};
7330
7396
 
@@ -7377,7 +7443,7 @@ var init_types = __esm(() => {
7377
7443
  });
7378
7444
 
7379
7445
  // ../../loki-ts/src/util/spec_file.ts
7380
- import { isAbsolute, join as join26, relative } from "path";
7446
+ import { isAbsolute, join as join27, relative } from "path";
7381
7447
  function specReceiptBlock(env) {
7382
7448
  const { LOKI_E10_SPEC_PATH: p, LOKI_E10_SPEC_SHA256: sha2 } = env;
7383
7449
  if (!p || !sha2)
@@ -7388,7 +7454,7 @@ function specReceiptBlock(env) {
7388
7454
  var SPEC_DIR;
7389
7455
  var init_spec_file = __esm(() => {
7390
7456
  init_types();
7391
- SPEC_DIR = join26(".loki", "specs");
7457
+ SPEC_DIR = join27(".loki", "specs");
7392
7458
  });
7393
7459
 
7394
7460
  // ../../loki-ts/src/engine10/pr_body.ts
@@ -7400,14 +7466,14 @@ function aiTrailers(i, env = process.env) {
7400
7466
  var markingOn = (env) => env["LOKI_AI_MARKING"] === "1", clean = (v) => v.replace(/[\u0000-\u001f\u007f\u2028\u2029]+/g, " ").replace(/\s+/g, " ").trim().slice(0, 100) || "unknown";
7401
7467
 
7402
7468
  // ../../loki-ts/src/util/credential_env.ts
7403
- import { mkdtempSync as mkdtempSync2, rmSync as rmSync4, statSync as statSync11, lstatSync as lstatSync4 } from "fs";
7469
+ import { mkdtempSync as mkdtempSync2, rmSync as rmSync4, statSync as statSync12, lstatSync as lstatSync4 } from "fs";
7404
7470
  import { tmpdir as tmpdir3 } from "os";
7405
- import { join as join27 } from "path";
7406
- import { randomBytes as randomBytes2 } from "crypto";
7471
+ import { join as join28 } from "path";
7472
+ import { randomBytes as randomBytes3 } from "crypto";
7407
7473
  function withheldSentinel(fresh = false) {
7408
7474
  if (fresh)
7409
7475
  sentinel = null;
7410
- return sentinel ??= `${SENTINEL_PREFIX}${process.pid}${randomBytes2(8).toString("hex")}INVALID`;
7476
+ return sentinel ??= `${SENTINEL_PREFIX}${process.pid}${randomBytes3(8).toString("hex")}INVALID`;
7411
7477
  }
7412
7478
  function removeConfigDir() {
7413
7479
  if (configDir)
@@ -7421,7 +7487,7 @@ function emptyGhConfigDir(env = process.env) {
7421
7487
  const runTmp = env["LOKI_RUN_TMP"] ?? "";
7422
7488
  const root = runTmp !== "" && isDir(runTmp) ? runTmp : tmpdir3();
7423
7489
  try {
7424
- configDir = mkdtempSync2(join27(root, "loki-gh-config-"));
7490
+ configDir = mkdtempSync2(join28(root, "loki-gh-config-"));
7425
7491
  } catch {
7426
7492
  return null;
7427
7493
  }
@@ -7462,7 +7528,7 @@ function hardenCredentialEnv(env, fresh = false) {
7462
7528
  }
7463
7529
  var GITHUB_TOKEN_VARS, SENTINEL_PREFIX = "ghp_LOKIWITHHELDsentinel", sentinel = null, configDir = null, exitHooked = false, sigtermHooked = false, isDir = (p) => {
7464
7530
  try {
7465
- return !lstatSync4(p).isSymbolicLink() && statSync11(p).isDirectory();
7531
+ return !lstatSync4(p).isSymbolicLink() && statSync12(p).isDirectory();
7466
7532
  } catch {
7467
7533
  return false;
7468
7534
  }
@@ -7588,9 +7654,9 @@ var init_run_cap = __esm(() => {
7588
7654
  });
7589
7655
 
7590
7656
  // ../../loki-ts/src/util/mutation_proof.ts
7591
- import { existsSync as existsSync15, mkdirSync as mkdirSync5, mkdtempSync as mkdtempSync3, readFileSync as readFileSync18, rmSync as rmSync5, symlinkSync, writeFileSync as writeFileSync8 } from "fs";
7657
+ import { existsSync as existsSync15, mkdirSync as mkdirSync6, mkdtempSync as mkdtempSync3, readFileSync as readFileSync19, rmSync as rmSync5, symlinkSync, writeFileSync as writeFileSync9 } from "fs";
7592
7658
  import { tmpdir as tmpdir4 } from "os";
7593
- import { basename as basename8, dirname as dirname7, isAbsolute as isAbsolute2, join as join28, relative as relative2 } from "path";
7659
+ import { basename as basename8, dirname as dirname8, isAbsolute as isAbsolute2, join as join29, relative as relative2 } from "path";
7594
7660
  function mutationProof(i) {
7595
7661
  const rels = i.wallFiles.map((f) => isAbsolute2(f.path) ? relative2(i.repoDir, f.path) : f.path);
7596
7662
  if (rels.length === 0)
@@ -7608,25 +7674,25 @@ function mutationProof(i) {
7608
7674
  const deadline = Date.now() + budgetS * 1000, remaining = () => deadline - Date.now();
7609
7675
  let parent = null, wt = null, added = false;
7610
7676
  try {
7611
- parent = mkdtempSync3(join28(tmpdir4(), "loki-mutproof-"));
7612
- wt = join28(parent, "wt");
7677
+ parent = mkdtempSync3(join29(tmpdir4(), "loki-mutproof-"));
7678
+ wt = join29(parent, "wt");
7613
7679
  const add2 = git(i.repoDir, ["worktree", "add", "--detach", wt, i.baseSha], Math.max(1000, Math.min(30000, remaining())));
7614
7680
  added = existsSync15(wt) || add2.status === 0;
7615
7681
  if (add2.status !== 0)
7616
7682
  return np("could not create the base worktree");
7617
7683
  for (const rel of rels) {
7618
- const sealed = join28(i.runDir, "wall", basename8(rel)), src = existsSync15(sealed) ? sealed : join28(i.repoDir, rel);
7684
+ const sealed = join29(i.runDir, "wall", basename8(rel)), src = existsSync15(sealed) ? sealed : join29(i.repoDir, rel);
7619
7685
  if (!existsSync15(src))
7620
7686
  return np(`Wall file missing: ${rel}`);
7621
- mkdirSync5(dirname7(join28(wt, rel)), { recursive: true });
7622
- writeFileSync8(join28(wt, rel), readFileSync18(src));
7687
+ mkdirSync6(dirname8(join29(wt, rel)), { recursive: true });
7688
+ writeFileSync9(join29(wt, rel), readFileSync19(src));
7623
7689
  }
7624
7690
  for (const rel of rels) {
7625
7691
  const parts = rel.split("/").slice(0, -1);
7626
7692
  for (let n2 = 0;n2 <= parts.length; n2++) {
7627
7693
  const anc = parts.slice(0, n2).join("/");
7628
7694
  for (const d of DEP_DIRS) {
7629
- const src = join28(i.repoDir, anc, d), dst = join28(wt, anc, d);
7695
+ const src = join29(i.repoDir, anc, d), dst = join29(wt, anc, d);
7630
7696
  if (existsSync15(src) && !existsSync15(dst)) {
7631
7697
  try {
7632
7698
  symlinkSync(src, dst);
@@ -7674,6 +7740,74 @@ var init_mutation_proof = __esm(() => {
7674
7740
  DEP_DIRS = ["node_modules", ".venv", "venv"];
7675
7741
  });
7676
7742
 
7743
+ // ../../loki-ts/src/features/speed/unit_mode.ts
7744
+ import { closeSync as closeSync5, constants, fstatSync as fstatSync4, lstatSync as lstatSync5, openSync as openSync5, readSync as readSync4, realpathSync as realpathSync14 } from "fs";
7745
+ function readBounded(path) {
7746
+ const l = lstatSync5(path);
7747
+ if (l.isSymbolicLink() || !l.isFile() || l.size > MAX_SPEC_BYTES)
7748
+ return null;
7749
+ const fd = openSync5(path, constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK);
7750
+ try {
7751
+ const st = fstatSync4(fd);
7752
+ if (!st.isFile() || st.size > MAX_SPEC_BYTES)
7753
+ return null;
7754
+ const buf = Buffer.alloc(st.size + 1);
7755
+ let n2 = 0;
7756
+ for (;; ) {
7757
+ const r = readSync4(fd, buf, n2, buf.length - n2, null);
7758
+ if (r <= 0)
7759
+ break;
7760
+ n2 += r;
7761
+ if (n2 >= buf.length)
7762
+ return null;
7763
+ }
7764
+ return buf.subarray(0, n2).toString("utf8");
7765
+ } finally {
7766
+ closeSync5(fd);
7767
+ }
7768
+ }
7769
+ function parseText(txt) {
7770
+ try {
7771
+ const j = JSON.parse(txt), ws = strs(j.writeSet), pack = strs(j.pack), tb = j.tokenBudget;
7772
+ if (typeof j.id !== "string" || !j.id || !ws || ws.length === 0 || !pack || typeof tb !== "number" || !Number.isFinite(tb) || !(tb > 0) || tb > MAX_TOKEN_BUDGET)
7773
+ return null;
7774
+ const acc = j.acceptance === undefined ? [] : strs(j.acceptance);
7775
+ if (acc === null)
7776
+ return null;
7777
+ return { id: j.id, writeSet: ws, pack, tokenBudget: tb, ...acc.length ? { acceptance: acc.map((a) => a.slice(0, 500)).slice(0, 50) } : {} };
7778
+ } catch {
7779
+ return null;
7780
+ }
7781
+ }
7782
+ function parseSpec(path) {
7783
+ try {
7784
+ const txt = readBounded(path);
7785
+ return txt === null ? null : parseText(txt);
7786
+ } catch {
7787
+ return null;
7788
+ }
7789
+ }
7790
+ function unitSpec(env = process.env) {
7791
+ const path = env["LOKI_UNIT_SPEC"], frozen = env["LOKI_UNIT_SPEC_FROZEN"];
7792
+ if (env["LOKI_SPEED"] === "0" || !path)
7793
+ return null;
7794
+ if (frozen !== undefined)
7795
+ return frozen.length <= MAX_SPEC_BYTES ? parseText(frozen) : null;
7796
+ if (!memo.has(path))
7797
+ memo.set(path, parseSpec(path));
7798
+ return memo.get(path) ?? null;
7799
+ }
7800
+ function unitFlags(staged, env = process.env, pre = {}) {
7801
+ const s = unitSpec(env);
7802
+ return s ? staged.filter(({ f }) => !inWriteSet(s, f) && !Object.hasOwn(pre, f)).map(({ f }) => f) : null;
7803
+ }
7804
+ 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);
7805
+ var init_unit_mode = __esm(() => {
7806
+ MAX_SPEC_BYTES = 1024 * 1024;
7807
+ CTRL = /[\x00-\x1f\x7f]/;
7808
+ memo = new Map;
7809
+ });
7810
+
7677
7811
  // ../../loki-ts/src/features/lean_prefix.ts
7678
7812
  var LEAN_PREFIX, STAGE_PREFIX, withStagePrefix = (brief) => process.env["LOKI_SPEED"] !== "0" ? `${STAGE_PREFIX}
7679
7813
 
@@ -7683,10 +7817,216 @@ var init_lean_prefix = __esm(() => {
7683
7817
  STAGE_PREFIX = "You are Loki, an autonomous coding agent working one stage of a run. " + "Follow the stage prompt exactly, including the role it gives you and the rules it sets, " + "and report only what you did. " + "Stage-specific instructions and the task follow this block.";
7684
7818
  });
7685
7819
 
7686
- // ../../loki-ts/src/features/wall_manifest.ts
7820
+ // ../../loki-ts/src/util/shell.ts
7821
+ async function readStreamCapped(stream, maxBytes = MAX_STDOUT_BYTES) {
7822
+ const reader = stream.getReader();
7823
+ const decoder = new TextDecoder;
7824
+ let out2 = "";
7825
+ let total = 0;
7826
+ try {
7827
+ while (total < maxBytes) {
7828
+ const { done, value } = await reader.read();
7829
+ if (done)
7830
+ break;
7831
+ if (!value)
7832
+ continue;
7833
+ total += value.byteLength;
7834
+ if (total > maxBytes) {
7835
+ const keep = value.byteLength - (total - maxBytes);
7836
+ out2 += decoder.decode(value.subarray(0, keep), { stream: true });
7837
+ break;
7838
+ }
7839
+ out2 += decoder.decode(value, { stream: true });
7840
+ }
7841
+ out2 += decoder.decode();
7842
+ } finally {
7843
+ try {
7844
+ await reader.cancel();
7845
+ } catch {}
7846
+ reader.releaseLock();
7847
+ }
7848
+ return out2;
7849
+ }
7850
+ async function run(argv, opts = {}) {
7851
+ const proc = Bun.spawn({
7852
+ cmd: [...argv],
7853
+ stdout: "pipe",
7854
+ stderr: "pipe",
7855
+ env: opts.env ? { ...process.env, ...opts.env } : process.env,
7856
+ cwd: opts.cwd
7857
+ });
7858
+ let timer;
7859
+ let killTimer;
7860
+ if (opts.timeoutMs && opts.timeoutMs > 0) {
7861
+ timer = setTimeout(() => {
7862
+ try {
7863
+ proc.kill("SIGTERM");
7864
+ } catch {}
7865
+ killTimer = setTimeout(() => {
7866
+ try {
7867
+ proc.kill("SIGKILL");
7868
+ } catch {}
7869
+ }, 2000);
7870
+ }, opts.timeoutMs);
7871
+ }
7872
+ try {
7873
+ const [stdout, stderr, exitCode] = await Promise.all([
7874
+ readStreamCapped(proc.stdout),
7875
+ new Response(proc.stderr).text(),
7876
+ proc.exited
7877
+ ]);
7878
+ return { stdout, stderr, exitCode };
7879
+ } finally {
7880
+ if (timer)
7881
+ clearTimeout(timer);
7882
+ if (killTimer)
7883
+ clearTimeout(killTimer);
7884
+ }
7885
+ }
7886
+ var MAX_STDOUT_BYTES;
7887
+ var init_shell = __esm(() => {
7888
+ MAX_STDOUT_BYTES = 16 * 1024 * 1024;
7889
+ });
7890
+
7891
+ // ../../loki-ts/src/util/python.ts
7892
+ import { accessSync, constants as constants2, existsSync as existsSync16, statSync as statSync13 } from "fs";
7687
7893
  import { spawnSync as spawnSync2 } from "child_process";
7688
- import { accessSync, constants, statSync as statSync12 } from "fs";
7689
7894
  import { tmpdir as tmpdir5 } from "os";
7895
+ function resolvePython3() {
7896
+ for (const dir of (process.env.PATH ?? "").split(":")) {
7897
+ if (!dir || !dir.startsWith("/"))
7898
+ continue;
7899
+ const cand = `${dir}/python3`;
7900
+ try {
7901
+ accessSync(cand, constants2.X_OK);
7902
+ if (statSync13(cand).isFile())
7903
+ return cand;
7904
+ } catch {}
7905
+ }
7906
+ return null;
7907
+ }
7908
+ function pySignatures(src) {
7909
+ try {
7910
+ const py = resolvePython3();
7911
+ if (!py)
7912
+ return [];
7913
+ const r = spawnSync2(py, ["-I", "-S", "-c", PY_SCRIPT], {
7914
+ input: src.replace(/\r\n?/g, `
7915
+ `),
7916
+ cwd: tmpdir5(),
7917
+ timeout: PY_TIMEOUT_MS,
7918
+ encoding: "utf8",
7919
+ env: { PATH: process.env.PATH ?? "/usr/bin:/bin" },
7920
+ maxBuffer: 8 * 1024 * 1024,
7921
+ stdio: ["pipe", "pipe", "ignore"]
7922
+ });
7923
+ if (r.error || r.status !== 0)
7924
+ return [];
7925
+ const parsed = JSON.parse(r.stdout);
7926
+ return Array.isArray(parsed) && parsed.every((l) => typeof l === "string") ? parsed : [];
7927
+ } catch {
7928
+ return [];
7929
+ }
7930
+ }
7931
+ var PY_TIMEOUT_MS = 5000, PY_SCRIPT = `
7932
+ import ast, sys, json, re
7933
+ def u(n): return ast.unparse(n)
7934
+ def chain(n):
7935
+ if isinstance(n, ast.Name): return n.id
7936
+ if isinstance(n, ast.Attribute):
7937
+ c = chain(n.value)
7938
+ return None if c is None else c + "." + n.attr
7939
+ return None
7940
+ def deco(d):
7941
+ if isinstance(d, ast.Call):
7942
+ c = chain(d.func)
7943
+ return "@" + c + "(...)" if c else "@..."
7944
+ c = chain(d)
7945
+ return "@" + c if c else "@..."
7946
+ def lit(n):
7947
+ if isinstance(n, ast.Constant): return u(n)
7948
+ if isinstance(n, ast.UnaryOp) and isinstance(n.op, ast.USub) and isinstance(n.operand, ast.Constant): return u(n)
7949
+ if isinstance(n, ast.Tuple):
7950
+ p = [lit(e) for e in n.elts]
7951
+ return None if None in p else ", ".join(p)
7952
+ return chain(n)
7953
+ def ann(n):
7954
+ if isinstance(n, (ast.Name, ast.Attribute)): return chain(n)
7955
+ if isinstance(n, ast.Constant): return u(n) if (n.value is None or n.value is Ellipsis) else None
7956
+ if isinstance(n, ast.BinOp) and isinstance(n.op, ast.BitOr):
7957
+ l, r = ann(n.left), ann(n.right)
7958
+ return None if l is None or r is None else l + " | " + r
7959
+ if isinstance(n, ast.List):
7960
+ p = [ann(e) for e in n.elts]
7961
+ return None if None in p else "[" + ", ".join(p) + "]"
7962
+ if isinstance(n, ast.Tuple):
7963
+ p = [ann(e) for e in n.elts]
7964
+ return None if None in p else ", ".join(p)
7965
+ if isinstance(n, ast.Subscript):
7966
+ v = chain(n.value)
7967
+ if v is None: return None
7968
+ last = v.split(".")[-1]
7969
+ if last == "Literal":
7970
+ s = lit(n.slice)
7971
+ return None if s is None else v + "[" + s + "]"
7972
+ if last == "Annotated" and isinstance(n.slice, ast.Tuple) and n.slice.elts:
7973
+ first = ann(n.slice.elts[0])
7974
+ return None if first is None else v + "[" + first + ", ...]"
7975
+ s = ann(n.slice)
7976
+ return None if s is None else v + "[" + s + "]"
7977
+ return None
7978
+ def a_or_dots(n):
7979
+ r = ann(n)
7980
+ return "..." if r is None else r
7981
+ def clean(n):
7982
+ r = ann(n)
7983
+ return "..." if r is None else r
7984
+ def arg(a, has_default):
7985
+ t = a.arg
7986
+ if a.annotation is not None: t += ": " + a_or_dots(a.annotation)
7987
+ if has_default: t += " = ..." if a.annotation is not None else "=..."
7988
+ return t
7989
+ def args(a):
7990
+ pos = list(a.posonlyargs) + list(a.args)
7991
+ first_default = len(pos) - len(a.defaults)
7992
+ out = []
7993
+ for i, x in enumerate(pos):
7994
+ out.append(arg(x, i >= first_default))
7995
+ if a.posonlyargs and i == len(a.posonlyargs) - 1: out.append("/")
7996
+ if a.vararg is not None: out.append("*" + arg(a.vararg, False))
7997
+ elif a.kwonlyargs: out.append("*")
7998
+ for x, d in zip(a.kwonlyargs, a.kw_defaults): out.append(arg(x, d is not None))
7999
+ if a.kwarg is not None: out.append("**" + arg(a.kwarg, False))
8000
+ return ", ".join(out)
8001
+ def fn(n, ind):
8002
+ r = [ind + deco(d) for d in n.decorator_list]
8003
+ h = ind + ("async def " if isinstance(n, ast.AsyncFunctionDef) else "def ") + n.name + "(" + args(n.args) + ")"
8004
+ if n.returns is not None: h += " -> " + a_or_dots(n.returns)
8005
+ return r + [h + ":"]
8006
+ def cls(n):
8007
+ r = [deco(d) for d in n.decorator_list]
8008
+ parts = [clean(b) for b in n.bases] + [(k.arg + "=" + clean(k.value)) if k.arg else "**..." for k in n.keywords]
8009
+ return r + ["class " + n.name + ("(" + ", ".join(parts) + ")" if parts else "") + ":"]
8010
+ tree = ast.parse(sys.stdin.read())
8011
+ out = []
8012
+ for n in tree.body:
8013
+ if isinstance(n, (ast.FunctionDef, ast.AsyncFunctionDef)) and not n.name.startswith("_"):
8014
+ out += fn(n, "")
8015
+ elif isinstance(n, ast.ClassDef) and not n.name.startswith("_"):
8016
+ out += cls(n)
8017
+ for m in n.body:
8018
+ if isinstance(m, (ast.FunctionDef, ast.AsyncFunctionDef)) and (not m.name.startswith("_") or re.fullmatch(r"__\\w+__", m.name)):
8019
+ out += fn(m, " ")
8020
+ else:
8021
+ t = n.targets[0] if isinstance(n, ast.Assign) and len(n.targets) == 1 else (n.target if isinstance(n, ast.AnnAssign) else None)
8022
+ if isinstance(t, ast.Name) and re.fullmatch(r"[A-Z][A-Z0-9_]*", t.id): out.append(t.id + " = ...")
8023
+ print(json.dumps(out))
8024
+ `;
8025
+ var init_python = __esm(() => {
8026
+ init_shell();
8027
+ });
8028
+
8029
+ // ../../loki-ts/src/util/ts_signatures.ts
7690
8030
  function templateEnd(s, i) {
7691
8031
  for (let j = i + 1;j < s.length; ) {
7692
8032
  const c = s[j];
@@ -8412,41 +8752,14 @@ function tsSignatures(src, jsx) {
8412
8752
  }
8413
8753
  return out2;
8414
8754
  }
8415
- function resolvePython3() {
8416
- for (const dir of (process.env.PATH ?? "").split(":")) {
8417
- if (!dir || !dir.startsWith("/"))
8418
- continue;
8419
- const cand = `${dir}/python3`;
8420
- try {
8421
- accessSync(cand, constants.X_OK);
8422
- if (statSync12(cand).isFile())
8423
- return cand;
8424
- } catch {}
8425
- }
8426
- return null;
8427
- }
8428
- function pySignatures(src) {
8429
- try {
8430
- const py = resolvePython3();
8431
- if (!py)
8432
- return [];
8433
- const r = spawnSync2(py, ["-I", "-S", "-c", PY_SCRIPT], {
8434
- input: norm2(src),
8435
- cwd: tmpdir5(),
8436
- timeout: PY_TIMEOUT_MS,
8437
- encoding: "utf8",
8438
- env: { PATH: process.env.PATH ?? "/usr/bin:/bin" },
8439
- maxBuffer: 8 * 1024 * 1024,
8440
- stdio: ["pipe", "pipe", "ignore"]
8441
- });
8442
- if (r.error || r.status !== 0)
8443
- return [];
8444
- const parsed = JSON.parse(r.stdout);
8445
- return Array.isArray(parsed) && parsed.every((l) => typeof l === "string") ? parsed : [];
8446
- } catch {
8447
- return [];
8448
- }
8449
- }
8755
+ 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;
8756
+ var init_ts_signatures = __esm(() => {
8757
+ MEMBER_GRAMMAR = new RegExp(`^(?:@[\\w$.]+(?:\\([^)]*\\))?\\s*)*${MODS}\\*?\\s*(?:[A-Za-z_$][\\w$]*|#[A-Za-z_$][\\w$]*|\\[[^\\]]*\\])\\s*[?!]?\\s*(?:[(<:=]|$)`);
8758
+ 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/;
8759
+ 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$])/;
8760
+ });
8761
+
8762
+ // ../../loki-ts/src/features/wall_manifest.ts
8450
8763
  function packageRunner(content) {
8451
8764
  try {
8452
8765
  const pkg = JSON.parse(content);
@@ -8459,7 +8772,7 @@ function packageRunner(content) {
8459
8772
  }
8460
8773
  }
8461
8774
  function pytestSection(content, header) {
8462
- const lines = norm2(content).split(`
8775
+ const lines = norm3(content).split(`
8463
8776
  `);
8464
8777
  const at = lines.findIndex((l) => header.test(l));
8465
8778
  if (at < 0)
@@ -8476,10 +8789,10 @@ function runnerSection(files) {
8476
8789
  if (js)
8477
8790
  out2.push(`runner: ${js}`, "config: package.json (scripts.test only)");
8478
8791
  for (const f of get(/^(bunfig\.toml|(vitest|jest)\.config\.[cm]?[jt]s)$/))
8479
- out2.push(`config: ${f.path}`, ...norm2(f.content).split(`
8792
+ out2.push(`config: ${f.path}`, ...norm3(f.content).split(`
8480
8793
  `).slice(0, 20));
8481
8794
  const py = [
8482
- ...get(/^pytest\.ini$/).map((f) => ({ f, lines: norm2(f.content).split(`
8795
+ ...get(/^pytest\.ini$/).map((f) => ({ f, lines: norm3(f.content).split(`
8483
8796
  `).filter((l) => l.trim()) })),
8484
8797
  ...get(/^pyproject\.toml$/).map((f) => ({ f, lines: pytestSection(f.content, /^\[tool\.pytest/) })),
8485
8798
  ...get(/^(setup\.cfg|tox\.ini)$/).map((f) => ({ f, lines: pytestSection(f.content, /^\[(tool:)?pytest\]/) }))
@@ -8567,13 +8880,13 @@ function importsNamed(path, raw2, stems, importStems = stems, hits = () => false
8567
8880
  function safeTs(content, path) {
8568
8881
  const jsx = /\.[jt]sx$/.test(path);
8569
8882
  try {
8570
- return tsAmbiguous(norm2(content), jsx) ? [] : tsSignatures(content, jsx);
8883
+ return tsAmbiguous(norm3(content), jsx) ? [] : tsSignatures(content, jsx);
8571
8884
  } catch {
8572
8885
  return [];
8573
8886
  }
8574
8887
  }
8575
8888
  function buildWallManifest(files, taskModules, alsoNamed = []) {
8576
- const all = [...files].sort(byPath).map((f) => ({ path: f.path.replace(/^\.\//, ""), content: norm2(f.content) }));
8889
+ const all = [...files].sort(byPath).map((f) => ({ path: f.path.replace(/^\.\//, ""), content: norm3(f.content) }));
8577
8890
  const mods = [...new Set(taskModules.map((m) => m.replace(/^\.\//, "")))].sort();
8578
8891
  const excl = [...new Set([...mods, ...alsoNamed.map((m) => m.replace(/^\.\//, ""))])];
8579
8892
  const stems = new Set(excl.flatMap(stemsOfTarget));
@@ -8623,127 +8936,33 @@ var MANIFEST_MAX_LINES = 400, MAX_EXAMPLES = 2, EXAMPLE_LINES = 40, MAX_LAYOUT =
8623
8936
  const c = content.toLowerCase().replace(/[._-]+/g, ".");
8624
8937
  return stems.some((s) => new RegExp(`(?<![a-z0-9])${s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?![a-z0-9])`).test(c));
8625
8938
  }, dirOf = (p) => p.includes("/") ? p.slice(0, p.lastIndexOf("/")) : "", resolveRel = (dir, spec) => {
8626
- const out2 = dir ? dir.split("/") : [];
8627
- for (const seg of spec.split("/")) {
8628
- if (seg === "" || seg === ".")
8629
- continue;
8630
- if (seg === "..")
8631
- out2.pop();
8632
- else
8633
- out2.push(seg);
8634
- }
8635
- return out2.join("/");
8636
- }, stemsOfTarget = (m) => {
8637
- const raw2 = stemOf(m).toLowerCase(), parent = m.split("/").slice(-2, -1)[0];
8638
- return [normStem(raw2), ...["index", "__init__", "mod", "main"].includes(raw2) && parent ? [normStem(parent)] : []];
8639
- }, norm2 = (s) => s.replace(/\r\n?/g, `
8640
- `), stemOf = (p) => (p.split("/").pop() ?? p).replace(/\.[^.]*$/, ""), byPath = (a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : a.content < b.content ? -1 : a.content > b.content ? 1 : 0, 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, PY_TIMEOUT_MS = 5000, PY_SCRIPT = `
8641
- import ast, sys, json, re
8642
- def u(n): return ast.unparse(n)
8643
- def chain(n):
8644
- if isinstance(n, ast.Name): return n.id
8645
- if isinstance(n, ast.Attribute):
8646
- c = chain(n.value)
8647
- return None if c is None else c + "." + n.attr
8648
- return None
8649
- def deco(d):
8650
- if isinstance(d, ast.Call):
8651
- c = chain(d.func)
8652
- return "@" + c + "(...)" if c else "@..."
8653
- c = chain(d)
8654
- return "@" + c if c else "@..."
8655
- def lit(n):
8656
- if isinstance(n, ast.Constant): return u(n)
8657
- if isinstance(n, ast.UnaryOp) and isinstance(n.op, ast.USub) and isinstance(n.operand, ast.Constant): return u(n)
8658
- if isinstance(n, ast.Tuple):
8659
- p = [lit(e) for e in n.elts]
8660
- return None if None in p else ", ".join(p)
8661
- return chain(n)
8662
- def ann(n):
8663
- if isinstance(n, (ast.Name, ast.Attribute)): return chain(n)
8664
- if isinstance(n, ast.Constant): return u(n) if (n.value is None or n.value is Ellipsis) else None
8665
- if isinstance(n, ast.BinOp) and isinstance(n.op, ast.BitOr):
8666
- l, r = ann(n.left), ann(n.right)
8667
- return None if l is None or r is None else l + " | " + r
8668
- if isinstance(n, ast.List):
8669
- p = [ann(e) for e in n.elts]
8670
- return None if None in p else "[" + ", ".join(p) + "]"
8671
- if isinstance(n, ast.Tuple):
8672
- p = [ann(e) for e in n.elts]
8673
- return None if None in p else ", ".join(p)
8674
- if isinstance(n, ast.Subscript):
8675
- v = chain(n.value)
8676
- if v is None: return None
8677
- last = v.split(".")[-1]
8678
- if last == "Literal":
8679
- s = lit(n.slice)
8680
- return None if s is None else v + "[" + s + "]"
8681
- if last == "Annotated" and isinstance(n.slice, ast.Tuple) and n.slice.elts:
8682
- first = ann(n.slice.elts[0])
8683
- return None if first is None else v + "[" + first + ", ...]"
8684
- s = ann(n.slice)
8685
- return None if s is None else v + "[" + s + "]"
8686
- return None
8687
- def a_or_dots(n):
8688
- r = ann(n)
8689
- return "..." if r is None else r
8690
- def clean(n):
8691
- r = ann(n)
8692
- return "..." if r is None else r
8693
- def arg(a, has_default):
8694
- t = a.arg
8695
- if a.annotation is not None: t += ": " + a_or_dots(a.annotation)
8696
- if has_default: t += " = ..." if a.annotation is not None else "=..."
8697
- return t
8698
- def args(a):
8699
- pos = list(a.posonlyargs) + list(a.args)
8700
- first_default = len(pos) - len(a.defaults)
8701
- out = []
8702
- for i, x in enumerate(pos):
8703
- out.append(arg(x, i >= first_default))
8704
- if a.posonlyargs and i == len(a.posonlyargs) - 1: out.append("/")
8705
- if a.vararg is not None: out.append("*" + arg(a.vararg, False))
8706
- elif a.kwonlyargs: out.append("*")
8707
- for x, d in zip(a.kwonlyargs, a.kw_defaults): out.append(arg(x, d is not None))
8708
- if a.kwarg is not None: out.append("**" + arg(a.kwarg, False))
8709
- return ", ".join(out)
8710
- def fn(n, ind):
8711
- r = [ind + deco(d) for d in n.decorator_list]
8712
- h = ind + ("async def " if isinstance(n, ast.AsyncFunctionDef) else "def ") + n.name + "(" + args(n.args) + ")"
8713
- if n.returns is not None: h += " -> " + a_or_dots(n.returns)
8714
- return r + [h + ":"]
8715
- def cls(n):
8716
- r = [deco(d) for d in n.decorator_list]
8717
- parts = [clean(b) for b in n.bases] + [(k.arg + "=" + clean(k.value)) if k.arg else "**..." for k in n.keywords]
8718
- return r + ["class " + n.name + ("(" + ", ".join(parts) + ")" if parts else "") + ":"]
8719
- tree = ast.parse(sys.stdin.read())
8720
- out = []
8721
- for n in tree.body:
8722
- if isinstance(n, (ast.FunctionDef, ast.AsyncFunctionDef)) and not n.name.startswith("_"):
8723
- out += fn(n, "")
8724
- elif isinstance(n, ast.ClassDef) and not n.name.startswith("_"):
8725
- out += cls(n)
8726
- for m in n.body:
8727
- if isinstance(m, (ast.FunctionDef, ast.AsyncFunctionDef)) and (not m.name.startswith("_") or re.fullmatch(r"__\\w+__", m.name)):
8728
- out += fn(m, " ")
8729
- else:
8730
- t = n.targets[0] if isinstance(n, ast.Assign) and len(n.targets) == 1 else (n.target if isinstance(n, ast.AnnAssign) else None)
8731
- if isinstance(t, ast.Name) and re.fullmatch(r"[A-Z][A-Z0-9_]*", t.id): out.append(t.id + " = ...")
8732
- print(json.dumps(out))
8733
- `;
8939
+ const out2 = dir ? dir.split("/") : [];
8940
+ for (const seg of spec.split("/")) {
8941
+ if (seg === "" || seg === ".")
8942
+ continue;
8943
+ if (seg === "..")
8944
+ out2.pop();
8945
+ else
8946
+ out2.push(seg);
8947
+ }
8948
+ return out2.join("/");
8949
+ }, stemsOfTarget = (m) => {
8950
+ const raw2 = stemOf(m).toLowerCase(), parent = m.split("/").slice(-2, -1)[0];
8951
+ return [normStem(raw2), ...["index", "__init__", "mod", "main"].includes(raw2) && parent ? [normStem(parent)] : []];
8952
+ }, norm3 = (s) => s.replace(/\r\n?/g, `
8953
+ `), stemOf = (p) => (p.split("/").pop() ?? p).replace(/\.[^.]*$/, ""), byPath = (a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : a.content < b.content ? -1 : a.content > b.content ? 1 : 0;
8734
8954
  var init_wall_manifest = __esm(() => {
8955
+ init_python();
8956
+ init_ts_signatures();
8735
8957
  TEST_PATH = /(^|\/)(tests?|__tests__|spec)\/|\.(test|spec)\.[jt]sx?$|(^|\/)test_[^/]*\.py$|_test\.(py|go)$/;
8736
8958
  TEST_FILE = /\.(test|spec)\.[cm]?[jt]sx?$|(^|\/)test_[^/]*\.py$|_test\.py$/;
8737
8959
  EXAMPLE_EXT = /\.(py|[cm]?[jt]sx?)$/;
8738
8960
  GENERIC_STEMS = ["index", "init", "mod", "main"];
8739
- MEMBER_GRAMMAR = new RegExp(`^(?:@[\\w$.]+(?:\\([^)]*\\))?\\s*)*${MODS}\\*?\\s*(?:[A-Za-z_$][\\w$]*|#[A-Za-z_$][\\w$]*|\\[[^\\]]*\\])\\s*[?!]?\\s*(?:[(<:=]|$)`);
8740
- 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/;
8741
- 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$])/;
8742
8961
  });
8743
8962
 
8744
8963
  // ../../loki-ts/src/features/wall_manifest_wire.ts
8745
- import { createHash as createHash6 } from "crypto";
8746
- import { basename as basename9, dirname as dirname8 } from "path";
8964
+ import { createHash as createHash7 } from "crypto";
8965
+ import { basename as basename9, dirname as dirname9 } from "path";
8747
8966
  function blobEntries(ls) {
8748
8967
  const out2 = [];
8749
8968
  for (const raw2 of ls.toString("latin1").split("\x00")) {
@@ -8773,7 +8992,7 @@ function wallManifestFor(repoDir, tree, task, env = process.env) {
8773
8992
  return null;
8774
8993
  const low = task.toLowerCase().replace(/\\/g, "/");
8775
8994
  const token = (n2) => new RegExp(`(?<![A-Za-z0-9_])${n2.toLowerCase().replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?![A-Za-z0-9_])`).test(low);
8776
- const dirModule = (e) => /^(index|__init__|mod|main)\.[^.]+$/.test(basename9(e.path)) && e.path.includes("/") && token(dirname8(e.path));
8995
+ const dirModule = (e) => /^(index|__init__|mod|main)\.[^.]+$/.test(basename9(e.path)) && e.path.includes("/") && token(dirname9(e.path));
8777
8996
  const matched = all.filter((e) => SOURCE.test(e.path) && (token(basename9(e.path)) || token(e.path) || token(basename9(e.path).replace(/\.[^.]*$/, "")) || dirModule(e)));
8778
8997
  const spans = (p) => [...low.matchAll(new RegExp(`(?<![A-Za-z0-9_])${p.toLowerCase().replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?![A-Za-z0-9_])`, "g"))].map((m) => [m.index, m.index + m[0].length]);
8779
8998
  const occ = new Map(matched.map((e) => [e, spans(e.path)]));
@@ -8802,7 +9021,7 @@ function wallManifestFor(repoDir, tree, task, env = process.env) {
8802
9021
  if (at !== cat.length)
8803
9022
  return null;
8804
9023
  const text2 = buildWallManifest([...files, ...listed.map((e) => ({ path: e.path, content: "" }))], named.map((e) => e.path), matched.map((e) => e.path));
8805
- return { text: text2, sha256: createHash6("sha256").update(text2).digest("hex") };
9024
+ return { text: text2, sha256: createHash7("sha256").update(text2).digest("hex") };
8806
9025
  } catch {
8807
9026
  return null;
8808
9027
  }
@@ -8834,12 +9053,12 @@ Example: test('name', () => { assert.strictEqual(fn(1), 2); }); Run: node --tes
8834
9053
  });
8835
9054
 
8836
9055
  // ../../loki-ts/src/engine10/cache.ts
8837
- import { createHash as createHash7 } from "crypto";
8838
- import { existsSync as existsSync16, mkdirSync as mkdirSync6, readFileSync as readFileSync19, writeFileSync as writeFileSync9 } from "fs";
9056
+ import { createHash as createHash8 } from "crypto";
9057
+ import { existsSync as existsSync17, mkdirSync as mkdirSync7, readFileSync as readFileSync20, writeFileSync as writeFileSync10 } from "fs";
8839
9058
  import { resolve as resolve8 } from "path";
8840
9059
  function repoKey(originUrl, repoDir) {
8841
9060
  const basis = originUrl && originUrl.trim() !== "" ? originUrl : resolve8(repoDir);
8842
- return createHash7("sha256").update(basis).digest("hex");
9061
+ return createHash8("sha256").update(basis).digest("hex");
8843
9062
  }
8844
9063
  function defaultCacheRoot() {
8845
9064
  return resolve8(homeLokiDir(), "cache", "v10");
@@ -8848,10 +9067,10 @@ function repoCacheDir(key, cacheRoot = defaultCacheRoot()) {
8848
9067
  return resolve8(cacheRoot, key);
8849
9068
  }
8850
9069
  function readJson3(path, isValid = (_v) => true) {
8851
- if (!existsSync16(path))
9070
+ if (!existsSync17(path))
8852
9071
  return null;
8853
9072
  try {
8854
- const parsed = JSON.parse(readFileSync19(path, "utf8"));
9073
+ const parsed = JSON.parse(readFileSync20(path, "utf8"));
8855
9074
  return isValid(parsed) ? parsed : null;
8856
9075
  } catch {
8857
9076
  return null;
@@ -8901,12 +9120,12 @@ function speedLikelyFiles(task, map, env = process.env) {
8901
9120
  var init_lean_select = () => {};
8902
9121
 
8903
9122
  // ../../loki-ts/src/util/claude_model.ts
8904
- import { readFileSync as readFileSync20 } from "fs";
9123
+ import { readFileSync as readFileSync21 } from "fs";
8905
9124
  import { resolve as resolve9 } from "path";
8906
9125
  function resolveClaudeModel(want) {
8907
9126
  try {
8908
9127
  const path = process.env["LOKI_MODEL_CATALOG"] || resolve9(REPO_ROOT, "providers/model_catalog.json");
8909
- return JSON.parse(readFileSync20(path, "utf8")).providers?.claude?.cli_aliases?.[want] ?? want;
9128
+ return JSON.parse(readFileSync21(path, "utf8")).providers?.claude?.cli_aliases?.[want] ?? want;
8910
9129
  } catch {
8911
9130
  return want;
8912
9131
  }
@@ -8966,8 +9185,8 @@ function routerMode(env = process.env) {
8966
9185
  var routerEnabled = (env = process.env) => routerMode(env) === "on";
8967
9186
 
8968
9187
  // ../../loki-ts/src/engine10/cost.ts
8969
- import { mkdirSync as mkdirSync7, readdirSync as readdirSync9, readFileSync as readFileSync21, writeFileSync as writeFileSync10 } from "fs";
8970
- import { join as join29 } from "path";
9188
+ import { readdirSync as readdirSync9, readFileSync as readFileSync22 } from "fs";
9189
+ import { join as join30 } from "path";
8971
9190
  function routerTelemetryOn(env = process.env) {
8972
9191
  return routerEnabled(env);
8973
9192
  }
@@ -8975,7 +9194,7 @@ function num3(v) {
8975
9194
  return typeof v === "number" && Number.isFinite(v) ? v : 0;
8976
9195
  }
8977
9196
  function resultCostPath(lokiRoot, iteration) {
8978
- return join29(lokiRoot, "metrics", `result-cost-${iteration}.json`);
9197
+ return join30(lokiRoot, "metrics", `result-cost-${iteration}.json`);
8979
9198
  }
8980
9199
  function sumResultCosts(lokiRoot, iterations, context = []) {
8981
9200
  const out2 = {
@@ -9006,7 +9225,7 @@ function sumResultCosts(lokiRoot, iterations, context = []) {
9006
9225
  const parsed = new Map;
9007
9226
  for (const iter of iterations) {
9008
9227
  try {
9009
- parsed.set(iter, JSON.parse(readFileSync21(resultCostPath(lokiRoot, iter), "utf8")));
9228
+ parsed.set(iter, JSON.parse(readFileSync22(resultCostPath(lokiRoot, iter), "utf8")));
9010
9229
  } catch {}
9011
9230
  }
9012
9231
  const own = new Set(iterations);
@@ -9015,7 +9234,7 @@ function sumResultCosts(lokiRoot, iterations, context = []) {
9015
9234
  if (own.has(iter))
9016
9235
  continue;
9017
9236
  try {
9018
- lookup.push({ iter, rec: JSON.parse(readFileSync21(resultCostPath(lokiRoot, iter), "utf8")) });
9237
+ lookup.push({ iter, rec: JSON.parse(readFileSync22(resultCostPath(lokiRoot, iter), "utf8")) });
9019
9238
  } catch {}
9020
9239
  }
9021
9240
  const verdicts = hooks.costRecords?.resumeVerdicts(lookup) ?? { ambiguous: [], separate: [] };
@@ -9110,34 +9329,36 @@ function sumResultCosts(lokiRoot, iterations, context = []) {
9110
9329
  var UNMETERED = "cli-invoker-unmetered";
9111
9330
  var init_cost = __esm(() => {
9112
9331
  init_hooks();
9332
+ init_provider_cost();
9333
+ init_provider_cost();
9113
9334
  });
9114
9335
 
9115
9336
  // ../../loki-ts/src/util/atomic.ts
9116
9337
  import {
9117
- closeSync as closeSync5,
9118
- fstatSync as fstatSync4,
9338
+ closeSync as closeSync6,
9339
+ fstatSync as fstatSync5,
9119
9340
  fsyncSync as fsyncSync2,
9120
- lstatSync as lstatSync5,
9121
- mkdirSync as mkdirSync8,
9122
- openSync as openSync5,
9123
- readSync as readSync4,
9341
+ lstatSync as lstatSync6,
9342
+ mkdirSync as mkdirSync9,
9343
+ openSync as openSync6,
9344
+ readSync as readSync5,
9124
9345
  renameSync as renameSync4,
9125
9346
  rmSync as rmSync6,
9126
- statSync as statSync13,
9127
- unlinkSync as unlinkSync3,
9128
- writeFileSync as writeFileSync11,
9347
+ statSync as statSync14,
9348
+ unlinkSync as unlinkSync4,
9349
+ writeFileSync as writeFileSync12,
9129
9350
  writeSync as writeSync3
9130
9351
  } from "fs";
9131
- import { dirname as dirname9 } from "path";
9352
+ import { dirname as dirname10 } from "path";
9132
9353
  function durableWriteThenRename(target, body) {
9133
- mkdirSync8(dirname9(target), { recursive: true });
9354
+ mkdirSync9(dirname10(target), { recursive: true });
9134
9355
  const tmp = tmpPathFor(target);
9135
- const fd = openSync5(tmp, "w");
9356
+ const fd = openSync6(tmp, "w");
9136
9357
  try {
9137
9358
  writeSync3(fd, body);
9138
9359
  fsyncSync2(fd);
9139
9360
  } finally {
9140
- closeSync5(fd);
9361
+ closeSync6(fd);
9141
9362
  }
9142
9363
  renameSync4(tmp, target);
9143
9364
  fsyncParentDir(target);
@@ -9145,12 +9366,12 @@ function durableWriteThenRename(target, body) {
9145
9366
  function fsyncParentDir(target) {
9146
9367
  let dfd = null;
9147
9368
  try {
9148
- dfd = openSync5(dirname9(target), "r");
9369
+ dfd = openSync6(dirname10(target), "r");
9149
9370
  fsyncSync2(dfd);
9150
9371
  } catch {} finally {
9151
9372
  if (dfd !== null) {
9152
9373
  try {
9153
- closeSync5(dfd);
9374
+ closeSync6(dfd);
9154
9375
  } catch {}
9155
9376
  }
9156
9377
  }
@@ -9168,14 +9389,14 @@ var init_atomic = __esm(() => {
9168
9389
  });
9169
9390
 
9170
9391
  // ../../loki-ts/src/runner/budget.ts
9171
- import { existsSync as existsSync17, mkdirSync as mkdirSync9, readdirSync as readdirSync10, readFileSync as readFileSync22, writeFileSync as writeFileSync12 } from "fs";
9172
- import { dirname as dirname10, join as join30, resolve as resolve10 } from "path";
9392
+ import { existsSync as existsSync18, mkdirSync as mkdirSync10, readdirSync as readdirSync10, readFileSync as readFileSync23, writeFileSync as writeFileSync13 } from "fs";
9393
+ import { dirname as dirname11, join as join31, resolve as resolve10 } from "path";
9173
9394
  import { fileURLToPath as fileURLToPath2 } from "url";
9174
9395
  function loadDefaultMaxCostUsd() {
9175
- const here = dirname10(fileURLToPath2(import.meta.url));
9396
+ const here = dirname11(fileURLToPath2(import.meta.url));
9176
9397
  for (const rel of [["..", "..", "data"], ["..", "data"]]) {
9177
9398
  try {
9178
- const j = JSON.parse(readFileSync22(resolve10(here, ...rel, "model-pricing.json"), "utf8"));
9399
+ const j = JSON.parse(readFileSync23(resolve10(here, ...rel, "model-pricing.json"), "utf8"));
9179
9400
  if (typeof j.default_max_cost_usd === "number" && Number.isFinite(j.default_max_cost_usd) && j.default_max_cost_usd > 0)
9180
9401
  return j.default_max_cost_usd;
9181
9402
  } catch {}
@@ -9184,11 +9405,11 @@ function loadDefaultMaxCostUsd() {
9184
9405
  }
9185
9406
  function _loadPricing() {
9186
9407
  try {
9187
- const moduleDir = dirname10(fileURLToPath2(import.meta.url));
9408
+ const moduleDir = dirname11(fileURLToPath2(import.meta.url));
9188
9409
  const jsonPath = resolve10(moduleDir, "..", "..", "data", "model-pricing.json");
9189
- if (!existsSync17(jsonPath))
9410
+ if (!existsSync18(jsonPath))
9190
9411
  return _FALLBACK_PRICING;
9191
- const raw2 = JSON.parse(readFileSync22(jsonPath, "utf8"));
9412
+ const raw2 = JSON.parse(readFileSync23(jsonPath, "utf8"));
9192
9413
  const pricing2 = raw2["pricing"];
9193
9414
  if (!pricing2 || typeof pricing2 !== "object")
9194
9415
  return _FALLBACK_PRICING;
@@ -9318,7 +9539,7 @@ var init_model_rank = __esm(() => {
9318
9539
  });
9319
9540
 
9320
9541
  // ../../loki-ts/src/engine10/sizing.ts
9321
- import { existsSync as existsSync18, readFileSync as readFileSync23 } from "fs";
9542
+ import { existsSync as existsSync19, readFileSync as readFileSync24 } from "fs";
9322
9543
  import { basename as basename10 } from "path";
9323
9544
  function namedFiles(task, map) {
9324
9545
  return (map?.files ?? []).filter((f) => task.toLowerCase().includes(basename10(f).toLowerCase()));
@@ -9337,10 +9558,10 @@ function sizeTask(task, map, tests) {
9337
9558
  return why.length ? { size: "normal", reasons: why } : { size: "small", reasons: [`task ${task.length} chars, names ${named} files`] };
9338
9559
  }
9339
9560
  function loadRepoMap(repomapRef) {
9340
- if (!repomapRef || !existsSync18(repomapRef))
9561
+ if (!repomapRef || !existsSync19(repomapRef))
9341
9562
  return null;
9342
9563
  try {
9343
- return JSON.parse(readFileSync23(repomapRef, "utf8"));
9564
+ return JSON.parse(readFileSync24(repomapRef, "utf8"));
9344
9565
  } catch {
9345
9566
  return null;
9346
9567
  }
@@ -9371,8 +9592,8 @@ var init_sizing = __esm(() => {
9371
9592
  });
9372
9593
 
9373
9594
  // ../../loki-ts/src/project_model/schema.ts
9374
- import { existsSync as existsSync19, lstatSync as lstatSync6, realpathSync as realpathSync14, statSync as statSync14 } from "fs";
9375
- import { isAbsolute as isAbsolute3, join as join32, normalize, sep as sep9 } from "path";
9595
+ import { existsSync as existsSync20, lstatSync as lstatSync7, realpathSync as realpathSync15, statSync as statSync15 } from "fs";
9596
+ import { isAbsolute as isAbsolute3, join as join33, normalize, sep as sep9 } from "path";
9376
9597
  function safeRel(p) {
9377
9598
  const n2 = normalize(p.trim()).split(sep9).join("/").replace(/\/$/, "");
9378
9599
  if (n2 === "" || isAbsolute3(n2) || n2 === ".." || n2.startsWith("../"))
@@ -9381,8 +9602,8 @@ function safeRel(p) {
9381
9602
  }
9382
9603
  function insideRepo(repoDir, rel) {
9383
9604
  try {
9384
- const real2 = realpathSync14(join32(repoDir, rel));
9385
- const base = realpathSync14(repoDir);
9605
+ const real2 = realpathSync15(join33(repoDir, rel));
9606
+ const base = realpathSync15(repoDir);
9386
9607
  return real2 === base || real2.startsWith(base + sep9);
9387
9608
  } catch {
9388
9609
  return false;
@@ -9394,8 +9615,8 @@ function checkCitation(repoDir, raw2) {
9394
9615
  const rel = safeRel(raw2.replace(/:\d+(?:-\d+)?$/, ""));
9395
9616
  if (rel === null)
9396
9617
  return `citation "${raw2}" is not a repo-relative path`;
9397
- const abs = join32(repoDir, rel);
9398
- if (!existsSync19(abs) || !statSync14(abs).isFile() || !insideRepo(repoDir, rel))
9618
+ const abs = join33(repoDir, rel);
9619
+ if (!existsSync20(abs) || !statSync15(abs).isFile() || !insideRepo(repoDir, rel))
9399
9620
  return `citation "${raw2}" names a file that does not exist`;
9400
9621
  return null;
9401
9622
  }
@@ -9406,7 +9627,7 @@ function checkFingerprint(repoDir, raw2) {
9406
9627
  if (rel === null)
9407
9628
  return `fingerprint "${raw2}" is not a repo-relative path`;
9408
9629
  try {
9409
- if (!lstatSync6(join32(repoDir, rel)).isFile() || !insideRepo(repoDir, rel))
9630
+ if (!lstatSync7(join33(repoDir, rel)).isFile() || !insideRepo(repoDir, rel))
9410
9631
  return `fingerprint "${raw2}" is not a regular file inside the repo`;
9411
9632
  } catch {
9412
9633
  return `fingerprint "${raw2}" names a file that does not exist`;
@@ -9434,8 +9655,8 @@ function checkDir(repoDir, v, where, errs) {
9434
9655
  errs.push(`${where}: must be a repo-relative directory ("." for the repo root)`);
9435
9656
  return ".";
9436
9657
  }
9437
- const abs = join32(repoDir, rel);
9438
- if (!existsSync19(abs) || !statSync14(abs).isDirectory() || !insideRepo(repoDir, rel))
9658
+ const abs = join33(repoDir, rel);
9659
+ if (!existsSync20(abs) || !statSync15(abs).isDirectory() || !insideRepo(repoDir, rel))
9439
9660
  errs.push(`${where}: directory "${rel}" does not exist`);
9440
9661
  return rel;
9441
9662
  }
@@ -9537,9 +9758,9 @@ var init_schema = __esm(() => {
9537
9758
  });
9538
9759
 
9539
9760
  // ../../loki-ts/src/project_model/gather.ts
9540
- import { createHash as createHash8 } from "crypto";
9541
- import { closeSync as closeSync6, lstatSync as lstatSync7, openSync as openSync6, readFileSync as readFileSync24, readSync as readSync5 } from "fs";
9542
- import { dirname as dirname11, join as join33 } from "path";
9761
+ import { createHash as createHash9 } from "crypto";
9762
+ import { closeSync as closeSync7, lstatSync as lstatSync8, openSync as openSync7, readFileSync as readFileSync25, readSync as readSync6 } from "fs";
9763
+ import { dirname as dirname12, join as join34 } from "path";
9543
9764
  function listShallow(repoDir) {
9544
9765
  try {
9545
9766
  const out2 = safeGit(repoDir, ["ls-files", "-z"], { maxBuffer: 256 * 1024 * 1024 });
@@ -9549,10 +9770,10 @@ function listShallow(repoDir) {
9549
9770
  }
9550
9771
  }
9551
9772
  function shallowDirs(repoDir) {
9552
- return [...new Set(listShallow(repoDir).map((p) => dirname11(p)))].sort();
9773
+ return [...new Set(listShallow(repoDir).map((p) => dirname12(p)))].sort();
9553
9774
  }
9554
9775
  function computeKey(repoDir, fingerprintFiles, dirs, committedHash = null) {
9555
- const h = createHash8("sha256");
9776
+ const h = createHash9("sha256");
9556
9777
  h.update(`${MODEL_REV}
9557
9778
  `);
9558
9779
  if (committedHash !== null)
@@ -9565,9 +9786,9 @@ function computeKey(repoDir, fingerprintFiles, dirs, committedHash = null) {
9565
9786
  if (checkFingerprint(repoDir, f) !== null)
9566
9787
  h.update("invalid");
9567
9788
  else {
9568
- const abs = join33(repoDir, f);
9569
- const size = lstatSync7(abs).size;
9570
- h.update(size <= GATHER_CAPS.maxHashBytes ? readFileSync24(abs) : `too-large:${size}`);
9789
+ const abs = join34(repoDir, f);
9790
+ const size = lstatSync8(abs).size;
9791
+ h.update(size <= GATHER_CAPS.maxHashBytes ? readFileSync25(abs) : `too-large:${size}`);
9571
9792
  }
9572
9793
  } catch {
9573
9794
  h.update("missing");
@@ -9592,7 +9813,7 @@ function committedModelHash(raw2) {
9592
9813
  if (typeof raw2 !== "object" || raw2 === null || Array.isArray(raw2))
9593
9814
  return null;
9594
9815
  const { key: _key, expiresAt: _exp, ...rest } = raw2;
9595
- return createHash8("sha256").update(JSON.stringify(rest)).digest("hex");
9816
+ return createHash9("sha256").update(JSON.stringify(rest)).digest("hex");
9596
9817
  }
9597
9818
  var GATHER_CAPS, depthOf = (p) => p.split("/").length, MODEL_REV = "model-rev:2";
9598
9819
  var init_gather = __esm(() => {
@@ -9610,14 +9831,14 @@ var init_gather = __esm(() => {
9610
9831
  });
9611
9832
 
9612
9833
  // ../../loki-ts/src/project_model/discover.ts
9613
- import { existsSync as existsSync20, mkdirSync as mkdirSync10, readFileSync as readFileSync25, rmSync as rmSync7, writeFileSync as writeFileSync13 } from "fs";
9614
- import { join as join34 } from "path";
9834
+ import { existsSync as existsSync21, mkdirSync as mkdirSync11, readFileSync as readFileSync26, rmSync as rmSync7, writeFileSync as writeFileSync14 } from "fs";
9835
+ import { join as join35 } from "path";
9615
9836
  function projectModelEnabled(env = process.env) {
9616
9837
  return env["LOKI_E10_PROJECT_MODEL"] !== "0";
9617
9838
  }
9618
9839
  function loadCached(repoDir, now2 = Date.now()) {
9619
9840
  try {
9620
- return parseCached(repoDir, JSON.parse(readFileSync25(join34(repoDir, PROJECT_FILE), "utf8")), now2);
9841
+ return parseCached(repoDir, JSON.parse(readFileSync26(join35(repoDir, PROJECT_FILE), "utf8")), now2);
9621
9842
  } catch {
9622
9843
  return null;
9623
9844
  }
@@ -9632,7 +9853,7 @@ function loadCommitted(repoDir) {
9632
9853
  };
9633
9854
  let raw2;
9634
9855
  try {
9635
- raw2 = JSON.parse(readFileSync25(join34(repoDir, PROJECT_FILE), "utf8"));
9856
+ raw2 = JSON.parse(readFileSync26(join35(repoDir, PROJECT_FILE), "utf8"));
9636
9857
  } catch (e) {
9637
9858
  return ignore(`unreadable JSON (${e instanceof Error ? e.message : String(e)})`);
9638
9859
  }
@@ -9705,7 +9926,7 @@ var clean2 = (p) => typeof p === "string" ? posix.normalize(p.replace(/\\/g, "/"
9705
9926
  var init_api = () => {};
9706
9927
 
9707
9928
  // ../../loki-ts/src/project_model/resolve.ts
9708
- import { join as join35 } from "path";
9929
+ import { join as join36 } from "path";
9709
9930
  function loadProjectApi(repoDir, env = process.env) {
9710
9931
  if (!projectModelEnabled(env))
9711
9932
  return null;
@@ -9727,7 +9948,7 @@ function siteFor(api, repoDir, file) {
9727
9948
  const rel = api.relativeToRoot(file);
9728
9949
  if (!pkg || pkg.root === "." || rel === null)
9729
9950
  return null;
9730
- return { pkg, root: pkg.root, cwd: join35(repoDir, pkg.root), file: rel };
9951
+ return { pkg, root: pkg.root, cwd: join36(repoDir, pkg.root), file: rel };
9731
9952
  }
9732
9953
  function groupByPackage(api, repoDir, files) {
9733
9954
  const out2 = new Map;
@@ -9760,9 +9981,9 @@ var init_scope = __esm(() => {
9760
9981
 
9761
9982
  // ../../loki-ts/src/e10ext/assert_delta.ts
9762
9983
  import { execFileSync as execFileSync5 } from "child_process";
9763
- import { mkdtempSync as mkdtempSync4, readFileSync as readFileSync26, rmSync as rmSync8 } from "fs";
9984
+ import { mkdtempSync as mkdtempSync4, readFileSync as readFileSync27, rmSync as rmSync8 } from "fs";
9764
9985
  import { tmpdir as tmpdir6 } from "os";
9765
- import { join as join36 } from "path";
9986
+ import { join as join37 } from "path";
9766
9987
  import { basename as basename11 } from "path";
9767
9988
  function classifyAssertDelta(inp) {
9768
9989
  const f = basename11(inp.path);
@@ -9772,7 +9993,7 @@ function classifyAssertDelta(inp) {
9772
9993
  return WEAKENED;
9773
9994
  let cwd = "";
9774
9995
  try {
9775
- cwd = mkdtempSync4(join36(tmpdir6(), "loki-ad-"));
9996
+ cwd = mkdtempSync4(join37(tmpdir6(), "loki-ad-"));
9776
9997
  const raw2 = execFileSync5("python3", ["-I", "-c", PY], {
9777
9998
  input: JSON.stringify({ path: inp.path, base: inp.base, head: inp.head, task: inp.task }),
9778
9999
  cwd,
@@ -9803,7 +10024,7 @@ function assertDeltaNotes(repoDir, baseSha, headRef, path, task, baseCounts, hea
9803
10024
  return null;
9804
10025
  try {
9805
10026
  const show = (ref) => safeGit(repoDir, ["show", `${ref}:${path}`]);
9806
- const head = headRef ? show(headRef) : readFileSync26(join36(repoDir, path), "utf8");
10027
+ const head = headRef ? show(headRef) : readFileSync27(join37(repoDir, path), "utf8");
9807
10028
  const r = classifyTestEdit(path, show(baseSha), head, task, baseCounts, headCounts);
9808
10029
  return r[0]?.kind === "literal-only" ? r.map((e) => `assertion value changed (not shown to be required by the task): ${e.file}:${e.line} ${e.old} -> ${e.new}`) : null;
9809
10030
  } catch {
@@ -10027,7 +10248,7 @@ var init_runner_errors = __esm(() => {
10027
10248
  });
10028
10249
 
10029
10250
  // ../../loki-ts/src/engine10/testmap.ts
10030
- import { basename as basename12, extname, join as join37, relative as relative3, sep as sep10 } from "path";
10251
+ import { basename as basename12, extname, join as join38, relative as relative3, sep as sep10 } from "path";
10031
10252
  function isTestFile(rel) {
10032
10253
  const name = basename12(rel);
10033
10254
  return JS_TEST_RE.test(name) || PY_TEST_RE.test(name) || GO_TEST_RE.test(name);
@@ -10057,8 +10278,8 @@ var init_testmap = __esm(() => {
10057
10278
  });
10058
10279
 
10059
10280
  // ../../loki-ts/src/util/check_result.ts
10060
- import { mkdirSync as mkdirSync11, readFileSync as readFileSync27, renameSync as renameSync5, writeFileSync as writeFileSync14 } from "fs";
10061
- import { basename as basename13, join as join38 } from "path";
10281
+ import { mkdirSync as mkdirSync12, readFileSync as readFileSync28, renameSync as renameSync5, writeFileSync as writeFileSync15 } from "fs";
10282
+ import { basename as basename13, join as join39 } from "path";
10062
10283
  function plainTestEnv(base = process.env) {
10063
10284
  const e = {};
10064
10285
  for (const [k, v] of Object.entries(tokenFreeEnv(base)))
@@ -10324,10 +10545,10 @@ function classifyRisk(files, env = process.env) {
10324
10545
  };
10325
10546
  }
10326
10547
  function changedFiles(repoDir, baseSha) {
10327
- const run = (args) => safeGit(repoDir, args).split(`
10548
+ const run2 = (args) => safeGit(repoDir, args).split(`
10328
10549
  `).map((l) => l.trim()).filter(Boolean);
10329
- const tracked = run(["diff", "--name-only", baseSha]);
10330
- const untracked = run(["ls-files", "--others", "--exclude-standard"]);
10550
+ const tracked = run2(["diff", "--name-only", baseSha]);
10551
+ const untracked = run2(["ls-files", "--others", "--exclude-standard"]);
10331
10552
  return [...new Set([...tracked, ...untracked])].filter((f) => !f.startsWith(".loki/"));
10332
10553
  }
10333
10554
  function diffFiles(repoDir, baseSha) {
@@ -10350,7 +10571,7 @@ function diffFiles(repoDir, baseSha) {
10350
10571
  return f;
10351
10572
  let n2 = 0;
10352
10573
  try {
10353
- n2 = readFileSync27(join38(repoDir, path), "utf8").split(`
10574
+ n2 = readFileSync28(join39(repoDir, path), "utf8").split(`
10354
10575
  `).length;
10355
10576
  } catch {}
10356
10577
  return { path, added: n2, deleted: 0, status: "added" };
@@ -10358,9 +10579,9 @@ function diffFiles(repoDir, baseSha) {
10358
10579
  }
10359
10580
  function recordRisk(repoDir, a) {
10360
10581
  try {
10361
- const dir = join38(repoDir, ".loki", "state"), p = join38(dir, "risk-tier.json");
10362
- mkdirSync11(dir, { recursive: true });
10363
- writeFileSync14(`${p}.tmp`, JSON.stringify(a, null, 2) + `
10582
+ const dir = join39(repoDir, ".loki", "state"), p = join39(dir, "risk-tier.json");
10583
+ mkdirSync12(dir, { recursive: true });
10584
+ writeFileSync15(`${p}.tmp`, JSON.stringify(a, null, 2) + `
10364
10585
  `);
10365
10586
  renameSync5(`${p}.tmp`, p);
10366
10587
  return p;
@@ -10401,12 +10622,12 @@ var init_check_result = __esm(() => {
10401
10622
 
10402
10623
  // ../../loki-ts/src/runner/load_owner.ts
10403
10624
  import { spawnSync as spawnSync3 } from "child_process";
10404
- import { mkdtempSync as mkdtempSync5, realpathSync as realpathSync15, rmSync as rmSync9 } from "fs";
10625
+ import { mkdtempSync as mkdtempSync5, realpathSync as realpathSync16, rmSync as rmSync9 } from "fs";
10405
10626
  import { tmpdir as tmpdir7 } from "os";
10406
- import { basename as basename14, extname as extname2, join as join39 } from "path";
10627
+ import { basename as basename14, extname as extname2, join as join40 } from "path";
10407
10628
  function changedFilesNamed(out2, repoDir, changed) {
10408
10629
  return changed.filter((f) => {
10409
- if (out2.includes(f) || out2.includes(join39(repoDir, f)))
10630
+ if (out2.includes(f) || out2.includes(join40(repoDir, f)))
10410
10631
  return true;
10411
10632
  const segs = f.split("/");
10412
10633
  if (suffixes(segs, 2).some((x) => x !== f && bounded(out2, x, "(?![\\w-])")))
@@ -10423,7 +10644,7 @@ function loadSignature(out2, roots) {
10423
10644
  let o = out2;
10424
10645
  for (const r of roots.flatMap((x) => {
10425
10646
  try {
10426
- return [x, realpathSync15(x)];
10647
+ return [x, realpathSync16(x)];
10427
10648
  } catch {
10428
10649
  return [x];
10429
10650
  }
@@ -10517,7 +10738,7 @@ async function loadErrorIsHarnessOwned(i) {
10517
10738
  return false;
10518
10739
  if (i.args.some((a) => changed.includes(a.replace(/^\.\//, ""))))
10519
10740
  return false;
10520
- const dir = mkdtempSync5(join39(tmpdir7(), "e10-loadown-"));
10741
+ const dir = mkdtempSync5(join40(tmpdir7(), "e10-loadown-"));
10521
10742
  const git3 = (args) => {
10522
10743
  safeGit(i.repoDir, args, { stdio: "ignore" });
10523
10744
  };
@@ -10549,7 +10770,7 @@ var init_load_owner = __esm(() => {
10549
10770
  });
10550
10771
 
10551
10772
  // ../../loki-ts/src/util/wall_owned.ts
10552
- import { isAbsolute as isAbsolute4, join as join40, relative as relative4 } from "path";
10773
+ import { isAbsolute as isAbsolute4, join as join41, relative as relative4 } from "path";
10553
10774
  function failingLocations(raw2, cwd, repoDir) {
10554
10775
  const out2 = raw2.replace(ANSI2, "").replace(/\r\n?/g, `
10555
10776
  `), files = new Set;
@@ -10581,7 +10802,7 @@ function failingLocations(raw2, cwd, repoDir) {
10581
10802
  }
10582
10803
  if (errors2 === 0 || located !== errors2)
10583
10804
  return null;
10584
- return [...files].map((f) => relative4(repoDir, isAbsolute4(f) ? f : join40(cwd, f)));
10805
+ return [...files].map((f) => relative4(repoDir, isAbsolute4(f) ? f : join41(cwd, f)));
10585
10806
  }
10586
10807
  function wallOwnedFailure(raw2, cwd, repoDir, wall) {
10587
10808
  const locs = failingLocations(raw2, cwd, repoDir);
@@ -10610,11 +10831,11 @@ var init_state = __esm(() => {
10610
10831
  });
10611
10832
 
10612
10833
  // ../../loki-ts/src/runner/repo_profile.ts
10613
- import { existsSync as existsSync21, readFileSync as readFileSync28, mkdirSync as mkdirSync12, readdirSync as readdirSync11, renameSync as renameSync6, rmSync as rmSync10, writeFileSync as writeFileSync15 } from "fs";
10614
- import { basename as basename15, join as join41, resolve as resolve11, isAbsolute as isAbsolute5 } from "path";
10834
+ import { existsSync as existsSync22, readFileSync as readFileSync29, mkdirSync as mkdirSync13, readdirSync as readdirSync11, renameSync as renameSync6, rmSync as rmSync10, writeFileSync as writeFileSync16 } from "fs";
10835
+ import { basename as basename15, join as join42, resolve as resolve11, isAbsolute as isAbsolute5 } from "path";
10615
10836
  function loadProfile(repoDir) {
10616
10837
  try {
10617
- const p = JSON.parse(readFileSync28(commandProfilePath(repoDir), "utf8"));
10838
+ const p = JSON.parse(readFileSync29(commandProfilePath(repoDir), "utf8"));
10618
10839
  return p?.version === 1 && Array.isArray(p.commands) && p.baseline ? p : null;
10619
10840
  } catch {
10620
10841
  return null;
@@ -10637,15 +10858,15 @@ function attribute(repoDir, baseSha, failing) {
10637
10858
  }
10638
10859
  function attributeFullSuite(repoDir, baseSha, full, blocked, changed) {
10639
10860
  const bad = full.filter((c) => c.result === "fail"), none = { preExistingChecks: [], preExistingIds: [], introducedChecks: bad.map((c) => c.name), line: null };
10640
- const norm3 = (s) => s.replace(/\s+--silent\b/, "").trim(), p = loadProfile(repoDir);
10861
+ const norm4 = (s) => s.replace(/\s+--silent\b/, "").trim(), p = loadProfile(repoDir);
10641
10862
  if (!bad.length || !p || p.revision !== baseSha || !p.baseline.attributable || !p.baseline.failing_ids.length)
10642
10863
  return none;
10643
- if (bad.some((c) => norm3(c.cmd) !== norm3(p.baseline.command ?? "")))
10864
+ if (bad.some((c) => norm4(c.cmd) !== norm4(p.baseline.command ?? "")))
10644
10865
  return { ...none, line: `baseline: ran "${p.baseline.command}", not the full suite command, ids not comparable, full suite failures not excused` };
10645
10866
  const block = new Set(blocked);
10646
10867
  return attribute(repoDir, baseSha, bad.map((c) => ({ name: c.name, ...c.ids ? { ids: c.ids } : {}, eligible: (c.ids ?? []).every((i) => !block.has(i) && !changed.some((f) => i.includes(f))) })));
10647
10868
  }
10648
- var commandProfilePath = (repoDir) => join41(repoDir, ".loki", "state", "repo-profile.json");
10869
+ var commandProfilePath = (repoDir) => join42(repoDir, ".loki", "state", "repo-profile.json");
10649
10870
  var init_repo_profile = __esm(() => {
10650
10871
  init_failures();
10651
10872
  init_check_result();
@@ -10655,23 +10876,23 @@ var init_repo_profile = __esm(() => {
10655
10876
  });
10656
10877
 
10657
10878
  // ../../loki-ts/src/project_graph.ts
10658
- import { createHash as createHash9 } from "crypto";
10879
+ import { createHash as createHash10 } from "crypto";
10659
10880
  import {
10660
- existsSync as existsSync22,
10881
+ existsSync as existsSync23,
10661
10882
  readdirSync as readdirSync12,
10662
- readFileSync as readFileSync29,
10663
- statSync as statSync15,
10664
- writeFileSync as writeFileSync16,
10665
- mkdirSync as mkdirSync13
10883
+ readFileSync as readFileSync30,
10884
+ statSync as statSync16,
10885
+ writeFileSync as writeFileSync17,
10886
+ mkdirSync as mkdirSync14
10666
10887
  } from "fs";
10667
- import { basename as basename16, dirname as dirname12, join as join42, resolve as resolve12 } from "path";
10888
+ import { basename as basename16, dirname as dirname13, join as join43, resolve as resolve12 } from "path";
10668
10889
  function readManifest(path) {
10669
- if (!existsSync22(path)) {
10890
+ if (!existsSync23(path)) {
10670
10891
  return null;
10671
10892
  }
10672
10893
  let raw2;
10673
10894
  try {
10674
- raw2 = readFileSync29(path, "utf8");
10895
+ raw2 = readFileSync30(path, "utf8");
10675
10896
  } catch {
10676
10897
  return null;
10677
10898
  }
@@ -10723,12 +10944,12 @@ function resolveMembers(rootDir, declared, selfBasename) {
10723
10944
  for (const child of children) {
10724
10945
  if (!matchPattern(name, child))
10725
10946
  continue;
10726
- const full = join42(rootDir, child);
10947
+ const full = join43(rootDir, child);
10727
10948
  if (seen.has(full))
10728
10949
  continue;
10729
10950
  let st;
10730
10951
  try {
10731
- st = statSync15(full);
10952
+ st = statSync16(full);
10732
10953
  } catch {
10733
10954
  continue;
10734
10955
  }
@@ -10740,18 +10961,18 @@ function resolveMembers(rootDir, declared, selfBasename) {
10740
10961
  }
10741
10962
  if (declared.length === 0) {
10742
10963
  for (const child of children) {
10743
- const full = join42(rootDir, child);
10964
+ const full = join43(rootDir, child);
10744
10965
  if (seen.has(full))
10745
10966
  continue;
10746
10967
  let st;
10747
10968
  try {
10748
- st = statSync15(full);
10969
+ st = statSync16(full);
10749
10970
  } catch {
10750
10971
  continue;
10751
10972
  }
10752
10973
  if (!st.isDirectory())
10753
10974
  continue;
10754
- if (!existsSync22(join42(full, ".loki", "app.json")))
10975
+ if (!existsSync23(join43(full, ".loki", "app.json")))
10755
10976
  continue;
10756
10977
  seen.add(full);
10757
10978
  out2.push(full);
@@ -10764,34 +10985,34 @@ function cacheKey(manifestPaths) {
10764
10985
  for (const p of manifestPaths.sort()) {
10765
10986
  let mtime = 0;
10766
10987
  try {
10767
- mtime = statSync15(p).mtimeMs;
10988
+ mtime = statSync16(p).mtimeMs;
10768
10989
  } catch {}
10769
10990
  parts.push(`${p}:${mtime}`);
10770
10991
  }
10771
- return createHash9("sha256").update(parts.join("|")).digest("hex");
10992
+ return createHash10("sha256").update(parts.join("|")).digest("hex");
10772
10993
  }
10773
10994
  function writeCache(targetDir, key, result) {
10774
10995
  try {
10775
- const cacheDir = join42(targetDir, ".loki", "state");
10776
- mkdirSync13(cacheDir, { recursive: true });
10777
- const cachePath = join42(cacheDir, "project-graph.json");
10778
- writeFileSync16(cachePath, JSON.stringify({ key, result }, null, 2), "utf8");
10996
+ const cacheDir = join43(targetDir, ".loki", "state");
10997
+ mkdirSync14(cacheDir, { recursive: true });
10998
+ const cachePath = join43(cacheDir, "project-graph.json");
10999
+ writeFileSync17(cachePath, JSON.stringify({ key, result }, null, 2), "utf8");
10779
11000
  } catch {}
10780
11001
  }
10781
11002
  function discoverProjectGraph(targetDir) {
10782
11003
  const absTarget = resolve12(targetDir);
10783
- const targetManifest = join42(absTarget, ".loki", "app.json");
10784
- const parentDir = dirname12(absTarget);
10785
- const parentManifest = join42(parentDir, ".loki", "app.json");
11004
+ const targetManifest = join43(absTarget, ".loki", "app.json");
11005
+ const parentDir = dirname13(absTarget);
11006
+ const parentManifest = join43(parentDir, ".loki", "app.json");
10786
11007
  const examined = [];
10787
11008
  let chosenManifest = null;
10788
11009
  let chosenRoot = "";
10789
- if (existsSync22(parentManifest) && parentDir !== absTarget) {
11010
+ if (existsSync23(parentManifest) && parentDir !== absTarget) {
10790
11011
  examined.push(parentManifest);
10791
11012
  chosenManifest = readManifest(parentManifest);
10792
11013
  chosenRoot = parentDir;
10793
11014
  }
10794
- if (!chosenManifest && existsSync22(targetManifest)) {
11015
+ if (!chosenManifest && existsSync23(targetManifest)) {
10795
11016
  examined.push(targetManifest);
10796
11017
  chosenManifest = readManifest(targetManifest);
10797
11018
  chosenRoot = absTarget;
@@ -10802,8 +11023,8 @@ function discoverProjectGraph(targetDir) {
10802
11023
  const members = resolveMembers(chosenRoot, declared, basename16(absTarget));
10803
11024
  const filtered = [];
10804
11025
  for (const m of members) {
10805
- const sibManifestPath = join42(m, ".loki", "app.json");
10806
- if (existsSync22(sibManifestPath)) {
11026
+ const sibManifestPath = join43(m, ".loki", "app.json");
11027
+ if (existsSync23(sibManifestPath)) {
10807
11028
  examined.push(sibManifestPath);
10808
11029
  const sibManifest = readManifest(sibManifestPath);
10809
11030
  if (sibManifest && sibManifest.app_id !== chosenManifest.app_id) {
@@ -10826,77 +11047,6 @@ var init_project_graph = __esm(() => {
10826
11047
  APP_ID_REGEX = /^[a-z0-9-]{3,40}$/;
10827
11048
  });
10828
11049
 
10829
- // ../../loki-ts/src/util/shell.ts
10830
- async function readStreamCapped(stream, maxBytes = MAX_STDOUT_BYTES) {
10831
- const reader = stream.getReader();
10832
- const decoder = new TextDecoder;
10833
- let out2 = "";
10834
- let total = 0;
10835
- try {
10836
- while (total < maxBytes) {
10837
- const { done, value } = await reader.read();
10838
- if (done)
10839
- break;
10840
- if (!value)
10841
- continue;
10842
- total += value.byteLength;
10843
- if (total > maxBytes) {
10844
- const keep = value.byteLength - (total - maxBytes);
10845
- out2 += decoder.decode(value.subarray(0, keep), { stream: true });
10846
- break;
10847
- }
10848
- out2 += decoder.decode(value, { stream: true });
10849
- }
10850
- out2 += decoder.decode();
10851
- } finally {
10852
- try {
10853
- await reader.cancel();
10854
- } catch {}
10855
- reader.releaseLock();
10856
- }
10857
- return out2;
10858
- }
10859
- async function run(argv, opts = {}) {
10860
- const proc = Bun.spawn({
10861
- cmd: [...argv],
10862
- stdout: "pipe",
10863
- stderr: "pipe",
10864
- env: opts.env ? { ...process.env, ...opts.env } : process.env,
10865
- cwd: opts.cwd
10866
- });
10867
- let timer;
10868
- let killTimer;
10869
- if (opts.timeoutMs && opts.timeoutMs > 0) {
10870
- timer = setTimeout(() => {
10871
- try {
10872
- proc.kill("SIGTERM");
10873
- } catch {}
10874
- killTimer = setTimeout(() => {
10875
- try {
10876
- proc.kill("SIGKILL");
10877
- } catch {}
10878
- }, 2000);
10879
- }, opts.timeoutMs);
10880
- }
10881
- try {
10882
- const [stdout, stderr, exitCode] = await Promise.all([
10883
- readStreamCapped(proc.stdout),
10884
- new Response(proc.stderr).text(),
10885
- proc.exited
10886
- ]);
10887
- return { stdout, stderr, exitCode };
10888
- } finally {
10889
- if (timer)
10890
- clearTimeout(timer);
10891
- if (killTimer)
10892
- clearTimeout(killTimer);
10893
- }
10894
- }
10895
- var MAX_STDOUT_BYTES;
10896
- var init_shell = __esm(() => {
10897
- MAX_STDOUT_BYTES = 16 * 1024 * 1024;
10898
- });
10899
-
10900
11050
  // ../../loki-ts/src/e10ext/budget_cap.ts
10901
11051
  var DEFAULT_MAX_COST_USD;
10902
11052
  var init_budget_cap = __esm(() => {
@@ -10908,71 +11058,6 @@ var init_budget_cap = __esm(() => {
10908
11058
  // ../../loki-ts/src/runner/retry_class.ts
10909
11059
  var init_retry_class = () => {};
10910
11060
 
10911
- // ../../loki-ts/src/features/speed/unit_mode.ts
10912
- import { closeSync as closeSync7, constants as constants2, fstatSync as fstatSync5, lstatSync as lstatSync8, openSync as openSync7, readSync as readSync6, realpathSync as realpathSync16 } from "fs";
10913
- function readBounded(path) {
10914
- const l = lstatSync8(path);
10915
- if (l.isSymbolicLink() || !l.isFile() || l.size > MAX_SPEC_BYTES)
10916
- return null;
10917
- const fd = openSync7(path, constants2.O_RDONLY | constants2.O_NOFOLLOW | constants2.O_NONBLOCK);
10918
- try {
10919
- const st = fstatSync5(fd);
10920
- if (!st.isFile() || st.size > MAX_SPEC_BYTES)
10921
- return null;
10922
- const buf = Buffer.alloc(st.size + 1);
10923
- let n2 = 0;
10924
- for (;; ) {
10925
- const r = readSync6(fd, buf, n2, buf.length - n2, null);
10926
- if (r <= 0)
10927
- break;
10928
- n2 += r;
10929
- if (n2 >= buf.length)
10930
- return null;
10931
- }
10932
- return buf.subarray(0, n2).toString("utf8");
10933
- } finally {
10934
- closeSync7(fd);
10935
- }
10936
- }
10937
- function parseText(txt) {
10938
- try {
10939
- const j = JSON.parse(txt), ws = strs(j.writeSet), pack = strs(j.pack), tb = j.tokenBudget;
10940
- if (typeof j.id !== "string" || !j.id || !ws || ws.length === 0 || !pack || typeof tb !== "number" || !Number.isFinite(tb) || !(tb > 0) || tb > MAX_TOKEN_BUDGET)
10941
- return null;
10942
- return { id: j.id, writeSet: ws, pack, tokenBudget: tb };
10943
- } catch {
10944
- return null;
10945
- }
10946
- }
10947
- function parseSpec(path) {
10948
- try {
10949
- const txt = readBounded(path);
10950
- return txt === null ? null : parseText(txt);
10951
- } catch {
10952
- return null;
10953
- }
10954
- }
10955
- function unitSpec(env = process.env) {
10956
- const path = env["LOKI_UNIT_SPEC"], frozen = env["LOKI_UNIT_SPEC_FROZEN"];
10957
- if (env["LOKI_SPEED"] === "0" || !path)
10958
- return null;
10959
- if (frozen !== undefined)
10960
- return frozen.length <= MAX_SPEC_BYTES ? parseText(frozen) : null;
10961
- if (!memo.has(path))
10962
- memo.set(path, parseSpec(path));
10963
- return memo.get(path) ?? null;
10964
- }
10965
- function unitFlags(staged, env = process.env, pre = {}) {
10966
- const s = unitSpec(env);
10967
- return s ? staged.filter(({ f }) => !inWriteSet(s, f) && !Object.hasOwn(pre, f)).map(({ f }) => f) : null;
10968
- }
10969
- var MAX_SPEC_BYTES, MAX_TOKEN_BUDGET = 1e9, norm3 = (p) => p.replace(/^\.\//, ""), CTRL, strs = (v) => Array.isArray(v) && v.every((x) => typeof x === "string" && !CTRL.test(x)) ? v.map(norm3) : null, memo, inWriteSet = (spec, f) => spec.writeSet.some((w) => w.endsWith("/") ? f.startsWith(w) : f === w);
10970
- var init_unit_mode = __esm(() => {
10971
- MAX_SPEC_BYTES = 1024 * 1024;
10972
- CTRL = /[\x00-\x1f\x7f]/;
10973
- memo = new Map;
10974
- });
10975
-
10976
11061
  // ../../loki-ts/src/e10ext/repomemory.ts
10977
11062
  var init_repomemory = __esm(() => {
10978
11063
  init_cache();
@@ -11011,14 +11096,15 @@ var init_steer = __esm(() => {
11011
11096
  });
11012
11097
 
11013
11098
  // ../../loki-ts/src/util/pr_lessons.ts
11014
- import { existsSync as existsSync23, mkdirSync as mkdirSync14, readFileSync as readFileSync30 } from "fs";
11015
- import { dirname as dirname13, join as join43 } from "path";
11099
+ import { existsSync as existsSync24, mkdirSync as mkdirSync15, readFileSync as readFileSync31 } from "fs";
11100
+ import { dirname as dirname14, join as join44 } from "path";
11101
+ import { createHash as createHash11 } from "crypto";
11016
11102
  function loadLessons(repoDir) {
11017
11103
  const p = lessonsPath(repoDir);
11018
- if (!existsSync23(p))
11104
+ if (!existsSync24(p))
11019
11105
  return [];
11020
11106
  try {
11021
- const j = JSON.parse(readFileSync30(p, "utf-8"));
11107
+ const j = JSON.parse(readFileSync31(p, "utf-8"));
11022
11108
  return Array.isArray(j.lessons) ? j.lessons : [];
11023
11109
  } catch {
11024
11110
  return [];
@@ -11026,9 +11112,28 @@ function loadLessons(repoDir) {
11026
11112
  }
11027
11113
  function save(repoDir, lessons) {
11028
11114
  const p = lessonsPath(repoDir);
11029
- mkdirSync14(dirname13(p), { recursive: true });
11115
+ mkdirSync15(dirname14(p), { recursive: true });
11030
11116
  atomicWriteJson(p, { version: 2, lessons });
11031
11117
  }
11118
+ function appendReceiptFact(repoDir, f, now2 = () => new Date) {
11119
+ const id = `rcp-${createHash11("sha256").update(f.runId).digest("hex").slice(0, 12)}`;
11120
+ const lessons = loadLessons(repoDir);
11121
+ if (lessons.some((l) => l.id === id))
11122
+ return false;
11123
+ const cited = f.receiptSha256 !== null;
11124
+ const title = f.title.replace(/\s+/g, " ").trim().slice(0, 200);
11125
+ lessons.push({
11126
+ id,
11127
+ kind: "receipt",
11128
+ text: `Run ${f.runId} sealed ${f.verdict}: ${title}`,
11129
+ source: { pr: `run:${f.runId}`, pr_url: "", comment_url: "", kind: "receipt", author: "loki" },
11130
+ learned_at: now2().toISOString(),
11131
+ uses: [],
11132
+ justified_by: { run_id: f.runId, receipt_sha256: f.receiptSha256, verdict: cited ? f.verdict : "unverified" }
11133
+ });
11134
+ save(repoDir, lessons);
11135
+ return true;
11136
+ }
11032
11137
  function recordRunVerdict(repoDir, runId, verdict) {
11033
11138
  const lessons = loadLessons(repoDir);
11034
11139
  let dirty = false;
@@ -11041,9 +11146,10 @@ function recordRunVerdict(repoDir, runId, verdict) {
11041
11146
  if (dirty)
11042
11147
  save(repoDir, lessons);
11043
11148
  }
11044
- var lessonsPath = (repoDir) => join43(repoDir, ".loki", "memory", "semantic", "pr-lessons.json");
11149
+ var lessonsPath = (repoDir) => join44(repoDir, ".loki", "memory", "semantic", "pr-lessons.json"), KINDS;
11045
11150
  var init_pr_lessons = __esm(() => {
11046
11151
  init_atomic();
11152
+ KINDS = new Set(["pr", "receipt", "ticket", "rejected"]);
11047
11153
  });
11048
11154
 
11049
11155
  // ../../loki-ts/src/runner/model_downgrades.ts
@@ -11056,19 +11162,19 @@ var init_model_downgrades = __esm(() => {
11056
11162
  var init_mcp_config = () => {};
11057
11163
 
11058
11164
  // ../../loki-ts/src/providers/claude_flags.ts
11059
- import { existsSync as existsSync24, readFileSync as readFileSync31 } from "fs";
11060
- import { resolve as resolvePath, dirname as dirname14 } from "path";
11165
+ import { existsSync as existsSync25, readFileSync as readFileSync32 } from "fs";
11166
+ import { resolve as resolvePath, dirname as dirname15 } from "path";
11061
11167
  import { fileURLToPath as fileURLToPath3 } from "url";
11062
11168
  function designArchetypesText() {
11063
11169
  try {
11064
- const moduleDir = dirname14(fileURLToPath3(import.meta.url));
11170
+ const moduleDir = dirname15(fileURLToPath3(import.meta.url));
11065
11171
  for (const rel of [
11066
11172
  ["..", "..", "..", "references", "design-archetypes.md"],
11067
11173
  ["..", "..", "references", "design-archetypes.md"]
11068
11174
  ]) {
11069
11175
  const p = resolvePath(moduleDir, ...rel);
11070
- if (existsSync24(p))
11071
- return readFileSync31(p, "utf8");
11176
+ if (existsSync25(p))
11177
+ return readFileSync32(p, "utf8");
11072
11178
  }
11073
11179
  } catch {}
11074
11180
  return "";
@@ -11146,14 +11252,14 @@ var init_providers = __esm(() => {
11146
11252
  });
11147
11253
 
11148
11254
  // ../../loki-ts/src/runner/router/route_record.ts
11149
- import { existsSync as existsSync25, lstatSync as lstatSync9, mkdirSync as mkdirSync15, readFileSync as readFileSync32, writeFileSync as writeFileSync17 } from "fs";
11150
- import { join as join44 } from "path";
11255
+ import { existsSync as existsSync26, lstatSync as lstatSync9, mkdirSync as mkdirSync16, readFileSync as readFileSync33, writeFileSync as writeFileSync18 } from "fs";
11256
+ import { join as join45 } from "path";
11151
11257
  function loadRouteRecord(runDir) {
11152
11258
  try {
11153
- const p = join44(runDir, ROUTE_RECORD_FILE);
11154
- if (!existsSync25(p) || !lstatSync9(p).isFile())
11259
+ const p = join45(runDir, ROUTE_RECORD_FILE);
11260
+ if (!existsSync26(p) || !lstatSync9(p).isFile())
11155
11261
  return;
11156
- const v = JSON.parse(readFileSync32(p, "utf8"));
11262
+ const v = JSON.parse(readFileSync33(p, "utf8"));
11157
11263
  return typeof v === "object" && v !== null && !Array.isArray(v) ? v : undefined;
11158
11264
  } catch {
11159
11265
  return;
@@ -11163,7 +11269,7 @@ var ROUTE_RECORD_FILE = "route.json";
11163
11269
  var init_route_record = () => {};
11164
11270
 
11165
11271
  // ../../loki-ts/src/runner/router/history_store.ts
11166
- import { existsSync as existsSync26, mkdirSync as mkdirSync16, readFileSync as readFileSync33, renameSync as renameSync7, rmSync as rmSync11, writeFileSync as writeFileSync18 } from "fs";
11272
+ import { existsSync as existsSync27, mkdirSync as mkdirSync17, readFileSync as readFileSync34, renameSync as renameSync7, rmSync as rmSync11, writeFileSync as writeFileSync19 } from "fs";
11167
11273
  import { resolve as resolve13 } from "path";
11168
11274
  function validRun(v) {
11169
11275
  if (typeof v !== "object" || v === null)
@@ -11176,10 +11282,10 @@ function historyPath(key, cacheRoot) {
11176
11282
  }
11177
11283
  function readRunHistory(key, cacheRoot) {
11178
11284
  const path = historyPath(key, cacheRoot);
11179
- if (!existsSync26(path))
11285
+ if (!existsSync27(path))
11180
11286
  return [];
11181
11287
  try {
11182
- const parsed = JSON.parse(readFileSync33(path, "utf8"));
11288
+ const parsed = JSON.parse(readFileSync34(path, "utf8"));
11183
11289
  const runs2 = parsed?.runs;
11184
11290
  return Array.isArray(runs2) ? runs2.filter(validRun) : [];
11185
11291
  } catch {
@@ -11189,11 +11295,11 @@ function readRunHistory(key, cacheRoot) {
11189
11295
  function appendRunOutcome(key, outcome, cacheRoot) {
11190
11296
  try {
11191
11297
  const runs2 = [...readRunHistory(key, cacheRoot), outcome].slice(-HISTORY_CAP);
11192
- mkdirSync16(repoCacheDir(key, cacheRoot), { recursive: true });
11298
+ mkdirSync17(repoCacheDir(key, cacheRoot), { recursive: true });
11193
11299
  const target = historyPath(key, cacheRoot);
11194
11300
  const tmp = `${target}.${process.pid}.tmp`;
11195
11301
  try {
11196
- writeFileSync18(tmp, JSON.stringify({ runs: runs2 }));
11302
+ writeFileSync19(tmp, JSON.stringify({ runs: runs2 }));
11197
11303
  renameSync7(tmp, target);
11198
11304
  } catch (e) {
11199
11305
  rmSync11(tmp, { force: true, recursive: false });
@@ -11213,7 +11319,7 @@ var init_history_store = __esm(() => {
11213
11319
  });
11214
11320
 
11215
11321
  // ../../loki-ts/src/runner/router/history.ts
11216
- import { dirname as dirname15, join as join45, resolve as resolve14 } from "path";
11322
+ import { dirname as dirname16, join as join46, resolve as resolve14 } from "path";
11217
11323
  import { fileURLToPath as fileURLToPath4 } from "url";
11218
11324
  function shapeKey(model) {
11219
11325
  if (!model || model.status !== "ok")
@@ -11242,7 +11348,7 @@ var init_history = __esm(() => {
11242
11348
  init_discover2();
11243
11349
  init_gather();
11244
11350
  init_history_store();
11245
- SHAPE_DEFAULTS_FILE = resolve14(dirname15(fileURLToPath4(import.meta.url)), "..", "..", "..", "data", "router-shape-defaults.json");
11351
+ SHAPE_DEFAULTS_FILE = resolve14(dirname16(fileURLToPath4(import.meta.url)), "..", "..", "..", "data", "router-shape-defaults.json");
11246
11352
  });
11247
11353
 
11248
11354
  // ../../loki-ts/src/runner/router/plan_route.ts
@@ -11373,7 +11479,7 @@ var init_worker = __esm(() => {
11373
11479
  });
11374
11480
 
11375
11481
  // ../../loki-ts/src/project_model/package_suite.ts
11376
- import { join as join46 } from "path";
11482
+ import { join as join47 } from "path";
11377
11483
  async function runPackageSuites(repoDir, signal, opts, checks, notProven) {
11378
11484
  const api = loadProjectApi(repoDir);
11379
11485
  if (!isMultiRoot(api))
@@ -11384,7 +11490,7 @@ async function runPackageSuites(repoDir, signal, opts, checks, notProven) {
11384
11490
  for (const pkg of withTests) {
11385
11491
  const tc = pkg.commands.test, name = `full suite: ${pkg.name}`, cmd = `(cd ${tc.cwd} && ${tc.cmd})`;
11386
11492
  const started = Date.now();
11387
- const r = await run(["bash", "-c", tc.cmd], { cwd: join46(repoDir, tc.cwd), timeoutMs: opts.timeoutMs, ...opts.path ? { env: { PATH: opts.path } } : {} });
11493
+ const r = await run(["bash", "-c", tc.cmd], { cwd: join47(repoDir, tc.cwd), timeoutMs: opts.timeoutMs, ...opts.path ? { env: { PATH: opts.path } } : {} });
11388
11494
  const duration_s = (Date.now() - started) / 1000;
11389
11495
  if (signal.aborted) {
11390
11496
  checks.push({ name, cmd, result: "not_run", duration_s });
@@ -11393,7 +11499,7 @@ async function runPackageSuites(repoDir, signal, opts, checks, notProven) {
11393
11499
  }
11394
11500
  const out2 = `${r.stdout}
11395
11501
  ${r.stderr}`;
11396
- const lr = r.exitCode === 0 || !opts.baseSha ? undefined : await harnessLoadReason({ repoDir, baseSha: opts.baseSha, out: out2, cmd: "bash", args: ["-c", tc.cmd], signal, cwd: join46(repoDir, tc.cwd), ...opts.path ? { env: { PATH: opts.path } } : {} });
11502
+ const lr = r.exitCode === 0 || !opts.baseSha ? undefined : await harnessLoadReason({ repoDir, baseSha: opts.baseSha, out: out2, cmd: "bash", args: ["-c", tc.cmd], signal, cwd: join47(repoDir, tc.cwd), ...opts.path ? { env: { PATH: opts.path } } : {} });
11397
11503
  if (lr) {
11398
11504
  checks.push({ name, cmd, result: "not_run", duration_s });
11399
11505
  notProven.add(`${lr} (${name}; harness-owned)`);
@@ -11426,8 +11532,8 @@ __export(exports_deep, {
11426
11532
  DEFAULT_SECRET_SCAN_SH: () => DEFAULT_SECRET_SCAN_SH,
11427
11533
  DEFAULT_PUSH_SH: () => DEFAULT_PUSH_SH
11428
11534
  });
11429
- import { existsSync as existsSync27, mkdirSync as mkdirSync17, writeFileSync as writeFileSync19 } from "fs";
11430
- import { join as join47 } from "path";
11535
+ import { existsSync as existsSync28, mkdirSync as mkdirSync18, writeFileSync as writeFileSync20 } from "fs";
11536
+ import { join as join48 } from "path";
11431
11537
  async function runFullSuite(ctx, signal, opts, checks, notProven) {
11432
11538
  if (await runPackageSuites(ctx.repoDir, signal, { path: opts.path, timeoutMs: opts.fullSuiteTimeoutMs ?? FULL_SUITE_TIMEOUT_MS, baseSha: ctx.baseSha }, checks, notProven))
11433
11539
  return;
@@ -11513,7 +11619,7 @@ async function runCouncilCheck(ctx, changed, baseSha, signal, opts, checks, notP
11513
11619
  async function runSecretScan(ctx, changed, opts, checks, notProven) {
11514
11620
  const scriptPath = opts.secretScanShPath ?? DEFAULT_SECRET_SCAN_SH;
11515
11621
  const name = "security scan";
11516
- if (!existsSync27(scriptPath)) {
11622
+ if (!existsSync28(scriptPath)) {
11517
11623
  checks.push({ name, cmd: "secret-scan.sh", result: "not_run", duration_s: 0 });
11518
11624
  notProven.add(`not run: ${name} (secret-scan.sh not found)`);
11519
11625
  return;
@@ -11524,7 +11630,7 @@ async function runSecretScan(ctx, changed, opts, checks, notProven) {
11524
11630
  }
11525
11631
  const inline = 'script="$1"; shift; . "$script" || exit 3; hit=0; for f in "$@"; do if _commit_scan_secret_file "$f" || _commit_path_looks_secret "$f"; then printf "%s\\n" "$f"; hit=1; fi; done; exit "$hit"';
11526
11632
  const started = Date.now();
11527
- const r = await run(["bash", "-c", inline, "_", scriptPath, ...changed.map((f) => join47(ctx.repoDir, f))], { cwd: ctx.repoDir, timeoutMs: 30000 });
11633
+ const r = await run(["bash", "-c", inline, "_", scriptPath, ...changed.map((f) => join48(ctx.repoDir, f))], { cwd: ctx.repoDir, timeoutMs: 30000 });
11528
11634
  const durationS = (Date.now() - started) / 1000;
11529
11635
  if (r.exitCode === 3) {
11530
11636
  checks.push({ name, cmd: "secret-scan.sh", result: "not_run", duration_s: durationS });
@@ -11564,9 +11670,9 @@ async function postResults(ctx, opts, checks, notProven, statusState) {
11564
11670
  const prUrl = ctx.outputs().pr?.pr_url;
11565
11671
  const prNumber = prUrl ? PR_NUMBER_RE.exec(prUrl)?.[1] : undefined;
11566
11672
  if (prNumber) {
11567
- mkdirSync17(ctx.runDir, { recursive: true });
11568
- const bodyFile = join47(ctx.runDir, "deep-comment.md");
11569
- writeFileSync19(bodyFile, buildCommentBody(checks, notProven, statusState), "utf8");
11673
+ mkdirSync18(ctx.runDir, { recursive: true });
11674
+ const bodyFile = join48(ctx.runDir, "deep-comment.md");
11675
+ writeFileSync20(bodyFile, buildCommentBody(checks, notProven, statusState), "utf8");
11570
11676
  const commentResult = await run(["bash", scriptPath, ...commentArgv(prNumber, bodyFile)], { cwd: ctx.repoDir, env, timeoutMs: 30000 });
11571
11677
  if (commentResult.exitCode !== 0)
11572
11678
  out2.push(`deep comment not posted (engine10-push.sh exit ${commentResult.exitCode})`);
@@ -11618,8 +11724,8 @@ async function runDeep(ctx, signal, opts = {}) {
11618
11724
  const addendumSha256 = sha2562(canonicalJson(addendumBody));
11619
11725
  const sig = signReceipt(`${ctx.runId}-deep`, addendumSha256);
11620
11726
  const addendum = { ...addendumBody, addendum_sha256: addendumSha256, verification: { jwt: sig.jwt, kid: sig.kid } };
11621
- mkdirSync17(ctx.runDir, { recursive: true });
11622
- writeFileSync19(join47(ctx.runDir, "receipt-addendum-1.json"), JSON.stringify(addendum, null, 2) + `
11727
+ mkdirSync18(ctx.runDir, { recursive: true });
11728
+ writeFileSync20(join48(ctx.runDir, "receipt-addendum-1.json"), JSON.stringify(addendum, null, 2) + `
11623
11729
  `);
11624
11730
  const pushNotProven = await postResults(ctx, opts, checks, notProvenList, statusState);
11625
11731
  const allNotProven = [...notProvenList, ...pushNotProven];
@@ -11662,9 +11768,9 @@ var init_deep = __esm(() => {
11662
11768
  });
11663
11769
 
11664
11770
  // ../../loki-ts/src/engine10/stages/verify.ts
11665
- import { existsSync as existsSync28, mkdtempSync as mkdtempSync6, realpathSync as realpathSync17, rmSync as rmSync12 } from "fs";
11771
+ import { existsSync as existsSync29, mkdtempSync as mkdtempSync6, realpathSync as realpathSync17, rmSync as rmSync12 } from "fs";
11666
11772
  import { tmpdir as tmpdir8 } from "os";
11667
- import { dirname as dirname16, isAbsolute as isAbsolute6, join as join48, relative as relative5 } from "path";
11773
+ import { dirname as dirname17, isAbsolute as isAbsolute6, join as join49, relative as relative5 } from "path";
11668
11774
  function resolveTool(repoDir, bin, fallback, lastResort = fallback) {
11669
11775
  const inRepo = (p) => {
11670
11776
  try {
@@ -11675,7 +11781,7 @@ function resolveTool(repoDir, bin, fallback, lastResort = fallback) {
11675
11781
  }
11676
11782
  };
11677
11783
  const venvEnv = process.env["VIRTUAL_ENV"];
11678
- const project = [join48(repoDir, ".venv", "bin", bin), join48(repoDir, "venv", "bin", bin), ...venvEnv && inRepo(venvEnv) ? [join48(venvEnv, "bin", bin)] : []].find(existsSync28);
11784
+ const project = [join49(repoDir, ".venv", "bin", bin), join49(repoDir, "venv", "bin", bin), ...venvEnv && inRepo(venvEnv) ? [join49(venvEnv, "bin", bin)] : []].find(existsSync29);
11679
11785
  return project ? [project, "project"] : [Bun.which(fallback) ? fallback : lastResort, "system"];
11680
11786
  }
11681
11787
  function runnerCmd(t, repoDir) {
@@ -11695,7 +11801,7 @@ function runnerCmd(t, repoDir) {
11695
11801
  case "npm":
11696
11802
  return ["npm", ["test", "--silent"]];
11697
11803
  case "go":
11698
- return ["go", ["test", "-json", `./${dirname16(t.path)}`]];
11804
+ return ["go", ["test", "-json", `./${dirname17(t.path)}`]];
11699
11805
  case "cargo":
11700
11806
  return ["cargo", ["test"]];
11701
11807
  }
@@ -11793,7 +11899,7 @@ async function subtractBase(ctx, api, checks, tests, changed, wall, rel, signal)
11793
11899
  const sus = pairs.filter(({ c }) => rel.has(c.name) && c.result === "pass");
11794
11900
  if (!(un.some(({ c }) => c.result === "fail" && c.ids?.length) || sus.length) || signal.aborted)
11795
11901
  return out2;
11796
- const dir = mkdtempSync6(join48(tmpdir8(), "e10-base-"));
11902
+ const dir = mkdtempSync6(join49(tmpdir8(), "e10-base-"));
11797
11903
  const git3 = (args) => {
11798
11904
  safeGit(ctx.repoDir, args, { stdio: "ignore", repoDrivers: true });
11799
11905
  };
@@ -11828,7 +11934,7 @@ async function subtractBase(ctx, api, checks, tests, changed, wall, rel, signal)
11828
11934
  return out2;
11829
11935
  }
11830
11936
  function isLokiModeRepo(repoDir) {
11831
- return existsSync28(join48(repoDir, "scripts", "select-tests.sh"));
11937
+ return existsSync29(join49(repoDir, "scripts", "select-tests.sh"));
11832
11938
  }
11833
11939
  async function runLintChecks(ctx, changed, signal, checks, opts = {}) {
11834
11940
  const { api, ...runOpts } = opts;
@@ -11845,9 +11951,9 @@ async function runLintChecks(ctx, changed, signal, checks, opts = {}) {
11845
11951
  }
11846
11952
  for (const g of groupByPackage(api, ctx.repoDir, changed.filter((f) => /\.(ts|tsx|js|jsx)$/.test(f)))) {
11847
11953
  const sfx = g.root === "." ? "" : `:${g.root}`, co = g.root === "." ? opts : { ...opts, cwd: g.cwd };
11848
- if (existsSync28(join48(g.cwd, "tsconfig.json")))
11954
+ if (existsSync29(join49(g.cwd, "tsconfig.json")))
11849
11955
  await runCheck(ctx, `lint:tsc${sfx}`, "npx", ["tsc", "--noEmit", "-p", "."], signal, checks, { ...co, kind: "static" });
11850
- if (ESLINT_CONFIGS.some((f) => existsSync28(join48(g.cwd, f))))
11956
+ if (ESLINT_CONFIGS.some((f) => existsSync29(join49(g.cwd, f))))
11851
11957
  await runCheck(ctx, `lint:eslint${sfx}`, "npx", ["eslint", ...g.rel], signal, checks, { ...co, kind: "static" });
11852
11958
  }
11853
11959
  }
@@ -11979,8 +12085,8 @@ var init_verify = __esm(() => {
11979
12085
  });
11980
12086
 
11981
12087
  // ../../loki-ts/src/project_model/conventions.ts
11982
- import { existsSync as existsSync29, readdirSync as readdirSync13, readFileSync as readFileSync34 } from "fs";
11983
- import { dirname as dirname17, join as join49 } from "path";
12088
+ import { existsSync as existsSync30, readdirSync as readdirSync13, readFileSync as readFileSync35 } from "fs";
12089
+ import { dirname as dirname18, join as join50 } from "path";
11984
12090
  function parseJsonc(text2) {
11985
12091
  let out2 = "", i = 0;
11986
12092
  while (i < text2.length) {
@@ -12015,8 +12121,8 @@ function moduleOf(file, depth = 0) {
12015
12121
  const ext = typeof j.extends === "string" ? j.extends : null;
12016
12122
  if (!ext || !ext.startsWith("."))
12017
12123
  return null;
12018
- const next = join49(dirname17(file), ext.endsWith(".json") ? ext : `${ext}.json`);
12019
- return existsSync29(next) ? moduleOf(next, depth + 1) : null;
12124
+ const next = join50(dirname18(file), ext.endsWith(".json") ? ext : `${ext}.json`);
12125
+ return existsSync30(next) ? moduleOf(next, depth + 1) : null;
12020
12126
  }
12021
12127
  function readPackageConventions(pkgDir, runner = null) {
12022
12128
  let names = [];
@@ -12027,13 +12133,13 @@ function readPackageConventions(pkgDir, runner = null) {
12027
12133
  }
12028
12134
  let packageType = null, tsModule = null;
12029
12135
  for (const n2 of names) {
12030
- const j = readJson4(join49(pkgDir, n2));
12136
+ const j = readJson4(join50(pkgDir, n2));
12031
12137
  if (!j)
12032
12138
  continue;
12033
12139
  if (packageType === null && typeof j.type === "string" && typeof j.name === "string")
12034
12140
  packageType = j.type;
12035
12141
  if (tsModule === null && j.compilerOptions !== undefined)
12036
- tsModule = moduleOf(join49(pkgDir, n2));
12142
+ tsModule = moduleOf(join50(pkgDir, n2));
12037
12143
  }
12038
12144
  if (packageType === null && tsModule === null)
12039
12145
  return null;
@@ -12055,7 +12161,7 @@ function conventionViolation(c, fileName, content) {
12055
12161
  }
12056
12162
  var readJson4 = (p) => {
12057
12163
  try {
12058
- const v = parseJsonc(readFileSync34(p, "utf8"));
12164
+ const v = parseJsonc(readFileSync35(p, "utf8"));
12059
12165
  return v && typeof v === "object" && !Array.isArray(v) ? v : null;
12060
12166
  } catch {
12061
12167
  return null;
@@ -12065,15 +12171,15 @@ var init_conventions = () => {};
12065
12171
 
12066
12172
  // ../../loki-ts/src/engine10/stages/wall.ts
12067
12173
  import { spawnSync as spawnSync4 } from "child_process";
12068
- import { existsSync as existsSync30, mkdirSync as mkdirSync18, mkdtempSync as mkdtempSync7, readFileSync as readFileSync35, readdirSync as readdirSync14, realpathSync as realpathSync18, rmSync as rmSync13, writeFileSync as writeFileSync20 } from "fs";
12174
+ import { existsSync as existsSync31, mkdirSync as mkdirSync19, mkdtempSync as mkdtempSync7, readFileSync as readFileSync36, readdirSync as readdirSync14, realpathSync as realpathSync18, rmSync as rmSync13, writeFileSync as writeFileSync21 } from "fs";
12069
12175
  import { tmpdir as tmpdir9 } from "os";
12070
- import { dirname as dirname18, isAbsolute as isAbsolute7, join as join50, relative as relative6 } from "path";
12176
+ import { dirname as dirname19, isAbsolute as isAbsolute7, join as join51, relative as relative6 } from "path";
12071
12177
  function pytestCollectionIsRed(output, repoDir) {
12072
12178
  if (/ModuleNotFoundError/.test(output))
12073
12179
  return false;
12074
12180
  const real2 = (p) => {
12075
12181
  try {
12076
- return realpathSync18(isAbsolute7(p) ? p : join50(repoDir, p));
12182
+ return realpathSync18(isAbsolute7(p) ? p : join51(repoDir, p));
12077
12183
  } catch {
12078
12184
  return null;
12079
12185
  }
@@ -12110,7 +12216,7 @@ function pytestCollectionIsRed(output, repoDir) {
12110
12216
  }
12111
12217
  function moduleUnderRepo(repoDir, name) {
12112
12218
  const rel = name.replace(/\./g, "/");
12113
- return existsSync30(join50(repoDir, `${rel}.py`)) || existsSync30(join50(repoDir, rel, "__init__.py"));
12219
+ return existsSync31(join51(repoDir, `${rel}.py`)) || existsSync31(join51(repoDir, rel, "__init__.py"));
12114
12220
  }
12115
12221
  function pytestExit1IsRed(output) {
12116
12222
  if (/^!+ _pytest\.outcomes\.Exit\b/m.test(output))
@@ -12187,13 +12293,16 @@ ${r.stderr ?? ""}`), repoDir, interpreter);
12187
12293
  return { pass, fail, not_run };
12188
12294
  }
12189
12295
  }
12190
- function buildWallBrief(task, repomapText = "", runners = [], hasManifest = false, conventions = "") {
12296
+ function buildWallBrief(task, repomapText = "", runners = [], hasManifest = false, conventions = "", acceptance = []) {
12191
12297
  return withStagePrefix([
12192
12298
  "You are the Loki 10 Wall author.",
12193
12299
  `You cannot see the repository. This directory holds only task.md and repomap.txt${hasManifest ? " and wall_manifest.txt (signatures, runner config, test layout and test-style examples)" : ""}.`,
12194
12300
  ...repomapText ? [`Repository paths (repomap.txt):
12195
12301
  ${repomapText}`] : [],
12196
12302
  ...taskBlock(task),
12303
+ ...acceptance.length ? [`Acceptance criteria frozen for this unit (each must be covered by a test):
12304
+ ${acceptance.map((a) => `Acceptance: ${a}`).join(`
12305
+ `)}`] : [],
12197
12306
  "Write behavioral acceptance tests that prove the task is done. A test that errors on import, uses another framework's globals, or fails for a reason unrelated to the task is discarded.",
12198
12307
  `Test runner: ${runners.map((r) => RUNNER_HINT[r]).find(Boolean) ?? "the framework named in repomap.txt"}`,
12199
12308
  ...conventions ? [conventions] : [],
@@ -12205,10 +12314,10 @@ ${repomapText}`] : [],
12205
12314
  function loadTaskText(ctx, fromPrior) {
12206
12315
  if (fromPrior)
12207
12316
  return fromPrior;
12208
- const issueJsonPath = process.env.LOKI_E10_ISSUE_JSON ?? join50(ctx.runDir, "issue.json");
12209
- if (existsSync30(issueJsonPath)) {
12317
+ const issueJsonPath = process.env.LOKI_E10_ISSUE_JSON ?? join51(ctx.runDir, "issue.json");
12318
+ if (existsSync31(issueJsonPath)) {
12210
12319
  try {
12211
- const issue = JSON.parse(readFileSync35(issueJsonPath, "utf8"));
12320
+ const issue = JSON.parse(readFileSync36(issueJsonPath, "utf8"));
12212
12321
  const text2 = [issue.title, issue.body].filter((s) => typeof s === "string" && s.length > 0).join(`
12213
12322
 
12214
12323
  `);
@@ -12220,7 +12329,7 @@ function loadTaskText(ctx, fromPrior) {
12220
12329
  }
12221
12330
  function wallTargetDir(repoDir, existingTests) {
12222
12331
  const first = existingTests[0];
12223
- return first ? join50(repoDir, dirname18(first.path)) : join50(repoDir, "tests");
12332
+ return first ? join51(repoDir, dirname19(first.path)) : join51(repoDir, "tests");
12224
12333
  }
12225
12334
  function guessRunner(fileName, runners) {
12226
12335
  if (fileName.endsWith(".py"))
@@ -12251,14 +12360,14 @@ async function wallAuthor(ctx, signal) {
12251
12360
  const tree = prior.intake?.tree;
12252
12361
  const repomapText = repoMapText(ctx.repoDir, tree, repomapRef, WALL_MAP_MAX_LINES);
12253
12362
  const targetDir = wallTargetDir(ctx.repoDir, existingTests), pkg = loadProjectApi(ctx.repoDir)?.packageOf(`${relative6(ctx.repoDir, targetDir)}/x`);
12254
- const conv2 = readPackageConventions(join50(ctx.repoDir, pkg?.root ?? "."), pkg?.runner ?? null);
12255
- const cwd = mkdtempSync7(join50(tmpdir9(), "loki-e15-wall-"));
12363
+ const conv2 = readPackageConventions(join51(ctx.repoDir, pkg?.root ?? "."), pkg?.runner ?? null);
12364
+ const cwd = mkdtempSync7(join51(tmpdir9(), "loki-e15-wall-"));
12256
12365
  const wm = wallManifestFor(ctx.repoDir, tree, task);
12257
12366
  for (const [n2, c] of [["task.md", task], ["repomap.txt", repomapText], ...wm ? [["wall_manifest.txt", wm.text]] : []])
12258
- writeFileSync20(join50(cwd, n2), c, "utf8");
12367
+ writeFileSync21(join51(cwd, n2), c, "utf8");
12259
12368
  const session = await ctx.sessions.run({
12260
12369
  stage: "wall",
12261
- brief: buildWallBrief(task, repomapText, runners, !!wm, conventionsBrief(conv2)),
12370
+ brief: buildWallBrief(task, repomapText, runners, !!wm, conventionsBrief(conv2), unitSpec()?.acceptance ?? []),
12262
12371
  tier: "development",
12263
12372
  model: wallModel(),
12264
12373
  iterationId: `${ctx.runId}-wall`,
@@ -12271,14 +12380,14 @@ async function wallAuthor(ctx, signal) {
12271
12380
  return done({ status: "failed", data: {}, reason: "wall session aborted, killed, or timed out", killed: true });
12272
12381
  }
12273
12382
  const generated = readdirSync14(cwd).filter((f) => f.startsWith(WALL_PREFIX));
12274
- const sealedDir = join50(ctx.runDir, "wall");
12383
+ const sealedDir = join51(ctx.runDir, "wall");
12275
12384
  if (generated.length > 0)
12276
- mkdirSync18(sealedDir, { recursive: true });
12385
+ mkdirSync19(sealedDir, { recursive: true });
12277
12386
  const contents = new Map;
12278
12387
  const discarded = [];
12279
12388
  for (const name of generated) {
12280
- const content = readFileSync35(join50(cwd, name), "utf8"), bad = conventionViolation(conv2, name, content);
12281
- writeFileSync20(join50(sealedDir, name), content, "utf8");
12389
+ const content = readFileSync36(join51(cwd, name), "utf8"), bad = conventionViolation(conv2, name, content);
12390
+ writeFileSync21(join51(sealedDir, name), content, "utf8");
12282
12391
  if (bad)
12283
12392
  discarded.push({ file: name, reason: `${bad}; wall test did not compile under the package config` });
12284
12393
  else
@@ -12290,7 +12399,7 @@ async function wallAuthor(ctx, signal) {
12290
12399
  function installWall(ctx, a, baseDir, opts = {}) {
12291
12400
  const { generated, contents, discarded, targetDir, runners } = a;
12292
12401
  if (generated.length > 0)
12293
- mkdirSync18(targetDir, { recursive: true });
12402
+ mkdirSync19(targetDir, { recursive: true });
12294
12403
  const sealedFiles = [];
12295
12404
  const readOnlyFiles = [];
12296
12405
  const wallTests = [];
@@ -12298,8 +12407,8 @@ function installWall(ctx, a, baseDir, opts = {}) {
12298
12407
  const content = contents.get(name);
12299
12408
  if (content === undefined)
12300
12409
  continue;
12301
- const dest = join50(targetDir, name), runner = guessRunner(name, runners);
12302
- writeFileSync20(dest, content, "utf8");
12410
+ const dest = join51(targetDir, name), runner = guessRunner(name, runners);
12411
+ writeFileSync21(dest, content, "utf8");
12303
12412
  sealedFiles.push({ path: dest, sha256: sha2562(content) });
12304
12413
  readOnlyFiles.push({ path: dest, content });
12305
12414
  if (runner)
@@ -12308,7 +12417,7 @@ function installWall(ctx, a, baseDir, opts = {}) {
12308
12417
  ctx.emit("wall.sealed", "wall", { files: sealedFiles, ...a.manifestSha256 ? { manifest_sha256: a.manifestSha256 } : {} });
12309
12418
  const baseRunner = opts.baseRunner ?? new RealBaseTestRunner, baseRun = { pass: 0, fail: 0, not_run: discarded.length }, notRunFiles = [];
12310
12419
  for (const t of wallTests) {
12311
- const r = baseRunner.run(baseDir, [t]), abs = join50(ctx.repoDir, t.path);
12420
+ const r = baseRunner.run(baseDir, [t]), abs = join51(ctx.repoDir, t.path);
12312
12421
  baseRun.pass += r.pass;
12313
12422
  baseRun.fail += r.fail;
12314
12423
  baseRun.not_run += r.not_run ?? 0;
@@ -12341,6 +12450,7 @@ async function runWall(ctx, signal, opts = {}) {
12341
12450
  var WALL_PREFIX = "loki_wall_", BASE_RUN_TIMEOUT_MS = 60000, WALL_MAP_MAX_LINES = 200, wallStage, stage2;
12342
12451
  var init_wall = __esm(() => {
12343
12452
  init_types();
12453
+ init_unit_mode();
12344
12454
  init_lean_prefix();
12345
12455
  init_wall_hints();
12346
12456
  init_sizing();
@@ -12359,20 +12469,20 @@ var init_wall = __esm(() => {
12359
12469
  });
12360
12470
 
12361
12471
  // ../../loki-ts/src/e10ext/discard.ts
12362
- import { chmodSync as chmodSync4, closeSync as closeSync8, lstatSync as lstatSync10, mkdirSync as mkdirSync19, openSync as openSync8, readFileSync as readFileSync36, realpathSync as realpathSync19, rmdirSync, unlinkSync as unlinkSync4, writeFileSync as writeFileSync21 } from "fs";
12363
- import { dirname as dirname19, join as join51, relative as relative7, sep as sep11 } from "path";
12472
+ import { chmodSync as chmodSync5, closeSync as closeSync8, lstatSync as lstatSync10, mkdirSync as mkdirSync20, openSync as openSync8, readFileSync as readFileSync37, realpathSync as realpathSync19, rmdirSync, unlinkSync as unlinkSync5, writeFileSync as writeFileSync22 } from "fs";
12473
+ import { dirname as dirname20, join as join52, relative as relative7, sep as sep11 } from "path";
12364
12474
  function safeRestore(repoDir, f, want, mode) {
12365
- const root = realpathSync19(repoDir), fp = join51(root, f), rel = relative7(root, dirname19(fp));
12475
+ const root = realpathSync19(repoDir), fp = join52(root, f), rel = relative7(root, dirname20(fp));
12366
12476
  if (rel.startsWith(".."))
12367
12477
  throw new Error("outside repo");
12368
12478
  let cur = root;
12369
12479
  for (const part of rel === "" ? [] : rel.split(sep11)) {
12370
- cur = join51(cur, part);
12480
+ cur = join52(cur, part);
12371
12481
  let st2;
12372
12482
  try {
12373
12483
  st2 = lstatSync10(cur);
12374
12484
  } catch {
12375
- mkdirSync19(cur);
12485
+ mkdirSync20(cur);
12376
12486
  continue;
12377
12487
  }
12378
12488
  if (!st2.isDirectory())
@@ -12384,23 +12494,23 @@ function safeRestore(repoDir, f, want, mode) {
12384
12494
  } catch {
12385
12495
  st = null;
12386
12496
  }
12387
- if (st?.isFile() && readFileSync36(fp).equals(want)) {
12388
- chmodSync4(fp, mode);
12497
+ if (st?.isFile() && readFileSync37(fp).equals(want)) {
12498
+ chmodSync5(fp, mode);
12389
12499
  return;
12390
12500
  }
12391
12501
  if (st) {
12392
12502
  if (st.isDirectory())
12393
12503
  rmdirSync(fp);
12394
12504
  else
12395
- unlinkSync4(fp);
12505
+ unlinkSync5(fp);
12396
12506
  }
12397
12507
  const fd = openSync8(fp, "wx", mode);
12398
12508
  try {
12399
- writeFileSync21(fd, want);
12509
+ writeFileSync22(fd, want);
12400
12510
  } finally {
12401
12511
  closeSync8(fd);
12402
12512
  }
12403
- chmodSync4(fp, mode);
12513
+ chmodSync5(fp, mode);
12404
12514
  }
12405
12515
  function alreadySatisfied(o) {
12406
12516
  const b = o.wall?.base_run ?? {};
@@ -12422,7 +12532,7 @@ async function discardIfSatisfied(git3, base, o, staged, keep, repoDir) {
12422
12532
  return { status: "failed", data: {}, reason: "already-satisfied discard failed" };
12423
12533
  const notProven = [];
12424
12534
  for (const [f, v] of Object.entries(o.intake?.preexisting_untracked_blobs ?? {})) {
12425
- const fp = join51(repoDir, f);
12535
+ const fp = join52(repoDir, f);
12426
12536
  try {
12427
12537
  if (v.startsWith("!")) {
12428
12538
  const st = lstatSync10(fp);
@@ -12441,7 +12551,7 @@ async function discardIfSatisfied(git3, base, o, staged, keep, repoDir) {
12441
12551
  try {
12442
12552
  let m = 420;
12443
12553
  try {
12444
- const l = lstatSync10(join51(repoDir, f));
12554
+ const l = lstatSync10(join52(repoDir, f));
12445
12555
  if (l.isFile())
12446
12556
  m = l.mode & 511;
12447
12557
  } catch {}
@@ -12474,9 +12584,9 @@ var init_preexisting_dirty = __esm(() => {
12474
12584
  });
12475
12585
 
12476
12586
  // ../../loki-ts/src/e10ext/commit_filter.ts
12477
- import { dirname as dirname20 } from "path";
12587
+ import { dirname as dirname21 } from "path";
12478
12588
  function dropSet(repoDir, staged, preexistingDirty, env = process.env) {
12479
- return staged.concat(untouchedSinceIntake(repoDir, preexistingDirty, env).map((f) => ({ st: "L", f }))).filter(({ st, f }) => f.startsWith(".loki/") || st === "L" || /(^|\/)loki_wall_[^/]*$/.test(f) || st === "A" && !staged.some(({ f: m }) => /(^|\/)(package\.json|pyproject\.toml|requirements\.txt|Cargo\.toml|go\.mod)$/.test(m) && dirname20(m) === dirname20(f)) && /(^|\/)(package-lock\.json|yarn\.lock|pnpm-lock\.yaml|bun\.lockb?|poetry\.lock|Cargo\.lock|go\.sum)$/.test(f));
12589
+ return staged.concat(untouchedSinceIntake(repoDir, preexistingDirty, env).map((f) => ({ st: "L", f }))).filter(({ st, f }) => f.startsWith(".loki/") || st === "L" || /(^|\/)loki_wall_[^/]*$/.test(f) || st === "A" && !staged.some(({ f: m }) => /(^|\/)(package\.json|pyproject\.toml|requirements\.txt|Cargo\.toml|go\.mod)$/.test(m) && dirname21(m) === dirname21(f)) && /(^|\/)(package-lock\.json|yarn\.lock|pnpm-lock\.yaml|bun\.lockb?|poetry\.lock|Cargo\.lock|go\.sum)$/.test(f));
12480
12590
  }
12481
12591
  var parseStaged = (out2) => out2.split("\x00").reduce((a, t, i, all) => i % 2 === 0 && t ? [...a, { st: t, f: all[i + 1] }] : a, []);
12482
12592
  var init_commit_filter = __esm(() => {
@@ -12485,7 +12595,7 @@ var init_commit_filter = __esm(() => {
12485
12595
  });
12486
12596
 
12487
12597
  // ../../loki-ts/src/e10ext/scope.ts
12488
- import { basename as basename18, dirname as dirname21 } from "path";
12598
+ import { basename as basename18, dirname as dirname22 } from "path";
12489
12599
  function contractText(o) {
12490
12600
  const c = o.intake?.contract_snapshot?.contract;
12491
12601
  return Array.isArray(c?.criteria) ? c.criteria.map((x) => typeof x?.text === "string" ? x.text : "").join(`
@@ -12499,8 +12609,8 @@ function outsideScope(o, staged) {
12499
12609
  const text2 = `${plan}
12500
12610
  ${typeof o.intake?.task === "string" ? o.intake.task : ""}
12501
12611
  ${contractText(o)}`, pre = o.intake?.preexisting_dirty ?? {};
12502
- const toks = surfaceTokens(text2), plannedDirs = new Set(rel.map((r) => dirname21(r)).filter((d) => d !== "."));
12503
- return staged.filter(({ st, f }) => st !== "A" && !isTestFile(f) && !Object.hasOwn(pre, f) && !rel.includes(f) && !mentions(text2, f) && !mentions(text2, basename18(f)) && !plannedDirs.has(dirname21(f)) && !dirSegs(f).some((d) => toks.has(d))).map(({ f }) => f);
12612
+ const toks = surfaceTokens(text2), plannedDirs = new Set(rel.map((r) => dirname22(r)).filter((d) => d !== "."));
12613
+ return staged.filter(({ st, f }) => st !== "A" && !isTestFile(f) && !Object.hasOwn(pre, f) && !rel.includes(f) && !mentions(text2, f) && !mentions(text2, basename18(f)) && !plannedDirs.has(dirname22(f)) && !dirSegs(f).some((d) => toks.has(d))).map(({ f }) => f);
12504
12614
  }
12505
12615
  function flagOutsideScope(o, staged) {
12506
12616
  const unit = unitFlags(staged, process.env, o.intake?.preexisting_dirty ?? {});
@@ -12514,76 +12624,10 @@ var init_scope2 = __esm(() => {
12514
12624
  STOP = new Set(["the", "and", "for", "with", "that", "this", "all", "any", "src", "from", "into", "each", "across", "apply", "add", "use", "make"]);
12515
12625
  });
12516
12626
 
12517
- // ../../loki-ts/src/util/receipt_signer.ts
12518
- import { createHash as createHash10, randomBytes as randomBytes3, createPrivateKey, createPublicKey as createPublicKey2, generateKeyPairSync, sign } from "crypto";
12519
- import { chmodSync as chmodSync5, linkSync, mkdirSync as mkdirSync20, readFileSync as readFileSync37, statSync as statSync16, unlinkSync as unlinkSync5, writeFileSync as writeFileSync22 } from "fs";
12520
- import { homedir as homedir8 } from "os";
12521
- import { dirname as dirname22, join as join52 } from "path";
12522
- function canonicalJson(x) {
12523
- const walk = (v) => {
12524
- if (Array.isArray(v))
12525
- return `[${v.map(walk).join(",")}]`;
12526
- if (v !== null && typeof v === "object") {
12527
- const o = v;
12528
- return `{${Object.keys(o).filter((k) => o[k] !== undefined).sort().map((k) => `${JSON.stringify(k)}:${walk(o[k])}`).join(",")}}`;
12529
- }
12530
- return JSON.stringify(v ?? null);
12531
- };
12532
- return walk(x).replace(/[\u0080-\uFFFF]/g, (c) => `\\u${c.charCodeAt(0).toString(16).padStart(4, "0")}`);
12533
- }
12534
- function loadSigningKey(generate = true) {
12535
- try {
12536
- const inline = process.env["LOKI_RECEIPT_SIGNING_KEY"]?.trim();
12537
- let pem = inline ?? "";
12538
- if (!inline) {
12539
- const given = process.env["LOKI_RECEIPT_SIGNING_KEY_FILE"]?.trim();
12540
- const file = given || join52(process.env["HOME"] || homedir8(), ".loki", "keys", RECEIPT_SIGNER_BASENAME);
12541
- try {
12542
- pem = readFileSync37(file);
12543
- if (!given) {
12544
- for (const [f, m] of [[file, 384], [dirname22(file), 448]])
12545
- if (statSync16(f).mode & 63)
12546
- chmodSync5(f, m);
12547
- }
12548
- } catch (e) {
12549
- if (e.code !== "ENOENT" || !generate)
12550
- return null;
12551
- mkdirSync20(dirname22(dirname22(file)), { recursive: true, mode: 448 });
12552
- mkdirSync20(dirname22(file), { recursive: true, mode: 448 });
12553
- const tmp = `${file}.${randomBytes3(6).toString("hex")}.tmp`;
12554
- writeFileSync22(tmp, generateKeyPairSync("ed25519").privateKey.export({ type: "pkcs8", format: "pem" }), { flag: "wx", mode: 384 });
12555
- try {
12556
- linkSync(tmp, file);
12557
- } catch (l) {
12558
- if (l.code !== "EEXIST")
12559
- throw l;
12560
- } finally {
12561
- unlinkSync5(tmp);
12562
- }
12563
- pem = readFileSync37(file);
12564
- }
12565
- }
12566
- const k = createPrivateKey(pem);
12567
- return k.asymmetricKeyType === "ed25519" ? k : null;
12568
- } catch {
12569
- return null;
12570
- }
12571
- }
12572
- function signReceipt(runId, hash) {
12573
- const key = loadSigningKey();
12574
- if (!key)
12575
- return { jwt: null, kid: null };
12576
- const kid = kidOf2(createPublicKey2(key));
12577
- 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) }))}`;
12578
- return { jwt: `${input}.${b64u(sign(null, Buffer.from(input), key))}`, kid };
12579
- }
12580
- var RECEIPT_SIGNER_BASENAME = "receipt-ed25519.pem", b64u = (b) => Buffer.from(b).toString("base64url"), kidOf2 = (pub) => b64u(createHash10("sha256").update(`{"crv":"Ed25519","kty":"OKP","x":"${pub.export({ format: "jwk" }).x}"}`).digest());
12581
- var init_receipt_signer = () => {};
12582
-
12583
12627
  // ../../loki-ts/src/features/visual_evidence.ts
12584
12628
  import { spawn as spawn7 } from "child_process";
12585
- import { createHash as createHash11, randomBytes as randomBytes4 } from "crypto";
12586
- import { existsSync as existsSync31, lstatSync as lstatSync11, mkdirSync as mkdirSync21, readdirSync as readdirSync15, readFileSync as readFileSync38, writeFileSync as writeFileSync23 } from "fs";
12629
+ import { createHash as createHash12, randomBytes as randomBytes4 } from "crypto";
12630
+ import { existsSync as existsSync32, lstatSync as lstatSync11, mkdirSync as mkdirSync21, readdirSync as readdirSync15, readFileSync as readFileSync38, writeFileSync as writeFileSync23 } from "fs";
12587
12631
  import { createServer } from "net";
12588
12632
  import { dirname as dirname23, isAbsolute as isAbsolute8, join as join53, normalize as normalize2, relative as relative8, sep as sep12 } from "path";
12589
12633
  function routeFor(file) {
@@ -12615,7 +12659,7 @@ function checkScreens(runDir, screens) {
12615
12659
  if (isSymlink(cur))
12616
12660
  return `evidence screenshot path is a symlink: ${s.path}`;
12617
12661
  }
12618
- if (!existsSync31(file))
12662
+ if (!existsSync32(file))
12619
12663
  return `evidence screenshot is missing: ${s.path}`;
12620
12664
  if (!isRegularFile(file))
12621
12665
  return `evidence screenshot is not a regular file: ${s.path}`;
@@ -12659,7 +12703,7 @@ async function waitUp(url, ms, signal) {
12659
12703
  function openapiPaths(repoDir) {
12660
12704
  for (const f of ["openapi.json", "openapi.yaml", "openapi.yml", "swagger.json"]) {
12661
12705
  const p = join53(repoDir, f);
12662
- if (!existsSync31(p))
12706
+ if (!existsSync32(p))
12663
12707
  continue;
12664
12708
  try {
12665
12709
  const txt = readFileSync38(p, "utf8");
@@ -12766,7 +12810,7 @@ async function captureVisualEvidence(repoDir, runDir, changed, opts = {}) {
12766
12810
  const pkgPath = join53(appDir, "package.json");
12767
12811
  let script = null;
12768
12812
  if (!boot) {
12769
- if (!existsSync31(pkgPath))
12813
+ if (!existsSync32(pkgPath))
12770
12814
  return skip("no package.json");
12771
12815
  script = pickScript(JSON.parse(readFileSync38(pkgPath, "utf8")));
12772
12816
  if (!script)
@@ -12808,7 +12852,7 @@ async function captureVisualEvidence(repoDir, runDir, changed, opts = {}) {
12808
12852
  break;
12809
12853
  const rel = join53(rel0, `${screenName(route)}.png`);
12810
12854
  const ok = await shoot(pw, ["screenshot", `${base}${route}`, join53(runDir, rel)], appDir, left(), signal);
12811
- if (ok && !aborted() && !isSymlink(join53(runDir, rel)) && existsSync31(join53(runDir, rel)))
12855
+ if (ok && !aborted() && !isSymlink(join53(runDir, rel)) && existsSync32(join53(runDir, rel)))
12812
12856
  rels.push(rel);
12813
12857
  }
12814
12858
  if (aborted())
@@ -12860,12 +12904,12 @@ async function sealEvidence(repoDir, runDir, o, notProven, signal, emit) {
12860
12904
  function receiptScreensProblem(receiptPath, receipt) {
12861
12905
  return receipt["evidence_screens"] === undefined ? null : checkScreens(dirname23(receiptPath), receipt["evidence_screens"]);
12862
12906
  }
12863
- var visualEvidenceEnabled = (env = process.env) => env["LOKI_VISUAL_EVIDENCE"] !== "0", PAGE_RE, isPageFile = (p) => PAGE_RE.test(p), screenName = (route) => (route === "/" ? "index" : route.slice(1).replace(/[^A-Za-z0-9._-]+/g, "_")) || "index", sha256File = (p) => createHash11("sha256").update(readFileSync38(p)).digest("hex"), playwrightBin = (repoDir) => {
12907
+ var visualEvidenceEnabled = (env = process.env) => env["LOKI_VISUAL_EVIDENCE"] !== "0", PAGE_RE, isPageFile = (p) => PAGE_RE.test(p), screenName = (route) => (route === "/" ? "index" : route.slice(1).replace(/[^A-Za-z0-9._-]+/g, "_")) || "index", sha256File = (p) => createHash12("sha256").update(readFileSync38(p)).digest("hex"), playwrightBin = (repoDir) => {
12864
12908
  const bin = join53(repoDir, "node_modules", ".bin", "playwright");
12865
- return existsSync31(bin) ? bin : null;
12909
+ return existsSync32(bin) ? bin : null;
12866
12910
  }, playwrightTestPkg = (repoDir) => {
12867
12911
  const d = join53(repoDir, "node_modules", "@playwright", "test");
12868
- return existsSync31(join53(d, "package.json")) ? d : null;
12912
+ return existsSync32(join53(d, "package.json")) ? d : null;
12869
12913
  }, e2eSpecSource = (pkgDir, base, routes) => `const { test } = require(${JSON.stringify(pkgDir)});
12870
12914
  test("loki e2e walkthrough", async ({ page }) => {
12871
12915
  for (const r of ${JSON.stringify(routes)}) { await page.goto(${JSON.stringify(base)} + r); await page.waitForLoadState("load"); }
@@ -12896,7 +12940,7 @@ var init_visual_evidence = __esm(() => {
12896
12940
  });
12897
12941
 
12898
12942
  // ../../loki-ts/src/council/role_agents.ts
12899
- import { existsSync as existsSync32, readdirSync as readdirSync16, readFileSync as readFileSync39 } from "fs";
12943
+ import { existsSync as existsSync33, readdirSync as readdirSync16, readFileSync as readFileSync39 } from "fs";
12900
12944
  import { homedir as homedir9 } from "os";
12901
12945
  import { join as join54 } from "path";
12902
12946
  function agentDirs(repoDir, env = process.env) {
@@ -12917,7 +12961,7 @@ function parseAgent(file, stem2) {
12917
12961
  }
12918
12962
  function loadRoleAgent(repoDir, role, env = process.env) {
12919
12963
  for (const d of agentDirs(repoDir, env)) {
12920
- if (!existsSync32(d))
12964
+ if (!existsSync33(d))
12921
12965
  continue;
12922
12966
  let names;
12923
12967
  try {
@@ -12945,7 +12989,7 @@ var init_role_agents = __esm(() => {
12945
12989
  });
12946
12990
 
12947
12991
  // ../../loki-ts/src/engine10/stages/xreview.ts
12948
- import { existsSync as existsSync33, readFileSync as readFileSync40 } from "fs";
12992
+ import { existsSync as existsSync34, readFileSync as readFileSync40 } from "fs";
12949
12993
  import { join as join55 } from "path";
12950
12994
  function minVerdict(verdict, review) {
12951
12995
  const ceiling = review?.level === "block" ? "PARTIAL" : verdict;
@@ -12960,7 +13004,7 @@ function explicitReview(repoDir, env) {
12960
13004
  return "off";
12961
13005
  for (const f of ["loki.yaml", "loki.yml"]) {
12962
13006
  const p = join55(repoDir, f);
12963
- if (!existsSync33(p))
13007
+ if (!existsSync34(p))
12964
13008
  continue;
12965
13009
  try {
12966
13010
  const m = /^review:[ \t]*["']?([A-Za-z]+)["']?[ \t]*(?:#.*)?$/m.exec(readFileSync40(p, "utf8"));
@@ -13181,9 +13225,9 @@ var init_cost_preview = __esm(() => {
13181
13225
  });
13182
13226
 
13183
13227
  // ../../loki-ts/src/features/contract.ts
13184
- import { existsSync as existsSync34, lstatSync as lstatSync12, mkdirSync as mkdirSync22, readFileSync as readFileSync41, rmSync as rmSync14, writeFileSync as writeFileSync24 } from "fs";
13228
+ import { existsSync as existsSync35, lstatSync as lstatSync12, mkdirSync as mkdirSync22, readFileSync as readFileSync41, rmSync as rmSync14, writeFileSync as writeFileSync24 } from "fs";
13185
13229
  import { dirname as dirname24, join as join56 } from "path";
13186
- import { createHash as createHash12 } from "crypto";
13230
+ import { createHash as createHash13 } from "crypto";
13187
13231
  function contractEnabled(env = process.env) {
13188
13232
  return env["LOKI_CONTRACT"] !== "0";
13189
13233
  }
@@ -13234,7 +13278,7 @@ function readContract(repoDir) {
13234
13278
  } catch {
13235
13279
  return { contract: null, notes: ["contract unreadable: read failed"], sha256: null };
13236
13280
  }
13237
- const sha2563 = createHash12("sha256").update(raw2).digest("hex");
13281
+ const sha2563 = createHash13("sha256").update(raw2).digest("hex");
13238
13282
  let j;
13239
13283
  try {
13240
13284
  j = JSON.parse(raw2.toString("utf8"));
@@ -13304,12 +13348,12 @@ var init_contract = __esm(() => {
13304
13348
  });
13305
13349
 
13306
13350
  // ../../loki-ts/src/features/speed/seal_group.ts
13307
- import { createHash as createHash13 } from "crypto";
13308
- import { existsSync as existsSync35, readdirSync as readdirSync17, readFileSync as readFileSync42, statSync as statSync17 } from "fs";
13309
- import { dirname as dirname25, join as join57 } from "path";
13351
+ import { createHash as createHash14 } from "crypto";
13352
+ import { existsSync as existsSync36, mkdirSync as mkdirSync23, readdirSync as readdirSync17, readFileSync as readFileSync42, renameSync as renameSync8, statSync as statSync17, writeFileSync as writeFileSync25 } from "fs";
13353
+ import { basename as basename19, dirname as dirname25, join as join57 } from "path";
13310
13354
  function sealGroup(runDir, rh) {
13311
13355
  const manifestPath = join57(runDir, "group", "manifest.json");
13312
- if (!existsSync35(manifestPath))
13356
+ if (!existsSync36(manifestPath))
13313
13357
  return { section: undefined, notProven: [], problems: 0, allUnitsPass: true };
13314
13358
  const notProven = [];
13315
13359
  let problems = 0;
@@ -13353,7 +13397,7 @@ function sealGroup(runDir, rh) {
13353
13397
  unit_id: id,
13354
13398
  sub_receipt_sha256: sha2(bytes),
13355
13399
  receipt_sha256: sub["receipt_sha256"],
13356
- events_file_sha256: existsSync35(ep) ? sha2(readFileSync42(ep)) : null,
13400
+ events_file_sha256: existsSync36(ep) ? sha2(readFileSync42(ep)) : null,
13357
13401
  verdict,
13358
13402
  goal: text2(u["goal"], 200),
13359
13403
  files: (Array.isArray(u["files"]) ? u["files"] : []).map((f) => text2(f, 200)).filter(Boolean).slice(0, 50),
@@ -13378,7 +13422,7 @@ async function verifyGroup(receiptPath, receipt, rh, verifyOne, deps = {}) {
13378
13422
  const bad = (reason) => ({ verdict: "TAMPERED", reason: `group: ${reason}` });
13379
13423
  const g = receipt["group"];
13380
13424
  if (g === undefined)
13381
- return existsSync35(join57(runDir, "group", "manifest.json")) ? bad("group directory present but the receipt has no group section") : null;
13425
+ return existsSync36(join57(runDir, "group", "manifest.json")) ? bad("group directory present but the receipt has no group section") : null;
13382
13426
  if (!isObj10(g) || !ID_RE.test(String(g["group_id"] ?? "")) || !Array.isArray(g["units"]) || g["units"].length === 0)
13383
13427
  return bad("group section malformed");
13384
13428
  const entries = g["units"];
@@ -13403,7 +13447,7 @@ async function verifyGroup(receiptPath, receipt, rh, verifyOne, deps = {}) {
13403
13447
  }
13404
13448
  if (sha2(bytes) !== e["sub_receipt_sha256"])
13405
13449
  return bad(`unit ${id} sub-receipt does not match its recorded hash`);
13406
- const evSha = existsSync35(ep) ? sha2(readFileSync42(ep)) : null;
13450
+ const evSha = existsSync36(ep) ? sha2(readFileSync42(ep)) : null;
13407
13451
  if (evSha !== (e["events_file_sha256"] ?? null))
13408
13452
  return bad(`unit ${id} events.jsonl does not match its recorded hash`);
13409
13453
  const sub = readJson5(rp);
@@ -13421,7 +13465,83 @@ async function verifyGroup(receiptPath, receipt, rh, verifyOne, deps = {}) {
13421
13465
  }
13422
13466
  return unchecked ? { verdict: "UNCHECKED", reason: `group: ${unchecked}` } : null;
13423
13467
  }
13424
- var ID_RE, PASSING, sha2 = (b) => createHash13("sha256").update(b).digest("hex"), isObj10 = (v) => v !== null && typeof v === "object" && !Array.isArray(v), text2 = (v, cap) => typeof v === "string" ? v.replace(/[\x00-\x1f\x7f]+/g, " ").trim().slice(0, cap) : "", num4 = (v) => typeof v === "number" && Number.isFinite(v) && v >= 0 ? v : 0, unitsDir = (runDir) => join57(runDir, "group", "units"), readJson5 = (p) => {
13468
+ function compositeVerdict(childVerdicts) {
13469
+ const base = childVerdicts.includes("SPEC_CONFLICT") ? "SPEC_CONFLICT" : childVerdicts.length > 0 && childVerdicts.every((v) => v === "ALREADY_SATISFIED") ? "ALREADY_SATISFIED" : "VERIFIED";
13470
+ const failed = childVerdicts.filter((v) => v === "FAILED" || !["VERIFIED", "ALREADY_SATISFIED", "PARTIAL", "SPEC_CONFLICT"].includes(v)).length;
13471
+ return capGroupVerdict(base, { section: undefined, notProven: [], problems: failed, allUnitsPass: childVerdicts.every((v) => PASSING.has(v)) });
13472
+ }
13473
+ async function verifyComposite(receiptPath, receipt, rh, verifyOne, deps = {}) {
13474
+ const runDir = dirname25(receiptPath);
13475
+ const signed = isObj10(receipt["verification"]) && typeof receipt["verification"]["jwt"] === "string";
13476
+ const bad = (reason) => ({ problem: { verdict: "TAMPERED", reason: `composite: ${reason}` }, verdict: "FAILED" });
13477
+ const kids = receipt["children"];
13478
+ if (!Array.isArray(kids) || kids.length === 0 || !ID_RE.test(String(receipt["run_id"] ?? "")))
13479
+ return bad("body malformed");
13480
+ let ibytes;
13481
+ try {
13482
+ ibytes = readFileSync42(join57(runDir, "integration.json"));
13483
+ } catch {
13484
+ return bad("integration.json missing");
13485
+ }
13486
+ if (sha2(ibytes) !== receipt["integration_sha256"])
13487
+ return bad("integration.json does not match its recorded hash");
13488
+ const integ = readJson5(join57(runDir, "integration.json"));
13489
+ if (!isObj10(integ) || !isObj10(integ["outcomes"]))
13490
+ return bad("integration.json has no outcomes");
13491
+ const heads = isObj10(integ["heads"]) ? integ["heads"] : {}, outcomes = integ["outcomes"];
13492
+ const order2 = Array.isArray(integ["order"]) ? integ["order"].map(String) : Object.keys(outcomes).sort();
13493
+ const kidRepos = kids.map((k) => isObj10(k) ? String(k["repo"] ?? "") : "");
13494
+ if (new Set(kidRepos).size !== kidRepos.length)
13495
+ return bad("children list a repo more than once");
13496
+ if (kidRepos.length !== order2.length || kidRepos.some((r, i) => r !== order2[i]))
13497
+ return bad("children do not cover the integrated repos in order (a repo is missing or extra)");
13498
+ let unchecked = null;
13499
+ const verdicts = [];
13500
+ for (const raw2 of kids) {
13501
+ const c = isObj10(raw2) ? raw2 : {}, repo = String(c["repo"] ?? ""), rel = String(c["receipt_path"] ?? "");
13502
+ if (!repo || !relOk(rel) || !rel.startsWith(`worktrees/${slugOf(repo)}/.loki/runs/`))
13503
+ return bad(`child ${text2(repo, 80) || "?"} has an invalid receipt path`);
13504
+ if (outcomes[repo] !== "ok")
13505
+ return bad(`child ${text2(repo, 80)} did not finish ok in integration.json`);
13506
+ const rp = join57(runDir, rel);
13507
+ let bytes;
13508
+ try {
13509
+ bytes = readFileSync42(rp);
13510
+ } catch {
13511
+ return bad(`child ${repo} receipt missing`);
13512
+ }
13513
+ if (sha2(bytes) !== c["receipt_file_sha256"])
13514
+ return bad(`child ${repo} receipt does not match its recorded hash (swapped or edited)`);
13515
+ const sub = readJson5(rp);
13516
+ if (!isObj10(sub) || sub["receipt_sha256"] !== c["receipt_sha256"] || rh(sub) !== c["receipt_sha256"])
13517
+ return bad(`child ${repo} receipt hash does not recompute`);
13518
+ if (String(sub["verdict"] ?? "") !== c["verdict"])
13519
+ return bad(`child ${repo} verdict differs from the composite`);
13520
+ if (String(sub["head_sha"] ?? "") !== c["head_sha"] || !c["head_sha"])
13521
+ return bad(`child ${repo} head_sha differs from the composite`);
13522
+ verdicts.push(String(c["verdict"]));
13523
+ if (typeof heads[repo] !== "string" || heads[repo] === "")
13524
+ unchecked ??= `child ${repo} has no integrated head to compare (freshness not checked)`;
13525
+ else if (heads[repo] !== c["head_sha"])
13526
+ unchecked ??= `child ${repo} head ${String(c["head_sha"])} is not the integrated head ${String(heads[repo])}`;
13527
+ const r = await verifyOne(rp, deps);
13528
+ if (r.verdict === "TAMPERED")
13529
+ return bad(`child ${repo} failed verify: ${r.reasons.join("; ")}`);
13530
+ if (r.verdict === "UNSIGNED" && signed)
13531
+ unchecked ??= `child ${repo} receipt is unsigned under a signed composite`;
13532
+ if (r.verdict === "UNCHECKED")
13533
+ unchecked ??= `child ${repo} not checked: ${r.reasons.join("; ")}`;
13534
+ }
13535
+ const expect = compositeVerdict(verdicts);
13536
+ if (receipt["verdict"] !== expect)
13537
+ return bad(`recorded verdict ${String(receipt["verdict"])} differs from the children (${expect})`);
13538
+ return { problem: unchecked ? { verdict: "UNCHECKED", reason: `composite: ${unchecked}` } : null, verdict: expect };
13539
+ }
13540
+ function compositeHash(r) {
13541
+ const { composite_sha256: _c, verification: _v, ...body } = r;
13542
+ return sha2(canonicalJson(body));
13543
+ }
13544
+ var ID_RE, PASSING, sha2 = (b) => createHash14("sha256").update(b).digest("hex"), isObj10 = (v) => v !== null && typeof v === "object" && !Array.isArray(v), text2 = (v, cap) => typeof v === "string" ? v.replace(/[\x00-\x1f\x7f]+/g, " ").trim().slice(0, cap) : "", num4 = (v) => typeof v === "number" && Number.isFinite(v) && v >= 0 ? v : 0, unitsDir = (runDir) => join57(runDir, "group", "units"), readJson5 = (p) => {
13425
13545
  try {
13426
13546
  return JSON.parse(readFileSync42(p, "utf8"));
13427
13547
  } catch {
@@ -13433,15 +13553,16 @@ var ID_RE, PASSING, sha2 = (b) => createHash13("sha256").update(b).digest("hex")
13433
13553
  } catch {
13434
13554
  return null;
13435
13555
  }
13436
- };
13556
+ }, COMPOSITE_SCHEMA = "loki.v10.composite/1", slugOf = (repo) => repo.replace(/\//g, "__"), isComposite = (r) => r["schema"] === COMPOSITE_SCHEMA, relOk = (p) => p !== "" && !p.startsWith("/") && !p.split("/").includes("..");
13437
13557
  var init_seal_group = __esm(() => {
13558
+ init_receipt_signer();
13438
13559
  ID_RE = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;
13439
13560
  PASSING = new Set(["VERIFIED", "ALREADY_SATISFIED"]);
13440
13561
  });
13441
13562
 
13442
13563
  // ../../loki-ts/src/supply/supply_guard.ts
13443
- import { existsSync as existsSync36, readFileSync as readFileSync43, statSync as statSync18 } from "fs";
13444
- import { basename as basename19, join as join58 } from "path";
13564
+ import { existsSync as existsSync37, readFileSync as readFileSync43, statSync as statSync18 } from "fs";
13565
+ import { basename as basename20, join as join58 } from "path";
13445
13566
  function supplyEnabled(env) {
13446
13567
  return env["LOKI_SUPPLY_GUARD"] !== "0";
13447
13568
  }
@@ -13453,11 +13574,11 @@ function supplyVerdict(verdict, r) {
13453
13574
  return r.blocked && verdict === "VERIFIED" ? "FAILED" : verdict;
13454
13575
  }
13455
13576
  function isManifestPath(path) {
13456
- return MANIFESTS.test(basename19(path));
13577
+ return MANIFESTS.test(basename20(path));
13457
13578
  }
13458
13579
  function readDeclared(repoDir) {
13459
13580
  const p = join58(repoDir, ".loki", DECLARED_FILE);
13460
- if (!existsSync36(p))
13581
+ if (!existsSync37(p))
13461
13582
  return { deps: [], problem: null };
13462
13583
  try {
13463
13584
  if (statSync18(p).size > MAX_FILE_BYTES)
@@ -13483,7 +13604,7 @@ function readDeclared(repoDir) {
13483
13604
  }
13484
13605
  function readAllowlist(repoDir) {
13485
13606
  const p = join58(repoDir, ".loki", "supply-allowlist");
13486
- if (!existsSync36(p))
13607
+ if (!existsSync37(p))
13487
13608
  return new Set;
13488
13609
  try {
13489
13610
  return new Set(readFileSync43(p, "utf8").split(`
@@ -13684,9 +13805,9 @@ var init_supply_guard = __esm(() => {
13684
13805
  });
13685
13806
 
13686
13807
  // ../../loki-ts/src/engine10/stages/seal.ts
13687
- import { createHash as createHash14 } from "crypto";
13688
- import { existsSync as existsSync37, readdirSync as readdirSync18, mkdirSync as mkdirSync23, readFileSync as readFileSync44, writeFileSync as writeFileSync25 } from "fs";
13689
- import { basename as basename20, isAbsolute as isAbsolute9, join as join59, relative as relative9 } from "path";
13808
+ import { createHash as createHash15 } from "crypto";
13809
+ import { existsSync as existsSync38, readdirSync as readdirSync18, mkdirSync as mkdirSync24, readFileSync as readFileSync44, writeFileSync as writeFileSync26 } from "fs";
13810
+ import { basename as basename21, isAbsolute as isAbsolute9, join as join59, relative as relative9 } from "path";
13690
13811
  function sanitizeReason(s) {
13691
13812
  const collapsed = s.replace(/[\x00-\x1f\x7f]+/g, " ").trim();
13692
13813
  const capped = collapsed.length > 500 ? `${collapsed.slice(0, 500)}...` : collapsed;
@@ -13760,7 +13881,7 @@ function renderReceiptMd(r) {
13760
13881
  ].join(`
13761
13882
  `);
13762
13883
  }
13763
- var DEEP_NOT_PROVEN, SIGNING_UNAVAILABLE = "receipt signing unavailable (no usable signing key: invalid key or unwritable ~/.loki/keys)", EXCLUDE_LOKI = ":(exclude).loki", sha2562 = (s) => createHash14("sha256").update(s).digest("hex"), str3 = (v) => typeof v === "string" && v !== "" ? v : null, strs2 = (v) => Array.isArray(v) ? v.filter((x) => typeof x === "string" && x !== "") : [], receiptRel = (ctx) => {
13884
+ var DEEP_NOT_PROVEN, SIGNING_UNAVAILABLE = "receipt signing unavailable (no usable signing key: invalid key or unwritable ~/.loki/keys)", EXCLUDE_LOKI = ":(exclude).loki", sha2562 = (s) => createHash15("sha256").update(s).digest("hex"), str3 = (v) => typeof v === "string" && v !== "" ? v : null, strs2 = (v) => Array.isArray(v) ? v.filter((x) => typeof x === "string" && x !== "") : [], receiptRel = (ctx) => {
13764
13885
  const r = relative9(ctx.repoDir, join59(ctx.runDir, "receipt.json"));
13765
13886
  return r.startsWith("..") || isAbsolute9(r) ? `.loki/runs/${ctx.runId}/receipt.json` : r;
13766
13887
  }, commitStage, sealStage;
@@ -13863,7 +13984,7 @@ var init_seal = __esm(() => {
13863
13984
  notProven.add(`Wall test not executed: ${wallNotRun} file(s), no reason recorded`);
13864
13985
  if (wallNotRun > 0) {
13865
13986
  try {
13866
- const kept = new Set((Array.isArray(o.wall?.files) ? o.wall.files : []).map((f) => basename20(String(f.path))));
13987
+ const kept = new Set((Array.isArray(o.wall?.files) ? o.wall.files : []).map((f) => basename21(String(f.path))));
13867
13988
  for (const n2 of readdirSync18(join59(ctx.runDir, "wall")).sort())
13868
13989
  if (!kept.has(n2))
13869
13990
  notProven.add(`wall test discarded: ${n2} (not_run)`);
@@ -13979,7 +14100,7 @@ var init_seal = __esm(() => {
13979
14100
  provider: ctx.provider,
13980
14101
  model: ctx.model,
13981
14102
  resumed: o.intake?.resumed === true,
13982
- events_sha256: sha2562(existsSync37(eventsPath) ? readFileSync44(eventsPath) : ""),
14103
+ events_sha256: sha2562(existsSync38(eventsPath) ? readFileSync44(eventsPath) : ""),
13983
14104
  ...await sealEvidence(ctx.repoDir, ctx.runDir, o, notProven, signal, ctx.emit),
13984
14105
  log_seal: true,
13985
14106
  ...receiptBlock(process.env, cost.usd, cost.unmetered === true, totalS),
@@ -13999,17 +14120,22 @@ var init_seal = __esm(() => {
13999
14120
  hash = receiptSha256(body);
14000
14121
  }
14001
14122
  const receipt = { ...body, receipt_sha256: hash, verification: { jwt: sig.jwt, kid: sig.kid } };
14002
- mkdirSync23(ctx.runDir, { recursive: true });
14123
+ mkdirSync24(ctx.runDir, { recursive: true });
14003
14124
  const path = join59(ctx.runDir, "receipt.json");
14004
- writeFileSync25(path, JSON.stringify(receipt, null, 2) + `
14125
+ writeFileSync26(path, JSON.stringify(receipt, null, 2) + `
14005
14126
  `);
14006
- writeFileSync25(join59(ctx.runDir, "receipt.md"), renderReceiptMd(receipt));
14127
+ writeFileSync26(join59(ctx.runDir, "receipt.md"), renderReceiptMd(receipt));
14007
14128
  recordRun(process.env, ctx.repoDir, ctx.model, verdict, cost.usd, cost.unmetered === true, totalS);
14008
14129
  const signed = sig.jwt !== null;
14009
14130
  const data = { receipt_path: path, receipt_sha256: hash, signed, kid: sig.kid, verdict, not_proven: receipt.not_proven, ...mp ? { mutation_line: mp.line } : {}, ...routeBlock ? { route_line: routePrLine(routeBlock) } : {} };
14010
14131
  try {
14011
14132
  recordRunVerdict(ctx.repoDir, ctx.runId, verdict);
14012
14133
  } catch {}
14134
+ if (process.env["LOKI_AUTO_LEARN_PR"] !== "0") {
14135
+ try {
14136
+ appendReceiptFact(ctx.repoDir, { runId: ctx.runId, verdict, receiptSha256: signed ? hash : null, title: str3(ctx.outputs().intake?.title) ?? `run ${ctx.runId}`, headSha: receipt.head_sha });
14137
+ } catch {}
14138
+ }
14013
14139
  ctx.emit("receipt.sealed", "seal", { path, receipt_sha256: hash, signed, kid: sig.kid, verdict, not_proven: receipt.not_proven, ...routeBlock ? { route_line: routePrLine(routeBlock) } : {} });
14014
14140
  return { status: "completed", data: { ...data, summary: `${verdict} receipt ${hash.slice(0, 12)} ${signed ? `SIGNED kid ${sig.kid}` : "UNSIGNED"}` } };
14015
14141
  }
@@ -14109,9 +14235,9 @@ var init_keys_cmd = __esm(() => {
14109
14235
  });
14110
14236
 
14111
14237
  // ../../loki-ts/src/engine10/verify_cmd.ts
14112
- import { existsSync as existsSync38, readFileSync as readFileSync46, statSync as statSync20 } from "fs";
14113
- import { dirname as dirname27, join as join60 } from "path";
14114
- import { createHash as createHash15, createPublicKey as createPublicKey4, verify as verify3 } from "crypto";
14238
+ import { existsSync as existsSync39, readFileSync as readFileSync46, renameSync as renameSync9, statSync as statSync20, writeFileSync as writeFileSync27 } from "fs";
14239
+ import { basename as basename22, dirname as dirname27, join as join60 } from "path";
14240
+ import { createHash as createHash16, createPublicKey as createPublicKey4, verify as verify3 } from "crypto";
14115
14241
  function pubFor(kid, given) {
14116
14242
  if (given)
14117
14243
  return kidOf2(given) === kid ? given : undefined;
@@ -14148,10 +14274,10 @@ function checkAttestation(jwt, expectedHash, given) {
14148
14274
  }
14149
14275
  function checkEventLog(receiptPath, receipt) {
14150
14276
  const bound = receipt["events_sha256"];
14151
- if (typeof bound !== "string" || bound === createHash15("sha256").digest("hex"))
14277
+ if (typeof bound !== "string" || bound === createHash16("sha256").digest("hex"))
14152
14278
  return null;
14153
14279
  const path = join60(dirname27(receiptPath), "events.jsonl");
14154
- if (!existsSync38(path))
14280
+ if (!existsSync39(path))
14155
14281
  return "events.jsonl is missing";
14156
14282
  const raw2 = readFileSync46(path, "utf8"), events2 = readEvents(path), lines = raw2.split(`
14157
14283
  `).filter((l) => l.trim() !== "");
@@ -14159,7 +14285,7 @@ function checkEventLog(receiptPath, receipt) {
14159
14285
  return "events.jsonl has a forged, edited or missing line";
14160
14286
  if (events2.some((e) => e.type === "tamper.detected"))
14161
14287
  return "the supervisor detected events.jsonl being modified during the run";
14162
- const h = createHash15("sha256");
14288
+ const h = createHash16("sha256");
14163
14289
  for (const l of lines) {
14164
14290
  if (h.copy().digest("hex") === bound)
14165
14291
  return null;
@@ -14178,7 +14304,7 @@ function checkLogSeal(receiptPath, hash, kid, given) {
14178
14304
  if (!d)
14179
14305
  return { verdict: "TAMPERED", reason: "events.jsonl has no signed log.sealed line after run.completed" };
14180
14306
  const lines = readFileSync46(path, "utf8").split(`
14181
- `).filter((l) => l.trim() !== ""), sha3 = createHash15("sha256").update(lines.slice(0, done + 1).join(`
14307
+ `).filter((l) => l.trim() !== ""), sha3 = createHash16("sha256").update(lines.slice(0, done + 1).join(`
14182
14308
  `) + `
14183
14309
  `).digest("hex");
14184
14310
  const pub = d["kid"] === kid ? pubFor(kid, given) : undefined;
@@ -14187,7 +14313,7 @@ function checkLogSeal(receiptPath, hash, kid, given) {
14187
14313
  return null;
14188
14314
  }
14189
14315
  async function verifyReceipt(receiptPath, deps = {}) {
14190
- if (!existsSync38(receiptPath))
14316
+ if (!existsSync39(receiptPath))
14191
14317
  return { verdict: "UNCHECKED", reasons: [`receipt not found: ${receiptPath}`] };
14192
14318
  let receipt;
14193
14319
  try {
@@ -14197,11 +14323,12 @@ async function verifyReceipt(receiptPath, deps = {}) {
14197
14323
  }
14198
14324
  if (isEnvelope(receipt))
14199
14325
  return verifyDsseReceipt(receipt, { pubFor: (kid) => pubFor(kid, deps.pubkey), hash: computeReceiptHash, attest: (j, h) => checkAttestation(j, h, deps.pubkey) });
14326
+ if (isComposite(receipt))
14327
+ return verifyCompositeFile(receiptPath, receipt, deps);
14200
14328
  const recorded = receipt["receipt_sha256"];
14201
14329
  const computed = computeReceiptHash(receipt);
14202
- if (typeof recorded !== "string" || recorded !== computed) {
14330
+ if (typeof recorded !== "string" || recorded !== computed)
14203
14331
  return { verdict: "TAMPERED", reasons: [`receipt_sha256 mismatch: recorded ${JSON.stringify(recorded)}, computed ${computed}`] };
14204
- }
14205
14332
  const logProblem = checkEventLog(receiptPath, receipt);
14206
14333
  if (logProblem)
14207
14334
  return { verdict: "TAMPERED", reasons: [logProblem] };
@@ -14234,9 +14361,34 @@ async function verifyReceipt(receiptPath, deps = {}) {
14234
14361
  return { verdict: gp.verdict, reasons: [gp.reason] };
14235
14362
  return { verdict: "VERIFIED", reasons: [], receiptSha256: computed };
14236
14363
  }
14364
+ async function verifyCompositeFile(receiptPath, receipt, deps) {
14365
+ const computed = compositeHash(receipt);
14366
+ if (receipt["composite_sha256"] !== computed)
14367
+ return { verdict: "TAMPERED", reasons: [`composite_sha256 mismatch: recorded ${JSON.stringify(receipt["composite_sha256"])}, computed ${computed}`] };
14368
+ const c = await verifyComposite(receiptPath, receipt, computeReceiptHash, verifyReceipt, deps);
14369
+ if (c.problem?.verdict === "TAMPERED")
14370
+ return { verdict: "TAMPERED", reasons: [c.problem.reason] };
14371
+ const jwt = (receipt["verification"] ?? {}).jwt ?? null;
14372
+ if (jwt !== null && typeof jwt !== "string")
14373
+ return { verdict: "UNCHECKED", reasons: ["verification.jwt is not a string"] };
14374
+ if (!jwt)
14375
+ return c.problem ? { verdict: c.problem.verdict, reasons: [c.problem.reason] } : { verdict: "UNSIGNED", reasons: [], receiptSha256: computed };
14376
+ const outcome = checkAttestation(jwt, computed, deps.pubkey);
14377
+ if (outcome.status === "unchecked")
14378
+ return { verdict: "UNCHECKED", reasons: [outcome.reason ?? "attestation not checked"] };
14379
+ if (outcome.status === "tampered")
14380
+ return { verdict: "TAMPERED", reasons: [outcome.reason ?? "attestation invalid"] };
14381
+ const shown = receipt["verification"].kid, signed = JSON.parse(Buffer.from(jwt.split(".")[0], "base64url").toString()).kid;
14382
+ if (shown !== undefined && shown !== signed)
14383
+ return { verdict: "TAMPERED", reasons: ["verification.kid differs from the signing kid in the attestation header"] };
14384
+ if (c.problem)
14385
+ return { verdict: c.problem.verdict, reasons: [c.problem.reason] };
14386
+ return { verdict: "VERIFIED", reasons: [], receiptSha256: computed };
14387
+ }
14237
14388
  var computeReceiptHash = (receipt) => receiptSha256(receipt);
14238
14389
  var init_verify_cmd = __esm(() => {
14239
14390
  init_paths();
14391
+ init_receipt_signer();
14240
14392
  init_run_status();
14241
14393
  init_events();
14242
14394
  init_seal();
@@ -14247,7 +14399,7 @@ var init_verify_cmd = __esm(() => {
14247
14399
  });
14248
14400
 
14249
14401
  // src/server/app.ts
14250
- import { existsSync as existsSync40, readFileSync as readFileSync49, statSync as statSync21 } from "fs";
14402
+ import { existsSync as existsSync41, readFileSync as readFileSync49, statSync as statSync21 } from "fs";
14251
14403
  import { join as join63, resolve as resolve15, sep as sep14 } from "path";
14252
14404
 
14253
14405
  // node_modules/hono/dist/request/constants.js
@@ -22455,7 +22607,7 @@ var loki_yaml_schema_default = {
22455
22607
  items: { type: "string", minLength: 1 }
22456
22608
  },
22457
22609
  workspaces: {
22458
- description: "Named groups of repos for cross-repo runs (`loki workspace run`). Accepted unconditionally; runtime behavior is gated by LOKI_WORKSPACES=1.",
22610
+ description: "Named groups of repos for cross-repo runs (`loki workspace run`). On by default; LOKI_WORKSPACES=0 disables. Unknown or mistyped keys fail validation.",
22459
22611
  type: "object",
22460
22612
  additionalProperties: {
22461
22613
  type: "object",
@@ -22479,9 +22631,12 @@ var loki_yaml_schema_default = {
22479
22631
  additionalProperties: false,
22480
22632
  properties: {
22481
22633
  command: { type: "string", minLength: 1 },
22482
- timeout_s: { type: "integer", minimum: 1 }
22634
+ timeout_s: { type: "integer", minimum: 1 },
22635
+ per_stage: { type: "boolean" }
22483
22636
  }
22484
22637
  },
22638
+ concurrency: { type: "integer", minimum: 1 },
22639
+ plan: { type: "boolean" },
22485
22640
  integration_files: { type: "array", items: { type: "string", minLength: 1 } }
22486
22641
  }
22487
22642
  }
@@ -24609,7 +24764,7 @@ function ttlCache(ttlMs = PROBE_CACHE_MS, now2 = Date.now) {
24609
24764
  }
24610
24765
 
24611
24766
  // src/server/legacy/shim.ts
24612
- import { existsSync as existsSync39, readFileSync as readFileSync48 } from "fs";
24767
+ import { existsSync as existsSync40, readFileSync as readFileSync48 } from "fs";
24613
24768
  import { join as join62 } from "path";
24614
24769
 
24615
24770
  // src/server/legacy/routes.ts
@@ -24984,7 +25139,7 @@ var NOT_SUPPORTED = "not yet supported by the Control Plane";
24984
25139
  var repoVersion = () => {
24985
25140
  for (const f of [join62(import.meta.dir, "../../../../../VERSION")]) {
24986
25141
  try {
24987
- if (existsSync39(f))
25142
+ if (existsSync40(f))
24988
25143
  return readFileSync48(f, "utf8").trim();
24989
25144
  } catch {}
24990
25145
  }
@@ -25167,7 +25322,7 @@ function legacyShim(opts) {
25167
25322
 
25168
25323
  // src/server/app.ts
25169
25324
  var MAX_BODY2 = 1e6;
25170
- var defaultUiDir = () => [join63(import.meta.dir, "../../ui/dist"), join63(import.meta.dir, "../ui/dist")].find((d) => existsSync40(join63(d, "index.html")));
25325
+ var defaultUiDir = () => [join63(import.meta.dir, "../../ui/dist"), join63(import.meta.dir, "../ui/dist")].find((d) => existsSync41(join63(d, "index.html")));
25171
25326
  function createApp(opts) {
25172
25327
  const uiDir = opts.uiDir ?? defaultUiDir();
25173
25328
  const { db, sqlite } = openDb(opts.dbPath);
@@ -25310,7 +25465,7 @@ function createApp(opts) {
25310
25465
  return c.json({ error: "not found" }, 404);
25311
25466
  const root = resolve15(uiDir);
25312
25467
  const f = resolve15(root, `.${p}`);
25313
- if (f.startsWith(root + sep14) && existsSync40(f) && statSync21(f).isFile())
25468
+ if (f.startsWith(root + sep14) && existsSync41(f) && statSync21(f).isFile())
25314
25469
  return new Response(Bun.file(f));
25315
25470
  if (/\.[a-z0-9]+$/i.test(p))
25316
25471
  return c.json({ error: "not found" }, 404);