loki-mode 11.3.9 → 11.4.2

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 (100) hide show
  1. package/README.md +34 -18
  2. package/SKILL.md +3 -3
  3. package/VERSION +1 -1
  4. package/agents/custom_agents.py +280 -0
  5. package/autonomy/council-v2.sh +22 -1
  6. package/autonomy/edit.sh +242 -0
  7. package/autonomy/lib/chat.sh +312 -0
  8. package/autonomy/lib/help-core.sh +164 -0
  9. package/autonomy/lib/json-output.sh +38 -0
  10. package/autonomy/lib/run-summary.sh +96 -0
  11. package/autonomy/loki +105 -10
  12. package/autonomy/route.sh +270 -0
  13. package/autonomy/run.sh +6 -0
  14. package/autonomy/setup-wizard.sh +161 -0
  15. package/autonomy/verify.sh +22 -2
  16. package/bin/loki +73 -18
  17. package/completions/_loki +55 -0
  18. package/completions/loki.bash +49 -3
  19. package/dashboard/__init__.py +1 -1
  20. package/docs/CLI-CORE-VERBS.md +112 -0
  21. package/docs/CLI-REFERENCE.md +7 -1
  22. package/docs/COMPARE-OPENCODE.md +94 -0
  23. package/docs/COMPONENTS.md +1 -0
  24. package/docs/INSTALLATION.md +56 -12
  25. package/docs/KNOWN-ISSUES.md +2 -12
  26. package/docs/QUICKSTART.md +108 -0
  27. package/docs/environment-variables.md +9 -2
  28. package/docs/exit-codes.md +29 -12
  29. package/docs/v10/CLI-MODERN.md +10 -4
  30. package/docs/v10/GUIDE.md +270 -44
  31. package/docs/v10/OPENCODE-PARITY-PLAN.md +84 -0
  32. package/loki-ts/dist/loki.js +968 -933
  33. package/mcp/__init__.py +1 -1
  34. package/package.json +2 -2
  35. package/packages/control-plane/dist/server.js +180 -80
  36. package/plugins/loki-mode/.claude-plugin/plugin.json +1 -1
  37. package/providers/claude.sh +16 -2
  38. package/providers/model_route_exec.sh +22 -0
  39. package/providers/model_routes.sh +188 -0
  40. package/skills/providers.md +13 -0
  41. package/templates/agents/planner.md +10 -0
  42. package/templates/agents/reviewer.md +10 -0
  43. package/templates/agents/tester.md +10 -0
  44. package/web-app/dist/assets/{AdminPage-QK-i_x-O.js → AdminPage-CYJ2BWRC.js} +1 -1
  45. package/web-app/dist/assets/{Avatar-DblkLNjB.js → Avatar-DdK4wynI.js} +1 -1
  46. package/web-app/dist/assets/{Badge-DIV2ny5H.js → Badge-CrX3MI7J.js} +1 -1
  47. package/web-app/dist/assets/{Button--rePJOlQ.js → Button-709-wwGM.js} +1 -1
  48. package/web-app/dist/assets/{CockpitPage-BZlKxpGy.js → CockpitPage-BapdTInO.js} +1 -1
  49. package/web-app/dist/assets/{ComparePage-3_INSVux.js → ComparePage-DT99pGvA.js} +1 -1
  50. package/web-app/dist/assets/{ErrorBoundary-DHG63CNr.js → ErrorBoundary-B0uJSys8.js} +1 -1
  51. package/web-app/dist/assets/{EvidenceReceiptPanel-DFsBnLlO.js → EvidenceReceiptPanel-BtMFgElP.js} +1 -1
  52. package/web-app/dist/assets/{GitHubIssuesPanel-phPGah98.js → GitHubIssuesPanel-DjJuU941.js} +1 -1
  53. package/web-app/dist/assets/{GitHubPRsPanel-T7KQqJmx.js → GitHubPRsPanel-HXfEX1BC.js} +1 -1
  54. package/web-app/dist/assets/{HomePage-DFjGUF5I.js → HomePage-Cqyves-S.js} +2 -2
  55. package/web-app/dist/assets/{LoginPage-CorUid_5.js → LoginPage-jUbtC7_Y.js} +1 -1
  56. package/web-app/dist/assets/{MagicPage-DofbSxKD.js → MagicPage-DHRsqjgR.js} +1 -1
  57. package/web-app/dist/assets/{MetricsPage-qzrhhfLC.js → MetricsPage-B-pED05R.js} +1 -1
  58. package/web-app/dist/assets/{NotFoundPage-BAZR0mMb.js → NotFoundPage-CsbUs21R.js} +1 -1
  59. package/web-app/dist/assets/{ProjectPage-BOSUzG9J.js → ProjectPage-BtlLEQwB.js} +3 -3
  60. package/web-app/dist/assets/{ProjectsPage-Ba0YLtzD.js → ProjectsPage-DaRbRjSp.js} +1 -1
  61. package/web-app/dist/assets/{SettingsPage-Cr10Ue2K.js → SettingsPage-DALrtP3F.js} +1 -1
  62. package/web-app/dist/assets/{ShowcasePage-Czn655fe.js → ShowcasePage-CoPHSNP0.js} +1 -1
  63. package/web-app/dist/assets/{SystemSettingsPage-Dwjnw6Ob.js → SystemSettingsPage-kOuSN50O.js} +1 -1
  64. package/web-app/dist/assets/{TeamsPage-D8nc69Be.js → TeamsPage-DGWK0LMp.js} +1 -1
  65. package/web-app/dist/assets/{TemplatesPage-DHsaCDGv.js → TemplatesPage-C-_10CwA.js} +1 -1
  66. package/web-app/dist/assets/{TerminalOutput-DBejni2a.js → TerminalOutput-Cz_vPbXL.js} +1 -1
  67. package/web-app/dist/assets/{activity-B2VsBl1C.js → activity-icIsXJVa.js} +1 -1
  68. package/web-app/dist/assets/{bell-CT77Fq2H.js → bell-DbPR2_4r.js} +1 -1
  69. package/web-app/dist/assets/{bot-DfaWgF4H.js → bot-DvjP3Yh5.js} +1 -1
  70. package/web-app/dist/assets/{check-D1yp7Ndv.js → check-D1OBxHeU.js} +1 -1
  71. package/web-app/dist/assets/{chevron-left-B6Pa8IOX.js → chevron-left-D09jDZ29.js} +1 -1
  72. package/web-app/dist/assets/{circle-alert-D5jrL_Y7.js → circle-alert-DyZMLOcC.js} +1 -1
  73. package/web-app/dist/assets/{clock-DIsY_fa_.js → clock-CdpdJTId.js} +1 -1
  74. package/web-app/dist/assets/{cloud-eGwLh1As.js → cloud-CoLTlmoj.js} +1 -1
  75. package/web-app/dist/assets/{code-xml-CGDIcZ3m.js → code-xml-DrkE2ZZY.js} +1 -1
  76. package/web-app/dist/assets/{database-CWpdaykI.js → database-CMQQuM2R.js} +1 -1
  77. package/web-app/dist/assets/{dollar-sign-DZEuTCs6.js → dollar-sign-CuioDaOG.js} +1 -1
  78. package/web-app/dist/assets/{file-code-corner-BHBW6m9v.js → file-code-corner-Cx9j9K1S.js} +1 -1
  79. package/web-app/dist/assets/{file-plus-D4h1jZSR.js → file-plus-CYD2zaaI.js} +1 -1
  80. package/web-app/dist/assets/{globe-CXdqKgwd.js → globe-6w-hnQkL.js} +1 -1
  81. package/web-app/dist/assets/{hammer-B3YuuYI_.js → hammer-BpqtautL.js} +1 -1
  82. package/web-app/dist/assets/{index-BLEnboSv.js → index-Dzs-9EgJ.js} +3 -3
  83. package/web-app/dist/assets/{layers-Dvp5nFtw.js → layers-D5hd7jb7.js} +1 -1
  84. package/web-app/dist/assets/{loader-circle-DodDmIZ3.js → loader-circle-C6Y9x_7H.js} +1 -1
  85. package/web-app/dist/assets/{lock-EiuaH1Ep.js → lock-D51L7rYh.js} +1 -1
  86. package/web-app/dist/assets/{package-Cx4m2hFg.js → package-BJF8hkP8.js} +1 -1
  87. package/web-app/dist/assets/{plus-D-Sd5rpH.js → plus-D5kUQ79n.js} +1 -1
  88. package/web-app/dist/assets/{refresh-cw-C0w1cdmF.js → refresh-cw-BKdlCa7F.js} +1 -1
  89. package/web-app/dist/assets/{rotate-ccw-C_qk6MTT.js → rotate-ccw-_bd_YI1e.js} +1 -1
  90. package/web-app/dist/assets/{scroll-text-DSxiFtkc.js → scroll-text-DbkegIxF.js} +1 -1
  91. package/web-app/dist/assets/{server-NTJt7I8M.js → server-DAwqFIsq.js} +1 -1
  92. package/web-app/dist/assets/{shield-alert-HBxMQOvH.js → shield-alert-BuR1tPHu.js} +1 -1
  93. package/web-app/dist/assets/{thumbs-up-DEgsHzSD.js → thumbs-up-BF6p3mRd.js} +1 -1
  94. package/web-app/dist/assets/{trash-2-px46FnTD.js → trash-2-BVpnkqqz.js} +1 -1
  95. package/web-app/dist/assets/{trending-down-CBosVGKa.js → trending-down-DpsDvRwW.js} +1 -1
  96. package/web-app/dist/assets/{trending-up-Dbd7zrQj.js → trending-up-mFogTjt3.js} +1 -1
  97. package/web-app/dist/assets/{upload-nhdFKAs1.js → upload-4T8zpBsi.js} +1 -1
  98. package/web-app/dist/assets/{usePolling-BgaGdinh.js → usePolling-DPXQv8WO.js} +1 -1
  99. package/web-app/dist/assets/{user-CzIihUy9.js → user-BYGIFaTm.js} +1 -1
  100. package/web-app/dist/index.html +1 -1
