jev-agent-tools 0.1.4 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +106 -1
- package/CONTRIBUTING.md +43 -0
- package/README.md +58 -17
- package/SECURITY.md +43 -0
- package/dist/adapters/analysis-context.js +75 -0
- package/dist/adapters/ask-files.js +198 -0
- package/dist/adapters/ask-proof.js +200 -0
- package/dist/adapters/ask-syntax.js +385 -0
- package/dist/adapters/canonical-path.js +17 -0
- package/dist/adapters/command.js +234 -0
- package/dist/adapters/docs.js +192 -0
- package/dist/adapters/evidence-context.js +119 -0
- package/dist/adapters/exec.js +207 -0
- package/dist/adapters/files.js +418 -0
- package/dist/adapters/find.js +150 -0
- package/dist/adapters/git-base.js +32 -0
- package/dist/adapters/git-inventory.js +71 -0
- package/dist/adapters/git.js +483 -0
- package/dist/adapters/locate-file.js +197 -0
- package/dist/adapters/output-lines.js +46 -0
- package/dist/adapters/private-storage.js +106 -0
- package/dist/adapters/risk-callers.js +429 -0
- package/dist/adapters/runner-version.js +78 -0
- package/dist/adapters/shell.js +92 -0
- package/dist/adapters/syntax.js +187 -0
- package/dist/adapters/test-inventory.js +139 -0
- package/dist/adapters/usage.js +20 -0
- package/dist/adapters/utf8.js +47 -0
- package/dist/configuration.js +267 -0
- package/dist/constants.js +140 -0
- package/dist/core/ask-closure.js +282 -0
- package/dist/core/ask-proof.js +1 -0
- package/dist/core/ask-references.js +278 -0
- package/dist/core/asks.js +507 -0
- package/dist/core/batches.js +65 -0
- package/dist/core/command-output.js +224 -0
- package/dist/core/diff.js +178 -0
- package/dist/core/docs.js +302 -0
- package/dist/core/find.js +108 -0
- package/dist/core/git.js +1 -0
- package/dist/core/imports.js +550 -0
- package/dist/core/integrity.js +45 -0
- package/dist/core/lexical.js +132 -0
- package/dist/core/locate.js +169 -0
- package/dist/core/output.js +137 -0
- package/dist/core/pointer.js +29 -0
- package/dist/core/result-report.js +302 -0
- package/dist/core/risk-callers.js +851 -0
- package/dist/core/runner-version.js +45 -0
- package/dist/core/secret-path.js +34 -0
- package/dist/core/sections.js +230 -0
- package/dist/core/state.js +51 -0
- package/dist/core/syntax.js +1 -0
- package/dist/core/test-commands.js +334 -0
- package/dist/core/test-coverage.js +74 -0
- package/dist/core/test-discovery.js +1382 -0
- package/dist/core/test-evidence.js +527 -0
- package/dist/core/test-state.js +81 -0
- package/dist/core/truncate.js +12 -0
- package/dist/core/units.js +349 -0
- package/dist/describe.js +23 -0
- package/dist/guide.js +33 -0
- package/dist/host.js +24 -0
- package/dist/jev/client.js +456 -0
- package/dist/jev/pool.js +54 -0
- package/dist/jev/types.js +1 -0
- package/dist/mcp/main.js +124 -0
- package/dist/mcp/protocol.js +210 -0
- package/dist/mcp/tools.js +129 -0
- package/dist/presets/docs.js +62 -0
- package/dist/presets/risk.js +179 -0
- package/dist/presets/spec.js +81 -0
- package/dist/presets/witnesses.js +249 -0
- package/dist/render.js +114 -0
- package/dist/report-schema.js +1356 -0
- package/dist/result-types.js +1 -0
- package/dist/result.js +3 -0
- package/dist/runtime.js +1 -0
- package/dist/session.js +147 -0
- package/dist/texts/ask-files.js +3 -0
- package/dist/texts/ask.js +4 -0
- package/dist/texts/check-diff.js +20 -0
- package/dist/texts/configuration.js +1 -0
- package/dist/texts/find.js +19 -0
- package/dist/texts/guide.js +3 -0
- package/dist/texts/instructions.js +72 -0
- package/dist/texts/locate.js +15 -0
- package/dist/texts/select-tests.js +4 -0
- package/dist/tools/ask-files.js +450 -0
- package/dist/tools/ask-schema.js +70 -0
- package/dist/tools/ask.js +1147 -0
- package/dist/tools/check-diff.js +594 -0
- package/dist/tools/docs-check.js +408 -0
- package/dist/tools/find.js +682 -0
- package/dist/tools/locate.js +602 -0
- package/dist/tools/review-report.js +230 -0
- package/dist/tools/select-tests.js +821 -0
- package/dist/tools/spec-check.js +263 -0
- package/docs/adr/0001-strict-typescript-pure-core-offline-tests.md +31 -0
- package/docs/adr/0002-one-http-protocol-across-hosts.md +17 -0
- package/docs/adr/0003-explicit-scope-conservative-automation.md +19 -0
- package/docs/adr/0004-compiled-typed-intents.md +19 -0
- package/docs/adr/0005-evidence-construction-before-judgment.md +19 -0
- package/docs/adr/0006-visible-uncertainty-constrained-controls.md +21 -0
- package/docs/adr/0007-bounded-evidence-visible-limits.md +21 -0
- package/docs/adr/0008-static-test-discovery-conservative-plans.md +19 -0
- package/docs/adr/0009-session-cache-requested-model-identity.md +17 -0
- package/docs/adr/0010-mcp-server-thin-host.md +23 -0
- package/docs/agent-instructions.md +120 -0
- package/docs/design.md +16 -4
- package/docs/mcp.md +233 -0
- 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 +19 -4
- package/rules/jev-ask.md +22 -1
- package/server.json +57 -0
- package/src/adapters/ask-files.ts +11 -3
- package/src/adapters/ask-proof.ts +69 -11
- package/src/adapters/canonical-path.ts +18 -0
- package/src/adapters/command.ts +102 -36
- package/src/adapters/docs.ts +33 -14
- package/src/adapters/evidence-context.ts +169 -0
- package/src/adapters/exec.ts +226 -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 +155 -0
- package/src/adapters/risk-callers.ts +7 -2
- package/src/adapters/shell.ts +113 -0
- package/src/adapters/test-inventory.ts +12 -4
- package/src/configuration.ts +55 -14
- package/src/constants.ts +37 -5
- package/src/core/ask-references.ts +262 -146
- package/src/core/asks.ts +79 -7
- package/src/core/command-output.ts +17 -1
- 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/host.ts +11 -0
- package/src/index.ts +3 -0
- package/src/jev/client.ts +66 -16
- package/src/jev/types.ts +24 -3
- package/src/mcp/main.ts +135 -0
- package/src/mcp/protocol.ts +332 -0
- package/src/mcp/tools.ts +179 -0
- 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/session.ts +59 -0
- package/src/setup.ts +13 -5
- 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 +311 -18
- package/src/tools/ask.ts +722 -95
- package/src/tools/check-diff.ts +337 -31
- package/src/tools/docs-check.ts +241 -38
- package/src/tools/find.ts +389 -29
- package/src/tools/locate.ts +387 -25
- package/src/tools/review-report.ts +308 -0
- package/src/tools/select-tests.ts +484 -23
- package/src/tools/spec-check.ts +194 -19
package/src/tools/find.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import { type Static, Type } from "@sinclair/typebox";
|
|
2
|
+
import {
|
|
3
|
+
resolveEvidenceContext,
|
|
4
|
+
withEvidenceContext,
|
|
5
|
+
} from "../adapters/evidence-context.ts";
|
|
2
6
|
import { collectSearchExcerpts } from "../adapters/files.ts";
|
|
3
7
|
import { prefilter } from "../adapters/find.ts";
|
|
4
8
|
import { shareGitInventory } from "../adapters/git-inventory.ts";
|
|
@@ -18,16 +22,29 @@ import {
|
|
|
18
22
|
type EnvelopeInput,
|
|
19
23
|
} from "../core/output.ts";
|
|
20
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";
|
|
21
37
|
import { interpolate } from "../describe.ts";
|
|
22
38
|
import type { GuideContext } from "../guide.ts";
|
|
23
|
-
import type { Judgment, Question, State } from "../jev/types.ts";
|
|
24
|
-
import {
|
|
39
|
+
import type { Answer, Judgment, Question, State } from "../jev/types.ts";
|
|
40
|
+
import { renderResultReport } from "../render.ts";
|
|
25
41
|
import type { ToolDependencies } from "../runtime.ts";
|
|
26
42
|
import { NOT_CONFIGURED } from "../texts/configuration.ts";
|
|
27
43
|
import { FIND_DESCRIPTION } from "../texts/find.ts";
|
|
28
44
|
|
|
29
45
|
export const findParameters = Type.Object(
|
|
30
46
|
{
|
|
47
|
+
root: Type.Optional(Type.String()),
|
|
31
48
|
goal: Type.String({ minLength: 1 }),
|
|
32
49
|
keywords: Type.Optional(Type.Array(Type.String({ minLength: 1 }))),
|
|
33
50
|
scope: Type.Optional(
|
|
@@ -74,6 +91,13 @@ export function createFindFilesTool(dependencies: ToolDependencies) {
|
|
|
74
91
|
ctx: { cwd: string } & GuideContext,
|
|
75
92
|
) {
|
|
76
93
|
const client = dependencies.client;
|
|
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;
|
|
77
101
|
const started = performance.now();
|
|
78
102
|
const exec = shareGitInventory(execute);
|
|
79
103
|
const totals = {
|
|
@@ -88,6 +112,101 @@ export function createFindFilesTool(dependencies: ToolDependencies) {
|
|
|
88
112
|
const unchecked: string[] = [];
|
|
89
113
|
let budget: BudgetRefusal | undefined;
|
|
90
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
|
+
};
|
|
91
210
|
const finish = (input: Omit<EnvelopeInput, "yield">) => {
|
|
92
211
|
const envelope = buildEnvelope({
|
|
93
212
|
...input,
|
|
@@ -99,15 +218,166 @@ export function createFindFilesTool(dependencies: ToolDependencies) {
|
|
|
99
218
|
elapsedMs: performance.now() - started,
|
|
100
219
|
},
|
|
101
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
|
+
});
|
|
102
330
|
runtime.session.record(envelope);
|
|
103
331
|
runtime.guide.deliver(ctx);
|
|
104
332
|
return {
|
|
105
|
-
content: [
|
|
106
|
-
|
|
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 },
|
|
107
370
|
...hostUsage(host.isOmp, totals.usage),
|
|
108
371
|
};
|
|
109
372
|
};
|
|
110
|
-
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
|
+
}
|
|
111
381
|
const words = contentWords(args.goal);
|
|
112
382
|
const short = words.length < C.FIND_GOAL_MIN_WORDS;
|
|
113
383
|
if (short)
|
|
@@ -130,14 +400,37 @@ export function createFindFilesTool(dependencies: ToolDependencies) {
|
|
|
130
400
|
: C.FIND_FILES_READ;
|
|
131
401
|
const candidates = await prefilter(
|
|
132
402
|
exec,
|
|
133
|
-
|
|
403
|
+
cwd,
|
|
134
404
|
keywords,
|
|
135
405
|
candidateLimit,
|
|
136
406
|
args.scope,
|
|
137
407
|
args.exclude,
|
|
138
408
|
signal,
|
|
139
409
|
);
|
|
140
|
-
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
|
+
});
|
|
141
434
|
const entry = (
|
|
142
435
|
path: string,
|
|
143
436
|
p: number,
|
|
@@ -173,23 +466,27 @@ export function createFindFilesTool(dependencies: ToolDependencies) {
|
|
|
173
466
|
],
|
|
174
467
|
});
|
|
175
468
|
if (!candidates.paths.length) {
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
});
|
|
183
|
-
return finish({
|
|
184
|
-
lines: [entry("none", 1, short ? "unsure" : "verdict")],
|
|
185
|
-
});
|
|
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({});
|
|
186
476
|
}
|
|
187
477
|
const judge = async (
|
|
188
478
|
state: State,
|
|
189
479
|
questions: Record<string, Question>,
|
|
190
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
|
+
};
|
|
191
487
|
const result = await client.judge(state, questions, {
|
|
192
488
|
signal,
|
|
489
|
+
...runtime.session.requestGate(),
|
|
193
490
|
beforeRequest: (count) => {
|
|
194
491
|
if (args.max_calls !== undefined && sent >= args.max_calls) {
|
|
195
492
|
budget = {
|
|
@@ -207,6 +504,8 @@ export function createFindFilesTool(dependencies: ToolDependencies) {
|
|
|
207
504
|
return admission;
|
|
208
505
|
},
|
|
209
506
|
onUsage: (usage) => runtime.session.recordUsage(usage),
|
|
507
|
+
admissionCause: () =>
|
|
508
|
+
budget?.kind === "max_calls" ? "call_budget" : "session_budget",
|
|
210
509
|
});
|
|
211
510
|
for (const key of [
|
|
212
511
|
"calls",
|
|
@@ -216,7 +515,21 @@ export function createFindFilesTool(dependencies: ToolDependencies) {
|
|
|
216
515
|
] as const)
|
|
217
516
|
totals[key] += result[key] ?? 0;
|
|
218
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
|
+
}
|
|
219
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
|
+
}
|
|
220
533
|
return result;
|
|
221
534
|
};
|
|
222
535
|
const boolQuestions = (
|
|
@@ -267,6 +580,12 @@ export function createFindFilesTool(dependencies: ToolDependencies) {
|
|
|
267
580
|
const rankedNames = rankFiles(names);
|
|
268
581
|
let bestRanking = rankedNames;
|
|
269
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
|
+
});
|
|
270
589
|
const top = bestRanking.slice(0, 2);
|
|
271
590
|
return finish({
|
|
272
591
|
lines: [
|
|
@@ -308,13 +627,16 @@ export function createFindFilesTool(dependencies: ToolDependencies) {
|
|
|
308
627
|
const contentResults = await Promise.all(
|
|
309
628
|
contentBatches.map(async (paths) => {
|
|
310
629
|
const read = await collectSearchExcerpts(
|
|
311
|
-
|
|
630
|
+
cwd,
|
|
312
631
|
paths,
|
|
313
632
|
keywords,
|
|
314
633
|
signal,
|
|
315
634
|
exec,
|
|
316
635
|
);
|
|
317
|
-
if (!read.ok)
|
|
636
|
+
if (!read.ok) {
|
|
637
|
+
failureCause = read.cause ?? "file_unavailable";
|
|
638
|
+
return { paths, result: read };
|
|
639
|
+
}
|
|
318
640
|
Object.assign(files, read.pointers);
|
|
319
641
|
limitations.push(
|
|
320
642
|
...read.limits.map((limit) => ({
|
|
@@ -322,11 +644,18 @@ export function createFindFilesTool(dependencies: ToolDependencies) {
|
|
|
322
644
|
next: "provide a UTF-8 text file",
|
|
323
645
|
})),
|
|
324
646
|
);
|
|
325
|
-
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
|
+
);
|
|
326
654
|
limitations.push({
|
|
327
655
|
fact: `${read.ignored.length} binary, empty or unreadable paths ignored: ${read.ignored.join(", ")}`,
|
|
328
656
|
next: "narrow scope to text files",
|
|
329
657
|
});
|
|
658
|
+
}
|
|
330
659
|
const textPaths = paths.filter(
|
|
331
660
|
(path) => read.files[path] !== undefined,
|
|
332
661
|
);
|
|
@@ -395,12 +724,26 @@ export function createFindFilesTool(dependencies: ToolDependencies) {
|
|
|
395
724
|
],
|
|
396
725
|
});
|
|
397
726
|
if (!retained.length) {
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
return
|
|
727
|
+
diagnostic(
|
|
728
|
+
"collection_empty",
|
|
729
|
+
"no file retained by content or names guard; no entry decision was judged",
|
|
730
|
+
);
|
|
731
|
+
return finish({});
|
|
403
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
|
+
});
|
|
404
747
|
const idByPath = Object.fromEntries(
|
|
405
748
|
retained.map((item, index) => [item.path, `c${index + 1}`]),
|
|
406
749
|
);
|
|
@@ -434,6 +777,7 @@ export function createFindFilesTool(dependencies: ToolDependencies) {
|
|
|
434
777
|
if (!firstResult.ok && !budget)
|
|
435
778
|
return finish({ refusal: firstResult.error });
|
|
436
779
|
const first = firstResult.ok ? firstResult.answers.entry : undefined;
|
|
780
|
+
primary = first;
|
|
437
781
|
if (first?.type !== "choice") {
|
|
438
782
|
unchecked.push("entry");
|
|
439
783
|
return fallback();
|
|
@@ -456,14 +800,30 @@ export function createFindFilesTool(dependencies: ToolDependencies) {
|
|
|
456
800
|
(args.effort !== "quick" &&
|
|
457
801
|
needsReverse(first.probabilities, retained.length)))
|
|
458
802
|
) {
|
|
803
|
+
confirmationRequired = true;
|
|
459
804
|
const secondResult = await pointer([...paths].reverse());
|
|
460
|
-
if (!secondResult.ok && !budget)
|
|
805
|
+
if (!secondResult.ok && !budget) {
|
|
806
|
+
auxiliary.passages.notJudged--;
|
|
807
|
+
auxiliary.controls.notJudged++;
|
|
461
808
|
return finish({ refusal: secondResult.error });
|
|
809
|
+
}
|
|
462
810
|
const second = secondResult.ok ? secondResult.answers.entry : undefined;
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
811
|
+
confirmation = second;
|
|
812
|
+
if (second?.type === "choice" && second.source) {
|
|
813
|
+
auxiliary.passages[second.source]--;
|
|
814
|
+
auxiliary.controls[second.source]++;
|
|
466
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
|
+
{
|
|
467
827
|
const secondRanks = ranked(second.probabilities);
|
|
468
828
|
const combined = readPointer(
|
|
469
829
|
first.probabilities,
|