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
package/src/tools/find.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { type Static, Type } from "@sinclair/typebox";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
resolveEvidenceContext,
|
|
4
|
+
withEvidenceContext,
|
|
5
|
+
} from "../adapters/evidence-context.ts";
|
|
3
6
|
import { collectSearchExcerpts } from "../adapters/files.ts";
|
|
4
7
|
import { prefilter } from "../adapters/find.ts";
|
|
5
8
|
import { shareGitInventory } from "../adapters/git-inventory.ts";
|
|
@@ -19,16 +22,29 @@ import {
|
|
|
19
22
|
type EnvelopeInput,
|
|
20
23
|
} from "../core/output.ts";
|
|
21
24
|
import { needsReverse, pointerQuestion, readPointer } from "../core/pointer.ts";
|
|
25
|
+
import {
|
|
26
|
+
type Action,
|
|
27
|
+
answerItemFromInput,
|
|
28
|
+
buildResultReport,
|
|
29
|
+
type Cause,
|
|
30
|
+
contextFromEvidence,
|
|
31
|
+
type Diagnostic,
|
|
32
|
+
type Item,
|
|
33
|
+
known,
|
|
34
|
+
type RawValue,
|
|
35
|
+
} from "../core/result-report.ts";
|
|
36
|
+
import { secretRefusal } from "../core/secret-path.ts";
|
|
22
37
|
import { interpolate } from "../describe.ts";
|
|
23
38
|
import type { GuideContext } from "../guide.ts";
|
|
24
|
-
import type { Judgment, Question, State } from "../jev/types.ts";
|
|
25
|
-
import {
|
|
39
|
+
import type { Answer, Judgment, Question, State } from "../jev/types.ts";
|
|
40
|
+
import { renderResultReport } from "../render.ts";
|
|
26
41
|
import type { ToolDependencies } from "../runtime.ts";
|
|
27
42
|
import { NOT_CONFIGURED } from "../texts/configuration.ts";
|
|
28
43
|
import { FIND_DESCRIPTION } from "../texts/find.ts";
|
|
29
44
|
|
|
30
45
|
export const findParameters = Type.Object(
|
|
31
46
|
{
|
|
47
|
+
root: Type.Optional(Type.String()),
|
|
32
48
|
goal: Type.String({ minLength: 1 }),
|
|
33
49
|
keywords: Type.Optional(Type.Array(Type.String({ minLength: 1 }))),
|
|
34
50
|
scope: Type.Optional(
|
|
@@ -75,7 +91,13 @@ export function createFindFilesTool(dependencies: ToolDependencies) {
|
|
|
75
91
|
ctx: { cwd: string } & GuideContext,
|
|
76
92
|
) {
|
|
77
93
|
const client = dependencies.client;
|
|
78
|
-
const
|
|
94
|
+
const evidence = await resolveEvidenceContext(ctx.cwd, args.root, {
|
|
95
|
+
exec: execute,
|
|
96
|
+
signal,
|
|
97
|
+
origin: dependencies.evidenceOrigin,
|
|
98
|
+
});
|
|
99
|
+
const evidenceContext = evidence.context;
|
|
100
|
+
const cwd = evidence.ok ? evidence.cwd : evidenceContext.authority.path;
|
|
79
101
|
const started = performance.now();
|
|
80
102
|
const exec = shareGitInventory(execute);
|
|
81
103
|
const totals = {
|
|
@@ -90,6 +112,101 @@ export function createFindFilesTool(dependencies: ToolDependencies) {
|
|
|
90
112
|
const unchecked: string[] = [];
|
|
91
113
|
let budget: BudgetRefusal | undefined;
|
|
92
114
|
let sent = 0;
|
|
115
|
+
const context = contextFromEvidence(evidenceContext);
|
|
116
|
+
const diagnostics: Diagnostic[] = [];
|
|
117
|
+
const actions: Action[] = [];
|
|
118
|
+
const auxiliary = {
|
|
119
|
+
controls: { fresh: 0, cache: 0, notJudged: 0, static: 0 },
|
|
120
|
+
passages: { fresh: 0, cache: 0, notJudged: 0 },
|
|
121
|
+
};
|
|
122
|
+
let entryPaths: string[] = [];
|
|
123
|
+
let costObserved = true;
|
|
124
|
+
let failureCause: Cause = "invalid_response";
|
|
125
|
+
let primary: Answer | undefined;
|
|
126
|
+
let confirmation: Answer | undefined;
|
|
127
|
+
let confirmationRequired = false;
|
|
128
|
+
const raw = (answer: Answer): RawValue[] =>
|
|
129
|
+
answer.type === "choice"
|
|
130
|
+
? Object.entries(answer.probabilities).map(
|
|
131
|
+
([label, probability]) => ({
|
|
132
|
+
label,
|
|
133
|
+
value: label,
|
|
134
|
+
probability: known(probability),
|
|
135
|
+
}),
|
|
136
|
+
)
|
|
137
|
+
: [];
|
|
138
|
+
const diagnostic = (
|
|
139
|
+
cause: Cause,
|
|
140
|
+
fact: string,
|
|
141
|
+
members: string[] = [],
|
|
142
|
+
) => {
|
|
143
|
+
const id = `d${diagnostics.length + 1}`;
|
|
144
|
+
const actionId = `a${actions.length + 1}`;
|
|
145
|
+
const code =
|
|
146
|
+
cause === "invalid_root"
|
|
147
|
+
? "correct_context"
|
|
148
|
+
: cause === "not_configured"
|
|
149
|
+
? "configure_client"
|
|
150
|
+
: cause === "service_unavailable" || cause === "transport_failure"
|
|
151
|
+
? "recover_service"
|
|
152
|
+
: "inspect_native";
|
|
153
|
+
actions.push({
|
|
154
|
+
id: actionId,
|
|
155
|
+
code,
|
|
156
|
+
target: known(members.join(", ") || "entry"),
|
|
157
|
+
scope: { kind: "call" },
|
|
158
|
+
condition:
|
|
159
|
+
code === "correct_context"
|
|
160
|
+
? "If the requested root is incorrect"
|
|
161
|
+
: code === "configure_client"
|
|
162
|
+
? "If Jev judgment is still useful after configuring the client"
|
|
163
|
+
: code === "recover_service"
|
|
164
|
+
? "Only after service recovery is observed"
|
|
165
|
+
: "If the entry decision remains open",
|
|
166
|
+
instruction:
|
|
167
|
+
code === "correct_context"
|
|
168
|
+
? "Provide the exact registered worktree top-level; no fallback was performed."
|
|
169
|
+
: code === "configure_client"
|
|
170
|
+
? "Configure the Jev client, or continue with native discovery."
|
|
171
|
+
: code === "recover_service"
|
|
172
|
+
? "Continue natively while unavailable; revisit only after a material service change."
|
|
173
|
+
: "Inspect the candidate paths or collected passages natively; retain uncertainty without repeating unchanged evidence.",
|
|
174
|
+
repeatUnchanged: false,
|
|
175
|
+
});
|
|
176
|
+
diagnostics.push({
|
|
177
|
+
id,
|
|
178
|
+
cause,
|
|
179
|
+
fact,
|
|
180
|
+
target: known(
|
|
181
|
+
members.join(", ") ||
|
|
182
|
+
(cause === "invalid_root"
|
|
183
|
+
? (args.root ?? cwd)
|
|
184
|
+
: cause === "forbidden_path"
|
|
185
|
+
? JSON.stringify({ scope: args.scope, exclude: args.exclude })
|
|
186
|
+
: "entry"),
|
|
187
|
+
),
|
|
188
|
+
origin:
|
|
189
|
+
cause === "invalid_root" || cause === "forbidden_path"
|
|
190
|
+
? "input"
|
|
191
|
+
: cause === "call_budget" || cause === "session_budget"
|
|
192
|
+
? "budget"
|
|
193
|
+
: cause === "control_failure"
|
|
194
|
+
? "control"
|
|
195
|
+
: cause === "service_unavailable" ||
|
|
196
|
+
cause === "transport_failure" ||
|
|
197
|
+
cause === "invalid_response" ||
|
|
198
|
+
cause === "provider_context_refusal" ||
|
|
199
|
+
cause === "not_configured"
|
|
200
|
+
? "provider"
|
|
201
|
+
: "collection",
|
|
202
|
+
scope: { kind: "call" },
|
|
203
|
+
effect: "blocking",
|
|
204
|
+
material: true,
|
|
205
|
+
omittedMembers: members,
|
|
206
|
+
memberCount: known(members.length),
|
|
207
|
+
actionIds: [actionId],
|
|
208
|
+
});
|
|
209
|
+
};
|
|
93
210
|
const finish = (input: Omit<EnvelopeInput, "yield">) => {
|
|
94
211
|
const envelope = buildEnvelope({
|
|
95
212
|
...input,
|
|
@@ -101,15 +218,166 @@ export function createFindFilesTool(dependencies: ToolDependencies) {
|
|
|
101
218
|
elapsedMs: performance.now() - started,
|
|
102
219
|
},
|
|
103
220
|
});
|
|
221
|
+
if (input.refusal) diagnostic(failureCause, input.refusal);
|
|
222
|
+
if (
|
|
223
|
+
!input.refusal &&
|
|
224
|
+
!diagnostics.length &&
|
|
225
|
+
!budget &&
|
|
226
|
+
!unchecked.length &&
|
|
227
|
+
!primary?.source
|
|
228
|
+
)
|
|
229
|
+
diagnostic(
|
|
230
|
+
"invalid_response",
|
|
231
|
+
"entry decision was not received with established answer provenance",
|
|
232
|
+
);
|
|
233
|
+
if (budget)
|
|
234
|
+
diagnostic(
|
|
235
|
+
budget.kind === "max_calls" ? "call_budget" : "session_budget",
|
|
236
|
+
budget.message,
|
|
237
|
+
[...new Set(unchecked)],
|
|
238
|
+
);
|
|
239
|
+
if (unchecked.length && !budget)
|
|
240
|
+
diagnostic(
|
|
241
|
+
failureCause,
|
|
242
|
+
"candidate ranking or entry work was not judged",
|
|
243
|
+
[...new Set(unchecked)],
|
|
244
|
+
);
|
|
245
|
+
const entryLine = input.lines?.find((line) => line.type === "entry");
|
|
246
|
+
if (entryLine?.type === "entry" && primary?.source)
|
|
247
|
+
actions.push({
|
|
248
|
+
id: `a${actions.length + 1}`,
|
|
249
|
+
code: "inspect_native",
|
|
250
|
+
target: known(
|
|
251
|
+
(entryLine.nextRead ?? []).join(", ") || "candidate inventory",
|
|
252
|
+
),
|
|
253
|
+
scope: { kind: "group", groupIds: ["entry"] },
|
|
254
|
+
condition:
|
|
255
|
+
"If the entry decision remains open after this bounded judgment",
|
|
256
|
+
instruction:
|
|
257
|
+
entryLine.candidate.label === "none"
|
|
258
|
+
? "Search elsewhere or widen the admitted scope for the goal; inspect any named alternative before collecting genuinely new evidence. Do not retry unchanged evidence."
|
|
259
|
+
: "Read the named candidate passages; add only genuinely discriminating evidence if needed. Do not reword unchanged evidence to retry.",
|
|
260
|
+
repeatUnchanged: false,
|
|
261
|
+
});
|
|
262
|
+
const common = {
|
|
263
|
+
id: "entry",
|
|
264
|
+
kind: "entry" as const,
|
|
265
|
+
label: "entry",
|
|
266
|
+
groupId: "entry",
|
|
267
|
+
evidence: entryPaths.map((path) => ({
|
|
268
|
+
target: path,
|
|
269
|
+
canonicalPath: known(`${cwd}/${path}`),
|
|
270
|
+
aliases: [],
|
|
271
|
+
side: "current" as const,
|
|
272
|
+
revision: context.resolvedBase,
|
|
273
|
+
})),
|
|
274
|
+
diagnosticIds: diagnostics.map((d) => d.id),
|
|
275
|
+
actionIds: actions.map((a) => a.id),
|
|
276
|
+
};
|
|
277
|
+
let item: Item = { ...common, treatment: "not_judged", source: "none" };
|
|
278
|
+
if (
|
|
279
|
+
entryLine?.type === "entry" &&
|
|
280
|
+
primary?.type === "choice" &&
|
|
281
|
+
primary.source &&
|
|
282
|
+
(!confirmationRequired ||
|
|
283
|
+
(confirmation?.type === "choice" && confirmation.source))
|
|
284
|
+
) {
|
|
285
|
+
auxiliary.passages[primary.source]--;
|
|
286
|
+
item = answerItemFromInput(common, {
|
|
287
|
+
label: "entry",
|
|
288
|
+
answer: primary,
|
|
289
|
+
value: entryLine.candidate.value,
|
|
290
|
+
publicResult: entryLine.candidate.value.head,
|
|
291
|
+
band: entryLine.band,
|
|
292
|
+
reason: limitations.map((limit) => limit.fact).join("; "),
|
|
293
|
+
rawValues: raw(primary),
|
|
294
|
+
reportControls:
|
|
295
|
+
confirmation?.type === "choice" && confirmation.source
|
|
296
|
+
? [
|
|
297
|
+
{
|
|
298
|
+
id: "entry-order",
|
|
299
|
+
kind: "order",
|
|
300
|
+
source: confirmation.source,
|
|
301
|
+
outcome: entryLine.orderDependent
|
|
302
|
+
? "order-dependent"
|
|
303
|
+
: "consistent",
|
|
304
|
+
rawValues: raw(confirmation),
|
|
305
|
+
},
|
|
306
|
+
]
|
|
307
|
+
: [],
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
const report = buildResultReport({
|
|
311
|
+
tool: "jev_find_files",
|
|
312
|
+
context,
|
|
313
|
+
items: [item],
|
|
314
|
+
diagnostics,
|
|
315
|
+
actions,
|
|
316
|
+
metrics: {
|
|
317
|
+
...totals,
|
|
318
|
+
costUsd: costObserved ? totals.usage.costUsd : undefined,
|
|
319
|
+
elapsedMs: performance.now() - started,
|
|
320
|
+
},
|
|
321
|
+
auxiliary,
|
|
322
|
+
total: known(1),
|
|
323
|
+
refused: Boolean(
|
|
324
|
+
input.refusal &&
|
|
325
|
+
(failureCause === "invalid_root" ||
|
|
326
|
+
failureCause === "forbidden_path"),
|
|
327
|
+
),
|
|
328
|
+
missingWork: unchecked.length > 0,
|
|
329
|
+
});
|
|
104
330
|
runtime.session.record(envelope);
|
|
105
331
|
runtime.guide.deliver(ctx);
|
|
106
332
|
return {
|
|
107
|
-
content: [
|
|
108
|
-
|
|
333
|
+
content: [
|
|
334
|
+
{
|
|
335
|
+
type: "text" as const,
|
|
336
|
+
text: renderResultReport(report, {
|
|
337
|
+
details: {
|
|
338
|
+
lines: [
|
|
339
|
+
...envelope.lines,
|
|
340
|
+
...(entryLine?.type === "entry"
|
|
341
|
+
? [
|
|
342
|
+
{
|
|
343
|
+
type: "list" as const,
|
|
344
|
+
title: "read",
|
|
345
|
+
items: [...(entryLine.nextRead ?? [])],
|
|
346
|
+
},
|
|
347
|
+
{
|
|
348
|
+
type: "list" as const,
|
|
349
|
+
title: "auxiliary ranking",
|
|
350
|
+
items: (entryLine.relevant ?? []).map(
|
|
351
|
+
(candidate) =>
|
|
352
|
+
`${candidate.label} (${candidate.value.p})`,
|
|
353
|
+
),
|
|
354
|
+
},
|
|
355
|
+
]
|
|
356
|
+
: []),
|
|
357
|
+
{
|
|
358
|
+
type: "list" as const,
|
|
359
|
+
title: "discovery reservations",
|
|
360
|
+
items: [...limitations, ...(input.limitations ?? [])].map(
|
|
361
|
+
(limit) => limit.fact,
|
|
362
|
+
),
|
|
363
|
+
},
|
|
364
|
+
],
|
|
365
|
+
},
|
|
366
|
+
}),
|
|
367
|
+
},
|
|
368
|
+
],
|
|
369
|
+
details: { ...totals, evidenceContext, result: report },
|
|
109
370
|
...hostUsage(host.isOmp, totals.usage),
|
|
110
371
|
};
|
|
111
372
|
};
|
|
112
|
-
if (!
|
|
373
|
+
if (!evidence.ok) {
|
|
374
|
+
failureCause = evidence.cause ?? "invalid_root";
|
|
375
|
+
return finish({ refusal: evidence.error });
|
|
376
|
+
}
|
|
377
|
+
if (!client) {
|
|
378
|
+
failureCause = "not_configured";
|
|
379
|
+
return finish({ refusal: NOT_CONFIGURED });
|
|
380
|
+
}
|
|
113
381
|
const words = contentWords(args.goal);
|
|
114
382
|
const short = words.length < C.FIND_GOAL_MIN_WORDS;
|
|
115
383
|
if (short)
|
|
@@ -139,7 +407,30 @@ export function createFindFilesTool(dependencies: ToolDependencies) {
|
|
|
139
407
|
args.exclude,
|
|
140
408
|
signal,
|
|
141
409
|
);
|
|
142
|
-
if (!candidates.ok)
|
|
410
|
+
if (!candidates.ok) {
|
|
411
|
+
failureCause = candidates.cause ?? "git_failure";
|
|
412
|
+
return finish({ refusal: candidates.error });
|
|
413
|
+
}
|
|
414
|
+
for (const path of candidates.secret)
|
|
415
|
+
diagnostic("secret_pattern", secretRefusal(path), [path]);
|
|
416
|
+
context.inventories.push({
|
|
417
|
+
id: "candidates",
|
|
418
|
+
kind: "repository",
|
|
419
|
+
rules: [
|
|
420
|
+
"Git tracked and non-ignored untracked files",
|
|
421
|
+
"keyword prefilter with existing effort limit",
|
|
422
|
+
],
|
|
423
|
+
restrictions: [
|
|
424
|
+
...(typeof args.scope === "string"
|
|
425
|
+
? [args.scope]
|
|
426
|
+
: (args.scope ?? [])),
|
|
427
|
+
...(args.exclude ?? []).map((path) => `exclude ${path}`),
|
|
428
|
+
],
|
|
429
|
+
discovered: known(candidates.scopeFiles),
|
|
430
|
+
considered: known(candidates.paths.length),
|
|
431
|
+
scopeRestricted: Boolean(args.scope || args.exclude?.length),
|
|
432
|
+
criteria: [],
|
|
433
|
+
});
|
|
143
434
|
const entry = (
|
|
144
435
|
path: string,
|
|
145
436
|
p: number,
|
|
@@ -175,21 +466,24 @@ export function createFindFilesTool(dependencies: ToolDependencies) {
|
|
|
175
466
|
],
|
|
176
467
|
});
|
|
177
468
|
if (!candidates.paths.length) {
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
});
|
|
185
|
-
return finish({
|
|
186
|
-
lines: [entry("none", 1, short ? "unsure" : "verdict")],
|
|
187
|
-
});
|
|
469
|
+
diagnostic(
|
|
470
|
+
"collection_empty",
|
|
471
|
+
candidates.scopeFiles === 0
|
|
472
|
+
? "scope matched 0 files; no entry judgment"
|
|
473
|
+
: "no file shares a word with the goal; no entry judgment",
|
|
474
|
+
);
|
|
475
|
+
return finish({});
|
|
188
476
|
}
|
|
189
477
|
const judge = async (
|
|
190
478
|
state: State,
|
|
191
479
|
questions: Record<string, Question>,
|
|
192
480
|
): Promise<Judgment> => {
|
|
481
|
+
state = withEvidenceContext(state, evidenceContext);
|
|
482
|
+
if (JSON.stringify(state).length > C.STATE_MAX_CHARS)
|
|
483
|
+
return {
|
|
484
|
+
ok: false,
|
|
485
|
+
error: `required evidence exceeds STATE_MAX_CHARS=${C.STATE_MAX_CHARS}`,
|
|
486
|
+
};
|
|
193
487
|
const result = await client.judge(state, questions, {
|
|
194
488
|
signal,
|
|
195
489
|
...runtime.session.requestGate(),
|
|
@@ -210,6 +504,8 @@ export function createFindFilesTool(dependencies: ToolDependencies) {
|
|
|
210
504
|
return admission;
|
|
211
505
|
},
|
|
212
506
|
onUsage: (usage) => runtime.session.recordUsage(usage),
|
|
507
|
+
admissionCause: () =>
|
|
508
|
+
budget?.kind === "max_calls" ? "call_budget" : "session_budget",
|
|
213
509
|
});
|
|
214
510
|
for (const key of [
|
|
215
511
|
"calls",
|
|
@@ -219,7 +515,21 @@ export function createFindFilesTool(dependencies: ToolDependencies) {
|
|
|
219
515
|
] as const)
|
|
220
516
|
totals[key] += result[key] ?? 0;
|
|
221
517
|
totals.usage.inputTokens += result.usage?.inputTokens ?? 0;
|
|
518
|
+
if (result.calls && !result.usage) costObserved = false;
|
|
519
|
+
if (!result.ok) {
|
|
520
|
+
failureCause = result.failureCause ?? "invalid_response";
|
|
521
|
+
auxiliary.passages.notJudged += Object.keys(questions).length;
|
|
522
|
+
}
|
|
222
523
|
totals.usage.costUsd += result.usage?.costUsd ?? 0;
|
|
524
|
+
if (result.ok)
|
|
525
|
+
for (const id of Object.keys(questions)) {
|
|
526
|
+
const answer = result.answers[id];
|
|
527
|
+
if (answer?.type === "unjudged") {
|
|
528
|
+
failureCause = answer.cause ?? "invalid_response";
|
|
529
|
+
auxiliary.passages.notJudged++;
|
|
530
|
+
} else if (answer?.source) auxiliary.passages[answer.source]++;
|
|
531
|
+
else auxiliary.passages.notJudged++;
|
|
532
|
+
}
|
|
223
533
|
return result;
|
|
224
534
|
};
|
|
225
535
|
const boolQuestions = (
|
|
@@ -270,6 +580,12 @@ export function createFindFilesTool(dependencies: ToolDependencies) {
|
|
|
270
580
|
const rankedNames = rankFiles(names);
|
|
271
581
|
let bestRanking = rankedNames;
|
|
272
582
|
const fallback = () => {
|
|
583
|
+
primary = undefined;
|
|
584
|
+
if (bestRanking.length)
|
|
585
|
+
limitations.push({
|
|
586
|
+
fact: `auxiliary ranking retained: ${bestRanking.map((item) => `${item.path} (${item.p})`).join(" · ")}; entry was not judged`,
|
|
587
|
+
next: "inspect these paths natively",
|
|
588
|
+
});
|
|
273
589
|
const top = bestRanking.slice(0, 2);
|
|
274
590
|
return finish({
|
|
275
591
|
lines: [
|
|
@@ -317,7 +633,10 @@ export function createFindFilesTool(dependencies: ToolDependencies) {
|
|
|
317
633
|
signal,
|
|
318
634
|
exec,
|
|
319
635
|
);
|
|
320
|
-
if (!read.ok)
|
|
636
|
+
if (!read.ok) {
|
|
637
|
+
failureCause = read.cause ?? "file_unavailable";
|
|
638
|
+
return { paths, result: read };
|
|
639
|
+
}
|
|
321
640
|
Object.assign(files, read.pointers);
|
|
322
641
|
limitations.push(
|
|
323
642
|
...read.limits.map((limit) => ({
|
|
@@ -325,11 +644,18 @@ export function createFindFilesTool(dependencies: ToolDependencies) {
|
|
|
325
644
|
next: "provide a UTF-8 text file",
|
|
326
645
|
})),
|
|
327
646
|
);
|
|
328
|
-
if (read.ignored.length)
|
|
647
|
+
if (read.ignored.length) {
|
|
648
|
+
for (const omission of read.omissions)
|
|
649
|
+
diagnostic(
|
|
650
|
+
omission.cause,
|
|
651
|
+
`${omission.path}: ${omission.reason}`,
|
|
652
|
+
[omission.path],
|
|
653
|
+
);
|
|
329
654
|
limitations.push({
|
|
330
655
|
fact: `${read.ignored.length} binary, empty or unreadable paths ignored: ${read.ignored.join(", ")}`,
|
|
331
656
|
next: "narrow scope to text files",
|
|
332
657
|
});
|
|
658
|
+
}
|
|
333
659
|
const textPaths = paths.filter(
|
|
334
660
|
(path) => read.files[path] !== undefined,
|
|
335
661
|
);
|
|
@@ -398,12 +724,26 @@ export function createFindFilesTool(dependencies: ToolDependencies) {
|
|
|
398
724
|
],
|
|
399
725
|
});
|
|
400
726
|
if (!retained.length) {
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
return
|
|
727
|
+
diagnostic(
|
|
728
|
+
"collection_empty",
|
|
729
|
+
"no file retained by content or names guard; no entry decision was judged",
|
|
730
|
+
);
|
|
731
|
+
return finish({});
|
|
406
732
|
}
|
|
733
|
+
entryPaths = retained.map((item) => item.path);
|
|
734
|
+
context.inventories.push({
|
|
735
|
+
id: "entry-passages",
|
|
736
|
+
kind: "repository",
|
|
737
|
+
rules: [
|
|
738
|
+
"name ranking then bounded passage ranking",
|
|
739
|
+
"existing content and names guard",
|
|
740
|
+
],
|
|
741
|
+
restrictions: entryPaths,
|
|
742
|
+
discovered: known(content.length),
|
|
743
|
+
considered: known(retained.length),
|
|
744
|
+
scopeRestricted: true,
|
|
745
|
+
criteria: [],
|
|
746
|
+
});
|
|
407
747
|
const idByPath = Object.fromEntries(
|
|
408
748
|
retained.map((item, index) => [item.path, `c${index + 1}`]),
|
|
409
749
|
);
|
|
@@ -437,6 +777,7 @@ export function createFindFilesTool(dependencies: ToolDependencies) {
|
|
|
437
777
|
if (!firstResult.ok && !budget)
|
|
438
778
|
return finish({ refusal: firstResult.error });
|
|
439
779
|
const first = firstResult.ok ? firstResult.answers.entry : undefined;
|
|
780
|
+
primary = first;
|
|
440
781
|
if (first?.type !== "choice") {
|
|
441
782
|
unchecked.push("entry");
|
|
442
783
|
return fallback();
|
|
@@ -459,14 +800,30 @@ export function createFindFilesTool(dependencies: ToolDependencies) {
|
|
|
459
800
|
(args.effort !== "quick" &&
|
|
460
801
|
needsReverse(first.probabilities, retained.length)))
|
|
461
802
|
) {
|
|
803
|
+
confirmationRequired = true;
|
|
462
804
|
const secondResult = await pointer([...paths].reverse());
|
|
463
|
-
if (!secondResult.ok && !budget)
|
|
805
|
+
if (!secondResult.ok && !budget) {
|
|
806
|
+
auxiliary.passages.notJudged--;
|
|
807
|
+
auxiliary.controls.notJudged++;
|
|
464
808
|
return finish({ refusal: secondResult.error });
|
|
809
|
+
}
|
|
465
810
|
const second = secondResult.ok ? secondResult.answers.entry : undefined;
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
811
|
+
confirmation = second;
|
|
812
|
+
if (second?.type === "choice" && second.source) {
|
|
813
|
+
auxiliary.passages[second.source]--;
|
|
814
|
+
auxiliary.controls[second.source]++;
|
|
469
815
|
} else {
|
|
816
|
+
auxiliary.passages.notJudged--;
|
|
817
|
+
auxiliary.controls.notJudged++;
|
|
818
|
+
diagnostic(
|
|
819
|
+
second?.type === "unjudged"
|
|
820
|
+
? (second.cause ?? "control_failure")
|
|
821
|
+
: "control_failure",
|
|
822
|
+
"required reversed-order entry control was not judged",
|
|
823
|
+
);
|
|
824
|
+
return fallback();
|
|
825
|
+
}
|
|
826
|
+
{
|
|
470
827
|
const secondRanks = ranked(second.probabilities);
|
|
471
828
|
const combined = readPointer(
|
|
472
829
|
first.probabilities,
|