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.
Files changed (180) hide show
  1. package/CHANGELOG.md +106 -1
  2. package/CONTRIBUTING.md +43 -0
  3. package/README.md +58 -17
  4. package/SECURITY.md +43 -0
  5. package/dist/adapters/analysis-context.js +75 -0
  6. package/dist/adapters/ask-files.js +198 -0
  7. package/dist/adapters/ask-proof.js +200 -0
  8. package/dist/adapters/ask-syntax.js +385 -0
  9. package/dist/adapters/canonical-path.js +17 -0
  10. package/dist/adapters/command.js +234 -0
  11. package/dist/adapters/docs.js +192 -0
  12. package/dist/adapters/evidence-context.js +119 -0
  13. package/dist/adapters/exec.js +207 -0
  14. package/dist/adapters/files.js +418 -0
  15. package/dist/adapters/find.js +150 -0
  16. package/dist/adapters/git-base.js +32 -0
  17. package/dist/adapters/git-inventory.js +71 -0
  18. package/dist/adapters/git.js +483 -0
  19. package/dist/adapters/locate-file.js +197 -0
  20. package/dist/adapters/output-lines.js +46 -0
  21. package/dist/adapters/private-storage.js +106 -0
  22. package/dist/adapters/risk-callers.js +429 -0
  23. package/dist/adapters/runner-version.js +78 -0
  24. package/dist/adapters/shell.js +92 -0
  25. package/dist/adapters/syntax.js +187 -0
  26. package/dist/adapters/test-inventory.js +139 -0
  27. package/dist/adapters/usage.js +20 -0
  28. package/dist/adapters/utf8.js +47 -0
  29. package/dist/configuration.js +267 -0
  30. package/dist/constants.js +140 -0
  31. package/dist/core/ask-closure.js +282 -0
  32. package/dist/core/ask-proof.js +1 -0
  33. package/dist/core/ask-references.js +278 -0
  34. package/dist/core/asks.js +507 -0
  35. package/dist/core/batches.js +65 -0
  36. package/dist/core/command-output.js +224 -0
  37. package/dist/core/diff.js +178 -0
  38. package/dist/core/docs.js +302 -0
  39. package/dist/core/find.js +108 -0
  40. package/dist/core/git.js +1 -0
  41. package/dist/core/imports.js +550 -0
  42. package/dist/core/integrity.js +45 -0
  43. package/dist/core/lexical.js +132 -0
  44. package/dist/core/locate.js +169 -0
  45. package/dist/core/output.js +137 -0
  46. package/dist/core/pointer.js +29 -0
  47. package/dist/core/result-report.js +302 -0
  48. package/dist/core/risk-callers.js +851 -0
  49. package/dist/core/runner-version.js +45 -0
  50. package/dist/core/secret-path.js +34 -0
  51. package/dist/core/sections.js +230 -0
  52. package/dist/core/state.js +51 -0
  53. package/dist/core/syntax.js +1 -0
  54. package/dist/core/test-commands.js +334 -0
  55. package/dist/core/test-coverage.js +74 -0
  56. package/dist/core/test-discovery.js +1382 -0
  57. package/dist/core/test-evidence.js +527 -0
  58. package/dist/core/test-state.js +81 -0
  59. package/dist/core/truncate.js +12 -0
  60. package/dist/core/units.js +349 -0
  61. package/dist/describe.js +23 -0
  62. package/dist/guide.js +33 -0
  63. package/dist/host.js +24 -0
  64. package/dist/jev/client.js +456 -0
  65. package/dist/jev/pool.js +54 -0
  66. package/dist/jev/types.js +1 -0
  67. package/dist/mcp/main.js +124 -0
  68. package/dist/mcp/protocol.js +210 -0
  69. package/dist/mcp/tools.js +129 -0
  70. package/dist/presets/docs.js +62 -0
  71. package/dist/presets/risk.js +179 -0
  72. package/dist/presets/spec.js +81 -0
  73. package/dist/presets/witnesses.js +249 -0
  74. package/dist/render.js +114 -0
  75. package/dist/report-schema.js +1356 -0
  76. package/dist/result-types.js +1 -0
  77. package/dist/result.js +3 -0
  78. package/dist/runtime.js +1 -0
  79. package/dist/session.js +147 -0
  80. package/dist/texts/ask-files.js +3 -0
  81. package/dist/texts/ask.js +4 -0
  82. package/dist/texts/check-diff.js +20 -0
  83. package/dist/texts/configuration.js +1 -0
  84. package/dist/texts/find.js +19 -0
  85. package/dist/texts/guide.js +3 -0
  86. package/dist/texts/instructions.js +72 -0
  87. package/dist/texts/locate.js +15 -0
  88. package/dist/texts/select-tests.js +4 -0
  89. package/dist/tools/ask-files.js +450 -0
  90. package/dist/tools/ask-schema.js +70 -0
  91. package/dist/tools/ask.js +1147 -0
  92. package/dist/tools/check-diff.js +594 -0
  93. package/dist/tools/docs-check.js +408 -0
  94. package/dist/tools/find.js +682 -0
  95. package/dist/tools/locate.js +602 -0
  96. package/dist/tools/review-report.js +230 -0
  97. package/dist/tools/select-tests.js +821 -0
  98. package/dist/tools/spec-check.js +263 -0
  99. package/docs/adr/0001-strict-typescript-pure-core-offline-tests.md +31 -0
  100. package/docs/adr/0002-one-http-protocol-across-hosts.md +17 -0
  101. package/docs/adr/0003-explicit-scope-conservative-automation.md +19 -0
  102. package/docs/adr/0004-compiled-typed-intents.md +19 -0
  103. package/docs/adr/0005-evidence-construction-before-judgment.md +19 -0
  104. package/docs/adr/0006-visible-uncertainty-constrained-controls.md +21 -0
  105. package/docs/adr/0007-bounded-evidence-visible-limits.md +21 -0
  106. package/docs/adr/0008-static-test-discovery-conservative-plans.md +19 -0
  107. package/docs/adr/0009-session-cache-requested-model-identity.md +17 -0
  108. package/docs/adr/0010-mcp-server-thin-host.md +23 -0
  109. package/docs/agent-instructions.md +120 -0
  110. package/docs/design.md +16 -4
  111. package/docs/mcp.md +233 -0
  112. package/docs/tools/jev_ask.md +8 -5
  113. package/docs/tools/jev_ask_files.md +2 -1
  114. package/docs/tools/jev_check_diff.md +4 -1
  115. package/docs/tools/jev_find_files.md +2 -1
  116. package/docs/tools/jev_locate_in_file.md +5 -0
  117. package/docs/tools/jev_select_tests.md +4 -1
  118. package/package.json +19 -4
  119. package/rules/jev-ask.md +22 -1
  120. package/server.json +57 -0
  121. package/src/adapters/ask-files.ts +11 -3
  122. package/src/adapters/ask-proof.ts +69 -11
  123. package/src/adapters/canonical-path.ts +18 -0
  124. package/src/adapters/command.ts +102 -36
  125. package/src/adapters/docs.ts +33 -14
  126. package/src/adapters/evidence-context.ts +169 -0
  127. package/src/adapters/exec.ts +226 -0
  128. package/src/adapters/files.ts +146 -16
  129. package/src/adapters/find.ts +37 -7
  130. package/src/adapters/git-base.ts +7 -1
  131. package/src/adapters/git.ts +61 -8
  132. package/src/adapters/locate-file.ts +51 -9
  133. package/src/adapters/private-storage.ts +155 -0
  134. package/src/adapters/risk-callers.ts +7 -2
  135. package/src/adapters/shell.ts +113 -0
  136. package/src/adapters/test-inventory.ts +12 -4
  137. package/src/configuration.ts +55 -14
  138. package/src/constants.ts +37 -5
  139. package/src/core/ask-references.ts +262 -146
  140. package/src/core/asks.ts +79 -7
  141. package/src/core/command-output.ts +17 -1
  142. package/src/core/import-boundaries.ts +8 -3
  143. package/src/core/locate.ts +8 -5
  144. package/src/core/output.ts +34 -0
  145. package/src/core/result-report.ts +410 -0
  146. package/src/core/secret-path.ts +37 -0
  147. package/src/core/state.ts +8 -1
  148. package/src/core/units.ts +3 -2
  149. package/src/host.ts +11 -0
  150. package/src/index.ts +3 -0
  151. package/src/jev/client.ts +66 -16
  152. package/src/jev/types.ts +24 -3
  153. package/src/mcp/main.ts +135 -0
  154. package/src/mcp/protocol.ts +332 -0
  155. package/src/mcp/tools.ts +179 -0
  156. package/src/render.ts +109 -0
  157. package/src/report-schema.ts +1380 -0
  158. package/src/result-types.ts +234 -0
  159. package/src/result.ts +4 -1
  160. package/src/runtime.ts +6 -0
  161. package/src/session.ts +59 -0
  162. package/src/setup.ts +13 -5
  163. package/src/texts/ask-files.ts +4 -1
  164. package/src/texts/ask.ts +8 -1
  165. package/src/texts/check-diff.ts +7 -4
  166. package/src/texts/find.ts +8 -2
  167. package/src/texts/guide.ts +8 -16
  168. package/src/texts/instructions.ts +98 -0
  169. package/src/texts/locate.ts +8 -2
  170. package/src/texts/run-end.ts +2 -2
  171. package/src/texts/select-tests.ts +4 -1
  172. package/src/tools/ask-files.ts +311 -18
  173. package/src/tools/ask.ts +722 -95
  174. package/src/tools/check-diff.ts +337 -31
  175. package/src/tools/docs-check.ts +241 -38
  176. package/src/tools/find.ts +389 -29
  177. package/src/tools/locate.ts +387 -25
  178. package/src/tools/review-report.ts +308 -0
  179. package/src/tools/select-tests.ts +484 -23
  180. package/src/tools/spec-check.ts +194 -19
