@wrongstack/cli 0.305.1 → 0.306.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/dist/{auth-EPERLKYI.js → auth-GVPYLJPS.js} +7 -7
- package/dist/boot/short-circuit-flags.d.ts +7 -1
- package/dist/boot/system-prompt-menu.d.ts +151 -0
- package/dist/chimera-cascade-evidence.d.ts +102 -0
- package/dist/chimera-review-task.d.ts +7 -0
- package/dist/{chunk-2H47YMCV.js → chunk-5JW3XCRA.js} +3 -3
- package/dist/{chunk-5KFLLTSQ.js → chunk-WC3DK6BI.js} +5 -5
- package/dist/{cli-main-6YE423OH.js → cli-main-DHNFGP3Z.js} +191 -172
- package/dist/{execution-QYFWDD3Y.js → execution-TTE7R5UY.js} +311 -36
- package/dist/execution-chimera-cascade.d.ts +7 -1
- package/dist/{hq-5G6D5YIU.js → hq-ZK6PGN3W.js} +95 -6
- package/dist/index.js +755 -542
- package/dist/{modeldiag-SIVUOX2Y.js → modeldiag-6VS5KDP5.js} +4 -4
- package/dist/wiring/management-tools.d.ts +8 -1
- package/package.json +24 -24
|
@@ -37,23 +37,24 @@ import {
|
|
|
37
37
|
writeWebuiSessionChildReady
|
|
38
38
|
} from "./chunk-3JPTNADJ.js";
|
|
39
39
|
import {
|
|
40
|
-
|
|
41
|
-
} from "./chunk-
|
|
42
|
-
import {
|
|
43
|
-
activeProfileConfigPath
|
|
44
|
-
} from "./chunk-YMXXOOFN.js";
|
|
40
|
+
CLI_VERSION
|
|
41
|
+
} from "./chunk-XJXDOF63.js";
|
|
45
42
|
import {
|
|
46
43
|
terminalLink,
|
|
47
44
|
terminalText
|
|
48
45
|
} from "./chunk-KE7E7DPX.js";
|
|
49
46
|
import {
|
|
50
|
-
|
|
51
|
-
} from "./chunk-
|
|
47
|
+
resolveActiveApiKey
|
|
48
|
+
} from "./chunk-SZ42FYPT.js";
|
|
49
|
+
import {
|
|
50
|
+
activeProfileConfigPath
|
|
51
|
+
} from "./chunk-YMXXOOFN.js";
|
|
52
52
|
import "./chunk-7OCVIDC7.js";
|
|
53
53
|
|
|
54
54
|
// src/execution.ts
|
|
55
55
|
import * as path13 from "node:path";
|
|
56
56
|
import { effectiveFallbackChain as effectiveFallbackChain2, setQueuedMessagesSnapshot as setQueuedMessagesSnapshot2 } from "@wrongstack/core/agent";
|
|
57
|
+
import { updateReviewReportEvidence } from "@wrongstack/core/plugin";
|
|
57
58
|
import { attachTodosCheckpoint as attachTodosCheckpoint2 } from "@wrongstack/core/storage";
|
|
58
59
|
import { normalizeTokenSavingTier as normalizeTokenSavingTier2 } from "@wrongstack/core/types";
|
|
59
60
|
import { mergeCustomModelDefs } from "@wrongstack/core/utils";
|
|
@@ -2746,6 +2747,128 @@ import * as fsp from "node:fs/promises";
|
|
|
2746
2747
|
import * as path10 from "node:path";
|
|
2747
2748
|
import { emitReviewIfChanged } from "@wrongstack/core/plugin";
|
|
2748
2749
|
|
|
2750
|
+
// src/chimera-cascade-evidence.ts
|
|
2751
|
+
import { spawn as spawn2 } from "node:child_process";
|
|
2752
|
+
var JSON_BLOCK_RE = /```json\s*([\s\S]*?)```/gi;
|
|
2753
|
+
function extractCascadeEvidence(text) {
|
|
2754
|
+
if (!text) return null;
|
|
2755
|
+
for (const match of text.matchAll(JSON_BLOCK_RE)) {
|
|
2756
|
+
const body = match[1]?.trim();
|
|
2757
|
+
if (!body) continue;
|
|
2758
|
+
let parsed;
|
|
2759
|
+
try {
|
|
2760
|
+
parsed = JSON.parse(body);
|
|
2761
|
+
} catch {
|
|
2762
|
+
continue;
|
|
2763
|
+
}
|
|
2764
|
+
const evidence = parseEvidenceObject(parsed);
|
|
2765
|
+
if (evidence) return evidence;
|
|
2766
|
+
}
|
|
2767
|
+
return null;
|
|
2768
|
+
}
|
|
2769
|
+
function parseEvidenceObject(parsed) {
|
|
2770
|
+
if (typeof parsed !== "object" || parsed === null) return null;
|
|
2771
|
+
const root = parsed;
|
|
2772
|
+
const raw = root.verification_evidence;
|
|
2773
|
+
if (typeof raw !== "object" || raw === null) return null;
|
|
2774
|
+
const checks = raw;
|
|
2775
|
+
const out = {};
|
|
2776
|
+
let found = false;
|
|
2777
|
+
for (const key of ["typecheck", "lint", "tests"]) {
|
|
2778
|
+
const check = parseSingleCheck(checks[key]);
|
|
2779
|
+
if (check) {
|
|
2780
|
+
out[key] = check;
|
|
2781
|
+
found = true;
|
|
2782
|
+
}
|
|
2783
|
+
}
|
|
2784
|
+
return found ? out : null;
|
|
2785
|
+
}
|
|
2786
|
+
function parseSingleCheck(raw) {
|
|
2787
|
+
if (typeof raw !== "object" || raw === null) return void 0;
|
|
2788
|
+
const check = raw;
|
|
2789
|
+
const command = check.command;
|
|
2790
|
+
const exitCode = check.exitCode;
|
|
2791
|
+
if (typeof command !== "string" || command.trim().length === 0) return void 0;
|
|
2792
|
+
if (typeof exitCode !== "number" || !Number.isInteger(exitCode) || exitCode < 0 || exitCode > 255) {
|
|
2793
|
+
return void 0;
|
|
2794
|
+
}
|
|
2795
|
+
const output = typeof check.output === "string" ? truncateOutput(check.output) : void 0;
|
|
2796
|
+
return { command: command.trim(), exitCode, output };
|
|
2797
|
+
}
|
|
2798
|
+
var MAX_EVIDENCE_OUTPUT_CHARS = 2e3;
|
|
2799
|
+
function truncateOutput(output) {
|
|
2800
|
+
return output.length > MAX_EVIDENCE_OUTPUT_CHARS ? `${output.slice(0, MAX_EVIDENCE_OUTPUT_CHARS)}
|
|
2801
|
+
\u2026 (truncated)` : output;
|
|
2802
|
+
}
|
|
2803
|
+
var SAFE_EXECUTABLES = /* @__PURE__ */ new Set([
|
|
2804
|
+
"pnpm",
|
|
2805
|
+
"npm",
|
|
2806
|
+
"yarn",
|
|
2807
|
+
"npx",
|
|
2808
|
+
"bun",
|
|
2809
|
+
"node",
|
|
2810
|
+
"tsc",
|
|
2811
|
+
"biome",
|
|
2812
|
+
"eslint",
|
|
2813
|
+
"vitest",
|
|
2814
|
+
"jest"
|
|
2815
|
+
]);
|
|
2816
|
+
var CASCADE_EVIDENCE_COMMAND_TIMEOUT_MS = 12e4;
|
|
2817
|
+
var CASCADE_EVIDENCE_UNSAFE_EXIT = 126;
|
|
2818
|
+
var CASCADE_EVIDENCE_RUN_ERROR_EXIT = 127;
|
|
2819
|
+
function isSafeCascadeCommand(command) {
|
|
2820
|
+
if (!command || command.length > 500) return false;
|
|
2821
|
+
const tokens = command.trim().split(/\s+/);
|
|
2822
|
+
if (tokens.length === 0) return false;
|
|
2823
|
+
if (!SAFE_EXECUTABLES.has(tokens[0])) return false;
|
|
2824
|
+
return tokens.every((token) => /^[A-Za-z0-9@/._:=~+*?\[\]-]+$/.test(token));
|
|
2825
|
+
}
|
|
2826
|
+
var runCascadeVerificationCommand = (command, cwd, timeoutMs) => {
|
|
2827
|
+
if (!isSafeCascadeCommand(command)) {
|
|
2828
|
+
return Promise.resolve({ exitCode: CASCADE_EVIDENCE_UNSAFE_EXIT });
|
|
2829
|
+
}
|
|
2830
|
+
const tokens = command.trim().split(/\s+/);
|
|
2831
|
+
const executable = tokens[0];
|
|
2832
|
+
const args = tokens.slice(1);
|
|
2833
|
+
return new Promise((resolve3) => {
|
|
2834
|
+
let child;
|
|
2835
|
+
try {
|
|
2836
|
+
child = spawn2(executable, args, {
|
|
2837
|
+
cwd,
|
|
2838
|
+
stdio: "ignore",
|
|
2839
|
+
windowsHide: true,
|
|
2840
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
2841
|
+
});
|
|
2842
|
+
} catch {
|
|
2843
|
+
resolve3({ exitCode: CASCADE_EVIDENCE_RUN_ERROR_EXIT });
|
|
2844
|
+
return;
|
|
2845
|
+
}
|
|
2846
|
+
child.on("error", () => resolve3({ exitCode: CASCADE_EVIDENCE_RUN_ERROR_EXIT }));
|
|
2847
|
+
child.on("close", (code) => resolve3({ exitCode: code ?? CASCADE_EVIDENCE_RUN_ERROR_EXIT }));
|
|
2848
|
+
});
|
|
2849
|
+
};
|
|
2850
|
+
async function verifyCascadeEvidence(evidence, cwd, runCommand = runCascadeVerificationCommand, timeoutMs = CASCADE_EVIDENCE_COMMAND_TIMEOUT_MS) {
|
|
2851
|
+
if (!evidence) return { status: "missing", checks: [] };
|
|
2852
|
+
if (!evidence.typecheck) return { status: "failed", checks: [] };
|
|
2853
|
+
const checks = [];
|
|
2854
|
+
for (const name of ["typecheck", "lint", "tests"]) {
|
|
2855
|
+
const claimed = evidence[name];
|
|
2856
|
+
if (!claimed) continue;
|
|
2857
|
+
const actual = await runCommand(claimed.command, cwd, timeoutMs);
|
|
2858
|
+
const ok = claimed.exitCode === 0 && actual.exitCode === claimed.exitCode;
|
|
2859
|
+
checks.push({
|
|
2860
|
+
name,
|
|
2861
|
+
command: claimed.command,
|
|
2862
|
+
claimedExitCode: claimed.exitCode,
|
|
2863
|
+
actualExitCode: actual.exitCode,
|
|
2864
|
+
ok
|
|
2865
|
+
});
|
|
2866
|
+
}
|
|
2867
|
+
if (checks.length === 0) return { status: "missing", checks };
|
|
2868
|
+
const status = checks.every((c) => c.ok) ? "verified" : "failed";
|
|
2869
|
+
return { status, checks };
|
|
2870
|
+
}
|
|
2871
|
+
|
|
2749
2872
|
// src/chimera-review-task.ts
|
|
2750
2873
|
import { parseReviewSeverity } from "@wrongstack/core/plugin";
|
|
2751
2874
|
function truncateAtCodePointBoundary(text, maxCodeUnits) {
|
|
@@ -2846,6 +2969,34 @@ function buildChimeraReviewTaskDescription(p) {
|
|
|
2846
2969
|
lines.push("structured review report.");
|
|
2847
2970
|
return lines.join("\n");
|
|
2848
2971
|
}
|
|
2972
|
+
var CASCADE_EVIDENCE_INSTRUCTIONS = [
|
|
2973
|
+
`After fixing, run the project's REAL verification commands on the code you changed`,
|
|
2974
|
+
`(typecheck, lint, and the tests covering the fixed code) and return a machine-evidence`,
|
|
2975
|
+
`block as the LAST section of your response. The block MUST be a fenced JSON block`,
|
|
2976
|
+
`(exactly \`\`\`json ... \`\`\`) with this shape:`,
|
|
2977
|
+
``,
|
|
2978
|
+
"```json",
|
|
2979
|
+
"{",
|
|
2980
|
+
' "verification_evidence": {',
|
|
2981
|
+
' "typecheck": { "command": "pnpm typecheck", "exitCode": 0 },',
|
|
2982
|
+
' "lint": { "command": "pnpm lint", "exitCode": 0 },',
|
|
2983
|
+
' "tests": { "command": "pnpm test --filter affected", "exitCode": 0 }',
|
|
2984
|
+
" }",
|
|
2985
|
+
"}",
|
|
2986
|
+
"```",
|
|
2987
|
+
``,
|
|
2988
|
+
`Rules:`,
|
|
2989
|
+
`- \`command\` must be the EXACT command you ran (plain executable + args, no shell`,
|
|
2990
|
+
` chaining like \`&&\`/\`;\`/\`|\`, no redirection, no shell expansion). These commands are`,
|
|
2991
|
+
` re-run verbatim by the orchestrator to verify your work.`,
|
|
2992
|
+
`- \`exitCode\` is the real process exit code you observed: 0 = passed, non-zero = failed.`,
|
|
2993
|
+
` Never invent or omit an exit code. If a command cannot be run, omit that key entirely`,
|
|
2994
|
+
` and explain why in your report text \u2014 an omitted key is honest, a fabricated 0 is not.`,
|
|
2995
|
+
`- If your fix does not pass a command, report the true non-zero exit code; the`,
|
|
2996
|
+
` orchestrator will treat the fix as unverified and keep the finding open.`,
|
|
2997
|
+
`- At least \`typecheck\` must be present; include \`lint\` and \`tests\` when the project`,
|
|
2998
|
+
` has them.`
|
|
2999
|
+
].join("\n");
|
|
2849
3000
|
function buildChimeraCascadeTaskDescription(agentKind, p) {
|
|
2850
3001
|
const fileList = p.bundle.files.map((f) => `- ${f.path}`).join("\n");
|
|
2851
3002
|
const reportSlice = truncateAtCodePointBoundary(p.reviewText, 12e3);
|
|
@@ -2866,8 +3017,9 @@ function buildChimeraCascadeTaskDescription(agentKind, p) {
|
|
|
2866
3017
|
`Investigate the security findings above. Read the flagged files, confirm or refute`,
|
|
2867
3018
|
`each finding, and **apply fixes** for confirmed vulnerabilities using the edit tool.`,
|
|
2868
3019
|
`Use severity (Critical/High/Medium), file:line citations, and remediation steps.`,
|
|
2869
|
-
`
|
|
2870
|
-
|
|
3020
|
+
`If a finding is a false positive, say so and do not modify the file.`,
|
|
3021
|
+
``,
|
|
3022
|
+
CASCADE_EVIDENCE_INSTRUCTIONS
|
|
2871
3023
|
].join("\n");
|
|
2872
3024
|
}
|
|
2873
3025
|
return [
|
|
@@ -2884,9 +3036,10 @@ function buildChimeraCascadeTaskDescription(agentKind, p) {
|
|
|
2884
3036
|
``,
|
|
2885
3037
|
`Hunt for the bugs flagged above. Read the affected files, trace each finding to its`,
|
|
2886
3038
|
`root cause, and **apply minimal fixes** for confirmed bugs using the edit tool.`,
|
|
2887
|
-
`Use severity (Critical/High/Medium), file:line citations.
|
|
2888
|
-
`
|
|
2889
|
-
|
|
3039
|
+
`Use severity (Critical/High/Medium), file:line citations. If a finding is a false`,
|
|
3040
|
+
`positive, say so and do not modify the file.`,
|
|
3041
|
+
``,
|
|
3042
|
+
CASCADE_EVIDENCE_INSTRUCTIONS
|
|
2890
3043
|
].join("\n");
|
|
2891
3044
|
}
|
|
2892
3045
|
|
|
@@ -3003,7 +3156,9 @@ function installChimeraCascadeHandler({
|
|
|
3003
3156
|
session,
|
|
3004
3157
|
getPendingWork,
|
|
3005
3158
|
trackWork,
|
|
3006
|
-
buildLadder
|
|
3159
|
+
buildLadder,
|
|
3160
|
+
verifyEvidence = verifyCascadeEvidence,
|
|
3161
|
+
persistEvidence
|
|
3007
3162
|
}) {
|
|
3008
3163
|
events.onPattern("chimera.cascade_needed", (_event, payload) => {
|
|
3009
3164
|
const p = payload;
|
|
@@ -3016,6 +3171,7 @@ function installChimeraCascadeHandler({
|
|
|
3016
3171
|
await previousWork;
|
|
3017
3172
|
} catch {
|
|
3018
3173
|
}
|
|
3174
|
+
const agentEvidence = [];
|
|
3019
3175
|
for (const agentKind of p.agents) {
|
|
3020
3176
|
try {
|
|
3021
3177
|
const taskDesc = buildChimeraCascadeTaskDescription(agentKind, p);
|
|
@@ -3059,6 +3215,10 @@ function installChimeraCascadeHandler({
|
|
|
3059
3215
|
const result = outcome.result;
|
|
3060
3216
|
if (result?.status === "success") {
|
|
3061
3217
|
const resultText = typeof result.result === "string" ? result.result : JSON.stringify(result.result);
|
|
3218
|
+
const claimed = extractCascadeEvidence(resultText);
|
|
3219
|
+
if (claimed) {
|
|
3220
|
+
agentEvidence.push({ agentKind, evidence: claimed });
|
|
3221
|
+
}
|
|
3062
3222
|
await session.append({
|
|
3063
3223
|
type: "llm_response",
|
|
3064
3224
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -3089,7 +3249,21 @@ function installChimeraCascadeHandler({
|
|
|
3089
3249
|
});
|
|
3090
3250
|
}
|
|
3091
3251
|
}
|
|
3092
|
-
|
|
3252
|
+
const accumulatedEvidence = {};
|
|
3253
|
+
for (const { evidence } of agentEvidence) {
|
|
3254
|
+
for (const name of ["typecheck", "lint", "tests"]) {
|
|
3255
|
+
if (evidence[name]) accumulatedEvidence[name] = evidence[name];
|
|
3256
|
+
}
|
|
3257
|
+
}
|
|
3258
|
+
await maybeReReviewCascade({
|
|
3259
|
+
events,
|
|
3260
|
+
session,
|
|
3261
|
+
bundle: p.bundle,
|
|
3262
|
+
reportId: p.reportId,
|
|
3263
|
+
claimedEvidence: accumulatedEvidence,
|
|
3264
|
+
verifyEvidence,
|
|
3265
|
+
persistEvidence
|
|
3266
|
+
});
|
|
3093
3267
|
})();
|
|
3094
3268
|
trackWork(pendingWork);
|
|
3095
3269
|
});
|
|
@@ -3097,16 +3271,71 @@ function installChimeraCascadeHandler({
|
|
|
3097
3271
|
async function maybeReReviewCascade({
|
|
3098
3272
|
events,
|
|
3099
3273
|
session,
|
|
3100
|
-
bundle
|
|
3274
|
+
bundle,
|
|
3275
|
+
reportId,
|
|
3276
|
+
claimedEvidence,
|
|
3277
|
+
verifyEvidence,
|
|
3278
|
+
persistEvidence
|
|
3101
3279
|
}) {
|
|
3102
3280
|
const maxDepth = bundle.maxCascadeDepth ?? 0;
|
|
3103
3281
|
const currentDepth = bundle.cascadeDepth ?? 0;
|
|
3282
|
+
const hasClaimedEvidence = Object.keys(claimedEvidence).length > 0;
|
|
3283
|
+
const verification = hasClaimedEvidence ? await verifyEvidence(
|
|
3284
|
+
claimedEvidence,
|
|
3285
|
+
bundle.cwd,
|
|
3286
|
+
void 0,
|
|
3287
|
+
CASCADE_EVIDENCE_COMMAND_TIMEOUT_MS
|
|
3288
|
+
) : { status: "missing", checks: [] };
|
|
3289
|
+
const evidenceBundle = {
|
|
3290
|
+
...bundle,
|
|
3291
|
+
evidenceStatus: verification.status,
|
|
3292
|
+
evidenceChecks: verification.checks
|
|
3293
|
+
};
|
|
3294
|
+
if (reportId && persistEvidence) {
|
|
3295
|
+
try {
|
|
3296
|
+
await persistEvidence(reportId, verification.status, verification.checks);
|
|
3297
|
+
} catch (error) {
|
|
3298
|
+
await session.append({
|
|
3299
|
+
type: "error",
|
|
3300
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3301
|
+
message: `Chimera cascade evidence persistence failed: ${errorText2(error)}`,
|
|
3302
|
+
phase: "agent"
|
|
3303
|
+
});
|
|
3304
|
+
}
|
|
3305
|
+
}
|
|
3306
|
+
await session.append({
|
|
3307
|
+
type: "llm_response",
|
|
3308
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3309
|
+
content: [
|
|
3310
|
+
{
|
|
3311
|
+
type: "text",
|
|
3312
|
+
text: renderEvidenceSummary(verification.status, verification.checks)
|
|
3313
|
+
}
|
|
3314
|
+
],
|
|
3315
|
+
stopReason: "end_turn",
|
|
3316
|
+
usage: { input: 0, output: 0 }
|
|
3317
|
+
});
|
|
3318
|
+
if (verification.status !== "verified") {
|
|
3319
|
+
await session.append({
|
|
3320
|
+
type: "llm_response",
|
|
3321
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3322
|
+
content: [
|
|
3323
|
+
{
|
|
3324
|
+
type: "text",
|
|
3325
|
+
text: `\u{1F982} Chimera cascade evidence ${verification.status} \u2014 fix not confirmed, skipping re-review. Findings remain open.`
|
|
3326
|
+
}
|
|
3327
|
+
],
|
|
3328
|
+
stopReason: "end_turn",
|
|
3329
|
+
usage: { input: 0, output: 0 }
|
|
3330
|
+
});
|
|
3331
|
+
return;
|
|
3332
|
+
}
|
|
3104
3333
|
if (maxDepth > 0 && currentDepth < maxDepth) {
|
|
3105
3334
|
try {
|
|
3106
3335
|
const reReadFiles = [];
|
|
3107
|
-
for (const f of
|
|
3336
|
+
for (const f of evidenceBundle.files) {
|
|
3108
3337
|
try {
|
|
3109
|
-
const absPath = path10.join(
|
|
3338
|
+
const absPath = path10.join(evidenceBundle.cwd, f.path);
|
|
3110
3339
|
const content = await fsp.readFile(absPath, "utf8");
|
|
3111
3340
|
reReadFiles.push({ path: f.path, status: "modified", content });
|
|
3112
3341
|
} catch {
|
|
@@ -3114,7 +3343,7 @@ async function maybeReReviewCascade({
|
|
|
3114
3343
|
}
|
|
3115
3344
|
if (reReadFiles.length > 0) {
|
|
3116
3345
|
const reReviewBundle = {
|
|
3117
|
-
...
|
|
3346
|
+
...evidenceBundle,
|
|
3118
3347
|
files: reReadFiles,
|
|
3119
3348
|
cascadeDepth: currentDepth + 1
|
|
3120
3349
|
};
|
|
@@ -3172,18 +3401,28 @@ async function maybeReReviewCascade({
|
|
|
3172
3401
|
});
|
|
3173
3402
|
}
|
|
3174
3403
|
}
|
|
3404
|
+
function renderEvidenceSummary(status, checks) {
|
|
3405
|
+
if (status === "missing") return "no machine evidence supplied";
|
|
3406
|
+
const summary = checks.map((check) => `${check.name}:${check.ok ? "pass" : "fail"}(${check.actualExitCode})`).join(", ");
|
|
3407
|
+
return `${status}${summary ? ` \u2014 ${summary}` : ""}`;
|
|
3408
|
+
}
|
|
3409
|
+
function errorText2(error) {
|
|
3410
|
+
return error instanceof Error ? error.message : String(error);
|
|
3411
|
+
}
|
|
3175
3412
|
|
|
3176
3413
|
// src/execution-chimera-review.ts
|
|
3177
3414
|
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
3178
3415
|
import path11 from "node:path";
|
|
3179
|
-
import { effectiveFallbackChain } from "@wrongstack/core/agent";
|
|
3416
|
+
import { effectiveFallbackChain, fallbackProfileChain as fallbackProfileChain2 } from "@wrongstack/core/agent";
|
|
3180
3417
|
import {
|
|
3181
3418
|
CHIMERA_REVIEW_PROMPT,
|
|
3419
|
+
classifyChimeraReviewSource,
|
|
3182
3420
|
integrateFindings,
|
|
3183
3421
|
maybeCompactReviewStores,
|
|
3184
3422
|
parseChimeraReviewReport,
|
|
3185
3423
|
persistReviewReport,
|
|
3186
|
-
recordCompletedReview
|
|
3424
|
+
recordCompletedReview,
|
|
3425
|
+
verifyFindingsAgainstDisk
|
|
3187
3426
|
} from "@wrongstack/core/plugin";
|
|
3188
3427
|
function normalizeFileKeyForCitation(raw, cwd) {
|
|
3189
3428
|
const forward = raw.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
@@ -3267,7 +3506,13 @@ function installChimeraReviewHandler({
|
|
|
3267
3506
|
const rawModel = p.reviewFallbackModels ? p.config.model?.trim() || void 0 : tModel;
|
|
3268
3507
|
const baseProvider = rawProvider || tProvider || config.provider;
|
|
3269
3508
|
const baseModel = rawModel || tModel || config.model;
|
|
3270
|
-
const
|
|
3509
|
+
const configFallbacks = [...p.config.fallbackModels ?? []];
|
|
3510
|
+
if (p.config.fallbackProfile) {
|
|
3511
|
+
configFallbacks.push(...fallbackProfileChain2(config, p.config.fallbackProfile));
|
|
3512
|
+
}
|
|
3513
|
+
const baseFallbacks = p.reviewFallbackModels ? [...p.reviewFallbackModels] : resolveReviewerFallbackModels(
|
|
3514
|
+
configFallbacks.length > 0 ? configFallbacks : void 0
|
|
3515
|
+
);
|
|
3271
3516
|
const assigned = assignReviewerModels(
|
|
3272
3517
|
baseProvider,
|
|
3273
3518
|
baseModel,
|
|
@@ -3368,18 +3613,34 @@ function installChimeraReviewHandler({
|
|
|
3368
3613
|
}
|
|
3369
3614
|
const reviewText = typeof result.result === "string" ? result.result.trim() : JSON.stringify(result.result);
|
|
3370
3615
|
const reportId = randomUUID3();
|
|
3616
|
+
const reviewSource = classifyChimeraReviewSource(p);
|
|
3617
|
+
const winningAttempt = outcome.wonAt ? ladder[outcome.wonAt - 1] : void 0;
|
|
3618
|
+
const parsedReport = parseChimeraReviewReport(reviewText, {
|
|
3619
|
+
sessionId: reviewSessionId,
|
|
3620
|
+
agentId: p.fileProvenance?.find((entry) => entry.agentId)?.agentId ?? "chimera-review",
|
|
3621
|
+
reviewerModel: winningAttempt ? attemptLabel(winningAttempt) : void 0,
|
|
3622
|
+
reviewType: reviewSource,
|
|
3623
|
+
reportId
|
|
3624
|
+
});
|
|
3625
|
+
const verifiedFindings = await verifyFindingsAgainstDisk(parsedReport.findings, {
|
|
3626
|
+
cwd: p.cwd
|
|
3627
|
+
});
|
|
3628
|
+
const verifiedParsedReport = {
|
|
3629
|
+
...parsedReport,
|
|
3630
|
+
findings: verifiedFindings
|
|
3631
|
+
};
|
|
3371
3632
|
await persistAndEmitCompletion({
|
|
3372
3633
|
bundle: p,
|
|
3373
3634
|
reviewText,
|
|
3374
3635
|
status: "success",
|
|
3375
3636
|
cwd: p.cwd,
|
|
3376
3637
|
sessionId: reviewSessionId,
|
|
3377
|
-
reportId
|
|
3638
|
+
reportId,
|
|
3639
|
+
parsedReport: verifiedParsedReport
|
|
3378
3640
|
});
|
|
3379
3641
|
if (reviewText) {
|
|
3380
3642
|
const reviewHasFindings = !isChimeraAllClearReview(reviewText);
|
|
3381
|
-
const
|
|
3382
|
-
const citedFindings = parsedReview.findings.filter((finding) => finding.location);
|
|
3643
|
+
const citedFindings = verifiedFindings.filter((finding) => finding.location);
|
|
3383
3644
|
const citedPaths = new Set(
|
|
3384
3645
|
p.files.map((file) => normalizeFileKeyForCitation(file.path, p.cwd))
|
|
3385
3646
|
);
|
|
@@ -3391,14 +3652,28 @@ function installChimeraReviewHandler({
|
|
|
3391
3652
|
if (bucket) bucket.push(normalized);
|
|
3392
3653
|
else changedBasenameIndex.set(basename7, [normalized]);
|
|
3393
3654
|
}
|
|
3394
|
-
const
|
|
3655
|
+
const inScopeCited = citedFindings.filter((finding) => {
|
|
3395
3656
|
const citedKey = normalizeFileKeyForCitation(finding.location.file, p.cwd);
|
|
3396
|
-
if (citedPaths.has(citedKey)) return
|
|
3657
|
+
if (citedPaths.has(citedKey)) return true;
|
|
3397
3658
|
const basename7 = path11.basename(finding.location.file).toLowerCase();
|
|
3398
3659
|
const bucket = changedBasenameIndex.get(basename7);
|
|
3399
|
-
return
|
|
3660
|
+
return !!(bucket && bucket.length === 1);
|
|
3400
3661
|
});
|
|
3401
|
-
const
|
|
3662
|
+
const droppedCitations = citedFindings.filter(
|
|
3663
|
+
(finding) => !inScopeCited.includes(finding)
|
|
3664
|
+
);
|
|
3665
|
+
const actionableFindings = inScopeCited.filter(
|
|
3666
|
+
(finding) => finding.verification?.status === "verified"
|
|
3667
|
+
);
|
|
3668
|
+
const unverifiedInScope = inScopeCited.filter(
|
|
3669
|
+
(finding) => finding.verification?.status === "unverified"
|
|
3670
|
+
);
|
|
3671
|
+
const failedVerificationCount = inScopeCited.filter(
|
|
3672
|
+
(finding) => finding.verification?.status === "failed"
|
|
3673
|
+
).length;
|
|
3674
|
+
const effectiveHasFindings = reviewHasFindings && actionableFindings.length > 0;
|
|
3675
|
+
const verifiedCount = actionableFindings.length;
|
|
3676
|
+
const unverifiedCount = unverifiedInScope.length;
|
|
3402
3677
|
const hallucinationNote = droppedCitations.length > 0 ? `
|
|
3403
3678
|
|
|
3404
3679
|
\u26A0\uFE0F Citation validation: ${droppedCitations.length} finding(s) cite files outside the changed-file set and should be verified before acting.` : "";
|
|
@@ -3441,7 +3716,7 @@ ${reviewBody}`;
|
|
|
3441
3716
|
reviewLength: reviewText.length,
|
|
3442
3717
|
messageId: reviewMailMessageId
|
|
3443
3718
|
});
|
|
3444
|
-
if (!
|
|
3719
|
+
if (!reviewHasFindings) {
|
|
3445
3720
|
try {
|
|
3446
3721
|
await mailbox.ack({
|
|
3447
3722
|
messageId: reviewMailMessageId,
|
|
@@ -3485,11 +3760,8 @@ ${reviewBody}`;
|
|
|
3485
3760
|
phase: "agent"
|
|
3486
3761
|
});
|
|
3487
3762
|
}
|
|
3488
|
-
const findingCount =
|
|
3489
|
-
|
|
3490
|
-
parsedReview.findings.length - droppedCitations.length
|
|
3491
|
-
);
|
|
3492
|
-
const message = effectiveHasFindings ? `\u{1F982} Chimera report ready \u2014 ${findingCount || "unparsed"} potential finding(s) across ${p.files.length} file(s). No follow-up started; open the mailbox and explicitly ask the leader to act if wanted.` : `\u{1F982} Chimera report ready \u2014 ${p.files.length} file(s) checked, no actionable findings. No follow-up started.`;
|
|
3763
|
+
const findingCount = actionableFindings.length;
|
|
3764
|
+
const message = effectiveHasFindings ? `\u{1F982} Chimera report ready \u2014 ${findingCount || "unparsed"} potential finding(s) (${verifiedCount} verified against disk, ${unverifiedCount} unverified, ${failedVerificationCount} failed) across ${p.files.length} file(s). No follow-up started; open the mailbox and explicitly ask the leader to act if wanted.` : `\u{1F982} Chimera report ready \u2014 ${p.files.length} file(s) checked, no actionable findings. No follow-up started.`;
|
|
3493
3765
|
events.emitCustom("chimera.report_available", {
|
|
3494
3766
|
reportId,
|
|
3495
3767
|
sessionId: reviewSessionId,
|
|
@@ -3711,7 +3983,7 @@ function parseSuggestionsFromOutput(finalText, todos) {
|
|
|
3711
3983
|
setAutoSuggestions([]);
|
|
3712
3984
|
return null;
|
|
3713
3985
|
}
|
|
3714
|
-
const { texts, autoTexts } = parseNextSteps(finalText
|
|
3986
|
+
const { texts, autoTexts } = parseNextSteps(finalText);
|
|
3715
3987
|
if (autoTexts.length > 0) {
|
|
3716
3988
|
setAutoSuggestions(autoTexts);
|
|
3717
3989
|
}
|
|
@@ -4909,6 +5181,9 @@ async function execute(deps) {
|
|
|
4909
5181
|
session: { provider: config.provider, model: config.model }
|
|
4910
5182
|
}),
|
|
4911
5183
|
getPendingWork: () => chimeraWork.pending(),
|
|
5184
|
+
persistEvidence: async (reportId, status, checks) => {
|
|
5185
|
+
await updateReviewReportEvidence(reportId, wpaths.projectDir, status, checks);
|
|
5186
|
+
},
|
|
4912
5187
|
trackWork: (work) => {
|
|
4913
5188
|
chimeraWork.track(work);
|
|
4914
5189
|
}
|
|
@@ -5347,4 +5622,4 @@ export {
|
|
|
5347
5622
|
execute,
|
|
5348
5623
|
resolveReviewerFallbackModels
|
|
5349
5624
|
};
|
|
5350
|
-
//# sourceMappingURL=execution-
|
|
5625
|
+
//# sourceMappingURL=execution-TTE7R5UY.js.map
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { CascadeEvidenceCheckResult, CascadeEvidenceStatus } from '@wrongstack/core/plugin';
|
|
2
|
+
import { verifyCascadeEvidence } from './chimera-cascade-evidence.js';
|
|
1
3
|
import type { ReviewerAttempt } from './chimera-reviewer-policy.js';
|
|
2
4
|
import type { ExecuteDeps } from './execute-deps.js';
|
|
3
5
|
type Director = NonNullable<ExecuteDeps['fleet']['director']>;
|
|
@@ -14,7 +16,11 @@ export type InstallChimeraCascadeHandlerOptions = {
|
|
|
14
16
|
* holds the live config. Omit to keep the single unpinned spawn.
|
|
15
17
|
*/
|
|
16
18
|
buildLadder?: (() => ReviewerAttempt[]) | undefined;
|
|
19
|
+
/** Test seam; production defaults to the real safe command verifier. */
|
|
20
|
+
verifyEvidence?: typeof verifyCascadeEvidence | undefined;
|
|
21
|
+
/** Persist every evidence verdict on the source report, even when re-review stops. */
|
|
22
|
+
persistEvidence?: ((reportId: string, status: CascadeEvidenceStatus, checks: CascadeEvidenceCheckResult[]) => Promise<void>) | undefined;
|
|
17
23
|
};
|
|
18
|
-
export declare function installChimeraCascadeHandler({ events, director, session, getPendingWork, trackWork, buildLadder, }: InstallChimeraCascadeHandlerOptions): void;
|
|
24
|
+
export declare function installChimeraCascadeHandler({ events, director, session, getPendingWork, trackWork, buildLadder, verifyEvidence, persistEvidence, }: InstallChimeraCascadeHandlerOptions): void;
|
|
19
25
|
export {};
|
|
20
26
|
//# sourceMappingURL=execution-chimera-cascade.d.ts.map
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
resolveAuditActor
|
|
3
|
-
} from "./chunk-Q5GTM25S.js";
|
|
4
1
|
import {
|
|
5
2
|
parseTokenTtlValue
|
|
6
3
|
} from "./chunk-BGFM3PD7.js";
|
|
4
|
+
import {
|
|
5
|
+
resolveAuditActor
|
|
6
|
+
} from "./chunk-Q5GTM25S.js";
|
|
7
7
|
import "./chunk-7OCVIDC7.js";
|
|
8
8
|
|
|
9
9
|
// src/subcommands/handlers/hq.ts
|
|
@@ -28,6 +28,10 @@ function resolveDataDir(deps) {
|
|
|
28
28
|
}
|
|
29
29
|
var hqCmd = async (args, deps) => {
|
|
30
30
|
const sub = args[0];
|
|
31
|
+
if (deps.flags?.["help"] === true && (!sub || sub === "serve")) {
|
|
32
|
+
printHelp(deps);
|
|
33
|
+
return 0;
|
|
34
|
+
}
|
|
31
35
|
if (!sub || sub === "serve") {
|
|
32
36
|
return startServer(deps);
|
|
33
37
|
}
|
|
@@ -160,6 +164,10 @@ HQ auth loaded from ${handle.firstRunSetup.dataDir}
|
|
|
160
164
|
}
|
|
161
165
|
async function hqTokenCmd(args, deps) {
|
|
162
166
|
const action = args[0];
|
|
167
|
+
if (deps.flags?.["help"] === true || action === "help" || action === "--help") {
|
|
168
|
+
printTokenHelp(deps);
|
|
169
|
+
return 0;
|
|
170
|
+
}
|
|
163
171
|
if (action === "create") {
|
|
164
172
|
return tokenCreate(args.slice(1), deps);
|
|
165
173
|
}
|
|
@@ -171,17 +179,21 @@ async function hqTokenCmd(args, deps) {
|
|
|
171
179
|
}
|
|
172
180
|
deps.renderer.writeError(`Unknown hq token subcommand: ${action ?? "(none)"}
|
|
173
181
|
`);
|
|
174
|
-
deps
|
|
182
|
+
printTokenHelp(deps);
|
|
175
183
|
return 1;
|
|
176
184
|
}
|
|
177
185
|
async function hqAuditCmd(args, deps) {
|
|
178
186
|
const action = args[0];
|
|
187
|
+
if (deps.flags?.["help"] === true || action === "help" || action === "--help") {
|
|
188
|
+
printAuditHelp(deps);
|
|
189
|
+
return 0;
|
|
190
|
+
}
|
|
179
191
|
if (action === "verify" || action === void 0) {
|
|
180
192
|
return hqAuditVerify(deps);
|
|
181
193
|
}
|
|
182
194
|
deps.renderer.writeError(`Unknown hq audit subcommand: ${action ?? "(none)"}
|
|
183
195
|
`);
|
|
184
|
-
deps
|
|
196
|
+
printAuditHelp(deps);
|
|
185
197
|
return 1;
|
|
186
198
|
}
|
|
187
199
|
async function hqAuditVerify(deps) {
|
|
@@ -608,8 +620,85 @@ function printHelp(deps) {
|
|
|
608
620
|
deps.renderer.write(`auth.json schema version: ${HQ_AUTH_FILE_VERSION}.
|
|
609
621
|
`);
|
|
610
622
|
}
|
|
623
|
+
function printTokenHelp(deps) {
|
|
624
|
+
deps.renderer.write(`Usage: wstack hq token <create | list | revoke>
|
|
625
|
+
`);
|
|
626
|
+
deps.renderer.write("\n");
|
|
627
|
+
deps.renderer.write(
|
|
628
|
+
` wstack hq token create [label] [--ttl <dur>] Mint a browser token (enters token mode).
|
|
629
|
+
`
|
|
630
|
+
);
|
|
631
|
+
deps.renderer.write(
|
|
632
|
+
` wstack hq token create --client [label] [--ttl <dur>] Mint a client token (/ws/client).
|
|
633
|
+
`
|
|
634
|
+
);
|
|
635
|
+
deps.renderer.write(` wstack hq token list List issued browser tokens (alias: ls).
|
|
636
|
+
`);
|
|
637
|
+
deps.renderer.write(` wstack hq token list --client List issued client tokens.
|
|
638
|
+
`);
|
|
639
|
+
deps.renderer.write(
|
|
640
|
+
` wstack hq token revoke <id> Revoke a browser token (id prefix match).
|
|
641
|
+
`
|
|
642
|
+
);
|
|
643
|
+
deps.renderer.write(` wstack hq token revoke --client <id> Revoke a client token.
|
|
644
|
+
`);
|
|
645
|
+
deps.renderer.write("\n");
|
|
646
|
+
deps.renderer.write(`The token secret is printed once at create time and cannot be recovered
|
|
647
|
+
`);
|
|
648
|
+
deps.renderer.write(`from auth.json (only its SHA-256 verifier is persisted). Tokens live in
|
|
649
|
+
`);
|
|
650
|
+
deps.renderer.write(`<dataDir>/auth.json (default ~/.wrongstack/hq/auth.json).
|
|
651
|
+
`);
|
|
652
|
+
deps.renderer.write("\n");
|
|
653
|
+
deps.renderer.write(`Flags:
|
|
654
|
+
`);
|
|
655
|
+
deps.renderer.write(
|
|
656
|
+
` --data-dir <path> Override HQ data directory (default ~/.wrongstack/hq).
|
|
657
|
+
`
|
|
658
|
+
);
|
|
659
|
+
deps.renderer.write(` --client, -c Operate on client tokens instead of browser tokens.
|
|
660
|
+
`);
|
|
661
|
+
deps.renderer.write(
|
|
662
|
+
` --capabilities <csv> Comma-separated capability grants (browser: control.enqueue; client: telemetry.publish,control.execute).
|
|
663
|
+
`
|
|
664
|
+
);
|
|
665
|
+
deps.renderer.write(
|
|
666
|
+
` --ttl <duration> Stamp an expiresAt on the token (e.g. --ttl 1h, --ttl 7d, --ttl 3600s).
|
|
667
|
+
`
|
|
668
|
+
);
|
|
669
|
+
deps.renderer.write("\n");
|
|
670
|
+
deps.renderer.write(`Run \`wstack hq --help\` for the full HQ command list.
|
|
671
|
+
`);
|
|
672
|
+
}
|
|
673
|
+
function printAuditHelp(deps) {
|
|
674
|
+
deps.renderer.write(`Usage: wstack hq audit <verify>
|
|
675
|
+
`);
|
|
676
|
+
deps.renderer.write("\n");
|
|
677
|
+
deps.renderer.write(
|
|
678
|
+
` wstack hq audit verify Re-derive the SHA-256 contentHash from the current
|
|
679
|
+
`
|
|
680
|
+
);
|
|
681
|
+
deps.renderer.write(
|
|
682
|
+
` auth.json and print it so an operator can compare
|
|
683
|
+
`
|
|
684
|
+
);
|
|
685
|
+
deps.renderer.write(
|
|
686
|
+
` it against a contentHash field in an audit-log entry.
|
|
687
|
+
`
|
|
688
|
+
);
|
|
689
|
+
deps.renderer.write("\n");
|
|
690
|
+
deps.renderer.write(`Flags:
|
|
691
|
+
`);
|
|
692
|
+
deps.renderer.write(
|
|
693
|
+
` --data-dir <path> Override HQ data directory (default ~/.wrongstack/hq).
|
|
694
|
+
`
|
|
695
|
+
);
|
|
696
|
+
deps.renderer.write("\n");
|
|
697
|
+
deps.renderer.write(`Run \`wstack hq --help\` for the full HQ command list.
|
|
698
|
+
`);
|
|
699
|
+
}
|
|
611
700
|
export {
|
|
612
701
|
hqCmd,
|
|
613
702
|
resolveAuditActor
|
|
614
703
|
};
|
|
615
|
-
//# sourceMappingURL=hq-
|
|
704
|
+
//# sourceMappingURL=hq-ZK6PGN3W.js.map
|