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/dist/tools/find.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Type } from "@sinclair/typebox";
|
|
2
|
-
import {
|
|
2
|
+
import { resolveEvidenceContext, withEvidenceContext, } from "../adapters/evidence-context.js";
|
|
3
3
|
import { collectSearchExcerpts } from "../adapters/files.js";
|
|
4
4
|
import { prefilter } from "../adapters/find.js";
|
|
5
5
|
import { shareGitInventory } from "../adapters/git-inventory.js";
|
|
@@ -8,11 +8,14 @@ import * as C from "../constants.js";
|
|
|
8
8
|
import { contentWords, findKeywords, rankFiles, retainFiles, } from "../core/find.js";
|
|
9
9
|
import { buildEnvelope, } from "../core/output.js";
|
|
10
10
|
import { needsReverse, pointerQuestion, readPointer } from "../core/pointer.js";
|
|
11
|
+
import { answerItemFromInput, buildResultReport, contextFromEvidence, known, } from "../core/result-report.js";
|
|
12
|
+
import { secretRefusal } from "../core/secret-path.js";
|
|
11
13
|
import { interpolate } from "../describe.js";
|
|
12
|
-
import {
|
|
14
|
+
import { renderResultReport } from "../render.js";
|
|
13
15
|
import { NOT_CONFIGURED } from "../texts/configuration.js";
|
|
14
16
|
import { FIND_DESCRIPTION } from "../texts/find.js";
|
|
15
17
|
export const findParameters = Type.Object({
|
|
18
|
+
root: Type.Optional(Type.String()),
|
|
16
19
|
goal: Type.String({ minLength: 1 }),
|
|
17
20
|
keywords: Type.Optional(Type.Array(Type.String({ minLength: 1 }))),
|
|
18
21
|
scope: Type.Optional(Type.Union([Type.String(), Type.Array(Type.String())])),
|
|
@@ -45,7 +48,13 @@ export function createFindFilesTool(dependencies) {
|
|
|
45
48
|
}),
|
|
46
49
|
async execute(_id, args, signal, _update, ctx) {
|
|
47
50
|
const client = dependencies.client;
|
|
48
|
-
const
|
|
51
|
+
const evidence = await resolveEvidenceContext(ctx.cwd, args.root, {
|
|
52
|
+
exec: execute,
|
|
53
|
+
signal,
|
|
54
|
+
origin: dependencies.evidenceOrigin,
|
|
55
|
+
});
|
|
56
|
+
const evidenceContext = evidence.context;
|
|
57
|
+
const cwd = evidence.ok ? evidence.cwd : evidenceContext.authority.path;
|
|
49
58
|
const started = performance.now();
|
|
50
59
|
const exec = shareGitInventory(execute);
|
|
51
60
|
const totals = {
|
|
@@ -59,6 +68,88 @@ export function createFindFilesTool(dependencies) {
|
|
|
59
68
|
const unchecked = [];
|
|
60
69
|
let budget;
|
|
61
70
|
let sent = 0;
|
|
71
|
+
const context = contextFromEvidence(evidenceContext);
|
|
72
|
+
const diagnostics = [];
|
|
73
|
+
const actions = [];
|
|
74
|
+
const auxiliary = {
|
|
75
|
+
controls: { fresh: 0, cache: 0, notJudged: 0, static: 0 },
|
|
76
|
+
passages: { fresh: 0, cache: 0, notJudged: 0 },
|
|
77
|
+
};
|
|
78
|
+
let entryPaths = [];
|
|
79
|
+
let costObserved = true;
|
|
80
|
+
let failureCause = "invalid_response";
|
|
81
|
+
let primary;
|
|
82
|
+
let confirmation;
|
|
83
|
+
let confirmationRequired = false;
|
|
84
|
+
const raw = (answer) => answer.type === "choice"
|
|
85
|
+
? Object.entries(answer.probabilities).map(([label, probability]) => ({
|
|
86
|
+
label,
|
|
87
|
+
value: label,
|
|
88
|
+
probability: known(probability),
|
|
89
|
+
}))
|
|
90
|
+
: [];
|
|
91
|
+
const diagnostic = (cause, fact, members = []) => {
|
|
92
|
+
const id = `d${diagnostics.length + 1}`;
|
|
93
|
+
const actionId = `a${actions.length + 1}`;
|
|
94
|
+
const code = cause === "invalid_root"
|
|
95
|
+
? "correct_context"
|
|
96
|
+
: cause === "not_configured"
|
|
97
|
+
? "configure_client"
|
|
98
|
+
: cause === "service_unavailable" || cause === "transport_failure"
|
|
99
|
+
? "recover_service"
|
|
100
|
+
: "inspect_native";
|
|
101
|
+
actions.push({
|
|
102
|
+
id: actionId,
|
|
103
|
+
code,
|
|
104
|
+
target: known(members.join(", ") || "entry"),
|
|
105
|
+
scope: { kind: "call" },
|
|
106
|
+
condition: code === "correct_context"
|
|
107
|
+
? "If the requested root is incorrect"
|
|
108
|
+
: code === "configure_client"
|
|
109
|
+
? "If Jev judgment is still useful after configuring the client"
|
|
110
|
+
: code === "recover_service"
|
|
111
|
+
? "Only after service recovery is observed"
|
|
112
|
+
: "If the entry decision remains open",
|
|
113
|
+
instruction: code === "correct_context"
|
|
114
|
+
? "Provide the exact registered worktree top-level; no fallback was performed."
|
|
115
|
+
: code === "configure_client"
|
|
116
|
+
? "Configure the Jev client, or continue with native discovery."
|
|
117
|
+
: code === "recover_service"
|
|
118
|
+
? "Continue natively while unavailable; revisit only after a material service change."
|
|
119
|
+
: "Inspect the candidate paths or collected passages natively; retain uncertainty without repeating unchanged evidence.",
|
|
120
|
+
repeatUnchanged: false,
|
|
121
|
+
});
|
|
122
|
+
diagnostics.push({
|
|
123
|
+
id,
|
|
124
|
+
cause,
|
|
125
|
+
fact,
|
|
126
|
+
target: known(members.join(", ") ||
|
|
127
|
+
(cause === "invalid_root"
|
|
128
|
+
? (args.root ?? cwd)
|
|
129
|
+
: cause === "forbidden_path"
|
|
130
|
+
? JSON.stringify({ scope: args.scope, exclude: args.exclude })
|
|
131
|
+
: "entry")),
|
|
132
|
+
origin: cause === "invalid_root" || cause === "forbidden_path"
|
|
133
|
+
? "input"
|
|
134
|
+
: cause === "call_budget" || cause === "session_budget"
|
|
135
|
+
? "budget"
|
|
136
|
+
: cause === "control_failure"
|
|
137
|
+
? "control"
|
|
138
|
+
: cause === "service_unavailable" ||
|
|
139
|
+
cause === "transport_failure" ||
|
|
140
|
+
cause === "invalid_response" ||
|
|
141
|
+
cause === "provider_context_refusal" ||
|
|
142
|
+
cause === "not_configured"
|
|
143
|
+
? "provider"
|
|
144
|
+
: "collection",
|
|
145
|
+
scope: { kind: "call" },
|
|
146
|
+
effect: "blocking",
|
|
147
|
+
material: true,
|
|
148
|
+
omittedMembers: members,
|
|
149
|
+
memberCount: known(members.length),
|
|
150
|
+
actionIds: [actionId],
|
|
151
|
+
});
|
|
152
|
+
};
|
|
62
153
|
const finish = (input) => {
|
|
63
154
|
const envelope = buildEnvelope({
|
|
64
155
|
...input,
|
|
@@ -70,16 +161,140 @@ export function createFindFilesTool(dependencies) {
|
|
|
70
161
|
elapsedMs: performance.now() - started,
|
|
71
162
|
},
|
|
72
163
|
});
|
|
164
|
+
if (input.refusal)
|
|
165
|
+
diagnostic(failureCause, input.refusal);
|
|
166
|
+
if (!input.refusal &&
|
|
167
|
+
!diagnostics.length &&
|
|
168
|
+
!budget &&
|
|
169
|
+
!unchecked.length &&
|
|
170
|
+
!primary?.source)
|
|
171
|
+
diagnostic("invalid_response", "entry decision was not received with established answer provenance");
|
|
172
|
+
if (budget)
|
|
173
|
+
diagnostic(budget.kind === "max_calls" ? "call_budget" : "session_budget", budget.message, [...new Set(unchecked)]);
|
|
174
|
+
if (unchecked.length && !budget)
|
|
175
|
+
diagnostic(failureCause, "candidate ranking or entry work was not judged", [...new Set(unchecked)]);
|
|
176
|
+
const entryLine = input.lines?.find((line) => line.type === "entry");
|
|
177
|
+
if (entryLine?.type === "entry" && primary?.source)
|
|
178
|
+
actions.push({
|
|
179
|
+
id: `a${actions.length + 1}`,
|
|
180
|
+
code: "inspect_native",
|
|
181
|
+
target: known((entryLine.nextRead ?? []).join(", ") || "candidate inventory"),
|
|
182
|
+
scope: { kind: "group", groupIds: ["entry"] },
|
|
183
|
+
condition: "If the entry decision remains open after this bounded judgment",
|
|
184
|
+
instruction: entryLine.candidate.label === "none"
|
|
185
|
+
? "Search elsewhere or widen the admitted scope for the goal; inspect any named alternative before collecting genuinely new evidence. Do not retry unchanged evidence."
|
|
186
|
+
: "Read the named candidate passages; add only genuinely discriminating evidence if needed. Do not reword unchanged evidence to retry.",
|
|
187
|
+
repeatUnchanged: false,
|
|
188
|
+
});
|
|
189
|
+
const common = {
|
|
190
|
+
id: "entry",
|
|
191
|
+
kind: "entry",
|
|
192
|
+
label: "entry",
|
|
193
|
+
groupId: "entry",
|
|
194
|
+
evidence: entryPaths.map((path) => ({
|
|
195
|
+
target: path,
|
|
196
|
+
canonicalPath: known(`${cwd}/${path}`),
|
|
197
|
+
aliases: [],
|
|
198
|
+
side: "current",
|
|
199
|
+
revision: context.resolvedBase,
|
|
200
|
+
})),
|
|
201
|
+
diagnosticIds: diagnostics.map((d) => d.id),
|
|
202
|
+
actionIds: actions.map((a) => a.id),
|
|
203
|
+
};
|
|
204
|
+
let item = { ...common, treatment: "not_judged", source: "none" };
|
|
205
|
+
if (entryLine?.type === "entry" &&
|
|
206
|
+
primary?.type === "choice" &&
|
|
207
|
+
primary.source &&
|
|
208
|
+
(!confirmationRequired ||
|
|
209
|
+
(confirmation?.type === "choice" && confirmation.source))) {
|
|
210
|
+
auxiliary.passages[primary.source]--;
|
|
211
|
+
item = answerItemFromInput(common, {
|
|
212
|
+
label: "entry",
|
|
213
|
+
answer: primary,
|
|
214
|
+
value: entryLine.candidate.value,
|
|
215
|
+
publicResult: entryLine.candidate.value.head,
|
|
216
|
+
band: entryLine.band,
|
|
217
|
+
reason: limitations.map((limit) => limit.fact).join("; "),
|
|
218
|
+
rawValues: raw(primary),
|
|
219
|
+
reportControls: confirmation?.type === "choice" && confirmation.source
|
|
220
|
+
? [
|
|
221
|
+
{
|
|
222
|
+
id: "entry-order",
|
|
223
|
+
kind: "order",
|
|
224
|
+
source: confirmation.source,
|
|
225
|
+
outcome: entryLine.orderDependent
|
|
226
|
+
? "order-dependent"
|
|
227
|
+
: "consistent",
|
|
228
|
+
rawValues: raw(confirmation),
|
|
229
|
+
},
|
|
230
|
+
]
|
|
231
|
+
: [],
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
const report = buildResultReport({
|
|
235
|
+
tool: "jev_find_files",
|
|
236
|
+
context,
|
|
237
|
+
items: [item],
|
|
238
|
+
diagnostics,
|
|
239
|
+
actions,
|
|
240
|
+
metrics: {
|
|
241
|
+
...totals,
|
|
242
|
+
costUsd: costObserved ? totals.usage.costUsd : undefined,
|
|
243
|
+
elapsedMs: performance.now() - started,
|
|
244
|
+
},
|
|
245
|
+
auxiliary,
|
|
246
|
+
total: known(1),
|
|
247
|
+
refused: Boolean(input.refusal &&
|
|
248
|
+
(failureCause === "invalid_root" ||
|
|
249
|
+
failureCause === "forbidden_path")),
|
|
250
|
+
missingWork: unchecked.length > 0,
|
|
251
|
+
});
|
|
73
252
|
runtime.session.record(envelope);
|
|
74
253
|
runtime.guide.deliver(ctx);
|
|
75
254
|
return {
|
|
76
|
-
content: [
|
|
77
|
-
|
|
255
|
+
content: [
|
|
256
|
+
{
|
|
257
|
+
type: "text",
|
|
258
|
+
text: renderResultReport(report, {
|
|
259
|
+
details: {
|
|
260
|
+
lines: [
|
|
261
|
+
...envelope.lines,
|
|
262
|
+
...(entryLine?.type === "entry"
|
|
263
|
+
? [
|
|
264
|
+
{
|
|
265
|
+
type: "list",
|
|
266
|
+
title: "read",
|
|
267
|
+
items: [...(entryLine.nextRead ?? [])],
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
type: "list",
|
|
271
|
+
title: "auxiliary ranking",
|
|
272
|
+
items: (entryLine.relevant ?? []).map((candidate) => `${candidate.label} (${candidate.value.p})`),
|
|
273
|
+
},
|
|
274
|
+
]
|
|
275
|
+
: []),
|
|
276
|
+
{
|
|
277
|
+
type: "list",
|
|
278
|
+
title: "discovery reservations",
|
|
279
|
+
items: [...limitations, ...(input.limitations ?? [])].map((limit) => limit.fact),
|
|
280
|
+
},
|
|
281
|
+
],
|
|
282
|
+
},
|
|
283
|
+
}),
|
|
284
|
+
},
|
|
285
|
+
],
|
|
286
|
+
details: { ...totals, evidenceContext, result: report },
|
|
78
287
|
...hostUsage(host.isOmp, totals.usage),
|
|
79
288
|
};
|
|
80
289
|
};
|
|
81
|
-
if (!
|
|
290
|
+
if (!evidence.ok) {
|
|
291
|
+
failureCause = evidence.cause ?? "invalid_root";
|
|
292
|
+
return finish({ refusal: evidence.error });
|
|
293
|
+
}
|
|
294
|
+
if (!client) {
|
|
295
|
+
failureCause = "not_configured";
|
|
82
296
|
return finish({ refusal: NOT_CONFIGURED });
|
|
297
|
+
}
|
|
83
298
|
const words = contentWords(args.goal);
|
|
84
299
|
const short = words.length < C.FIND_GOAL_MIN_WORDS;
|
|
85
300
|
if (short)
|
|
@@ -99,8 +314,30 @@ export function createFindFilesTool(dependencies) {
|
|
|
99
314
|
? C.FIND_THOROUGH_READ
|
|
100
315
|
: C.FIND_FILES_READ;
|
|
101
316
|
const candidates = await prefilter(exec, cwd, keywords, candidateLimit, args.scope, args.exclude, signal);
|
|
102
|
-
if (!candidates.ok)
|
|
317
|
+
if (!candidates.ok) {
|
|
318
|
+
failureCause = candidates.cause ?? "git_failure";
|
|
103
319
|
return finish({ refusal: candidates.error });
|
|
320
|
+
}
|
|
321
|
+
for (const path of candidates.secret)
|
|
322
|
+
diagnostic("secret_pattern", secretRefusal(path), [path]);
|
|
323
|
+
context.inventories.push({
|
|
324
|
+
id: "candidates",
|
|
325
|
+
kind: "repository",
|
|
326
|
+
rules: [
|
|
327
|
+
"Git tracked and non-ignored untracked files",
|
|
328
|
+
"keyword prefilter with existing effort limit",
|
|
329
|
+
],
|
|
330
|
+
restrictions: [
|
|
331
|
+
...(typeof args.scope === "string"
|
|
332
|
+
? [args.scope]
|
|
333
|
+
: (args.scope ?? [])),
|
|
334
|
+
...(args.exclude ?? []).map((path) => `exclude ${path}`),
|
|
335
|
+
],
|
|
336
|
+
discovered: known(candidates.scopeFiles),
|
|
337
|
+
considered: known(candidates.paths.length),
|
|
338
|
+
scopeRestricted: Boolean(args.scope || args.exclude?.length),
|
|
339
|
+
criteria: [],
|
|
340
|
+
});
|
|
104
341
|
const entry = (path, p, band, alternatives = [], orderDependent = false, orderProbabilities, relevant = []) => ({
|
|
105
342
|
type: "entry",
|
|
106
343
|
candidate: { label: path, value: { head: path, p } },
|
|
@@ -127,17 +364,18 @@ export function createFindFilesTool(dependencies) {
|
|
|
127
364
|
],
|
|
128
365
|
});
|
|
129
366
|
if (!candidates.paths.length) {
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
next: "rephrase goal or widen scope",
|
|
135
|
-
});
|
|
136
|
-
return finish({
|
|
137
|
-
lines: [entry("none", 1, short ? "unsure" : "verdict")],
|
|
138
|
-
});
|
|
367
|
+
diagnostic("collection_empty", candidates.scopeFiles === 0
|
|
368
|
+
? "scope matched 0 files; no entry judgment"
|
|
369
|
+
: "no file shares a word with the goal; no entry judgment");
|
|
370
|
+
return finish({});
|
|
139
371
|
}
|
|
140
372
|
const judge = async (state, questions) => {
|
|
373
|
+
state = withEvidenceContext(state, evidenceContext);
|
|
374
|
+
if (JSON.stringify(state).length > C.STATE_MAX_CHARS)
|
|
375
|
+
return {
|
|
376
|
+
ok: false,
|
|
377
|
+
error: `required evidence exceeds STATE_MAX_CHARS=${C.STATE_MAX_CHARS}`,
|
|
378
|
+
};
|
|
141
379
|
const result = await client.judge(state, questions, {
|
|
142
380
|
signal,
|
|
143
381
|
...runtime.session.requestGate(),
|
|
@@ -158,6 +396,7 @@ export function createFindFilesTool(dependencies) {
|
|
|
158
396
|
return admission;
|
|
159
397
|
},
|
|
160
398
|
onUsage: (usage) => runtime.session.recordUsage(usage),
|
|
399
|
+
admissionCause: () => budget?.kind === "max_calls" ? "call_budget" : "session_budget",
|
|
161
400
|
});
|
|
162
401
|
for (const key of [
|
|
163
402
|
"calls",
|
|
@@ -167,7 +406,25 @@ export function createFindFilesTool(dependencies) {
|
|
|
167
406
|
])
|
|
168
407
|
totals[key] += result[key] ?? 0;
|
|
169
408
|
totals.usage.inputTokens += result.usage?.inputTokens ?? 0;
|
|
409
|
+
if (result.calls && !result.usage)
|
|
410
|
+
costObserved = false;
|
|
411
|
+
if (!result.ok) {
|
|
412
|
+
failureCause = result.failureCause ?? "invalid_response";
|
|
413
|
+
auxiliary.passages.notJudged += Object.keys(questions).length;
|
|
414
|
+
}
|
|
170
415
|
totals.usage.costUsd += result.usage?.costUsd ?? 0;
|
|
416
|
+
if (result.ok)
|
|
417
|
+
for (const id of Object.keys(questions)) {
|
|
418
|
+
const answer = result.answers[id];
|
|
419
|
+
if (answer?.type === "unjudged") {
|
|
420
|
+
failureCause = answer.cause ?? "invalid_response";
|
|
421
|
+
auxiliary.passages.notJudged++;
|
|
422
|
+
}
|
|
423
|
+
else if (answer?.source)
|
|
424
|
+
auxiliary.passages[answer.source]++;
|
|
425
|
+
else
|
|
426
|
+
auxiliary.passages.notJudged++;
|
|
427
|
+
}
|
|
171
428
|
return result;
|
|
172
429
|
};
|
|
173
430
|
const boolQuestions = (paths, content) => Object.fromEntries(paths.map((path) => [
|
|
@@ -203,6 +460,12 @@ export function createFindFilesTool(dependencies) {
|
|
|
203
460
|
const rankedNames = rankFiles(names);
|
|
204
461
|
let bestRanking = rankedNames;
|
|
205
462
|
const fallback = () => {
|
|
463
|
+
primary = undefined;
|
|
464
|
+
if (bestRanking.length)
|
|
465
|
+
limitations.push({
|
|
466
|
+
fact: `auxiliary ranking retained: ${bestRanking.map((item) => `${item.path} (${item.p})`).join(" · ")}; entry was not judged`,
|
|
467
|
+
next: "inspect these paths natively",
|
|
468
|
+
});
|
|
206
469
|
const top = bestRanking.slice(0, 2);
|
|
207
470
|
return finish({
|
|
208
471
|
lines: [
|
|
@@ -227,18 +490,23 @@ export function createFindFilesTool(dependencies) {
|
|
|
227
490
|
const contentBatches = Array.from({ length: Math.ceil(readPaths.length / C.FIND_READ_PER_CALL) }, (_, index) => readPaths.slice(index * C.FIND_READ_PER_CALL, (index + 1) * C.FIND_READ_PER_CALL));
|
|
228
491
|
const contentResults = await Promise.all(contentBatches.map(async (paths) => {
|
|
229
492
|
const read = await collectSearchExcerpts(cwd, paths, keywords, signal, exec);
|
|
230
|
-
if (!read.ok)
|
|
493
|
+
if (!read.ok) {
|
|
494
|
+
failureCause = read.cause ?? "file_unavailable";
|
|
231
495
|
return { paths, result: read };
|
|
496
|
+
}
|
|
232
497
|
Object.assign(files, read.pointers);
|
|
233
498
|
limitations.push(...read.limits.map((limit) => ({
|
|
234
499
|
fact: `${limit.path}: ${limit.reason}`,
|
|
235
500
|
next: "provide a UTF-8 text file",
|
|
236
501
|
})));
|
|
237
|
-
if (read.ignored.length)
|
|
502
|
+
if (read.ignored.length) {
|
|
503
|
+
for (const omission of read.omissions)
|
|
504
|
+
diagnostic(omission.cause, `${omission.path}: ${omission.reason}`, [omission.path]);
|
|
238
505
|
limitations.push({
|
|
239
506
|
fact: `${read.ignored.length} binary, empty or unreadable paths ignored: ${read.ignored.join(", ")}`,
|
|
240
507
|
next: "narrow scope to text files",
|
|
241
508
|
});
|
|
509
|
+
}
|
|
242
510
|
const textPaths = paths.filter((path) => read.files[path] !== undefined);
|
|
243
511
|
if (!textPaths.length)
|
|
244
512
|
return { paths: [], result: { ok: true, answers: {} } };
|
|
@@ -279,12 +547,23 @@ export function createFindFilesTool(dependencies) {
|
|
|
279
547
|
],
|
|
280
548
|
});
|
|
281
549
|
if (!retained.length) {
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
next: "rephrase goal or widen scope",
|
|
285
|
-
});
|
|
286
|
-
return output("none", 1, short ? "unsure" : "verdict");
|
|
550
|
+
diagnostic("collection_empty", "no file retained by content or names guard; no entry decision was judged");
|
|
551
|
+
return finish({});
|
|
287
552
|
}
|
|
553
|
+
entryPaths = retained.map((item) => item.path);
|
|
554
|
+
context.inventories.push({
|
|
555
|
+
id: "entry-passages",
|
|
556
|
+
kind: "repository",
|
|
557
|
+
rules: [
|
|
558
|
+
"name ranking then bounded passage ranking",
|
|
559
|
+
"existing content and names guard",
|
|
560
|
+
],
|
|
561
|
+
restrictions: entryPaths,
|
|
562
|
+
discovered: known(content.length),
|
|
563
|
+
considered: known(retained.length),
|
|
564
|
+
scopeRestricted: true,
|
|
565
|
+
criteria: [],
|
|
566
|
+
});
|
|
288
567
|
const idByPath = Object.fromEntries(retained.map((item, index) => [item.path, `c${index + 1}`]));
|
|
289
568
|
const pathById = Object.fromEntries(retained.map((item, index) => [`c${index + 1}`, item.path]));
|
|
290
569
|
const pointer = async (paths) => {
|
|
@@ -311,6 +590,7 @@ export function createFindFilesTool(dependencies) {
|
|
|
311
590
|
if (!firstResult.ok && !budget)
|
|
312
591
|
return finish({ refusal: firstResult.error });
|
|
313
592
|
const first = firstResult.ok ? firstResult.answers.entry : undefined;
|
|
593
|
+
primary = first;
|
|
314
594
|
if (first?.type !== "choice") {
|
|
315
595
|
unchecked.push("entry");
|
|
316
596
|
return fallback();
|
|
@@ -329,15 +609,28 @@ export function createFindFilesTool(dependencies) {
|
|
|
329
609
|
(args.effort === "thorough" ||
|
|
330
610
|
(args.effort !== "quick" &&
|
|
331
611
|
needsReverse(first.probabilities, retained.length)))) {
|
|
612
|
+
confirmationRequired = true;
|
|
332
613
|
const secondResult = await pointer([...paths].reverse());
|
|
333
|
-
if (!secondResult.ok && !budget)
|
|
614
|
+
if (!secondResult.ok && !budget) {
|
|
615
|
+
auxiliary.passages.notJudged--;
|
|
616
|
+
auxiliary.controls.notJudged++;
|
|
334
617
|
return finish({ refusal: secondResult.error });
|
|
618
|
+
}
|
|
335
619
|
const second = secondResult.ok ? secondResult.answers.entry : undefined;
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
620
|
+
confirmation = second;
|
|
621
|
+
if (second?.type === "choice" && second.source) {
|
|
622
|
+
auxiliary.passages[second.source]--;
|
|
623
|
+
auxiliary.controls[second.source]++;
|
|
339
624
|
}
|
|
340
625
|
else {
|
|
626
|
+
auxiliary.passages.notJudged--;
|
|
627
|
+
auxiliary.controls.notJudged++;
|
|
628
|
+
diagnostic(second?.type === "unjudged"
|
|
629
|
+
? (second.cause ?? "control_failure")
|
|
630
|
+
: "control_failure", "required reversed-order entry control was not judged");
|
|
631
|
+
return fallback();
|
|
632
|
+
}
|
|
633
|
+
{
|
|
341
634
|
const secondRanks = ranked(second.probabilities);
|
|
342
635
|
const combined = readPointer(first.probabilities, second.probabilities);
|
|
343
636
|
ranks = combined.ranked.map(({ id, p }) => ({ path: id, p }));
|