jev-agent-tools 0.2.0 → 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 +37 -1
- package/CONTRIBUTING.md +3 -0
- package/README.md +22 -14
- package/SECURITY.md +17 -1
- package/dist/adapters/ask-files.js +11 -2
- package/dist/adapters/ask-proof.js +63 -7
- package/dist/adapters/command.js +82 -29
- package/dist/adapters/docs.js +30 -10
- package/dist/adapters/evidence-context.js +119 -0
- package/dist/adapters/files.js +141 -16
- package/dist/adapters/find.js +34 -6
- package/dist/adapters/git-base.js +7 -1
- package/dist/adapters/git.js +51 -7
- package/dist/adapters/locate-file.js +47 -9
- package/dist/adapters/private-storage.js +14 -6
- package/dist/adapters/risk-callers.js +3 -0
- package/dist/adapters/shell.js +23 -7
- package/dist/adapters/test-inventory.js +10 -2
- package/dist/configuration.js +17 -7
- package/dist/constants.js +26 -5
- package/dist/core/ask-references.js +193 -109
- package/dist/core/asks.js +78 -7
- package/dist/core/locate.js +8 -8
- package/dist/core/output.js +17 -0
- package/dist/core/result-report.js +302 -0
- package/dist/core/secret-path.js +34 -0
- package/dist/core/state.js +8 -1
- package/dist/core/units.js +1 -1
- package/dist/jev/client.js +34 -12
- package/dist/mcp/protocol.js +50 -27
- package/dist/mcp/tools.js +20 -7
- package/dist/render.js +72 -0
- package/dist/report-schema.js +1356 -0
- package/dist/result-types.js +1 -0
- package/dist/texts/ask-files.js +3 -1
- package/dist/texts/ask.js +3 -1
- package/dist/texts/check-diff.js +7 -4
- package/dist/texts/find.js +7 -2
- package/dist/texts/guide.js +3 -16
- package/dist/texts/instructions.js +72 -0
- package/dist/texts/locate.js +7 -2
- package/dist/texts/select-tests.js +3 -1
- package/dist/tools/ask-files.js +248 -15
- package/dist/tools/ask.js +523 -62
- package/dist/tools/check-diff.js +222 -30
- package/dist/tools/docs-check.js +122 -13
- package/dist/tools/find.js +320 -27
- package/dist/tools/locate.js +317 -18
- package/dist/tools/review-report.js +230 -0
- package/dist/tools/select-tests.js +273 -19
- package/dist/tools/spec-check.js +119 -22
- package/docs/adr/0001-strict-typescript-pure-core-offline-tests.md +3 -3
- package/docs/agent-instructions.md +59 -30
- package/docs/design.md +13 -1
- package/docs/mcp.md +8 -6
- 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 +1 -1
- package/rules/jev-ask.md +22 -1
- package/server.json +2 -2
- package/src/adapters/ask-files.ts +11 -3
- package/src/adapters/ask-proof.ts +69 -11
- package/src/adapters/command.ts +96 -33
- package/src/adapters/docs.ts +33 -14
- package/src/adapters/evidence-context.ts +169 -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 +17 -5
- package/src/adapters/risk-callers.ts +3 -0
- package/src/adapters/shell.ts +23 -7
- package/src/adapters/test-inventory.ts +12 -4
- package/src/configuration.ts +16 -2
- package/src/constants.ts +26 -5
- package/src/core/ask-references.ts +262 -146
- package/src/core/asks.ts +79 -7
- 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/index.ts +3 -0
- package/src/jev/client.ts +54 -16
- package/src/jev/types.ts +18 -3
- package/src/mcp/protocol.ts +91 -41
- package/src/mcp/tools.ts +26 -13
- 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/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 +309 -14
- package/src/tools/ask.ts +700 -77
- package/src/tools/check-diff.ts +331 -28
- package/src/tools/docs-check.ts +241 -39
- package/src/tools/find.ts +386 -29
- package/src/tools/locate.ts +384 -19
- package/src/tools/review-report.ts +308 -0
- package/src/tools/select-tests.ts +479 -21
- package/src/tools/spec-check.ts +193 -19
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
import type { EvidenceContext } from "../adapters/evidence-context.ts";
|
|
2
|
+
import type { AnswerInput, Envelope, Yield } from "../core/output.ts";
|
|
3
|
+
import {
|
|
4
|
+
type Action,
|
|
5
|
+
answerItemFromInput,
|
|
6
|
+
buildResultReport,
|
|
7
|
+
type Cause,
|
|
8
|
+
contextFromEvidence,
|
|
9
|
+
type Diagnostic,
|
|
10
|
+
type Inventory,
|
|
11
|
+
type Item,
|
|
12
|
+
type ItemCommon,
|
|
13
|
+
known,
|
|
14
|
+
type ResultReportV1,
|
|
15
|
+
unknown,
|
|
16
|
+
} from "../core/result-report.ts";
|
|
17
|
+
import type { Answer, Judgment } from "../jev/types.ts";
|
|
18
|
+
|
|
19
|
+
export function reportMetrics(envelope: Envelope): Yield {
|
|
20
|
+
const line = envelope.lines.find((line) => line.type === "yield");
|
|
21
|
+
if (line?.type !== "yield")
|
|
22
|
+
throw new Error("Report metrics missing from envelope");
|
|
23
|
+
return line.value;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function controlsFor(
|
|
27
|
+
id: string,
|
|
28
|
+
judgment: Judgment,
|
|
29
|
+
witnessIds: readonly string[],
|
|
30
|
+
): Answer[] {
|
|
31
|
+
const batches = judgment.batches?.filter((batch) =>
|
|
32
|
+
batch.questionIds.includes(id),
|
|
33
|
+
);
|
|
34
|
+
const responses = batches?.length
|
|
35
|
+
? batches.map((batch) => batch.answers)
|
|
36
|
+
: [judgment.ok ? judgment.answers : {}];
|
|
37
|
+
return responses.flatMap((answers) =>
|
|
38
|
+
witnessIds.map(
|
|
39
|
+
(witnessId) =>
|
|
40
|
+
answers[witnessId] ?? {
|
|
41
|
+
type: "unjudged" as const,
|
|
42
|
+
reason: "Required witness response unavailable",
|
|
43
|
+
},
|
|
44
|
+
),
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Producer-owned inventory: register before admission, retain negative answers too. */
|
|
49
|
+
export class ReviewReport {
|
|
50
|
+
readonly items = new Map<string, Item>();
|
|
51
|
+
readonly diagnostics: Diagnostic[] = [];
|
|
52
|
+
readonly actions: Action[] = [];
|
|
53
|
+
readonly inventories: Inventory[] = [];
|
|
54
|
+
readonly auxiliary = {
|
|
55
|
+
controls: { fresh: 0, cache: 0, notJudged: 0, static: 0 },
|
|
56
|
+
passages: { fresh: 0, cache: 0, notJudged: 0 },
|
|
57
|
+
};
|
|
58
|
+
refusal = false;
|
|
59
|
+
missingWork = false;
|
|
60
|
+
totalUnknown = false;
|
|
61
|
+
countControls(judgment: Judgment, ids: readonly string[]): void {
|
|
62
|
+
const batches = judgment.batches ?? [
|
|
63
|
+
{ answers: judgment.ok ? judgment.answers : {}, questionIds: ids },
|
|
64
|
+
];
|
|
65
|
+
for (const batch of batches)
|
|
66
|
+
for (const id of ids) {
|
|
67
|
+
if (!batch.questionIds.includes(id) && judgment.batches) continue;
|
|
68
|
+
const answer = batch.answers[id];
|
|
69
|
+
this.auxiliary.controls[
|
|
70
|
+
answer && answer.type !== "unjudged" && answer.source
|
|
71
|
+
? answer.source
|
|
72
|
+
: "notJudged"
|
|
73
|
+
]++;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
expect(
|
|
78
|
+
id: string,
|
|
79
|
+
label: string,
|
|
80
|
+
kind: ItemCommon["kind"],
|
|
81
|
+
groupId = id,
|
|
82
|
+
): void {
|
|
83
|
+
if (!this.items.has(id))
|
|
84
|
+
this.items.set(id, {
|
|
85
|
+
id,
|
|
86
|
+
label,
|
|
87
|
+
kind,
|
|
88
|
+
groupId,
|
|
89
|
+
evidence: [],
|
|
90
|
+
diagnosticIds: [],
|
|
91
|
+
actionIds: [],
|
|
92
|
+
treatment: "not_judged",
|
|
93
|
+
source: "none",
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
diagnose(
|
|
97
|
+
cause: Cause,
|
|
98
|
+
fact: string,
|
|
99
|
+
target?: string,
|
|
100
|
+
itemIds: string[] = [],
|
|
101
|
+
blocking = true,
|
|
102
|
+
omittedMembers: string[] = [],
|
|
103
|
+
): void {
|
|
104
|
+
const id = `diagnostic:${this.diagnostics.length}`;
|
|
105
|
+
const actionId = `action:${this.actions.length}`;
|
|
106
|
+
const scope = itemIds.length
|
|
107
|
+
? { kind: "item" as const, itemIds }
|
|
108
|
+
: { kind: "call" as const };
|
|
109
|
+
const code =
|
|
110
|
+
cause === "invalid_root" || cause === "invalid_base"
|
|
111
|
+
? "correct_context"
|
|
112
|
+
: cause === "not_configured"
|
|
113
|
+
? "configure_client"
|
|
114
|
+
: cause === "service_unavailable" || cause === "transport_failure"
|
|
115
|
+
? "recover_service"
|
|
116
|
+
: "inspect_native";
|
|
117
|
+
this.actions.push({
|
|
118
|
+
id: actionId,
|
|
119
|
+
code,
|
|
120
|
+
target:
|
|
121
|
+
target === undefined
|
|
122
|
+
? unknown("target not established")
|
|
123
|
+
: known(target),
|
|
124
|
+
scope,
|
|
125
|
+
condition:
|
|
126
|
+
"Only after the named context, evidence, or service condition materially changes; otherwise use native inspection or the retained runner plan.",
|
|
127
|
+
instruction:
|
|
128
|
+
code === "correct_context"
|
|
129
|
+
? "Correct the named repository root or base if it does not match the intended comparison."
|
|
130
|
+
: code === "configure_client"
|
|
131
|
+
? "Configure the required Jev endpoint before requesting judgment; use native inspection meanwhile."
|
|
132
|
+
: code === "recover_service"
|
|
133
|
+
? "Establish service recovery before another useful judgment request; do not repeat the unchanged call."
|
|
134
|
+
: "Inspect the named evidence natively or execute the retained runner plan; preserve this limitation.",
|
|
135
|
+
repeatUnchanged: false,
|
|
136
|
+
});
|
|
137
|
+
this.diagnostics.push({
|
|
138
|
+
id,
|
|
139
|
+
cause,
|
|
140
|
+
fact,
|
|
141
|
+
target:
|
|
142
|
+
target === undefined
|
|
143
|
+
? unknown("target not established")
|
|
144
|
+
: known(target),
|
|
145
|
+
origin:
|
|
146
|
+
cause === "control_failure"
|
|
147
|
+
? "control"
|
|
148
|
+
: cause === "call_budget" || cause === "session_budget"
|
|
149
|
+
? "budget"
|
|
150
|
+
: cause === "not_configured" ||
|
|
151
|
+
cause === "service_unavailable" ||
|
|
152
|
+
cause === "transport_failure" ||
|
|
153
|
+
cause === "invalid_response" ||
|
|
154
|
+
cause === "provider_context_refusal"
|
|
155
|
+
? "provider"
|
|
156
|
+
: "collection",
|
|
157
|
+
scope,
|
|
158
|
+
effect: blocking ? "blocking" : "reservation",
|
|
159
|
+
material: true,
|
|
160
|
+
omittedMembers,
|
|
161
|
+
memberCount: known(omittedMembers.length),
|
|
162
|
+
actionIds: [actionId],
|
|
163
|
+
});
|
|
164
|
+
for (const itemId of itemIds) {
|
|
165
|
+
const item = this.items.get(itemId);
|
|
166
|
+
if (item) {
|
|
167
|
+
item.diagnosticIds.push(id);
|
|
168
|
+
item.actionIds.push(actionId);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
static(id: string, reason: string, selected?: boolean): void {
|
|
173
|
+
const item = this.items.get(id);
|
|
174
|
+
if (!item) throw new Error(`Unregistered review result ${id}`);
|
|
175
|
+
this.items.set(id, {
|
|
176
|
+
...item,
|
|
177
|
+
treatment: "static",
|
|
178
|
+
source: "none",
|
|
179
|
+
staticReason: reason,
|
|
180
|
+
...(selected === undefined ? {} : { selection: { selected, reason } }),
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
answer(
|
|
184
|
+
id: string,
|
|
185
|
+
answer: Answer | undefined,
|
|
186
|
+
input: Partial<AnswerInput> = {},
|
|
187
|
+
controls: Answer[] = [],
|
|
188
|
+
countControls = true,
|
|
189
|
+
): void {
|
|
190
|
+
const item = this.items.get(id);
|
|
191
|
+
if (!item) throw new Error(`Unregistered review result ${id}`);
|
|
192
|
+
const unavailable = !answer || answer.type === "unjudged" || !answer.source;
|
|
193
|
+
const missingControl = controls.some(
|
|
194
|
+
(control) => control.type === "unjudged" || !control.source,
|
|
195
|
+
);
|
|
196
|
+
if (countControls)
|
|
197
|
+
for (const control of controls)
|
|
198
|
+
this.auxiliary.controls[
|
|
199
|
+
control.type === "unjudged" || !control.source
|
|
200
|
+
? "notJudged"
|
|
201
|
+
: control.source
|
|
202
|
+
]++;
|
|
203
|
+
if (unavailable || missingControl) {
|
|
204
|
+
this.diagnose(
|
|
205
|
+
missingControl
|
|
206
|
+
? "control_failure"
|
|
207
|
+
: answer?.type === "unjudged"
|
|
208
|
+
? (answer.cause ?? "invalid_response")
|
|
209
|
+
: "invalid_response",
|
|
210
|
+
missingControl
|
|
211
|
+
? "Required control response is unavailable; the requested group has no admissible judgment."
|
|
212
|
+
: answer?.type === "unjudged"
|
|
213
|
+
? answer.reason
|
|
214
|
+
: "Requested answer or its transport provenance is unavailable.",
|
|
215
|
+
item.label,
|
|
216
|
+
[id],
|
|
217
|
+
);
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
const valid = answer as Exclude<Answer, { type: "unjudged" }>;
|
|
221
|
+
const rawValues =
|
|
222
|
+
valid.type === "bool"
|
|
223
|
+
? [{ label: "true", value: true, probability: known(valid.p) }]
|
|
224
|
+
: Object.entries(valid.probabilities).map(([label, p]) => ({
|
|
225
|
+
label,
|
|
226
|
+
value: label,
|
|
227
|
+
probability: known(p),
|
|
228
|
+
}));
|
|
229
|
+
this.items.set(
|
|
230
|
+
id,
|
|
231
|
+
answerItemFromInput(item, {
|
|
232
|
+
label: item.label,
|
|
233
|
+
answer: valid,
|
|
234
|
+
source: valid.source,
|
|
235
|
+
band: "verdict",
|
|
236
|
+
rawValues,
|
|
237
|
+
...input,
|
|
238
|
+
reportControls: controls.map((control, index) => {
|
|
239
|
+
const source = control.source;
|
|
240
|
+
if (!source) throw new Error("Admitted control lacks provenance");
|
|
241
|
+
return {
|
|
242
|
+
id: `${id}:control:${index}`,
|
|
243
|
+
kind: "integrity",
|
|
244
|
+
source,
|
|
245
|
+
outcome:
|
|
246
|
+
control.type === "bool"
|
|
247
|
+
? String(control.p)
|
|
248
|
+
: control.type === "choice"
|
|
249
|
+
? control.choice
|
|
250
|
+
: control.type === "score"
|
|
251
|
+
? String(control.score)
|
|
252
|
+
: control.reason,
|
|
253
|
+
rawValues:
|
|
254
|
+
control.type === "choice" || control.type === "score"
|
|
255
|
+
? Object.entries(control.probabilities).map(([label, p]) => ({
|
|
256
|
+
label,
|
|
257
|
+
value: label,
|
|
258
|
+
probability: known(p),
|
|
259
|
+
}))
|
|
260
|
+
: control.type === "bool"
|
|
261
|
+
? [
|
|
262
|
+
{
|
|
263
|
+
label: "true",
|
|
264
|
+
value: true,
|
|
265
|
+
probability: known(control.p),
|
|
266
|
+
},
|
|
267
|
+
]
|
|
268
|
+
: [],
|
|
269
|
+
};
|
|
270
|
+
}),
|
|
271
|
+
}),
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
failure(judgment: Judgment, ids: string[], cause?: Cause): void {
|
|
275
|
+
this.diagnose(
|
|
276
|
+
cause ??
|
|
277
|
+
(!judgment.ok ? judgment.cause : undefined) ??
|
|
278
|
+
judgment.failureCause ??
|
|
279
|
+
"invalid_response",
|
|
280
|
+
judgment.ok ? "Required responses unavailable" : judgment.error,
|
|
281
|
+
undefined,
|
|
282
|
+
ids,
|
|
283
|
+
);
|
|
284
|
+
}
|
|
285
|
+
build(
|
|
286
|
+
tool: ResultReportV1["tool"],
|
|
287
|
+
evidence: EvidenceContext,
|
|
288
|
+
metrics: Yield,
|
|
289
|
+
): ResultReportV1 {
|
|
290
|
+
const context = contextFromEvidence(evidence);
|
|
291
|
+
context.inventories = this.inventories;
|
|
292
|
+
return buildResultReport({
|
|
293
|
+
tool,
|
|
294
|
+
context,
|
|
295
|
+
items: [...this.items.values()],
|
|
296
|
+
diagnostics: this.diagnostics,
|
|
297
|
+
actions: this.actions,
|
|
298
|
+
metrics,
|
|
299
|
+
auxiliary: this.auxiliary,
|
|
300
|
+
total:
|
|
301
|
+
this.totalUnknown || (!this.inventories.length && !this.items.size)
|
|
302
|
+
? unknown("Expected inventory was not completely enumerable")
|
|
303
|
+
: known(this.items.size),
|
|
304
|
+
refused: this.refusal,
|
|
305
|
+
missingWork: this.missingWork,
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
}
|