jev-agent-tools 0.1.4 → 0.3.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/CHANGELOG.md +106 -1
- package/CONTRIBUTING.md +43 -0
- package/README.md +58 -17
- package/SECURITY.md +43 -0
- package/dist/adapters/analysis-context.js +75 -0
- package/dist/adapters/ask-files.js +198 -0
- package/dist/adapters/ask-proof.js +200 -0
- package/dist/adapters/ask-syntax.js +385 -0
- package/dist/adapters/canonical-path.js +17 -0
- package/dist/adapters/command.js +234 -0
- package/dist/adapters/docs.js +192 -0
- package/dist/adapters/evidence-context.js +119 -0
- package/dist/adapters/exec.js +207 -0
- package/dist/adapters/files.js +418 -0
- package/dist/adapters/find.js +150 -0
- package/dist/adapters/git-base.js +32 -0
- package/dist/adapters/git-inventory.js +71 -0
- package/dist/adapters/git.js +483 -0
- package/dist/adapters/locate-file.js +197 -0
- package/dist/adapters/output-lines.js +46 -0
- package/dist/adapters/private-storage.js +106 -0
- package/dist/adapters/risk-callers.js +429 -0
- package/dist/adapters/runner-version.js +78 -0
- package/dist/adapters/shell.js +92 -0
- package/dist/adapters/syntax.js +187 -0
- package/dist/adapters/test-inventory.js +139 -0
- package/dist/adapters/usage.js +20 -0
- package/dist/adapters/utf8.js +47 -0
- package/dist/configuration.js +267 -0
- package/dist/constants.js +140 -0
- package/dist/core/ask-closure.js +282 -0
- package/dist/core/ask-proof.js +1 -0
- package/dist/core/ask-references.js +278 -0
- package/dist/core/asks.js +507 -0
- package/dist/core/batches.js +65 -0
- package/dist/core/command-output.js +224 -0
- package/dist/core/diff.js +178 -0
- package/dist/core/docs.js +302 -0
- package/dist/core/find.js +108 -0
- package/dist/core/git.js +1 -0
- package/dist/core/imports.js +550 -0
- package/dist/core/integrity.js +45 -0
- package/dist/core/lexical.js +132 -0
- package/dist/core/locate.js +169 -0
- package/dist/core/output.js +137 -0
- package/dist/core/pointer.js +29 -0
- package/dist/core/result-report.js +302 -0
- package/dist/core/risk-callers.js +851 -0
- package/dist/core/runner-version.js +45 -0
- package/dist/core/secret-path.js +34 -0
- package/dist/core/sections.js +230 -0
- package/dist/core/state.js +51 -0
- package/dist/core/syntax.js +1 -0
- package/dist/core/test-commands.js +334 -0
- package/dist/core/test-coverage.js +74 -0
- package/dist/core/test-discovery.js +1382 -0
- package/dist/core/test-evidence.js +527 -0
- package/dist/core/test-state.js +81 -0
- package/dist/core/truncate.js +12 -0
- package/dist/core/units.js +349 -0
- package/dist/describe.js +23 -0
- package/dist/guide.js +33 -0
- package/dist/host.js +24 -0
- package/dist/jev/client.js +456 -0
- package/dist/jev/pool.js +54 -0
- package/dist/jev/types.js +1 -0
- package/dist/mcp/main.js +124 -0
- package/dist/mcp/protocol.js +210 -0
- package/dist/mcp/tools.js +129 -0
- package/dist/presets/docs.js +62 -0
- package/dist/presets/risk.js +179 -0
- package/dist/presets/spec.js +81 -0
- package/dist/presets/witnesses.js +249 -0
- package/dist/render.js +114 -0
- package/dist/report-schema.js +1356 -0
- package/dist/result-types.js +1 -0
- package/dist/result.js +3 -0
- package/dist/runtime.js +1 -0
- package/dist/session.js +147 -0
- package/dist/texts/ask-files.js +3 -0
- package/dist/texts/ask.js +4 -0
- package/dist/texts/check-diff.js +20 -0
- package/dist/texts/configuration.js +1 -0
- package/dist/texts/find.js +19 -0
- package/dist/texts/guide.js +3 -0
- package/dist/texts/instructions.js +72 -0
- package/dist/texts/locate.js +15 -0
- package/dist/texts/select-tests.js +4 -0
- package/dist/tools/ask-files.js +450 -0
- package/dist/tools/ask-schema.js +70 -0
- package/dist/tools/ask.js +1147 -0
- package/dist/tools/check-diff.js +594 -0
- package/dist/tools/docs-check.js +408 -0
- package/dist/tools/find.js +682 -0
- package/dist/tools/locate.js +602 -0
- package/dist/tools/review-report.js +230 -0
- package/dist/tools/select-tests.js +821 -0
- package/dist/tools/spec-check.js +263 -0
- package/docs/adr/0001-strict-typescript-pure-core-offline-tests.md +31 -0
- package/docs/adr/0002-one-http-protocol-across-hosts.md +17 -0
- package/docs/adr/0003-explicit-scope-conservative-automation.md +19 -0
- package/docs/adr/0004-compiled-typed-intents.md +19 -0
- package/docs/adr/0005-evidence-construction-before-judgment.md +19 -0
- package/docs/adr/0006-visible-uncertainty-constrained-controls.md +21 -0
- package/docs/adr/0007-bounded-evidence-visible-limits.md +21 -0
- package/docs/adr/0008-static-test-discovery-conservative-plans.md +19 -0
- package/docs/adr/0009-session-cache-requested-model-identity.md +17 -0
- package/docs/adr/0010-mcp-server-thin-host.md +23 -0
- package/docs/agent-instructions.md +120 -0
- package/docs/design.md +16 -4
- package/docs/mcp.md +233 -0
- package/docs/tools/jev_ask.md +8 -5
- package/docs/tools/jev_ask_files.md +2 -1
- package/docs/tools/jev_check_diff.md +4 -1
- package/docs/tools/jev_find_files.md +2 -1
- package/docs/tools/jev_locate_in_file.md +5 -0
- package/docs/tools/jev_select_tests.md +4 -1
- package/package.json +19 -4
- package/rules/jev-ask.md +22 -1
- package/server.json +57 -0
- package/src/adapters/ask-files.ts +11 -3
- package/src/adapters/ask-proof.ts +69 -11
- package/src/adapters/canonical-path.ts +18 -0
- package/src/adapters/command.ts +102 -36
- package/src/adapters/docs.ts +33 -14
- package/src/adapters/evidence-context.ts +169 -0
- package/src/adapters/exec.ts +226 -0
- package/src/adapters/files.ts +146 -16
- package/src/adapters/find.ts +37 -7
- package/src/adapters/git-base.ts +7 -1
- package/src/adapters/git.ts +61 -8
- package/src/adapters/locate-file.ts +51 -9
- package/src/adapters/private-storage.ts +155 -0
- package/src/adapters/risk-callers.ts +7 -2
- package/src/adapters/shell.ts +113 -0
- package/src/adapters/test-inventory.ts +12 -4
- package/src/configuration.ts +55 -14
- package/src/constants.ts +37 -5
- package/src/core/ask-references.ts +262 -146
- package/src/core/asks.ts +79 -7
- package/src/core/command-output.ts +17 -1
- package/src/core/import-boundaries.ts +8 -3
- package/src/core/locate.ts +8 -5
- package/src/core/output.ts +34 -0
- package/src/core/result-report.ts +410 -0
- package/src/core/secret-path.ts +37 -0
- package/src/core/state.ts +8 -1
- package/src/core/units.ts +3 -2
- package/src/host.ts +11 -0
- package/src/index.ts +3 -0
- package/src/jev/client.ts +66 -16
- package/src/jev/types.ts +24 -3
- package/src/mcp/main.ts +135 -0
- package/src/mcp/protocol.ts +332 -0
- package/src/mcp/tools.ts +179 -0
- package/src/render.ts +109 -0
- package/src/report-schema.ts +1380 -0
- package/src/result-types.ts +234 -0
- package/src/result.ts +4 -1
- package/src/runtime.ts +6 -0
- package/src/session.ts +59 -0
- package/src/setup.ts +13 -5
- package/src/texts/ask-files.ts +4 -1
- package/src/texts/ask.ts +8 -1
- package/src/texts/check-diff.ts +7 -4
- package/src/texts/find.ts +8 -2
- package/src/texts/guide.ts +8 -16
- package/src/texts/instructions.ts +98 -0
- package/src/texts/locate.ts +8 -2
- package/src/texts/run-end.ts +2 -2
- package/src/texts/select-tests.ts +4 -1
- package/src/tools/ask-files.ts +311 -18
- package/src/tools/ask.ts +722 -95
- package/src/tools/check-diff.ts +337 -31
- package/src/tools/docs-check.ts +241 -38
- package/src/tools/find.ts +389 -29
- package/src/tools/locate.ts +387 -25
- package/src/tools/review-report.ts +308 -0
- package/src/tools/select-tests.ts +484 -23
- package/src/tools/spec-check.ts +194 -19
package/src/tools/check-diff.ts
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { type Static, Type } from "@sinclair/typebox";
|
|
3
3
|
import { createAnalysisContext } from "../adapters/analysis-context.ts";
|
|
4
|
+
import {
|
|
5
|
+
type EvidenceContext,
|
|
6
|
+
resolveEvidenceContext,
|
|
7
|
+
withEvidenceContext,
|
|
8
|
+
} from "../adapters/evidence-context.ts";
|
|
4
9
|
import { collectUnits } from "../adapters/git.ts";
|
|
5
10
|
import { resolveBase } from "../adapters/git-base.ts";
|
|
6
11
|
import { shareGitInventory } from "../adapters/git-inventory.ts";
|
|
@@ -12,6 +17,7 @@ import {
|
|
|
12
17
|
CANNOT_TELL_MIN,
|
|
13
18
|
FLAG_MIN,
|
|
14
19
|
STATE_MAX_CHARS,
|
|
20
|
+
TIMEOUT_MS,
|
|
15
21
|
} from "../constants.ts";
|
|
16
22
|
import { prepareBatches } from "../core/batches.ts";
|
|
17
23
|
import { isTestFile } from "../core/diff.ts";
|
|
@@ -22,6 +28,11 @@ import {
|
|
|
22
28
|
type Envelope,
|
|
23
29
|
type Limitation,
|
|
24
30
|
} from "../core/output.ts";
|
|
31
|
+
import {
|
|
32
|
+
type Cause,
|
|
33
|
+
known,
|
|
34
|
+
type ResultReportV1,
|
|
35
|
+
} from "../core/result-report.ts";
|
|
25
36
|
import type { CallerProof, EvidenceSpan } from "../core/risk-callers.ts";
|
|
26
37
|
import type { EvidenceUnit } from "../core/units.ts";
|
|
27
38
|
import type { GuideContext } from "../guide.ts";
|
|
@@ -37,15 +48,18 @@ import {
|
|
|
37
48
|
prepareRiskMatrix,
|
|
38
49
|
prepareRiskSeverity,
|
|
39
50
|
} from "../presets/risk.ts";
|
|
40
|
-
import {
|
|
51
|
+
import { renderResultReport } from "../render.ts";
|
|
41
52
|
import type { ToolDependencies } from "../runtime.ts";
|
|
42
53
|
import { CHECK_DIFF_DESCRIPTION } from "../texts/check-diff.ts";
|
|
43
54
|
import { NOT_CONFIGURED } from "../texts/configuration.ts";
|
|
55
|
+
import { CHECK_DIFF_GUIDELINE } from "../texts/instructions.ts";
|
|
44
56
|
import { runDocsCheck } from "./docs-check.ts";
|
|
57
|
+
import { controlsFor, ReviewReport, reportMetrics } from "./review-report.ts";
|
|
45
58
|
import { runSpecCheck } from "./spec-check.ts";
|
|
46
59
|
|
|
47
60
|
export const checkDiffParameters = Type.Object(
|
|
48
61
|
{
|
|
62
|
+
root: Type.Optional(Type.String()),
|
|
49
63
|
check: Type.Union([
|
|
50
64
|
Type.Literal("risk"),
|
|
51
65
|
Type.Literal("docs"),
|
|
@@ -71,6 +85,8 @@ export const checkDiffParameters = Type.Object(
|
|
|
71
85
|
export type CheckDiffArgs = Static<typeof checkDiffParameters>;
|
|
72
86
|
export interface RiskDetails {
|
|
73
87
|
ok: boolean;
|
|
88
|
+
evidenceContext: EvidenceContext;
|
|
89
|
+
result: ResultReportV1;
|
|
74
90
|
envelope: Envelope;
|
|
75
91
|
judgments: Judgment[];
|
|
76
92
|
callerProofs: CallerProof[];
|
|
@@ -95,9 +111,7 @@ export function createCheckDiffTool(dependencies: ToolDependencies) {
|
|
|
95
111
|
: {
|
|
96
112
|
promptSnippet:
|
|
97
113
|
"Check the uncommitted diff for risky changes, stale docs or spec drift",
|
|
98
|
-
promptGuidelines: [
|
|
99
|
-
"jev_check_diff: run it once before you report done, not on a half-written diff",
|
|
100
|
-
],
|
|
114
|
+
promptGuidelines: [CHECK_DIFF_GUIDELINE],
|
|
101
115
|
}),
|
|
102
116
|
async execute(
|
|
103
117
|
_id: string,
|
|
@@ -107,23 +121,71 @@ export function createCheckDiffTool(dependencies: ToolDependencies) {
|
|
|
107
121
|
ctx: { cwd: string } & GuideContext,
|
|
108
122
|
) {
|
|
109
123
|
const client = dependencies.client;
|
|
124
|
+
const evidence = await resolveEvidenceContext(ctx.cwd, args.root, {
|
|
125
|
+
exec: execute,
|
|
126
|
+
signal,
|
|
127
|
+
origin: dependencies.evidenceOrigin,
|
|
128
|
+
});
|
|
129
|
+
const evidenceContext = evidence.context;
|
|
130
|
+
const report = new ReviewReport();
|
|
131
|
+
const cwd = evidence.ok ? evidence.cwd : evidenceContext.authority.path;
|
|
110
132
|
const exec = shareGitInventory(execute);
|
|
133
|
+
if (!evidence.ok) {
|
|
134
|
+
const envelope = buildEnvelope({
|
|
135
|
+
refusal: evidence.error,
|
|
136
|
+
yield: {
|
|
137
|
+
calls: 0,
|
|
138
|
+
questions: 0,
|
|
139
|
+
cacheHits: 0,
|
|
140
|
+
cacheRequests: 0,
|
|
141
|
+
elapsedMs: 0,
|
|
142
|
+
},
|
|
143
|
+
});
|
|
144
|
+
runtime.session.record(envelope);
|
|
145
|
+
runtime.guide.deliver(ctx);
|
|
146
|
+
report.refusal = true;
|
|
147
|
+
report.diagnose(evidence.cause, evidence.error, args.root);
|
|
148
|
+
const result = report.build(
|
|
149
|
+
"jev_check_diff",
|
|
150
|
+
evidenceContext,
|
|
151
|
+
reportMetrics(envelope),
|
|
152
|
+
);
|
|
153
|
+
return {
|
|
154
|
+
content: [
|
|
155
|
+
{
|
|
156
|
+
type: "text" as const,
|
|
157
|
+
text: renderResultReport(result, { details: envelope }),
|
|
158
|
+
},
|
|
159
|
+
],
|
|
160
|
+
details: {
|
|
161
|
+
ok: false,
|
|
162
|
+
envelope,
|
|
163
|
+
judgments: [],
|
|
164
|
+
callerProofs: [],
|
|
165
|
+
evidenceContext,
|
|
166
|
+
result,
|
|
167
|
+
},
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
evidenceContext.requestedBase = args.base ?? "HEAD";
|
|
111
171
|
if (args.check === "docs" || args.check === "spec") {
|
|
112
172
|
const deps = { client, host, runtime, exec };
|
|
113
173
|
const result =
|
|
114
174
|
args.check === "docs"
|
|
115
175
|
? await runDocsCheck(deps, {
|
|
116
|
-
cwd:
|
|
176
|
+
cwd: cwd,
|
|
117
177
|
base: args.base,
|
|
118
178
|
signal,
|
|
119
179
|
maxCalls: args.max_calls,
|
|
180
|
+
evidenceContext,
|
|
120
181
|
})
|
|
121
182
|
: await runSpecCheck(deps, {
|
|
122
|
-
cwd:
|
|
183
|
+
cwd: cwd,
|
|
123
184
|
base: args.base,
|
|
124
185
|
specPath: args.spec_path,
|
|
125
186
|
signal,
|
|
126
187
|
maxCalls: args.max_calls,
|
|
188
|
+
evidenceContext,
|
|
127
189
|
});
|
|
128
190
|
runtime.session.record(result.envelope);
|
|
129
191
|
runtime.guide.deliver(ctx);
|
|
@@ -136,9 +198,14 @@ export function createCheckDiffTool(dependencies: ToolDependencies) {
|
|
|
136
198
|
);
|
|
137
199
|
return {
|
|
138
200
|
content: [
|
|
139
|
-
{
|
|
201
|
+
{
|
|
202
|
+
type: "text" as const,
|
|
203
|
+
text: renderResultReport(result.result, {
|
|
204
|
+
details: result.envelope,
|
|
205
|
+
}),
|
|
206
|
+
},
|
|
140
207
|
],
|
|
141
|
-
details: { ...result, callerProofs: [] },
|
|
208
|
+
details: { ...result, callerProofs: [], evidenceContext },
|
|
142
209
|
...hostUsage(host.isOmp, usage),
|
|
143
210
|
};
|
|
144
211
|
}
|
|
@@ -153,7 +220,7 @@ export function createCheckDiffTool(dependencies: ToolDependencies) {
|
|
|
153
220
|
let sent = 0;
|
|
154
221
|
let matrixHealthy = true;
|
|
155
222
|
let emptyBase: string | undefined;
|
|
156
|
-
const finish = (refusal?: string) => {
|
|
223
|
+
const finish = (refusal?: string, cause?: Cause) => {
|
|
157
224
|
answers.sort((a, b) =>
|
|
158
225
|
(unitOrder.get(a) ?? "").localeCompare(unitOrder.get(b) ?? ""),
|
|
159
226
|
);
|
|
@@ -170,7 +237,14 @@ export function createCheckDiffTool(dependencies: ToolDependencies) {
|
|
|
170
237
|
{ inputTokens: 0, costUsd: 0 },
|
|
171
238
|
);
|
|
172
239
|
const noFindings =
|
|
173
|
-
!refusal &&
|
|
240
|
+
!refusal &&
|
|
241
|
+
matrixHealthy &&
|
|
242
|
+
!answers.length &&
|
|
243
|
+
!unchecked.length &&
|
|
244
|
+
report.items.size > 0 &&
|
|
245
|
+
[...report.items.values()].every(
|
|
246
|
+
(item) => item.treatment === "judged",
|
|
247
|
+
);
|
|
174
248
|
const envelope = buildEnvelope({
|
|
175
249
|
answers,
|
|
176
250
|
limitations:
|
|
@@ -200,7 +274,11 @@ export function createCheckDiffTool(dependencies: ToolDependencies) {
|
|
|
200
274
|
yield: {
|
|
201
275
|
calls: judgments.reduce((n, j) => n + (j.calls ?? 0), 0),
|
|
202
276
|
questions: judgments.reduce((n, j) => n + (j.questions ?? 0), 0),
|
|
203
|
-
costUsd:
|
|
277
|
+
costUsd:
|
|
278
|
+
judgments.every((j) => j.usage !== undefined) &&
|
|
279
|
+
judgments.length > 0
|
|
280
|
+
? usage.costUsd
|
|
281
|
+
: undefined,
|
|
204
282
|
cacheHits: judgments.reduce((n, j) => n + (j.cacheHits ?? 0), 0),
|
|
205
283
|
cacheRequests: judgments.reduce(
|
|
206
284
|
(n, j) => n + (j.cacheRequests ?? 0),
|
|
@@ -209,11 +287,38 @@ export function createCheckDiffTool(dependencies: ToolDependencies) {
|
|
|
209
287
|
elapsedMs: performance.now() - started,
|
|
210
288
|
},
|
|
211
289
|
});
|
|
290
|
+
if (emptyBase !== undefined)
|
|
291
|
+
report.diagnose(
|
|
292
|
+
"no_changed_units",
|
|
293
|
+
`No changed units against ${emptyBase}; no risk judgment requested.`,
|
|
294
|
+
emptyBase,
|
|
295
|
+
[],
|
|
296
|
+
false,
|
|
297
|
+
);
|
|
298
|
+
if (
|
|
299
|
+
refusal &&
|
|
300
|
+
!report.diagnostics.some((d) => d.effect === "blocking")
|
|
301
|
+
) {
|
|
302
|
+
report.refusal = refusal !== NOT_CONFIGURED;
|
|
303
|
+
report.diagnose(cause ?? "internal_error", refusal);
|
|
304
|
+
}
|
|
305
|
+
const result = report.build(
|
|
306
|
+
"jev_check_diff",
|
|
307
|
+
evidenceContext,
|
|
308
|
+
reportMetrics(envelope),
|
|
309
|
+
);
|
|
212
310
|
runtime.session.record(envelope);
|
|
213
311
|
runtime.guide.deliver(ctx);
|
|
214
312
|
return {
|
|
215
|
-
content: [
|
|
313
|
+
content: [
|
|
314
|
+
{
|
|
315
|
+
type: "text" as const,
|
|
316
|
+
text: renderResultReport(result, { details: envelope }),
|
|
317
|
+
},
|
|
318
|
+
],
|
|
216
319
|
details: {
|
|
320
|
+
evidenceContext,
|
|
321
|
+
result,
|
|
217
322
|
ok: !refusal,
|
|
218
323
|
envelope,
|
|
219
324
|
judgments,
|
|
@@ -222,21 +327,57 @@ export function createCheckDiffTool(dependencies: ToolDependencies) {
|
|
|
222
327
|
...hostUsage(host.isOmp, usage),
|
|
223
328
|
};
|
|
224
329
|
};
|
|
225
|
-
if (!client) return finish(NOT_CONFIGURED);
|
|
226
|
-
const comparison = await resolveBase(exec,
|
|
227
|
-
if (!comparison.ok)
|
|
330
|
+
if (!client) return finish(NOT_CONFIGURED, "not_configured");
|
|
331
|
+
const comparison = await resolveBase(exec, cwd, args.base, signal);
|
|
332
|
+
if (!comparison.ok)
|
|
333
|
+
return finish(comparison.error, comparison.cause ?? "invalid_base");
|
|
334
|
+
evidenceContext.resolvedBase = comparison.base;
|
|
228
335
|
const base = comparison.base;
|
|
229
336
|
const analysis = await createAnalysisContext();
|
|
230
337
|
const collected = await collectUnits(
|
|
231
338
|
exec,
|
|
232
339
|
{
|
|
233
|
-
cwd:
|
|
340
|
+
cwd: cwd,
|
|
234
341
|
base,
|
|
235
342
|
signal,
|
|
236
343
|
},
|
|
237
344
|
analysis.parser,
|
|
238
345
|
);
|
|
239
|
-
if (!collected.ok)
|
|
346
|
+
if (!collected.ok)
|
|
347
|
+
return finish(collected.error, collected.cause ?? "git_failure");
|
|
348
|
+
const repositoryRoot = await exec(
|
|
349
|
+
"git",
|
|
350
|
+
["rev-parse", "--show-toplevel"],
|
|
351
|
+
{ cwd, timeout: TIMEOUT_MS, signal },
|
|
352
|
+
);
|
|
353
|
+
if (
|
|
354
|
+
!repositoryRoot.code &&
|
|
355
|
+
!repositoryRoot.killed &&
|
|
356
|
+
evidenceContext.effectiveRoot
|
|
357
|
+
)
|
|
358
|
+
evidenceContext.effectiveRoot.path = repositoryRoot.stdout.trim();
|
|
359
|
+
report.inventories.push({
|
|
360
|
+
id: "changed-units",
|
|
361
|
+
kind: "units",
|
|
362
|
+
rules: [
|
|
363
|
+
"Changed source units against resolved base; tests are evidence, not risk units",
|
|
364
|
+
],
|
|
365
|
+
restrictions: args.only ?? [],
|
|
366
|
+
discovered: known(collected.units.length),
|
|
367
|
+
considered: known(collected.units.length),
|
|
368
|
+
scopeRestricted: Boolean(args.only?.length),
|
|
369
|
+
criteria: [],
|
|
370
|
+
});
|
|
371
|
+
for (const unit of collected.units)
|
|
372
|
+
report.expect(`unit:${unit.id}`, unitLabel(unit), "unit");
|
|
373
|
+
for (const limit of collected.limits)
|
|
374
|
+
report.diagnose(
|
|
375
|
+
limit.kind === "secret_pattern"
|
|
376
|
+
? "secret_pattern"
|
|
377
|
+
: "collection_omitted",
|
|
378
|
+
`${limit.file}: ${limit.kind}`,
|
|
379
|
+
limit.file,
|
|
380
|
+
);
|
|
240
381
|
for (const limit of collected.limits)
|
|
241
382
|
limitations.push({
|
|
242
383
|
fact: `${limit.file}: ${limit.kind}`,
|
|
@@ -245,6 +386,12 @@ export function createCheckDiffTool(dependencies: ToolDependencies) {
|
|
|
245
386
|
const readableUnits = collected.units.filter((unit) => {
|
|
246
387
|
if (unit.before !== null || unit.after !== null) return true;
|
|
247
388
|
unchecked.push(`${unitLabel(unit)} source unavailable`);
|
|
389
|
+
report.diagnose(
|
|
390
|
+
"binary_or_non_utf8",
|
|
391
|
+
"Changed source unavailable",
|
|
392
|
+
unit.file,
|
|
393
|
+
[`unit:${unit.id}`],
|
|
394
|
+
);
|
|
248
395
|
return false;
|
|
249
396
|
});
|
|
250
397
|
if (!collected.units.length) {
|
|
@@ -256,21 +403,43 @@ export function createCheckDiffTool(dependencies: ToolDependencies) {
|
|
|
256
403
|
...args,
|
|
257
404
|
testFilesPresent: collected.files.some((file) => isTestFile(file.path)),
|
|
258
405
|
});
|
|
259
|
-
if (!matrix.ok) return finish(matrix.error);
|
|
406
|
+
if (!matrix.ok) return finish(matrix.error, "invalid_arguments");
|
|
260
407
|
const prepared = matrix;
|
|
261
|
-
|
|
408
|
+
prepared.state = withEvidenceContext(prepared.state, evidenceContext);
|
|
409
|
+
for (const unit of readableUnits) report.items.delete(`unit:${unit.id}`);
|
|
410
|
+
for (const cell of prepared.cells)
|
|
411
|
+
report.expect(
|
|
412
|
+
`risk:${cell.id}`,
|
|
413
|
+
`${cell.unitId} ${cell.dimension}`,
|
|
414
|
+
"unit",
|
|
415
|
+
`risk:${cell.unitId}`,
|
|
416
|
+
);
|
|
417
|
+
for (const warning of prepared.warnings) {
|
|
418
|
+
limitations.push(warning);
|
|
419
|
+
report.diagnose(
|
|
420
|
+
"unsupported_syntax",
|
|
421
|
+
warning.fact,
|
|
422
|
+
undefined,
|
|
423
|
+
[],
|
|
424
|
+
false,
|
|
425
|
+
);
|
|
426
|
+
}
|
|
262
427
|
if (JSON.stringify(prepared.state).length > STATE_MAX_CHARS)
|
|
263
428
|
return finish(
|
|
264
429
|
`risk state exceeds STATE_MAX_CHARS=${STATE_MAX_CHARS}; rerun with base= a nearer ref`,
|
|
430
|
+
"evidence_too_large",
|
|
265
431
|
);
|
|
266
432
|
const batches = prepareBatches(prepared.state, prepared.questions, {
|
|
267
433
|
groups: prepared.groups,
|
|
268
434
|
witnesses: prepared.witnessQuestionIds,
|
|
269
435
|
});
|
|
270
|
-
if (!batches.ok) return finish(batches.error);
|
|
436
|
+
if (!batches.ok) return finish(batches.error, "group_too_large");
|
|
271
437
|
let reservedMatrixCalls = batches.batches.length;
|
|
272
438
|
const optionsFor = (matrixRequest = false): JudgmentOptions => ({
|
|
273
439
|
signal,
|
|
440
|
+
...runtime.session.requestGate(),
|
|
441
|
+
admissionCause: () =>
|
|
442
|
+
budget?.kind === "session" ? "session_budget" : "call_budget",
|
|
274
443
|
beforeRequest(questionCount) {
|
|
275
444
|
if (matrixRequest && reservedMatrixCalls > 0) reservedMatrixCalls--;
|
|
276
445
|
const limit =
|
|
@@ -300,6 +469,12 @@ export function createCheckDiffTool(dependencies: ToolDependencies) {
|
|
|
300
469
|
extra?: JudgmentOptions,
|
|
301
470
|
matrixRequest = false,
|
|
302
471
|
) => {
|
|
472
|
+
state = withEvidenceContext(state, evidenceContext);
|
|
473
|
+
if (JSON.stringify(state).length > STATE_MAX_CHARS)
|
|
474
|
+
return {
|
|
475
|
+
ok: false as const,
|
|
476
|
+
error: `required evidence exceeds STATE_MAX_CHARS=${STATE_MAX_CHARS}`,
|
|
477
|
+
};
|
|
303
478
|
const result = await client.judge(state, questions, {
|
|
304
479
|
...optionsFor(matrixRequest),
|
|
305
480
|
...extra,
|
|
@@ -312,7 +487,7 @@ export function createCheckDiffTool(dependencies: ToolDependencies) {
|
|
|
312
487
|
)
|
|
313
488
|
? await collectRiskCallers(
|
|
314
489
|
exec,
|
|
315
|
-
{ cwd:
|
|
490
|
+
{ cwd: cwd, base, signal },
|
|
316
491
|
collected.units,
|
|
317
492
|
collected.files,
|
|
318
493
|
analysis.parser,
|
|
@@ -320,6 +495,23 @@ export function createCheckDiffTool(dependencies: ToolDependencies) {
|
|
|
320
495
|
: { proofs: [], limits: [] };
|
|
321
496
|
const uncheckedCallers = new Set<string>();
|
|
322
497
|
for (const limit of local.limits) {
|
|
498
|
+
if (limit.kind !== "dynamic_access_uncovered") {
|
|
499
|
+
const id = `caller:${limit.unitId}`;
|
|
500
|
+
report.expect(id, `${limit.unitId} local caller`, "unit");
|
|
501
|
+
report.diagnose(
|
|
502
|
+
"collection_omitted",
|
|
503
|
+
limit.reason,
|
|
504
|
+
limit.paths.join(" + "),
|
|
505
|
+
[id],
|
|
506
|
+
);
|
|
507
|
+
} else
|
|
508
|
+
report.diagnose(
|
|
509
|
+
"dynamic_dependency",
|
|
510
|
+
limit.reason,
|
|
511
|
+
limit.paths.join(" + "),
|
|
512
|
+
[],
|
|
513
|
+
false,
|
|
514
|
+
);
|
|
323
515
|
limitations.push({
|
|
324
516
|
fact: `${limit.unitId}: ${limit.reason} — ${limit.paths.join(" + ")}`,
|
|
325
517
|
next: "read the named caller/provider pieces or supply an observation via jev_ask",
|
|
@@ -365,6 +557,53 @@ export function createCheckDiffTool(dependencies: ToolDependencies) {
|
|
|
365
557
|
unhealthyQuestionIds: new Map<string, string>(),
|
|
366
558
|
};
|
|
367
559
|
matrixHealthy = health.healthy;
|
|
560
|
+
for (const failure of health.controls)
|
|
561
|
+
report.diagnose(
|
|
562
|
+
health.healthy ? "conservative_widening" : "control_failure",
|
|
563
|
+
failure.fact,
|
|
564
|
+
undefined,
|
|
565
|
+
prepared.cells
|
|
566
|
+
.filter((cell) => health.unhealthyQuestionIds.has(cell.id))
|
|
567
|
+
.map((cell) => `risk:${cell.id}`),
|
|
568
|
+
false,
|
|
569
|
+
);
|
|
570
|
+
report.countControls(result, prepared.witnessQuestionIds);
|
|
571
|
+
for (const cell of prepared.cells) {
|
|
572
|
+
const rawAnswer = result.ok ? result.answers[cell.id] : undefined;
|
|
573
|
+
const answer =
|
|
574
|
+
rawAnswer?.type === "unjudged" && !rawAnswer.cause && budget
|
|
575
|
+
? {
|
|
576
|
+
...rawAnswer,
|
|
577
|
+
cause:
|
|
578
|
+
budget.kind === "session"
|
|
579
|
+
? ("session_budget" as const)
|
|
580
|
+
: ("call_budget" as const),
|
|
581
|
+
}
|
|
582
|
+
: rawAnswer;
|
|
583
|
+
const invalid = health.unhealthyQuestionIds.get(cell.id);
|
|
584
|
+
if (!result.ok)
|
|
585
|
+
report.failure(
|
|
586
|
+
result,
|
|
587
|
+
[`risk:${cell.id}`],
|
|
588
|
+
budget
|
|
589
|
+
? budget.kind === "session"
|
|
590
|
+
? "session_budget"
|
|
591
|
+
: "call_budget"
|
|
592
|
+
: undefined,
|
|
593
|
+
);
|
|
594
|
+
else
|
|
595
|
+
report.answer(
|
|
596
|
+
`risk:${cell.id}`,
|
|
597
|
+
answer,
|
|
598
|
+
{
|
|
599
|
+
band: invalid ? "unsure" : "verdict",
|
|
600
|
+
reason: invalid,
|
|
601
|
+
uncalibrated: cell.uncalibrated,
|
|
602
|
+
},
|
|
603
|
+
controlsFor(cell.id, result, prepared.witnessQuestionIds),
|
|
604
|
+
false,
|
|
605
|
+
);
|
|
606
|
+
}
|
|
368
607
|
const decoys = new Set(
|
|
369
608
|
prepared.witnesses
|
|
370
609
|
.filter((witness) => witness.expected === "no")
|
|
@@ -425,16 +664,36 @@ export function createCheckDiffTool(dependencies: ToolDependencies) {
|
|
|
425
664
|
for (const { proof, result: localResult } of locals) {
|
|
426
665
|
callerProofs.push(proof);
|
|
427
666
|
const unit = collected.units.find((u) => u.id === proof.unitId);
|
|
428
|
-
|
|
429
|
-
|
|
667
|
+
const reportId = `caller:${proof.unitId}`;
|
|
668
|
+
report.expect(
|
|
669
|
+
reportId,
|
|
670
|
+
`${unit ? unitLabel(unit) : proof.unitId} local caller`,
|
|
671
|
+
"unit",
|
|
672
|
+
);
|
|
673
|
+
if (!localResult.ok) {
|
|
674
|
+
report.failure(
|
|
675
|
+
localResult,
|
|
676
|
+
[reportId],
|
|
677
|
+
budget
|
|
678
|
+
? budget.kind === "session"
|
|
679
|
+
? "session_budget"
|
|
680
|
+
: "call_budget"
|
|
681
|
+
: undefined,
|
|
682
|
+
);
|
|
683
|
+
continue;
|
|
684
|
+
}
|
|
685
|
+
const answer = localResult.answers.caller;
|
|
430
686
|
if (answer?.type !== "choice") {
|
|
431
|
-
|
|
687
|
+
report.answer(reportId, answer);
|
|
688
|
+
unchecked.push(
|
|
689
|
+
`${unit ? unitLabel(unit) : proof.unitId} local caller`,
|
|
690
|
+
);
|
|
432
691
|
continue;
|
|
433
692
|
}
|
|
434
693
|
const p = answer.probabilities.new_failure ?? 0;
|
|
435
694
|
const missing =
|
|
436
695
|
(answer.probabilities.cannot_tell ?? 0) >= CANNOT_TELL_MIN;
|
|
437
|
-
|
|
696
|
+
const negative = !missing && p < BAND_BOOL_GRAY_A;
|
|
438
697
|
const merged: EvidenceSpan[] = [];
|
|
439
698
|
for (const span of [...proof.paths].sort(
|
|
440
699
|
(a, b) =>
|
|
@@ -455,15 +714,45 @@ export function createCheckDiffTool(dependencies: ToolDependencies) {
|
|
|
455
714
|
.map((span) => `${span.path}:${span.start}-${span.end}`)
|
|
456
715
|
.join(" + ");
|
|
457
716
|
const line: AnswerInput = {
|
|
458
|
-
label: `${unitLabel(unit)} reliability — static code only; ${passages}${omitted ? ` (+${omitted} passages)` : ""}`,
|
|
717
|
+
label: `${unit ? unitLabel(unit) : proof.unitId} reliability — static code only; ${passages}${omitted ? ` (+${omitted} passages)` : ""}`,
|
|
459
718
|
value: {
|
|
460
|
-
head: missing
|
|
461
|
-
|
|
719
|
+
head: missing
|
|
720
|
+
? "cannot_tell"
|
|
721
|
+
: negative
|
|
722
|
+
? "no_new_failure"
|
|
723
|
+
: "new_failure",
|
|
724
|
+
p: missing
|
|
725
|
+
? (answer.probabilities.cannot_tell ?? 0)
|
|
726
|
+
: negative
|
|
727
|
+
? (answer.probabilities.no_new_failure ?? 0)
|
|
728
|
+
: p,
|
|
462
729
|
},
|
|
463
|
-
band: missing
|
|
464
|
-
|
|
465
|
-
|
|
730
|
+
band: missing
|
|
731
|
+
? "abstain"
|
|
732
|
+
: negative || p >= FLAG_MIN
|
|
733
|
+
? "verdict"
|
|
734
|
+
: "unsure",
|
|
735
|
+
reason: missing
|
|
736
|
+
? "actual provider or binding missing; supply its declaration or an observation via jev_ask"
|
|
737
|
+
: negative
|
|
738
|
+
? "Local caller check does not flag a new failure; static evidence does not prove every caller safe."
|
|
739
|
+
: p < FLAG_MIN
|
|
740
|
+
? "Local caller new-failure probability is in the gray band; inspect the stated proof passages natively."
|
|
741
|
+
: "Local caller new-failure probability meets the finding threshold; static code evidence only.",
|
|
466
742
|
};
|
|
743
|
+
const callerItem = report.items.get(reportId);
|
|
744
|
+
if (callerItem)
|
|
745
|
+
report.items.set(reportId, { ...callerItem, label: line.label });
|
|
746
|
+
report.answer(reportId, answer, line);
|
|
747
|
+
if (missing && line.reason)
|
|
748
|
+
report.diagnose(
|
|
749
|
+
"missing_required",
|
|
750
|
+
line.reason,
|
|
751
|
+
line.label,
|
|
752
|
+
[reportId],
|
|
753
|
+
false,
|
|
754
|
+
);
|
|
755
|
+
if (!unit || negative) continue;
|
|
467
756
|
answers.push(line);
|
|
468
757
|
unitOrder.set(line, unit.id);
|
|
469
758
|
if (!missing && p >= FLAG_MIN) {
|
|
@@ -491,6 +780,23 @@ export function createCheckDiffTool(dependencies: ToolDependencies) {
|
|
|
491
780
|
item.evidence?.callerEvidence,
|
|
492
781
|
);
|
|
493
782
|
const value = await judge(request.state, request.questions);
|
|
783
|
+
const reportId = `severity:${item.unit.id}:${item.dimension}`;
|
|
784
|
+
report.expect(
|
|
785
|
+
reportId,
|
|
786
|
+
`${item.unit.id} ${item.dimension} severity`,
|
|
787
|
+
"unit",
|
|
788
|
+
);
|
|
789
|
+
if (value.ok) report.answer(reportId, value.answers.severity);
|
|
790
|
+
else
|
|
791
|
+
report.failure(
|
|
792
|
+
value,
|
|
793
|
+
[reportId],
|
|
794
|
+
budget
|
|
795
|
+
? budget.kind === "session"
|
|
796
|
+
? "session_budget"
|
|
797
|
+
: "call_budget"
|
|
798
|
+
: undefined,
|
|
799
|
+
);
|
|
494
800
|
const answer = value.ok ? value.answers.severity : undefined;
|
|
495
801
|
if (answer?.type === "score") {
|
|
496
802
|
const suffix = ` · severity ${answer.score.toFixed(1)}/3`;
|