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
package/dist/mcp/tools.js CHANGED
@@ -2,15 +2,19 @@ import { Value } from "@sinclair/typebox/value";
2
2
  import { spawnExec } from "../adapters/exec.js";
3
3
  import { ConfigController } from "../configuration.js";
4
4
  import { MCP_VALIDATION_MAX_ERRORS } from "../constants.js";
5
+ import { resultIsError } from "../core/result-report.js";
5
6
  import { Guide } from "../guide.js";
6
7
  import { mcpHost } from "../host.js";
8
+ import { mcpStructuredResultSchema } from "../report-schema.js";
7
9
  import { readSessionLimits, Session } from "../session.js";
10
+ import { renderAgentInstructions } from "../texts/instructions.js";
8
11
  import { createAskTool } from "../tools/ask.js";
9
12
  import { createAskFilesTool } from "../tools/ask-files.js";
10
13
  import { createCheckDiffTool } from "../tools/check-diff.js";
11
14
  import { createFindFilesTool } from "../tools/find.js";
12
15
  import { createLocateTool } from "../tools/locate.js";
13
16
  import { createSelectTestsTool } from "../tools/select-tests.js";
17
+ import { McpInvalidParams } from "./protocol.js";
14
18
  function validationError(schema, value) {
15
19
  if (Value.Check(schema, value))
16
20
  return undefined;
@@ -34,17 +38,20 @@ export async function loadMcpClient(env, configDirectory) {
34
38
  env,
35
39
  ...(configDirectory ? { directory: configDirectory } : {}),
36
40
  });
41
+ const configured = () => controller.client
42
+ ? { client: controller.client, apiKey: controller.values().apiKey }
43
+ : {};
37
44
  try {
38
45
  await controller.initialize({});
39
46
  }
40
47
  catch (error) {
41
48
  // Saved storage unusable: environment configuration (if any) still applies.
42
49
  return {
43
- ...(controller.client ? { client: controller.client } : {}),
50
+ ...configured(),
44
51
  warning: error instanceof Error ? error.message : String(error),
45
52
  };
46
53
  }
47
- return controller.client ? { client: controller.client } : {};
54
+ return configured();
48
55
  }
