jev-agent-tools 0.1.3 → 0.2.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 (135) hide show
  1. package/CHANGELOG.md +88 -3
  2. package/CONTRIBUTING.md +40 -0
  3. package/README.md +64 -9
  4. package/SECURITY.md +27 -0
  5. package/dist/adapters/analysis-context.js +75 -0
  6. package/dist/adapters/ask-files.js +189 -0
  7. package/dist/adapters/ask-proof.js +144 -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 +181 -0
  11. package/dist/adapters/docs.js +172 -0
  12. package/dist/adapters/exec.js +207 -0
  13. package/dist/adapters/files.js +293 -0
  14. package/dist/adapters/find.js +122 -0
  15. package/dist/adapters/git-base.js +26 -0
  16. package/dist/adapters/git-inventory.js +71 -0
  17. package/dist/adapters/git.js +439 -0
  18. package/dist/adapters/locate-file.js +159 -0
  19. package/dist/adapters/output-lines.js +46 -0
  20. package/dist/adapters/private-storage.js +98 -0
  21. package/dist/adapters/risk-callers.js +426 -0
  22. package/dist/adapters/runner-version.js +78 -0
  23. package/dist/adapters/shell.js +76 -0
  24. package/dist/adapters/syntax.js +187 -0
  25. package/dist/adapters/test-inventory.js +131 -0
  26. package/dist/adapters/usage.js +20 -0
  27. package/dist/adapters/utf8.js +47 -0
  28. package/dist/configuration.js +257 -0
  29. package/dist/constants.js +119 -0
  30. package/dist/core/ask-closure.js +282 -0
  31. package/dist/core/ask-proof.js +1 -0
  32. package/dist/core/ask-references.js +194 -0
  33. package/dist/core/asks.js +436 -0
  34. package/dist/core/batches.js +65 -0
  35. package/dist/core/command-output.js +224 -0
  36. package/dist/core/diff.js +178 -0
  37. package/dist/core/docs.js +302 -0
  38. package/dist/core/find.js +108 -0
  39. package/dist/core/git.js +1 -0
  40. package/dist/core/imports.js +550 -0
  41. package/dist/core/integrity.js +45 -0
  42. package/dist/core/lexical.js +132 -0
  43. package/dist/core/locate.js +169 -0
  44. package/dist/core/output.js +120 -0
  45. package/dist/core/pointer.js +29 -0
  46. package/dist/core/risk-callers.js +851 -0
  47. package/dist/core/runner-version.js +45 -0
  48. package/dist/core/sections.js +230 -0
  49. package/dist/core/state.js +44 -0
  50. package/dist/core/syntax.js +1 -0
  51. package/dist/core/test-commands.js +334 -0
  52. package/dist/core/test-coverage.js +74 -0
  53. package/dist/core/test-discovery.js +1382 -0
  54. package/dist/core/test-evidence.js +527 -0
  55. package/dist/core/test-state.js +81 -0
  56. package/dist/core/truncate.js +12 -0
  57. package/dist/core/units.js +349 -0
  58. package/dist/describe.js +23 -0
  59. package/dist/guide.js +33 -0
  60. package/dist/host.js +24 -0
  61. package/dist/jev/client.js +434 -0
  62. package/dist/jev/pool.js +54 -0
  63. package/dist/jev/types.js +1 -0
  64. package/dist/mcp/main.js +124 -0
  65. package/dist/mcp/protocol.js +187 -0
  66. package/dist/mcp/tools.js +116 -0
  67. package/dist/presets/docs.js +62 -0
  68. package/dist/presets/risk.js +179 -0
  69. package/dist/presets/spec.js +81 -0
  70. package/dist/presets/witnesses.js +249 -0
  71. package/dist/render.js +42 -0
  72. package/dist/result.js +3 -0
  73. package/dist/runtime.js +1 -0
  74. package/dist/session.js +147 -0
  75. package/dist/texts/ask-files.js +1 -0
  76. package/dist/texts/ask.js +2 -0
  77. package/dist/texts/check-diff.js +17 -0
  78. package/dist/texts/configuration.js +1 -0
  79. package/dist/texts/find.js +14 -0
  80. package/dist/texts/guide.js +16 -0
  81. package/dist/texts/locate.js +10 -0
  82. package/dist/texts/select-tests.js +2 -0
  83. package/dist/tools/ask-files.js +217 -0
  84. package/dist/tools/ask-schema.js +70 -0
  85. package/dist/tools/ask.js +686 -0
  86. package/dist/tools/check-diff.js +402 -0
  87. package/dist/tools/docs-check.js +299 -0
  88. package/dist/tools/find.js +389 -0
  89. package/dist/tools/locate.js +303 -0
  90. package/dist/tools/select-tests.js +567 -0
  91. package/dist/tools/spec-check.js +166 -0
  92. package/docs/adr/0001-strict-typescript-pure-core-offline-tests.md +31 -0
  93. package/docs/adr/0002-one-http-protocol-across-hosts.md +17 -0
  94. package/docs/adr/0003-explicit-scope-conservative-automation.md +19 -0
  95. package/docs/adr/0004-compiled-typed-intents.md +19 -0
  96. package/docs/adr/0005-evidence-construction-before-judgment.md +19 -0
  97. package/docs/adr/0006-visible-uncertainty-constrained-controls.md +21 -0
  98. package/docs/adr/0007-bounded-evidence-visible-limits.md +21 -0
  99. package/docs/adr/0008-static-test-discovery-conservative-plans.md +19 -0
  100. package/docs/adr/0009-session-cache-requested-model-identity.md +17 -0
  101. package/docs/adr/0010-mcp-server-thin-host.md +23 -0
  102. package/docs/agent-instructions.md +91 -0
  103. package/docs/design.md +3 -3
  104. package/docs/mcp.md +231 -0
  105. package/package.json +19 -4
  106. package/server.json +57 -0
  107. package/src/adapters/canonical-path.ts +18 -0
  108. package/src/adapters/command.ts +7 -4
  109. package/src/adapters/exec.ts +226 -0
  110. package/src/adapters/private-storage.ts +143 -0
  111. package/src/adapters/risk-callers.ts +4 -2
  112. package/src/adapters/shell.ts +97 -0
  113. package/src/configuration.ts +294 -0
  114. package/src/constants.ts +11 -0
  115. package/src/core/command-output.ts +17 -1
  116. package/src/host-tui.d.ts +14 -0
  117. package/src/host.ts +11 -0
  118. package/src/index.ts +13 -5
  119. package/src/jev/client.ts +12 -0
  120. package/src/jev/types.ts +6 -0
  121. package/src/mcp/main.ts +135 -0
  122. package/src/mcp/protocol.ts +282 -0
  123. package/src/mcp/tools.ts +166 -0
  124. package/src/secret-input.ts +222 -0
  125. package/src/session.ts +59 -0
  126. package/src/setup.ts +170 -0
  127. package/src/texts/configuration.ts +1 -1
  128. package/src/tools/ask-files.ts +8 -13
  129. package/src/tools/ask.ts +29 -28
  130. package/src/tools/check-diff.ts +11 -11
  131. package/src/tools/docs-check.ts +1 -0
  132. package/src/tools/find.ts +8 -8
  133. package/src/tools/locate.ts +8 -9
  134. package/src/tools/select-tests.ts +10 -10
  135. package/src/tools/spec-check.ts +1 -0