@@ -9122,6 +9122,37 @@ var init_claude_model = __esm(() => {
9122
9122
  FAMILY_ALIASES = new Set(["haiku", "sonnet", "opus", "fable"]);
9123
9123
  });
9124
9124
 
9125
+ // ../../loki-ts/src/providers/price_table.ts
9126
+ var PRICE_TABLE;
9127
+ var init_price_table = __esm(() => {
9128
+ PRICE_TABLE = Object.freeze({
9129
+ "gpt-5.3-codex": { input: 1.75, output: 14, cachedInput: 0.175 },
9130
+ "gpt-5-codex": { input: 1.25, output: 10, cachedInput: 0.125 },
9131
+ "gpt-5": { input: 1.25, output: 10, cachedInput: 0.125 },
9132
+ "gpt-4.1": { input: 2, output: 8, cachedInput: 0.5 },
9133
+ "gpt-4o": { input: 2.5, output: 10, cachedInput: 1.25 },
9134
+ "o4-mini": { input: 1.1, output: 4.4, cachedInput: 0.275 },
9135
+ o3: { input: 2, output: 8, cachedInput: 0.5 },
9136
+ "claude-opus": { input: 4, output: 20, cachedInput: 0.2 },
9137
+ "claude-sonnet": { input: 2, output: 10, cachedInput: 0.1 },
9138
+ "claude-haiku": { input: 0.1, output: 0.5, cachedInput: 0.01 },
9139
+ "gemini-2.5-pro": { input: 1.25, output: 10 },
9140
+ gemini: { input: 1.25, output: 10 },
9141
+ deepseek: { input: 0.28, output: 0.42 },
9142
+ minimax: { input: 0.3, output: 1.2 }
9143
+ });
9144
+ });
9145
+
9146
+ // ../../loki-ts/src/providers/provider_cost.ts
9147
+ var ESTIMATED_PROVIDERS;
9148
+ var init_provider_cost = __esm(() => {
9149
+ init_price_table();
9150
+ ESTIMATED_PROVIDERS = new Set(["codex", "cline", "aider"]);
9151
+ });
9152
+
9153
+ // ../../loki-ts/src/providers/model_routes.ts
9154
+ var init_model_routes = () => {};
9155
+
9125
9156
  // ../../loki-ts/src/engine10/hooks.ts
9126
9157
  var hooks;