@@ -0,0 +1,302 @@
1
+ import { askBand } from "./output.js";
2
+ export const known = (value) => ({
3
+ status: "known",
4
+ value,
5
+ });
6
+ export const unknown = (reason) => ({
7
+ status: "unknown",
8
+ reason,
9
+ });
10
+ export const notApplicable = (reason) => ({
11
+ status: "not_applicable",
12
+ reason,
13
+ });
14
+ export const notCollected = (reason) => ({
15
+ status: "not_collected",
16
+ reason,
17
+ });
18
+ export function uncollectedContext(input) {
19
+ return {
20
+ authority: input.authority,
21
+ requestedRoot: input.requestedRoot ?? notApplicable("no override requested"),
22
+ effectiveRoot: unknown("effective root not established"),
23
+ requestedBase: input.requestedBase ?? notApplicable("no base requested"),
24
+ resolvedBase: input.requestedBase
25
+ ? notCollected("base resolution not performed")
26
+ : notApplicable("no base requested"),
27
+ inventories: [],
28
+ command: input.command ?? {
29
+ execution: "not_requested",
30
+ cwd: notApplicable("no command"),
31
+ exitCode: notApplicable("no command"),
32
+ timedOut: notApplicable("no command"),
33
+ },
34
+ };
35
+ }
36
+ export function contextFromEvidence(evidence, options = {}) {
37
+ const context = uncollectedContext({
38
+ authority: evidence?.authorityReason
39
+ ? unknown(evidence.authorityReason)
40
+ : evidence?.authority
41
+ ? known(evidence.authority)
42
+ : unknown("canonical authority not established"),
43
+ ...(evidence?.requestedRoot !== undefined
44
+ ? { requestedRoot: known(evidence.requestedRoot) }
45
+ : {}),
46
+ ...(evidence?.requestedBase !== undefined
47
+ ? { requestedBase: known(evidence.requestedBase) }
48
+ : {}),
49
+ ...(options.command ? { command: options.command } : {}),
50
+ });
51
+ if (evidence?.effectiveRoot)
52
+ context.effectiveRoot = known({
53
+ path: evidence.effectiveRoot.path,
54
+ origin: evidence.effectiveRoot.origin,
55
+ commonDir: evidence.effectiveRoot.commonDir === undefined
56
+ ? unknown("Git common directory not collected")
57
+ : known(evidence.effectiveRoot.commonDir),
58
+ });
59
+ if (evidence?.resolvedBase !== undefined)
60
+ context.resolvedBase = known(evidence.resolvedBase);
61
+ else if (evidence?.requestedBase !== undefined)
62
+ context.resolvedBase = unknown("requested base not resolved");
63
+ context.inventories = options.inventories ?? [];
64
+ return context;
65
+ }
66
+ export function buildResultReport(input) {
67
+ const counts = { fresh: 0, cache: 0, notJudged: 0, static: 0 };
68
+ for (const item of input.items) {
69
+ if (item.treatment === "judged")
70
+ counts[item.source]++;
71
+ else if (item.treatment === "static")
72
+ counts.static++;
73
+ else
74
+ counts.notJudged++;
75
+ }
76
+ const admissible = counts.fresh + counts.cache + counts.static;
77
+ const usefulFallback = input.items.some((item) => item.treatment === "not_judged" && item.selection?.selected);
78
+ const missing = input.missingWork ||
79
+ counts.notJudged > 0 ||
80
+ input.diagnostics.some((d) => d.cause === "criteria_no_match" || d.effect === "blocking");
81
+ const report = {
82
+ schemaVersion: 1,
83
+ tool: input.tool,
84
+ execution: !admissible && !usefulFallback
85
+ ? input.refused
86
+ ? "refused"
87
+ : "not_judged"
88
+ : missing
89
+ ? "partial"
90
+ : "complete",
91
+ context: input.context,
92
+ items: input.items,
93
+ diagnostics: input.diagnostics,
94
+ actions: input.actions,
95
+ accounting: {
96
+ toolInvocations: 1,
97
+ httpAttempts: input.metrics.calls,
98
+ questionsSent: input.metrics.questions,
99
+ cacheHits: input.metrics.cacheHits,
100
+ cacheRequests: input.metrics.cacheRequests,
101
+ requestedResults: { total: input.total, ...counts },
102
+ auxiliary: input.auxiliary,
103
+ costUsd: input.metrics.costUsd === undefined
104
+ ? unknown("current request cost not reported")
105
+ : known(input.metrics.costUsd),
106
+ elapsedMs: input.metrics.elapsedMs,
107
+ },
108
+ };
109
+ const errors = validateResultReport(report);
110
+ if (errors.length)
111
+ throw new Error(`Invalid ResultReportV1: ${errors.join("; ")}`);
112
+ return report;
113
+ }
114
+ /** Semantic checks complement, rather than replace, the public JSON Schema. */
115
+ export function validateResultReport(report) {
116
+ const errors = [];
117
+ const counter = (value, label) => {
118
+ if (!Number.isSafeInteger(value) || value < 0)
119
+ errors.push(`invalid counter ${label}`);
120
+ };
121
+ const measurement = (value, label, probability = false) => {
122
+ if (value.status === "known" &&
123
+ (!Number.isFinite(value.value) ||
124
+ value.value < 0 ||
125
+ (probability && value.value > 1)))
126
+ errors.push(`invalid measure ${label}`);
127
+ };
128
+ const accounting = report.accounting;
129
+ for (const key of [
130
+ "httpAttempts",
131
+ "questionsSent",
132
+ "cacheHits",
133
+ "cacheRequests",
134
+ ])
135
+ counter(accounting[key], key);
136
+ if (!Number.isFinite(accounting.elapsedMs) || accounting.elapsedMs < 0)
137
+ errors.push("invalid elapsedMs");
138
+ measurement(accounting.costUsd, "costUsd");
139
+ for (const [kind, values] of Object.entries(accounting.auxiliary))
140
+ for (const [source, value] of Object.entries(values))
141
+ counter(value, `${kind}.${source}`);
142
+ for (const inv of report.context.inventories) {
143
+ for (const [name, value] of [
144
+ ["discovered", inv.discovered],
145
+ ["considered", inv.considered],
146
+ ])
147
+ if (value.status === "known")
148
+ counter(value.value, `${inv.id}.${name}`);
149
+ for (const criterion of inv.criteria)
150
+ if (criterion.matches.status === "known")
151
+ counter(criterion.matches.value, `${inv.id}.matches`);
152
+ }
153
+ const ids = (values, label) => {
154
+ const set = new Set(values.map((v) => v.id));
155
+ if (set.size !== values.length)
156
+ errors.push(`duplicate ${label} IDs`);
157
+ return set;
158
+ };
159
+ const items = ids(report.items, "item");
160
+ const diagnostics = ids(report.diagnostics, "diagnostic");
161
+ const actions = ids(report.actions, "action");
162
+ const inventories = ids(report.context.inventories, "inventory");
163
+ const groups = new Set(report.items.map((i) => i.groupId));
164
+ const refs = (values, set, label) => {
165
+ for (const id of values)
166
+ if (!set.has(id))
167
+ errors.push(`unknown ${label} ID ${id}`);
168
+ };
169
+ const scope = (s) => {
170
+ if (s.kind === "item")
171
+ refs(s.itemIds, items, "item");
172
+ else if (s.kind === "group")
173
+ refs(s.groupIds, groups, "group");
174
+ else if (s.kind === "inventory")
175
+ refs(s.inventoryIds, inventories, "inventory");
176
+ };
177
+ for (const item of report.items) {
178
+ refs(item.diagnosticIds, diagnostics, "diagnostic");
179
+ refs(item.actionIds, actions, "action");
180
+ if (item.treatment === "judged") {
181
+ ids(item.judgment.controls, `control in ${item.id}`);
182
+ measurement(item.judgment.measure.value, `${item.id}.measure`, true);
183
+ for (const raw of [
184
+ ...item.judgment.rawValues,
185
+ ...item.judgment.controls.flatMap((c) => c.rawValues),
186
+ ])
187
+ measurement(raw.probability, `${item.id}.${raw.label}`, true);
188
+ if (typeof item.judgment.result === "number" &&
189
+ !Number.isFinite(item.judgment.result))
190
+ errors.push(`invalid result ${item.id}`);
191
+ }
192
+ }
193
+ for (const d of report.diagnostics) {
194
+ scope(d.scope);
195
+ refs(d.actionIds, actions, "action");
196
+ }
197
+ for (const a of report.actions)
198
+ scope(a.scope);
199
+ for (const inv of report.context.inventories)
200
+ for (const criterion of inv.criteria)
201
+ refs(criterion.diagnosticIds, diagnostics, "diagnostic");
202
+ const c = report.accounting.requestedResults;
203
+ for (const key of ["fresh", "cache", "static", "notJudged"])
204
+ counter(c[key], `requestedResults.${key}`);
205
+ if (c.total.status === "known")
206
+ counter(c.total.value, "requestedResults.total");
207
+ const actual = { fresh: 0, cache: 0, static: 0, notJudged: 0 };
208
+ for (const i of report.items) {
209
+ if (i.treatment === "judged")
210
+ actual[i.source]++;
211
+ else if (i.treatment === "static")
212
+ actual.static++;
213
+ else
214
+ actual.notJudged++;
215
+ }
216
+ for (const key of ["fresh", "cache", "static", "notJudged"])
217
+ if (c[key] !== actual[key])
218
+ errors.push(`requestedResults.${key} does not match items`);
219
+ if (c.total.status === "known" && c.total.value !== report.items.length)
220
+ errors.push("known requested total does not match items");
221
+ const admissible = actual.fresh + actual.cache + actual.static;
222
+ const usefulFallback = report.items.some((item) => item.treatment === "not_judged" && item.selection?.selected);
223
+ if ((report.execution === "refused" || report.execution === "not_judged") &&
224
+ (admissible || usefulFallback))
225
+ errors.push("non-result execution has admissible items or useful fallback");
226
+ if ((report.execution === "complete" || report.execution === "partial") &&
227
+ !admissible &&
228
+ !usefulFallback)
229
+ errors.push("result execution has no admissible items or useful fallback");
230
+ if (report.execution === "complete" &&
231
+ (actual.notJudged ||
232
+ report.diagnostics.some((d) => d.effect === "blocking" || d.cause === "criteria_no_match")))
233
+ errors.push("complete execution omits requested work");
234
+ if (!admissible && !report.diagnostics.length)
235
+ errors.push("absent judgment needs an explicit cause");
236
+ return errors;
237
+ }
238
+ export function resultIsError(report) {
239
+ if (report.execution === "refused")
240
+ return true;
241
+ if (report.items.some((i) => i.treatment !== "not_judged" || i.selection?.selected))
242
+ return false;
243
+ return report.diagnostics.some((d) => d.effect === "blocking" &&
244
+ [
245
+ "provider_context_refusal",
246
+ "not_configured",
247
+ "service_unavailable",
248
+ "transport_failure",
249
+ "invalid_response",
250
+ "internal_error",
251
+ ].includes(d.cause));
252
+ }
253
+ /** Source must be established by the client, never guessed from footer totals. */
254
+ export function answerItemFromInput(common, input) {
255
+ if (input.unjudged)
256
+ return { ...common, treatment: "not_judged", source: "none" };
257
+ const source = input.source ?? input.answer?.source;
258
+ if (source !== "fresh" && source !== "cache")
259
+ throw new Error(`Answer source not established: ${common.id}`);
260
+ const value = input.value;
261
+ const answer = input.answer;
262
+ if (!value && !answer)
263
+ throw new Error(`Answer value not established: ${common.id}`);
264
+ const result = input.publicResult ??
265
+ (answer?.type === "bool"
266
+ ? answer.p >= 0.5
267
+ : (value?.head ??
268
+ (answer?.type === "choice" ? answer.choice : answer?.score)));
269
+ if (result === undefined)
270
+ throw new Error(`Answer result not established: ${common.id}`);
271
+ const p = value?.p ??
272
+ (answer?.type === "bool"
273
+ ? answer.p
274
+ : answer
275
+ ? Math.max(...Object.values(answer.probabilities))
276
+ : undefined);
277
+ return {
278
+ ...common,
279
+ treatment: "judged",
280
+ source,
281
+ judgment: {
282
+ band: input.band ??
283
+ (p === undefined
284
+ ? "unsure"
285
+ : askBand({
286
+ head: typeof result === "boolean" ? String(result) : result,
287
+ p,
288
+ }, input)),
289
+ result,
290
+ measure: {
291
+ kind: "probability",
292
+ value: p === undefined
293
+ ? unknown("leading probability not collected")
294
+ : known(p),
295
+ },
296
+ reason: input.reason ?? "",
297
+ uncalibrated: input.uncalibrated ?? false,
298
+ rawValues: input.rawValues ?? [],
299
+ controls: input.reportControls ?? [],
300
+ },
301
+ };
302
+ }