loki-mode 11.4.2 → 11.6.0
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.
- package/SKILL.md +2 -2
- package/VERSION +1 -1
- package/autonomy/lib/chat.sh +6 -3
- package/autonomy/lib/engine10-push.sh +19 -1
- package/autonomy/lib/help-core.sh +1 -1
- package/autonomy/lib/proof-generator.py +23 -5
- package/autonomy/lib/proof-verify.py +607 -5
- package/autonomy/lib/run-summary.sh +17 -1
- package/autonomy/lib/setup-requirements.json +35 -0
- package/autonomy/loki +244 -237
- package/autonomy/notification-checker.py +1 -1
- package/autonomy/provider-offer.sh +95 -47
- package/autonomy/route.sh +38 -6
- package/autonomy/run.sh +42 -14
- package/autonomy/setup-wizard.sh +19 -10
- package/autonomy/verify.sh +40 -1
- package/completions/_loki +3 -1
- package/completions/loki.bash +9 -3
- package/dashboard/__init__.py +3 -10
- package/dashboard/control.py +5 -435
- package/docs/ARCHITECTURE.md +1 -2
- package/docs/CLI-CORE-VERBS.md +2 -6
- package/docs/CLI-REFERENCE.md +3 -1
- package/docs/COMPONENTS.md +0 -2
- package/docs/EVALUATING.md +27 -0
- package/docs/HARNESS-INTELLIGENCE-PLAN.md +20 -0
- package/docs/INSTALLATION.md +1 -1
- package/docs/KNOWN-ISSUES.md +10 -1
- package/docs/PROOF-PASSPORT.md +114 -0
- package/docs/alternative-installations.md +103 -0
- package/docs/authentication.md +19 -1
- package/docs/cost-controls.md +139 -0
- package/docs/dev/release-checklist.md +9 -8
- package/docs/enterprise/security.md +3 -234
- package/docs/environment-variables.md +36 -0
- package/docs/exit-codes.md +102 -2
- package/docs/spec-first-intent.md +29 -0
- package/docs/v10/ARCHITECT-CUTS.md +304 -0
- package/docs/v10/CLI-MODERN.md +2 -2
- package/docs/v10/DECISIONS.md +5 -5
- package/docs/v10/ENGINE.md +735 -267
- package/docs/v10/GUARDS.md +16 -14
- package/docs/v10/GUIDE.md +5 -7
- package/docs/v10/PROGRESS.md +12 -1
- package/docs/v11/MARKET-SLICES.md +1 -5
- package/integrations/openclaw/README.md +20 -0
- package/integrations/vibe-kanban.md +12 -50
- package/loki-ts/data/model-pricing.json +1 -0
- package/loki-ts/dist/loki.js +1156 -1103
- package/mcp/__init__.py +1 -1
- package/mcp/_sdk_loader.py +1 -1
- package/package.json +2 -2
- package/packages/control-plane/dist/server.js +2063 -1032
- package/plugins/loki-mode/.claude-plugin/plugin.json +1 -1
- package/plugins/loki-mode/README.md +41 -1
- package/plugins/loki-mode/commands/handoff.md +12 -0
- package/plugins/loki-mode/commands/loki-verify.md +9 -2
- package/plugins/loki-mode/commands/start.md +20 -0
- package/plugins/loki-mode/hooks/hooks.json +11 -0
- package/plugins/loki-mode/scripts/loki-stop-verify.sh +84 -0
- package/references/quality-control.md +1 -116
- package/skills/00-index.md +2 -2
- package/skills/artifacts.md +0 -22
- package/skills/production.md +2 -38
- package/web-app/dist/assets/{AdminPage-CYJ2BWRC.js → AdminPage-DhXdJNQk.js} +1 -1
- package/web-app/dist/assets/{Avatar-DdK4wynI.js → Avatar-ixVj76fM.js} +1 -1
- package/web-app/dist/assets/{Badge-CrX3MI7J.js → Badge-BGMSp22s.js} +1 -1
- package/web-app/dist/assets/{Button-709-wwGM.js → Button-pYJc32tG.js} +1 -1
- package/web-app/dist/assets/{CockpitPage-BapdTInO.js → CockpitPage-C1JBBouA.js} +1 -1
- package/web-app/dist/assets/{ComparePage-DT99pGvA.js → ComparePage-qD0NV4RK.js} +1 -1
- package/web-app/dist/assets/{ErrorBoundary-B0uJSys8.js → ErrorBoundary-C-yxO58j.js} +1 -1
- package/web-app/dist/assets/{EvidenceReceiptPanel-BtMFgElP.js → EvidenceReceiptPanel-ByPzuE1S.js} +1 -1
- package/web-app/dist/assets/{GitHubIssuesPanel-DjJuU941.js → GitHubIssuesPanel-BNv_2BEJ.js} +1 -1
- package/web-app/dist/assets/{GitHubPRsPanel-HXfEX1BC.js → GitHubPRsPanel-CuLZQ9CS.js} +1 -1
- package/web-app/dist/assets/{HomePage-Cqyves-S.js → HomePage-CS_FW8Je.js} +2 -2
- package/web-app/dist/assets/{LoginPage-jUbtC7_Y.js → LoginPage-DwiNf1Wm.js} +1 -1
- package/web-app/dist/assets/{MagicPage-DHRsqjgR.js → MagicPage-CmQybLPZ.js} +1 -1
- package/web-app/dist/assets/{MetricsPage-B-pED05R.js → MetricsPage-D9ERdpv8.js} +1 -1
- package/web-app/dist/assets/{NotFoundPage-CsbUs21R.js → NotFoundPage-D9ccgdbF.js} +1 -1
- package/web-app/dist/assets/{ProjectPage-BtlLEQwB.js → ProjectPage-B9Jslmgv.js} +3 -3
- package/web-app/dist/assets/{ProjectsPage-DaRbRjSp.js → ProjectsPage-DnU99Go2.js} +1 -1
- package/web-app/dist/assets/{SettingsPage-DALrtP3F.js → SettingsPage-DcTyx6wo.js} +1 -1
- package/web-app/dist/assets/{ShowcasePage-CoPHSNP0.js → ShowcasePage-jEY9ghSr.js} +1 -1
- package/web-app/dist/assets/{SystemSettingsPage-kOuSN50O.js → SystemSettingsPage-CSGltW-8.js} +1 -1
- package/web-app/dist/assets/{TeamsPage-DGWK0LMp.js → TeamsPage-gUuJXb9l.js} +1 -1
- package/web-app/dist/assets/{TemplatesPage-C-_10CwA.js → TemplatesPage-37Zhe--v.js} +1 -1
- package/web-app/dist/assets/{TerminalOutput-Cz_vPbXL.js → TerminalOutput-B09hRwaR.js} +1 -1
- package/web-app/dist/assets/{activity-icIsXJVa.js → activity-q_4MY9J0.js} +1 -1
- package/web-app/dist/assets/{bell-DbPR2_4r.js → bell-X8OC_PFP.js} +1 -1
- package/web-app/dist/assets/{bot-DvjP3Yh5.js → bot-C-N1PUOD.js} +1 -1
- package/web-app/dist/assets/{check-D1OBxHeU.js → check-B2c76Jmp.js} +1 -1
- package/web-app/dist/assets/{chevron-left-D09jDZ29.js → chevron-left-BF76YLNw.js} +1 -1
- package/web-app/dist/assets/{circle-alert-DyZMLOcC.js → circle-alert-AeFKBD8h.js} +1 -1
- package/web-app/dist/assets/{clock-CdpdJTId.js → clock-CiXRf-FH.js} +1 -1
- package/web-app/dist/assets/{cloud-CoLTlmoj.js → cloud-DKQ8SRJ5.js} +1 -1
- package/web-app/dist/assets/{code-xml-DrkE2ZZY.js → code-xml-DEb6jXWP.js} +1 -1
- package/web-app/dist/assets/{database-CMQQuM2R.js → database-6W7e-E9c.js} +1 -1
- package/web-app/dist/assets/{dollar-sign-CuioDaOG.js → dollar-sign-Ds2GOuyW.js} +1 -1
- package/web-app/dist/assets/{file-code-corner-Cx9j9K1S.js → file-code-corner-CZU0jg52.js} +1 -1
- package/web-app/dist/assets/{file-plus-CYD2zaaI.js → file-plus-DTVYnXCN.js} +1 -1
- package/web-app/dist/assets/{globe-6w-hnQkL.js → globe-0mUzKYDe.js} +1 -1
- package/web-app/dist/assets/{hammer-BpqtautL.js → hammer-BMHZg3YX.js} +1 -1
- package/web-app/dist/assets/{index-Dzs-9EgJ.js → index-BvOTvjax.js} +3 -3
- package/web-app/dist/assets/{layers-D5hd7jb7.js → layers-Df7TdcWB.js} +1 -1
- package/web-app/dist/assets/{loader-circle-C6Y9x_7H.js → loader-circle-kPsVfrSY.js} +1 -1
- package/web-app/dist/assets/{lock-D51L7rYh.js → lock-CXYcbEc5.js} +1 -1
- package/web-app/dist/assets/{package-BJF8hkP8.js → package-Cqhua1ID.js} +1 -1
- package/web-app/dist/assets/{plus-D5kUQ79n.js → plus-lseXhsn-.js} +1 -1
- package/web-app/dist/assets/{refresh-cw-BKdlCa7F.js → refresh-cw-OZriI6gD.js} +1 -1
- package/web-app/dist/assets/{rotate-ccw-_bd_YI1e.js → rotate-ccw-CKCfjPg8.js} +1 -1
- package/web-app/dist/assets/{scroll-text-DbkegIxF.js → scroll-text-DoVyyfKb.js} +1 -1
- package/web-app/dist/assets/{server-DAwqFIsq.js → server-DGy7TflW.js} +1 -1
- package/web-app/dist/assets/{shield-alert-BuR1tPHu.js → shield-alert-BCzRcMXi.js} +1 -1
- package/web-app/dist/assets/{thumbs-up-BF6p3mRd.js → thumbs-up-C_znJd5g.js} +1 -1
- package/web-app/dist/assets/{trash-2-BVpnkqqz.js → trash-2-BosCbeeA.js} +1 -1
- package/web-app/dist/assets/{trending-down-DpsDvRwW.js → trending-down-CkR5zDkV.js} +1 -1
- package/web-app/dist/assets/{trending-up-mFogTjt3.js → trending-up-DpUIfOH4.js} +1 -1
- package/web-app/dist/assets/{upload-4T8zpBsi.js → upload-CH9aKc8L.js} +1 -1
- package/web-app/dist/assets/{usePolling-DPXQv8WO.js → usePolling-pzxrHJeg.js} +1 -1
- package/web-app/dist/assets/{user-BYGIFaTm.js → user-B7OHa-VQ.js} +1 -1
- package/web-app/dist/index.html +1 -1
- package/dashboard/run.py +0 -63
- package/docs/openclaw-integration.md +0 -52
- package/docs/v10/HANDOFF-NEXT-WEEK.md +0 -40
- package/docs/v10/MORNING-BRIEF-1003.md +0 -25
- package/docs/v10/MORNING-BRIEF.md +0 -52
- package/docs/v10/TODO.md +0 -35
- package/mcp/resources.py +0 -351
- package/mcp/tools.py +0 -200
|
@@ -311,8 +311,10 @@ function verifyRunPid(runDir, runId, startOf = processStartTime) {
|
|
|
311
311
|
return { ok: false, reason: "the pid now belongs to a different process (start time differs)" };
|
|
312
312
|
return { ok: true, pid: rec.pid };
|
|
313
313
|
}
|
|
314
|
-
var runPidPath = (runDir) => join8(runDir, "run.pid");
|
|
315
|
-
var init_run_pid = () => {
|
|
314
|
+
var runPidPath = (runDir) => join8(runDir, "run.pid"), liveSessions;
|
|
315
|
+
var init_run_pid = __esm(() => {
|
|
316
|
+
liveSessions = new Set;
|
|
317
|
+
});
|
|
316
318
|
|
|
317
319
|
// src/shipper/watch.ts
|
|
318
320
|
import { readFileSync as readFileSync5, readdirSync as readdirSync2, statSync } from "fs";
|
|
@@ -7323,7 +7325,38 @@ var init_paths = __esm(() => {
|
|
|
7323
7325
|
REPO_ROOT = findRepoRoot();
|
|
7324
7326
|
});
|
|
7325
7327
|
|
|
7328
|
+
// ../../loki-ts/src/engine10/registry.ts
|
|
7329
|
+
var init_registry = () => {};
|
|
7330
|
+
|
|
7331
|
+
// ../../loki-ts/src/engine10/output.ts
|
|
7332
|
+
var init_output = __esm(() => {
|
|
7333
|
+
init_registry();
|
|
7334
|
+
init_redact();
|
|
7335
|
+
});
|
|
7336
|
+
|
|
7337
|
+
// ../../loki-ts/src/commands/run_status.ts
|
|
7338
|
+
var STALE_S, TERMINAL2;
|
|
7339
|
+
var init_run_status = __esm(() => {
|
|
7340
|
+
init_discover();
|
|
7341
|
+
init_events();
|
|
7342
|
+
init_output();
|
|
7343
|
+
STALE_S = 30 * 60;
|
|
7344
|
+
TERMINAL2 = new Set(["stage.completed", "stage.failed", "stage.skipped"]);
|
|
7345
|
+
});
|
|
7346
|
+
|
|
7326
7347
|
// ../../loki-ts/src/engine10/types.ts
|
|
7348
|
+
function pushArgv(a) {
|
|
7349
|
+
switch (a.cmd) {
|
|
7350
|
+
case "push-pr":
|
|
7351
|
+
return ["push-pr", a.repoDir, a.branch, a.title, a.bodyFile, a.draft ? "1" : "0"];
|
|
7352
|
+
case "comment":
|
|
7353
|
+
return ["comment", a.runId, a.prUrl, a.file];
|
|
7354
|
+
case "status":
|
|
7355
|
+
return ["status", a.sha, a.state, a.description];
|
|
7356
|
+
case "issue-comment":
|
|
7357
|
+
return ["issue-comment", a.issueRef, a.bodyFile];
|
|
7358
|
+
}
|
|
7359
|
+
}
|
|
7327
7360
|
function taskBlock(task) {
|
|
7328
7361
|
return ["Task (untrusted, quoted verbatim):", "<<<TASK", task, "TASK"];
|
|
7329
7362
|
}
|
|
@@ -7358,243 +7391,6 @@ var init_spec_file = __esm(() => {
|
|
|
7358
7391
|
SPEC_DIR = join26(".loki", "specs");
|
|
7359
7392
|
});
|
|
7360
7393
|
|
|
7361
|
-
// ../../loki-ts/src/engine10/registry.ts
|
|
7362
|
-
var init_registry = () => {};
|
|
7363
|
-
|
|
7364
|
-
// ../../loki-ts/src/engine10/output.ts
|
|
7365
|
-
var init_output = __esm(() => {
|
|
7366
|
-
init_registry();
|
|
7367
|
-
init_redact();
|
|
7368
|
-
});
|
|
7369
|
-
|
|
7370
|
-
// ../../loki-ts/src/runner/router/flag.ts
|
|
7371
|
-
function routerMode(env = process.env) {
|
|
7372
|
-
const v = (env.LOKI_ROUTER ?? "").trim().toLowerCase();
|
|
7373
|
-
if (v === "0" || v === "false" || v === "off")
|
|
7374
|
-
return "opt-out";
|
|
7375
|
-
if (v === "1" || v === "true" || v === "on")
|
|
7376
|
-
return "on";
|
|
7377
|
-
return "off";
|
|
7378
|
-
}
|
|
7379
|
-
var routerEnabled = (env = process.env) => routerMode(env) === "on";
|
|
7380
|
-
|
|
7381
|
-
// ../../loki-ts/src/runner/router/route_block.ts
|
|
7382
|
-
function advisorUnavailableReason(env, provider) {
|
|
7383
|
-
if (provider !== "claude")
|
|
7384
|
-
return `provider ${provider} has no advisor tool`;
|
|
7385
|
-
if (env["CLAUDE_CODE_USE_BEDROCK"] || env["CLAUDE_CODE_USE_VERTEX"] || env["CLAUDE_CODE_USE_FOUNDRY"])
|
|
7386
|
-
return "advisor tool is unsupported on Bedrock, Vertex and Foundry";
|
|
7387
|
-
if ((env["LOKI_ROUTER_ADVISOR"] ?? "").trim().toLowerCase() === "off")
|
|
7388
|
-
return "LOKI_ROUTER_ADVISOR=off";
|
|
7389
|
-
return null;
|
|
7390
|
-
}
|
|
7391
|
-
function toEsc(raw2) {
|
|
7392
|
-
const esc = Array.isArray(raw2) ? raw2 : [];
|
|
7393
|
-
return esc.map((e) => ({ trigger: s0(e["trigger"]) ?? "unknown", from: modelLabel(s0(e["from"]) ?? "?"), to: modelLabel(s0(e["to"]) ?? "?"), evidence: s0(e["evidence"]) ?? "none recorded" }));
|
|
7394
|
-
}
|
|
7395
|
-
function buildRouteBlock(env, provider, route, telemetry, runModel) {
|
|
7396
|
-
if (!routerEnabled(env))
|
|
7397
|
-
return null;
|
|
7398
|
-
const applied = route?.["applied"] !== false;
|
|
7399
|
-
const executor = s0(route?.["executor"]);
|
|
7400
|
-
const unavailable = s0(route?.["advisor_unavailable_reason"]) ?? advisorUnavailableReason(env, provider);
|
|
7401
|
-
const total = nN(telemetry?.requests_total);
|
|
7402
|
-
const over = nN(telemetry?.requests_over_100k);
|
|
7403
|
-
const shape = s0(route?.["shape_key"]);
|
|
7404
|
-
const escalations = toEsc(route?.["escalations"]);
|
|
7405
|
-
const source = s0(route?.["source"]);
|
|
7406
|
-
const reason = s0(route?.["reason"]) ?? (executor ? "no reason recorded" : "no route recorded by implement");
|
|
7407
|
-
const rawUnits = Array.isArray(route?.["units"]) ? route["units"] : [];
|
|
7408
|
-
const units = rawUnits.length > 0 ? rawUnits.map((u, i) => ({ id: s0(u["id"]) ?? String(i + 1), executor: modelLabel(s0(u["executor"]) ?? "unknown"), applied, assigned_by: assignedBy(s0(u["source"]) ?? source, u["assigned_by"]), reason: s0(u["reason"]) ?? "no reason recorded", escalations: toEsc(u["escalations"]) })) : executor && route?.["routed"] !== false ? [{ id: "run", executor: modelLabel(executor), applied, assigned_by: assignedBy(source, route?.["assigned_by"]), reason, escalations }] : [];
|
|
7409
|
-
return {
|
|
7410
|
-
routed: route?.["routed"] === false ? false : executor !== null || units.length > 0,
|
|
7411
|
-
units,
|
|
7412
|
-
executor: !applied ? `${runModel ? modelLabel(runModel) : "run model"} (per-unit assignment not applied)` : executor ? modelLabel(executor) : units[0]?.executor ?? null,
|
|
7413
|
-
advisor: unavailable ? null : modelLabel(s0(route?.["advisor"]) ?? "opus"),
|
|
7414
|
-
advisor_unavailable_reason: unavailable,
|
|
7415
|
-
reason,
|
|
7416
|
-
source,
|
|
7417
|
-
shape_key: shape,
|
|
7418
|
-
shape_parity: shape ? `NOT PROVEN (no B9 row for shape ${shape})` : "NOT PROVEN (no shape key recorded)",
|
|
7419
|
-
escalations: units.length > 0 ? units.flatMap((u) => u.escalations) : escalations,
|
|
7420
|
-
requests_total: total,
|
|
7421
|
-
requests_over_100k: over,
|
|
7422
|
-
over_100k_share: total !== null && over !== null && total > 0 ? Math.round(over / total * 1e4) / 1e4 : null,
|
|
7423
|
-
advisor_calls: nN(telemetry?.advisor_calls),
|
|
7424
|
-
advisor_input_tokens: nN(telemetry?.advisor_input_tokens),
|
|
7425
|
-
advisor_output_tokens: nN(telemetry?.advisor_output_tokens),
|
|
7426
|
-
plan_model: s0(route?.["plan_model"]),
|
|
7427
|
-
applied,
|
|
7428
|
-
run_model: runModel ?? null,
|
|
7429
|
-
not_proven: Array.isArray(route?.["route_not_proven"]) ? route["route_not_proven"].filter((x) => typeof x === "string") : []
|
|
7430
|
-
};
|
|
7431
|
-
}
|
|
7432
|
-
function routePrLine(r) {
|
|
7433
|
-
if (!r.routed)
|
|
7434
|
-
return `Route: not routed (${r.reason})`;
|
|
7435
|
-
const adv = r.advisor ? `advisor ${r.advisor}` : "advisor unavailable";
|
|
7436
|
-
return `Route: executor ${r.executor}, ${adv}; advisor tokens ${rec(r.advisor_input_tokens)} in / ${rec(r.advisor_output_tokens)} out; over 100K: ${over(r, " requests")}`;
|
|
7437
|
-
}
|
|
7438
|
-
function routeReceiptLines(r) {
|
|
7439
|
-
if (!r.routed)
|
|
7440
|
-
return [`- Route: not routed (${r.reason})`, `- Route shape_parity: ${r.shape_parity}`];
|
|
7441
|
-
return [
|
|
7442
|
-
`- Route: executor ${r.executor}, ${r.advisor ? `advisor ${r.advisor}` : `advisor unavailable: ${r.advisor_unavailable_reason}`}: ${r.reason}`,
|
|
7443
|
-
`- Route shape_key: ${r.shape_key ?? "unknown"} shape_parity: ${r.shape_parity}`,
|
|
7444
|
-
`- Route escalations: ${r.escalations.length === 0 ? "none" : r.escalations.map((e) => `${e.from} -> ${e.to} (${e.trigger}; evidence ${e.evidence})`).join("; ")}`,
|
|
7445
|
-
`- Route advisor: ${rec(r.advisor_calls)} calls, ${rec(r.advisor_input_tokens)} in / ${rec(r.advisor_output_tokens)} out tokens`,
|
|
7446
|
-
...r.units.map((u) => `- Route unit ${u.id}: executor ${u.applied ? u.executor : `not applied (assigned ${u.executor})`}, assigned by ${u.assigned_by}: ${u.reason}; escalation: ${u.escalations.length === 0 ? "none" : u.escalations.map((e) => `${e.from} -> ${e.to} (${e.trigger})`).join(", ")}`),
|
|
7447
|
-
`- Route requests over 100K: ${over(r, "")}`
|
|
7448
|
-
];
|
|
7449
|
-
}
|
|
7450
|
-
function routeNotProven(r) {
|
|
7451
|
-
return [
|
|
7452
|
-
...r.advisor_unavailable_reason ? [`advisor: NOT PROVEN (owner provider): ${r.advisor_unavailable_reason}`] : [],
|
|
7453
|
-
`route.shape_parity: ${r.shape_parity}`,
|
|
7454
|
-
...r.applied ? [] : [`route.executor: NOT PROVEN (owner engine): per-unit executor assignment is not applied (R1-11); every stage ran ${r.run_model ?? "the run model"}`],
|
|
7455
|
-
...r.not_proven
|
|
7456
|
-
];
|
|
7457
|
-
}
|
|
7458
|
-
var ALIAS, modelLabel = (m) => ALIAS[m] ?? m, nN = (v) => typeof v === "number" && Number.isFinite(v) ? v : null, rec = (v) => v === null ? "not recorded" : String(v), s0 = (v) => typeof v === "string" && v.trim() ? v.trim() : null, assignedBy = (src, explicit) => s0(explicit) ?? (src === null || src === "opus" ? "opus" : src), over = (r, unit) => r.requests_total === null || r.requests_over_100k === null ? "not recorded" : `${r.requests_over_100k} of ${r.requests_total}${unit} (${pct(r)})`, pct = (r) => r.over_100k_share === null ? "n/a (no requests)" : `${(r.over_100k_share * 100).toFixed(1)}%`;
|
|
7459
|
-
var init_route_block = __esm(() => {
|
|
7460
|
-
ALIAS = { haiku: "haiku-5.5", sonnet: "sonnet-5.5", opus: "opus-5.5" };
|
|
7461
|
-
});
|
|
7462
|
-
|
|
7463
|
-
// ../../loki-ts/src/features/speed/seal_group.ts
|
|
7464
|
-
import { createHash as createHash6 } from "crypto";
|
|
7465
|
-
import { existsSync as existsSync15, readdirSync as readdirSync9, readFileSync as readFileSync18, statSync as statSync11 } from "fs";
|
|
7466
|
-
import { dirname as dirname7, join as join27 } from "path";
|
|
7467
|
-
function sealGroup(runDir, rh) {
|
|
7468
|
-
const manifestPath = join27(runDir, "group", "manifest.json");
|
|
7469
|
-
if (!existsSync15(manifestPath))
|
|
7470
|
-
return { section: undefined, notProven: [], problems: 0, allUnitsPass: true };
|
|
7471
|
-
const notProven = [];
|
|
7472
|
-
let problems = 0;
|
|
7473
|
-
const fail = (m2) => {
|
|
7474
|
-
problems++;
|
|
7475
|
-
notProven.push(`group: ${m2}`);
|
|
7476
|
-
};
|
|
7477
|
-
const m = readJson3(manifestPath);
|
|
7478
|
-
const list = isObj10(m) && Array.isArray(m["units"]) ? m["units"] : [];
|
|
7479
|
-
if (!isObj10(m) || !ID_RE.test(String(m["group_id"] ?? "")) || list.length === 0) {
|
|
7480
|
-
fail("manifest missing, unreadable or empty");
|
|
7481
|
-
return { section: undefined, notProven, problems, allUnitsPass: false };
|
|
7482
|
-
}
|
|
7483
|
-
const units = [];
|
|
7484
|
-
const seen = new Set, seenLower = new Set;
|
|
7485
|
-
let allUnitsPass = true;
|
|
7486
|
-
list.forEach((raw2, index2) => {
|
|
7487
|
-
const u = isObj10(raw2) ? raw2 : {};
|
|
7488
|
-
const id = String(u["unit_id"] ?? "");
|
|
7489
|
-
if (!ID_RE.test(id) || seen.has(id) || seenLower.has(id.toLowerCase()))
|
|
7490
|
-
return fail(`unit ${index2} has an invalid, duplicate or case-colliding id`);
|
|
7491
|
-
seen.add(id);
|
|
7492
|
-
seenLower.add(id.toLowerCase());
|
|
7493
|
-
const dir = join27(unitsDir(runDir), id), rp = join27(dir, "receipt.json"), ep = join27(dir, "events.jsonl");
|
|
7494
|
-
let bytes;
|
|
7495
|
-
try {
|
|
7496
|
-
bytes = readFileSync18(rp);
|
|
7497
|
-
} catch {
|
|
7498
|
-
return fail(`unit ${id} sub-receipt missing`);
|
|
7499
|
-
}
|
|
7500
|
-
const sub = readJson3(rp);
|
|
7501
|
-
if (!isObj10(sub) || typeof sub["receipt_sha256"] !== "string" || rh(sub) !== sub["receipt_sha256"])
|
|
7502
|
-
return fail(`unit ${id} sub-receipt unreadable or its hash does not recompute`);
|
|
7503
|
-
const verdict = String(sub["verdict"] ?? "");
|
|
7504
|
-
if (!PASSING.has(verdict)) {
|
|
7505
|
-
allUnitsPass = false;
|
|
7506
|
-
notProven.push(`group: unit ${id} verdict ${verdict || "unknown"}`);
|
|
7507
|
-
}
|
|
7508
|
-
units.push({
|
|
7509
|
-
index: index2,
|
|
7510
|
-
unit_id: id,
|
|
7511
|
-
sub_receipt_sha256: sha2(bytes),
|
|
7512
|
-
receipt_sha256: sub["receipt_sha256"],
|
|
7513
|
-
events_file_sha256: existsSync15(ep) ? sha2(readFileSync18(ep)) : null,
|
|
7514
|
-
verdict,
|
|
7515
|
-
goal: text2(u["goal"], 200),
|
|
7516
|
-
files: (Array.isArray(u["files"]) ? u["files"] : []).map((f) => text2(f, 200)).filter(Boolean).slice(0, 50),
|
|
7517
|
-
tests: num3(u["tests"]),
|
|
7518
|
-
tokens: num3(u["tokens"]),
|
|
7519
|
-
model: text2(u["model"], 64)
|
|
7520
|
-
});
|
|
7521
|
-
});
|
|
7522
|
-
const extra = (dirNames(unitsDir(runDir)) ?? []).filter((n2) => !seen.has(n2));
|
|
7523
|
-
for (const n2 of extra)
|
|
7524
|
-
fail(`unit directory ${text2(n2, 64)} is not in the manifest`);
|
|
7525
|
-
return { section: problems === 0 ? { group_id: String(m["group_id"]), units } : undefined, notProven, problems, allUnitsPass: allUnitsPass && problems === 0 };
|
|
7526
|
-
}
|
|
7527
|
-
function capGroupVerdict(v, g) {
|
|
7528
|
-
if (g.problems > 0)
|
|
7529
|
-
return v === "SPEC_CONFLICT" ? v : "FAILED";
|
|
7530
|
-
return !g.allUnitsPass && (v === "VERIFIED" || v === "ALREADY_SATISFIED") ? "PARTIAL" : v;
|
|
7531
|
-
}
|
|
7532
|
-
async function verifyGroup(receiptPath, receipt, rh, verifyOne, deps = {}) {
|
|
7533
|
-
const runDir = dirname7(receiptPath);
|
|
7534
|
-
const signed = isObj10(receipt["verification"]) && typeof receipt["verification"]["jwt"] === "string";
|
|
7535
|
-
const bad = (reason) => ({ verdict: "TAMPERED", reason: `group: ${reason}` });
|
|
7536
|
-
const g = receipt["group"];
|
|
7537
|
-
if (g === undefined)
|
|
7538
|
-
return existsSync15(join27(runDir, "group", "manifest.json")) ? bad("group directory present but the receipt has no group section") : null;
|
|
7539
|
-
if (!isObj10(g) || !ID_RE.test(String(g["group_id"] ?? "")) || !Array.isArray(g["units"]) || g["units"].length === 0)
|
|
7540
|
-
return bad("group section malformed");
|
|
7541
|
-
const entries = g["units"];
|
|
7542
|
-
const ids = entries.map((e) => isObj10(e) ? String(e["unit_id"] ?? "") : "");
|
|
7543
|
-
if (ids.some((id) => !ID_RE.test(id)) || new Set(ids.map((i) => i.toLowerCase())).size !== ids.length)
|
|
7544
|
-
return bad("unit ids invalid, duplicated or case-colliding");
|
|
7545
|
-
const onDisk = dirNames(unitsDir(runDir));
|
|
7546
|
-
if (onDisk === null || onDisk.join("\x00") !== [...ids].sort().join("\x00"))
|
|
7547
|
-
return bad("unit directories do not match the receipt (missing or extra unit)");
|
|
7548
|
-
let unchecked = null;
|
|
7549
|
-
for (let i = 0;i < entries.length; i++) {
|
|
7550
|
-
const e = entries[i], id = ids[i], dir = join27(unitsDir(runDir), id), rp = join27(dir, "receipt.json"), ep = join27(dir, "events.jsonl");
|
|
7551
|
-
if (e["index"] !== i)
|
|
7552
|
-
return bad(`unit ${id} is out of order (index ${String(e["index"])} at position ${i})`);
|
|
7553
|
-
let bytes;
|
|
7554
|
-
try {
|
|
7555
|
-
if (!statSync11(dir).isDirectory())
|
|
7556
|
-
return bad(`unit ${id} is not a directory`);
|
|
7557
|
-
bytes = readFileSync18(rp);
|
|
7558
|
-
} catch {
|
|
7559
|
-
return bad(`unit ${id} sub-receipt missing`);
|
|
7560
|
-
}
|
|
7561
|
-
if (sha2(bytes) !== e["sub_receipt_sha256"])
|
|
7562
|
-
return bad(`unit ${id} sub-receipt does not match its recorded hash`);
|
|
7563
|
-
const evSha = existsSync15(ep) ? sha2(readFileSync18(ep)) : null;
|
|
7564
|
-
if (evSha !== (e["events_file_sha256"] ?? null))
|
|
7565
|
-
return bad(`unit ${id} events.jsonl does not match its recorded hash`);
|
|
7566
|
-
const sub = readJson3(rp);
|
|
7567
|
-
if (!isObj10(sub) || sub["receipt_sha256"] !== e["receipt_sha256"] || rh(sub) !== e["receipt_sha256"])
|
|
7568
|
-
return bad(`unit ${id} sub-receipt hash does not recompute`);
|
|
7569
|
-
if (String(sub["verdict"] ?? "") !== e["verdict"])
|
|
7570
|
-
return bad(`unit ${id} verdict differs from the combined receipt`);
|
|
7571
|
-
const r = await verifyOne(rp, deps);
|
|
7572
|
-
if (r.verdict === "UNSIGNED" && signed)
|
|
7573
|
-
unchecked ??= `unit ${id} sub-receipt is unsigned under a signed combined receipt`;
|
|
7574
|
-
if (r.verdict === "TAMPERED")
|
|
7575
|
-
return bad(`unit ${id} sub-receipt failed verify: ${r.reasons.join("; ")}`);
|
|
7576
|
-
if (r.verdict === "UNCHECKED")
|
|
7577
|
-
unchecked ??= `unit ${id} sub-receipt not checked: ${r.reasons.join("; ")}`;
|
|
7578
|
-
}
|
|
7579
|
-
return unchecked ? { verdict: "UNCHECKED", reason: `group: ${unchecked}` } : null;
|
|
7580
|
-
}
|
|
7581
|
-
var ID_RE, PASSING, sha2 = (b) => createHash6("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) : "", num3 = (v) => typeof v === "number" && Number.isFinite(v) && v >= 0 ? v : 0, unitsDir = (runDir) => join27(runDir, "group", "units"), readJson3 = (p) => {
|
|
7582
|
-
try {
|
|
7583
|
-
return JSON.parse(readFileSync18(p, "utf8"));
|
|
7584
|
-
} catch {
|
|
7585
|
-
return;
|
|
7586
|
-
}
|
|
7587
|
-
}, dirNames = (d) => {
|
|
7588
|
-
try {
|
|
7589
|
-
return readdirSync9(d).filter((n2) => !n2.startsWith(".")).sort();
|
|
7590
|
-
} catch {
|
|
7591
|
-
return null;
|
|
7592
|
-
}
|
|
7593
|
-
};
|
|
7594
|
-
var init_seal_group = __esm(() => {
|
|
7595
|
-
ID_RE = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;
|
|
7596
|
-
PASSING = new Set(["VERIFIED", "ALREADY_SATISFIED"]);
|
|
7597
|
-
});
|
|
7598
7394
|
// ../../loki-ts/src/engine10/pr_body.ts
|
|
7599
7395
|
function aiTrailers(i, env = process.env) {
|
|
7600
7396
|
if (!markingOn(env))
|
|
@@ -7602,16 +7398,11 @@ function aiTrailers(i, env = process.env) {
|
|
|
7602
7398
|
return ["AI-Generated: true", `AI-Provider: ${clean(i.provider)}`, `AI-Model: ${clean(i.model)}`, `Loki-Receipt: ${clean(i.receiptRel)}`];
|
|
7603
7399
|
}
|
|
7604
7400
|
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";
|
|
7605
|
-
var init_pr_body = __esm(() => {
|
|
7606
|
-
init_output();
|
|
7607
|
-
init_route_block();
|
|
7608
|
-
init_seal_group();
|
|
7609
|
-
});
|
|
7610
7401
|
|
|
7611
7402
|
// ../../loki-ts/src/util/credential_env.ts
|
|
7612
|
-
import { mkdtempSync as mkdtempSync2, rmSync as rmSync4, statSync as
|
|
7403
|
+
import { mkdtempSync as mkdtempSync2, rmSync as rmSync4, statSync as statSync11, lstatSync as lstatSync4 } from "fs";
|
|
7613
7404
|
import { tmpdir as tmpdir3 } from "os";
|
|
7614
|
-
import { join as
|
|
7405
|
+
import { join as join27 } from "path";
|
|
7615
7406
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
7616
7407
|
function withheldSentinel(fresh = false) {
|
|
7617
7408
|
if (fresh)
|
|
@@ -7630,7 +7421,7 @@ function emptyGhConfigDir(env = process.env) {
|
|
|
7630
7421
|
const runTmp = env["LOKI_RUN_TMP"] ?? "";
|
|
7631
7422
|
const root = runTmp !== "" && isDir(runTmp) ? runTmp : tmpdir3();
|
|
7632
7423
|
try {
|
|
7633
|
-
configDir = mkdtempSync2(
|
|
7424
|
+
configDir = mkdtempSync2(join27(root, "loki-gh-config-"));
|
|
7634
7425
|
} catch {
|
|
7635
7426
|
return null;
|
|
7636
7427
|
}
|
|
@@ -7671,7 +7462,7 @@ function hardenCredentialEnv(env, fresh = false) {
|
|
|
7671
7462
|
}
|
|
7672
7463
|
var GITHUB_TOKEN_VARS, SENTINEL_PREFIX = "ghp_LOKIWITHHELDsentinel", sentinel = null, configDir = null, exitHooked = false, sigtermHooked = false, isDir = (p) => {
|
|
7673
7464
|
try {
|
|
7674
|
-
return !lstatSync4(p).isSymbolicLink() &&
|
|
7465
|
+
return !lstatSync4(p).isSymbolicLink() && statSync11(p).isDirectory();
|
|
7675
7466
|
} catch {
|
|
7676
7467
|
return false;
|
|
7677
7468
|
}
|
|
@@ -7785,17 +7576,21 @@ var init_safe_git = __esm(() => {
|
|
|
7785
7576
|
OPT_WITH_VALUE = new Set(["-C", "-c", "--git-dir", "--work-tree", "--namespace", "--exec-path", "--config-env", "--attr-source"]);
|
|
7786
7577
|
SECRET_VARS = ["GH_TOKEN", "GITHUB_TOKEN", "GH_ENTERPRISE_TOKEN", "GITHUB_ENTERPRISE_TOKEN", "SSH_AUTH_SOCK"];
|
|
7787
7578
|
});
|
|
7579
|
+
// ../../loki-ts/src/util/yaml_key.ts
|
|
7580
|
+
var init_yaml_key = () => {};
|
|
7581
|
+
|
|
7788
7582
|
// ../../loki-ts/src/util/run_cap.ts
|
|
7789
7583
|
var MAX_SCOPE_BYTES;
|
|
7790
7584
|
var init_run_cap = __esm(() => {
|
|
7791
7585
|
init_types();
|
|
7586
|
+
init_yaml_key();
|
|
7792
7587
|
MAX_SCOPE_BYTES = 256 * 1024;
|
|
7793
7588
|
});
|
|
7794
7589
|
|
|
7795
7590
|
// ../../loki-ts/src/util/mutation_proof.ts
|
|
7796
|
-
import { existsSync as
|
|
7591
|
+
import { existsSync as existsSync15, mkdirSync as mkdirSync5, mkdtempSync as mkdtempSync3, readFileSync as readFileSync18, rmSync as rmSync5, symlinkSync, writeFileSync as writeFileSync8 } from "fs";
|
|
7797
7592
|
import { tmpdir as tmpdir4 } from "os";
|
|
7798
|
-
import { basename as basename8, dirname as
|
|
7593
|
+
import { basename as basename8, dirname as dirname7, isAbsolute as isAbsolute2, join as join28, relative as relative2 } from "path";
|
|
7799
7594
|
function mutationProof(i) {
|
|
7800
7595
|
const rels = i.wallFiles.map((f) => isAbsolute2(f.path) ? relative2(i.repoDir, f.path) : f.path);
|
|
7801
7596
|
if (rels.length === 0)
|
|
@@ -7813,26 +7608,26 @@ function mutationProof(i) {
|
|
|
7813
7608
|
const deadline = Date.now() + budgetS * 1000, remaining = () => deadline - Date.now();
|
|
7814
7609
|
let parent = null, wt = null, added = false;
|
|
7815
7610
|
try {
|
|
7816
|
-
parent = mkdtempSync3(
|
|
7817
|
-
wt =
|
|
7611
|
+
parent = mkdtempSync3(join28(tmpdir4(), "loki-mutproof-"));
|
|
7612
|
+
wt = join28(parent, "wt");
|
|
7818
7613
|
const add2 = git(i.repoDir, ["worktree", "add", "--detach", wt, i.baseSha], Math.max(1000, Math.min(30000, remaining())));
|
|
7819
|
-
added =
|
|
7614
|
+
added = existsSync15(wt) || add2.status === 0;
|
|
7820
7615
|
if (add2.status !== 0)
|
|
7821
7616
|
return np("could not create the base worktree");
|
|
7822
7617
|
for (const rel of rels) {
|
|
7823
|
-
const sealed =
|
|
7824
|
-
if (!
|
|
7618
|
+
const sealed = join28(i.runDir, "wall", basename8(rel)), src = existsSync15(sealed) ? sealed : join28(i.repoDir, rel);
|
|
7619
|
+
if (!existsSync15(src))
|
|
7825
7620
|
return np(`Wall file missing: ${rel}`);
|
|
7826
|
-
mkdirSync5(
|
|
7827
|
-
writeFileSync8(
|
|
7621
|
+
mkdirSync5(dirname7(join28(wt, rel)), { recursive: true });
|
|
7622
|
+
writeFileSync8(join28(wt, rel), readFileSync18(src));
|
|
7828
7623
|
}
|
|
7829
7624
|
for (const rel of rels) {
|
|
7830
7625
|
const parts = rel.split("/").slice(0, -1);
|
|
7831
7626
|
for (let n2 = 0;n2 <= parts.length; n2++) {
|
|
7832
7627
|
const anc = parts.slice(0, n2).join("/");
|
|
7833
7628
|
for (const d of DEP_DIRS) {
|
|
7834
|
-
const src =
|
|
7835
|
-
if (
|
|
7629
|
+
const src = join28(i.repoDir, anc, d), dst = join28(wt, anc, d);
|
|
7630
|
+
if (existsSync15(src) && !existsSync15(dst)) {
|
|
7836
7631
|
try {
|
|
7837
7632
|
symlinkSync(src, dst);
|
|
7838
7633
|
} catch {}
|
|
@@ -7890,7 +7685,7 @@ var init_lean_prefix = __esm(() => {
|
|
|
7890
7685
|
|
|
7891
7686
|
// ../../loki-ts/src/features/wall_manifest.ts
|
|
7892
7687
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
7893
|
-
import { accessSync, constants, statSync as
|
|
7688
|
+
import { accessSync, constants, statSync as statSync12 } from "fs";
|
|
7894
7689
|
import { tmpdir as tmpdir5 } from "os";
|
|
7895
7690
|
function templateEnd(s, i) {
|
|
7896
7691
|
for (let j = i + 1;j < s.length; ) {
|
|
@@ -8624,7 +8419,7 @@ function resolvePython3() {
|
|
|
8624
8419
|
const cand = `${dir}/python3`;
|
|
8625
8420
|
try {
|
|
8626
8421
|
accessSync(cand, constants.X_OK);
|
|
8627
|
-
if (
|
|
8422
|
+
if (statSync12(cand).isFile())
|
|
8628
8423
|
return cand;
|
|
8629
8424
|
} catch {}
|
|
8630
8425
|
}
|
|
@@ -8947,8 +8742,8 @@ var init_wall_manifest = __esm(() => {
|
|
|
8947
8742
|
});
|
|
8948
8743
|
|
|
8949
8744
|
// ../../loki-ts/src/features/wall_manifest_wire.ts
|
|
8950
|
-
import { createHash as
|
|
8951
|
-
import { basename as basename9, dirname as
|
|
8745
|
+
import { createHash as createHash6 } from "crypto";
|
|
8746
|
+
import { basename as basename9, dirname as dirname8 } from "path";
|
|
8952
8747
|
function blobEntries(ls) {
|
|
8953
8748
|
const out2 = [];
|
|
8954
8749
|
for (const raw2 of ls.toString("latin1").split("\x00")) {
|
|
@@ -8978,7 +8773,7 @@ function wallManifestFor(repoDir, tree, task, env = process.env) {
|
|
|
8978
8773
|
return null;
|
|
8979
8774
|
const low = task.toLowerCase().replace(/\\/g, "/");
|
|
8980
8775
|
const token = (n2) => new RegExp(`(?<![A-Za-z0-9_])${n2.toLowerCase().replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?![A-Za-z0-9_])`).test(low);
|
|
8981
|
-
const dirModule = (e) => /^(index|__init__|mod|main)\.[^.]+$/.test(basename9(e.path)) && e.path.includes("/") && token(
|
|
8776
|
+
const dirModule = (e) => /^(index|__init__|mod|main)\.[^.]+$/.test(basename9(e.path)) && e.path.includes("/") && token(dirname8(e.path));
|
|
8982
8777
|
const matched = all.filter((e) => SOURCE.test(e.path) && (token(basename9(e.path)) || token(e.path) || token(basename9(e.path).replace(/\.[^.]*$/, "")) || dirModule(e)));
|
|
8983
8778
|
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]);
|
|
8984
8779
|
const occ = new Map(matched.map((e) => [e, spans(e.path)]));
|
|
@@ -9006,8 +8801,8 @@ function wallManifestFor(repoDir, tree, task, env = process.env) {
|
|
|
9006
8801
|
}
|
|
9007
8802
|
if (at !== cat.length)
|
|
9008
8803
|
return null;
|
|
9009
|
-
const
|
|
9010
|
-
return { text:
|
|
8804
|
+
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") };
|
|
9011
8806
|
} catch {
|
|
9012
8807
|
return null;
|
|
9013
8808
|
}
|
|
@@ -9039,12 +8834,12 @@ Example: test('name', () => { assert.strictEqual(fn(1), 2); }); Run: node --tes
|
|
|
9039
8834
|
});
|
|
9040
8835
|
|
|
9041
8836
|
// ../../loki-ts/src/engine10/cache.ts
|
|
9042
|
-
import { createHash as
|
|
9043
|
-
import { existsSync as
|
|
8837
|
+
import { createHash as createHash7 } from "crypto";
|
|
8838
|
+
import { existsSync as existsSync16, mkdirSync as mkdirSync6, readFileSync as readFileSync19, writeFileSync as writeFileSync9 } from "fs";
|
|
9044
8839
|
import { resolve as resolve8 } from "path";
|
|
9045
8840
|
function repoKey(originUrl, repoDir) {
|
|
9046
8841
|
const basis = originUrl && originUrl.trim() !== "" ? originUrl : resolve8(repoDir);
|
|
9047
|
-
return
|
|
8842
|
+
return createHash7("sha256").update(basis).digest("hex");
|
|
9048
8843
|
}
|
|
9049
8844
|
function defaultCacheRoot() {
|
|
9050
8845
|
return resolve8(homeLokiDir(), "cache", "v10");
|
|
@@ -9052,11 +8847,11 @@ function defaultCacheRoot() {
|
|
|
9052
8847
|
function repoCacheDir(key, cacheRoot = defaultCacheRoot()) {
|
|
9053
8848
|
return resolve8(cacheRoot, key);
|
|
9054
8849
|
}
|
|
9055
|
-
function
|
|
9056
|
-
if (!
|
|
8850
|
+
function readJson3(path, isValid = (_v) => true) {
|
|
8851
|
+
if (!existsSync16(path))
|
|
9057
8852
|
return null;
|
|
9058
8853
|
try {
|
|
9059
|
-
const parsed = JSON.parse(
|
|
8854
|
+
const parsed = JSON.parse(readFileSync19(path, "utf8"));
|
|
9060
8855
|
return isValid(parsed) ? parsed : null;
|
|
9061
8856
|
} catch {
|
|
9062
8857
|
return null;
|
|
@@ -9066,7 +8861,7 @@ function isRepoMap(v) {
|
|
|
9066
8861
|
return typeof v === "object" && v !== null && Array.isArray(v.files) && Array.isArray(v.entries);
|
|
9067
8862
|
}
|
|
9068
8863
|
function readRepoMapCache(dir, tree) {
|
|
9069
|
-
return
|
|
8864
|
+
return readJson3(resolve8(dir, `repomap-${tree}.json`), isRepoMap);
|
|
9070
8865
|
}
|
|
9071
8866
|
var init_cache = __esm(() => {
|
|
9072
8867
|
init_paths();
|
|
@@ -9091,7 +8886,7 @@ function selectSpecificFiles(task, repoMap, max = MAX_RELEVANT_FILES) {
|
|
|
9091
8886
|
const scored = sets.map((toks, idx) => ({ path: entries[idx].path, score: specific.reduce((n2, w) => n2 + (toks.has(w) ? 1 : 0), 0), idx }));
|
|
9092
8887
|
return scored.filter((s) => s.score > 0).sort((a, b) => b.score - a.score || a.idx - b.idx).slice(0, max).map((s) => s.path);
|
|
9093
8888
|
}
|
|
9094
|
-
var MAX_RELEVANT_FILES = 8, haystack = (entry) => `${entry.path} ${entry.symbols.join(" ")}`.toLowerCase(), wordTokens = (
|
|
8889
|
+
var MAX_RELEVANT_FILES = 8, haystack = (entry) => `${entry.path} ${entry.symbols.join(" ")}`.toLowerCase(), wordTokens = (text2) => text2.toLowerCase().match(/[a-z0-9_]+/g) ?? [];
|
|
9095
8890
|
|
|
9096
8891
|
// ../../loki-ts/src/features/speed/lean_select.ts
|
|
9097
8892
|
function speedLikelyFiles(task, map, env = process.env) {
|
|
@@ -9106,12 +8901,12 @@ function speedLikelyFiles(task, map, env = process.env) {
|
|
|
9106
8901
|
var init_lean_select = () => {};
|
|
9107
8902
|
|
|
9108
8903
|
// ../../loki-ts/src/util/claude_model.ts
|
|
9109
|
-
import { readFileSync as
|
|
8904
|
+
import { readFileSync as readFileSync20 } from "fs";
|
|
9110
8905
|
import { resolve as resolve9 } from "path";
|
|
9111
8906
|
function resolveClaudeModel(want) {
|
|
9112
8907
|
try {
|
|
9113
8908
|
const path = process.env["LOKI_MODEL_CATALOG"] || resolve9(REPO_ROOT, "providers/model_catalog.json");
|
|
9114
|
-
return JSON.parse(
|
|
8909
|
+
return JSON.parse(readFileSync20(path, "utf8")).providers?.claude?.cli_aliases?.[want] ?? want;
|
|
9115
8910
|
} catch {
|
|
9116
8911
|
return want;
|
|
9117
8912
|
}
|
|
@@ -9159,17 +8954,28 @@ var init_hooks = __esm(() => {
|
|
|
9159
8954
|
hooks = {};
|
|
9160
8955
|
});
|
|
9161
8956
|
|
|
8957
|
+
// ../../loki-ts/src/runner/router/flag.ts
|
|
8958
|
+
function routerMode(env = process.env) {
|
|
8959
|
+
const v = (env.LOKI_ROUTER ?? "").trim().toLowerCase();
|
|
8960
|
+
if (v === "0" || v === "false" || v === "off")
|
|
8961
|
+
return "opt-out";
|
|
8962
|
+
if (v === "1" || v === "true" || v === "on")
|
|
8963
|
+
return "on";
|
|
8964
|
+
return "off";
|
|
8965
|
+
}
|
|
8966
|
+
var routerEnabled = (env = process.env) => routerMode(env) === "on";
|
|
8967
|
+
|
|
9162
8968
|
// ../../loki-ts/src/engine10/cost.ts
|
|
9163
|
-
import { mkdirSync as mkdirSync7, readdirSync as
|
|
9164
|
-
import { join as
|
|
8969
|
+
import { mkdirSync as mkdirSync7, readdirSync as readdirSync9, readFileSync as readFileSync21, writeFileSync as writeFileSync10 } from "fs";
|
|
8970
|
+
import { join as join29 } from "path";
|
|
9165
8971
|
function routerTelemetryOn(env = process.env) {
|
|
9166
8972
|
return routerEnabled(env);
|
|
9167
8973
|
}
|
|
9168
|
-
function
|
|
8974
|
+
function num3(v) {
|
|
9169
8975
|
return typeof v === "number" && Number.isFinite(v) ? v : 0;
|
|
9170
8976
|
}
|
|
9171
8977
|
function resultCostPath(lokiRoot, iteration) {
|
|
9172
|
-
return
|
|
8978
|
+
return join29(lokiRoot, "metrics", `result-cost-${iteration}.json`);
|
|
9173
8979
|
}
|
|
9174
8980
|
function sumResultCosts(lokiRoot, iterations, context = []) {
|
|
9175
8981
|
const out2 = {
|
|
@@ -9200,7 +9006,7 @@ function sumResultCosts(lokiRoot, iterations, context = []) {
|
|
|
9200
9006
|
const parsed = new Map;
|
|
9201
9007
|
for (const iter of iterations) {
|
|
9202
9008
|
try {
|
|
9203
|
-
parsed.set(iter, JSON.parse(
|
|
9009
|
+
parsed.set(iter, JSON.parse(readFileSync21(resultCostPath(lokiRoot, iter), "utf8")));
|
|
9204
9010
|
} catch {}
|
|
9205
9011
|
}
|
|
9206
9012
|
const own = new Set(iterations);
|
|
@@ -9209,15 +9015,15 @@ function sumResultCosts(lokiRoot, iterations, context = []) {
|
|
|
9209
9015
|
if (own.has(iter))
|
|
9210
9016
|
continue;
|
|
9211
9017
|
try {
|
|
9212
|
-
lookup.push({ iter, rec: JSON.parse(
|
|
9018
|
+
lookup.push({ iter, rec: JSON.parse(readFileSync21(resultCostPath(lokiRoot, iter), "utf8")) });
|
|
9213
9019
|
} catch {}
|
|
9214
9020
|
}
|
|
9215
9021
|
const verdicts = hooks.costRecords?.resumeVerdicts(lookup) ?? { ambiguous: [], separate: [] };
|
|
9216
9022
|
const rv = { ambiguous: verdicts.ambiguous.filter((i) => own.has(i)), separate: verdicts.separate.filter((i) => own.has(i)) };
|
|
9217
9023
|
for (const iter of iterations) {
|
|
9218
9024
|
const path = resultCostPath(lokiRoot, iter);
|
|
9219
|
-
const
|
|
9220
|
-
if (!
|
|
9025
|
+
const rec = parsed.get(iter);
|
|
9026
|
+
if (!rec) {
|
|
9221
9027
|
out2.missing.push(iter);
|
|
9222
9028
|
continue;
|
|
9223
9029
|
}
|
|
@@ -9227,45 +9033,45 @@ function sumResultCosts(lokiRoot, iterations, context = []) {
|
|
|
9227
9033
|
continue;
|
|
9228
9034
|
}
|
|
9229
9035
|
tokenSessions++;
|
|
9230
|
-
const inTok =
|
|
9231
|
-
const outTok =
|
|
9232
|
-
const cacheR =
|
|
9233
|
-
const cacheC =
|
|
9036
|
+
const inTok = num3(rec["input_tokens"]);
|
|
9037
|
+
const outTok = num3(rec["output_tokens"]);
|
|
9038
|
+
const cacheR = num3(rec["cache_read_tokens"]);
|
|
9039
|
+
const cacheC = num3(rec["cache_creation_tokens"]);
|
|
9234
9040
|
out2.input_tokens += inTok;
|
|
9235
9041
|
out2.output_tokens += outTok;
|
|
9236
9042
|
out2.cache_read_tokens += cacheR;
|
|
9237
9043
|
out2.cache_creation_tokens += cacheC;
|
|
9238
|
-
if (typeof
|
|
9044
|
+
if (typeof rec["cache_read_tokens"] === "number")
|
|
9239
9045
|
readSeen++;
|
|
9240
|
-
if (typeof
|
|
9046
|
+
if (typeof rec["cache_creation_tokens"] === "number")
|
|
9241
9047
|
creationSeen++;
|
|
9242
|
-
if (typeof
|
|
9243
|
-
out2.duration_ms = (out2.duration_ms ?? 0) +
|
|
9048
|
+
if (typeof rec["duration_ms"] === "number" && Number.isFinite(rec["duration_ms"])) {
|
|
9049
|
+
out2.duration_ms = (out2.duration_ms ?? 0) + rec["duration_ms"];
|
|
9244
9050
|
durSeen++;
|
|
9245
9051
|
}
|
|
9246
9052
|
if (out2.router)
|
|
9247
|
-
out2.router["requests_total"] +=
|
|
9053
|
+
out2.router["requests_total"] += num3(rec["requests_total"]);
|
|
9248
9054
|
if (out2.router)
|
|
9249
|
-
out2.router["requests_over_100k"] +=
|
|
9055
|
+
out2.router["requests_over_100k"] += num3(rec["requests_over_100k"]);
|
|
9250
9056
|
if (out2.router)
|
|
9251
|
-
out2.router["over_100k_input_tokens"] +=
|
|
9057
|
+
out2.router["over_100k_input_tokens"] += num3(rec["over_100k_input_tokens"]);
|
|
9252
9058
|
if (out2.router)
|
|
9253
|
-
out2.router["over_100k_output_tokens"] +=
|
|
9059
|
+
out2.router["over_100k_output_tokens"] += num3(rec["over_100k_output_tokens"]);
|
|
9254
9060
|
if (out2.router)
|
|
9255
|
-
out2.router["advisor_calls"] +=
|
|
9061
|
+
out2.router["advisor_calls"] += num3(rec["advisor_calls"]);
|
|
9256
9062
|
if (out2.router)
|
|
9257
|
-
out2.router["advisor_input_tokens"] +=
|
|
9063
|
+
out2.router["advisor_input_tokens"] += num3(rec["advisor_input_tokens"]);
|
|
9258
9064
|
if (out2.router)
|
|
9259
|
-
out2.router["advisor_output_tokens"] +=
|
|
9260
|
-
if (typeof
|
|
9261
|
-
out2.model =
|
|
9065
|
+
out2.router["advisor_output_tokens"] += num3(rec["advisor_output_tokens"]);
|
|
9066
|
+
if (typeof rec["model"] === "string" && rec["model"])
|
|
9067
|
+
out2.model = rec["model"];
|
|
9262
9068
|
sources2.push(path);
|
|
9263
|
-
recs.push({ iter, rec
|
|
9264
|
-
const c =
|
|
9265
|
-
const unmetered =
|
|
9069
|
+
recs.push({ iter, rec });
|
|
9070
|
+
const c = rec["total_cost_usd"];
|
|
9071
|
+
const unmetered = rec["source"] === UNMETERED;
|
|
9266
9072
|
if (unmetered)
|
|
9267
9073
|
out2.unmetered = true;
|
|
9268
|
-
if (
|
|
9074
|
+
if (rec["estimated"] === true)
|
|
9269
9075
|
out2.estimated = true;
|
|
9270
9076
|
const noUsage = !unmetered && inTok === 0 && outTok === 0 && cacheR === 0 && cacheC === 0;
|
|
9271
9077
|
if (typeof c !== "number" || !Number.isFinite(c) || noUsage) {
|
|
@@ -9306,17 +9112,83 @@ var init_cost = __esm(() => {
|
|
|
9306
9112
|
init_hooks();
|
|
9307
9113
|
});
|
|
9308
9114
|
|
|
9309
|
-
// ../../loki-ts/src/
|
|
9310
|
-
import {
|
|
9311
|
-
|
|
9312
|
-
|
|
9313
|
-
|
|
9314
|
-
|
|
9315
|
-
|
|
9115
|
+
// ../../loki-ts/src/util/atomic.ts
|
|
9116
|
+
import {
|
|
9117
|
+
closeSync as closeSync5,
|
|
9118
|
+
fstatSync as fstatSync4,
|
|
9119
|
+
fsyncSync as fsyncSync2,
|
|
9120
|
+
lstatSync as lstatSync5,
|
|
9121
|
+
mkdirSync as mkdirSync8,
|
|
9122
|
+
openSync as openSync5,
|
|
9123
|
+
readSync as readSync4,
|
|
9124
|
+
renameSync as renameSync4,
|
|
9125
|
+
rmSync as rmSync6,
|
|
9126
|
+
statSync as statSync13,
|
|
9127
|
+
unlinkSync as unlinkSync3,
|
|
9128
|
+
writeFileSync as writeFileSync11,
|
|
9129
|
+
writeSync as writeSync3
|
|
9130
|
+
} from "fs";
|
|
9131
|
+
import { dirname as dirname9 } from "path";
|
|
9132
|
+
function durableWriteThenRename(target, body) {
|
|
9133
|
+
mkdirSync8(dirname9(target), { recursive: true });
|
|
9134
|
+
const tmp = tmpPathFor(target);
|
|
9135
|
+
const fd = openSync5(tmp, "w");
|
|
9136
|
+
try {
|
|
9137
|
+
writeSync3(fd, body);
|
|
9138
|
+
fsyncSync2(fd);
|
|
9139
|
+
} finally {
|
|
9140
|
+
closeSync5(fd);
|
|
9141
|
+
}
|
|
9142
|
+
renameSync4(tmp, target);
|
|
9143
|
+
fsyncParentDir(target);
|
|
9144
|
+
}
|
|
9145
|
+
function fsyncParentDir(target) {
|
|
9146
|
+
let dfd = null;
|
|
9147
|
+
try {
|
|
9148
|
+
dfd = openSync5(dirname9(target), "r");
|
|
9149
|
+
fsyncSync2(dfd);
|
|
9150
|
+
} catch {} finally {
|
|
9151
|
+
if (dfd !== null) {
|
|
9152
|
+
try {
|
|
9153
|
+
closeSync5(dfd);
|
|
9154
|
+
} catch {}
|
|
9155
|
+
}
|
|
9156
|
+
}
|
|
9157
|
+
}
|
|
9158
|
+
function atomicWriteJson(target, data) {
|
|
9159
|
+
durableWriteThenRename(target, `${JSON.stringify(data, null, 2)}
|
|
9160
|
+
`);
|
|
9161
|
+
}
|
|
9162
|
+
function tmpPathFor(target) {
|
|
9163
|
+
return `${target}.tmp.${process.pid}.${++_tmpCounter}`;
|
|
9164
|
+
}
|
|
9165
|
+
var _tmpCounter = 0, _appendChains;
|
|
9166
|
+
var init_atomic = __esm(() => {
|
|
9167
|
+
_appendChains = new Map;
|
|
9168
|
+
});
|
|
9169
|
+
|
|
9170
|
+
// ../../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";
|
|
9173
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
9174
|
+
function loadDefaultMaxCostUsd() {
|
|
9175
|
+
const here = dirname10(fileURLToPath2(import.meta.url));
|
|
9176
|
+
for (const rel of [["..", "..", "data"], ["..", "data"]]) {
|
|
9177
|
+
try {
|
|
9178
|
+
const j = JSON.parse(readFileSync22(resolve10(here, ...rel, "model-pricing.json"), "utf8"));
|
|
9179
|
+
if (typeof j.default_max_cost_usd === "number" && Number.isFinite(j.default_max_cost_usd) && j.default_max_cost_usd > 0)
|
|
9180
|
+
return j.default_max_cost_usd;
|
|
9181
|
+
} catch {}
|
|
9182
|
+
}
|
|
9183
|
+
return DEFAULT_FALLBACK_USD;
|
|
9184
|
+
}
|
|
9185
|
+
function _loadPricing() {
|
|
9186
|
+
try {
|
|
9187
|
+
const moduleDir = dirname10(fileURLToPath2(import.meta.url));
|
|
9316
9188
|
const jsonPath = resolve10(moduleDir, "..", "..", "data", "model-pricing.json");
|
|
9317
|
-
if (!
|
|
9189
|
+
if (!existsSync17(jsonPath))
|
|
9318
9190
|
return _FALLBACK_PRICING;
|
|
9319
|
-
const raw2 = JSON.parse(
|
|
9191
|
+
const raw2 = JSON.parse(readFileSync22(jsonPath, "utf8"));
|
|
9320
9192
|
const pricing2 = raw2["pricing"];
|
|
9321
9193
|
if (!pricing2 || typeof pricing2 !== "object")
|
|
9322
9194
|
return _FALLBACK_PRICING;
|
|
@@ -9344,8 +9216,9 @@ function _loadPricing() {
|
|
|
9344
9216
|
return _FALLBACK_PRICING;
|
|
9345
9217
|
}
|
|
9346
9218
|
}
|
|
9347
|
-
var _FALLBACK_PRICING, PRICING;
|
|
9219
|
+
var DEFAULT_FALLBACK_USD = 100, _FALLBACK_PRICING, PRICING;
|
|
9348
9220
|
var init_budget = __esm(() => {
|
|
9221
|
+
init_atomic();
|
|
9349
9222
|
init_paths();
|
|
9350
9223
|
init_cost();
|
|
9351
9224
|
_FALLBACK_PRICING = {
|
|
@@ -9393,13 +9266,37 @@ var init_session_route = __esm(() => {
|
|
|
9393
9266
|
});
|
|
9394
9267
|
|
|
9395
9268
|
// ../../loki-ts/src/providers/provider_guard.ts
|
|
9269
|
+
function providerSandboxFlags(provider) {
|
|
9270
|
+
switch (provider) {
|
|
9271
|
+
case "codex":
|
|
9272
|
+
return ["--sandbox", "workspace-write"];
|
|
9273
|
+
case "aider":
|
|
9274
|
+
return ["--no-auto-commits"];
|
|
9275
|
+
default:
|
|
9276
|
+
return [];
|
|
9277
|
+
}
|
|
9278
|
+
}
|
|
9279
|
+
var E10_COMMON = "loki-ts/src/engine10/session.ts (detached spawn, killGroupWithGrace, recordCost, watchBudget), loki-ts/src/providers/provider_guard.ts, loki-ts/src/e10ext/budget_cap.ts", U = "unknown", CAPABILITIES;
|
|
9396
9280
|
var init_provider_guard = __esm(() => {
|
|
9397
9281
|
init_safe_git();
|
|
9282
|
+
CAPABILITIES = [
|
|
9283
|
+
{ provider: "claude", engine: "engine10", invocation: "Agent SDK query() (default) or claude CLI via loki-ts/src/runner/providers.ts", cost: "measured", budgetKill: "post-iteration", cancelProcessGroup: "yes", sandboxFlags: [], receipt: "yes", resume: "yes", guardScan: "native", killBlocking: "native", source: `${E10_COMMON}; loki-ts/src/runner/providers.ts (LOKI_E10_RESUME_SESSION, claude only)` },
|
|
9284
|
+
{ provider: "codex", engine: "engine10", invocation: "codex exec --sandbox workspace-write --skip-git-repo-check", cost: "estimated", budgetKill: "post-iteration", cancelProcessGroup: "yes", sandboxFlags: providerSandboxFlags("codex"), receipt: "yes", resume: "no", guardScan: "post-iteration", killBlocking: "none", source: `${E10_COMMON}; loki-ts/src/providers/provider_cost.ts (ESTIMATED_PROVIDERS); loki-ts/src/runner/providers.ts codexProvider` },
|
|
9285
|
+
{ provider: "cline", engine: "engine10", invocation: "cline -y", cost: "estimated", budgetKill: "post-iteration", cancelProcessGroup: "yes", sandboxFlags: providerSandboxFlags("cline"), receipt: "yes", resume: "no", guardScan: "post-iteration", killBlocking: "none", source: `${E10_COMMON}; loki-ts/src/providers/provider_cost.ts; providers/cline.sh` },
|
|
9286
|
+
{ provider: "aider", engine: "engine10", invocation: "aider --yes-always", cost: "estimated", budgetKill: "post-iteration", cancelProcessGroup: "yes", sandboxFlags: providerSandboxFlags("aider"), receipt: "yes", resume: "no", guardScan: "post-iteration", killBlocking: "none", source: `${E10_COMMON}; loki-ts/src/providers/provider_cost.ts (a reported "Cost: $X session" line is used as-is); providers/aider.sh` },
|
|
9287
|
+
{ provider: "opencode", engine: "engine10", invocation: "opencode run --auto [-m provider/model]", cost: "none", budgetKill: "none", cancelProcessGroup: "yes", sandboxFlags: providerSandboxFlags("opencode"), receipt: "yes", resume: "no", guardScan: "post-iteration", killBlocking: "none", source: `${E10_COMMON}; loki-ts/src/runner/provider_opencode.ts (costMeasured false); loki-ts/src/providers/provider_cost.ts (opencode not in ESTIMATED_PROVIDERS); docs/v10/GUIDE.md provider table` },
|
|
9288
|
+
{ provider: "claude", engine: "legacy", invocation: "claude --dangerously-skip-permissions (bash loop)", cost: U, budgetKill: U, cancelProcessGroup: U, sandboxFlags: U, receipt: U, resume: U, guardScan: U, killBlocking: U, source: "providers/claude.sh (PROVIDER_AUTONOMOUS_FLAG); autonomy/run.sh (LOKI_BUDGET_LIMIT); other fields not confirmed" },
|
|
9289
|
+
{ provider: "codex", engine: "legacy", invocation: "codex exec --sandbox workspace-write --skip-git-repo-check (bash loop)", cost: U, budgetKill: U, cancelProcessGroup: U, sandboxFlags: ["--sandbox", "workspace-write"], receipt: U, resume: U, guardScan: U, killBlocking: U, source: "providers/codex.sh (PROVIDER_AUTONOMOUS_FLAG); other fields not confirmed" },
|
|
9290
|
+
{ provider: "cline", engine: "legacy", invocation: "cline -y (bash loop)", cost: U, budgetKill: U, cancelProcessGroup: U, sandboxFlags: U, receipt: U, resume: U, guardScan: U, killBlocking: U, source: "providers/cline.sh (PROVIDER_AUTONOMOUS_FLAG); other fields not confirmed" },
|
|
9291
|
+
{ provider: "aider", engine: "legacy", invocation: "aider --yes-always (bash loop)", cost: U, budgetKill: U, cancelProcessGroup: U, sandboxFlags: U, receipt: U, resume: U, guardScan: U, killBlocking: U, source: "providers/aider.sh (PROVIDER_AUTONOMOUS_FLAG); other fields not confirmed" },
|
|
9292
|
+
{ provider: "opencode", engine: "legacy", invocation: "opencode run --auto (bash loop)", cost: U, budgetKill: U, cancelProcessGroup: U, sandboxFlags: U, receipt: U, resume: U, guardScan: U, killBlocking: U, source: "providers/opencode.sh (PROVIDER_AUTONOMOUS_FLAG); other fields not confirmed" }
|
|
9293
|
+
];
|
|
9398
9294
|
});
|
|
9399
9295
|
|
|
9400
9296
|
// ../../loki-ts/src/engine10/session.ts
|
|
9401
9297
|
var STDERR_TAIL_BYTES, ADVISOR_MARKED_STAGES;
|
|
9402
9298
|
var init_session = __esm(() => {
|
|
9299
|
+
init_run_pid();
|
|
9403
9300
|
init_provider_cost();
|
|
9404
9301
|
init_model_routes();
|
|
9405
9302
|
init_cost();
|
|
@@ -9421,7 +9318,7 @@ var init_model_rank = __esm(() => {
|
|
|
9421
9318
|
});
|
|
9422
9319
|
|
|
9423
9320
|
// ../../loki-ts/src/engine10/sizing.ts
|
|
9424
|
-
import { existsSync as
|
|
9321
|
+
import { existsSync as existsSync18, readFileSync as readFileSync23 } from "fs";
|
|
9425
9322
|
import { basename as basename10 } from "path";
|
|
9426
9323
|
function namedFiles(task, map) {
|
|
9427
9324
|
return (map?.files ?? []).filter((f) => task.toLowerCase().includes(basename10(f).toLowerCase()));
|
|
@@ -9440,10 +9337,10 @@ function sizeTask(task, map, tests) {
|
|
|
9440
9337
|
return why.length ? { size: "normal", reasons: why } : { size: "small", reasons: [`task ${task.length} chars, names ${named} files`] };
|
|
9441
9338
|
}
|
|
9442
9339
|
function loadRepoMap(repomapRef) {
|
|
9443
|
-
if (!repomapRef || !
|
|
9340
|
+
if (!repomapRef || !existsSync18(repomapRef))
|
|
9444
9341
|
return null;
|
|
9445
9342
|
try {
|
|
9446
|
-
return JSON.parse(
|
|
9343
|
+
return JSON.parse(readFileSync23(repomapRef, "utf8"));
|
|
9447
9344
|
} catch {
|
|
9448
9345
|
return null;
|
|
9449
9346
|
}
|
|
@@ -9474,8 +9371,8 @@ var init_sizing = __esm(() => {
|
|
|
9474
9371
|
});
|
|
9475
9372
|
|
|
9476
9373
|
// ../../loki-ts/src/project_model/schema.ts
|
|
9477
|
-
import { existsSync as
|
|
9478
|
-
import { isAbsolute as isAbsolute3, join as
|
|
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";
|
|
9479
9376
|
function safeRel(p) {
|
|
9480
9377
|
const n2 = normalize(p.trim()).split(sep9).join("/").replace(/\/$/, "");
|
|
9481
9378
|
if (n2 === "" || isAbsolute3(n2) || n2 === ".." || n2.startsWith("../"))
|
|
@@ -9484,7 +9381,7 @@ function safeRel(p) {
|
|
|
9484
9381
|
}
|
|
9485
9382
|
function insideRepo(repoDir, rel) {
|
|
9486
9383
|
try {
|
|
9487
|
-
const real2 = realpathSync14(
|
|
9384
|
+
const real2 = realpathSync14(join32(repoDir, rel));
|
|
9488
9385
|
const base = realpathSync14(repoDir);
|
|
9489
9386
|
return real2 === base || real2.startsWith(base + sep9);
|
|
9490
9387
|
} catch {
|
|
@@ -9497,8 +9394,8 @@ function checkCitation(repoDir, raw2) {
|
|
|
9497
9394
|
const rel = safeRel(raw2.replace(/:\d+(?:-\d+)?$/, ""));
|
|
9498
9395
|
if (rel === null)
|
|
9499
9396
|
return `citation "${raw2}" is not a repo-relative path`;
|
|
9500
|
-
const abs =
|
|
9501
|
-
if (!
|
|
9397
|
+
const abs = join32(repoDir, rel);
|
|
9398
|
+
if (!existsSync19(abs) || !statSync14(abs).isFile() || !insideRepo(repoDir, rel))
|
|
9502
9399
|
return `citation "${raw2}" names a file that does not exist`;
|
|
9503
9400
|
return null;
|
|
9504
9401
|
}
|
|
@@ -9509,7 +9406,7 @@ function checkFingerprint(repoDir, raw2) {
|
|
|
9509
9406
|
if (rel === null)
|
|
9510
9407
|
return `fingerprint "${raw2}" is not a repo-relative path`;
|
|
9511
9408
|
try {
|
|
9512
|
-
if (!
|
|
9409
|
+
if (!lstatSync6(join32(repoDir, rel)).isFile() || !insideRepo(repoDir, rel))
|
|
9513
9410
|
return `fingerprint "${raw2}" is not a regular file inside the repo`;
|
|
9514
9411
|
} catch {
|
|
9515
9412
|
return `fingerprint "${raw2}" names a file that does not exist`;
|
|
@@ -9537,8 +9434,8 @@ function checkDir(repoDir, v, where, errs) {
|
|
|
9537
9434
|
errs.push(`${where}: must be a repo-relative directory ("." for the repo root)`);
|
|
9538
9435
|
return ".";
|
|
9539
9436
|
}
|
|
9540
|
-
const abs =
|
|
9541
|
-
if (!
|
|
9437
|
+
const abs = join32(repoDir, rel);
|
|
9438
|
+
if (!existsSync19(abs) || !statSync14(abs).isDirectory() || !insideRepo(repoDir, rel))
|
|
9542
9439
|
errs.push(`${where}: directory "${rel}" does not exist`);
|
|
9543
9440
|
return rel;
|
|
9544
9441
|
}
|
|
@@ -9640,9 +9537,9 @@ var init_schema = __esm(() => {
|
|
|
9640
9537
|
});
|
|
9641
9538
|
|
|
9642
9539
|
// ../../loki-ts/src/project_model/gather.ts
|
|
9643
|
-
import { createHash as
|
|
9644
|
-
import { closeSync as
|
|
9645
|
-
import { dirname as dirname11, join as
|
|
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";
|
|
9646
9543
|
function listShallow(repoDir) {
|
|
9647
9544
|
try {
|
|
9648
9545
|
const out2 = safeGit(repoDir, ["ls-files", "-z"], { maxBuffer: 256 * 1024 * 1024 });
|
|
@@ -9655,7 +9552,7 @@ function shallowDirs(repoDir) {
|
|
|
9655
9552
|
return [...new Set(listShallow(repoDir).map((p) => dirname11(p)))].sort();
|
|
9656
9553
|
}
|
|
9657
9554
|
function computeKey(repoDir, fingerprintFiles, dirs, committedHash = null) {
|
|
9658
|
-
const h =
|
|
9555
|
+
const h = createHash8("sha256");
|
|
9659
9556
|
h.update(`${MODEL_REV}
|
|
9660
9557
|
`);
|
|
9661
9558
|
if (committedHash !== null)
|
|
@@ -9668,9 +9565,9 @@ function computeKey(repoDir, fingerprintFiles, dirs, committedHash = null) {
|
|
|
9668
9565
|
if (checkFingerprint(repoDir, f) !== null)
|
|
9669
9566
|
h.update("invalid");
|
|
9670
9567
|
else {
|
|
9671
|
-
const abs =
|
|
9672
|
-
const size =
|
|
9673
|
-
h.update(size <= GATHER_CAPS.maxHashBytes ?
|
|
9568
|
+
const abs = join33(repoDir, f);
|
|
9569
|
+
const size = lstatSync7(abs).size;
|
|
9570
|
+
h.update(size <= GATHER_CAPS.maxHashBytes ? readFileSync24(abs) : `too-large:${size}`);
|
|
9674
9571
|
}
|
|
9675
9572
|
} catch {
|
|
9676
9573
|
h.update("missing");
|
|
@@ -9695,7 +9592,7 @@ function committedModelHash(raw2) {
|
|
|
9695
9592
|
if (typeof raw2 !== "object" || raw2 === null || Array.isArray(raw2))
|
|
9696
9593
|
return null;
|
|
9697
9594
|
const { key: _key, expiresAt: _exp, ...rest } = raw2;
|
|
9698
|
-
return
|
|
9595
|
+
return createHash8("sha256").update(JSON.stringify(rest)).digest("hex");
|
|
9699
9596
|
}
|
|
9700
9597
|
var GATHER_CAPS, depthOf = (p) => p.split("/").length, MODEL_REV = "model-rev:2";
|
|
9701
9598
|
var init_gather = __esm(() => {
|
|
@@ -9713,14 +9610,14 @@ var init_gather = __esm(() => {
|
|
|
9713
9610
|
});
|
|
9714
9611
|
|
|
9715
9612
|
// ../../loki-ts/src/project_model/discover.ts
|
|
9716
|
-
import { existsSync as
|
|
9717
|
-
import { join as
|
|
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";
|
|
9718
9615
|
function projectModelEnabled(env = process.env) {
|
|
9719
9616
|
return env["LOKI_E10_PROJECT_MODEL"] !== "0";
|
|
9720
9617
|
}
|
|
9721
9618
|
function loadCached(repoDir, now2 = Date.now()) {
|
|
9722
9619
|
try {
|
|
9723
|
-
return parseCached(repoDir, JSON.parse(
|
|
9620
|
+
return parseCached(repoDir, JSON.parse(readFileSync25(join34(repoDir, PROJECT_FILE), "utf8")), now2);
|
|
9724
9621
|
} catch {
|
|
9725
9622
|
return null;
|
|
9726
9623
|
}
|
|
@@ -9735,7 +9632,7 @@ function loadCommitted(repoDir) {
|
|
|
9735
9632
|
};
|
|
9736
9633
|
let raw2;
|
|
9737
9634
|
try {
|
|
9738
|
-
raw2 = JSON.parse(
|
|
9635
|
+
raw2 = JSON.parse(readFileSync25(join34(repoDir, PROJECT_FILE), "utf8"));
|
|
9739
9636
|
} catch (e) {
|
|
9740
9637
|
return ignore(`unreadable JSON (${e instanceof Error ? e.message : String(e)})`);
|
|
9741
9638
|
}
|
|
@@ -9808,7 +9705,7 @@ var clean2 = (p) => typeof p === "string" ? posix.normalize(p.replace(/\\/g, "/"
|
|
|
9808
9705
|
var init_api = () => {};
|
|
9809
9706
|
|
|
9810
9707
|
// ../../loki-ts/src/project_model/resolve.ts
|
|
9811
|
-
import { join as
|
|
9708
|
+
import { join as join35 } from "path";
|
|
9812
9709
|
function loadProjectApi(repoDir, env = process.env) {
|
|
9813
9710
|
if (!projectModelEnabled(env))
|
|
9814
9711
|
return null;
|
|
@@ -9830,7 +9727,7 @@ function siteFor(api, repoDir, file) {
|
|
|
9830
9727
|
const rel = api.relativeToRoot(file);
|
|
9831
9728
|
if (!pkg || pkg.root === "." || rel === null)
|
|
9832
9729
|
return null;
|
|
9833
|
-
return { pkg, root: pkg.root, cwd:
|
|
9730
|
+
return { pkg, root: pkg.root, cwd: join35(repoDir, pkg.root), file: rel };
|
|
9834
9731
|
}
|
|
9835
9732
|
function groupByPackage(api, repoDir, files) {
|
|
9836
9733
|
const out2 = new Map;
|
|
@@ -9863,9 +9760,9 @@ var init_scope = __esm(() => {
|
|
|
9863
9760
|
|
|
9864
9761
|
// ../../loki-ts/src/e10ext/assert_delta.ts
|
|
9865
9762
|
import { execFileSync as execFileSync5 } from "child_process";
|
|
9866
|
-
import { mkdtempSync as mkdtempSync4, readFileSync as
|
|
9763
|
+
import { mkdtempSync as mkdtempSync4, readFileSync as readFileSync26, rmSync as rmSync8 } from "fs";
|
|
9867
9764
|
import { tmpdir as tmpdir6 } from "os";
|
|
9868
|
-
import { join as
|
|
9765
|
+
import { join as join36 } from "path";
|
|
9869
9766
|
import { basename as basename11 } from "path";
|
|
9870
9767
|
function classifyAssertDelta(inp) {
|
|
9871
9768
|
const f = basename11(inp.path);
|
|
@@ -9875,7 +9772,7 @@ function classifyAssertDelta(inp) {
|
|
|
9875
9772
|
return WEAKENED;
|
|
9876
9773
|
let cwd = "";
|
|
9877
9774
|
try {
|
|
9878
|
-
cwd = mkdtempSync4(
|
|
9775
|
+
cwd = mkdtempSync4(join36(tmpdir6(), "loki-ad-"));
|
|
9879
9776
|
const raw2 = execFileSync5("python3", ["-I", "-c", PY], {
|
|
9880
9777
|
input: JSON.stringify({ path: inp.path, base: inp.base, head: inp.head, task: inp.task }),
|
|
9881
9778
|
cwd,
|
|
@@ -9898,7 +9795,7 @@ function classifyAssertDelta(inp) {
|
|
|
9898
9795
|
return WEAKENED;
|
|
9899
9796
|
} finally {
|
|
9900
9797
|
if (cwd)
|
|
9901
|
-
|
|
9798
|
+
rmSync8(cwd, { recursive: true, force: true });
|
|
9902
9799
|
}
|
|
9903
9800
|
}
|
|
9904
9801
|
function assertDeltaNotes(repoDir, baseSha, headRef, path, task, baseCounts, headCounts) {
|
|
@@ -9906,7 +9803,7 @@ function assertDeltaNotes(repoDir, baseSha, headRef, path, task, baseCounts, hea
|
|
|
9906
9803
|
return null;
|
|
9907
9804
|
try {
|
|
9908
9805
|
const show = (ref) => safeGit(repoDir, ["show", `${ref}:${path}`]);
|
|
9909
|
-
const head = headRef ? show(headRef) :
|
|
9806
|
+
const head = headRef ? show(headRef) : readFileSync26(join36(repoDir, path), "utf8");
|
|
9910
9807
|
const r = classifyTestEdit(path, show(baseSha), head, task, baseCounts, headCounts);
|
|
9911
9808
|
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;
|
|
9912
9809
|
} catch {
|
|
@@ -10129,8 +10026,39 @@ var init_runner_errors = __esm(() => {
|
|
|
10129
10026
|
];
|
|
10130
10027
|
});
|
|
10131
10028
|
|
|
10029
|
+
// ../../loki-ts/src/engine10/testmap.ts
|
|
10030
|
+
import { basename as basename12, extname, join as join37, relative as relative3, sep as sep10 } from "path";
|
|
10031
|
+
function isTestFile(rel) {
|
|
10032
|
+
const name = basename12(rel);
|
|
10033
|
+
return JS_TEST_RE.test(name) || PY_TEST_RE.test(name) || GO_TEST_RE.test(name);
|
|
10034
|
+
}
|
|
10035
|
+
var SKIP_DIRS, JS_TEST_RE, PY_TEST_RE, GO_TEST_RE;
|
|
10036
|
+
var init_testmap = __esm(() => {
|
|
10037
|
+
init_resolve();
|
|
10038
|
+
init_scope();
|
|
10039
|
+
SKIP_DIRS = new Set([
|
|
10040
|
+
".git",
|
|
10041
|
+
"node_modules",
|
|
10042
|
+
"dist",
|
|
10043
|
+
"build",
|
|
10044
|
+
"target",
|
|
10045
|
+
"coverage",
|
|
10046
|
+
".venv",
|
|
10047
|
+
"venv",
|
|
10048
|
+
"__pycache__",
|
|
10049
|
+
".tox",
|
|
10050
|
+
".pytest_cache",
|
|
10051
|
+
".loki",
|
|
10052
|
+
".next"
|
|
10053
|
+
]);
|
|
10054
|
+
JS_TEST_RE = /\.(test|spec)\.[cm]?[jt]sx?$/;
|
|
10055
|
+
PY_TEST_RE = /^(test_.+|.+_test)\.py$/;
|
|
10056
|
+
GO_TEST_RE = /_test\.go$/;
|
|
10057
|
+
});
|
|
10058
|
+
|
|
10132
10059
|
// ../../loki-ts/src/util/check_result.ts
|
|
10133
|
-
import {
|
|
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";
|
|
10134
10062
|
function plainTestEnv(base = process.env) {
|
|
10135
10063
|
const e = {};
|
|
10136
10064
|
for (const [k, v] of Object.entries(tokenFreeEnv(base)))
|
|
@@ -10148,7 +10076,7 @@ function ran(raw2, path, ok) {
|
|
|
10148
10076
|
if (blk) {
|
|
10149
10077
|
const c = n2(blk, /(?:#|\u2139) pass (\d+)/) + n2(blk, /(?:#|\u2139) fail (\d+)/);
|
|
10150
10078
|
const nm = out2.match(/^(?:ok \d+ - |\u2714 )(\S+\.[cm]?[jt]s)(?: \(|$)/m)?.[1];
|
|
10151
|
-
return c === 1 && nm && (!path ||
|
|
10079
|
+
return c === 1 && nm && (!path || basename13(nm) === basename13(path)) ? 0 : c;
|
|
10152
10080
|
}
|
|
10153
10081
|
const cg = cargoTrailer(out2);
|
|
10154
10082
|
if (cg.length)
|
|
@@ -10239,16 +10167,16 @@ function goRunner(cmd, args) {
|
|
|
10239
10167
|
let k = 0;
|
|
10240
10168
|
for (;k < t.length; k++) {
|
|
10241
10169
|
const x = t[k];
|
|
10242
|
-
if (/^[A-Za-z_]\w*=/.test(x) || /^(?:env|command|exec|time|nohup)$/.test(
|
|
10170
|
+
if (/^[A-Za-z_]\w*=/.test(x) || /^(?:env|command|exec|time|nohup)$/.test(basename13(x)))
|
|
10243
10171
|
continue;
|
|
10244
|
-
if (/^-/.test(x) && k > 0 &&
|
|
10172
|
+
if (/^-/.test(x) && k > 0 && basename13(t[k - 1]) === "env") {
|
|
10245
10173
|
if (x === "-u" || x === "-C" || x === "-S")
|
|
10246
10174
|
k++;
|
|
10247
10175
|
continue;
|
|
10248
10176
|
}
|
|
10249
10177
|
break;
|
|
10250
10178
|
}
|
|
10251
|
-
if (t[k] !== undefined &&
|
|
10179
|
+
if (t[k] !== undefined && basename13(t[k]) === "go")
|
|
10252
10180
|
return { runner: "go" };
|
|
10253
10181
|
}
|
|
10254
10182
|
return {};
|
|
@@ -10309,17 +10237,17 @@ function classifyCheck(i) {
|
|
|
10309
10237
|
return { result: "not_run", reason: "timed out or aborted" };
|
|
10310
10238
|
if (i.kind === "static")
|
|
10311
10239
|
return { result: i.ok ? "pass" : "fail" };
|
|
10312
|
-
const
|
|
10240
|
+
const text2 = stripAnsi(i.out);
|
|
10313
10241
|
if (i.runner === "go") {
|
|
10314
10242
|
if (i.ok)
|
|
10315
10243
|
return { result: "not_run", reason: GO_EXIT0_REASON };
|
|
10316
|
-
const j = goJson(
|
|
10244
|
+
const j = goJson(text2);
|
|
10317
10245
|
if (j)
|
|
10318
10246
|
return j.failed ? { result: "fail" } : { result: "not_run", reason: UNCONFIRMED_REASON };
|
|
10319
|
-
return FAIL_EVIDENCE.test(
|
|
10247
|
+
return FAIL_EVIDENCE.test(text2) || GO_FAIL_EVIDENCE.test(text2) ? { result: "fail" } : { result: "not_run", reason: UNCONFIRMED_REASON };
|
|
10320
10248
|
}
|
|
10321
10249
|
const n2 = testCount(i.out, i.path, i.ok);
|
|
10322
|
-
if (!i.ok && FAIL_EVIDENCE.test(
|
|
10250
|
+
if (!i.ok && FAIL_EVIDENCE.test(text2))
|
|
10323
10251
|
return { result: "fail", ...n2 !== null ? { n: n2 } : {} };
|
|
10324
10252
|
if (n2 === 0)
|
|
10325
10253
|
return { result: "not_run", n: 0, reason: `${NO_TESTS_REASON} (ran 0 tests, empty or all skipped)` };
|
|
@@ -10329,31 +10257,163 @@ function classifyCheck(i) {
|
|
|
10329
10257
|
return { result: "not_run", reason: UNMEASURED_REASON };
|
|
10330
10258
|
return { result: "pass", n: n2 };
|
|
10331
10259
|
}
|
|
10260
|
+
function summaryOf(a) {
|
|
10261
|
+
const why = a.tier === a.computed ? a.reasons.slice(0, 2).join("; ") : `computed ${a.computed}; override ${a.override?.from} to ${a.override?.to}: ${a.override?.reason}`;
|
|
10262
|
+
const def = a.checks_deferred;
|
|
10263
|
+
return `risk: ${a.tier} (${why}), ${def.length ? def.join(", ") : "no deeper checks"} not run`;
|
|
10264
|
+
}
|
|
10265
|
+
function markExecuted(a, executed) {
|
|
10266
|
+
const checks_deferred = deferredOf(a.tier, a.checks_selected, executed);
|
|
10267
|
+
return { ...a, checks_deferred, summary: summaryOf({ ...a, checks_deferred }) };
|
|
10268
|
+
}
|
|
10269
|
+
function classifyRisk(files, env = process.env) {
|
|
10270
|
+
const lines = files.reduce((n2, f) => n2 + f.added + f.deleted, 0);
|
|
10271
|
+
const sensitive = files.flatMap((f) => {
|
|
10272
|
+
const hit = SENSITIVE.find(([, re]) => re.test(f.path));
|
|
10273
|
+
return hit && !isTestFile(f.path) ? [`${f.path} (${hit[0]})`] : [];
|
|
10274
|
+
});
|
|
10275
|
+
const testFiles = files.filter((f) => isTestFile(f.path));
|
|
10276
|
+
const testsDeleted = testFiles.filter((f) => f.status === "deleted").map((f) => f.path);
|
|
10277
|
+
const testsModified = [...testFiles.filter((f) => f.status !== "added" && f.status !== "deleted").map((f) => f.path), ...files.filter((f) => TEST_CFG.test(f.path)).map((f) => f.path)];
|
|
10278
|
+
const api = files.filter((f) => API.test(f.path) && !isTestFile(f.path)).map((f) => f.path);
|
|
10279
|
+
const high = [], med = [];
|
|
10280
|
+
if (sensitive.length)
|
|
10281
|
+
high.push(`sensitive paths: ${sensitive.slice(0, 5).join(", ")}${sensitive.length > 5 ? ` (+${sensitive.length - 5})` : ""}`);
|
|
10282
|
+
if (testsDeleted.length)
|
|
10283
|
+
high.push(`test files deleted: ${testsDeleted.join(", ")}`);
|
|
10284
|
+
if (api.length)
|
|
10285
|
+
high.push(`public API surface: ${api.slice(0, 3).join(", ")}`);
|
|
10286
|
+
if (files.length >= FILES_HIGH)
|
|
10287
|
+
high.push(`${files.length} files >= ${FILES_HIGH}`);
|
|
10288
|
+
if (lines >= LINES_HIGH)
|
|
10289
|
+
high.push(`${lines} changed lines >= ${LINES_HIGH}`);
|
|
10290
|
+
if (testsModified.length)
|
|
10291
|
+
med.push(`existing tests or test config modified: ${testsModified.slice(0, 3).join(", ")}`);
|
|
10292
|
+
if (files.length >= FILES_MEDIUM)
|
|
10293
|
+
med.push(`${files.length} files >= ${FILES_MEDIUM}`);
|
|
10294
|
+
if (lines >= LINES_MEDIUM)
|
|
10295
|
+
med.push(`${lines} changed lines >= ${LINES_MEDIUM}`);
|
|
10296
|
+
const computed = high.length ? "high" : med.length ? "medium" : "low";
|
|
10297
|
+
const reasons = high.length ? [...high, ...med] : med.length ? med : [`${files.length} file${files.length === 1 ? "" : "s"}, no sensitive paths`];
|
|
10298
|
+
let tier = computed, override, ignored;
|
|
10299
|
+
const asked = (env["LOKI_RISK_TIER"] ?? "").trim().toLowerCase();
|
|
10300
|
+
if (asked in RANK2 && asked !== computed) {
|
|
10301
|
+
const reason = (env["LOKI_RISK_OVERRIDE_REASON"] ?? "").trim();
|
|
10302
|
+
if (RANK2[asked] > RANK2[computed]) {
|
|
10303
|
+
tier = asked;
|
|
10304
|
+
override = { from: computed, to: asked, reason: reason || "raised by LOKI_RISK_TIER" };
|
|
10305
|
+
} else if (reason) {
|
|
10306
|
+
tier = asked;
|
|
10307
|
+
override = { from: computed, to: asked, reason };
|
|
10308
|
+
} else
|
|
10309
|
+
ignored = `LOKI_RISK_TIER=${asked} ignored: lowering from ${computed} needs LOKI_RISK_OVERRIDE_REASON`;
|
|
10310
|
+
}
|
|
10311
|
+
const plan = PLAN[tier], checks_deferred = deferredOf(tier, plan.selected, []);
|
|
10312
|
+
const summary = summaryOf({ tier, computed, reasons, ...override ? { override } : {}, checks_deferred });
|
|
10313
|
+
return {
|
|
10314
|
+
version: 1,
|
|
10315
|
+
tier,
|
|
10316
|
+
computed,
|
|
10317
|
+
reasons,
|
|
10318
|
+
stats: { files: files.length, lines, sensitive, tests_modified: testsModified, tests_deleted: testsDeleted, api },
|
|
10319
|
+
checks_selected: plan.selected,
|
|
10320
|
+
checks_deferred,
|
|
10321
|
+
...override ? { override } : {},
|
|
10322
|
+
...ignored ? { override_ignored: ignored } : {},
|
|
10323
|
+
summary
|
|
10324
|
+
};
|
|
10325
|
+
}
|
|
10326
|
+
function changedFiles(repoDir, baseSha) {
|
|
10327
|
+
const run = (args) => safeGit(repoDir, args).split(`
|
|
10328
|
+
`).map((l) => l.trim()).filter(Boolean);
|
|
10329
|
+
const tracked = run(["diff", "--name-only", baseSha]);
|
|
10330
|
+
const untracked = run(["ls-files", "--others", "--exclude-standard"]);
|
|
10331
|
+
return [...new Set([...tracked, ...untracked])].filter((f) => !f.startsWith(".loki/"));
|
|
10332
|
+
}
|
|
10333
|
+
function diffFiles(repoDir, baseSha) {
|
|
10334
|
+
const paths = changedFiles(repoDir, baseSha), stat = new Map;
|
|
10335
|
+
for (const l of safeGit(repoDir, ["diff", "--numstat", baseSha]).split(`
|
|
10336
|
+
`)) {
|
|
10337
|
+
const [a, d, ...p] = l.split("\t"), path = p.join("\t");
|
|
10338
|
+
if (path)
|
|
10339
|
+
stat.set(path, { path, added: Number(a) || 0, deleted: Number(d) || 0 });
|
|
10340
|
+
}
|
|
10341
|
+
for (const l of safeGit(repoDir, ["diff", "--name-status", baseSha]).split(`
|
|
10342
|
+
`)) {
|
|
10343
|
+
const [s, ...p] = l.split("\t"), f = stat.get(p[p.length - 1] ?? "");
|
|
10344
|
+
if (f)
|
|
10345
|
+
f.status = s?.startsWith("A") ? "added" : s?.startsWith("D") ? "deleted" : "modified";
|
|
10346
|
+
}
|
|
10347
|
+
return paths.map((path) => {
|
|
10348
|
+
const f = stat.get(path);
|
|
10349
|
+
if (f)
|
|
10350
|
+
return f;
|
|
10351
|
+
let n2 = 0;
|
|
10352
|
+
try {
|
|
10353
|
+
n2 = readFileSync27(join38(repoDir, path), "utf8").split(`
|
|
10354
|
+
`).length;
|
|
10355
|
+
} catch {}
|
|
10356
|
+
return { path, added: n2, deleted: 0, status: "added" };
|
|
10357
|
+
});
|
|
10358
|
+
}
|
|
10359
|
+
function recordRisk(repoDir, a) {
|
|
10360
|
+
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) + `
|
|
10364
|
+
`);
|
|
10365
|
+
renameSync5(`${p}.tmp`, p);
|
|
10366
|
+
return p;
|
|
10367
|
+
} catch {
|
|
10368
|
+
return null;
|
|
10369
|
+
}
|
|
10370
|
+
}
|
|
10332
10371
|
var NO_TESTS_REASON = "no tests executed", GO_EXIT0_REASON = "test count could not be confirmed: go test output is produced by the code under test and cannot confirm execution (a Go exit 0 is never a pass)", UNCONFIRMED_REASON = "test count could not be confirmed", FAIL_EVIDENCE, GO_FAIL_EVIDENCE, UNMEASURED_REASON = "executed count unmeasured (Loki could not parse the runner summary, harness-owned)", stripAnsi = (s) => s.replace(/\u001b\][^\u0007\u001b]*(?:\u0007|\u001b\\)/g, "").replace(/(?:\u001b\[|\u009b)[0-9;?]*[ -\/]*[@-~]/g, "").replace(/\r\n?/g, `
|
|
10333
10372
|
`), tailLines = (out2) => out2.split(`
|
|
10334
|
-
`).filter((x) => x.trim()).slice(-12), GO_PKG_RE, isExecutedProof = (c) => c.result === "pass" && typeof c.n === "number" && c.n > 0, hasExecutedProof = (checks) => checks.some(isExecutedProof);
|
|
10373
|
+
`).filter((x) => x.trim()).slice(-12), GO_PKG_RE, isExecutedProof = (c) => c.result === "pass" && typeof c.n === "number" && c.n > 0, hasExecutedProof = (checks) => checks.some(isExecutedProof), SENSITIVE, API, TEST_CFG, RANK2, FILES_MEDIUM = 4, FILES_HIGH = 15, LINES_MEDIUM = 100, LINES_HIGH = 500, RAN_ALWAYS, PLAN, LOW_SKIPPED, deferredOf = (tier, selected, executed) => (tier === "low" ? LOW_SKIPPED : selected).filter((c) => !RAN_ALWAYS.includes(c) && !executed.includes(c));
|
|
10335
10374
|
var init_check_result = __esm(() => {
|
|
10375
|
+
init_testmap();
|
|
10336
10376
|
init_safe_git();
|
|
10337
10377
|
FAIL_EVIDENCE = /\[(?:build|setup) failed\]|^--- FAIL|^[\w./-]+\.go:\d+:\d+: \S|\b[1-9]\d* (?:failed|errors?)\b/m;
|
|
10338
10378
|
GO_FAIL_EVIDENCE = /^FAIL\t\S+\t[\d.]+s$|^panic: /m;
|
|
10339
10379
|
GO_PKG_RE = /^(?:ok|FAIL|\?)\s+\S+\s+(?:\(cached\)|[\d.]+s\b|\[(?:no test files|build failed|setup failed)\])/;
|
|
10380
|
+
SENSITIVE = [
|
|
10381
|
+
["auth", /(^|\/)(auth|oauth|sso|session|login|permission|rbac|acl)[\w.-]*(\/|\.|$)/i],
|
|
10382
|
+
["crypto", /(^|\/)(crypt|crypto|jwt|signing|keys?|secrets?|tokens?|credential|password)[\w.-]*(\/|\.|$)/i],
|
|
10383
|
+
["payments", /(^|\/)(payments?|billing|checkout|stripe|invoice)[\w.-]*(\/|\.|$)/i],
|
|
10384
|
+
["migration", /(^|\/)(migrations?|alembic)\/|\.sql$/i],
|
|
10385
|
+
["ci", /(^|\/)(\.github\/(workflows|actions)|\.gitlab-ci\.yml|\.circleci|Jenkinsfile|azure-pipelines)/i],
|
|
10386
|
+
["lockfile", /(^|\/)(package-lock\.json|pnpm-lock\.yaml|bun\.lockb?|go\.sum|[\w-]+\.lock)$/],
|
|
10387
|
+
["infra", /(^|\/)(Dockerfile[\w.-]*|docker-compose[\w.-]*\.ya?ml|terraform|helm|k8s|kubernetes|infra|deploy)(\/|\.|$)|\.tf$/i],
|
|
10388
|
+
["env", /(^|\/)\.env[\w.]*$/]
|
|
10389
|
+
];
|
|
10390
|
+
API = /(^|\/)(openapi|swagger)[\w.-]*\.(ya?ml|json)$|\.proto$|\.d\.ts$|(^|\/)(api|routes|public)\/|(^|\/)index\.(ts|js|py)$/i;
|
|
10391
|
+
TEST_CFG = /(^|\/)(conftest\.py|\.?pytest\.(ini|toml)|tox\.ini|jest\.config\.[\w.]+|vitest\.config\.[\w.]+)$/;
|
|
10392
|
+
RANK2 = { low: 0, medium: 1, high: 2 };
|
|
10393
|
+
RAN_ALWAYS = ["targeted tests", "lint of changed files"];
|
|
10394
|
+
PLAN = {
|
|
10395
|
+
low: { selected: [...RAN_ALWAYS] },
|
|
10396
|
+
medium: { selected: [...RAN_ALWAYS, "full test command", "one reviewer"] },
|
|
10397
|
+
high: { selected: [...RAN_ALWAYS, "full test command", "full suite", "council", "security scan"] }
|
|
10398
|
+
};
|
|
10399
|
+
LOW_SKIPPED = ["full test command", "reviewer", "council", "security scan"];
|
|
10340
10400
|
});
|
|
10341
10401
|
|
|
10342
10402
|
// ../../loki-ts/src/runner/load_owner.ts
|
|
10343
10403
|
import { spawnSync as spawnSync3 } from "child_process";
|
|
10344
|
-
import { mkdtempSync as mkdtempSync5, realpathSync as realpathSync15, rmSync as
|
|
10404
|
+
import { mkdtempSync as mkdtempSync5, realpathSync as realpathSync15, rmSync as rmSync9 } from "fs";
|
|
10345
10405
|
import { tmpdir as tmpdir7 } from "os";
|
|
10346
|
-
import { basename as
|
|
10406
|
+
import { basename as basename14, extname as extname2, join as join39 } from "path";
|
|
10347
10407
|
function changedFilesNamed(out2, repoDir, changed) {
|
|
10348
10408
|
return changed.filter((f) => {
|
|
10349
|
-
if (out2.includes(f) || out2.includes(
|
|
10409
|
+
if (out2.includes(f) || out2.includes(join39(repoDir, f)))
|
|
10350
10410
|
return true;
|
|
10351
10411
|
const segs = f.split("/");
|
|
10352
10412
|
if (suffixes(segs, 2).some((x) => x !== f && bounded(out2, x, "(?![\\w-])")))
|
|
10353
10413
|
return true;
|
|
10354
|
-
if (
|
|
10414
|
+
if (extname2(f) === ".go" && segs.length > 1 && suffixes(segs.slice(0, -1), 1).some((d) => new RegExp(`(?<=[\\w-])/${esc(d)}(?![\\w./-])`).test(out2)))
|
|
10355
10415
|
return true;
|
|
10356
|
-
const stem =
|
|
10416
|
+
const stem = basename14(f, extname2(f));
|
|
10357
10417
|
if (stem.length < 2 || stem === "index" || stem === "__init__")
|
|
10358
10418
|
return false;
|
|
10359
10419
|
return new RegExp(`(?<![\\w-])${esc(stem)}(?![\\w-])`).test(out2);
|
|
@@ -10457,7 +10517,7 @@ async function loadErrorIsHarnessOwned(i) {
|
|
|
10457
10517
|
return false;
|
|
10458
10518
|
if (i.args.some((a) => changed.includes(a.replace(/^\.\//, ""))))
|
|
10459
10519
|
return false;
|
|
10460
|
-
const dir = mkdtempSync5(
|
|
10520
|
+
const dir = mkdtempSync5(join39(tmpdir7(), "e10-loadown-"));
|
|
10461
10521
|
const git3 = (args) => {
|
|
10462
10522
|
safeGit(i.repoDir, args, { stdio: "ignore" });
|
|
10463
10523
|
};
|
|
@@ -10471,7 +10531,7 @@ async function loadErrorIsHarnessOwned(i) {
|
|
|
10471
10531
|
try {
|
|
10472
10532
|
git3(["worktree", "remove", "--force", dir]);
|
|
10473
10533
|
} catch {}
|
|
10474
|
-
|
|
10534
|
+
rmSync9(dir, { recursive: true, force: true });
|
|
10475
10535
|
try {
|
|
10476
10536
|
git3(["worktree", "prune"]);
|
|
10477
10537
|
} catch {}
|
|
@@ -10488,36 +10548,6 @@ var init_load_owner = __esm(() => {
|
|
|
10488
10548
|
init_check_result();
|
|
10489
10549
|
});
|
|
10490
10550
|
|
|
10491
|
-
// ../../loki-ts/src/engine10/testmap.ts
|
|
10492
|
-
import { basename as basename14, extname as extname2, join as join39, relative as relative3, sep as sep10 } from "path";
|
|
10493
|
-
function isTestFile(rel) {
|
|
10494
|
-
const name = basename14(rel);
|
|
10495
|
-
return JS_TEST_RE.test(name) || PY_TEST_RE.test(name) || GO_TEST_RE.test(name);
|
|
10496
|
-
}
|
|
10497
|
-
var SKIP_DIRS, JS_TEST_RE, PY_TEST_RE, GO_TEST_RE;
|
|
10498
|
-
var init_testmap = __esm(() => {
|
|
10499
|
-
init_resolve();
|
|
10500
|
-
init_scope();
|
|
10501
|
-
SKIP_DIRS = new Set([
|
|
10502
|
-
".git",
|
|
10503
|
-
"node_modules",
|
|
10504
|
-
"dist",
|
|
10505
|
-
"build",
|
|
10506
|
-
"target",
|
|
10507
|
-
"coverage",
|
|
10508
|
-
".venv",
|
|
10509
|
-
"venv",
|
|
10510
|
-
"__pycache__",
|
|
10511
|
-
".tox",
|
|
10512
|
-
".pytest_cache",
|
|
10513
|
-
".loki",
|
|
10514
|
-
".next"
|
|
10515
|
-
]);
|
|
10516
|
-
JS_TEST_RE = /\.(test|spec)\.[cm]?[jt]sx?$/;
|
|
10517
|
-
PY_TEST_RE = /^(test_.+|.+_test)\.py$/;
|
|
10518
|
-
GO_TEST_RE = /_test\.go$/;
|
|
10519
|
-
});
|
|
10520
|
-
|
|
10521
10551
|
// ../../loki-ts/src/util/wall_owned.ts
|
|
10522
10552
|
import { isAbsolute as isAbsolute4, join as join40, relative as relative4 } from "path";
|
|
10523
10553
|
function failingLocations(raw2, cwd, repoDir) {
|
|
@@ -10562,58 +10592,1120 @@ var init_wall_owned = __esm(() => {
|
|
|
10562
10592
|
ANSI2 = /\u001b\[[0-9;?]*[ -/]*[@-~]/g;
|
|
10563
10593
|
});
|
|
10564
10594
|
|
|
10565
|
-
// ../../loki-ts/src/
|
|
10566
|
-
|
|
10567
|
-
|
|
10568
|
-
|
|
10569
|
-
|
|
10570
|
-
|
|
10571
|
-
|
|
10572
|
-
|
|
10573
|
-
|
|
10574
|
-
|
|
10575
|
-
|
|
10576
|
-
|
|
10595
|
+
// ../../loki-ts/src/runner/state.ts
|
|
10596
|
+
var TERMINAL_STATUSES;
|
|
10597
|
+
var init_state = __esm(() => {
|
|
10598
|
+
init_paths();
|
|
10599
|
+
init_atomic();
|
|
10600
|
+
TERMINAL_STATUSES = new Set([
|
|
10601
|
+
"failed",
|
|
10602
|
+
"max_iterations_reached",
|
|
10603
|
+
"max_retries_exceeded",
|
|
10604
|
+
"exited",
|
|
10605
|
+
"council_approved",
|
|
10606
|
+
"council_force_approved",
|
|
10607
|
+
"completion_promise_fulfilled",
|
|
10608
|
+
"running"
|
|
10609
|
+
]);
|
|
10610
|
+
});
|
|
10611
|
+
|
|
10612
|
+
// ../../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";
|
|
10615
|
+
function loadProfile(repoDir) {
|
|
10616
|
+
try {
|
|
10617
|
+
const p = JSON.parse(readFileSync28(commandProfilePath(repoDir), "utf8"));
|
|
10618
|
+
return p?.version === 1 && Array.isArray(p.commands) && p.baseline ? p : null;
|
|
10619
|
+
} catch {
|
|
10620
|
+
return null;
|
|
10621
|
+
}
|
|
10622
|
+
}
|
|
10623
|
+
function attribute(repoDir, baseSha, failing) {
|
|
10624
|
+
const p = loadProfile(repoDir), none = { preExistingChecks: [], preExistingIds: [], introducedChecks: failing.map((f) => f.name), line: null };
|
|
10625
|
+
if (!p || p.revision !== baseSha || !p.baseline.attributable || p.baseline.failing_ids.length === 0 || failing.length === 0)
|
|
10626
|
+
return none;
|
|
10627
|
+
const base = new Set(p.baseline.failing_ids), pre = failing.filter((f) => f.eligible && f.ids?.length && f.ids.every((i) => base.has(i)));
|
|
10628
|
+
const introduced = failing.filter((f) => !pre.includes(f)).map((f) => f.name);
|
|
10629
|
+
if (pre.length === 0)
|
|
10630
|
+
return { ...none, line: `baseline: ${base.size} known failure(s) before this run; none of the failing checks match them, ${introduced.length} introduced (blocking)` };
|
|
10631
|
+
return {
|
|
10632
|
+
preExistingChecks: pre.map((f) => f.name),
|
|
10633
|
+
preExistingIds: pre.flatMap((f) => f.ids ?? []),
|
|
10634
|
+
introducedChecks: introduced,
|
|
10635
|
+
line: `baseline: ${pre.length} pre-existing failing check(s) (not blocking), ${introduced.length} introduced${introduced.length ? " (blocking)" : ""}`
|
|
10577
10636
|
};
|
|
10578
|
-
const venvEnv = process.env["VIRTUAL_ENV"];
|
|
10579
|
-
const project = [join41(repoDir, ".venv", "bin", bin), join41(repoDir, "venv", "bin", bin), ...venvEnv && inRepo(venvEnv) ? [join41(venvEnv, "bin", bin)] : []].find(existsSync22);
|
|
10580
|
-
return project ? [project, "project"] : [Bun.which(fallback) ? fallback : lastResort, "system"];
|
|
10581
10637
|
}
|
|
10582
|
-
function
|
|
10583
|
-
|
|
10584
|
-
|
|
10585
|
-
|
|
10586
|
-
|
|
10638
|
+
function attributeFullSuite(repoDir, baseSha, full, blocked, changed) {
|
|
10639
|
+
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);
|
|
10641
|
+
if (!bad.length || !p || p.revision !== baseSha || !p.baseline.attributable || !p.baseline.failing_ids.length)
|
|
10642
|
+
return none;
|
|
10643
|
+
if (bad.some((c) => norm3(c.cmd) !== norm3(p.baseline.command ?? "")))
|
|
10644
|
+
return { ...none, line: `baseline: ran "${p.baseline.command}", not the full suite command, ids not comparable, full suite failures not excused` };
|
|
10645
|
+
const block = new Set(blocked);
|
|
10646
|
+
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
|
+
}
|
|
10648
|
+
var commandProfilePath = (repoDir) => join41(repoDir, ".loki", "state", "repo-profile.json");
|
|
10649
|
+
var init_repo_profile = __esm(() => {
|
|
10650
|
+
init_failures();
|
|
10651
|
+
init_check_result();
|
|
10652
|
+
init_safe_git();
|
|
10653
|
+
init_paths();
|
|
10654
|
+
init_state();
|
|
10655
|
+
});
|
|
10656
|
+
|
|
10657
|
+
// ../../loki-ts/src/project_graph.ts
|
|
10658
|
+
import { createHash as createHash9 } from "crypto";
|
|
10659
|
+
import {
|
|
10660
|
+
existsSync as existsSync22,
|
|
10661
|
+
readdirSync as readdirSync12,
|
|
10662
|
+
readFileSync as readFileSync29,
|
|
10663
|
+
statSync as statSync15,
|
|
10664
|
+
writeFileSync as writeFileSync16,
|
|
10665
|
+
mkdirSync as mkdirSync13
|
|
10666
|
+
} from "fs";
|
|
10667
|
+
import { basename as basename16, dirname as dirname12, join as join42, resolve as resolve12 } from "path";
|
|
10668
|
+
function readManifest(path) {
|
|
10669
|
+
if (!existsSync22(path)) {
|
|
10670
|
+
return null;
|
|
10671
|
+
}
|
|
10672
|
+
let raw2;
|
|
10673
|
+
try {
|
|
10674
|
+
raw2 = readFileSync29(path, "utf8");
|
|
10675
|
+
} catch {
|
|
10676
|
+
return null;
|
|
10677
|
+
}
|
|
10678
|
+
let parsed;
|
|
10679
|
+
try {
|
|
10680
|
+
parsed = JSON.parse(raw2);
|
|
10681
|
+
} catch {
|
|
10682
|
+
return null;
|
|
10683
|
+
}
|
|
10684
|
+
if (typeof parsed !== "object" || parsed === null) {
|
|
10685
|
+
return null;
|
|
10686
|
+
}
|
|
10687
|
+
const obj = parsed;
|
|
10688
|
+
if (obj["schema_version"] !== SUPPORTED_SCHEMA_VERSION) {
|
|
10689
|
+
console.warn(`[loki:project_graph] unsupported schema_version in ${path}: ` + `${String(obj["schema_version"])} (expected ${SUPPORTED_SCHEMA_VERSION})`);
|
|
10690
|
+
return null;
|
|
10691
|
+
}
|
|
10692
|
+
const appId = obj["app_id"];
|
|
10693
|
+
if (typeof appId !== "string" || !APP_ID_REGEX.test(appId)) {
|
|
10694
|
+
console.warn(`[loki:project_graph] invalid app_id in ${path}: ${String(appId)}`);
|
|
10695
|
+
return null;
|
|
10696
|
+
}
|
|
10697
|
+
const members = Array.isArray(obj["members"]) ? obj["members"].filter((m) => typeof m === "string") : [];
|
|
10698
|
+
const sharedMemoryDir = typeof obj["shared_memory_dir"] === "string" ? obj["shared_memory_dir"] : undefined;
|
|
10699
|
+
return {
|
|
10700
|
+
schema_version: SUPPORTED_SCHEMA_VERSION,
|
|
10701
|
+
app_id: appId,
|
|
10702
|
+
members,
|
|
10703
|
+
shared_memory_dir: sharedMemoryDir
|
|
10704
|
+
};
|
|
10705
|
+
}
|
|
10706
|
+
function resolveMembers(rootDir, declared, selfBasename) {
|
|
10707
|
+
const out2 = [];
|
|
10708
|
+
const seen = new Set;
|
|
10709
|
+
let children;
|
|
10710
|
+
try {
|
|
10711
|
+
children = readdirSync12(rootDir);
|
|
10712
|
+
} catch {
|
|
10713
|
+
children = [];
|
|
10714
|
+
}
|
|
10715
|
+
const matchPattern = (pattern, name) => {
|
|
10716
|
+
if (!pattern.includes("*") && !pattern.includes("?")) {
|
|
10717
|
+
return pattern === name;
|
|
10718
|
+
}
|
|
10719
|
+
const re = new RegExp("^" + pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".") + "$");
|
|
10720
|
+
return re.test(name);
|
|
10721
|
+
};
|
|
10722
|
+
for (const name of declared) {
|
|
10723
|
+
for (const child of children) {
|
|
10724
|
+
if (!matchPattern(name, child))
|
|
10725
|
+
continue;
|
|
10726
|
+
const full = join42(rootDir, child);
|
|
10727
|
+
if (seen.has(full))
|
|
10728
|
+
continue;
|
|
10729
|
+
let st;
|
|
10730
|
+
try {
|
|
10731
|
+
st = statSync15(full);
|
|
10732
|
+
} catch {
|
|
10733
|
+
continue;
|
|
10734
|
+
}
|
|
10735
|
+
if (!st.isDirectory())
|
|
10736
|
+
continue;
|
|
10737
|
+
seen.add(full);
|
|
10738
|
+
out2.push(full);
|
|
10739
|
+
}
|
|
10740
|
+
}
|
|
10741
|
+
if (declared.length === 0) {
|
|
10742
|
+
for (const child of children) {
|
|
10743
|
+
const full = join42(rootDir, child);
|
|
10744
|
+
if (seen.has(full))
|
|
10745
|
+
continue;
|
|
10746
|
+
let st;
|
|
10747
|
+
try {
|
|
10748
|
+
st = statSync15(full);
|
|
10749
|
+
} catch {
|
|
10750
|
+
continue;
|
|
10751
|
+
}
|
|
10752
|
+
if (!st.isDirectory())
|
|
10753
|
+
continue;
|
|
10754
|
+
if (!existsSync22(join42(full, ".loki", "app.json")))
|
|
10755
|
+
continue;
|
|
10756
|
+
seen.add(full);
|
|
10757
|
+
out2.push(full);
|
|
10587
10758
|
}
|
|
10588
|
-
case "vitest":
|
|
10589
|
-
return ["npx", ["vitest", "run", t.path]];
|
|
10590
|
-
case "jest":
|
|
10591
|
-
return ["npx", ["jest", t.path]];
|
|
10592
|
-
case "bun":
|
|
10593
|
-
return ["bun", ["test", t.path]];
|
|
10594
|
-
case "node":
|
|
10595
|
-
return ["node", ["--test", `./${t.path}`]];
|
|
10596
|
-
case "npm":
|
|
10597
|
-
return ["npm", ["test", "--silent"]];
|
|
10598
|
-
case "go":
|
|
10599
|
-
return ["go", ["test", "-json", `./${dirname12(t.path)}`]];
|
|
10600
|
-
case "cargo":
|
|
10601
|
-
return ["cargo", ["test"]];
|
|
10602
10759
|
}
|
|
10760
|
+
return out2;
|
|
10603
10761
|
}
|
|
10604
|
-
function
|
|
10605
|
-
const
|
|
10606
|
-
const
|
|
10607
|
-
|
|
10762
|
+
function cacheKey(manifestPaths) {
|
|
10763
|
+
const parts = [];
|
|
10764
|
+
for (const p of manifestPaths.sort()) {
|
|
10765
|
+
let mtime = 0;
|
|
10766
|
+
try {
|
|
10767
|
+
mtime = statSync15(p).mtimeMs;
|
|
10768
|
+
} catch {}
|
|
10769
|
+
parts.push(`${p}:${mtime}`);
|
|
10770
|
+
}
|
|
10771
|
+
return createHash9("sha256").update(parts.join("|")).digest("hex");
|
|
10608
10772
|
}
|
|
10609
|
-
function
|
|
10610
|
-
|
|
10611
|
-
|
|
10612
|
-
|
|
10613
|
-
|
|
10614
|
-
|
|
10773
|
+
function writeCache(targetDir, key, result) {
|
|
10774
|
+
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");
|
|
10779
|
+
} catch {}
|
|
10615
10780
|
}
|
|
10616
|
-
function
|
|
10781
|
+
function discoverProjectGraph(targetDir) {
|
|
10782
|
+
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");
|
|
10786
|
+
const examined = [];
|
|
10787
|
+
let chosenManifest = null;
|
|
10788
|
+
let chosenRoot = "";
|
|
10789
|
+
if (existsSync22(parentManifest) && parentDir !== absTarget) {
|
|
10790
|
+
examined.push(parentManifest);
|
|
10791
|
+
chosenManifest = readManifest(parentManifest);
|
|
10792
|
+
chosenRoot = parentDir;
|
|
10793
|
+
}
|
|
10794
|
+
if (!chosenManifest && existsSync22(targetManifest)) {
|
|
10795
|
+
examined.push(targetManifest);
|
|
10796
|
+
chosenManifest = readManifest(targetManifest);
|
|
10797
|
+
chosenRoot = absTarget;
|
|
10798
|
+
}
|
|
10799
|
+
if (!chosenManifest)
|
|
10800
|
+
return null;
|
|
10801
|
+
const declared = chosenManifest.members ?? [];
|
|
10802
|
+
const members = resolveMembers(chosenRoot, declared, basename16(absTarget));
|
|
10803
|
+
const filtered = [];
|
|
10804
|
+
for (const m of members) {
|
|
10805
|
+
const sibManifestPath = join42(m, ".loki", "app.json");
|
|
10806
|
+
if (existsSync22(sibManifestPath)) {
|
|
10807
|
+
examined.push(sibManifestPath);
|
|
10808
|
+
const sibManifest = readManifest(sibManifestPath);
|
|
10809
|
+
if (sibManifest && sibManifest.app_id !== chosenManifest.app_id) {
|
|
10810
|
+
continue;
|
|
10811
|
+
}
|
|
10812
|
+
}
|
|
10813
|
+
filtered.push(m);
|
|
10814
|
+
}
|
|
10815
|
+
const result = {
|
|
10816
|
+
appId: chosenManifest.app_id,
|
|
10817
|
+
root: chosenRoot,
|
|
10818
|
+
members: filtered,
|
|
10819
|
+
sharedMemoryDir: chosenManifest.shared_memory_dir
|
|
10820
|
+
};
|
|
10821
|
+
writeCache(absTarget, cacheKey(examined), result);
|
|
10822
|
+
return result;
|
|
10823
|
+
}
|
|
10824
|
+
var APP_ID_REGEX, SUPPORTED_SCHEMA_VERSION = 1;
|
|
10825
|
+
var init_project_graph = __esm(() => {
|
|
10826
|
+
APP_ID_REGEX = /^[a-z0-9-]{3,40}$/;
|
|
10827
|
+
});
|
|
10828
|
+
|
|
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
|
+
// ../../loki-ts/src/e10ext/budget_cap.ts
|
|
10901
|
+
var DEFAULT_MAX_COST_USD;
|
|
10902
|
+
var init_budget_cap = __esm(() => {
|
|
10903
|
+
init_yaml_key();
|
|
10904
|
+
init_budget();
|
|
10905
|
+
DEFAULT_MAX_COST_USD = loadDefaultMaxCostUsd();
|
|
10906
|
+
});
|
|
10907
|
+
|
|
10908
|
+
// ../../loki-ts/src/runner/retry_class.ts
|
|
10909
|
+
var init_retry_class = () => {};
|
|
10910
|
+
|
|
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
|
+
// ../../loki-ts/src/e10ext/repomemory.ts
|
|
10977
|
+
var init_repomemory = __esm(() => {
|
|
10978
|
+
init_cache();
|
|
10979
|
+
});
|
|
10980
|
+
|
|
10981
|
+
// ../../loki-ts/src/e10ext/context.ts
|
|
10982
|
+
var FINISH_LINE = "Finish with exactly one line: LOKI_DONE, or LOKI_ALREADY_DONE: <file:line evidence>, or LOKI_SPEC_CONFLICT: <reason>.", FIXED_RULES;
|
|
10983
|
+
var init_context = __esm(() => {
|
|
10984
|
+
init_cache();
|
|
10985
|
+
init_sizing();
|
|
10986
|
+
init_unit_mode();
|
|
10987
|
+
init_repomemory();
|
|
10988
|
+
init_repo_profile();
|
|
10989
|
+
FIXED_RULES = [
|
|
10990
|
+
"You are the Loki 10 implement stage, doing the complete task described below in this repository. Do whatever a senior engineer would do to finish it correctly: read, change and test as needed across any files the task requires.",
|
|
10991
|
+
"Rules:",
|
|
10992
|
+
"- The Wall tests are read-only: do not edit or delete them. Existing test files are append-only: you may add new test functions, but never edit or delete an existing one.",
|
|
10993
|
+
"- Exception: when the task text itself states the new expected value of an existing assertion, change only that literal in that assertion; never remove, skip or loosen an assertion, and never edit one the task text does not name.",
|
|
10994
|
+
"- Run any tests you need, including the package's full suite (the Project Model commands below say how). The impacted tests named below are a starting hint, not a limit.",
|
|
10995
|
+
"- Never leave a long-lived server running.",
|
|
10996
|
+
"- Never kill processes.",
|
|
10997
|
+
"- Write no documentation unless the task explicitly asks for it.",
|
|
10998
|
+
"- Do not commit or push.",
|
|
10999
|
+
"- If you add a new third-party registry dependency, list it in .loki/supply-declared.json as a JSON array of {ecosystem,name,version_spec,registry}, leaving out workspace, path, git and local packages.",
|
|
11000
|
+
FINISH_LINE
|
|
11001
|
+
].join(`
|
|
11002
|
+
`);
|
|
11003
|
+
});
|
|
11004
|
+
|
|
11005
|
+
// ../../loki-ts/src/util/steer.ts
|
|
11006
|
+
var STEER_MAX_BYTES, STEER_STAGES;
|
|
11007
|
+
var init_steer = __esm(() => {
|
|
11008
|
+
init_context();
|
|
11009
|
+
STEER_MAX_BYTES = 1024 * 1024;
|
|
11010
|
+
STEER_STAGES = new Set(["plan", "implement"]);
|
|
11011
|
+
});
|
|
11012
|
+
|
|
11013
|
+
// ../../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";
|
|
11016
|
+
function loadLessons(repoDir) {
|
|
11017
|
+
const p = lessonsPath(repoDir);
|
|
11018
|
+
if (!existsSync23(p))
|
|
11019
|
+
return [];
|
|
11020
|
+
try {
|
|
11021
|
+
const j = JSON.parse(readFileSync30(p, "utf-8"));
|
|
11022
|
+
return Array.isArray(j.lessons) ? j.lessons : [];
|
|
11023
|
+
} catch {
|
|
11024
|
+
return [];
|
|
11025
|
+
}
|
|
11026
|
+
}
|
|
11027
|
+
function save(repoDir, lessons) {
|
|
11028
|
+
const p = lessonsPath(repoDir);
|
|
11029
|
+
mkdirSync14(dirname13(p), { recursive: true });
|
|
11030
|
+
atomicWriteJson(p, { version: 2, lessons });
|
|
11031
|
+
}
|
|
11032
|
+
function recordRunVerdict(repoDir, runId, verdict) {
|
|
11033
|
+
const lessons = loadLessons(repoDir);
|
|
11034
|
+
let dirty = false;
|
|
11035
|
+
for (const l of lessons)
|
|
11036
|
+
for (const u of l.uses)
|
|
11037
|
+
if (u.run_id === runId && u.verdict === null) {
|
|
11038
|
+
u.verdict = verdict;
|
|
11039
|
+
dirty = true;
|
|
11040
|
+
}
|
|
11041
|
+
if (dirty)
|
|
11042
|
+
save(repoDir, lessons);
|
|
11043
|
+
}
|
|
11044
|
+
var lessonsPath = (repoDir) => join43(repoDir, ".loki", "memory", "semantic", "pr-lessons.json");
|
|
11045
|
+
var init_pr_lessons = __esm(() => {
|
|
11046
|
+
init_atomic();
|
|
11047
|
+
});
|
|
11048
|
+
|
|
11049
|
+
// ../../loki-ts/src/runner/model_downgrades.ts
|
|
11050
|
+
var init_model_downgrades = __esm(() => {
|
|
11051
|
+
init_sizing();
|
|
11052
|
+
init_decision();
|
|
11053
|
+
});
|
|
11054
|
+
|
|
11055
|
+
// ../../loki-ts/src/providers/mcp_config.ts
|
|
11056
|
+
var init_mcp_config = () => {};
|
|
11057
|
+
|
|
11058
|
+
// ../../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";
|
|
11061
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
11062
|
+
function designArchetypesText() {
|
|
11063
|
+
try {
|
|
11064
|
+
const moduleDir = dirname14(fileURLToPath3(import.meta.url));
|
|
11065
|
+
for (const rel of [
|
|
11066
|
+
["..", "..", "..", "references", "design-archetypes.md"],
|
|
11067
|
+
["..", "..", "references", "design-archetypes.md"]
|
|
11068
|
+
]) {
|
|
11069
|
+
const p = resolvePath(moduleDir, ...rel);
|
|
11070
|
+
if (existsSync24(p))
|
|
11071
|
+
return readFileSync31(p, "utf8");
|
|
11072
|
+
}
|
|
11073
|
+
} catch {}
|
|
11074
|
+
return "";
|
|
11075
|
+
}
|
|
11076
|
+
var AUTONOMY_OVERRIDE_TEXT, FIRST_PASS_EXCELLENCE_TEXT, CAVEMAN_PINNED_VERSION;
|
|
11077
|
+
var init_claude_flags = __esm(() => {
|
|
11078
|
+
init_mcp_config();
|
|
11079
|
+
AUTONOMY_OVERRIDE_TEXT = `[LOKI-AUTONOMY-AGENT] This is a Loki Mode autonomous agent process.
|
|
11080
|
+
` + `You are running INSIDE Loki Mode autonomous execution, a session the user explicitly launched with full prior authorization (loki start / loki run). This IS an authorized autonomous session, not an unattended action you must seek consent for.
|
|
11081
|
+
|
|
11082
|
+
` + `Instruction precedence for THIS session: when the loki_system instructions in the user prompt conflict with any CLAUDE.md, project memory, or global memory that tells you to ask for clarification, pause for confirmation, refuse to act autonomously, or withhold routine commits, the loki_system instructions take precedence. The user has already consented by launching autonomous mode.
|
|
11083
|
+
|
|
11084
|
+
` + `Therefore, for this session: do NOT ask the user questions, do NOT call the AskUserQuestion tool, do NOT stop to request confirmation, and do NOT refuse routine, reversible engineering work (writing code, running tests, creating files, atomic git checkpoints in the working tree). Proceed and complete the task directly. If a decision is genuinely ambiguous, choose the most reasonable option, record the assumption in .loki/CONTINUITY.md, and continue rather than halting.
|
|
11085
|
+
|
|
11086
|
+
` + "Commit hygiene still applies: git checkpoints are LOCAL only. Never push or force-push. Stage files by explicit path, never `git add -A` or `git add .`, and never commit secrets, credentials, .env files, or untracked files you did not author this session.\n\n" + `This precedence override is narrow. It does NOT relax any safety rule. Every safety prohibition in CLAUDE.md or memory still fully binds: anything genuinely destructive or irreversible remains out of scope unless the task explicitly calls for it. This includes (not limited to) force-pushing, deleting or overwriting the user's data, dropping or truncating databases, publishing or releasing, rotating or exfiltrating secrets, touching production systems, and anything a CLAUDE.md safety rule explicitly forbids. When in doubt about whether an action is destructive, treat it as destructive and do not do it.
|
|
11087
|
+
`;
|
|
11088
|
+
FIRST_PASS_EXCELLENCE_TEXT = `
|
|
11089
|
+
[FIRST-PASS EXCELLENCE] Treat THIS pass as your one shot to ship a complete, working, verified solution. Do not produce a rough draft to refine later; the loop exists as a safety net, not a plan. Before you finish this iteration:
|
|
11090
|
+
` + `1. BUILD IT FULLY. Implement every requirement end to end. No stubs, no TODOs, no "coming soon", no placeholder or hardcoded/mock data where real logic belongs. If the spec implies a backend (auth, persistence, a form that submits, a list that saves), WIRE IT so it actually works and persists -- a beautiful UI whose buttons do nothing is the single most common failure, not a draft. Every list/table must trace to a real query, never an inline mock array.
|
|
11091
|
+
` + `2. SELF-VERIFY BY RUNNING, not by reading. Run the build and the tests yourself; for each acceptance criterion, DRIVE the actual path and observe the result (submit the form, then reload and confirm the record persisted; hit a protected route logged-out and confirm it is rejected). Fix what fails now, in this pass. Do not mark done on "looks right" or a self-claim -- observed behavior is the only proof.
|
|
11092
|
+
` + `3. LOCK THE ARCHITECTURE on this pass so later edits are small and additive. Decide the data model, routes, and component structure up front and build to them; never rewrite whole files later to patch a small thing (that is the doom loop that breaks working features).
|
|
11093
|
+
` + `4. DESIGN: commit to ONE named aesthetic direction up front (editorial, brutalist, luxury, retro-futuristic, soft/pastel, industrial, etc. -- chosen from the product domain) and hold it on every surface. Use real content (never lorem). AVOID the AI-slop tells that instantly read as machine-generated: NO indigo/blue-to-purple gradient (the #1 tell), NO Inter/Roboto/system-font headlines (pick a real display+body pairing), NO three-equal-rounded-cards-in-a-row skeleton, NO flat 1px gray card borders or colored left-border strips, NO untouched shadcn defaults, NO reflexive dark mode. Cap the palette at ~3 hues (60/30/10), tinted not pure #fff/#000, separate sections by whitespace then a slight background shift before any border. Aim for Linear/Stripe/Duolingo-tier taste: "this does not look AI-generated".
|
|
11094
|
+
` + `5. SATISFY THE GATES THAT WILL JUDGE THIS PASS. Your work is checked by deterministic gates before it can be accepted, and in measured runs EVERY extra iteration was caused by one of these rejecting the work -- never by the model failing to finish. Clear them now, in this pass:
|
|
11095
|
+
` + ` - STATIC ANALYSIS: no syntax errors, no unused/undefined symbols, no lint errors in files you touched.
|
|
11096
|
+
` + ` - TEST SUITE: the existing tests must still pass. Run them; do not assume.
|
|
11097
|
+
` + ` - MUTATION/MOCK INTEGRITY: tests must assert real behaviour. No tautological assertions (expect(true).toBe(true)), no test that passes whether or not the code works, no mocking the very unit under test, no inline mock data standing in for a real query.
|
|
11098
|
+
` + ` - CODE REVIEW: no scope creep beyond the stated task, no dead or commented-out code, no leftover debug output, and follow the conventions already in the surrounding files.
|
|
11099
|
+
` + `Deliver the finished, self-verified, genuinely-designed result in THIS pass. Additional iterations should be the exception, not the plan.
|
|
11100
|
+
` + designArchetypesText();
|
|
11101
|
+
CAVEMAN_PINNED_VERSION = process.env["LOKI_CAVEMAN_VERSION"] || "1.9.0";
|
|
11102
|
+
});
|
|
11103
|
+
|
|
11104
|
+
// ../../loki-ts/src/runner/sdk_stream_parser.ts
|
|
11105
|
+
var init_sdk_stream_parser = __esm(() => {
|
|
11106
|
+
init_cost();
|
|
11107
|
+
init_budget();
|
|
11108
|
+
});
|
|
11109
|
+
|
|
11110
|
+
// ../../loki-ts/src/runner/trim.ts
|
|
11111
|
+
var init_trim = () => {};
|
|
11112
|
+
|
|
11113
|
+
// ../../loki-ts/src/util/update_check.ts
|
|
11114
|
+
var CACHE_TTL_MS;
|
|
11115
|
+
var init_update_check = __esm(() => {
|
|
11116
|
+
init_paths();
|
|
11117
|
+
CACHE_TTL_MS = 24 * 60 * 60 * 1000;
|
|
11118
|
+
});
|
|
11119
|
+
|
|
11120
|
+
// ../../loki-ts/src/runner/router/advisor_probe.ts
|
|
11121
|
+
var init_advisor_probe = __esm(() => {
|
|
11122
|
+
init_update_check();
|
|
11123
|
+
});
|
|
11124
|
+
|
|
11125
|
+
// ../../loki-ts/src/runner/provider_opencode.ts
|
|
11126
|
+
var init_provider_opencode = __esm(() => {
|
|
11127
|
+
init_shell();
|
|
11128
|
+
init_providers();
|
|
11129
|
+
});
|
|
11130
|
+
|
|
11131
|
+
// ../../loki-ts/src/runner/providers.ts
|
|
11132
|
+
var _installedVersionCache;
|
|
11133
|
+
var init_providers = __esm(() => {
|
|
11134
|
+
init_shell();
|
|
11135
|
+
init_paths();
|
|
11136
|
+
init_claude_flags();
|
|
11137
|
+
init_mcp_config();
|
|
11138
|
+
init_lean_prefix();
|
|
11139
|
+
init_sdk_stream_parser();
|
|
11140
|
+
init_trim();
|
|
11141
|
+
init_advisor_probe();
|
|
11142
|
+
init_claude_model();
|
|
11143
|
+
init_model_routes();
|
|
11144
|
+
init_provider_opencode();
|
|
11145
|
+
_installedVersionCache = new Map;
|
|
11146
|
+
});
|
|
11147
|
+
|
|
11148
|
+
// ../../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";
|
|
11151
|
+
function loadRouteRecord(runDir) {
|
|
11152
|
+
try {
|
|
11153
|
+
const p = join44(runDir, ROUTE_RECORD_FILE);
|
|
11154
|
+
if (!existsSync25(p) || !lstatSync9(p).isFile())
|
|
11155
|
+
return;
|
|
11156
|
+
const v = JSON.parse(readFileSync32(p, "utf8"));
|
|
11157
|
+
return typeof v === "object" && v !== null && !Array.isArray(v) ? v : undefined;
|
|
11158
|
+
} catch {
|
|
11159
|
+
return;
|
|
11160
|
+
}
|
|
11161
|
+
}
|
|
11162
|
+
var ROUTE_RECORD_FILE = "route.json";
|
|
11163
|
+
var init_route_record = () => {};
|
|
11164
|
+
|
|
11165
|
+
// ../../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";
|
|
11167
|
+
import { resolve as resolve13 } from "path";
|
|
11168
|
+
function validRun(v) {
|
|
11169
|
+
if (typeof v !== "object" || v === null)
|
|
11170
|
+
return false;
|
|
11171
|
+
const r = v;
|
|
11172
|
+
return typeof r["shape"] === "string" && typeof r["executor"] === "string" && EXECUTORS.includes(r["executor"]) && typeof r["verdict"] === "string" && VERDICTS.includes(r["verdict"]) && (r["owner"] === null || typeof r["owner"] === "string" && OWNERS.includes(r["owner"])) && typeof r["escalated"] === "boolean" && typeof r["usd"] === "number" && Number.isFinite(r["usd"]) && typeof r["wallS"] === "number" && Number.isFinite(r["wallS"]);
|
|
11173
|
+
}
|
|
11174
|
+
function historyPath(key, cacheRoot) {
|
|
11175
|
+
return resolve13(repoCacheDir(key, cacheRoot), HISTORY_FILE);
|
|
11176
|
+
}
|
|
11177
|
+
function readRunHistory(key, cacheRoot) {
|
|
11178
|
+
const path = historyPath(key, cacheRoot);
|
|
11179
|
+
if (!existsSync26(path))
|
|
11180
|
+
return [];
|
|
11181
|
+
try {
|
|
11182
|
+
const parsed = JSON.parse(readFileSync33(path, "utf8"));
|
|
11183
|
+
const runs2 = parsed?.runs;
|
|
11184
|
+
return Array.isArray(runs2) ? runs2.filter(validRun) : [];
|
|
11185
|
+
} catch {
|
|
11186
|
+
return [];
|
|
11187
|
+
}
|
|
11188
|
+
}
|
|
11189
|
+
function appendRunOutcome(key, outcome, cacheRoot) {
|
|
11190
|
+
try {
|
|
11191
|
+
const runs2 = [...readRunHistory(key, cacheRoot), outcome].slice(-HISTORY_CAP);
|
|
11192
|
+
mkdirSync16(repoCacheDir(key, cacheRoot), { recursive: true });
|
|
11193
|
+
const target = historyPath(key, cacheRoot);
|
|
11194
|
+
const tmp = `${target}.${process.pid}.tmp`;
|
|
11195
|
+
try {
|
|
11196
|
+
writeFileSync18(tmp, JSON.stringify({ runs: runs2 }));
|
|
11197
|
+
renameSync7(tmp, target);
|
|
11198
|
+
} catch (e) {
|
|
11199
|
+
rmSync11(tmp, { force: true, recursive: false });
|
|
11200
|
+
throw e;
|
|
11201
|
+
}
|
|
11202
|
+
return true;
|
|
11203
|
+
} catch {
|
|
11204
|
+
return false;
|
|
11205
|
+
}
|
|
11206
|
+
}
|
|
11207
|
+
var HISTORY_FILE = "router_history.json", HISTORY_CAP = 200, WALL_KIND = "total-v1", EXECUTORS, VERDICTS, OWNERS;
|
|
11208
|
+
var init_history_store = __esm(() => {
|
|
11209
|
+
init_cache();
|
|
11210
|
+
EXECUTORS = ["haiku", "sonnet"];
|
|
11211
|
+
VERDICTS = ["pass", "fail", "error", "not_proven"];
|
|
11212
|
+
OWNERS = ["code", "harness", "env", "provider"];
|
|
11213
|
+
});
|
|
11214
|
+
|
|
11215
|
+
// ../../loki-ts/src/runner/router/history.ts
|
|
11216
|
+
import { dirname as dirname15, join as join45, resolve as resolve14 } from "path";
|
|
11217
|
+
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
11218
|
+
function shapeKey(model) {
|
|
11219
|
+
if (!model || model.status !== "ok")
|
|
11220
|
+
return null;
|
|
11221
|
+
const runners = new Set(model.packages.map((p) => (p.runner ?? "").trim().toLowerCase() || "none"));
|
|
11222
|
+
return `${model.workspaceKind}:${[...runners].sort().join("+")}`;
|
|
11223
|
+
}
|
|
11224
|
+
function shapeKeyForRepo(repoDir) {
|
|
11225
|
+
try {
|
|
11226
|
+
const dirs = shallowDirs(repoDir);
|
|
11227
|
+
const committed = loadCommitted(repoDir);
|
|
11228
|
+
if (committed)
|
|
11229
|
+
return shapeKey({ ...committed.model, key: computeKey(repoDir, committed.model.fingerprintFiles, dirs, committed.hash) });
|
|
11230
|
+
const cached = loadCached(repoDir);
|
|
11231
|
+
if (!cached || computeKey(repoDir, cached.fingerprintFiles, dirs) !== cached.key)
|
|
11232
|
+
return null;
|
|
11233
|
+
return shapeKey(cached);
|
|
11234
|
+
} catch {
|
|
11235
|
+
return null;
|
|
11236
|
+
}
|
|
11237
|
+
}
|
|
11238
|
+
var SHAPE_DEFAULTS_FILE;
|
|
11239
|
+
var init_history = __esm(() => {
|
|
11240
|
+
init_schema();
|
|
11241
|
+
init_history_store();
|
|
11242
|
+
init_discover2();
|
|
11243
|
+
init_gather();
|
|
11244
|
+
init_history_store();
|
|
11245
|
+
SHAPE_DEFAULTS_FILE = resolve14(dirname15(fileURLToPath4(import.meta.url)), "..", "..", "..", "data", "router-shape-defaults.json");
|
|
11246
|
+
});
|
|
11247
|
+
|
|
11248
|
+
// ../../loki-ts/src/runner/router/plan_route.ts
|
|
11249
|
+
var init_plan_route = __esm(() => {
|
|
11250
|
+
init_run_cap();
|
|
11251
|
+
init_providers();
|
|
11252
|
+
init_decision();
|
|
11253
|
+
init_advisor_probe();
|
|
11254
|
+
init_route_record();
|
|
11255
|
+
init_history();
|
|
11256
|
+
});
|
|
11257
|
+
|
|
11258
|
+
// ../../loki-ts/src/util/intent_card.ts
|
|
11259
|
+
var INTENT_HEADER = "What I think you want:", CARD_MIN_LINES = 3, CARD_MAX_LINES = 5, INTENT_CARD_INSTRUCTION;
|
|
11260
|
+
var init_intent_card = __esm(() => {
|
|
11261
|
+
INTENT_CARD_INSTRUCTION = `After the plan lines, append an intent card of ${CARD_MIN_LINES} to ${CARD_MAX_LINES} lines: a first line starting "${INTENT_HEADER}" with one sentence on what you think the user wants, then ${CARD_MIN_LINES - 1} to ${CARD_MAX_LINES - 1} lines each starting "Acceptance:" naming one check you will hold yourself to. You may add one optional line starting "Out of scope:" naming what you will NOT do; it counts toward the ${CARD_MAX_LINES} lines.`;
|
|
11262
|
+
});
|
|
11263
|
+
|
|
11264
|
+
// ../../loki-ts/src/engine10/repomap.ts
|
|
11265
|
+
var init_repomap = __esm(() => {
|
|
11266
|
+
init_safe_git();
|
|
11267
|
+
});
|
|
11268
|
+
|
|
11269
|
+
// ../../loki-ts/src/util/reviewer_brief.ts
|
|
11270
|
+
var init_reviewer_brief = __esm(() => {
|
|
11271
|
+
init_repomap();
|
|
11272
|
+
init_safe_git();
|
|
11273
|
+
});
|
|
11274
|
+
|
|
11275
|
+
// ../../loki-ts/src/engine10/stages/plan.ts
|
|
11276
|
+
var init_plan = __esm(() => {
|
|
11277
|
+
init_session();
|
|
11278
|
+
init_sizing();
|
|
11279
|
+
init_lean_prefix();
|
|
11280
|
+
init_types();
|
|
11281
|
+
init_run_cap();
|
|
11282
|
+
init_wall();
|
|
11283
|
+
init_model_downgrades();
|
|
11284
|
+
init_plan_route();
|
|
11285
|
+
init_intent_card();
|
|
11286
|
+
init_mutation_proof();
|
|
11287
|
+
init_reviewer_brief();
|
|
11288
|
+
});
|
|
11289
|
+
|
|
11290
|
+
// ../../loki-ts/src/runner/session_resume.ts
|
|
11291
|
+
var init_session_resume = __esm(() => {
|
|
11292
|
+
init_session();
|
|
11293
|
+
});
|
|
11294
|
+
|
|
11295
|
+
// ../../loki-ts/src/util/conflict_resume.ts
|
|
11296
|
+
var CONFLICT_CORRECTION, EMPTY_DONE_CORRECTION;
|
|
11297
|
+
var init_conflict_resume = __esm(() => {
|
|
11298
|
+
init_session_resume();
|
|
11299
|
+
init_context();
|
|
11300
|
+
CONFLICT_CORRECTION = "Loki imposes no file or test limits on you. If your conflict is only about Loki's own rules, finish the task. If the task itself conflicts, restate the conflict. " + FINISH_LINE;
|
|
11301
|
+
EMPTY_DONE_CORRECTION = "Your session finished with LOKI_DONE but the working tree has no change against the base. Make the change the task asks for, or if the task is already satisfied finish with LOKI_ALREADY_DONE: <file:line evidence>, or if the task itself conflicts finish with LOKI_SPEC_CONFLICT: <reason>. " + FINISH_LINE;
|
|
11302
|
+
});
|
|
11303
|
+
|
|
11304
|
+
// ../../loki-ts/src/runner/router/implement_route.ts
|
|
11305
|
+
var init_implement_route = __esm(() => {
|
|
11306
|
+
init_model_rank();
|
|
11307
|
+
init_unit_model();
|
|
11308
|
+
});
|
|
11309
|
+
|
|
11310
|
+
// ../../loki-ts/src/engine10/stages/implement.ts
|
|
11311
|
+
var init_implement = __esm(() => {
|
|
11312
|
+
init_lean_prefix();
|
|
11313
|
+
init_pr_lessons();
|
|
11314
|
+
init_context();
|
|
11315
|
+
init_model_downgrades();
|
|
11316
|
+
init_sizing();
|
|
11317
|
+
init_plan();
|
|
11318
|
+
init_verify();
|
|
11319
|
+
init_resolve();
|
|
11320
|
+
init_session();
|
|
11321
|
+
init_conflict_resume();
|
|
11322
|
+
init_session_resume();
|
|
11323
|
+
init_implement_route();
|
|
11324
|
+
init_unit_model();
|
|
11325
|
+
init_types();
|
|
11326
|
+
init_spec_file();
|
|
11327
|
+
});
|
|
11328
|
+
|
|
11329
|
+
// ../../loki-ts/src/engine10/machine.ts
|
|
11330
|
+
var init_machine = __esm(() => {
|
|
11331
|
+
init_hooks();
|
|
11332
|
+
init_retry_class();
|
|
11333
|
+
init_registry();
|
|
11334
|
+
init_run_cap();
|
|
11335
|
+
init_context();
|
|
11336
|
+
init_steer();
|
|
11337
|
+
init_implement();
|
|
11338
|
+
init_unit_model();
|
|
11339
|
+
init_types();
|
|
11340
|
+
});
|
|
11341
|
+
|
|
11342
|
+
// ../../loki-ts/src/runner/provider_failover.ts
|
|
11343
|
+
var DEFAULT_ORDER, ALLOWED;
|
|
11344
|
+
var init_provider_failover = __esm(() => {
|
|
11345
|
+
DEFAULT_ORDER = ["claude", "cline", "codex", "aider", "opencode"];
|
|
11346
|
+
ALLOWED = new Set(DEFAULT_ORDER);
|
|
11347
|
+
});
|
|
11348
|
+
|
|
11349
|
+
// ../../loki-ts/src/engine10/worker.ts
|
|
11350
|
+
function assertWorkerEnv(env = process.env) {
|
|
11351
|
+
if (env.LOKI_ALLOW_AGENT_GITHUB_TOKEN === "1")
|
|
11352
|
+
return;
|
|
11353
|
+
for (const v of GITHUB_TOKEN_VARS) {
|
|
11354
|
+
const val = env[v] ?? "";
|
|
11355
|
+
if (val !== "" && !val.startsWith(SENTINEL_PREFIX)) {
|
|
11356
|
+
throw new Error(`engine10 worker: ${v} holds a real token; the worker must run with withheld credentials`);
|
|
11357
|
+
}
|
|
11358
|
+
}
|
|
11359
|
+
}
|
|
11360
|
+
var init_worker = __esm(() => {
|
|
11361
|
+
init_credential_env();
|
|
11362
|
+
init_cost();
|
|
11363
|
+
init_budget_cap();
|
|
11364
|
+
init_run_cap();
|
|
11365
|
+
init_resolve();
|
|
11366
|
+
init_budget_cap();
|
|
11367
|
+
init_machine();
|
|
11368
|
+
init_provider_failover();
|
|
11369
|
+
init_session();
|
|
11370
|
+
init_testmap();
|
|
11371
|
+
init_types();
|
|
11372
|
+
init_safe_git();
|
|
11373
|
+
});
|
|
11374
|
+
|
|
11375
|
+
// ../../loki-ts/src/project_model/package_suite.ts
|
|
11376
|
+
import { join as join46 } from "path";
|
|
11377
|
+
async function runPackageSuites(repoDir, signal, opts, checks, notProven) {
|
|
11378
|
+
const api = loadProjectApi(repoDir);
|
|
11379
|
+
if (!isMultiRoot(api))
|
|
11380
|
+
return false;
|
|
11381
|
+
const withTests = api.packages().filter((p) => p.commands.test);
|
|
11382
|
+
if (withTests.length === 0)
|
|
11383
|
+
return false;
|
|
11384
|
+
for (const pkg of withTests) {
|
|
11385
|
+
const tc = pkg.commands.test, name = `full suite: ${pkg.name}`, cmd = `(cd ${tc.cwd} && ${tc.cmd})`;
|
|
11386
|
+
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 } } : {} });
|
|
11388
|
+
const duration_s = (Date.now() - started) / 1000;
|
|
11389
|
+
if (signal.aborted) {
|
|
11390
|
+
checks.push({ name, cmd, result: "not_run", duration_s });
|
|
11391
|
+
notProven.add(`not run: ${name} (aborted)`);
|
|
11392
|
+
continue;
|
|
11393
|
+
}
|
|
11394
|
+
const out2 = `${r.stdout}
|
|
11395
|
+
${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 } } : {} });
|
|
11397
|
+
if (lr) {
|
|
11398
|
+
checks.push({ name, cmd, result: "not_run", duration_s });
|
|
11399
|
+
notProven.add(`${lr} (${name}; harness-owned)`);
|
|
11400
|
+
continue;
|
|
11401
|
+
}
|
|
11402
|
+
const cls = classifyCheck({ kind: "test", ok: r.exitCode === 0, out: out2, ...goRunner(tc.cmd) });
|
|
11403
|
+
checks.push({ name, cmd, result: cls.result, duration_s, ...cls.result === "fail" ? { ids: failIds(out2) } : {} });
|
|
11404
|
+
if (cls.result === "not_run")
|
|
11405
|
+
notProven.add(`not run: ${name} (${cls.reason ?? NO_TESTS_REASON})`);
|
|
11406
|
+
}
|
|
11407
|
+
for (const pkg of api.packages().filter((p) => !p.commands.test))
|
|
11408
|
+
notProven.add(`full suite: package ${pkg.name} defines no test command`);
|
|
11409
|
+
return true;
|
|
11410
|
+
}
|
|
11411
|
+
var init_package_suite = __esm(() => {
|
|
11412
|
+
init_load_owner();
|
|
11413
|
+
init_check_result();
|
|
11414
|
+
init_shell();
|
|
11415
|
+
init_failures();
|
|
11416
|
+
init_resolve();
|
|
11417
|
+
});
|
|
11418
|
+
|
|
11419
|
+
// ../../loki-ts/src/engine10/stages/deep.ts
|
|
11420
|
+
var exports_deep = {};
|
|
11421
|
+
__export(exports_deep, {
|
|
11422
|
+
stage: () => stage,
|
|
11423
|
+
runFullSuite: () => runFullSuite,
|
|
11424
|
+
runDeep: () => runDeep,
|
|
11425
|
+
deepStage: () => deepStage,
|
|
11426
|
+
DEFAULT_SECRET_SCAN_SH: () => DEFAULT_SECRET_SCAN_SH,
|
|
11427
|
+
DEFAULT_PUSH_SH: () => DEFAULT_PUSH_SH
|
|
11428
|
+
});
|
|
11429
|
+
import { existsSync as existsSync27, mkdirSync as mkdirSync17, writeFileSync as writeFileSync19 } from "fs";
|
|
11430
|
+
import { join as join47 } from "path";
|
|
11431
|
+
async function runFullSuite(ctx, signal, opts, checks, notProven) {
|
|
11432
|
+
if (await runPackageSuites(ctx.repoDir, signal, { path: opts.path, timeoutMs: opts.fullSuiteTimeoutMs ?? FULL_SUITE_TIMEOUT_MS, baseSha: ctx.baseSha }, checks, notProven))
|
|
11433
|
+
return;
|
|
11434
|
+
const map = await ctx.tests.detect(ctx.repoDir);
|
|
11435
|
+
if (map.runners.length === 0) {
|
|
11436
|
+
notProven.add("full suite (no test runner detected)");
|
|
11437
|
+
return;
|
|
11438
|
+
}
|
|
11439
|
+
for (const runner of map.runners) {
|
|
11440
|
+
const spec = FULL_SUITE_CMD[runner];
|
|
11441
|
+
const name = `full suite: ${runner}`;
|
|
11442
|
+
if (!Bun.which(spec.cmd, opts.path ? { PATH: opts.path } : undefined)) {
|
|
11443
|
+
checks.push({ name, cmd: [spec.cmd, ...spec.args].join(" "), result: "not_run", duration_s: 0 });
|
|
11444
|
+
notProven.add(`not run: ${name}`);
|
|
11445
|
+
continue;
|
|
11446
|
+
}
|
|
11447
|
+
const started = Date.now();
|
|
11448
|
+
const r = await run([spec.cmd, ...spec.args], {
|
|
11449
|
+
cwd: ctx.repoDir,
|
|
11450
|
+
timeoutMs: opts.fullSuiteTimeoutMs ?? FULL_SUITE_TIMEOUT_MS,
|
|
11451
|
+
...opts.path ? { env: { PATH: opts.path } } : {}
|
|
11452
|
+
});
|
|
11453
|
+
const durationS = (Date.now() - started) / 1000;
|
|
11454
|
+
if (signal.aborted) {
|
|
11455
|
+
checks.push({ name, cmd: [spec.cmd, ...spec.args].join(" "), result: "not_run", duration_s: durationS });
|
|
11456
|
+
notProven.add(`not run: ${name} (aborted)`);
|
|
11457
|
+
continue;
|
|
11458
|
+
}
|
|
11459
|
+
const out2 = `${r.stdout}
|
|
11460
|
+
${r.stderr}`, lr = r.exitCode === 0 ? undefined : await harnessLoadReason({ repoDir: ctx.repoDir, baseSha: ctx.baseSha, out: out2, cmd: spec.cmd, args: spec.args, signal, ...opts.path ? { env: { PATH: opts.path } } : {} });
|
|
11461
|
+
if (lr) {
|
|
11462
|
+
checks.push({ name, cmd: [spec.cmd, ...spec.args].join(" "), result: "not_run", duration_s: durationS });
|
|
11463
|
+
notProven.add(`${lr} (${name}; harness-owned)`);
|
|
11464
|
+
continue;
|
|
11465
|
+
}
|
|
11466
|
+
const cls = classifyCheck({ kind: "test", ok: r.exitCode === 0, out: out2, ...goRunner(spec.cmd, spec.args) });
|
|
11467
|
+
checks.push({ name, cmd: [spec.cmd, ...spec.args].join(" "), result: cls.result, duration_s: durationS, ...cls.result === "fail" ? { ids: failIds(out2) } : {} });
|
|
11468
|
+
if (cls.result === "not_run")
|
|
11469
|
+
notProven.add(`not run: ${name} (${cls.reason ?? NO_TESTS_REASON})`);
|
|
11470
|
+
}
|
|
11471
|
+
}
|
|
11472
|
+
function runAppBoot(ctx, opts, checks, notProven) {
|
|
11473
|
+
const discover = opts.discoverGraph ?? discoverProjectGraph;
|
|
11474
|
+
let graph = null;
|
|
11475
|
+
try {
|
|
11476
|
+
graph = discover(ctx.repoDir);
|
|
11477
|
+
} catch {
|
|
11478
|
+
graph = null;
|
|
11479
|
+
}
|
|
11480
|
+
checks.push({
|
|
11481
|
+
name: "app boot",
|
|
11482
|
+
cmd: "project_graph.ts discoverProjectGraph",
|
|
11483
|
+
result: "not_run",
|
|
11484
|
+
duration_s: 0
|
|
11485
|
+
});
|
|
11486
|
+
notProven.add(graph ? `app boot not run (app graph discovered, ${graph.members.length} member(s))` : "app boot not run (no app graph discovered)");
|
|
11487
|
+
}
|
|
11488
|
+
async function runCouncilCheck(ctx, changed, baseSha, signal, opts, checks, notProven) {
|
|
11489
|
+
const codeFiles = changed.filter((f) => !f.endsWith(".md"));
|
|
11490
|
+
if (codeFiles.length === 0) {
|
|
11491
|
+
notProven.add("council (no non-doc changes to review)");
|
|
11492
|
+
return;
|
|
11493
|
+
}
|
|
11494
|
+
if (!opts.council) {
|
|
11495
|
+
notProven.add("council (not wired: see the contract-gap note in stages/deep.ts)");
|
|
11496
|
+
return;
|
|
11497
|
+
}
|
|
11498
|
+
const diffResult = await safeGitRun(ctx.repoDir, ["diff", "--no-color", baseSha, "HEAD", "--", ...codeFiles], { timeoutMs: 20000 });
|
|
11499
|
+
const diff = diffResult.exitCode === 0 ? diffResult.stdout : "";
|
|
11500
|
+
const maxBytes = opts.maxCouncilDiffBytes ?? MAX_COUNCIL_DIFF_BYTES;
|
|
11501
|
+
if (Buffer.byteLength(diff, "utf8") > maxBytes) {
|
|
11502
|
+
notProven.add("council (context oversized, refused)");
|
|
11503
|
+
return;
|
|
11504
|
+
}
|
|
11505
|
+
const started = Date.now();
|
|
11506
|
+
try {
|
|
11507
|
+
const outcome = await opts.council.run({ runId: ctx.runId, diff, files: codeFiles }, signal);
|
|
11508
|
+
checks.push({ name: "council", cmd: "dispatchClaudeAgents", result: "pass", duration_s: (Date.now() - started) / 1000 });
|
|
11509
|
+
} catch (err) {
|
|
11510
|
+
notProven.add(`council (refused: ${err.message})`);
|
|
11511
|
+
}
|
|
11512
|
+
}
|
|
11513
|
+
async function runSecretScan(ctx, changed, opts, checks, notProven) {
|
|
11514
|
+
const scriptPath = opts.secretScanShPath ?? DEFAULT_SECRET_SCAN_SH;
|
|
11515
|
+
const name = "security scan";
|
|
11516
|
+
if (!existsSync27(scriptPath)) {
|
|
11517
|
+
checks.push({ name, cmd: "secret-scan.sh", result: "not_run", duration_s: 0 });
|
|
11518
|
+
notProven.add(`not run: ${name} (secret-scan.sh not found)`);
|
|
11519
|
+
return;
|
|
11520
|
+
}
|
|
11521
|
+
if (changed.length === 0) {
|
|
11522
|
+
checks.push({ name, cmd: "secret-scan.sh", result: "pass", duration_s: 0 });
|
|
11523
|
+
return;
|
|
11524
|
+
}
|
|
11525
|
+
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
|
+
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 });
|
|
11528
|
+
const durationS = (Date.now() - started) / 1000;
|
|
11529
|
+
if (r.exitCode === 3) {
|
|
11530
|
+
checks.push({ name, cmd: "secret-scan.sh", result: "not_run", duration_s: durationS });
|
|
11531
|
+
notProven.add(`not run: ${name} (secret-scan.sh failed to load)`);
|
|
11532
|
+
return;
|
|
11533
|
+
}
|
|
11534
|
+
const hits = r.stdout.split(`
|
|
11535
|
+
`).map((l) => l.trim()).filter(Boolean);
|
|
11536
|
+
checks.push({
|
|
11537
|
+
name,
|
|
11538
|
+
cmd: "secret-scan.sh",
|
|
11539
|
+
result: hits.length > 0 ? "fail" : "pass",
|
|
11540
|
+
duration_s: durationS,
|
|
11541
|
+
...hits.length > 0 ? { reason: `secret match: ${hits.join(", ")}` } : {}
|
|
11542
|
+
});
|
|
11543
|
+
}
|
|
11544
|
+
function commentArgv(prNumber, bodyFile) {
|
|
11545
|
+
return ["comment", prNumber, bodyFile];
|
|
11546
|
+
}
|
|
11547
|
+
function buildCommentBody(checks, notProven, statusState) {
|
|
11548
|
+
const lines = [`## Loki deep verify: ${statusState}`, "", "### Checks"];
|
|
11549
|
+
lines.push(...checks.length ? checks.map((c) => `- ${c.result}: ${c.name}${c.reason ? ` (${c.reason})` : ""}`) : ["- none"]);
|
|
11550
|
+
lines.push("", "### NOT PROVEN", ...notProven.length ? notProven.map((n2) => `- ${n2}`) : ["- none"]);
|
|
11551
|
+
return `${lines.join(`
|
|
11552
|
+
`)}
|
|
11553
|
+
`;
|
|
11554
|
+
}
|
|
11555
|
+
async function postResults(ctx, opts, checks, notProven, statusState) {
|
|
11556
|
+
const out2 = [];
|
|
11557
|
+
const pinnedOrigin = ctx.pinnedOrigin;
|
|
11558
|
+
if (!pinnedOrigin) {
|
|
11559
|
+
out2.push("push refused: no pinned origin (Rule of Two)");
|
|
11560
|
+
return out2;
|
|
11561
|
+
}
|
|
11562
|
+
const scriptPath = opts.pushScriptPath ?? DEFAULT_PUSH_SH;
|
|
11563
|
+
const env = { ...process.env, ...opts.pushExtraEnv, _LOKI_ORIGIN_PINNED: "1", _LOKI_PINNED_ORIGIN: pinnedOrigin };
|
|
11564
|
+
const prUrl = ctx.outputs().pr?.pr_url;
|
|
11565
|
+
const prNumber = prUrl ? PR_NUMBER_RE.exec(prUrl)?.[1] : undefined;
|
|
11566
|
+
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");
|
|
11570
|
+
const commentResult = await run(["bash", scriptPath, ...commentArgv(prNumber, bodyFile)], { cwd: ctx.repoDir, env, timeoutMs: 30000 });
|
|
11571
|
+
if (commentResult.exitCode !== 0)
|
|
11572
|
+
out2.push(`deep comment not posted (engine10-push.sh exit ${commentResult.exitCode})`);
|
|
11573
|
+
} else {
|
|
11574
|
+
out2.push("deep comment not posted (no PR number)");
|
|
11575
|
+
}
|
|
11576
|
+
const headSha = (await safeGitRun(ctx.repoDir, ["rev-parse", "HEAD"], { timeoutMs: 1e4 })).stdout.trim();
|
|
11577
|
+
if (SHA_RE.test(headSha)) {
|
|
11578
|
+
const statusArgs = { cmd: "status", sha: headSha, state: statusState, description: `Loki 10 deep verify: ${statusState}` };
|
|
11579
|
+
const statusResult = await run(["bash", scriptPath, ...pushArgv(statusArgs)], { cwd: ctx.repoDir, env, timeoutMs: 30000 });
|
|
11580
|
+
if (statusResult.exitCode !== 0)
|
|
11581
|
+
out2.push(`loki/deep-verify status not set (engine10-push.sh exit ${statusResult.exitCode})`);
|
|
11582
|
+
} else {
|
|
11583
|
+
out2.push("loki/deep-verify status not set (HEAD sha not resolvable)");
|
|
11584
|
+
}
|
|
11585
|
+
return out2;
|
|
11586
|
+
}
|
|
11587
|
+
async function runDeep(ctx, signal, opts = {}) {
|
|
11588
|
+
if (signal.aborted)
|
|
11589
|
+
return { status: "failed", data: {}, reason: "aborted before deep verify started" };
|
|
11590
|
+
try {
|
|
11591
|
+
assertWorkerEnv(opts.env ?? process.env);
|
|
11592
|
+
} catch (err) {
|
|
11593
|
+
return { status: "failed", data: {}, reason: err.message };
|
|
11594
|
+
}
|
|
11595
|
+
let changed;
|
|
11596
|
+
try {
|
|
11597
|
+
changed = changedFiles(ctx.repoDir, ctx.baseSha);
|
|
11598
|
+
} catch (err) {
|
|
11599
|
+
return { status: "failed", data: {}, reason: `git diff against base failed: ${err.message}` };
|
|
11600
|
+
}
|
|
11601
|
+
const checks = [];
|
|
11602
|
+
const notProven = new Set;
|
|
11603
|
+
await runFullSuite(ctx, signal, opts, checks, notProven);
|
|
11604
|
+
runAppBoot(ctx, opts, checks, notProven);
|
|
11605
|
+
await runCouncilCheck(ctx, changed, ctx.baseSha, signal, opts, checks, notProven);
|
|
11606
|
+
await runSecretScan(ctx, changed, opts, checks, notProven);
|
|
11607
|
+
const statusState = checks.some((c) => c.result === "fail") ? "failure" : "success";
|
|
11608
|
+
const notProvenList = [...notProven];
|
|
11609
|
+
const seal = ctx.outputs().seal;
|
|
11610
|
+
const addendumBody = {
|
|
11611
|
+
schema: "loki.v10.receipt-addendum/1",
|
|
11612
|
+
run_id: ctx.runId,
|
|
11613
|
+
base_receipt_sha256: seal?.receipt_sha256 ?? null,
|
|
11614
|
+
checks,
|
|
11615
|
+
not_proven: notProvenList,
|
|
11616
|
+
status_state: statusState
|
|
11617
|
+
};
|
|
11618
|
+
const addendumSha256 = sha2562(canonicalJson(addendumBody));
|
|
11619
|
+
const sig = signReceipt(`${ctx.runId}-deep`, addendumSha256);
|
|
11620
|
+
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) + `
|
|
11623
|
+
`);
|
|
11624
|
+
const pushNotProven = await postResults(ctx, opts, checks, notProvenList, statusState);
|
|
11625
|
+
const allNotProven = [...notProvenList, ...pushNotProven];
|
|
11626
|
+
ctx.emit("deep.completed", "deep", { checks, status_state: statusState, addendum_sha256: addendumSha256 });
|
|
11627
|
+
return { status: "completed", data: { checks, addendum_sha256: addendumSha256, status_state: statusState, not_proven: allNotProven } };
|
|
11628
|
+
}
|
|
11629
|
+
var DEFAULT_PUSH_SH, DEFAULT_SECRET_SCAN_SH, MAX_COUNCIL_DIFF_BYTES = 400000, FULL_SUITE_TIMEOUT_MS = 300000, PR_NUMBER_RE, SHA_RE, FULL_SUITE_CMD, deepStage, stage;
|
|
11630
|
+
var init_deep = __esm(() => {
|
|
11631
|
+
init_project_graph();
|
|
11632
|
+
init_shell();
|
|
11633
|
+
init_load_owner();
|
|
11634
|
+
init_worker();
|
|
11635
|
+
init_seal();
|
|
11636
|
+
init_verify();
|
|
11637
|
+
init_package_suite();
|
|
11638
|
+
init_check_result();
|
|
11639
|
+
init_failures();
|
|
11640
|
+
init_types();
|
|
11641
|
+
init_safe_git();
|
|
11642
|
+
DEFAULT_PUSH_SH = new URL("../../../../autonomy/lib/engine10-push.sh", import.meta.url).pathname;
|
|
11643
|
+
DEFAULT_SECRET_SCAN_SH = new URL("../../../../autonomy/lib/secret-scan.sh", import.meta.url).pathname;
|
|
11644
|
+
PR_NUMBER_RE = /\/pull\/(\d+)$/;
|
|
11645
|
+
SHA_RE = /^[0-9a-f]{40}$/;
|
|
11646
|
+
FULL_SUITE_CMD = {
|
|
11647
|
+
pytest: { cmd: "python", args: ["-m", "pytest", "-q"] },
|
|
11648
|
+
vitest: { cmd: "npx", args: ["vitest", "run"] },
|
|
11649
|
+
jest: { cmd: "npx", args: ["jest"] },
|
|
11650
|
+
bun: { cmd: "bun", args: ["test"] },
|
|
11651
|
+
node: { cmd: "node", args: ["--test"] },
|
|
11652
|
+
npm: { cmd: "npm", args: ["test", "--silent"] },
|
|
11653
|
+
go: { cmd: "go", args: ["test", "-json", "./..."] },
|
|
11654
|
+
cargo: { cmd: "cargo", args: ["test"] }
|
|
11655
|
+
};
|
|
11656
|
+
deepStage = {
|
|
11657
|
+
name: "deep",
|
|
11658
|
+
...STAGE_BUDGETS.deep,
|
|
11659
|
+
run: (ctx, signal) => runDeep(ctx, signal)
|
|
11660
|
+
};
|
|
11661
|
+
stage = deepStage;
|
|
11662
|
+
});
|
|
11663
|
+
|
|
11664
|
+
// ../../loki-ts/src/engine10/stages/verify.ts
|
|
11665
|
+
import { existsSync as existsSync28, mkdtempSync as mkdtempSync6, realpathSync as realpathSync17, rmSync as rmSync12 } from "fs";
|
|
11666
|
+
import { tmpdir as tmpdir8 } from "os";
|
|
11667
|
+
import { dirname as dirname16, isAbsolute as isAbsolute6, join as join48, relative as relative5 } from "path";
|
|
11668
|
+
function resolveTool(repoDir, bin, fallback, lastResort = fallback) {
|
|
11669
|
+
const inRepo = (p) => {
|
|
11670
|
+
try {
|
|
11671
|
+
const r = relative5(realpathSync17(repoDir), realpathSync17(p));
|
|
11672
|
+
return r !== "" && !r.startsWith("..") && !isAbsolute6(r);
|
|
11673
|
+
} catch {
|
|
11674
|
+
return false;
|
|
11675
|
+
}
|
|
11676
|
+
};
|
|
11677
|
+
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);
|
|
11679
|
+
return project ? [project, "project"] : [Bun.which(fallback) ? fallback : lastResort, "system"];
|
|
11680
|
+
}
|
|
11681
|
+
function runnerCmd(t, repoDir) {
|
|
11682
|
+
switch (t.runner) {
|
|
11683
|
+
case "pytest": {
|
|
11684
|
+
const [cmd, interpreter] = resolveTool(repoDir, "python", "python3", "python");
|
|
11685
|
+
return [cmd, ["-m", "pytest", "-q", t.path], interpreter];
|
|
11686
|
+
}
|
|
11687
|
+
case "vitest":
|
|
11688
|
+
return ["npx", ["vitest", "run", t.path]];
|
|
11689
|
+
case "jest":
|
|
11690
|
+
return ["npx", ["jest", t.path]];
|
|
11691
|
+
case "bun":
|
|
11692
|
+
return ["bun", ["test", t.path]];
|
|
11693
|
+
case "node":
|
|
11694
|
+
return ["node", ["--test", `./${t.path}`]];
|
|
11695
|
+
case "npm":
|
|
11696
|
+
return ["npm", ["test", "--silent"]];
|
|
11697
|
+
case "go":
|
|
11698
|
+
return ["go", ["test", "-json", `./${dirname16(t.path)}`]];
|
|
11699
|
+
case "cargo":
|
|
11700
|
+
return ["cargo", ["test"]];
|
|
11701
|
+
}
|
|
11702
|
+
}
|
|
11703
|
+
function commandFor(t, repoDir, api) {
|
|
11704
|
+
const site = siteFor(api, repoDir, t.path);
|
|
11705
|
+
const [cmd, args, interpreter] = runnerCmd(site ? { runner: t.runner, path: site.file } : t, repoDir);
|
|
11706
|
+
return { cwd: site?.cwd ?? repoDir, cmd, args, argv: [cmd, ...args], ...interpreter ? { interpreter } : {}, pkgRoot: site?.root ?? "." };
|
|
11707
|
+
}
|
|
11708
|
+
function testConfigChanged(repoDir, baseSha, changed) {
|
|
10617
11709
|
const diff = (f) => safeGit(repoDir, ["diff", "-U0", baseSha, "--", f]);
|
|
10618
11710
|
return changed.filter((f) => CFG_ALWAYS.test(f) || CFG_SHARED.test(f) && (() => {
|
|
10619
11711
|
const d = diff(f);
|
|
@@ -10701,7 +11793,7 @@ async function subtractBase(ctx, api, checks, tests, changed, wall, rel, signal)
|
|
|
10701
11793
|
const sus = pairs.filter(({ c }) => rel.has(c.name) && c.result === "pass");
|
|
10702
11794
|
if (!(un.some(({ c }) => c.result === "fail" && c.ids?.length) || sus.length) || signal.aborted)
|
|
10703
11795
|
return out2;
|
|
10704
|
-
const dir = mkdtempSync6(
|
|
11796
|
+
const dir = mkdtempSync6(join48(tmpdir8(), "e10-base-"));
|
|
10705
11797
|
const git3 = (args) => {
|
|
10706
11798
|
safeGit(ctx.repoDir, args, { stdio: "ignore", repoDrivers: true });
|
|
10707
11799
|
};
|
|
@@ -10728,7 +11820,7 @@ async function subtractBase(ctx, api, checks, tests, changed, wall, rel, signal)
|
|
|
10728
11820
|
try {
|
|
10729
11821
|
git3(["worktree", "remove", "--force", dir]);
|
|
10730
11822
|
} catch {}
|
|
10731
|
-
|
|
11823
|
+
rmSync12(dir, { recursive: true, force: true });
|
|
10732
11824
|
try {
|
|
10733
11825
|
git3(["worktree", "prune"]);
|
|
10734
11826
|
} catch {}
|
|
@@ -10736,7 +11828,7 @@ async function subtractBase(ctx, api, checks, tests, changed, wall, rel, signal)
|
|
|
10736
11828
|
return out2;
|
|
10737
11829
|
}
|
|
10738
11830
|
function isLokiModeRepo(repoDir) {
|
|
10739
|
-
return
|
|
11831
|
+
return existsSync28(join48(repoDir, "scripts", "select-tests.sh"));
|
|
10740
11832
|
}
|
|
10741
11833
|
async function runLintChecks(ctx, changed, signal, checks, opts = {}) {
|
|
10742
11834
|
const { api, ...runOpts } = opts;
|
|
@@ -10753,9 +11845,9 @@ async function runLintChecks(ctx, changed, signal, checks, opts = {}) {
|
|
|
10753
11845
|
}
|
|
10754
11846
|
for (const g of groupByPackage(api, ctx.repoDir, changed.filter((f) => /\.(ts|tsx|js|jsx)$/.test(f)))) {
|
|
10755
11847
|
const sfx = g.root === "." ? "" : `:${g.root}`, co = g.root === "." ? opts : { ...opts, cwd: g.cwd };
|
|
10756
|
-
if (
|
|
11848
|
+
if (existsSync28(join48(g.cwd, "tsconfig.json")))
|
|
10757
11849
|
await runCheck(ctx, `lint:tsc${sfx}`, "npx", ["tsc", "--noEmit", "-p", "."], signal, checks, { ...co, kind: "static" });
|
|
10758
|
-
if (ESLINT_CONFIGS.some((f) =>
|
|
11850
|
+
if (ESLINT_CONFIGS.some((f) => existsSync28(join48(g.cwd, f))))
|
|
10759
11851
|
await runCheck(ctx, `lint:eslint${sfx}`, "npx", ["eslint", ...g.rel], signal, checks, { ...co, kind: "static" });
|
|
10760
11852
|
}
|
|
10761
11853
|
}
|
|
@@ -10772,6 +11864,8 @@ var init_verify = __esm(() => {
|
|
|
10772
11864
|
init_types();
|
|
10773
11865
|
init_resolve();
|
|
10774
11866
|
init_safe_git();
|
|
11867
|
+
init_repo_profile();
|
|
11868
|
+
init_check_result();
|
|
10775
11869
|
init_check_result();
|
|
10776
11870
|
CODE_EXT = /\.(?:[cm]?[jt]sx?|py|go)$/;
|
|
10777
11871
|
CFG_ALWAYS = /(^|\/)(conftest\.py|\.?pytest\.(?:ini|toml)|tox\.ini|jest\.config\.[\w.]+|vitest\.config\.[\w.]+|\.mocharc[\w.]*)$/;
|
|
@@ -10796,13 +11890,22 @@ var init_verify = __esm(() => {
|
|
|
10796
11890
|
}
|
|
10797
11891
|
return { status: "failed", data: { changed_files: [] }, reason: "empty diff without an already_done marker" };
|
|
10798
11892
|
}
|
|
11893
|
+
let risk = (() => {
|
|
11894
|
+
try {
|
|
11895
|
+
const r = classifyRisk(diffFiles(ctx.repoDir, ctx.baseSha));
|
|
11896
|
+
recordRisk(ctx.repoDir, r);
|
|
11897
|
+
return r;
|
|
11898
|
+
} catch {
|
|
11899
|
+
return null;
|
|
11900
|
+
}
|
|
11901
|
+
})();
|
|
10799
11902
|
const checks = [];
|
|
10800
11903
|
const api = loadProjectApi(ctx.repoDir);
|
|
10801
11904
|
const map = await ctx.tests.detect(ctx.repoDir);
|
|
10802
11905
|
const impacted = ctx.tests.impacted(map, changed);
|
|
10803
11906
|
const changedTestFiles = map.tests.filter((t) => changed.includes(t.path));
|
|
10804
11907
|
const wall = ctx.outputs().wall ?? {};
|
|
10805
|
-
const wallPaths = new Set((wall.files ?? []).map((f) =>
|
|
11908
|
+
const wallPaths = new Set((wall.files ?? []).map((f) => isAbsolute6(f.path) ? relative5(ctx.repoDir, f.path) : f.path));
|
|
10806
11909
|
const wallTests = map.tests.filter((t) => wallPaths.has(t.path));
|
|
10807
11910
|
const intake = ctx.outputs().intake, relevant = ctx.tests.impacted(map, namedFiles(intake?.task ?? "", loadRepoMap(intake?.repomap_ref)));
|
|
10808
11911
|
const tests = dedupeTests([...impacted, ...changedTestFiles, ...wallTests, ...relevant]);
|
|
@@ -10817,6 +11920,9 @@ var init_verify = __esm(() => {
|
|
|
10817
11920
|
}
|
|
10818
11921
|
const targetNames = new Set([...wallTests, ...relevant].map((t) => `${t.runner}:${t.path}`));
|
|
10819
11922
|
const preRed = await subtractBase(ctx, api, checks, tests, changed, new Set(wallTests.map((t) => `${t.runner}:${t.path}`)), new Set(relevant.map((t) => `${t.runner}:${t.path}`)), signal);
|
|
11923
|
+
const attr = attribute(ctx.repoDir, ctx.baseSha, checks.filter((c) => c.result === "fail" && !preRed.names.includes(c.name)).map((c) => ({ name: c.name, ids: c.ids, eligible: !targetNames.has(c.name) && !changed.includes(c.name.slice(c.name.indexOf(":") + 1)) })));
|
|
11924
|
+
preRed.names.push(...attr.preExistingChecks);
|
|
11925
|
+
preRed.ids.push(...attr.preExistingIds);
|
|
10820
11926
|
if (!signal.aborted) {
|
|
10821
11927
|
await runLintChecks(ctx, changed, signal, checks, { api, wall: wallPaths });
|
|
10822
11928
|
if (isLokiModeRepo(ctx.repoDir)) {
|
|
@@ -10828,6 +11934,23 @@ var init_verify = __esm(() => {
|
|
|
10828
11934
|
});
|
|
10829
11935
|
}
|
|
10830
11936
|
}
|
|
11937
|
+
const fullNotProven = new Set;
|
|
11938
|
+
if (risk && risk.tier !== "low" && !signal.aborted) {
|
|
11939
|
+
const { runFullSuite: runFullSuite2 } = await Promise.resolve().then(() => (init_deep(), exports_deep));
|
|
11940
|
+
const full = [];
|
|
11941
|
+
await runFullSuite2(ctx, signal, {}, full, fullNotProven);
|
|
11942
|
+
for (const c of full)
|
|
11943
|
+
checks.push({ name: c.name, cmd: c.cmd, result: c.result, duration_s: c.duration_s, ...c.reason ? { reason: c.reason } : {} });
|
|
11944
|
+
const fa = attributeFullSuite(ctx.repoDir, ctx.baseSha, full, checks.filter((c) => c.result === "fail" && !preRed.names.includes(c.name)).flatMap((c) => c.ids ?? []), changed);
|
|
11945
|
+
preRed.names.push(...fa.preExistingChecks);
|
|
11946
|
+
preRed.ids.push(...fa.preExistingIds);
|
|
11947
|
+
attr.introducedChecks.push(...fa.introducedChecks);
|
|
11948
|
+
attr.line = [attr.line, fa.line].filter(Boolean).join("; ") || null;
|
|
11949
|
+
if (full.some((c) => c.result === "pass" || c.result === "fail")) {
|
|
11950
|
+
risk = markExecuted(risk, ["full test command", "full suite"]);
|
|
11951
|
+
recordRisk(ctx.repoDir, risk);
|
|
11952
|
+
}
|
|
11953
|
+
}
|
|
10831
11954
|
const cfgChanged = testConfigChanged(ctx.repoDir, ctx.baseSha, changed);
|
|
10832
11955
|
const covered = (f) => ctx.tests.impacted(map, [f]).some((t) => checks.some((c) => c.name === `${t.runner}:${t.path}` && hasExecutedProof([c])));
|
|
10833
11956
|
const uncoveredChanged = changed.filter((f) => CODE_EXT.test(f) && !isTestFile(f) && !wallPaths.has(f) && !cfgChanged.includes(f) && !covered(f));
|
|
@@ -10849,32 +11972,32 @@ var init_verify = __esm(() => {
|
|
|
10849
11972
|
return [`weakened test: ${t.path}`, ...assertDeltaNotes(ctx.repoDir, ctx.baseSha, null, t.path, intake?.task ?? "", k?.b, k?.h) ?? []];
|
|
10850
11973
|
});
|
|
10851
11974
|
const weakened = [...modifiedRel, ...cfgChanged.map((f) => `test configuration changed: ${f}`), ...preRed.weak.map((n2) => `skipped or fewer tests than base: ${n2}`)];
|
|
10852
|
-
const notProven = [...weakened, ...checks.filter((c) => c.owner === "harness").map((c) => `${c.reason} (${c.name}; harness-owned, no fix rounds)`), ...new Set(checks.filter((c) => c.interpreter === "system" && c.result !== "not_run").map((c) => c.name.startsWith("lint:") ? "lint ran on the system ruff" : "tests ran on the system interpreter"))];
|
|
10853
|
-
return { status: "completed", data: { checks, flaky, failures_grouped: failuresGrouped, changed_files: changed, not_proven: notProven, pre_red: preRed.ids, pre_red_checks: preRed.names, test_counts: testCounts, uncovered_changed: uncoveredChanged, target_checks: checks.filter((c) => c.result === "pass" && targetNames.has(c.name)).map((c) => c.name) } };
|
|
11975
|
+
const notProven = [...weakened, ...[...fullNotProven].filter((n2) => !n2.startsWith("full suite (no test runner")), ...checks.filter((c) => c.owner === "harness").map((c) => `${c.reason} (${c.name}; harness-owned, no fix rounds)`), ...new Set(checks.filter((c) => c.interpreter === "system" && c.result !== "not_run").map((c) => c.name.startsWith("lint:") ? "lint ran on the system ruff" : "tests ran on the system interpreter"))];
|
|
11976
|
+
return { status: "completed", data: { checks, flaky, failures_grouped: failuresGrouped, changed_files: changed, not_proven: notProven, pre_red: preRed.ids, pre_red_checks: preRed.names, ...attr.line ? { baseline_line: attr.line, introduced_checks: attr.introducedChecks } : {}, test_counts: testCounts, uncovered_changed: uncoveredChanged, target_checks: checks.filter((c) => c.result === "pass" && targetNames.has(c.name)).map((c) => c.name), risk } };
|
|
10854
11977
|
}
|
|
10855
11978
|
};
|
|
10856
11979
|
});
|
|
10857
11980
|
|
|
10858
11981
|
// ../../loki-ts/src/project_model/conventions.ts
|
|
10859
|
-
import { existsSync as
|
|
10860
|
-
import { dirname as
|
|
10861
|
-
function parseJsonc(
|
|
11982
|
+
import { existsSync as existsSync29, readdirSync as readdirSync13, readFileSync as readFileSync34 } from "fs";
|
|
11983
|
+
import { dirname as dirname17, join as join49 } from "path";
|
|
11984
|
+
function parseJsonc(text2) {
|
|
10862
11985
|
let out2 = "", i = 0;
|
|
10863
|
-
while (i <
|
|
10864
|
-
const c =
|
|
11986
|
+
while (i < text2.length) {
|
|
11987
|
+
const c = text2[i], n2 = text2[i + 1];
|
|
10865
11988
|
if (c === '"') {
|
|
10866
11989
|
let j = i + 1;
|
|
10867
|
-
while (j <
|
|
10868
|
-
j +=
|
|
10869
|
-
out2 +=
|
|
11990
|
+
while (j < text2.length && text2[j] !== '"')
|
|
11991
|
+
j += text2[j] === "\\" ? 2 : 1;
|
|
11992
|
+
out2 += text2.slice(i, j + 1);
|
|
10870
11993
|
i = j + 1;
|
|
10871
11994
|
} else if (c === "/" && n2 === "/") {
|
|
10872
|
-
while (i <
|
|
11995
|
+
while (i < text2.length && text2[i] !== `
|
|
10873
11996
|
`)
|
|
10874
11997
|
i++;
|
|
10875
11998
|
} else if (c === "/" && n2 === "*") {
|
|
10876
|
-
const e =
|
|
10877
|
-
i = e < 0 ?
|
|
11999
|
+
const e = text2.indexOf("*/", i + 2);
|
|
12000
|
+
i = e < 0 ? text2.length : e + 2;
|
|
10878
12001
|
} else {
|
|
10879
12002
|
out2 += c;
|
|
10880
12003
|
i++;
|
|
@@ -10883,7 +12006,7 @@ function parseJsonc(text3) {
|
|
|
10883
12006
|
return JSON.parse(out2.replace(/,(\s*[}\]])/g, "$1"));
|
|
10884
12007
|
}
|
|
10885
12008
|
function moduleOf(file, depth = 0) {
|
|
10886
|
-
const j =
|
|
12009
|
+
const j = readJson4(file);
|
|
10887
12010
|
if (!j || depth > 4)
|
|
10888
12011
|
return null;
|
|
10889
12012
|
const m = j.compilerOptions?.module;
|
|
@@ -10892,25 +12015,25 @@ function moduleOf(file, depth = 0) {
|
|
|
10892
12015
|
const ext = typeof j.extends === "string" ? j.extends : null;
|
|
10893
12016
|
if (!ext || !ext.startsWith("."))
|
|
10894
12017
|
return null;
|
|
10895
|
-
const next =
|
|
10896
|
-
return
|
|
12018
|
+
const next = join49(dirname17(file), ext.endsWith(".json") ? ext : `${ext}.json`);
|
|
12019
|
+
return existsSync29(next) ? moduleOf(next, depth + 1) : null;
|
|
10897
12020
|
}
|
|
10898
12021
|
function readPackageConventions(pkgDir, runner = null) {
|
|
10899
12022
|
let names = [];
|
|
10900
12023
|
try {
|
|
10901
|
-
names =
|
|
12024
|
+
names = readdirSync13(pkgDir).filter((f) => f.endsWith(".json")).sort((x, y) => x.length - y.length || x.localeCompare(y));
|
|
10902
12025
|
} catch {
|
|
10903
12026
|
return null;
|
|
10904
12027
|
}
|
|
10905
12028
|
let packageType = null, tsModule = null;
|
|
10906
12029
|
for (const n2 of names) {
|
|
10907
|
-
const j =
|
|
12030
|
+
const j = readJson4(join49(pkgDir, n2));
|
|
10908
12031
|
if (!j)
|
|
10909
12032
|
continue;
|
|
10910
12033
|
if (packageType === null && typeof j.type === "string" && typeof j.name === "string")
|
|
10911
12034
|
packageType = j.type;
|
|
10912
12035
|
if (tsModule === null && j.compilerOptions !== undefined)
|
|
10913
|
-
tsModule = moduleOf(
|
|
12036
|
+
tsModule = moduleOf(join49(pkgDir, n2));
|
|
10914
12037
|
}
|
|
10915
12038
|
if (packageType === null && tsModule === null)
|
|
10916
12039
|
return null;
|
|
@@ -10930,9 +12053,9 @@ function conventionViolation(c, fileName, content) {
|
|
|
10930
12053
|
const code = content.replace(/\/\*[\s\S]*?\*\//g, "").replace(/(^|[^:])\/\/.*$/gm, "$1").replace(/(["'`])(?:\\.|(?!\1)[^\\\n])*\1/g, '""');
|
|
10931
12054
|
return /\bimport\.meta\b/.test(code) ? "import.meta is not allowed in a CommonJS package (TS1470)" : null;
|
|
10932
12055
|
}
|
|
10933
|
-
var
|
|
12056
|
+
var readJson4 = (p) => {
|
|
10934
12057
|
try {
|
|
10935
|
-
const v = parseJsonc(
|
|
12058
|
+
const v = parseJsonc(readFileSync34(p, "utf8"));
|
|
10936
12059
|
return v && typeof v === "object" && !Array.isArray(v) ? v : null;
|
|
10937
12060
|
} catch {
|
|
10938
12061
|
return null;
|
|
@@ -10942,15 +12065,15 @@ var init_conventions = () => {};
|
|
|
10942
12065
|
|
|
10943
12066
|
// ../../loki-ts/src/engine10/stages/wall.ts
|
|
10944
12067
|
import { spawnSync as spawnSync4 } from "child_process";
|
|
10945
|
-
import { existsSync as
|
|
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";
|
|
10946
12069
|
import { tmpdir as tmpdir9 } from "os";
|
|
10947
|
-
import { dirname as
|
|
12070
|
+
import { dirname as dirname18, isAbsolute as isAbsolute7, join as join50, relative as relative6 } from "path";
|
|
10948
12071
|
function pytestCollectionIsRed(output, repoDir) {
|
|
10949
12072
|
if (/ModuleNotFoundError/.test(output))
|
|
10950
12073
|
return false;
|
|
10951
12074
|
const real2 = (p) => {
|
|
10952
12075
|
try {
|
|
10953
|
-
return
|
|
12076
|
+
return realpathSync18(isAbsolute7(p) ? p : join50(repoDir, p));
|
|
10954
12077
|
} catch {
|
|
10955
12078
|
return null;
|
|
10956
12079
|
}
|
|
@@ -10987,7 +12110,7 @@ function pytestCollectionIsRed(output, repoDir) {
|
|
|
10987
12110
|
}
|
|
10988
12111
|
function moduleUnderRepo(repoDir, name) {
|
|
10989
12112
|
const rel = name.replace(/\./g, "/");
|
|
10990
|
-
return
|
|
12113
|
+
return existsSync30(join50(repoDir, `${rel}.py`)) || existsSync30(join50(repoDir, rel, "__init__.py"));
|
|
10991
12114
|
}
|
|
10992
12115
|
function pytestExit1IsRed(output) {
|
|
10993
12116
|
if (/^!+ _pytest\.outcomes\.Exit\b/m.test(output))
|
|
@@ -11082,22 +12205,22 @@ ${repomapText}`] : [],
|
|
|
11082
12205
|
function loadTaskText(ctx, fromPrior) {
|
|
11083
12206
|
if (fromPrior)
|
|
11084
12207
|
return fromPrior;
|
|
11085
|
-
const issueJsonPath = process.env.LOKI_E10_ISSUE_JSON ??
|
|
11086
|
-
if (
|
|
12208
|
+
const issueJsonPath = process.env.LOKI_E10_ISSUE_JSON ?? join50(ctx.runDir, "issue.json");
|
|
12209
|
+
if (existsSync30(issueJsonPath)) {
|
|
11087
12210
|
try {
|
|
11088
|
-
const issue = JSON.parse(
|
|
11089
|
-
const
|
|
12211
|
+
const issue = JSON.parse(readFileSync35(issueJsonPath, "utf8"));
|
|
12212
|
+
const text2 = [issue.title, issue.body].filter((s) => typeof s === "string" && s.length > 0).join(`
|
|
11090
12213
|
|
|
11091
12214
|
`);
|
|
11092
|
-
if (
|
|
11093
|
-
return
|
|
12215
|
+
if (text2)
|
|
12216
|
+
return text2;
|
|
11094
12217
|
} catch {}
|
|
11095
12218
|
}
|
|
11096
12219
|
return process.env.LOKI_E10_TASK_TEXT ?? "";
|
|
11097
12220
|
}
|
|
11098
12221
|
function wallTargetDir(repoDir, existingTests) {
|
|
11099
12222
|
const first = existingTests[0];
|
|
11100
|
-
return first ?
|
|
12223
|
+
return first ? join50(repoDir, dirname18(first.path)) : join50(repoDir, "tests");
|
|
11101
12224
|
}
|
|
11102
12225
|
function guessRunner(fileName, runners) {
|
|
11103
12226
|
if (fileName.endsWith(".py"))
|
|
@@ -11128,11 +12251,11 @@ async function wallAuthor(ctx, signal) {
|
|
|
11128
12251
|
const tree = prior.intake?.tree;
|
|
11129
12252
|
const repomapText = repoMapText(ctx.repoDir, tree, repomapRef, WALL_MAP_MAX_LINES);
|
|
11130
12253
|
const targetDir = wallTargetDir(ctx.repoDir, existingTests), pkg = loadProjectApi(ctx.repoDir)?.packageOf(`${relative6(ctx.repoDir, targetDir)}/x`);
|
|
11131
|
-
const conv2 = readPackageConventions(
|
|
11132
|
-
const cwd = mkdtempSync7(
|
|
12254
|
+
const conv2 = readPackageConventions(join50(ctx.repoDir, pkg?.root ?? "."), pkg?.runner ?? null);
|
|
12255
|
+
const cwd = mkdtempSync7(join50(tmpdir9(), "loki-e15-wall-"));
|
|
11133
12256
|
const wm = wallManifestFor(ctx.repoDir, tree, task);
|
|
11134
12257
|
for (const [n2, c] of [["task.md", task], ["repomap.txt", repomapText], ...wm ? [["wall_manifest.txt", wm.text]] : []])
|
|
11135
|
-
|
|
12258
|
+
writeFileSync20(join50(cwd, n2), c, "utf8");
|
|
11136
12259
|
const session = await ctx.sessions.run({
|
|
11137
12260
|
stage: "wall",
|
|
11138
12261
|
brief: buildWallBrief(task, repomapText, runners, !!wm, conventionsBrief(conv2)),
|
|
@@ -11144,30 +12267,30 @@ async function wallAuthor(ctx, signal) {
|
|
|
11144
12267
|
cwd
|
|
11145
12268
|
});
|
|
11146
12269
|
if (session.killed || session.exit === null) {
|
|
11147
|
-
|
|
12270
|
+
rmSync13(cwd, { recursive: true, force: true });
|
|
11148
12271
|
return done({ status: "failed", data: {}, reason: "wall session aborted, killed, or timed out", killed: true });
|
|
11149
12272
|
}
|
|
11150
|
-
const generated =
|
|
11151
|
-
const sealedDir =
|
|
12273
|
+
const generated = readdirSync14(cwd).filter((f) => f.startsWith(WALL_PREFIX));
|
|
12274
|
+
const sealedDir = join50(ctx.runDir, "wall");
|
|
11152
12275
|
if (generated.length > 0)
|
|
11153
|
-
|
|
12276
|
+
mkdirSync18(sealedDir, { recursive: true });
|
|
11154
12277
|
const contents = new Map;
|
|
11155
12278
|
const discarded = [];
|
|
11156
12279
|
for (const name of generated) {
|
|
11157
|
-
const content =
|
|
11158
|
-
|
|
12280
|
+
const content = readFileSync35(join50(cwd, name), "utf8"), bad = conventionViolation(conv2, name, content);
|
|
12281
|
+
writeFileSync20(join50(sealedDir, name), content, "utf8");
|
|
11159
12282
|
if (bad)
|
|
11160
12283
|
discarded.push({ file: name, reason: `${bad}; wall test did not compile under the package config` });
|
|
11161
12284
|
else
|
|
11162
12285
|
contents.set(name, content);
|
|
11163
12286
|
}
|
|
11164
|
-
|
|
12287
|
+
rmSync13(cwd, { recursive: true, force: true });
|
|
11165
12288
|
return { kind: "authored", task, sizeName: sz.size, runners, targetDir, generated, contents, discarded, ...wm ? { manifestSha256: wm.sha256 } : {} };
|
|
11166
12289
|
}
|
|
11167
12290
|
function installWall(ctx, a, baseDir, opts = {}) {
|
|
11168
12291
|
const { generated, contents, discarded, targetDir, runners } = a;
|
|
11169
12292
|
if (generated.length > 0)
|
|
11170
|
-
|
|
12293
|
+
mkdirSync18(targetDir, { recursive: true });
|
|
11171
12294
|
const sealedFiles = [];
|
|
11172
12295
|
const readOnlyFiles = [];
|
|
11173
12296
|
const wallTests = [];
|
|
@@ -11175,8 +12298,8 @@ function installWall(ctx, a, baseDir, opts = {}) {
|
|
|
11175
12298
|
const content = contents.get(name);
|
|
11176
12299
|
if (content === undefined)
|
|
11177
12300
|
continue;
|
|
11178
|
-
const dest =
|
|
11179
|
-
|
|
12301
|
+
const dest = join50(targetDir, name), runner = guessRunner(name, runners);
|
|
12302
|
+
writeFileSync20(dest, content, "utf8");
|
|
11180
12303
|
sealedFiles.push({ path: dest, sha256: sha2562(content) });
|
|
11181
12304
|
readOnlyFiles.push({ path: dest, content });
|
|
11182
12305
|
if (runner)
|
|
@@ -11185,13 +12308,13 @@ function installWall(ctx, a, baseDir, opts = {}) {
|
|
|
11185
12308
|
ctx.emit("wall.sealed", "wall", { files: sealedFiles, ...a.manifestSha256 ? { manifest_sha256: a.manifestSha256 } : {} });
|
|
11186
12309
|
const baseRunner = opts.baseRunner ?? new RealBaseTestRunner, baseRun = { pass: 0, fail: 0, not_run: discarded.length }, notRunFiles = [];
|
|
11187
12310
|
for (const t of wallTests) {
|
|
11188
|
-
const r = baseRunner.run(baseDir, [t]), abs =
|
|
12311
|
+
const r = baseRunner.run(baseDir, [t]), abs = join50(ctx.repoDir, t.path);
|
|
11189
12312
|
baseRun.pass += r.pass;
|
|
11190
12313
|
baseRun.fail += r.fail;
|
|
11191
12314
|
baseRun.not_run += r.not_run ?? 0;
|
|
11192
12315
|
notRunFiles.push(...baseRunner.lastNotRunFiles ?? []);
|
|
11193
12316
|
if (r.pass + r.fail === 0) {
|
|
11194
|
-
|
|
12317
|
+
rmSync13(abs, { force: true });
|
|
11195
12318
|
for (const l of [sealedFiles, readOnlyFiles])
|
|
11196
12319
|
l.splice(0, l.length, ...l.filter((f) => f.path !== abs));
|
|
11197
12320
|
}
|
|
@@ -11215,7 +12338,7 @@ async function runWall(ctx, signal, opts = {}) {
|
|
|
11215
12338
|
const a = await wallAuthor(ctx, signal);
|
|
11216
12339
|
return a.kind === "done" ? a.result : installWall(ctx, a, ctx.repoDir, opts);
|
|
11217
12340
|
}
|
|
11218
|
-
var WALL_PREFIX = "loki_wall_", BASE_RUN_TIMEOUT_MS = 60000, WALL_MAP_MAX_LINES = 200, wallStage,
|
|
12341
|
+
var WALL_PREFIX = "loki_wall_", BASE_RUN_TIMEOUT_MS = 60000, WALL_MAP_MAX_LINES = 200, wallStage, stage2;
|
|
11219
12342
|
var init_wall = __esm(() => {
|
|
11220
12343
|
init_types();
|
|
11221
12344
|
init_lean_prefix();
|
|
@@ -11232,24 +12355,24 @@ var init_wall = __esm(() => {
|
|
|
11232
12355
|
limitS: 300,
|
|
11233
12356
|
run: (ctx, signal) => runWall(ctx, signal)
|
|
11234
12357
|
};
|
|
11235
|
-
|
|
12358
|
+
stage2 = Object.assign(wallStage, { split: { author: wallAuthor, install: installWall } });
|
|
11236
12359
|
});
|
|
11237
12360
|
|
|
11238
12361
|
// ../../loki-ts/src/e10ext/discard.ts
|
|
11239
|
-
import { chmodSync as chmodSync4, closeSync as
|
|
11240
|
-
import { dirname as
|
|
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";
|
|
11241
12364
|
function safeRestore(repoDir, f, want, mode) {
|
|
11242
|
-
const root =
|
|
12365
|
+
const root = realpathSync19(repoDir), fp = join51(root, f), rel = relative7(root, dirname19(fp));
|
|
11243
12366
|
if (rel.startsWith(".."))
|
|
11244
12367
|
throw new Error("outside repo");
|
|
11245
12368
|
let cur = root;
|
|
11246
12369
|
for (const part of rel === "" ? [] : rel.split(sep11)) {
|
|
11247
|
-
cur =
|
|
12370
|
+
cur = join51(cur, part);
|
|
11248
12371
|
let st2;
|
|
11249
12372
|
try {
|
|
11250
|
-
st2 =
|
|
12373
|
+
st2 = lstatSync10(cur);
|
|
11251
12374
|
} catch {
|
|
11252
|
-
|
|
12375
|
+
mkdirSync19(cur);
|
|
11253
12376
|
continue;
|
|
11254
12377
|
}
|
|
11255
12378
|
if (!st2.isDirectory())
|
|
@@ -11257,11 +12380,11 @@ function safeRestore(repoDir, f, want, mode) {
|
|
|
11257
12380
|
}
|
|
11258
12381
|
let st;
|
|
11259
12382
|
try {
|
|
11260
|
-
st =
|
|
12383
|
+
st = lstatSync10(fp);
|
|
11261
12384
|
} catch {
|
|
11262
12385
|
st = null;
|
|
11263
12386
|
}
|
|
11264
|
-
if (st?.isFile() &&
|
|
12387
|
+
if (st?.isFile() && readFileSync36(fp).equals(want)) {
|
|
11265
12388
|
chmodSync4(fp, mode);
|
|
11266
12389
|
return;
|
|
11267
12390
|
}
|
|
@@ -11269,13 +12392,13 @@ function safeRestore(repoDir, f, want, mode) {
|
|
|
11269
12392
|
if (st.isDirectory())
|
|
11270
12393
|
rmdirSync(fp);
|
|
11271
12394
|
else
|
|
11272
|
-
|
|
12395
|
+
unlinkSync4(fp);
|
|
11273
12396
|
}
|
|
11274
|
-
const fd =
|
|
12397
|
+
const fd = openSync8(fp, "wx", mode);
|
|
11275
12398
|
try {
|
|
11276
|
-
|
|
12399
|
+
writeFileSync21(fd, want);
|
|
11277
12400
|
} finally {
|
|
11278
|
-
|
|
12401
|
+
closeSync8(fd);
|
|
11279
12402
|
}
|
|
11280
12403
|
chmodSync4(fp, mode);
|
|
11281
12404
|
}
|
|
@@ -11283,10 +12406,10 @@ function alreadySatisfied(o) {
|
|
|
11283
12406
|
const b = o.wall?.base_run ?? {};
|
|
11284
12407
|
return o.intake?.already_satisfied === true || o.implement?.exit === "already_done" || typeof b.pass === "number" && b.pass > 0 && b.fail === 0 && (b.not_run ?? 0) === 0;
|
|
11285
12408
|
}
|
|
11286
|
-
function catBlob(repoDir,
|
|
11287
|
-
const r = safeGitSpawn(repoDir, ["cat-file", "blob",
|
|
12409
|
+
function catBlob(repoDir, sha2) {
|
|
12410
|
+
const r = safeGitSpawn(repoDir, ["cat-file", "blob", sha2], { maxBuffer: 1 << 28 });
|
|
11288
12411
|
if (r.error || r.status !== 0)
|
|
11289
|
-
throw r.error ?? new Error(`git cat-file blob ${
|
|
12412
|
+
throw r.error ?? new Error(`git cat-file blob ${sha2} failed`);
|
|
11290
12413
|
return r.stdout;
|
|
11291
12414
|
}
|
|
11292
12415
|
async function discardIfSatisfied(git3, base, o, staged, keep, repoDir) {
|
|
@@ -11299,15 +12422,15 @@ async function discardIfSatisfied(git3, base, o, staged, keep, repoDir) {
|
|
|
11299
12422
|
return { status: "failed", data: {}, reason: "already-satisfied discard failed" };
|
|
11300
12423
|
const notProven = [];
|
|
11301
12424
|
for (const [f, v] of Object.entries(o.intake?.preexisting_untracked_blobs ?? {})) {
|
|
11302
|
-
const fp =
|
|
12425
|
+
const fp = join51(repoDir, f);
|
|
11303
12426
|
try {
|
|
11304
12427
|
if (v.startsWith("!")) {
|
|
11305
|
-
const st =
|
|
12428
|
+
const st = lstatSync10(fp);
|
|
11306
12429
|
if (`!${st.size}:${st.mtimeMs}:${st.ctimeMs}` !== v)
|
|
11307
12430
|
throw new Error("changed, not snapshotted");
|
|
11308
12431
|
continue;
|
|
11309
12432
|
}
|
|
11310
|
-
const [
|
|
12433
|
+
const [sha2, mode] = v.split(" "), want = catBlob(repoDir, sha2);
|
|
11311
12434
|
safeRestore(repoDir, f, want, parseInt(mode ?? "644", 8));
|
|
11312
12435
|
} catch {
|
|
11313
12436
|
notProven.push(f);
|
|
@@ -11318,7 +12441,7 @@ async function discardIfSatisfied(git3, base, o, staged, keep, repoDir) {
|
|
|
11318
12441
|
try {
|
|
11319
12442
|
let m = 420;
|
|
11320
12443
|
try {
|
|
11321
|
-
const l =
|
|
12444
|
+
const l = lstatSync10(join51(repoDir, f));
|
|
11322
12445
|
if (l.isFile())
|
|
11323
12446
|
m = l.mode & 511;
|
|
11324
12447
|
} catch {}
|
|
@@ -11341,93 +12464,28 @@ function untouchedSinceIntake(repoDir, recorded, env = process.env) {
|
|
|
11341
12464
|
try {
|
|
11342
12465
|
return blob2(repoDir, p, env) === h;
|
|
11343
12466
|
} catch {
|
|
11344
|
-
return false;
|
|
11345
|
-
}
|
|
11346
|
-
}).map(([p]) => p);
|
|
11347
|
-
}
|
|
11348
|
-
var blob2 = (repoDir, path, env = process.env) => safeGit(repoDir, ["hash-object", "-w", "--no-filters", "--", path], { env }).trim();
|
|
11349
|
-
var init_preexisting_dirty = __esm(() => {
|
|
11350
|
-
init_safe_git();
|
|
11351
|
-
});
|
|
11352
|
-
|
|
11353
|
-
// ../../loki-ts/src/e10ext/commit_filter.ts
|
|
11354
|
-
import { dirname as dirname16 } from "path";
|
|
11355
|
-
function dropSet(repoDir, staged, preexistingDirty, env = process.env) {
|
|
11356
|
-
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) && dirname16(m) === dirname16(f)) && /(^|\/)(package-lock\.json|yarn\.lock|pnpm-lock\.yaml|bun\.lockb?|poetry\.lock|Cargo\.lock|go\.sum)$/.test(f));
|
|
11357
|
-
}
|
|
11358
|
-
var parseStaged = (out2) => out2.split("\x00").reduce((a, t, i, all) => i % 2 === 0 && t ? [...a, { st: t, f: all[i + 1] }] : a, []);
|
|
11359
|
-
var init_commit_filter = __esm(() => {
|
|
11360
|
-
init_preexisting_dirty();
|
|
11361
|
-
init_safe_git();
|
|
11362
|
-
});
|
|
11363
|
-
|
|
11364
|
-
// ../../loki-ts/src/features/speed/unit_mode.ts
|
|
11365
|
-
import { closeSync as closeSync7, constants as constants2, fstatSync as fstatSync4, lstatSync as lstatSync8, openSync as openSync7, readSync as readSync5, realpathSync as realpathSync19 } from "fs";
|
|
11366
|
-
function readBounded(path) {
|
|
11367
|
-
const l = lstatSync8(path);
|
|
11368
|
-
if (l.isSymbolicLink() || !l.isFile() || l.size > MAX_SPEC_BYTES)
|
|
11369
|
-
return null;
|
|
11370
|
-
const fd = openSync7(path, constants2.O_RDONLY | constants2.O_NOFOLLOW | constants2.O_NONBLOCK);
|
|
11371
|
-
try {
|
|
11372
|
-
const st = fstatSync4(fd);
|
|
11373
|
-
if (!st.isFile() || st.size > MAX_SPEC_BYTES)
|
|
11374
|
-
return null;
|
|
11375
|
-
const buf = Buffer.alloc(st.size + 1);
|
|
11376
|
-
let n2 = 0;
|
|
11377
|
-
for (;; ) {
|
|
11378
|
-
const r = readSync5(fd, buf, n2, buf.length - n2, null);
|
|
11379
|
-
if (r <= 0)
|
|
11380
|
-
break;
|
|
11381
|
-
n2 += r;
|
|
11382
|
-
if (n2 >= buf.length)
|
|
11383
|
-
return null;
|
|
11384
|
-
}
|
|
11385
|
-
return buf.subarray(0, n2).toString("utf8");
|
|
11386
|
-
} finally {
|
|
11387
|
-
closeSync7(fd);
|
|
11388
|
-
}
|
|
11389
|
-
}
|
|
11390
|
-
function parseText(txt) {
|
|
11391
|
-
try {
|
|
11392
|
-
const j = JSON.parse(txt), ws = strs(j.writeSet), pack = strs(j.pack), tb = j.tokenBudget;
|
|
11393
|
-
if (typeof j.id !== "string" || !j.id || !ws || ws.length === 0 || !pack || typeof tb !== "number" || !Number.isFinite(tb) || !(tb > 0) || tb > MAX_TOKEN_BUDGET)
|
|
11394
|
-
return null;
|
|
11395
|
-
return { id: j.id, writeSet: ws, pack, tokenBudget: tb };
|
|
11396
|
-
} catch {
|
|
11397
|
-
return null;
|
|
11398
|
-
}
|
|
11399
|
-
}
|
|
11400
|
-
function parseSpec(path) {
|
|
11401
|
-
try {
|
|
11402
|
-
const txt = readBounded(path);
|
|
11403
|
-
return txt === null ? null : parseText(txt);
|
|
11404
|
-
} catch {
|
|
11405
|
-
return null;
|
|
11406
|
-
}
|
|
11407
|
-
}
|
|
11408
|
-
function unitSpec(env = process.env) {
|
|
11409
|
-
const path = env["LOKI_UNIT_SPEC"], frozen = env["LOKI_UNIT_SPEC_FROZEN"];
|
|
11410
|
-
if (env["LOKI_SPEED"] === "0" || !path)
|
|
11411
|
-
return null;
|
|
11412
|
-
if (frozen !== undefined)
|
|
11413
|
-
return frozen.length <= MAX_SPEC_BYTES ? parseText(frozen) : null;
|
|
11414
|
-
if (!memo.has(path))
|
|
11415
|
-
memo.set(path, parseSpec(path));
|
|
11416
|
-
return memo.get(path) ?? null;
|
|
12467
|
+
return false;
|
|
12468
|
+
}
|
|
12469
|
+
}).map(([p]) => p);
|
|
11417
12470
|
}
|
|
11418
|
-
|
|
11419
|
-
|
|
11420
|
-
|
|
12471
|
+
var blob2 = (repoDir, path, env = process.env) => safeGit(repoDir, ["hash-object", "-w", "--no-filters", "--", path], { env }).trim();
|
|
12472
|
+
var init_preexisting_dirty = __esm(() => {
|
|
12473
|
+
init_safe_git();
|
|
12474
|
+
});
|
|
12475
|
+
|
|
12476
|
+
// ../../loki-ts/src/e10ext/commit_filter.ts
|
|
12477
|
+
import { dirname as dirname20 } from "path";
|
|
12478
|
+
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));
|
|
11421
12480
|
}
|
|
11422
|
-
var
|
|
11423
|
-
var
|
|
11424
|
-
|
|
11425
|
-
|
|
11426
|
-
memo = new Map;
|
|
12481
|
+
var parseStaged = (out2) => out2.split("\x00").reduce((a, t, i, all) => i % 2 === 0 && t ? [...a, { st: t, f: all[i + 1] }] : a, []);
|
|
12482
|
+
var init_commit_filter = __esm(() => {
|
|
12483
|
+
init_preexisting_dirty();
|
|
12484
|
+
init_safe_git();
|
|
11427
12485
|
});
|
|
11428
12486
|
|
|
11429
12487
|
// ../../loki-ts/src/e10ext/scope.ts
|
|
11430
|
-
import { basename as
|
|
12488
|
+
import { basename as basename18, dirname as dirname21 } from "path";
|
|
11431
12489
|
function contractText(o) {
|
|
11432
12490
|
const c = o.intake?.contract_snapshot?.contract;
|
|
11433
12491
|
return Array.isArray(c?.criteria) ? c.criteria.map((x) => typeof x?.text === "string" ? x.text : "").join(`
|
|
@@ -11438,18 +12496,18 @@ function outsideScope(o, staged) {
|
|
|
11438
12496
|
const plan = typeof o.plan?.plan === "string" ? o.plan.plan : "";
|
|
11439
12497
|
if (rel.length === 0 && plan.trim() === "")
|
|
11440
12498
|
return null;
|
|
11441
|
-
const
|
|
12499
|
+
const text2 = `${plan}
|
|
11442
12500
|
${typeof o.intake?.task === "string" ? o.intake.task : ""}
|
|
11443
12501
|
${contractText(o)}`, pre = o.intake?.preexisting_dirty ?? {};
|
|
11444
|
-
const toks = surfaceTokens(
|
|
11445
|
-
return staged.filter(({ st, f }) => st !== "A" && !isTestFile(f) && !Object.hasOwn(pre, f) && !rel.includes(f) && !mentions(
|
|
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);
|
|
11446
12504
|
}
|
|
11447
12505
|
function flagOutsideScope(o, staged) {
|
|
11448
12506
|
const unit = unitFlags(staged, process.env, o.intake?.preexisting_dirty ?? {});
|
|
11449
12507
|
const un = outsideScope(o, staged), seen = new Set((unit ?? []).map(outsideNote));
|
|
11450
12508
|
return [...(unit ?? []).map(outsideNote), ...un ? un.map(outsideNote).filter((n2) => !seen.has(n2)) : staged.length > 0 ? [SCOPE_UNDETERMINED] : []];
|
|
11451
12509
|
}
|
|
11452
|
-
var SCOPE_UNDETERMINED = "scope not determined; all edits committed", outsideNote = (f) => `outside stated scope: ${f}`, esc2 = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), mentions = (
|
|
12510
|
+
var SCOPE_UNDETERMINED = "scope not determined; all edits committed", outsideNote = (f) => `outside stated scope: ${f}`, esc2 = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), mentions = (text2, name) => new RegExp(`(?<![\\w./-])${esc2(name)}(?![\\w-]|\\.\\w)`).test(text2), STOP, stem = (w) => w.length > 3 && w.endsWith("s") ? w.slice(0, -1) : w, surfaceTokens = (text2) => new Set((text2.toLowerCase().match(/[a-z][a-z0-9_-]{2,}/g) ?? []).filter((w) => !STOP.has(w)).map(stem)), dirSegs = (f) => f.split("/").slice(0, -1).map((d) => stem(d.toLowerCase()));
|
|
11453
12511
|
var init_scope2 = __esm(() => {
|
|
11454
12512
|
init_testmap();
|
|
11455
12513
|
init_unit_mode();
|
|
@@ -11457,101 +12515,77 @@ var init_scope2 = __esm(() => {
|
|
|
11457
12515
|
});
|
|
11458
12516
|
|
|
11459
12517
|
// ../../loki-ts/src/util/receipt_signer.ts
|
|
11460
|
-
|
|
11461
|
-
|
|
11462
|
-
|
|
11463
|
-
import {
|
|
11464
|
-
|
|
11465
|
-
|
|
11466
|
-
|
|
11467
|
-
|
|
11468
|
-
|
|
11469
|
-
|
|
11470
|
-
|
|
11471
|
-
|
|
11472
|
-
|
|
11473
|
-
|
|
11474
|
-
|
|
11475
|
-
writeSync as writeSync3
|
|
11476
|
-
} from "fs";
|
|
11477
|
-
import { dirname as dirname18 } from "path";
|
|
11478
|
-
function durableWriteThenRename(target, body) {
|
|
11479
|
-
mkdirSync12(dirname18(target), { recursive: true });
|
|
11480
|
-
const tmp = `${target}.tmp.${process.pid}.${++_tmpCounter}`;
|
|
11481
|
-
const fd = openSync8(tmp, "w");
|
|
11482
|
-
try {
|
|
11483
|
-
writeSync3(fd, body);
|
|
11484
|
-
fsyncSync2(fd);
|
|
11485
|
-
} finally {
|
|
11486
|
-
closeSync8(fd);
|
|
11487
|
-
}
|
|
11488
|
-
renameSync5(tmp, target);
|
|
11489
|
-
fsyncParentDir(target);
|
|
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")}`);
|
|
11490
12533
|
}
|
|
11491
|
-
function
|
|
11492
|
-
let dfd = null;
|
|
12534
|
+
function loadSigningKey(generate = true) {
|
|
11493
12535
|
try {
|
|
11494
|
-
|
|
11495
|
-
|
|
11496
|
-
|
|
11497
|
-
|
|
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);
|
|
11498
12541
|
try {
|
|
11499
|
-
|
|
11500
|
-
|
|
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
|
+
}
|
|
11501
12565
|
}
|
|
11502
|
-
|
|
11503
|
-
|
|
11504
|
-
function atomicWriteJson(target, data) {
|
|
11505
|
-
durableWriteThenRename(target, `${JSON.stringify(data, null, 2)}
|
|
11506
|
-
`);
|
|
11507
|
-
}
|
|
11508
|
-
var _tmpCounter = 0, _appendChains;
|
|
11509
|
-
var init_atomic = __esm(() => {
|
|
11510
|
-
_appendChains = new Map;
|
|
11511
|
-
});
|
|
11512
|
-
|
|
11513
|
-
// ../../loki-ts/src/util/pr_lessons.ts
|
|
11514
|
-
import { existsSync as existsSync25, mkdirSync as mkdirSync13, readFileSync as readFileSync31 } from "fs";
|
|
11515
|
-
import { dirname as dirname19, join as join45 } from "path";
|
|
11516
|
-
function loadLessons(repoDir) {
|
|
11517
|
-
const p = lessonsPath(repoDir);
|
|
11518
|
-
if (!existsSync25(p))
|
|
11519
|
-
return [];
|
|
11520
|
-
try {
|
|
11521
|
-
const j = JSON.parse(readFileSync31(p, "utf-8"));
|
|
11522
|
-
return Array.isArray(j.lessons) ? j.lessons : [];
|
|
12566
|
+
const k = createPrivateKey(pem);
|
|
12567
|
+
return k.asymmetricKeyType === "ed25519" ? k : null;
|
|
11523
12568
|
} catch {
|
|
11524
|
-
return
|
|
12569
|
+
return null;
|
|
11525
12570
|
}
|
|
11526
12571
|
}
|
|
11527
|
-
function
|
|
11528
|
-
const
|
|
11529
|
-
|
|
11530
|
-
|
|
11531
|
-
|
|
11532
|
-
|
|
11533
|
-
|
|
11534
|
-
let dirty = false;
|
|
11535
|
-
for (const l of lessons)
|
|
11536
|
-
for (const u of l.uses)
|
|
11537
|
-
if (u.run_id === runId && u.verdict === null) {
|
|
11538
|
-
u.verdict = verdict;
|
|
11539
|
-
dirty = true;
|
|
11540
|
-
}
|
|
11541
|
-
if (dirty)
|
|
11542
|
-
save(repoDir, lessons);
|
|
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 };
|
|
11543
12579
|
}
|
|
11544
|
-
var
|
|
11545
|
-
var
|
|
11546
|
-
init_atomic();
|
|
11547
|
-
});
|
|
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 = () => {};
|
|
11548
12582
|
|
|
11549
12583
|
// ../../loki-ts/src/features/visual_evidence.ts
|
|
11550
12584
|
import { spawn as spawn7 } from "child_process";
|
|
11551
|
-
import { createHash as
|
|
11552
|
-
import { existsSync as
|
|
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";
|
|
11553
12587
|
import { createServer } from "net";
|
|
11554
|
-
import { dirname as
|
|
12588
|
+
import { dirname as dirname23, isAbsolute as isAbsolute8, join as join53, normalize as normalize2, relative as relative8, sep as sep12 } from "path";
|
|
11555
12589
|
function routeFor(file) {
|
|
11556
12590
|
let r = file.replace(/^(?:.*?\/)?(?:app|pages|src|public)\//, "").replace(/^(?:routes|pages)\//, "");
|
|
11557
12591
|
r = r.replace(/\.(?:html|jsx|tsx|vue|svelte)$/, "").replace(/\/?(?:page|index|\+page)$/, "");
|
|
@@ -11561,7 +12595,7 @@ function hashScreens(runDir, rels) {
|
|
|
11561
12595
|
const out2 = [];
|
|
11562
12596
|
for (const rel of rels) {
|
|
11563
12597
|
try {
|
|
11564
|
-
out2.push({ path: rel, sha256: sha256File(
|
|
12598
|
+
out2.push({ path: rel, sha256: sha256File(join53(runDir, rel)) });
|
|
11565
12599
|
} catch {}
|
|
11566
12600
|
}
|
|
11567
12601
|
return out2;
|
|
@@ -11572,16 +12606,16 @@ function checkScreens(runDir, screens) {
|
|
|
11572
12606
|
for (const s of screens) {
|
|
11573
12607
|
if (typeof s?.path !== "string" || typeof s.sha256 !== "string")
|
|
11574
12608
|
return "evidence_screens entry is malformed";
|
|
11575
|
-
if (
|
|
12609
|
+
if (isAbsolute8(s.path) || normalize2(s.path).startsWith(".."))
|
|
11576
12610
|
return `evidence screenshot path escapes the evidence dir: ${s.path}`;
|
|
11577
|
-
const file =
|
|
12611
|
+
const file = join53(runDir, s.path);
|
|
11578
12612
|
let cur = runDir;
|
|
11579
12613
|
for (const seg of normalize2(s.path).split(sep12)) {
|
|
11580
|
-
cur =
|
|
12614
|
+
cur = join53(cur, seg);
|
|
11581
12615
|
if (isSymlink(cur))
|
|
11582
12616
|
return `evidence screenshot path is a symlink: ${s.path}`;
|
|
11583
12617
|
}
|
|
11584
|
-
if (!
|
|
12618
|
+
if (!existsSync31(file))
|
|
11585
12619
|
return `evidence screenshot is missing: ${s.path}`;
|
|
11586
12620
|
if (!isRegularFile(file))
|
|
11587
12621
|
return `evidence screenshot is not a regular file: ${s.path}`;
|
|
@@ -11624,11 +12658,11 @@ async function waitUp(url, ms, signal) {
|
|
|
11624
12658
|
}
|
|
11625
12659
|
function openapiPaths(repoDir) {
|
|
11626
12660
|
for (const f of ["openapi.json", "openapi.yaml", "openapi.yml", "swagger.json"]) {
|
|
11627
|
-
const p =
|
|
11628
|
-
if (!
|
|
12661
|
+
const p = join53(repoDir, f);
|
|
12662
|
+
if (!existsSync31(p))
|
|
11629
12663
|
continue;
|
|
11630
12664
|
try {
|
|
11631
|
-
const txt =
|
|
12665
|
+
const txt = readFileSync38(p, "utf8");
|
|
11632
12666
|
const paths = f.endsWith(".json") ? Object.keys(JSON.parse(txt).paths ?? {}) : [...txt.matchAll(/^ {2}(\/[^\s:]*):\s*$/gm)].map((m) => m[1]);
|
|
11633
12667
|
return paths.filter((x) => !x.includes("{")).slice(0, 10);
|
|
11634
12668
|
} catch {
|
|
@@ -11642,24 +12676,24 @@ async function recordE2eMedia(repoDir, runDir, relDir, base, routes, ms, signal)
|
|
|
11642
12676
|
if (!pw || !pkg)
|
|
11643
12677
|
return { rels: [], skipped: "playwright e2e video and trace skipped: @playwright/test is not installed in the repo" };
|
|
11644
12678
|
try {
|
|
11645
|
-
const dir =
|
|
11646
|
-
|
|
11647
|
-
|
|
11648
|
-
|
|
12679
|
+
const dir = join53(runDir, relDir, "e2e");
|
|
12680
|
+
mkdirSync21(dir, { recursive: true });
|
|
12681
|
+
writeFileSync23(join53(dir, "walk.spec.cjs"), e2eSpecSource(pkg, base, routes));
|
|
12682
|
+
writeFileSync23(join53(dir, "playwright.config.cjs"), `module.exports = { testDir: ".", testMatch: "walk.spec.cjs", outputDir: "./out", reporter: "null", use: { video: "on", trace: "on" } };
|
|
11649
12683
|
`);
|
|
11650
|
-
if (!await shoot(pw, ["test", "--config",
|
|
12684
|
+
if (!await shoot(pw, ["test", "--config", join53(dir, "playwright.config.cjs")], repoDir, ms, signal))
|
|
11651
12685
|
return { rels: [], skipped: "playwright e2e video and trace skipped: the run failed or timed out" };
|
|
11652
12686
|
const rels = [];
|
|
11653
12687
|
const walk = (d) => {
|
|
11654
|
-
for (const e of
|
|
11655
|
-
const full =
|
|
12688
|
+
for (const e of readdirSync15(d, { withFileTypes: true })) {
|
|
12689
|
+
const full = join53(d, e.name);
|
|
11656
12690
|
if (e.isDirectory())
|
|
11657
12691
|
walk(full);
|
|
11658
12692
|
else if (e.isFile() && /\.(webm|zip)$/.test(e.name))
|
|
11659
|
-
rels.push(
|
|
12693
|
+
rels.push(join53(relDir, "e2e", relative8(dir, full)));
|
|
11660
12694
|
}
|
|
11661
12695
|
};
|
|
11662
|
-
walk(
|
|
12696
|
+
walk(join53(dir, "out"));
|
|
11663
12697
|
return rels.length > 0 ? { rels, skipped: null } : { rels, skipped: "playwright e2e video and trace skipped: no video or trace was produced" };
|
|
11664
12698
|
} catch (e) {
|
|
11665
12699
|
return { rels: [], skipped: `playwright e2e video and trace skipped: ${String(e?.message ?? e).slice(0, 120)}` };
|
|
@@ -11728,13 +12762,13 @@ async function captureVisualEvidence(repoDir, runDir, changed, opts = {}) {
|
|
|
11728
12762
|
if (!visualEvidenceEnabled(opts.env))
|
|
11729
12763
|
return skip("LOKI_VISUAL_EVIDENCE is 0");
|
|
11730
12764
|
const api = loadProjectApi(repoDir), boot = isMultiRoot(api) ? api.uiBoot() : null;
|
|
11731
|
-
const appDir = boot ?
|
|
11732
|
-
const pkgPath =
|
|
12765
|
+
const appDir = boot ? join53(repoDir, boot.boot.cwd) : repoDir;
|
|
12766
|
+
const pkgPath = join53(appDir, "package.json");
|
|
11733
12767
|
let script = null;
|
|
11734
12768
|
if (!boot) {
|
|
11735
|
-
if (!
|
|
12769
|
+
if (!existsSync31(pkgPath))
|
|
11736
12770
|
return skip("no package.json");
|
|
11737
|
-
script = pickScript(JSON.parse(
|
|
12771
|
+
script = pickScript(JSON.parse(readFileSync38(pkgPath, "utf8")));
|
|
11738
12772
|
if (!script)
|
|
11739
12773
|
return skip("no dev, preview or start script");
|
|
11740
12774
|
}
|
|
@@ -11761,20 +12795,20 @@ async function captureVisualEvidence(repoDir, runDir, changed, opts = {}) {
|
|
|
11761
12795
|
}
|
|
11762
12796
|
if (!await waitUp(base, left(), signal))
|
|
11763
12797
|
return skip(aborted() ? "aborted" : `${script} server did not answer within ${Math.round(budget / 1000)}s`);
|
|
11764
|
-
const evRoot =
|
|
12798
|
+
const evRoot = join53(runDir, "evidence");
|
|
11765
12799
|
if (isSymlink(evRoot))
|
|
11766
12800
|
return skip("evidence dir is a symlink");
|
|
11767
|
-
|
|
12801
|
+
mkdirSync21(evRoot, { recursive: true });
|
|
11768
12802
|
if (pages.length > 0) {
|
|
11769
|
-
const rel0 =
|
|
11770
|
-
|
|
12803
|
+
const rel0 = join53("evidence", randomBytes4(6).toString("hex"));
|
|
12804
|
+
mkdirSync21(join53(runDir, rel0));
|
|
11771
12805
|
const rels = [];
|
|
11772
12806
|
for (const route of [...new Set(pages.map(routeFor))].slice(0, opts.maxRoutes ?? MAX_ROUTES)) {
|
|
11773
12807
|
if (aborted() || left() <= 0)
|
|
11774
12808
|
break;
|
|
11775
|
-
const rel =
|
|
11776
|
-
const ok = await shoot(pw, ["screenshot", `${base}${route}`,
|
|
11777
|
-
if (ok && !aborted() && !isSymlink(
|
|
12809
|
+
const rel = join53(rel0, `${screenName(route)}.png`);
|
|
12810
|
+
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)))
|
|
11778
12812
|
rels.push(rel);
|
|
11779
12813
|
}
|
|
11780
12814
|
if (aborted())
|
|
@@ -11799,9 +12833,9 @@ async function captureVisualEvidence(repoDir, runDir, changed, opts = {}) {
|
|
|
11799
12833
|
}
|
|
11800
12834
|
if (aborted())
|
|
11801
12835
|
return skip("aborted");
|
|
11802
|
-
const hdir =
|
|
11803
|
-
|
|
11804
|
-
|
|
12836
|
+
const hdir = join53(evRoot, randomBytes4(6).toString("hex"));
|
|
12837
|
+
mkdirSync21(hdir);
|
|
12838
|
+
writeFileSync23(join53(hdir, "http.json"), JSON.stringify(rows, null, 2) + `
|
|
11805
12839
|
`);
|
|
11806
12840
|
return { screens: [], http: true, skipped: null };
|
|
11807
12841
|
} catch (e) {
|
|
@@ -11824,27 +12858,27 @@ async function sealEvidence(repoDir, runDir, o, notProven, signal, emit) {
|
|
|
11824
12858
|
return ev.screens.length > 0 ? { evidence_screens: ev.screens } : {};
|
|
11825
12859
|
}
|
|
11826
12860
|
function receiptScreensProblem(receiptPath, receipt) {
|
|
11827
|
-
return receipt["evidence_screens"] === undefined ? null : checkScreens(
|
|
12861
|
+
return receipt["evidence_screens"] === undefined ? null : checkScreens(dirname23(receiptPath), receipt["evidence_screens"]);
|
|
11828
12862
|
}
|
|
11829
|
-
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) =>
|
|
11830
|
-
const bin =
|
|
11831
|
-
return
|
|
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) => {
|
|
12864
|
+
const bin = join53(repoDir, "node_modules", ".bin", "playwright");
|
|
12865
|
+
return existsSync31(bin) ? bin : null;
|
|
11832
12866
|
}, playwrightTestPkg = (repoDir) => {
|
|
11833
|
-
const d =
|
|
11834
|
-
return
|
|
12867
|
+
const d = join53(repoDir, "node_modules", "@playwright", "test");
|
|
12868
|
+
return existsSync31(join53(d, "package.json")) ? d : null;
|
|
11835
12869
|
}, e2eSpecSource = (pkgDir, base, routes) => `const { test } = require(${JSON.stringify(pkgDir)});
|
|
11836
12870
|
test("loki e2e walkthrough", async ({ page }) => {
|
|
11837
12871
|
for (const r of ${JSON.stringify(routes)}) { await page.goto(${JSON.stringify(base)} + r); await page.waitForLoadState("load"); }
|
|
11838
12872
|
});
|
|
11839
12873
|
`, DEFAULT_BUDGET_MS = 25000, MAX_ROUTES = 5, isSymlink = (p) => {
|
|
11840
12874
|
try {
|
|
11841
|
-
return
|
|
12875
|
+
return lstatSync11(p).isSymbolicLink();
|
|
11842
12876
|
} catch {
|
|
11843
12877
|
return false;
|
|
11844
12878
|
}
|
|
11845
12879
|
}, isRegularFile = (p) => {
|
|
11846
12880
|
try {
|
|
11847
|
-
return
|
|
12881
|
+
return lstatSync11(p).isFile();
|
|
11848
12882
|
} catch {
|
|
11849
12883
|
return false;
|
|
11850
12884
|
}
|
|
@@ -11862,14 +12896,14 @@ var init_visual_evidence = __esm(() => {
|
|
|
11862
12896
|
});
|
|
11863
12897
|
|
|
11864
12898
|
// ../../loki-ts/src/council/role_agents.ts
|
|
11865
|
-
import { existsSync as
|
|
11866
|
-
import { homedir as
|
|
11867
|
-
import { join as
|
|
12899
|
+
import { existsSync as existsSync32, readdirSync as readdirSync16, readFileSync as readFileSync39 } from "fs";
|
|
12900
|
+
import { homedir as homedir9 } from "os";
|
|
12901
|
+
import { join as join54 } from "path";
|
|
11868
12902
|
function agentDirs(repoDir, env = process.env) {
|
|
11869
|
-
return [env["LOKI_AGENTS_PROJECT_DIR"] ||
|
|
12903
|
+
return [env["LOKI_AGENTS_PROJECT_DIR"] || join54(repoDir, ".loki", "agents"), env["LOKI_AGENTS_USER_DIR"] || join54(env["XDG_CONFIG_HOME"] || join54(homedir9(), ".config"), "loki", "agents")];
|
|
11870
12904
|
}
|
|
11871
12905
|
function parseAgent(file, stem2) {
|
|
11872
|
-
const m = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/.exec(
|
|
12906
|
+
const m = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/.exec(readFileSync39(file, "utf8"));
|
|
11873
12907
|
if (!m)
|
|
11874
12908
|
return null;
|
|
11875
12909
|
const body = m[2].trim();
|
|
@@ -11883,18 +12917,18 @@ function parseAgent(file, stem2) {
|
|
|
11883
12917
|
}
|
|
11884
12918
|
function loadRoleAgent(repoDir, role, env = process.env) {
|
|
11885
12919
|
for (const d of agentDirs(repoDir, env)) {
|
|
11886
|
-
if (!
|
|
12920
|
+
if (!existsSync32(d))
|
|
11887
12921
|
continue;
|
|
11888
12922
|
let names;
|
|
11889
12923
|
try {
|
|
11890
|
-
names =
|
|
12924
|
+
names = readdirSync16(d).filter((n2) => n2.endsWith(".md") && !n2.startsWith(".")).sort();
|
|
11891
12925
|
} catch {
|
|
11892
12926
|
continue;
|
|
11893
12927
|
}
|
|
11894
12928
|
const ordered = [...names.filter((n2) => n2 === `${role}.md`), ...names.filter((n2) => n2 !== `${role}.md`)];
|
|
11895
12929
|
for (const n2 of ordered) {
|
|
11896
12930
|
try {
|
|
11897
|
-
const a = parseAgent(
|
|
12931
|
+
const a = parseAgent(join54(d, n2), n2.slice(0, -3));
|
|
11898
12932
|
if (a && a.role === role)
|
|
11899
12933
|
return a;
|
|
11900
12934
|
} catch {}
|
|
@@ -11910,83 +12944,12 @@ var init_role_agents = __esm(() => {
|
|
|
11910
12944
|
ROLES = ["council", "reviewer"];
|
|
11911
12945
|
});
|
|
11912
12946
|
|
|
11913
|
-
// ../../loki-ts/src/util/shell.ts
|
|
11914
|
-
async function readStreamCapped(stream, maxBytes = MAX_STDOUT_BYTES) {
|
|
11915
|
-
const reader = stream.getReader();
|
|
11916
|
-
const decoder = new TextDecoder;
|
|
11917
|
-
let out2 = "";
|
|
11918
|
-
let total = 0;
|
|
11919
|
-
try {
|
|
11920
|
-
while (total < maxBytes) {
|
|
11921
|
-
const { done, value } = await reader.read();
|
|
11922
|
-
if (done)
|
|
11923
|
-
break;
|
|
11924
|
-
if (!value)
|
|
11925
|
-
continue;
|
|
11926
|
-
total += value.byteLength;
|
|
11927
|
-
if (total > maxBytes) {
|
|
11928
|
-
const keep = value.byteLength - (total - maxBytes);
|
|
11929
|
-
out2 += decoder.decode(value.subarray(0, keep), { stream: true });
|
|
11930
|
-
break;
|
|
11931
|
-
}
|
|
11932
|
-
out2 += decoder.decode(value, { stream: true });
|
|
11933
|
-
}
|
|
11934
|
-
out2 += decoder.decode();
|
|
11935
|
-
} finally {
|
|
11936
|
-
try {
|
|
11937
|
-
await reader.cancel();
|
|
11938
|
-
} catch {}
|
|
11939
|
-
reader.releaseLock();
|
|
11940
|
-
}
|
|
11941
|
-
return out2;
|
|
11942
|
-
}
|
|
11943
|
-
async function run(argv, opts = {}) {
|
|
11944
|
-
const proc = Bun.spawn({
|
|
11945
|
-
cmd: [...argv],
|
|
11946
|
-
stdout: "pipe",
|
|
11947
|
-
stderr: "pipe",
|
|
11948
|
-
env: opts.env ? { ...process.env, ...opts.env } : process.env,
|
|
11949
|
-
cwd: opts.cwd
|
|
11950
|
-
});
|
|
11951
|
-
let timer;
|
|
11952
|
-
let killTimer;
|
|
11953
|
-
if (opts.timeoutMs && opts.timeoutMs > 0) {
|
|
11954
|
-
timer = setTimeout(() => {
|
|
11955
|
-
try {
|
|
11956
|
-
proc.kill("SIGTERM");
|
|
11957
|
-
} catch {}
|
|
11958
|
-
killTimer = setTimeout(() => {
|
|
11959
|
-
try {
|
|
11960
|
-
proc.kill("SIGKILL");
|
|
11961
|
-
} catch {}
|
|
11962
|
-
}, 2000);
|
|
11963
|
-
}, opts.timeoutMs);
|
|
11964
|
-
}
|
|
11965
|
-
try {
|
|
11966
|
-
const [stdout, stderr, exitCode] = await Promise.all([
|
|
11967
|
-
readStreamCapped(proc.stdout),
|
|
11968
|
-
new Response(proc.stderr).text(),
|
|
11969
|
-
proc.exited
|
|
11970
|
-
]);
|
|
11971
|
-
return { stdout, stderr, exitCode };
|
|
11972
|
-
} finally {
|
|
11973
|
-
if (timer)
|
|
11974
|
-
clearTimeout(timer);
|
|
11975
|
-
if (killTimer)
|
|
11976
|
-
clearTimeout(killTimer);
|
|
11977
|
-
}
|
|
11978
|
-
}
|
|
11979
|
-
var MAX_STDOUT_BYTES;
|
|
11980
|
-
var init_shell = __esm(() => {
|
|
11981
|
-
MAX_STDOUT_BYTES = 16 * 1024 * 1024;
|
|
11982
|
-
});
|
|
11983
|
-
|
|
11984
12947
|
// ../../loki-ts/src/engine10/stages/xreview.ts
|
|
11985
|
-
import { existsSync as
|
|
11986
|
-
import { join as
|
|
12948
|
+
import { existsSync as existsSync33, readFileSync as readFileSync40 } from "fs";
|
|
12949
|
+
import { join as join55 } from "path";
|
|
11987
12950
|
function minVerdict(verdict, review) {
|
|
11988
12951
|
const ceiling = review?.level === "block" ? "PARTIAL" : verdict;
|
|
11989
|
-
return
|
|
12952
|
+
return RANK3[ceiling] < RANK3[verdict] ? ceiling : verdict;
|
|
11990
12953
|
}
|
|
11991
12954
|
function explicitReview(repoDir, env) {
|
|
11992
12955
|
const flag = xvendorDefault(env);
|
|
@@ -11996,11 +12959,11 @@ function explicitReview(repoDir, env) {
|
|
|
11996
12959
|
if (flag && isOff(ev))
|
|
11997
12960
|
return "off";
|
|
11998
12961
|
for (const f of ["loki.yaml", "loki.yml"]) {
|
|
11999
|
-
const p =
|
|
12000
|
-
if (!
|
|
12962
|
+
const p = join55(repoDir, f);
|
|
12963
|
+
if (!existsSync33(p))
|
|
12001
12964
|
continue;
|
|
12002
12965
|
try {
|
|
12003
|
-
const m = /^review:[ \t]*["']?([A-Za-z]+)["']?[ \t]*(?:#.*)?$/m.exec(
|
|
12966
|
+
const m = /^review:[ \t]*["']?([A-Za-z]+)["']?[ \t]*(?:#.*)?$/m.exec(readFileSync40(p, "utf8"));
|
|
12004
12967
|
const v = asProvider(m?.[1]);
|
|
12005
12968
|
if (v)
|
|
12006
12969
|
return v;
|
|
@@ -12078,7 +13041,7 @@ Review ONLY the diff below. Do not modify files.` : "You are an independent code
|
|
|
12078
13041
|
return { provider: p, level: "not_run", notes: [`cross-review not run: ${p} CLI unavailable`] };
|
|
12079
13042
|
}
|
|
12080
13043
|
}
|
|
12081
|
-
var
|
|
13044
|
+
var RANK3, TIMEOUT_MS = 300000, MAX_DIFF = 60000, MAX_NOTES = 5, asProvider = (v) => {
|
|
12082
13045
|
const s = v?.trim().toLowerCase();
|
|
12083
13046
|
return s === "codex" || s === "claude" ? s : null;
|
|
12084
13047
|
}, 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);
|
|
@@ -12086,107 +13049,89 @@ var init_xreview = __esm(() => {
|
|
|
12086
13049
|
init_role_agents();
|
|
12087
13050
|
init_shell();
|
|
12088
13051
|
init_safe_git();
|
|
12089
|
-
|
|
13052
|
+
RANK3 = { FAILED: 0, SPEC_CONFLICT: 0, PARTIAL: 1, ALREADY_SATISFIED: 2, VERIFIED: 2 };
|
|
12090
13053
|
});
|
|
12091
13054
|
|
|
12092
|
-
// ../../loki-ts/src/runner/router/
|
|
12093
|
-
|
|
12094
|
-
|
|
12095
|
-
|
|
12096
|
-
|
|
12097
|
-
|
|
12098
|
-
|
|
12099
|
-
|
|
12100
|
-
|
|
12101
|
-
return typeof v === "object" && v !== null && !Array.isArray(v) ? v : undefined;
|
|
12102
|
-
} catch {
|
|
12103
|
-
return;
|
|
12104
|
-
}
|
|
12105
|
-
}
|
|
12106
|
-
var ROUTE_RECORD_FILE = "route.json";
|
|
12107
|
-
var init_route_record = () => {};
|
|
12108
|
-
|
|
12109
|
-
// ../../loki-ts/src/runner/router/history_store.ts
|
|
12110
|
-
import { existsSync as existsSync30, mkdirSync as mkdirSync16, readFileSync as readFileSync36, renameSync as renameSync6, rmSync as rmSync12, writeFileSync as writeFileSync17 } from "fs";
|
|
12111
|
-
import { resolve as resolve11 } from "path";
|
|
12112
|
-
function validRun(v) {
|
|
12113
|
-
if (typeof v !== "object" || v === null)
|
|
12114
|
-
return false;
|
|
12115
|
-
const r = v;
|
|
12116
|
-
return typeof r["shape"] === "string" && typeof r["executor"] === "string" && EXECUTORS.includes(r["executor"]) && typeof r["verdict"] === "string" && VERDICTS.includes(r["verdict"]) && (r["owner"] === null || typeof r["owner"] === "string" && OWNERS.includes(r["owner"])) && typeof r["escalated"] === "boolean" && typeof r["usd"] === "number" && Number.isFinite(r["usd"]) && typeof r["wallS"] === "number" && Number.isFinite(r["wallS"]);
|
|
13055
|
+
// ../../loki-ts/src/runner/router/route_block.ts
|
|
13056
|
+
function advisorUnavailableReason(env, provider) {
|
|
13057
|
+
if (provider !== "claude")
|
|
13058
|
+
return `provider ${provider} has no advisor tool`;
|
|
13059
|
+
if (env["CLAUDE_CODE_USE_BEDROCK"] || env["CLAUDE_CODE_USE_VERTEX"] || env["CLAUDE_CODE_USE_FOUNDRY"])
|
|
13060
|
+
return "advisor tool is unsupported on Bedrock, Vertex and Foundry";
|
|
13061
|
+
if ((env["LOKI_ROUTER_ADVISOR"] ?? "").trim().toLowerCase() === "off")
|
|
13062
|
+
return "LOKI_ROUTER_ADVISOR=off";
|
|
13063
|
+
return null;
|
|
12117
13064
|
}
|
|
12118
|
-
function
|
|
12119
|
-
|
|
13065
|
+
function toEsc(raw2) {
|
|
13066
|
+
const esc3 = Array.isArray(raw2) ? raw2 : [];
|
|
13067
|
+
return esc3.map((e) => ({ trigger: s0(e["trigger"]) ?? "unknown", from: modelLabel(s0(e["from"]) ?? "?"), to: modelLabel(s0(e["to"]) ?? "?"), evidence: s0(e["evidence"]) ?? "none recorded" }));
|
|
12120
13068
|
}
|
|
12121
|
-
function
|
|
12122
|
-
|
|
12123
|
-
|
|
12124
|
-
|
|
12125
|
-
|
|
12126
|
-
|
|
12127
|
-
|
|
12128
|
-
|
|
12129
|
-
|
|
12130
|
-
|
|
12131
|
-
|
|
13069
|
+
function buildRouteBlock(env, provider, route, telemetry, runModel) {
|
|
13070
|
+
if (!routerEnabled(env))
|
|
13071
|
+
return null;
|
|
13072
|
+
const applied = route?.["applied"] !== false;
|
|
13073
|
+
const executor = s0(route?.["executor"]);
|
|
13074
|
+
const unavailable = s0(route?.["advisor_unavailable_reason"]) ?? advisorUnavailableReason(env, provider);
|
|
13075
|
+
const total = nN(telemetry?.requests_total);
|
|
13076
|
+
const over = nN(telemetry?.requests_over_100k);
|
|
13077
|
+
const shape = s0(route?.["shape_key"]);
|
|
13078
|
+
const escalations = toEsc(route?.["escalations"]);
|
|
13079
|
+
const source = s0(route?.["source"]);
|
|
13080
|
+
const reason = s0(route?.["reason"]) ?? (executor ? "no reason recorded" : "no route recorded by implement");
|
|
13081
|
+
const rawUnits = Array.isArray(route?.["units"]) ? route["units"] : [];
|
|
13082
|
+
const units = rawUnits.length > 0 ? rawUnits.map((u, i) => ({ id: s0(u["id"]) ?? String(i + 1), executor: modelLabel(s0(u["executor"]) ?? "unknown"), applied, assigned_by: assignedBy(s0(u["source"]) ?? source, u["assigned_by"]), reason: s0(u["reason"]) ?? "no reason recorded", escalations: toEsc(u["escalations"]) })) : executor && route?.["routed"] !== false ? [{ id: "run", executor: modelLabel(executor), applied, assigned_by: assignedBy(source, route?.["assigned_by"]), reason, escalations }] : [];
|
|
13083
|
+
return {
|
|
13084
|
+
routed: route?.["routed"] === false ? false : executor !== null || units.length > 0,
|
|
13085
|
+
units,
|
|
13086
|
+
executor: !applied ? `${runModel ? modelLabel(runModel) : "run model"} (per-unit assignment not applied)` : executor ? modelLabel(executor) : units[0]?.executor ?? null,
|
|
13087
|
+
advisor: unavailable ? null : modelLabel(s0(route?.["advisor"]) ?? "opus"),
|
|
13088
|
+
advisor_unavailable_reason: unavailable,
|
|
13089
|
+
reason,
|
|
13090
|
+
source,
|
|
13091
|
+
shape_key: shape,
|
|
13092
|
+
shape_parity: shape ? `NOT PROVEN (no B9 row for shape ${shape})` : "NOT PROVEN (no shape key recorded)",
|
|
13093
|
+
escalations: units.length > 0 ? units.flatMap((u) => u.escalations) : escalations,
|
|
13094
|
+
requests_total: total,
|
|
13095
|
+
requests_over_100k: over,
|
|
13096
|
+
over_100k_share: total !== null && over !== null && total > 0 ? Math.round(over / total * 1e4) / 1e4 : null,
|
|
13097
|
+
advisor_calls: nN(telemetry?.advisor_calls),
|
|
13098
|
+
advisor_input_tokens: nN(telemetry?.advisor_input_tokens),
|
|
13099
|
+
advisor_output_tokens: nN(telemetry?.advisor_output_tokens),
|
|
13100
|
+
plan_model: s0(route?.["plan_model"]),
|
|
13101
|
+
applied,
|
|
13102
|
+
run_model: runModel ?? null,
|
|
13103
|
+
not_proven: Array.isArray(route?.["route_not_proven"]) ? route["route_not_proven"].filter((x) => typeof x === "string") : []
|
|
13104
|
+
};
|
|
12132
13105
|
}
|
|
12133
|
-
function
|
|
12134
|
-
|
|
12135
|
-
|
|
12136
|
-
|
|
12137
|
-
|
|
12138
|
-
const tmp = `${target}.${process.pid}.tmp`;
|
|
12139
|
-
try {
|
|
12140
|
-
writeFileSync17(tmp, JSON.stringify({ runs: runs2 }));
|
|
12141
|
-
renameSync6(tmp, target);
|
|
12142
|
-
} catch (e) {
|
|
12143
|
-
rmSync12(tmp, { force: true, recursive: false });
|
|
12144
|
-
throw e;
|
|
12145
|
-
}
|
|
12146
|
-
return true;
|
|
12147
|
-
} catch {
|
|
12148
|
-
return false;
|
|
12149
|
-
}
|
|
13106
|
+
function routePrLine(r) {
|
|
13107
|
+
if (!r.routed)
|
|
13108
|
+
return `Route: not routed (${r.reason})`;
|
|
13109
|
+
const adv = r.advisor ? `advisor ${r.advisor}` : "advisor unavailable";
|
|
13110
|
+
return `Route: executor ${r.executor}, ${adv}; advisor tokens ${rec(r.advisor_input_tokens)} in / ${rec(r.advisor_output_tokens)} out; over 100K: ${over(r, " requests")}`;
|
|
12150
13111
|
}
|
|
12151
|
-
|
|
12152
|
-
|
|
12153
|
-
|
|
12154
|
-
|
|
12155
|
-
|
|
12156
|
-
|
|
12157
|
-
});
|
|
12158
|
-
|
|
12159
|
-
|
|
12160
|
-
|
|
12161
|
-
|
|
12162
|
-
function shapeKey(model) {
|
|
12163
|
-
if (!model || model.status !== "ok")
|
|
12164
|
-
return null;
|
|
12165
|
-
const runners = new Set(model.packages.map((p) => (p.runner ?? "").trim().toLowerCase() || "none"));
|
|
12166
|
-
return `${model.workspaceKind}:${[...runners].sort().join("+")}`;
|
|
13112
|
+
function routeReceiptLines(r) {
|
|
13113
|
+
if (!r.routed)
|
|
13114
|
+
return [`- Route: not routed (${r.reason})`, `- Route shape_parity: ${r.shape_parity}`];
|
|
13115
|
+
return [
|
|
13116
|
+
`- Route: executor ${r.executor}, ${r.advisor ? `advisor ${r.advisor}` : `advisor unavailable: ${r.advisor_unavailable_reason}`}: ${r.reason}`,
|
|
13117
|
+
`- Route shape_key: ${r.shape_key ?? "unknown"} shape_parity: ${r.shape_parity}`,
|
|
13118
|
+
`- Route escalations: ${r.escalations.length === 0 ? "none" : r.escalations.map((e) => `${e.from} -> ${e.to} (${e.trigger}; evidence ${e.evidence})`).join("; ")}`,
|
|
13119
|
+
`- Route advisor: ${rec(r.advisor_calls)} calls, ${rec(r.advisor_input_tokens)} in / ${rec(r.advisor_output_tokens)} out tokens`,
|
|
13120
|
+
...r.units.map((u) => `- Route unit ${u.id}: executor ${u.applied ? u.executor : `not applied (assigned ${u.executor})`}, assigned by ${u.assigned_by}: ${u.reason}; escalation: ${u.escalations.length === 0 ? "none" : u.escalations.map((e) => `${e.from} -> ${e.to} (${e.trigger})`).join(", ")}`),
|
|
13121
|
+
`- Route requests over 100K: ${over(r, "")}`
|
|
13122
|
+
];
|
|
12167
13123
|
}
|
|
12168
|
-
function
|
|
12169
|
-
|
|
12170
|
-
|
|
12171
|
-
|
|
12172
|
-
|
|
12173
|
-
|
|
12174
|
-
|
|
12175
|
-
if (!cached || computeKey(repoDir, cached.fingerprintFiles, dirs) !== cached.key)
|
|
12176
|
-
return null;
|
|
12177
|
-
return shapeKey(cached);
|
|
12178
|
-
} catch {
|
|
12179
|
-
return null;
|
|
12180
|
-
}
|
|
13124
|
+
function routeNotProven(r) {
|
|
13125
|
+
return [
|
|
13126
|
+
...r.advisor_unavailable_reason ? [`advisor: NOT PROVEN (owner provider): ${r.advisor_unavailable_reason}`] : [],
|
|
13127
|
+
`route.shape_parity: ${r.shape_parity}`,
|
|
13128
|
+
...r.applied ? [] : [`route.executor: NOT PROVEN (owner engine): per-unit executor assignment is not applied (R1-11); every stage ran ${r.run_model ?? "the run model"}`],
|
|
13129
|
+
...r.not_proven
|
|
13130
|
+
];
|
|
12181
13131
|
}
|
|
12182
|
-
var
|
|
12183
|
-
var
|
|
12184
|
-
|
|
12185
|
-
init_history_store();
|
|
12186
|
-
init_discover2();
|
|
12187
|
-
init_gather();
|
|
12188
|
-
init_history_store();
|
|
12189
|
-
SHAPE_DEFAULTS_FILE = resolve12(dirname21(fileURLToPath3(import.meta.url)), "..", "..", "..", "data", "router-shape-defaults.json");
|
|
13132
|
+
var ALIAS, modelLabel = (m) => ALIAS[m] ?? m, nN = (v) => typeof v === "number" && Number.isFinite(v) ? v : null, rec = (v) => v === null ? "not recorded" : String(v), s0 = (v) => typeof v === "string" && v.trim() ? v.trim() : null, assignedBy = (src, explicit) => s0(explicit) ?? (src === null || src === "opus" ? "opus" : src), over = (r, unit) => r.requests_total === null || r.requests_over_100k === null ? "not recorded" : `${r.requests_over_100k} of ${r.requests_total}${unit} (${pct(r)})`, pct = (r) => r.over_100k_share === null ? "n/a (no requests)" : `${(r.over_100k_share * 100).toFixed(1)}%`;
|
|
13133
|
+
var init_route_block = __esm(() => {
|
|
13134
|
+
ALIAS = { haiku: "haiku-5.5", sonnet: "sonnet-5.5", opus: "opus-5.5" };
|
|
12190
13135
|
});
|
|
12191
13136
|
|
|
12192
13137
|
// ../../loki-ts/src/util/engine_origin.ts
|
|
@@ -12199,6 +13144,7 @@ function readOriginUrl(repoDir) {
|
|
|
12199
13144
|
}
|
|
12200
13145
|
}
|
|
12201
13146
|
var init_engine_origin = __esm(() => {
|
|
13147
|
+
init_atomic();
|
|
12202
13148
|
init_safe_git();
|
|
12203
13149
|
});
|
|
12204
13150
|
|
|
@@ -12235,9 +13181,9 @@ var init_cost_preview = __esm(() => {
|
|
|
12235
13181
|
});
|
|
12236
13182
|
|
|
12237
13183
|
// ../../loki-ts/src/features/contract.ts
|
|
12238
|
-
import { existsSync as
|
|
12239
|
-
import { dirname as
|
|
12240
|
-
import { createHash as
|
|
13184
|
+
import { existsSync as existsSync34, lstatSync as lstatSync12, mkdirSync as mkdirSync22, readFileSync as readFileSync41, rmSync as rmSync14, writeFileSync as writeFileSync24 } from "fs";
|
|
13185
|
+
import { dirname as dirname24, join as join56 } from "path";
|
|
13186
|
+
import { createHash as createHash12 } from "crypto";
|
|
12241
13187
|
function contractEnabled(env = process.env) {
|
|
12242
13188
|
return env["LOKI_CONTRACT"] !== "0";
|
|
12243
13189
|
}
|
|
@@ -12268,7 +13214,7 @@ function untracedLines(trace) {
|
|
|
12268
13214
|
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)}`);
|
|
12269
13215
|
}
|
|
12270
13216
|
function contractPath(repoDir) {
|
|
12271
|
-
return
|
|
13217
|
+
return join56(repoDir, ".loki", "contract.json");
|
|
12272
13218
|
}
|
|
12273
13219
|
function readContract(repoDir) {
|
|
12274
13220
|
const p = contractPath(repoDir);
|
|
@@ -12284,11 +13230,11 @@ function readContract(repoDir) {
|
|
|
12284
13230
|
return { contract: null, notes: ["contract unreadable: too large"], sha256: null };
|
|
12285
13231
|
let raw2;
|
|
12286
13232
|
try {
|
|
12287
|
-
raw2 =
|
|
13233
|
+
raw2 = readFileSync41(p);
|
|
12288
13234
|
} catch {
|
|
12289
13235
|
return { contract: null, notes: ["contract unreadable: read failed"], sha256: null };
|
|
12290
13236
|
}
|
|
12291
|
-
const sha2563 =
|
|
13237
|
+
const sha2563 = createHash12("sha256").update(raw2).digest("hex");
|
|
12292
13238
|
let j;
|
|
12293
13239
|
try {
|
|
12294
13240
|
j = JSON.parse(raw2.toString("utf8"));
|
|
@@ -12345,16 +13291,157 @@ function sealContract(repoDir, body, rawDiff, checks, env = process.env, snap) {
|
|
|
12345
13291
|
return [`contract trace failed: ${sanitizeCriterion(e instanceof Error ? e.message : String(e))}`];
|
|
12346
13292
|
}
|
|
12347
13293
|
}
|
|
12348
|
-
var MAX_CRITERIA = 50, MAX_TEXT = 500, STOP2, MAX_CONTRACT_BYTES;
|
|
13294
|
+
var MAX_CRITERIA = 50, MAX_TEXT = 500, STOP2, MAX_CONTRACT_BYTES, REQ_MAP_REL, REQ_STOP;
|
|
12349
13295
|
var init_contract = __esm(() => {
|
|
13296
|
+
init_atomic();
|
|
13297
|
+
init_spec_file();
|
|
13298
|
+
init_paths();
|
|
12350
13299
|
init_safe_git();
|
|
12351
13300
|
STOP2 = new Set(["the", "and", "for", "with", "that", "this", "must", "should", "shall", "when", "then", "are", "was", "all", "any", "can", "not", "has", "have", "from", "into", "each", "user", "users"]);
|
|
12352
13301
|
MAX_CONTRACT_BYTES = 1024 * 1024;
|
|
13302
|
+
REQ_MAP_REL = join56(".loki", "state", "requirements-map.json");
|
|
13303
|
+
REQ_STOP = new Set(["with", "that", "this", "from", "have", "will", "should", "must", "when", "then", "into", "each", "every", "user", "users", "page", "able", "allow", "support", "ensure", "using", "their", "there", "which", "shall"]);
|
|
13304
|
+
});
|
|
13305
|
+
|
|
13306
|
+
// ../../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";
|
|
13310
|
+
function sealGroup(runDir, rh) {
|
|
13311
|
+
const manifestPath = join57(runDir, "group", "manifest.json");
|
|
13312
|
+
if (!existsSync35(manifestPath))
|
|
13313
|
+
return { section: undefined, notProven: [], problems: 0, allUnitsPass: true };
|
|
13314
|
+
const notProven = [];
|
|
13315
|
+
let problems = 0;
|
|
13316
|
+
const fail = (m2) => {
|
|
13317
|
+
problems++;
|
|
13318
|
+
notProven.push(`group: ${m2}`);
|
|
13319
|
+
};
|
|
13320
|
+
const m = readJson5(manifestPath);
|
|
13321
|
+
const list = isObj10(m) && Array.isArray(m["units"]) ? m["units"] : [];
|
|
13322
|
+
if (!isObj10(m) || !ID_RE.test(String(m["group_id"] ?? "")) || list.length === 0) {
|
|
13323
|
+
fail("manifest missing, unreadable or empty");
|
|
13324
|
+
return { section: undefined, notProven, problems, allUnitsPass: false };
|
|
13325
|
+
}
|
|
13326
|
+
const units = [];
|
|
13327
|
+
const seen = new Set, seenLower = new Set;
|
|
13328
|
+
let allUnitsPass = true;
|
|
13329
|
+
list.forEach((raw2, index2) => {
|
|
13330
|
+
const u = isObj10(raw2) ? raw2 : {};
|
|
13331
|
+
const id = String(u["unit_id"] ?? "");
|
|
13332
|
+
if (!ID_RE.test(id) || seen.has(id) || seenLower.has(id.toLowerCase()))
|
|
13333
|
+
return fail(`unit ${index2} has an invalid, duplicate or case-colliding id`);
|
|
13334
|
+
seen.add(id);
|
|
13335
|
+
seenLower.add(id.toLowerCase());
|
|
13336
|
+
const dir = join57(unitsDir(runDir), id), rp = join57(dir, "receipt.json"), ep = join57(dir, "events.jsonl");
|
|
13337
|
+
let bytes;
|
|
13338
|
+
try {
|
|
13339
|
+
bytes = readFileSync42(rp);
|
|
13340
|
+
} catch {
|
|
13341
|
+
return fail(`unit ${id} sub-receipt missing`);
|
|
13342
|
+
}
|
|
13343
|
+
const sub = readJson5(rp);
|
|
13344
|
+
if (!isObj10(sub) || typeof sub["receipt_sha256"] !== "string" || rh(sub) !== sub["receipt_sha256"])
|
|
13345
|
+
return fail(`unit ${id} sub-receipt unreadable or its hash does not recompute`);
|
|
13346
|
+
const verdict = String(sub["verdict"] ?? "");
|
|
13347
|
+
if (!PASSING.has(verdict)) {
|
|
13348
|
+
allUnitsPass = false;
|
|
13349
|
+
notProven.push(`group: unit ${id} verdict ${verdict || "unknown"}`);
|
|
13350
|
+
}
|
|
13351
|
+
units.push({
|
|
13352
|
+
index: index2,
|
|
13353
|
+
unit_id: id,
|
|
13354
|
+
sub_receipt_sha256: sha2(bytes),
|
|
13355
|
+
receipt_sha256: sub["receipt_sha256"],
|
|
13356
|
+
events_file_sha256: existsSync35(ep) ? sha2(readFileSync42(ep)) : null,
|
|
13357
|
+
verdict,
|
|
13358
|
+
goal: text2(u["goal"], 200),
|
|
13359
|
+
files: (Array.isArray(u["files"]) ? u["files"] : []).map((f) => text2(f, 200)).filter(Boolean).slice(0, 50),
|
|
13360
|
+
tests: num4(u["tests"]),
|
|
13361
|
+
tokens: num4(u["tokens"]),
|
|
13362
|
+
model: text2(u["model"], 64)
|
|
13363
|
+
});
|
|
13364
|
+
});
|
|
13365
|
+
const extra = (dirNames(unitsDir(runDir)) ?? []).filter((n2) => !seen.has(n2));
|
|
13366
|
+
for (const n2 of extra)
|
|
13367
|
+
fail(`unit directory ${text2(n2, 64)} is not in the manifest`);
|
|
13368
|
+
return { section: problems === 0 ? { group_id: String(m["group_id"]), units } : undefined, notProven, problems, allUnitsPass: allUnitsPass && problems === 0 };
|
|
13369
|
+
}
|
|
13370
|
+
function capGroupVerdict(v, g) {
|
|
13371
|
+
if (g.problems > 0)
|
|
13372
|
+
return v === "SPEC_CONFLICT" ? v : "FAILED";
|
|
13373
|
+
return !g.allUnitsPass && (v === "VERIFIED" || v === "ALREADY_SATISFIED") ? "PARTIAL" : v;
|
|
13374
|
+
}
|
|
13375
|
+
async function verifyGroup(receiptPath, receipt, rh, verifyOne, deps = {}) {
|
|
13376
|
+
const runDir = dirname25(receiptPath);
|
|
13377
|
+
const signed = isObj10(receipt["verification"]) && typeof receipt["verification"]["jwt"] === "string";
|
|
13378
|
+
const bad = (reason) => ({ verdict: "TAMPERED", reason: `group: ${reason}` });
|
|
13379
|
+
const g = receipt["group"];
|
|
13380
|
+
if (g === undefined)
|
|
13381
|
+
return existsSync35(join57(runDir, "group", "manifest.json")) ? bad("group directory present but the receipt has no group section") : null;
|
|
13382
|
+
if (!isObj10(g) || !ID_RE.test(String(g["group_id"] ?? "")) || !Array.isArray(g["units"]) || g["units"].length === 0)
|
|
13383
|
+
return bad("group section malformed");
|
|
13384
|
+
const entries = g["units"];
|
|
13385
|
+
const ids = entries.map((e) => isObj10(e) ? String(e["unit_id"] ?? "") : "");
|
|
13386
|
+
if (ids.some((id) => !ID_RE.test(id)) || new Set(ids.map((i) => i.toLowerCase())).size !== ids.length)
|
|
13387
|
+
return bad("unit ids invalid, duplicated or case-colliding");
|
|
13388
|
+
const onDisk = dirNames(unitsDir(runDir));
|
|
13389
|
+
if (onDisk === null || onDisk.join("\x00") !== [...ids].sort().join("\x00"))
|
|
13390
|
+
return bad("unit directories do not match the receipt (missing or extra unit)");
|
|
13391
|
+
let unchecked = null;
|
|
13392
|
+
for (let i = 0;i < entries.length; i++) {
|
|
13393
|
+
const e = entries[i], id = ids[i], dir = join57(unitsDir(runDir), id), rp = join57(dir, "receipt.json"), ep = join57(dir, "events.jsonl");
|
|
13394
|
+
if (e["index"] !== i)
|
|
13395
|
+
return bad(`unit ${id} is out of order (index ${String(e["index"])} at position ${i})`);
|
|
13396
|
+
let bytes;
|
|
13397
|
+
try {
|
|
13398
|
+
if (!statSync17(dir).isDirectory())
|
|
13399
|
+
return bad(`unit ${id} is not a directory`);
|
|
13400
|
+
bytes = readFileSync42(rp);
|
|
13401
|
+
} catch {
|
|
13402
|
+
return bad(`unit ${id} sub-receipt missing`);
|
|
13403
|
+
}
|
|
13404
|
+
if (sha2(bytes) !== e["sub_receipt_sha256"])
|
|
13405
|
+
return bad(`unit ${id} sub-receipt does not match its recorded hash`);
|
|
13406
|
+
const evSha = existsSync35(ep) ? sha2(readFileSync42(ep)) : null;
|
|
13407
|
+
if (evSha !== (e["events_file_sha256"] ?? null))
|
|
13408
|
+
return bad(`unit ${id} events.jsonl does not match its recorded hash`);
|
|
13409
|
+
const sub = readJson5(rp);
|
|
13410
|
+
if (!isObj10(sub) || sub["receipt_sha256"] !== e["receipt_sha256"] || rh(sub) !== e["receipt_sha256"])
|
|
13411
|
+
return bad(`unit ${id} sub-receipt hash does not recompute`);
|
|
13412
|
+
if (String(sub["verdict"] ?? "") !== e["verdict"])
|
|
13413
|
+
return bad(`unit ${id} verdict differs from the combined receipt`);
|
|
13414
|
+
const r = await verifyOne(rp, deps);
|
|
13415
|
+
if (r.verdict === "UNSIGNED" && signed)
|
|
13416
|
+
unchecked ??= `unit ${id} sub-receipt is unsigned under a signed combined receipt`;
|
|
13417
|
+
if (r.verdict === "TAMPERED")
|
|
13418
|
+
return bad(`unit ${id} sub-receipt failed verify: ${r.reasons.join("; ")}`);
|
|
13419
|
+
if (r.verdict === "UNCHECKED")
|
|
13420
|
+
unchecked ??= `unit ${id} sub-receipt not checked: ${r.reasons.join("; ")}`;
|
|
13421
|
+
}
|
|
13422
|
+
return unchecked ? { verdict: "UNCHECKED", reason: `group: ${unchecked}` } : null;
|
|
13423
|
+
}
|
|
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) => {
|
|
13425
|
+
try {
|
|
13426
|
+
return JSON.parse(readFileSync42(p, "utf8"));
|
|
13427
|
+
} catch {
|
|
13428
|
+
return;
|
|
13429
|
+
}
|
|
13430
|
+
}, dirNames = (d) => {
|
|
13431
|
+
try {
|
|
13432
|
+
return readdirSync17(d).filter((n2) => !n2.startsWith(".")).sort();
|
|
13433
|
+
} catch {
|
|
13434
|
+
return null;
|
|
13435
|
+
}
|
|
13436
|
+
};
|
|
13437
|
+
var init_seal_group = __esm(() => {
|
|
13438
|
+
ID_RE = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;
|
|
13439
|
+
PASSING = new Set(["VERIFIED", "ALREADY_SATISFIED"]);
|
|
12353
13440
|
});
|
|
12354
13441
|
|
|
12355
13442
|
// ../../loki-ts/src/supply/supply_guard.ts
|
|
12356
|
-
import { existsSync as
|
|
12357
|
-
import { basename as
|
|
13443
|
+
import { existsSync as existsSync36, readFileSync as readFileSync43, statSync as statSync18 } from "fs";
|
|
13444
|
+
import { basename as basename19, join as join58 } from "path";
|
|
12358
13445
|
function supplyEnabled(env) {
|
|
12359
13446
|
return env["LOKI_SUPPLY_GUARD"] !== "0";
|
|
12360
13447
|
}
|
|
@@ -12366,16 +13453,16 @@ function supplyVerdict(verdict, r) {
|
|
|
12366
13453
|
return r.blocked && verdict === "VERIFIED" ? "FAILED" : verdict;
|
|
12367
13454
|
}
|
|
12368
13455
|
function isManifestPath(path) {
|
|
12369
|
-
return MANIFESTS.test(
|
|
13456
|
+
return MANIFESTS.test(basename19(path));
|
|
12370
13457
|
}
|
|
12371
13458
|
function readDeclared(repoDir) {
|
|
12372
|
-
const p =
|
|
12373
|
-
if (!
|
|
13459
|
+
const p = join58(repoDir, ".loki", DECLARED_FILE);
|
|
13460
|
+
if (!existsSync36(p))
|
|
12374
13461
|
return { deps: [], problem: null };
|
|
12375
13462
|
try {
|
|
12376
|
-
if (
|
|
13463
|
+
if (statSync18(p).size > MAX_FILE_BYTES)
|
|
12377
13464
|
return { deps: [], problem: "declaration file too large" };
|
|
12378
|
-
const j = JSON.parse(
|
|
13465
|
+
const j = JSON.parse(readFileSync43(p, "utf8"));
|
|
12379
13466
|
if (!Array.isArray(j))
|
|
12380
13467
|
return { deps: [], problem: "declaration is not a JSON array" };
|
|
12381
13468
|
const deps = [];
|
|
@@ -12395,11 +13482,11 @@ function readDeclared(repoDir) {
|
|
|
12395
13482
|
}
|
|
12396
13483
|
}
|
|
12397
13484
|
function readAllowlist(repoDir) {
|
|
12398
|
-
const p =
|
|
12399
|
-
if (!
|
|
13485
|
+
const p = join58(repoDir, ".loki", "supply-allowlist");
|
|
13486
|
+
if (!existsSync36(p))
|
|
12400
13487
|
return new Set;
|
|
12401
13488
|
try {
|
|
12402
|
-
return new Set(
|
|
13489
|
+
return new Set(readFileSync43(p, "utf8").split(`
|
|
12403
13490
|
`).map((l) => l.replace(/#.*$/, "").trim()).filter(Boolean));
|
|
12404
13491
|
} catch {
|
|
12405
13492
|
return new Set;
|
|
@@ -12440,7 +13527,7 @@ function userConfigPrivate(eco, repoDir, env, goEnv = {}, name = "") {
|
|
|
12440
13527
|
if (eco === "npm") {
|
|
12441
13528
|
if (privateUrlList([env["npm_config_registry"] ?? "", env["NPM_CONFIG_REGISTRY"] ?? ""]))
|
|
12442
13529
|
return true;
|
|
12443
|
-
return files(
|
|
13530
|
+
return files(join58(repoDir, ".npmrc"), home ? join58(home, ".npmrc") : "").some((t) => t.split(`
|
|
12444
13531
|
`).some((l) => {
|
|
12445
13532
|
const m = /^\s*(?:(@[^:\s]+):)?registry\s*=\s*(\S+)/.exec(l);
|
|
12446
13533
|
return !!m && privateUrl(m[2]) && (m[1] === undefined || name.startsWith(`${m[1]}/`));
|
|
@@ -12449,7 +13536,7 @@ function userConfigPrivate(eco, repoDir, env, goEnv = {}, name = "") {
|
|
|
12449
13536
|
if (eco === "pypi") {
|
|
12450
13537
|
if (privateUrlList([env["PIP_INDEX_URL"] ?? "", env["PIP_EXTRA_INDEX_URL"] ?? ""]))
|
|
12451
13538
|
return true;
|
|
12452
|
-
const cfg = [env["PIP_CONFIG_FILE"] ?? "", home ?
|
|
13539
|
+
const cfg = [env["PIP_CONFIG_FILE"] ?? "", home ? join58(home, ".config", "pip", "pip.conf") : "", home ? join58(home, ".pip", "pip.conf") : "", join58(repoDir, "pip.conf")];
|
|
12453
13540
|
return files(...cfg).some((t) => t.split(`
|
|
12454
13541
|
`).some((l) => {
|
|
12455
13542
|
const m = /^\s*(?:extra-)?index-url\s*[=:]\s*(.+)$/.exec(l);
|
|
@@ -12459,7 +13546,7 @@ function userConfigPrivate(eco, repoDir, env, goEnv = {}, name = "") {
|
|
|
12459
13546
|
if (eco === "cargo") {
|
|
12460
13547
|
if (Object.keys(env).some((k) => /^CARGO_REGISTRIES_.+_INDEX$/.test(k) || /^CARGO_SOURCE_/.test(k)))
|
|
12461
13548
|
return true;
|
|
12462
|
-
return files(
|
|
13549
|
+
return files(join58(repoDir, ".cargo", "config.toml"), join58(repoDir, ".cargo", "config"), home ? join58(home, ".cargo", "config.toml") : "", home ? join58(home, ".cargo", "config") : "").some((t) => /^\s*\[(registries\.|source\.)/m.test(t) || /replace-with\s*=/.test(t));
|
|
12463
13550
|
}
|
|
12464
13551
|
if (eco === "go") {
|
|
12465
13552
|
const gp = goEnv["GOPROXY"] ?? env["GOPROXY"] ?? "";
|
|
@@ -12582,7 +13669,7 @@ var MIN_AGE_DAYS = 7, DECLARED_FILE = "supply-declared.json", MAX_CHECKED = 50,
|
|
|
12582
13669
|
}
|
|
12583
13670
|
}, 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) => {
|
|
12584
13671
|
try {
|
|
12585
|
-
return
|
|
13672
|
+
return statSync18(p).size <= MAX_FILE_BYTES ? readFileSync43(p, "utf8") : "";
|
|
12586
13673
|
} catch {
|
|
12587
13674
|
return "";
|
|
12588
13675
|
}
|
|
@@ -12597,77 +13684,18 @@ var init_supply_guard = __esm(() => {
|
|
|
12597
13684
|
});
|
|
12598
13685
|
|
|
12599
13686
|
// ../../loki-ts/src/engine10/stages/seal.ts
|
|
12600
|
-
import { createHash as
|
|
12601
|
-
import {
|
|
12602
|
-
import {
|
|
12603
|
-
import { basename as basename18, dirname as dirname23, isAbsolute as isAbsolute8, join as join53, relative as relative9 } from "path";
|
|
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";
|
|
12604
13690
|
function sanitizeReason(s) {
|
|
12605
13691
|
const collapsed = s.replace(/[\x00-\x1f\x7f]+/g, " ").trim();
|
|
12606
13692
|
const capped = collapsed.length > 500 ? `${collapsed.slice(0, 500)}...` : collapsed;
|
|
12607
13693
|
return capped.replace(/`/g, "'");
|
|
12608
13694
|
}
|
|
12609
|
-
function canonicalJson(x) {
|
|
12610
|
-
const walk = (v) => {
|
|
12611
|
-
if (Array.isArray(v))
|
|
12612
|
-
return `[${v.map(walk).join(",")}]`;
|
|
12613
|
-
if (v !== null && typeof v === "object") {
|
|
12614
|
-
const o = v;
|
|
12615
|
-
return `{${Object.keys(o).filter((k) => o[k] !== undefined).sort().map((k) => `${JSON.stringify(k)}:${walk(o[k])}`).join(",")}}`;
|
|
12616
|
-
}
|
|
12617
|
-
return JSON.stringify(v ?? null);
|
|
12618
|
-
};
|
|
12619
|
-
return walk(x).replace(/[\u0080-\uFFFF]/g, (c) => `\\u${c.charCodeAt(0).toString(16).padStart(4, "0")}`);
|
|
12620
|
-
}
|
|
12621
13695
|
function receiptSha256(r) {
|
|
12622
13696
|
const { verification: _v, receipt_sha256: _h, ...body } = r;
|
|
12623
13697
|
return sha2562(canonicalJson(body));
|
|
12624
13698
|
}
|
|
12625
|
-
function loadSigningKey(generate = true) {
|
|
12626
|
-
try {
|
|
12627
|
-
const inline = process.env["LOKI_RECEIPT_SIGNING_KEY"]?.trim();
|
|
12628
|
-
let pem = inline ?? "";
|
|
12629
|
-
if (!inline) {
|
|
12630
|
-
const given = process.env["LOKI_RECEIPT_SIGNING_KEY_FILE"]?.trim();
|
|
12631
|
-
const file = given || join53(process.env["HOME"] || homedir9(), ".loki", "keys", RECEIPT_SIGNER_BASENAME);
|
|
12632
|
-
try {
|
|
12633
|
-
pem = readFileSync39(file);
|
|
12634
|
-
if (!given) {
|
|
12635
|
-
for (const [f, m] of [[file, 384], [dirname23(file), 448]])
|
|
12636
|
-
if (statSync17(f).mode & 63)
|
|
12637
|
-
chmodSync5(f, m);
|
|
12638
|
-
}
|
|
12639
|
-
} catch (e) {
|
|
12640
|
-
if (e.code !== "ENOENT" || !generate)
|
|
12641
|
-
return null;
|
|
12642
|
-
mkdirSync18(dirname23(dirname23(file)), { recursive: true, mode: 448 });
|
|
12643
|
-
mkdirSync18(dirname23(file), { recursive: true, mode: 448 });
|
|
12644
|
-
const tmp = `${file}.${randomBytes4(6).toString("hex")}.tmp`;
|
|
12645
|
-
writeFileSync19(tmp, generateKeyPairSync("ed25519").privateKey.export({ type: "pkcs8", format: "pem" }), { flag: "wx", mode: 384 });
|
|
12646
|
-
try {
|
|
12647
|
-
linkSync(tmp, file);
|
|
12648
|
-
} catch (l) {
|
|
12649
|
-
if (l.code !== "EEXIST")
|
|
12650
|
-
throw l;
|
|
12651
|
-
} finally {
|
|
12652
|
-
unlinkSync5(tmp);
|
|
12653
|
-
}
|
|
12654
|
-
pem = readFileSync39(file);
|
|
12655
|
-
}
|
|
12656
|
-
}
|
|
12657
|
-
const k = createPrivateKey(pem);
|
|
12658
|
-
return k.asymmetricKeyType === "ed25519" ? k : null;
|
|
12659
|
-
} catch {
|
|
12660
|
-
return null;
|
|
12661
|
-
}
|
|
12662
|
-
}
|
|
12663
|
-
function signReceipt(runId, hash) {
|
|
12664
|
-
const key = loadSigningKey();
|
|
12665
|
-
if (!key)
|
|
12666
|
-
return { jwt: null, kid: null };
|
|
12667
|
-
const kid = kidOf2(createPublicKey2(key));
|
|
12668
|
-
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) }))}`;
|
|
12669
|
-
return { jwt: `${input}.${b64u(sign(null, Buffer.from(input), key))}`, kid };
|
|
12670
|
-
}
|
|
12671
13699
|
async function git3(ctx, args, keep = {}) {
|
|
12672
13700
|
const r = await safeGitRun(ctx.repoDir, args, { timeoutMs: 20000, ...keep });
|
|
12673
13701
|
return { out: r.stdout, code: r.exitCode };
|
|
@@ -12732,19 +13760,19 @@ function renderReceiptMd(r) {
|
|
|
12732
13760
|
].join(`
|
|
12733
13761
|
`);
|
|
12734
13762
|
}
|
|
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) =>
|
|
12736
|
-
const r = relative9(ctx.repoDir,
|
|
12737
|
-
return r.startsWith("..") ||
|
|
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) => {
|
|
13764
|
+
const r = relative9(ctx.repoDir, join59(ctx.runDir, "receipt.json"));
|
|
13765
|
+
return r.startsWith("..") || isAbsolute9(r) ? `.loki/runs/${ctx.runId}/receipt.json` : r;
|
|
12738
13766
|
}, commitStage, sealStage;
|
|
12739
13767
|
var init_seal = __esm(() => {
|
|
12740
13768
|
init_spec_file();
|
|
12741
|
-
init_pr_body();
|
|
12742
13769
|
init_mutation_proof();
|
|
12743
13770
|
init_wall();
|
|
12744
13771
|
init_assert_delta();
|
|
12745
13772
|
init_discard();
|
|
12746
13773
|
init_commit_filter();
|
|
12747
13774
|
init_scope2();
|
|
13775
|
+
init_receipt_signer();
|
|
12748
13776
|
init_pr_lessons();
|
|
12749
13777
|
init_visual_evidence();
|
|
12750
13778
|
init_testmap();
|
|
@@ -12835,8 +13863,8 @@ var init_seal = __esm(() => {
|
|
|
12835
13863
|
notProven.add(`Wall test not executed: ${wallNotRun} file(s), no reason recorded`);
|
|
12836
13864
|
if (wallNotRun > 0) {
|
|
12837
13865
|
try {
|
|
12838
|
-
const kept = new Set((Array.isArray(o.wall?.files) ? o.wall.files : []).map((f) =>
|
|
12839
|
-
for (const n2 of
|
|
13866
|
+
const kept = new Set((Array.isArray(o.wall?.files) ? o.wall.files : []).map((f) => basename20(String(f.path))));
|
|
13867
|
+
for (const n2 of readdirSync18(join59(ctx.runDir, "wall")).sort())
|
|
12840
13868
|
if (!kept.has(n2))
|
|
12841
13869
|
notProven.add(`wall test discarded: ${n2} (not_run)`);
|
|
12842
13870
|
} catch {}
|
|
@@ -12875,6 +13903,8 @@ var init_seal = __esm(() => {
|
|
|
12875
13903
|
notProven.add(n2);
|
|
12876
13904
|
for (const id of strs2(o.verify?.pre_red))
|
|
12877
13905
|
notProven.add(`pre red: ${id}`);
|
|
13906
|
+
if (typeof o.verify?.baseline_line === "string" && strs2(o.verify?.pre_red).length)
|
|
13907
|
+
notProven.add(o.verify.baseline_line);
|
|
12878
13908
|
for (const t of strs2(o.implement?.tests_reverted))
|
|
12879
13909
|
notProven.add(`reverted test edit: ${t}`);
|
|
12880
13910
|
if (ctx.provider !== "claude")
|
|
@@ -12895,19 +13925,19 @@ var init_seal = __esm(() => {
|
|
|
12895
13925
|
for (const [s, d] of Object.entries(o))
|
|
12896
13926
|
if (typeof d?.duration_s === "number")
|
|
12897
13927
|
stages[s] = d.duration_s;
|
|
12898
|
-
const T = hooks.time, time = T ? T.build(ctx, stages, T.firstEventMs(
|
|
13928
|
+
const T = hooks.time, time = T ? T.build(ctx, stages, T.firstEventMs(join59(ctx.runDir, "events.jsonl"))) : { wall_s: Object.values(stages).reduce((a, b) => a + (b ?? 0), 0), stages }, totalS = T?.reconciled(time) ?? null;
|
|
12899
13929
|
const iterIds = Object.values(o).flatMap((d) => [...strs2(d?.iteration_ids), ...strs2([d?.iteration_id])]);
|
|
12900
13930
|
if (iterIds.length === 0)
|
|
12901
13931
|
notProven.add("cost not measured (no iteration ids recorded)");
|
|
12902
13932
|
const cost = ctx.cost.read(ctx.repoDir, iterIds);
|
|
12903
13933
|
if (cost.unmetered)
|
|
12904
13934
|
notProven.add("cost unmetered (CLI invoker; recorded as 0)");
|
|
12905
|
-
const eventsPath =
|
|
13935
|
+
const eventsPath = join59(ctx.runDir, "events.jsonl");
|
|
12906
13936
|
const wallFiles = Array.isArray(o.wall?.files) ? o.wall.files : [];
|
|
12907
13937
|
const wallPassed = typeof o.verify?.wall_passed === "boolean" ? o.verify.wall_passed : null;
|
|
12908
13938
|
if (wallFiles.length > 0 && wallPassed === null)
|
|
12909
13939
|
notProven.add("wall result not recorded by verify");
|
|
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(
|
|
13940
|
+
const routeBlock = buildRouteBlock(process.env, ctx.provider, o.implement?.route ?? o.plan?.route_record ?? (routerEnabled(process.env) ? loadRouteRecord(ctx.runDir) : undefined), cost.router ?? (routerEnabled(process.env) ? sumResultCosts(join59(ctx.repoDir, ".loki"), iterIds).router : undefined), ctx.model);
|
|
12911
13941
|
if (routeBlock)
|
|
12912
13942
|
for (const l of routeNotProven(routeBlock))
|
|
12913
13943
|
notProven.add(l);
|
|
@@ -12949,7 +13979,7 @@ var init_seal = __esm(() => {
|
|
|
12949
13979
|
provider: ctx.provider,
|
|
12950
13980
|
model: ctx.model,
|
|
12951
13981
|
resumed: o.intake?.resumed === true,
|
|
12952
|
-
events_sha256: sha2562(
|
|
13982
|
+
events_sha256: sha2562(existsSync37(eventsPath) ? readFileSync44(eventsPath) : ""),
|
|
12953
13983
|
...await sealEvidence(ctx.repoDir, ctx.runDir, o, notProven, signal, ctx.emit),
|
|
12954
13984
|
log_seal: true,
|
|
12955
13985
|
...receiptBlock(process.env, cost.usd, cost.unmetered === true, totalS),
|
|
@@ -12969,11 +13999,11 @@ var init_seal = __esm(() => {
|
|
|
12969
13999
|
hash = receiptSha256(body);
|
|
12970
14000
|
}
|
|
12971
14001
|
const receipt = { ...body, receipt_sha256: hash, verification: { jwt: sig.jwt, kid: sig.kid } };
|
|
12972
|
-
|
|
12973
|
-
const path =
|
|
12974
|
-
|
|
14002
|
+
mkdirSync23(ctx.runDir, { recursive: true });
|
|
14003
|
+
const path = join59(ctx.runDir, "receipt.json");
|
|
14004
|
+
writeFileSync25(path, JSON.stringify(receipt, null, 2) + `
|
|
12975
14005
|
`);
|
|
12976
|
-
|
|
14006
|
+
writeFileSync25(join59(ctx.runDir, "receipt.md"), renderReceiptMd(receipt));
|
|
12977
14007
|
recordRun(process.env, ctx.repoDir, ctx.model, verdict, cost.usd, cost.unmetered === true, totalS);
|
|
12978
14008
|
const signed = sig.jwt !== null;
|
|
12979
14009
|
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) } : {} };
|
|
@@ -12988,7 +14018,7 @@ var init_seal = __esm(() => {
|
|
|
12988
14018
|
|
|
12989
14019
|
// ../../loki-ts/src/features/receipt_dsse.ts
|
|
12990
14020
|
import { createPublicKey as createPublicKey3, sign as sign2, verify as verify2 } from "crypto";
|
|
12991
|
-
import { readFileSync as
|
|
14021
|
+
import { readFileSync as readFileSync45 } from "fs";
|
|
12992
14022
|
function pae(payloadType, body) {
|
|
12993
14023
|
const t = Buffer.from(payloadType, "utf8");
|
|
12994
14024
|
return Buffer.concat([Buffer.from(`DSSEv1 ${t.length} `), t, Buffer.from(` ${body.length} `), body]);
|
|
@@ -13065,7 +14095,7 @@ function verifyDsseReceipt(env, d) {
|
|
|
13065
14095
|
}
|
|
13066
14096
|
function runIdGuard(receiptPath, runId) {
|
|
13067
14097
|
try {
|
|
13068
|
-
return envelopeRunIdProblem(JSON.parse(
|
|
14098
|
+
return envelopeRunIdProblem(JSON.parse(readFileSync45(receiptPath, "utf8")), runId);
|
|
13069
14099
|
} catch {
|
|
13070
14100
|
return null;
|
|
13071
14101
|
}
|
|
@@ -13079,16 +14109,16 @@ var init_keys_cmd = __esm(() => {
|
|
|
13079
14109
|
});
|
|
13080
14110
|
|
|
13081
14111
|
// ../../loki-ts/src/engine10/verify_cmd.ts
|
|
13082
|
-
import { existsSync as
|
|
13083
|
-
import { dirname as
|
|
13084
|
-
import { createHash as
|
|
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";
|
|
13085
14115
|
function pubFor(kid, given) {
|
|
13086
14116
|
if (given)
|
|
13087
14117
|
return kidOf2(given) === kid ? given : undefined;
|
|
13088
14118
|
const active = loadSigningKey(false);
|
|
13089
14119
|
const pubs = (process.env["LOKI_RECEIPT_RETIRED_PUBKEYS"] ?? "").split(":").map((f) => f.trim()).filter(Boolean).flatMap((f) => {
|
|
13090
14120
|
try {
|
|
13091
|
-
return [createPublicKey4(
|
|
14121
|
+
return [createPublicKey4(readFileSync46(f))];
|
|
13092
14122
|
} catch {
|
|
13093
14123
|
return [];
|
|
13094
14124
|
}
|
|
@@ -13118,18 +14148,18 @@ function checkAttestation(jwt, expectedHash, given) {
|
|
|
13118
14148
|
}
|
|
13119
14149
|
function checkEventLog(receiptPath, receipt) {
|
|
13120
14150
|
const bound = receipt["events_sha256"];
|
|
13121
|
-
if (typeof bound !== "string" || bound ===
|
|
14151
|
+
if (typeof bound !== "string" || bound === createHash15("sha256").digest("hex"))
|
|
13122
14152
|
return null;
|
|
13123
|
-
const path =
|
|
13124
|
-
if (!
|
|
14153
|
+
const path = join60(dirname27(receiptPath), "events.jsonl");
|
|
14154
|
+
if (!existsSync38(path))
|
|
13125
14155
|
return "events.jsonl is missing";
|
|
13126
|
-
const raw2 =
|
|
14156
|
+
const raw2 = readFileSync46(path, "utf8"), events2 = readEvents(path), lines = raw2.split(`
|
|
13127
14157
|
`).filter((l) => l.trim() !== "");
|
|
13128
14158
|
if (events2.length !== lines.length || events2.some((e, i) => e.seq !== i))
|
|
13129
14159
|
return "events.jsonl has a forged, edited or missing line";
|
|
13130
14160
|
if (events2.some((e) => e.type === "tamper.detected"))
|
|
13131
14161
|
return "the supervisor detected events.jsonl being modified during the run";
|
|
13132
|
-
const h =
|
|
14162
|
+
const h = createHash15("sha256");
|
|
13133
14163
|
for (const l of lines) {
|
|
13134
14164
|
if (h.copy().digest("hex") === bound)
|
|
13135
14165
|
return null;
|
|
@@ -13139,7 +14169,7 @@ function checkEventLog(receiptPath, receipt) {
|
|
|
13139
14169
|
return h.digest("hex") === bound ? null : "events.jsonl does not match the hash recorded at seal";
|
|
13140
14170
|
}
|
|
13141
14171
|
function checkLogSeal(receiptPath, hash, kid, given) {
|
|
13142
|
-
const path =
|
|
14172
|
+
const path = join60(dirname27(receiptPath), "events.jsonl"), events2 = readEvents(path), done = events2.findIndex((e) => e.type === "run.completed");
|
|
13143
14173
|
if (!events2.some((e) => e.type === "receipt.sealed" && e.data["receipt_sha256"] === hash))
|
|
13144
14174
|
return { verdict: "TAMPERED", reason: "events.jsonl has no receipt.sealed event for this receipt" };
|
|
13145
14175
|
if (done < 0)
|
|
@@ -13147,8 +14177,8 @@ function checkLogSeal(receiptPath, hash, kid, given) {
|
|
|
13147
14177
|
const seal = events2[done + 1], d = seal?.type === "log.sealed" ? seal.data : null;
|
|
13148
14178
|
if (!d)
|
|
13149
14179
|
return { verdict: "TAMPERED", reason: "events.jsonl has no signed log.sealed line after run.completed" };
|
|
13150
|
-
const lines =
|
|
13151
|
-
`).filter((l) => l.trim() !== ""), sha3 =
|
|
14180
|
+
const lines = readFileSync46(path, "utf8").split(`
|
|
14181
|
+
`).filter((l) => l.trim() !== ""), sha3 = createHash15("sha256").update(lines.slice(0, done + 1).join(`
|
|
13152
14182
|
`) + `
|
|
13153
14183
|
`).digest("hex");
|
|
13154
14184
|
const pub = d["kid"] === kid ? pubFor(kid, given) : undefined;
|
|
@@ -13157,11 +14187,11 @@ function checkLogSeal(receiptPath, hash, kid, given) {
|
|
|
13157
14187
|
return null;
|
|
13158
14188
|
}
|
|
13159
14189
|
async function verifyReceipt(receiptPath, deps = {}) {
|
|
13160
|
-
if (!
|
|
14190
|
+
if (!existsSync38(receiptPath))
|
|
13161
14191
|
return { verdict: "UNCHECKED", reasons: [`receipt not found: ${receiptPath}`] };
|
|
13162
14192
|
let receipt;
|
|
13163
14193
|
try {
|
|
13164
|
-
receipt = JSON.parse((deps.read ?? ((p) =>
|
|
14194
|
+
receipt = JSON.parse((deps.read ?? ((p) => readFileSync46(p, "utf8")))(receiptPath));
|
|
13165
14195
|
} catch {
|
|
13166
14196
|
return { verdict: "UNCHECKED", reasons: ["receipt.json is not valid JSON"] };
|
|
13167
14197
|
}
|
|
@@ -13207,6 +14237,7 @@ async function verifyReceipt(receiptPath, deps = {}) {
|
|
|
13207
14237
|
var computeReceiptHash = (receipt) => receiptSha256(receipt);
|
|
13208
14238
|
var init_verify_cmd = __esm(() => {
|
|
13209
14239
|
init_paths();
|
|
14240
|
+
init_run_status();
|
|
13210
14241
|
init_events();
|
|
13211
14242
|
init_seal();
|
|
13212
14243
|
init_receipt_dsse();
|
|
@@ -13216,8 +14247,8 @@ var init_verify_cmd = __esm(() => {
|
|
|
13216
14247
|
});
|
|
13217
14248
|
|
|
13218
14249
|
// src/server/app.ts
|
|
13219
|
-
import { existsSync as
|
|
13220
|
-
import { join as
|
|
14250
|
+
import { existsSync as existsSync40, readFileSync as readFileSync49, statSync as statSync21 } from "fs";
|
|
14251
|
+
import { join as join63, resolve as resolve15, sep as sep14 } from "path";
|
|
13221
14252
|
|
|
13222
14253
|
// node_modules/hono/dist/request/constants.js
|
|
13223
14254
|
var GET_MATCH_RESULT = Symbol();
|
|
@@ -23464,9 +24495,9 @@ function mount20(ctx) {
|
|
|
23464
24495
|
}
|
|
23465
24496
|
|
|
23466
24497
|
// src/server/routes/verify.ts
|
|
23467
|
-
import { readFileSync as
|
|
24498
|
+
import { readFileSync as readFileSync47, realpathSync as realpathSync20 } from "fs";
|
|
23468
24499
|
import { createPublicKey as createPublicKey5 } from "crypto";
|
|
23469
|
-
import { join as
|
|
24500
|
+
import { join as join61, sep as sep13 } from "path";
|
|
23470
24501
|
init_verify_cmd();
|
|
23471
24502
|
init_seal();
|
|
23472
24503
|
init_receipt_dsse();
|
|
@@ -23493,11 +24524,11 @@ function mount21(ctx) {
|
|
|
23493
24524
|
return done(404, "receipt not available", "not_found");
|
|
23494
24525
|
let runDir, file;
|
|
23495
24526
|
try {
|
|
23496
|
-
const root = realpathSync20(
|
|
23497
|
-
runDir = realpathSync20(
|
|
23498
|
-
if (runDir !==
|
|
24527
|
+
const root = realpathSync20(join61(repo.p, ".loki", "runs"));
|
|
24528
|
+
runDir = realpathSync20(join61(root, run2));
|
|
24529
|
+
if (runDir !== join61(root, run2))
|
|
23499
24530
|
return done(404, "receipt not found", "not_found");
|
|
23500
|
-
file = realpathSync20(
|
|
24531
|
+
file = realpathSync20(join61(runDir, "receipt.json"));
|
|
23501
24532
|
} catch {
|
|
23502
24533
|
return done(404, "receipt not found", "not_found");
|
|
23503
24534
|
}
|
|
@@ -23516,7 +24547,7 @@ function mount21(ctx) {
|
|
|
23516
24547
|
sha3 = r.receiptSha256 ?? null;
|
|
23517
24548
|
}
|
|
23518
24549
|
try {
|
|
23519
|
-
outcome = outcomeOf(JSON.parse(
|
|
24550
|
+
outcome = outcomeOf(JSON.parse(readFileSync47(file, "utf8")));
|
|
23520
24551
|
} catch {
|
|
23521
24552
|
outcome = "UNREADABLE";
|
|
23522
24553
|
}
|
|
@@ -23578,8 +24609,8 @@ function ttlCache(ttlMs = PROBE_CACHE_MS, now2 = Date.now) {
|
|
|
23578
24609
|
}
|
|
23579
24610
|
|
|
23580
24611
|
// src/server/legacy/shim.ts
|
|
23581
|
-
import { existsSync as
|
|
23582
|
-
import { join as
|
|
24612
|
+
import { existsSync as existsSync39, readFileSync as readFileSync48 } from "fs";
|
|
24613
|
+
import { join as join62 } from "path";
|
|
23583
24614
|
|
|
23584
24615
|
// src/server/legacy/routes.ts
|
|
23585
24616
|
var LEGACY_ROUTES = [
|
|
@@ -23951,10 +24982,10 @@ var logged = new Set;
|
|
|
23951
24982
|
var toHono = (p) => p.replace(/\{([A-Za-z_]+):path\}/g, ":$1{.+}").replace(/\{([A-Za-z_]+)\}/g, ":$1");
|
|
23952
24983
|
var NOT_SUPPORTED = "not yet supported by the Control Plane";
|
|
23953
24984
|
var repoVersion = () => {
|
|
23954
|
-
for (const f of [
|
|
24985
|
+
for (const f of [join62(import.meta.dir, "../../../../../VERSION")]) {
|
|
23955
24986
|
try {
|
|
23956
|
-
if (
|
|
23957
|
-
return
|
|
24987
|
+
if (existsSync39(f))
|
|
24988
|
+
return readFileSync48(f, "utf8").trim();
|
|
23958
24989
|
} catch {}
|
|
23959
24990
|
}
|
|
23960
24991
|
return "unknown";
|
|
@@ -24136,7 +25167,7 @@ function legacyShim(opts) {
|
|
|
24136
25167
|
|
|
24137
25168
|
// src/server/app.ts
|
|
24138
25169
|
var MAX_BODY2 = 1e6;
|
|
24139
|
-
var defaultUiDir = () => [
|
|
25170
|
+
var defaultUiDir = () => [join63(import.meta.dir, "../../ui/dist"), join63(import.meta.dir, "../ui/dist")].find((d) => existsSync40(join63(d, "index.html")));
|
|
24140
25171
|
function createApp(opts) {
|
|
24141
25172
|
const uiDir = opts.uiDir ?? defaultUiDir();
|
|
24142
25173
|
const { db, sqlite } = openDb(opts.dbPath);
|
|
@@ -24165,10 +25196,10 @@ function createApp(opts) {
|
|
|
24165
25196
|
await next();
|
|
24166
25197
|
});
|
|
24167
25198
|
const readInstalled = () => {
|
|
24168
|
-
for (const f of [process.env.LOKI_VERSION_FILE,
|
|
25199
|
+
for (const f of [process.env.LOKI_VERSION_FILE, join63(import.meta.dir, "../../../../VERSION")]) {
|
|
24169
25200
|
try {
|
|
24170
25201
|
if (f)
|
|
24171
|
-
return
|
|
25202
|
+
return readFileSync49(f, "utf8").trim() || "unknown";
|
|
24172
25203
|
} catch {}
|
|
24173
25204
|
}
|
|
24174
25205
|
return "unknown";
|
|
@@ -24277,13 +25308,13 @@ function createApp(opts) {
|
|
|
24277
25308
|
}
|
|
24278
25309
|
if (!uiDir || p.startsWith("/v1/"))
|
|
24279
25310
|
return c.json({ error: "not found" }, 404);
|
|
24280
|
-
const root =
|
|
24281
|
-
const f =
|
|
24282
|
-
if (f.startsWith(root + sep14) &&
|
|
25311
|
+
const root = resolve15(uiDir);
|
|
25312
|
+
const f = resolve15(root, `.${p}`);
|
|
25313
|
+
if (f.startsWith(root + sep14) && existsSync40(f) && statSync21(f).isFile())
|
|
24283
25314
|
return new Response(Bun.file(f));
|
|
24284
25315
|
if (/\.[a-z0-9]+$/i.test(p))
|
|
24285
25316
|
return c.json({ error: "not found" }, 404);
|
|
24286
|
-
return new Response(Bun.file(
|
|
25317
|
+
return new Response(Bun.file(join63(root, "index.html")));
|
|
24287
25318
|
});
|
|
24288
25319
|
return { app, db, close: () => {
|
|
24289
25320
|
clearInterval(reconciler);
|