package/src/tools/ask.ts CHANGED
@@ -1,10 +1,11 @@
1
1
  import { createHash } from "node:crypto";
2
- import { resolve } from "node:path";
2
+ import { isAbsolute, relative, resolve } from "node:path";
3
3
  import type { ToolDefinition } from "@earendil-works/pi-coding-agent";
4
4
  import { type Static, Type } from "@sinclair/typebox";
5
5
  import { createAnalysisContext } from "../adapters/analysis-context.ts";
6
6
  import { collectAskRepository, repositoryPath } from "../adapters/ask-proof.ts";
7
7
  import { collectProofSyntax } from "../adapters/ask-syntax.ts";
8
+ import { canonicalPath } from "../adapters/canonical-path.ts";
8
9
  import { captureCommand } from "../adapters/command.ts";
9
10
  import { collectFiles } from "../adapters/files.ts";
10
11
  import { shareGitInventory } from "../adapters/git-inventory.ts";
@@ -67,12 +68,8 @@ export const askParameters = Type.Object(
67
68
  );
68
69
  export type AskArgs = Static<typeof askParameters>;
69
70
 
70
- export function createAskTool({
71
- client,
72
- host,
73
- runtime,
74
- exec: execute,
75
- }: ToolDependencies) {
71
+ export function createAskTool(dependencies: ToolDependencies) {
72
+ const { host, runtime, exec: execute } = dependencies;
76
73
  const { isOmp, names } = host;
77
74
  const allowCommand = process.env.JEV_TOOLS_ALLOW_COMMAND !== "0";
78
75
  const parameters = allowCommand
@@ -120,6 +117,8 @@ export function createAskTool({
120
117
  };
121
118
  };
122
119
  }> {
120
+ const client = dependencies.client;
121
+ const cwd = await canonicalPath(ctx.cwd);
123
122
  const started = performance.now();
124
123
  const exec = shareGitInventory(execute);
125
124
  const finish = (
@@ -162,7 +161,7 @@ export function createAskTool({
162
161
  return textResult("Provide state, command or at least one path.");
163
162
  const asks = compileAsks(args.asks, { surface: "ask" });
164
163
  if (!asks.ok) return textResult(asks.error);
165
- const files = await collectFiles(ctx.cwd, args.paths ?? [], signal, {
164
+ const files = await collectFiles(cwd, args.paths ?? [], signal, {
166
165
  exec,
167
166
  allowEmpty: true,
168
167
  skipInvalidUtf8: true,
@@ -181,12 +180,7 @@ export function createAskTool({
181
180
  fact,
182
181
  next: "Provide this file as UTF-8 text if the judgment needs it.",
183
182
  }));
184
- let repository = await collectAskRepository(
185
- exec,
186
- ctx.cwd,
187
- args.base,
188
- signal,
189
- );
183
+ let repository = await collectAskRepository(exec, cwd, args.base, signal);
190
184
  if (!repository.ok && args.base) return textResult(repository.error);
191
185
  if (!repository.ok)
192
186
  proofLimitations.push({
@@ -196,7 +190,7 @@ export function createAskTool({
196
190
  const command = args.command
197
191
  ? await captureCommand(
198
192
  exec,
199
- repository.ok ? repository.root : ctx.cwd,
193
+ repository.ok ? repository.root : cwd,
200
194
  args.command,
201
195
  args.timeout_s,
202
196
  signal,
@@ -204,12 +198,7 @@ export function createAskTool({
204
198
  : undefined;
205
199
  if (command && !command.ok) return textResult(command.error);
206
200
  if (command?.ok) {
207
- repository = await collectAskRepository(
208
- exec,
209
- ctx.cwd,
210
- args.base,
211
- signal,
212
- );
201
+ repository = await collectAskRepository(exec, cwd, args.base, signal);
213
202
  if (!repository.ok && args.base) return textResult(repository.error);
214
203
  }
215
204
  const initialState = assembleState(args.state, files.files);
@@ -223,6 +212,17 @@ export function createAskTool({
223
212
  for (const target of command.targets) {
224
213
  if (target.path.startsWith(`${repository.root}/`))
225
214
  target.path = target.path.slice(repository.root.length + 1);
215
+ else if (isAbsolute(target.path)) {
216
+ // Windows reports C:/... or short 8.3 forms; compare canonically.
217
+ const local = relative(
218
+ repository.root,
219
+ await canonicalPath(target.path),
220
+ )
221
+ .split("\\")
222
+ .join("/");
223
+ if (local && !local.startsWith("../") && !isAbsolute(local))
224
+ target.path = local;
225
+ }
226
226
  let matches = [...repository.known].filter(
227
227
  (path) => path === target.path || path.endsWith(`/${target.path}`),
228
228
  );
@@ -255,7 +255,7 @@ export function createAskTool({
255
255
  }
256
256
  const path = matches[0];
257
257
  if (!path) continue;
258
- const local = repositoryPath(ctx.cwd, repository.root, path);
258
+ const local = repositoryPath(cwd, repository.root, path);
259
259
  if (!Object.hasOwn(files.files, local)) {
260
260
  const added = await collectFiles(repository.root, [path], signal, {
261
261
  exec,
@@ -351,7 +351,7 @@ export function createAskTool({
351
351
  if (repository?.ok) {
352
352
  const canonical = Object.fromEntries(
353
353
  Object.entries(files.files).map(([path, text]) => [
354
- repositoryPath(repository.root, ctx.cwd, path),
354
+ repositoryPath(repository.root, cwd, path),
355
355
  text,
356
356
  ]),
357
357
  );
@@ -373,7 +373,7 @@ export function createAskTool({
373
373
  const canonicalPath =
374
374
  references.additions.find(
375
375
  (addition) => addition.reference === path,
376
- )?.path ?? repositoryPath(repository.root, ctx.cwd, path);
376
+ )?.path ?? repositoryPath(repository.root, cwd, path);
377
377
  return [path, before[canonicalPath] ?? null];
378
378
  }),
379
379
  );
@@ -515,11 +515,11 @@ export function createAskTool({
515
515
  const identities = files.identities.map((file) => {
516
516
  const content = inserted[file.requestedPath];
517
517
  const insertedPath = Object.keys(inserted).find(
518
- (path) => resolve(ctx.cwd, path) === file.resolvedPath,
518
+ (path) => resolve(cwd, path) === file.resolvedPath,
519
519
  );
520
520
  return {
521
521
  ...file,
522
- insertedPath: insertedPath ? resolve(ctx.cwd, insertedPath) : "",
522
+ insertedPath: insertedPath ? resolve(cwd, insertedPath) : "",
523
523
  content: typeof content === "string" ? content : "",
524
524
  insertedSha256: createHash("sha256")
525
525
  .update(typeof content === "string" ? content : "")
@@ -547,6 +547,7 @@ export function createAskTool({
547
547
  const options = {
548
548
  signal,
549
549
  cache: !args.command,
550
+ ...runtime.session.requestGate(),
550
551
  beforeRequest: (questionCount: number) => {
551
552
  if (args.max_calls !== undefined && sent >= args.max_calls) {
552
553
  refusal = {
@@ -790,14 +791,14 @@ export function createAskTool({
790
791
  const selectedPath = repository?.ok
791
792
  ? (references.additions.find(
792
793
  (addition) => addition.reference === selected,
793
- )?.path ?? repositoryPath(repository.root, ctx.cwd, selected))
794
+ )?.path ?? repositoryPath(repository.root, cwd, selected))
794
795
  : selected;
795
796
  const retained = (path: string) => {
796
797
  if (!repository?.ok) return path !== selected;
797
798
  return (
798
799
  (references.additions.find(
799
800
  (addition) => addition.reference === path,
800
- )?.path ?? repositoryPath(repository.root, ctx.cwd, path)) !==
801
+ )?.path ?? repositoryPath(repository.root, cwd, path)) !==
801
802
  selectedPath
802
803
  );
803
804
  };
@@ -1,6 +1,7 @@
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 { canonicalPath } from "../adapters/canonical-path.ts";
4
5
  import { collectUnits } from "../adapters/git.ts";
5
6
  import { resolveBase } from "../adapters/git-base.ts";
6
7
  import { shareGitInventory } from "../adapters/git-inventory.ts";
@@ -83,12 +84,8 @@ function unitLabel(unit: EvidenceUnit): string {
83
84
  .slice(0, 8);
84
85
  return `${unit.file}${range ? `:${range.start}-${range.end}` : ""} ${unit.name} [${fingerprint}]`;
85
86
  }
86
- export function createCheckDiffTool({
87
- client,
88
- host,
89
- runtime,
90
- exec: execute,
91
- }: ToolDependencies) {
87
+ export function createCheckDiffTool(dependencies: ToolDependencies) {
88
+ const { host, runtime, exec: execute } = dependencies;
92
89
  return {
93
90
  name: "jev_check_diff",
94
91
  label: "Jev check diff",
@@ -110,19 +107,21 @@ export function createCheckDiffTool({
110
107
  _update: unknown,
111
108
  ctx: { cwd: string } & GuideContext,
112
109
  ) {
110
+ const client = dependencies.client;
111
+ const cwd = await canonicalPath(ctx.cwd);
113
112
  const exec = shareGitInventory(execute);
114
113
  if (args.check === "docs" || args.check === "spec") {
115
114
  const deps = { client, host, runtime, exec };
116
115
  const result =
117
116
  args.check === "docs"
118
117
  ? await runDocsCheck(deps, {
119
- cwd: ctx.cwd,
118
+ cwd: cwd,
120
119
  base: args.base,
121
120
  signal,
122
121
  maxCalls: args.max_calls,
123
122
  })
124
123
  : await runSpecCheck(deps, {
125
- cwd: ctx.cwd,
124
+ cwd: cwd,
126
125
  base: args.base,
127
126
  specPath: args.spec_path,
128
127
  signal,
@@ -226,14 +225,14 @@ export function createCheckDiffTool({
226
225
  };
227
226
  };
228
227
  if (!client) return finish(NOT_CONFIGURED);
229
- const comparison = await resolveBase(exec, ctx.cwd, args.base, signal);
228
+ const comparison = await resolveBase(exec, cwd, args.base, signal);
230
229
  if (!comparison.ok) return finish(comparison.error);
231
230
  const base = comparison.base;
232
231
  const analysis = await createAnalysisContext();
233
232
  const collected = await collectUnits(
234
233
  exec,
235
234
  {
236
- cwd: ctx.cwd,
235
+ cwd: cwd,
237
236
  base,
238
237
  signal,
239
238
  },
@@ -274,6 +273,7 @@ export function createCheckDiffTool({
274
273
  let reservedMatrixCalls = batches.batches.length;
275
274
  const optionsFor = (matrixRequest = false): JudgmentOptions => ({
276
275
  signal,
276
+ ...runtime.session.requestGate(),
277
277
  beforeRequest(questionCount) {
278
278
  if (matrixRequest && reservedMatrixCalls > 0) reservedMatrixCalls--;
279
279
  const limit =
@@ -315,7 +315,7 @@ export function createCheckDiffTool({
315
315
  )
316
316
  ? await collectRiskCallers(
317
317
  exec,
318
- { cwd: ctx.cwd, base, signal },
318
+ { cwd: cwd, base, signal },
319
319
  collected.units,
320
320
  collected.files,
321
321
  analysis.parser,
@@ -319,6 +319,7 @@ export async function runDocsCheck(
319
319
  prepared.questions,
320
320
  {
321
321
  signal,
322
+ ...deps.runtime.session.requestGate(),
322
323
  beforeRequest(questionCount) {
323
324
  if (budget?.kind === "session")
324
325
  return { ok: false, error: budget.message };
package/src/tools/find.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { type Static, Type } from "@sinclair/typebox";
2
+ import { canonicalPath } from "../adapters/canonical-path.ts";
2
3
  import { collectSearchExcerpts } from "../adapters/files.ts";
3
4
  import { prefilter } from "../adapters/find.ts";
4
5
  import { shareGitInventory } from "../adapters/git-inventory.ts";
@@ -46,12 +47,8 @@ export const findParameters = Type.Object(
46
47
  { additionalProperties: false },
47
48
  );
48
49
  export type FindArgs = Static<typeof findParameters>;
49
- export function createFindFilesTool({
50
- client,
51
- host,
52
- runtime,
53
- exec: execute,
54
- }: ToolDependencies) {
50
+ export function createFindFilesTool(dependencies: ToolDependencies) {
51
+ const { host, runtime, exec: execute } = dependencies;
55
52
  return {
56
53
  name: "jev_find_files",
57
54
  label: "Jev find files",
@@ -77,6 +74,8 @@ export function createFindFilesTool({
77
74
  _update: unknown,
78
75
  ctx: { cwd: string } & GuideContext,
79
76
  ) {
77
+ const client = dependencies.client;
78
+ const cwd = await canonicalPath(ctx.cwd);
80
79
  const started = performance.now();
81
80
  const exec = shareGitInventory(execute);
82
81
  const totals = {
@@ -133,7 +132,7 @@ export function createFindFilesTool({
133
132
  : C.FIND_FILES_READ;
134
133
  const candidates = await prefilter(
135
134
  exec,
136
- ctx.cwd,
135
+ cwd,
137
136
  keywords,
138
137
  candidateLimit,
139
138
  args.scope,
@@ -193,6 +192,7 @@ export function createFindFilesTool({
193
192
  ): Promise<Judgment> => {
194
193
  const result = await client.judge(state, questions, {
195
194
  signal,
195
+ ...runtime.session.requestGate(),
196
196
  beforeRequest: (count) => {
197
197
  if (args.max_calls !== undefined && sent >= args.max_calls) {
198
198
  budget = {
@@ -311,7 +311,7 @@ export function createFindFilesTool({
311
311
  const contentResults = await Promise.all(
312
312
  contentBatches.map(async (paths) => {
313
313
  const read = await collectSearchExcerpts(
314
- ctx.cwd,
314
+ cwd,
315
315
  paths,
316
316
  keywords,
317
317
  signal,
@@ -1,5 +1,6 @@
1
1
  import type { ToolDefinition } from "@earendil-works/pi-coding-agent";
2
2
  import { type Static, Type } from "@sinclair/typebox";
3
+ import { canonicalPath } from "../adapters/canonical-path.ts";
3
4
  import { shareGitInventory } from "../adapters/git-inventory.ts";
4
5
  import { readLocateFile, scanRange } from "../adapters/locate-file.ts";
5
6
  import { loadSyntaxParser } from "../adapters/syntax.ts";
@@ -43,9 +44,10 @@ const parameters = Type.Object(
43
44
  type LocateArgs = Static<typeof parameters>;
44
45
 
45
46
  export function createLocateTool(
46
- { client, host, runtime, exec: execute }: ToolDependencies,
47
+ dependencies: ToolDependencies,
47
48
  loadParser: () => Promise<SyntaxParser | undefined> = loadSyntaxParser,
48
49
  ) {
50
+ const { host, runtime, exec: execute } = dependencies;
49
51
  return {
50
52
  name: "jev_locate_in_file",
51
53
  label: "Jev locate in file",
@@ -72,6 +74,8 @@ export function createLocateTool(
72
74
  _update: unknown,
73
75
  ctx: { cwd: string } & GuideContext,
74
76
  ) {
77
+ const client = dependencies.client;
78
+ const cwd = await canonicalPath(ctx.cwd);
75
79
  const started = performance.now();
76
80
  const exec = shareGitInventory(execute);
77
81
  const results: Judgment[] = [];
@@ -116,7 +120,7 @@ export function createLocateTool(
116
120
  ),
117
121
  };
118
122
  };
119
- const file = await readLocateFile(ctx.cwd, args.path, signal, exec);
123
+ const file = await readLocateFile(cwd, args.path, signal, exec);
120
124
  if (!file.ok) return finish({ refusal: file.error });
121
125
  if (file.bytes < LOCATE_MIN_KB * 1000)
122
126
  return finish({
@@ -180,6 +184,7 @@ export function createLocateTool(
180
184
  },
181
185
  {
182
186
  signal,
187
+ ...runtime.session.requestGate(),
183
188
  beforeRequest: (n) => runtime.session.admit(n),
184
189
  onUsage: (usage) => runtime.session.recordUsage(usage),
185
190
  },
@@ -268,13 +273,7 @@ export function createLocateTool(
268
273
  });
269
274
  }
270
275
  if (file.kind === "outline") {
271
- const selected = await scanRange(
272
- ctx.cwd,
273
- args.path,
274
- block,
275
- signal,
276
- exec,
277
- );
276
+ const selected = await scanRange(cwd, args.path, block, signal, exec);
278
277
  if (!selected.ok) return finish({ refusal: selected.error });
279
278
  const lines = selected.text.split("\n");
280
279
  sections = sections
@@ -2,6 +2,7 @@ import { matchesGlob, relative, resolve } from "node:path";
2
2
  import type { ToolDefinition } from "@earendil-works/pi-coding-agent";
3
3
  import { type Static, Type } from "@sinclair/typebox";
4
4
  import { createAnalysisContext } from "../adapters/analysis-context.ts";
5
+ import { canonicalPath } from "../adapters/canonical-path.ts";
5
6
  import { collectUnits } from "../adapters/git.ts";
6
7
  import { resolveBase } from "../adapters/git-base.ts";
7
8
  import { shareGitInventory } from "../adapters/git-inventory.ts";
@@ -104,12 +105,8 @@ function makeCandidate(
104
105
  };
105
106
  }
106
107
 
107
- export function createSelectTestsTool({
108
- client,
109
- host,
110
- runtime,
111
- exec: execute,
112
- }: ToolDependencies) {
108
+ export function createSelectTestsTool(dependencies: ToolDependencies) {
109
+ const { host, runtime, exec: execute } = dependencies;
113
110
  return {
114
111
  name: "jev_select_tests",
115
112
  label: "Jev select tests",
@@ -128,6 +125,8 @@ export function createSelectTestsTool({
128
125
  _update: unknown,
129
126
  ctx: { cwd: string } & GuideContext,
130
127
  ) {
128
+ const client = dependencies.client;
129
+ const cwd = await canonicalPath(ctx.cwd);
131
130
  const started = performance.now();
132
131
  const exec = shareGitInventory(execute);
133
132
  const totals = {
@@ -171,14 +170,14 @@ export function createSelectTestsTool({
171
170
  ...hostUsage(host.isOmp, costKnown ? totals.usage : undefined),
172
171
  };
173
172
  };
174
- const comparison = await resolveBase(exec, ctx.cwd, args.base, signal);
173
+ const comparison = await resolveBase(exec, cwd, args.base, signal);
175
174
  if (!comparison.ok) return finish({ refusal: comparison.error });
176
175
  const analysis = await createAnalysisContext();
177
176
  const [inventory, diff] = await Promise.all([
178
- collectTestInventory(exec, ctx.cwd, signal),
177
+ collectTestInventory(exec, cwd, signal),
179
178
  collectUnits(
180
179
  exec,
181
- { cwd: ctx.cwd, base: comparison.base, signal },
180
+ { cwd: cwd, base: comparison.base, signal },
182
181
  analysis.parser,
183
182
  ),
184
183
  ]);
@@ -390,6 +389,7 @@ export function createSelectTestsTool({
390
389
  const result = await client.judge(state, questions, {
391
390
  signal,
392
391
  witnesses: witnessIds,
392
+ ...runtime.session.requestGate(),
393
393
  beforeRequest: (questionCount) => {
394
394
  if (args.max_calls !== undefined && sent >= args.max_calls) {
395
395
  budget = {
@@ -738,7 +738,7 @@ export function createSelectTestsTool({
738
738
  lines: commands.commands.map((command) => ({
739
739
  type: "command" as const,
740
740
  ...command,
741
- cwd: relative(ctx.cwd, resolve(inventory.cwd, command.cwd)) || ".",
741
+ cwd: relative(cwd, resolve(inventory.cwd, command.cwd)) || ".",
742
742
  })),
743
743
  });
744
744
  },
@@ -175,6 +175,7 @@ export async function runSpecCheck(
175
175
  if (!units.length) return finish();
176
176
  const judgment = await deps.client.judge(prepared.state, prepared.questions, {
177
177
  signal: input.signal,
178
+ ...deps.runtime.session.requestGate(),
178
179
  beforeRequest(questionCount) {
179
180
  if (input.maxCalls !== undefined && sent >= input.maxCalls) {
180
181
  budget = {