49
56
  catch (error) {
50
57
  return { warning: error instanceof Error ? error.message : String(error) };
@@ -58,12 +65,14 @@ export async function createMcpTools(options) {
58
65
  const env = options.env ?? process.env;
59
66
  const host = mcpHost();
60
67
  const loaded = options.client
61
- ? { client: options.client }
68
+ ? { client: options.client, apiKey: env.JEV_TOOLS_API_KEY }
62
69
  : await loadMcpClient(env, options.configDirectory);
63
70
  const client = loaded.client;
64
71
  const dependencies = {
65
72
  client,
73
+ apiKey: loaded.apiKey,
66
74
  host,
75
+ evidenceOrigin: "server",
67
76
  runtime: {
68
77
  session: new Session(readSessionLimits(env)),
69
78
  guide: new Guide(host),
@@ -96,17 +105,21 @@ export async function createMcpTools(options) {
96
105
  // Evidence is sent to the configured judgment endpoint.
97
106
  openWorldHint: true,
98
107
  },
108
+ outputSchema: mcpStructuredResultSchema,
99
109
  async call(args, signal) {
100
110
  const invalid = validationError(tool.parameters, args);
101
111
  if (invalid)
102
- return { content: [{ type: "text", text: invalid }], isError: true };
112
+ throw new McpInvalidParams(invalid);
103
113
  const result = await tool.execute(`mcp-${++id}`, args, signal, undefined, { cwd: options.root });
104
- return { content: result.content };
114
+ return {
115
+ content: result.content,
116
+ structuredContent: { result: result.details.result },
117
+ ...(resultIsError(result.details.result) ? { isError: true } : {}),
118
+ };
105
119
  },
106
120
  };
107
121
  });
108
- const guidelines = harness.flatMap((tool) => tool.promptGuidelines ?? []);
109
- const instructions = [dependencies.runtime.guide.text, ...guidelines].join("\n\n");
122
+ const instructions = renderAgentInstructions("mcp", host.names);
110
123
  return {
111
124
  tools,
112
125
  instructions,
package/dist/render.js CHANGED
@@ -40,3 +40,75 @@ function renderLine(line) {
40
40
  export function renderEnvelope(envelope) {
41
41
  return envelope.lines.map(renderLine).join("\n");
42
42
  }
43
+ function knowledge(value, format = String) {
44
+ return value.status === "known"
45
+ ? format(value.value)
46
+ : `${value.status} (${value.reason})`;
47
+ }
48
+ function scopeLabel(scope) {
49
+ return scope.kind === "call"
50
+ ? "call"
51
+ : scope.kind === "item"
52
+ ? `items ${scope.itemIds.join(", ")}`
53
+ : scope.kind === "group"
54
+ ? `groups ${scope.groupIds.join(", ")}`
55
+ : `inventory ${scope.inventoryIds.join(", ")}`;
56
+ }
57
+ /** Human projection uses the same typed facts as structured transport, never prose inference. */
58
+ export function renderResultReport(report, options = {}) {
59
+ const { context: c, accounting: a } = report;
60
+ const counts = a.requestedResults;
61
+ const mode = counts.fresh + counts.cache
62
+ ? `${counts.fresh} fresh / ${counts.cache} cache Jev results`
63
+ : counts.static
64
+ ? "static selection; no Jev judgment"
65
+ : report.items.some((item) => item.selection?.selected)
66
+ ? "conservative fallback selection; no Jev judgment"
67
+ : "no admissible judgment";
68
+ const lines = [
69
+ `${report.execution} — ${mode}${counts.notJudged ? `; ${counts.notJudged} not judged` : ""}`,
70
+ `context: authority ${knowledge(c.authority, (v) => `${v.path} (${v.origin})`)} · requested root ${knowledge(c.requestedRoot)} · effective root ${knowledge(c.effectiveRoot, (v) => `${v.path} (${v.origin}); common-dir ${knowledge(v.commonDir)}`)} · base ${knowledge(c.requestedBase)} → ${knowledge(c.resolvedBase)}`,
71
+ ];
72
+ for (const inv of c.inventories) {
73
+ lines.push(`inventory ${inv.id} (${inv.kind}): discovered ${knowledge(inv.discovered)} · considered ${knowledge(inv.considered)} · scopeRestricted ${inv.scopeRestricted} · rules ${inv.rules.join("; ")} · restrictions ${inv.restrictions.join("; ") || "none"}`);
74
+ for (const criterion of inv.criteria)
75
+ lines.push(`criterion ${JSON.stringify(criterion.criterion)}: ${criterion.outcome}; matches ${knowledge(criterion.matches)}`);
76
+ }
77
+ if (c.command.execution !== "not_requested")
78
+ lines.push(`command: ${c.command.execution} · cwd ${knowledge(c.command.cwd)} · exit ${knowledge(c.command.exitCode)} · timedOut ${knowledge(c.command.timedOut)}`);
79
+ for (const item of report.items) {
80
+ if (item.treatment === "judged") {
81
+ const j = item.judgment;
82
+ lines.push(`${j.band === "verdict" ? "" : `${j.band} `}${j.uncalibrated ? "uncalibrated " : ""}${item.label} = ${String(j.result)} (${j.measure.kind} ${knowledge(j.measure.value)}) [${item.source}]${j.reason ? ` — ${j.reason}` : ""}`);
83
+ for (const raw of j.rawValues)
84
+ lines.push(` raw ${raw.label} = ${String(raw.value)}; probability ${knowledge(raw.probability)}`);
85
+ for (const control of j.controls) {
86
+ lines.push(` control ${control.id} (${control.kind}, ${control.source}): ${control.outcome}`);
87
+ for (const raw of control.rawValues)
88
+ lines.push(` raw ${raw.label} = ${String(raw.value)}; probability ${knowledge(raw.probability)}`);
89
+ }
90
+ }
91
+ else
92
+ lines.push(`${item.treatment === "static" ? "static" : "unjudged"} ${item.label}${item.treatment === "static" ? ` — ${item.staticReason}` : ""}`);
93
+ for (const evidence of item.evidence)
94
+ lines.push(` evidence ${evidence.target} (${evidence.side}): ${knowledge(evidence.canonicalPath)} · aliases ${evidence.aliases.join(", ") || "none"} · revision ${knowledge(evidence.revision)}`);
95
+ if (item.selection)
96
+ lines.push(` selection: ${item.selection.selected ? "selected" : "not selected"} — ${item.selection.reason}`);
97
+ if (item.diagnosticIds.length)
98
+ lines.push(` diagnostics ${item.diagnosticIds.join(", ")}`);
99
+ if (item.actionIds.length)
100
+ lines.push(` actions ${item.actionIds.join(", ")}`);
101
+ }
102
+ // Complete diagnostic lists keep legacy text-only clients self-contained.
103
+ for (const d of report.diagnostics)
104
+ lines.push(`${d.material ? "material " : ""}${d.effect} ${d.id} — ${d.cause}: ${d.fact} · target ${knowledge(d.target)} · origin ${d.origin} · scope ${scopeLabel(d.scope)} · members ${knowledge(d.memberCount)}${d.omittedMembers.length ? ` · omitted ${d.omittedMembers.join(", ")}` : ""} · actions ${d.actionIds.join(", ") || "none"}`);
105
+ for (const action of report.actions)
106
+ lines.push(`next ${action.id} (${action.code}; ${scopeLabel(action.scope)}; target ${knowledge(action.target)}): ${action.condition} — ${action.instruction}`);
107
+ if (options.details) {
108
+ const detailLines = options.details.lines.filter((line) => line.type === "command" || line.type === "list");
109
+ if (detailLines.length)
110
+ lines.push("details:", renderEnvelope({ lines: detailLines }));
111
+ }
112
+ lines.push(`HTTP attempts ${a.httpAttempts} · questions sent ${a.questionsSent} · requested results total ${knowledge(counts.total)} / fresh ${counts.fresh} / cache ${counts.cache} / not judged ${counts.notJudged} / static ${counts.static} · cache probes ${a.cacheHits}/${a.cacheRequests} · auxiliary controls fresh ${a.auxiliary.controls.fresh} / cache ${a.auxiliary.controls.cache} / not judged ${a.auxiliary.controls.notJudged} / static ${a.auxiliary.controls.static} · passages fresh ${a.auxiliary.passages.fresh} / cache ${a.auxiliary.passages.cache} / not judged ${a.auxiliary.passages.notJudged} · current cost USD ${knowledge(a.costUsd)} · elapsed ${a.elapsedMs} ms`);
113
+ return lines.join("\n");
114
+ }