9127
9158
  var init_hooks = __esm(() => {
@@ -9234,6 +9265,8 @@ function sumResultCosts(lokiRoot, iterations, context = []) {
9234
9265
  const unmetered = rec2["source"] === UNMETERED;
9235
9266
  if (unmetered)
9236
9267
  out2.unmetered = true;
9268
+ if (rec2["estimated"] === true)
9269
+ out2.estimated = true;
9237
9270
  const noUsage = !unmetered && inTok === 0 && outTok === 0 && cacheR === 0 && cacheC === 0;
9238
9271
  if (typeof c !== "number" || !Number.isFinite(c) || noUsage) {
9239
9272
  out2.missing.push(iter);
@@ -9359,14 +9392,23 @@ var init_session_route = __esm(() => {
9359
9392
  init_unit_model();
9360
9393
  });
9361
9394
 
9395
+ // ../../loki-ts/src/providers/provider_guard.ts
9396
+ var init_provider_guard = __esm(() => {
9397
+ init_safe_git();
9398
+ });
9399
+
9362
9400
  // ../../loki-ts/src/engine10/session.ts
9363
9401
  var STDERR_TAIL_BYTES, ADVISOR_MARKED_STAGES;
9364
9402
  var init_session = __esm(() => {
9403
+ init_provider_cost();
9404
+ init_model_routes();
9365
9405
  init_cost();
9366
9406
  init_budget();
9367
9407
  init_hooks();
9368
9408
  init_session_route();
9369
9409
  init_safe_git();
9410
+ init_provider_guard();
9411
+ init_budget();
9370
9412
  STDERR_TAIL_BYTES = 64 * 1024;
9371
9413
  ADVISOR_MARKED_STAGES = new Set(["plan", "fix"]);
9372
9414
  });
@@ -11819,6 +11861,55 @@ var init_visual_evidence = __esm(() => {
11819
11861
  PAGE_RE = /^(?:.*\/)?(?:app|pages|src|public)\/.*\.(?:html|jsx|tsx|vue|svelte)$/;
11820
11862
  });
11821
11863
 
11864
+ // ../../loki-ts/src/council/role_agents.ts
11865
+ import { existsSync as existsSync27, readdirSync as readdirSync15, readFileSync as readFileSync33 } from "fs";
11866
+ import { homedir as homedir8 } from "os";
11867
+ import { join as join47 } from "path";
11868
+ function agentDirs(repoDir, env = process.env) {
11869
+ return [env["LOKI_AGENTS_PROJECT_DIR"] || join47(repoDir, ".loki", "agents"), env["LOKI_AGENTS_USER_DIR"] || join47(env["XDG_CONFIG_HOME"] || join47(homedir8(), ".config"), "loki", "agents")];
11870
+ }
11871
+ function parseAgent(file, stem2) {
11872
+ const m = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/.exec(readFileSync33(file, "utf8"));
11873
+ if (!m)
11874
+ return null;
11875
+ const body = m[2].trim();
11876
+ if (!body)
11877
+ return null;
11878
+ const kv = (k) => new RegExp(`^${k}:[ \\t]*["']?([^"'\\r\\n#]*?)["']?[ \\t]*$`, "m").exec(m[1])?.[1]?.trim() ?? "";
11879
+ const role = (kv("role") || stem2).toLowerCase();
11880
+ if (!ROLES.includes(role))
11881
+ return null;
11882
+ return { name: kv("name") || stem2, role, provider: (kv("provider") || "claude").toLowerCase(), model: kv("model"), prompt: body.slice(0, 4000) };
11883
+ }
11884
+ function loadRoleAgent(repoDir, role, env = process.env) {
11885
+ for (const d of agentDirs(repoDir, env)) {
11886
+ if (!existsSync27(d))
11887
+ continue;
11888
+ let names;
11889
+ try {
11890
+ names = readdirSync15(d).filter((n2) => n2.endsWith(".md") && !n2.startsWith(".")).sort();
11891
+ } catch {
11892
+ continue;
11893
+ }
11894
+ const ordered = [...names.filter((n2) => n2 === `${role}.md`), ...names.filter((n2) => n2 !== `${role}.md`)];
11895
+ for (const n2 of ordered) {
11896
+ try {
11897
+ const a = parseAgent(join47(d, n2), n2.slice(0, -3));
11898
+ if (a && a.role === role)
11899
+ return a;
11900
+ } catch {}
11901
+ }
11902
+ }
11903
+ return null;
11904
+ }
11905
+ function loadReviewStageAgent(repoDir, env = process.env) {
11906
+ return loadRoleAgent(repoDir, "reviewer", env) ?? loadRoleAgent(repoDir, "council", env);
11907
+ }
11908
+ var ROLES;
11909
+ var init_role_agents = __esm(() => {
11910
+ ROLES = ["council", "reviewer"];
11911
+ });
11912
+
11822
11913
  // ../../loki-ts/src/util/shell.ts
11823
11914
  async function readStreamCapped(stream, maxBytes = MAX_STDOUT_BYTES) {
11824
11915
  const reader = stream.getReader();
@@ -11891,8 +11982,8 @@ var init_shell = __esm(() => {
11891
11982
  });
11892
11983
 
11893
11984
  // ../../loki-ts/src/engine10/stages/xreview.ts
11894
- import { existsSync as existsSync27, readFileSync as readFileSync33 } from "fs";
11895
- import { join as join47 } from "path";
11985
+ import { existsSync as existsSync28, readFileSync as readFileSync34 } from "fs";
11986
+ import { join as join48 } from "path";
11896
11987
  function minVerdict(verdict, review) {
11897
11988
  const ceiling = review?.level === "block" ? "PARTIAL" : verdict;
11898
11989
  return RANK2[ceiling] < RANK2[verdict] ? ceiling : verdict;
@@ -11905,11 +11996,11 @@ function explicitReview(repoDir, env) {
11905
11996
  if (flag && isOff(ev))
11906
11997
  return "off";
11907
11998
  for (const f of ["loki.yaml", "loki.yml"]) {
11908
- const p = join47(repoDir, f);
11909
- if (!existsSync27(p))
11999
+ const p = join48(repoDir, f);
12000
+ if (!existsSync28(p))
11910
12001
  continue;
11911
12002
  try {
11912
- const m = /^review:[ \t]*["']?([A-Za-z]+)["']?[ \t]*(?:#.*)?$/m.exec(readFileSync33(p, "utf8"));
12003
+ const m = /^review:[ \t]*["']?([A-Za-z]+)["']?[ \t]*(?:#.*)?$/m.exec(readFileSync34(p, "utf8"));
11913
12004
  const v = asProvider(m?.[1]);
11914
12005
  if (v)
11915
12006
  return v;
@@ -11938,9 +12029,15 @@ function reviewReceipt(builder, xr, env = process.env) {
11938
12029
  const differs = !!xr && xr.level !== "not_run" && xr.provider !== builder && (builder === "claude" || builder === "codex");
11939
12030
  return { review: { provider: xr?.provider ?? null, vendor_differs: differs }, notProven: differs ? [] : ["judge shares builder vendor or none configured"] };
11940
12031
  }
11941
- function reviewArgv(p, prompt, env = process.env) {
12032
+ function reviewArgv(p, prompt, env = process.env, model) {
11942
12033
  const cli = reviewCli(p, env);
11943
- return p === "codex" ? [cli, "exec", "--sandbox", "read-only", prompt] : [cli, "-p", prompt, "--allowedTools", "Read,Grep,Glob", "--disallowedTools", "Write,Edit,NotebookEdit,Bash"];
12034
+ return p === "codex" ? [cli, "exec", "--sandbox", "read-only", prompt] : [cli, "-p", prompt, ...model ? ["--model", model] : [], "--allowedTools", "Read,Grep,Glob", "--disallowedTools", "Write,Edit,NotebookEdit,Bash"];
12035
+ }
12036
+ function configuredReviewer(repoDir, env = process.env) {
12037
+ const a = loadReviewStageAgent(repoDir, env);
12038
+ if (!a)
12039
+ return null;
12040
+ return { prompt: a.prompt, model: a.provider === "claude" ? a.model : "", provider: asProvider(a.provider) };
11944
12041
  }
11945
12042
  function parseReview(p, out2) {
11946
12043
  const m = /^\s*VERDICT:\s*(PASS|FLAG|BLOCK)\b/im.exec(out2);
@@ -11953,7 +12050,8 @@ function parseReview(p, out2) {
11953
12050
  return { provider: p, level, notes };
11954
12051
  }
11955
12052
  async function crossReview(ctx, verdict, head) {
11956
- const p = reviewProvider(ctx.repoDir, process.env, ctx.provider);
12053
+ const agent = configuredReviewer(ctx.repoDir);
12054
+ const p = reviewProvider(ctx.repoDir, process.env, ctx.provider) ?? agent?.provider ?? null;
11957
12055
  if (!p)
11958
12056
  return null;
11959
12057
  if (verdict !== "VERIFIED" && verdict !== "ALREADY_SATISFIED")
@@ -11962,7 +12060,8 @@ async function crossReview(ctx, verdict, head) {
11962
12060
  if (!d || d.exitCode !== 0)
11963
12061
  return { provider: p, level: "not_run", notes: [`cross-review not run: diff unavailable for ${p}`] };
11964
12062
  const prompt = [
11965
- "You are an independent code reviewer. Review ONLY the diff below for correctness bugs, spec violations and missing tests. Do not modify files.",
12063
+ agent ? `${agent.prompt}
12064
+ Review ONLY the diff below. Do not modify files.` : "You are an independent code reviewer. Review ONLY the diff below for correctness bugs, spec violations and missing tests. Do not modify files.",
11966
12065
  "Reply with a first line exactly `VERDICT: PASS`, `VERDICT: FLAG` (concerns) or `VERDICT: BLOCK` (a reproduced defect), then one `- reason` line per concern.",
11967
12066
  "",
11968
12067
  "```diff",
@@ -11971,7 +12070,7 @@ async function crossReview(ctx, verdict, head) {
11971
12070
  ].join(`
11972
12071
  `);
11973
12072
  try {
11974
- const r = await run(reviewArgv(p, prompt), { cwd: ctx.repoDir, timeoutMs: TIMEOUT_MS });
12073
+ const r = await run(reviewArgv(p, prompt, process.env, p === "claude" ? agent?.model : undefined), { cwd: ctx.repoDir, timeoutMs: TIMEOUT_MS });
11975
12074
  if (r.exitCode !== 0)
11976
12075
  return { provider: p, level: "not_run", notes: [`cross-review not run: ${p} exited ${r.exitCode}`] };
11977
12076
  return parseReview(p, r.stdout);
@@ -11984,20 +12083,21 @@ var RANK2, TIMEOUT_MS = 300000, MAX_DIFF = 60000, MAX_NOTES = 5, asProvider = (v
11984
12083
  return s === "codex" || s === "claude" ? s : null;
11985
12084
  }, isOff = (v) => /^(off|none|false|0)$/i.test(v?.trim() ?? ""), xvendorDefault = (env = process.env) => /^(1|true|yes|on)$/i.test(env["LOKI_XVENDOR_DEFAULT"]?.trim() ?? ""), reviewCli = (p, env = process.env) => env[`LOKI_${p.toUpperCase()}_CLI`]?.trim() || p, clean3 = (s) => s.replace(/[\x00-\x1f\x7f`]+/g, " ").trim().slice(0, 300);
11986
12085
  var init_xreview = __esm(() => {
12086
+ init_role_agents();
11987
12087
  init_shell();
11988
12088
  init_safe_git();
11989
12089
  RANK2 = { FAILED: 0, SPEC_CONFLICT: 0, PARTIAL: 1, ALREADY_SATISFIED: 2, VERIFIED: 2 };
11990
12090
  });
11991
12091
 
11992
12092
  // ../../loki-ts/src/runner/router/route_record.ts
11993
- import { existsSync as existsSync28, lstatSync as lstatSync11, mkdirSync as mkdirSync15, readFileSync as readFileSync34, writeFileSync as writeFileSync16 } from "fs";
11994
- import { join as join48 } from "path";
12093
+ import { existsSync as existsSync29, lstatSync as lstatSync11, mkdirSync as mkdirSync15, readFileSync as readFileSync35, writeFileSync as writeFileSync16 } from "fs";
12094
+ import { join as join49 } from "path";
11995
12095
  function loadRouteRecord(runDir) {
11996
12096
  try {
11997
- const p = join48(runDir, ROUTE_RECORD_FILE);
11998
- if (!existsSync28(p) || !lstatSync11(p).isFile())
12097
+ const p = join49(runDir, ROUTE_RECORD_FILE);
12098
+ if (!existsSync29(p) || !lstatSync11(p).isFile())
11999
12099
  return;
12000
- const v = JSON.parse(readFileSync34(p, "utf8"));
12100
+ const v = JSON.parse(readFileSync35(p, "utf8"));
12001
12101
  return typeof v === "object" && v !== null && !Array.isArray(v) ? v : undefined;
12002
12102
  } catch {
12003
12103
  return;
@@ -12007,7 +12107,7 @@ var ROUTE_RECORD_FILE = "route.json";
12007
12107
  var init_route_record = () => {};
12008
12108
 
12009
12109
  // ../../loki-ts/src/runner/router/history_store.ts
12010
- import { existsSync as existsSync29, mkdirSync as mkdirSync16, readFileSync as readFileSync35, renameSync as renameSync6, rmSync as rmSync12, writeFileSync as writeFileSync17 } from "fs";
12110
+ import { existsSync as existsSync30, mkdirSync as mkdirSync16, readFileSync as readFileSync36, renameSync as renameSync6, rmSync as rmSync12, writeFileSync as writeFileSync17 } from "fs";
12011
12111
  import { resolve as resolve11 } from "path";
12012
12112
  function validRun(v) {
12013
12113
  if (typeof v !== "object" || v === null)
@@ -12020,10 +12120,10 @@ function historyPath(key, cacheRoot) {
12020
12120
  }
12021
12121
  function readRunHistory(key, cacheRoot) {
12022
12122
  const path = historyPath(key, cacheRoot);
12023
- if (!existsSync29(path))
12123
+ if (!existsSync30(path))
12024
12124
  return [];
12025
12125
  try {
12026
- const parsed = JSON.parse(readFileSync35(path, "utf8"));
12126
+ const parsed = JSON.parse(readFileSync36(path, "utf8"));
12027
12127
  const runs2 = parsed?.runs;
12028
12128
  return Array.isArray(runs2) ? runs2.filter(validRun) : [];
12029
12129
  } catch {
@@ -12057,7 +12157,7 @@ var init_history_store = __esm(() => {
12057
12157
  });
12058
12158
 
12059
12159
  // ../../loki-ts/src/runner/router/history.ts
12060
- import { dirname as dirname21, join as join49, resolve as resolve12 } from "path";
12160
+ import { dirname as dirname21, join as join50, resolve as resolve12 } from "path";
12061
12161
  import { fileURLToPath as fileURLToPath3 } from "url";
12062
12162
  function shapeKey(model) {
12063
12163
  if (!model || model.status !== "ok")
@@ -12135,8 +12235,8 @@ var init_cost_preview = __esm(() => {
12135
12235
  });
12136
12236
 
12137
12237
  // ../../loki-ts/src/features/contract.ts
12138
- import { existsSync as existsSync30, lstatSync as lstatSync12, mkdirSync as mkdirSync17, readFileSync as readFileSync36, writeFileSync as writeFileSync18 } from "fs";
12139
- import { dirname as dirname22, join as join50 } from "path";
12238
+ import { existsSync as existsSync31, lstatSync as lstatSync12, mkdirSync as mkdirSync17, readFileSync as readFileSync37, writeFileSync as writeFileSync18 } from "fs";
12239
+ import { dirname as dirname22, join as join51 } from "path";
12140
12240
  import { createHash as createHash11 } from "crypto";
12141
12241
  function contractEnabled(env = process.env) {
12142
12242
  return env["LOKI_CONTRACT"] !== "0";
@@ -12168,7 +12268,7 @@ function untracedLines(trace) {
12168
12268
  return trace.criteria.filter((c) => c.status === "no_match").map((c) => `contract ${sanitizeCriterion(c.id)} untraced (no keyword match in changed files): ${sanitizeCriterion(c.text)}`);
12169
12269
  }
12170
12270
  function contractPath(repoDir) {
12171
- return join50(repoDir, ".loki", "contract.json");
12271
+ return join51(repoDir, ".loki", "contract.json");
12172
12272
  }
12173
12273
  function readContract(repoDir) {
12174
12274
  const p = contractPath(repoDir);
@@ -12184,7 +12284,7 @@ function readContract(repoDir) {
12184
12284
  return { contract: null, notes: ["contract unreadable: too large"], sha256: null };
12185
12285
  let raw2;
12186
12286
  try {
12187
- raw2 = readFileSync36(p);
12287
+ raw2 = readFileSync37(p);
12188
12288
  } catch {
12189
12289
  return { contract: null, notes: ["contract unreadable: read failed"], sha256: null };
12190
12290
  }
@@ -12253,8 +12353,8 @@ var init_contract = __esm(() => {
12253
12353
  });
12254
12354
 
12255
12355
  // ../../loki-ts/src/supply/supply_guard.ts
12256
- import { existsSync as existsSync31, readFileSync as readFileSync37, statSync as statSync16 } from "fs";
12257
- import { basename as basename17, join as join51 } from "path";
12356
+ import { existsSync as existsSync32, readFileSync as readFileSync38, statSync as statSync16 } from "fs";
12357
+ import { basename as basename17, join as join52 } from "path";
12258
12358
  function supplyEnabled(env) {
12259
12359
  return env["LOKI_SUPPLY_GUARD"] !== "0";
12260
12360
  }
@@ -12269,13 +12369,13 @@ function isManifestPath(path) {
12269
12369
  return MANIFESTS.test(basename17(path));
12270
12370
  }
12271
12371
  function readDeclared(repoDir) {
12272
- const p = join51(repoDir, ".loki", DECLARED_FILE);
12273
- if (!existsSync31(p))
12372
+ const p = join52(repoDir, ".loki", DECLARED_FILE);
12373
+ if (!existsSync32(p))
12274
12374
  return { deps: [], problem: null };
12275
12375
  try {
12276
12376
  if (statSync16(p).size > MAX_FILE_BYTES)
12277
12377
  return { deps: [], problem: "declaration file too large" };
12278
- const j = JSON.parse(readFileSync37(p, "utf8"));
12378
+ const j = JSON.parse(readFileSync38(p, "utf8"));
12279
12379
  if (!Array.isArray(j))
12280
12380
  return { deps: [], problem: "declaration is not a JSON array" };
12281
12381
  const deps = [];
@@ -12295,11 +12395,11 @@ function readDeclared(repoDir) {
12295
12395
  }
12296
12396
  }
12297
12397
  function readAllowlist(repoDir) {
12298
- const p = join51(repoDir, ".loki", "supply-allowlist");
12299
- if (!existsSync31(p))
12398
+ const p = join52(repoDir, ".loki", "supply-allowlist");
12399
+ if (!existsSync32(p))
12300
12400
  return new Set;
12301
12401
  try {
12302
- return new Set(readFileSync37(p, "utf8").split(`
12402
+ return new Set(readFileSync38(p, "utf8").split(`
12303
12403
  `).map((l) => l.replace(/#.*$/, "").trim()).filter(Boolean));
12304
12404
  } catch {
12305
12405
  return new Set;
@@ -12340,7 +12440,7 @@ function userConfigPrivate(eco, repoDir, env, goEnv = {}, name = "") {
12340
12440
  if (eco === "npm") {
12341
12441
  if (privateUrlList([env["npm_config_registry"] ?? "", env["NPM_CONFIG_REGISTRY"] ?? ""]))
12342
12442
  return true;
12343
- return files(join51(repoDir, ".npmrc"), home ? join51(home, ".npmrc") : "").some((t) => t.split(`
12443
+ return files(join52(repoDir, ".npmrc"), home ? join52(home, ".npmrc") : "").some((t) => t.split(`
12344
12444
  `).some((l) => {
12345
12445
  const m = /^\s*(?:(@[^:\s]+):)?registry\s*=\s*(\S+)/.exec(l);
12346
12446
  return !!m && privateUrl(m[2]) && (m[1] === undefined || name.startsWith(`${m[1]}/`));
@@ -12349,7 +12449,7 @@ function userConfigPrivate(eco, repoDir, env, goEnv = {}, name = "") {
12349
12449
  if (eco === "pypi") {
12350
12450
  if (privateUrlList([env["PIP_INDEX_URL"] ?? "", env["PIP_EXTRA_INDEX_URL"] ?? ""]))
12351
12451
  return true;
12352
- const cfg = [env["PIP_CONFIG_FILE"] ?? "", home ? join51(home, ".config", "pip", "pip.conf") : "", home ? join51(home, ".pip", "pip.conf") : "", join51(repoDir, "pip.conf")];
12452
+ const cfg = [env["PIP_CONFIG_FILE"] ?? "", home ? join52(home, ".config", "pip", "pip.conf") : "", home ? join52(home, ".pip", "pip.conf") : "", join52(repoDir, "pip.conf")];
12353
12453
  return files(...cfg).some((t) => t.split(`
12354
12454
  `).some((l) => {
12355
12455
  const m = /^\s*(?:extra-)?index-url\s*[=:]\s*(.+)$/.exec(l);
@@ -12359,7 +12459,7 @@ function userConfigPrivate(eco, repoDir, env, goEnv = {}, name = "") {
12359
12459
  if (eco === "cargo") {
12360
12460
  if (Object.keys(env).some((k) => /^CARGO_REGISTRIES_.+_INDEX$/.test(k) || /^CARGO_SOURCE_/.test(k)))
12361
12461
  return true;
12362
- return files(join51(repoDir, ".cargo", "config.toml"), join51(repoDir, ".cargo", "config"), home ? join51(home, ".cargo", "config.toml") : "", home ? join51(home, ".cargo", "config") : "").some((t) => /^\s*\[(registries\.|source\.)/m.test(t) || /replace-with\s*=/.test(t));
12462
+ return files(join52(repoDir, ".cargo", "config.toml"), join52(repoDir, ".cargo", "config"), home ? join52(home, ".cargo", "config.toml") : "", home ? join52(home, ".cargo", "config") : "").some((t) => /^\s*\[(registries\.|source\.)/m.test(t) || /replace-with\s*=/.test(t));
12363
12463
  }
12364
12464
  if (eco === "go") {
12365
12465
  const gp = goEnv["GOPROXY"] ?? env["GOPROXY"] ?? "";
@@ -12482,7 +12582,7 @@ var MIN_AGE_DAYS = 7, DECLARED_FILE = "supply-declared.json", MAX_CHECKED = 50,
12482
12582
  }
12483
12583
  }, PUBLIC_HOSTS, hostOf = (u) => u.trim().toLowerCase().replace(/^[a-z][a-z0-9+.-]*:\/\//, "").replace(/^www\./, "").split(/[/?#\s]/)[0].replace(/^[^@]*@/, "").replace(/:\d+$/, ""), privateUrl = (u) => /[a-z0-9]/i.test(u) && !PUBLIC_HOSTS.has(hostOf(u)), readSmall = (p) => {
12484
12584
  try {
12485
- return statSync16(p).size <= MAX_FILE_BYTES ? readFileSync37(p, "utf8") : "";
12585
+ return statSync16(p).size <= MAX_FILE_BYTES ? readFileSync38(p, "utf8") : "";
12486
12586
  } catch {
12487
12587
  return "";
12488
12588
  }
@@ -12498,9 +12598,9 @@ var init_supply_guard = __esm(() => {
12498
12598
 
12499
12599
  // ../../loki-ts/src/engine10/stages/seal.ts
12500
12600
  import { createHash as createHash12, randomBytes as randomBytes4, createPrivateKey, createPublicKey as createPublicKey2, generateKeyPairSync, sign } from "crypto";
12501
- import { chmodSync as chmodSync5, existsSync as existsSync32, readdirSync as readdirSync15, linkSync, mkdirSync as mkdirSync18, readFileSync as readFileSync38, statSync as statSync17, unlinkSync as unlinkSync5, writeFileSync as writeFileSync19 } from "fs";
12502
- import { homedir as homedir8 } from "os";
12503
- import { basename as basename18, dirname as dirname23, isAbsolute as isAbsolute8, join as join52, relative as relative9 } from "path";
12601
+ import { chmodSync as chmodSync5, existsSync as existsSync33, readdirSync as readdirSync16, linkSync, mkdirSync as mkdirSync18, readFileSync as readFileSync39, statSync as statSync17, unlinkSync as unlinkSync5, writeFileSync as writeFileSync19 } from "fs";
12602
+ import { homedir as homedir9 } from "os";
12603
+ import { basename as basename18, dirname as dirname23, isAbsolute as isAbsolute8, join as join53, relative as relative9 } from "path";
12504
12604
  function sanitizeReason(s) {
12505
12605
  const collapsed = s.replace(/[\x00-\x1f\x7f]+/g, " ").trim();
12506
12606
  const capped = collapsed.length > 500 ? `${collapsed.slice(0, 500)}...` : collapsed;
@@ -12528,9 +12628,9 @@ function loadSigningKey(generate = true) {
12528
12628
  let pem = inline ?? "";
12529
12629
  if (!inline) {
12530
12630
  const given = process.env["LOKI_RECEIPT_SIGNING_KEY_FILE"]?.trim();
12531
- const file = given || join52(process.env["HOME"] || homedir8(), ".loki", "keys", RECEIPT_SIGNER_BASENAME);
12631
+ const file = given || join53(process.env["HOME"] || homedir9(), ".loki", "keys", RECEIPT_SIGNER_BASENAME);
12532
12632
  try {
12533
- pem = readFileSync38(file);
12633
+ pem = readFileSync39(file);
12534
12634
  if (!given) {
12535
12635
  for (const [f, m] of [[file, 384], [dirname23(file), 448]])
12536
12636
  if (statSync17(f).mode & 63)
@@ -12551,7 +12651,7 @@ function loadSigningKey(generate = true) {
12551
12651
  } finally {
12552
12652
  unlinkSync5(tmp);
12553
12653
  }
12554
- pem = readFileSync38(file);
12654
+ pem = readFileSync39(file);
12555
12655
  }
12556
12656
  }
12557
12657
  const k = createPrivateKey(pem);
@@ -12633,7 +12733,7 @@ function renderReceiptMd(r) {
12633
12733
  `);
12634
12734
  }
12635
12735
  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) => createHash12("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 !== "") : [], b64u = (b) => Buffer.from(b).toString("base64url"), kidOf2 = (pub) => b64u(createHash12("sha256").update(`{"crv":"Ed25519","kty":"OKP","x":"${pub.export({ format: "jwk" }).x}"}`).digest()), receiptRel = (ctx) => {
12636
- const r = relative9(ctx.repoDir, join52(ctx.runDir, "receipt.json"));
12736
+ const r = relative9(ctx.repoDir, join53(ctx.runDir, "receipt.json"));
12637
12737
  return r.startsWith("..") || isAbsolute8(r) ? `.loki/runs/${ctx.runId}/receipt.json` : r;
12638
12738
  }, commitStage, sealStage;
12639
12739
  var init_seal = __esm(() => {
@@ -12736,7 +12836,7 @@ var init_seal = __esm(() => {
12736
12836
  if (wallNotRun > 0) {
12737
12837
  try {
12738
12838
  const kept = new Set((Array.isArray(o.wall?.files) ? o.wall.files : []).map((f) => basename18(String(f.path))));
12739
- for (const n2 of readdirSync15(join52(ctx.runDir, "wall")).sort())
12839
+ for (const n2 of readdirSync16(join53(ctx.runDir, "wall")).sort())
12740
12840
  if (!kept.has(n2))
12741
12841
  notProven.add(`wall test discarded: ${n2} (not_run)`);
12742
12842
  } catch {}
@@ -12795,19 +12895,19 @@ var init_seal = __esm(() => {
12795
12895
  for (const [s, d] of Object.entries(o))
12796
12896
  if (typeof d?.duration_s === "number")
12797
12897
  stages[s] = d.duration_s;
12798
- const T = hooks.time, time = T ? T.build(ctx, stages, T.firstEventMs(join52(ctx.runDir, "events.jsonl"))) : { wall_s: Object.values(stages).reduce((a, b) => a + (b ?? 0), 0), stages }, totalS = T?.reconciled(time) ?? null;
12898
+ const T = hooks.time, time = T ? T.build(ctx, stages, T.firstEventMs(join53(ctx.runDir, "events.jsonl"))) : { wall_s: Object.values(stages).reduce((a, b) => a + (b ?? 0), 0), stages }, totalS = T?.reconciled(time) ?? null;
12799
12899
  const iterIds = Object.values(o).flatMap((d) => [...strs2(d?.iteration_ids), ...strs2([d?.iteration_id])]);
12800
12900
  if (iterIds.length === 0)
12801
12901
  notProven.add("cost not measured (no iteration ids recorded)");
12802
12902
  const cost = ctx.cost.read(ctx.repoDir, iterIds);
12803
12903
  if (cost.unmetered)
12804
12904
  notProven.add("cost unmetered (CLI invoker; recorded as 0)");
12805
- const eventsPath = join52(ctx.runDir, "events.jsonl");
12905
+ const eventsPath = join53(ctx.runDir, "events.jsonl");
12806
12906
  const wallFiles = Array.isArray(o.wall?.files) ? o.wall.files : [];
12807
12907
  const wallPassed = typeof o.verify?.wall_passed === "boolean" ? o.verify.wall_passed : null;
12808
12908
  if (wallFiles.length > 0 && wallPassed === null)
12809
12909
  notProven.add("wall result not recorded by verify");
12810
- const routeBlock = buildRouteBlock(process.env, ctx.provider, o.implement?.route ?? o.plan?.route_record ?? (routerEnabled(process.env) ? loadRouteRecord(ctx.runDir) : undefined), cost.router ?? (routerEnabled(process.env) ? sumResultCosts(join52(ctx.repoDir, ".loki"), iterIds).router : undefined), ctx.model);
12910
+ const routeBlock = buildRouteBlock(process.env, ctx.provider, o.implement?.route ?? o.plan?.route_record ?? (routerEnabled(process.env) ? loadRouteRecord(ctx.runDir) : undefined), cost.router ?? (routerEnabled(process.env) ? sumResultCosts(join53(ctx.repoDir, ".loki"), iterIds).router : undefined), ctx.model);
12811
12911
  if (routeBlock)
12812
12912
  for (const l of routeNotProven(routeBlock))
12813
12913
  notProven.add(l);
@@ -12849,7 +12949,7 @@ var init_seal = __esm(() => {
12849
12949
  provider: ctx.provider,
12850
12950
  model: ctx.model,
12851
12951
  resumed: o.intake?.resumed === true,
12852
- events_sha256: sha2562(existsSync32(eventsPath) ? readFileSync38(eventsPath) : ""),
12952
+ events_sha256: sha2562(existsSync33(eventsPath) ? readFileSync39(eventsPath) : ""),
12853
12953
  ...await sealEvidence(ctx.repoDir, ctx.runDir, o, notProven, signal, ctx.emit),
12854
12954
  log_seal: true,
12855
12955
  ...receiptBlock(process.env, cost.usd, cost.unmetered === true, totalS),
@@ -12870,10 +12970,10 @@ var init_seal = __esm(() => {
12870
12970
  }
12871
12971
  const receipt = { ...body, receipt_sha256: hash, verification: { jwt: sig.jwt, kid: sig.kid } };
12872
12972
  mkdirSync18(ctx.runDir, { recursive: true });
12873
- const path = join52(ctx.runDir, "receipt.json");
12973
+ const path = join53(ctx.runDir, "receipt.json");
12874
12974
  writeFileSync19(path, JSON.stringify(receipt, null, 2) + `
12875
12975
  `);
12876
- writeFileSync19(join52(ctx.runDir, "receipt.md"), renderReceiptMd(receipt));
12976
+ writeFileSync19(join53(ctx.runDir, "receipt.md"), renderReceiptMd(receipt));
12877
12977
  recordRun(process.env, ctx.repoDir, ctx.model, verdict, cost.usd, cost.unmetered === true, totalS);
12878
12978
  const signed = sig.jwt !== null;
12879
12979
  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) } : {} };
@@ -12888,7 +12988,7 @@ var init_seal = __esm(() => {
12888
12988
 
12889
12989
  // ../../loki-ts/src/features/receipt_dsse.ts
12890
12990
  import { createPublicKey as createPublicKey3, sign as sign2, verify as verify2 } from "crypto";
12891
- import { readFileSync as readFileSync39 } from "fs";
12991
+ import { readFileSync as readFileSync40 } from "fs";
12892
12992
  function pae(payloadType, body) {
12893
12993
  const t = Buffer.from(payloadType, "utf8");
12894
12994
  return Buffer.concat([Buffer.from(`DSSEv1 ${t.length} `), t, Buffer.from(` ${body.length} `), body]);
@@ -12965,7 +13065,7 @@ function verifyDsseReceipt(env, d) {
12965
13065
  }
12966
13066
  function runIdGuard(receiptPath, runId) {
12967
13067
  try {
12968
- return envelopeRunIdProblem(JSON.parse(readFileSync39(receiptPath, "utf8")), runId);
13068
+ return envelopeRunIdProblem(JSON.parse(readFileSync40(receiptPath, "utf8")), runId);
12969
13069
  } catch {
12970
13070
  return null;
12971
13071
  }
@@ -12979,8 +13079,8 @@ var init_keys_cmd = __esm(() => {
12979
13079
  });
12980
13080
 
12981
13081
  // ../../loki-ts/src/engine10/verify_cmd.ts
12982
- import { existsSync as existsSync33, readdirSync as readdirSync16, readFileSync as readFileSync40, statSync as statSync18 } from "fs";
12983
- import { dirname as dirname24, join as join53 } from "path";
13082
+ import { existsSync as existsSync34, readdirSync as readdirSync17, readFileSync as readFileSync41, statSync as statSync18 } from "fs";
13083
+ import { dirname as dirname24, join as join54 } from "path";
12984
13084
  import { createHash as createHash13, createPublicKey as createPublicKey4, verify as verify3 } from "crypto";
12985
13085
  function pubFor(kid, given) {
12986
13086
  if (given)
@@ -12988,7 +13088,7 @@ function pubFor(kid, given) {
12988
13088
  const active = loadSigningKey(false);
12989
13089
  const pubs = (process.env["LOKI_RECEIPT_RETIRED_PUBKEYS"] ?? "").split(":").map((f) => f.trim()).filter(Boolean).flatMap((f) => {
12990
13090
  try {
12991
- return [createPublicKey4(readFileSync40(f))];
13091
+ return [createPublicKey4(readFileSync41(f))];
12992
13092
  } catch {
12993
13093
  return [];
12994
13094
  }
@@ -13020,10 +13120,10 @@ function checkEventLog(receiptPath, receipt) {
13020
13120
  const bound = receipt["events_sha256"];
13021
13121
  if (typeof bound !== "string" || bound === createHash13("sha256").digest("hex"))
13022
13122
  return null;
13023
- const path = join53(dirname24(receiptPath), "events.jsonl");
13024
- if (!existsSync33(path))
13123
+ const path = join54(dirname24(receiptPath), "events.jsonl");
13124
+ if (!existsSync34(path))
13025
13125
  return "events.jsonl is missing";
13026
- const raw2 = readFileSync40(path, "utf8"), events2 = readEvents(path), lines = raw2.split(`
13126
+ const raw2 = readFileSync41(path, "utf8"), events2 = readEvents(path), lines = raw2.split(`
13027
13127
  `).filter((l) => l.trim() !== "");
13028
13128
  if (events2.length !== lines.length || events2.some((e, i) => e.seq !== i))
13029
13129
  return "events.jsonl has a forged, edited or missing line";
@@ -13039,7 +13139,7 @@ function checkEventLog(receiptPath, receipt) {
13039
13139
  return h.digest("hex") === bound ? null : "events.jsonl does not match the hash recorded at seal";
13040
13140
  }
13041
13141
  function checkLogSeal(receiptPath, hash, kid, given) {
13042
- const path = join53(dirname24(receiptPath), "events.jsonl"), events2 = readEvents(path), done = events2.findIndex((e) => e.type === "run.completed");
13142
+ const path = join54(dirname24(receiptPath), "events.jsonl"), events2 = readEvents(path), done = events2.findIndex((e) => e.type === "run.completed");
13043
13143
  if (!events2.some((e) => e.type === "receipt.sealed" && e.data["receipt_sha256"] === hash))
13044
13144
  return { verdict: "TAMPERED", reason: "events.jsonl has no receipt.sealed event for this receipt" };
13045
13145
  if (done < 0)
@@ -13047,7 +13147,7 @@ function checkLogSeal(receiptPath, hash, kid, given) {
13047
13147
  const seal = events2[done + 1], d = seal?.type === "log.sealed" ? seal.data : null;
13048
13148
  if (!d)
13049
13149
  return { verdict: "TAMPERED", reason: "events.jsonl has no signed log.sealed line after run.completed" };
13050
- const lines = readFileSync40(path, "utf8").split(`
13150
+ const lines = readFileSync41(path, "utf8").split(`
13051
13151
  `).filter((l) => l.trim() !== ""), sha3 = createHash13("sha256").update(lines.slice(0, done + 1).join(`
13052
13152
  `) + `
13053
13153
  `).digest("hex");
@@ -13057,11 +13157,11 @@ function checkLogSeal(receiptPath, hash, kid, given) {
13057
13157
  return null;
13058
13158
  }
13059
13159
  async function verifyReceipt(receiptPath, deps = {}) {
13060
- if (!existsSync33(receiptPath))
13160
+ if (!existsSync34(receiptPath))
13061
13161
  return { verdict: "UNCHECKED", reasons: [`receipt not found: ${receiptPath}`] };
13062
13162
  let receipt;
13063
13163
  try {
13064
- receipt = JSON.parse((deps.read ?? ((p) => readFileSync40(p, "utf8")))(receiptPath));
13164
+ receipt = JSON.parse((deps.read ?? ((p) => readFileSync41(p, "utf8")))(receiptPath));
13065
13165
  } catch {
13066
13166
  return { verdict: "UNCHECKED", reasons: ["receipt.json is not valid JSON"] };
13067
13167
  }
@@ -13116,8 +13216,8 @@ var init_verify_cmd = __esm(() => {
13116
13216
  });
13117
13217
 
13118
13218
  // src/server/app.ts
13119
- import { existsSync as existsSync35, readFileSync as readFileSync43, statSync as statSync19 } from "fs";
13120
- import { join as join56, resolve as resolve13, sep as sep14 } from "path";
13219
+ import { existsSync as existsSync36, readFileSync as readFileSync44, statSync as statSync19 } from "fs";
13220
+ import { join as join57, resolve as resolve13, sep as sep14 } from "path";
13121
13221
 
13122
13222
  // node_modules/hono/dist/request/constants.js
13123
13223
  var GET_MATCH_RESULT = Symbol();
@@ -23364,9 +23464,9 @@ function mount20(ctx) {
23364
23464
  }
23365
23465
 
23366
23466
  // src/server/routes/verify.ts
23367
- import { readFileSync as readFileSync41, realpathSync as realpathSync20 } from "fs";
23467
+ import { readFileSync as readFileSync42, realpathSync as realpathSync20 } from "fs";
23368
23468
  import { createPublicKey as createPublicKey5 } from "crypto";
23369
- import { join as join54, sep as sep13 } from "path";
23469
+ import { join as join55, sep as sep13 } from "path";
23370
23470
  init_verify_cmd();
23371
23471
  init_seal();
23372
23472
  init_receipt_dsse();
@@ -23393,11 +23493,11 @@ function mount21(ctx) {
23393
23493
  return done(404, "receipt not available", "not_found");
23394
23494
  let runDir, file;
23395
23495
  try {
23396
- const root = realpathSync20(join54(repo.p, ".loki", "runs"));
23397
- runDir = realpathSync20(join54(root, run2));
23398
- if (runDir !== join54(root, run2))
23496
+ const root = realpathSync20(join55(repo.p, ".loki", "runs"));
23497
+ runDir = realpathSync20(join55(root, run2));
23498
+ if (runDir !== join55(root, run2))
23399
23499
  return done(404, "receipt not found", "not_found");
23400
- file = realpathSync20(join54(runDir, "receipt.json"));
23500
+ file = realpathSync20(join55(runDir, "receipt.json"));
23401
23501
  } catch {
23402
23502
  return done(404, "receipt not found", "not_found");
23403
23503
  }
@@ -23416,7 +23516,7 @@ function mount21(ctx) {
23416
23516
  sha3 = r.receiptSha256 ?? null;
23417
23517
  }
23418
23518
  try {
23419
- outcome = outcomeOf(JSON.parse(readFileSync41(file, "utf8")));
23519
+ outcome = outcomeOf(JSON.parse(readFileSync42(file, "utf8")));
23420
23520
  } catch {
23421
23521
  outcome = "UNREADABLE";
23422
23522
  }
@@ -23478,8 +23578,8 @@ function ttlCache(ttlMs = PROBE_CACHE_MS, now2 = Date.now) {
23478
23578
  }
23479
23579
 
23480
23580
  // src/server/legacy/shim.ts
23481
- import { existsSync as existsSync34, readFileSync as readFileSync42 } from "fs";
23482
- import { join as join55 } from "path";
23581
+ import { existsSync as existsSync35, readFileSync as readFileSync43 } from "fs";
23582
+ import { join as join56 } from "path";
23483
23583
 
23484
23584
  // src/server/legacy/routes.ts
23485
23585
  var LEGACY_ROUTES = [
@@ -23851,10 +23951,10 @@ var logged = new Set;
23851
23951
  var toHono = (p) => p.replace(/\{([A-Za-z_]+):path\}/g, ":$1{.+}").replace(/\{([A-Za-z_]+)\}/g, ":$1");
23852
23952
  var NOT_SUPPORTED = "not yet supported by the Control Plane";
23853
23953
  var repoVersion = () => {
23854
- for (const f of [join55(import.meta.dir, "../../../../../VERSION")]) {
23954
+ for (const f of [join56(import.meta.dir, "../../../../../VERSION")]) {
23855
23955
  try {
23856
- if (existsSync34(f))
23857
- return readFileSync42(f, "utf8").trim();
23956
+ if (existsSync35(f))
23957
+ return readFileSync43(f, "utf8").trim();
23858
23958
  } catch {}
23859
23959
  }
23860
23960
  return "unknown";
@@ -24036,7 +24136,7 @@ function legacyShim(opts) {
24036
24136
 
24037
24137
  // src/server/app.ts
24038
24138
  var MAX_BODY2 = 1e6;
24039
- var defaultUiDir = () => [join56(import.meta.dir, "../../ui/dist"), join56(import.meta.dir, "../ui/dist")].find((d) => existsSync35(join56(d, "index.html")));
24139
+ var defaultUiDir = () => [join57(import.meta.dir, "../../ui/dist"), join57(import.meta.dir, "../ui/dist")].find((d) => existsSync36(join57(d, "index.html")));
24040
24140
  function createApp(opts) {
24041
24141
  const uiDir = opts.uiDir ?? defaultUiDir();
24042
24142
  const { db, sqlite } = openDb(opts.dbPath);
@@ -24065,10 +24165,10 @@ function createApp(opts) {
24065
24165
  await next();
24066
24166
  });
24067
24167
  const readInstalled = () => {
24068
- for (const f of [process.env.LOKI_VERSION_FILE, join56(import.meta.dir, "../../../../VERSION")]) {
24168
+ for (const f of [process.env.LOKI_VERSION_FILE, join57(import.meta.dir, "../../../../VERSION")]) {
24069
24169
  try {
24070
24170
  if (f)
24071
- return readFileSync43(f, "utf8").trim() || "unknown";
24171
+ return readFileSync44(f, "utf8").trim() || "unknown";
24072
24172
  } catch {}
24073
24173
  }
24074
24174
  return "unknown";
@@ -24179,11 +24279,11 @@ function createApp(opts) {
24179
24279
  return c.json({ error: "not found" }, 404);
24180
24280
  const root = resolve13(uiDir);
24181
24281
  const f = resolve13(root, `.${p}`);
24182
- if (f.startsWith(root + sep14) && existsSync35(f) && statSync19(f).isFile())
24282
+ if (f.startsWith(root + sep14) && existsSync36(f) && statSync19(f).isFile())
24183
24283
  return new Response(Bun.file(f));
24184
24284
  if (/\.[a-z0-9]+$/i.test(p))
24185
24285
  return c.json({ error: "not found" }, 404);
24186
- return new Response(Bun.file(join56(root, "index.html")));
24286
+ return new Response(Bun.file(join57(root, "index.html")));
24187
24287
  });
24188
24288
  return { app, db, close: () => {
24189
24289
  clearInterval(reconciler);
@@ -2,7 +2,7 @@
2
2
  "$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
3
3
  "name": "loki-mode",
4
4
  "displayName": "Loki Mode",
5
- "version": "11.3.9",
5
+ "version": "11.4.2",
6
6
  "description": "Autonomous spec-to-product build system with a built-in trust layer (RARV-C closure loop, 8 quality gates, completion council). Ships Loki's spec-hardening, drift-detection, and deterministic PR verification commands plus the Loki MCP server.",
7
7
  "author": {
8
8
  "name": "Autonomi",