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.
Files changed (117) hide show
  1. package/CHANGELOG.md +37 -1
  2. package/CONTRIBUTING.md +3 -0
  3. package/README.md +22 -14
  4. package/SECURITY.md +17 -1
  5. package/dist/adapters/ask-files.js +11 -2
  6. package/dist/adapters/ask-proof.js +63 -7
  7. package/dist/adapters/command.js +82 -29
  8. package/dist/adapters/docs.js +30 -10
  9. package/dist/adapters/evidence-context.js +119 -0
  10. package/dist/adapters/files.js +141 -16
  11. package/dist/adapters/find.js +34 -6
  12. package/dist/adapters/git-base.js +7 -1
  13. package/dist/adapters/git.js +51 -7
  14. package/dist/adapters/locate-file.js +47 -9
  15. package/dist/adapters/private-storage.js +14 -6
  16. package/dist/adapters/risk-callers.js +3 -0
  17. package/dist/adapters/shell.js +23 -7
  18. package/dist/adapters/test-inventory.js +10 -2
  19. package/dist/configuration.js +17 -7
  20. package/dist/constants.js +26 -5
  21. package/dist/core/ask-references.js +193 -109
  22. package/dist/core/asks.js +78 -7
  23. package/dist/core/locate.js +8 -8
  24. package/dist/core/output.js +17 -0
  25. package/dist/core/result-report.js +302 -0
  26. package/dist/core/secret-path.js +34 -0
  27. package/dist/core/state.js +8 -1
  28. package/dist/core/units.js +1 -1
  29. package/dist/jev/client.js +34 -12
  30. package/dist/mcp/protocol.js +50 -27
  31. package/dist/mcp/tools.js +20 -7
  32. package/dist/render.js +72 -0
  33. package/dist/report-schema.js +1356 -0
  34. package/dist/result-types.js +1 -0
  35. package/dist/texts/ask-files.js +3 -1
  36. package/dist/texts/ask.js +3 -1
  37. package/dist/texts/check-diff.js +7 -4
  38. package/dist/texts/find.js +7 -2
  39. package/dist/texts/guide.js +3 -16
  40. package/dist/texts/instructions.js +72 -0
  41. package/dist/texts/locate.js +7 -2
  42. package/dist/texts/select-tests.js +3 -1
  43. package/dist/tools/ask-files.js +248 -15
  44. package/dist/tools/ask.js +523 -62
  45. package/dist/tools/check-diff.js +222 -30
  46. package/dist/tools/docs-check.js +122 -13
  47. package/dist/tools/find.js +320 -27
  48. package/dist/tools/locate.js +317 -18
  49. package/dist/tools/review-report.js +230 -0
  50. package/dist/tools/select-tests.js +273 -19
  51. package/dist/tools/spec-check.js +119 -22
  52. package/docs/adr/0001-strict-typescript-pure-core-offline-tests.md +3 -3
  53. package/docs/agent-instructions.md +59 -30
  54. package/docs/design.md +13 -1
  55. package/docs/mcp.md +8 -6
  56. package/docs/tools/jev_ask.md +8 -5
  57. package/docs/tools/jev_ask_files.md +2 -1
  58. package/docs/tools/jev_check_diff.md +4 -1
  59. package/docs/tools/jev_find_files.md +2 -1
  60. package/docs/tools/jev_locate_in_file.md +5 -0
  61. package/docs/tools/jev_select_tests.md +4 -1
  62. package/package.json +1 -1
  63. package/rules/jev-ask.md +22 -1
  64. package/server.json +2 -2
  65. package/src/adapters/ask-files.ts +11 -3
  66. package/src/adapters/ask-proof.ts +69 -11
  67. package/src/adapters/command.ts +96 -33
  68. package/src/adapters/docs.ts +33 -14
  69. package/src/adapters/evidence-context.ts +169 -0
  70. package/src/adapters/files.ts +146 -16
  71. package/src/adapters/find.ts +37 -7
  72. package/src/adapters/git-base.ts +7 -1
  73. package/src/adapters/git.ts +61 -8
  74. package/src/adapters/locate-file.ts +51 -9
  75. package/src/adapters/private-storage.ts +17 -5
  76. package/src/adapters/risk-callers.ts +3 -0
  77. package/src/adapters/shell.ts +23 -7
  78. package/src/adapters/test-inventory.ts +12 -4
  79. package/src/configuration.ts +16 -2
  80. package/src/constants.ts +26 -5
  81. package/src/core/ask-references.ts +262 -146
  82. package/src/core/asks.ts +79 -7
  83. package/src/core/import-boundaries.ts +8 -3
  84. package/src/core/locate.ts +8 -5
  85. package/src/core/output.ts +34 -0
  86. package/src/core/result-report.ts +410 -0
  87. package/src/core/secret-path.ts +37 -0
  88. package/src/core/state.ts +8 -1
  89. package/src/core/units.ts +3 -2
  90. package/src/index.ts +3 -0
  91. package/src/jev/client.ts +54 -16
  92. package/src/jev/types.ts +18 -3
  93. package/src/mcp/protocol.ts +91 -41
  94. package/src/mcp/tools.ts +26 -13
  95. package/src/render.ts +109 -0
  96. package/src/report-schema.ts +1380 -0
  97. package/src/result-types.ts +234 -0
  98. package/src/result.ts +4 -1
  99. package/src/runtime.ts +6 -0
  100. package/src/texts/ask-files.ts +4 -1
  101. package/src/texts/ask.ts +8 -1
  102. package/src/texts/check-diff.ts +7 -4
  103. package/src/texts/find.ts +8 -2
  104. package/src/texts/guide.ts +8 -16
  105. package/src/texts/instructions.ts +98 -0
  106. package/src/texts/locate.ts +8 -2
  107. package/src/texts/run-end.ts +2 -2
  108. package/src/texts/select-tests.ts +4 -1
  109. package/src/tools/ask-files.ts +309 -14
  110. package/src/tools/ask.ts +700 -77
  111. package/src/tools/check-diff.ts +331 -28
  112. package/src/tools/docs-check.ts +241 -39
  113. package/src/tools/find.ts +386 -29
  114. package/src/tools/locate.ts +384 -19
  115. package/src/tools/review-report.ts +308 -0
  116. package/src/tools/select-tests.ts +479 -21
  117. 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
+ }