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/locate.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import type { ToolDefinition } from "@earendil-works/pi-coding-agent";
|
|
2
2
|
import { type Static, Type } from "@sinclair/typebox";
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
resolveEvidenceContext,
|
|
5
|
+
withEvidenceContext,
|
|
6
|
+
} from "../adapters/evidence-context.ts";
|
|
4
7
|
import { shareGitInventory } from "../adapters/git-inventory.ts";
|
|
5
8
|
import { readLocateFile, scanRange } from "../adapters/locate-file.ts";
|
|
6
9
|
import { loadSyntaxParser } from "../adapters/syntax.ts";
|
|
@@ -27,18 +30,34 @@ import {
|
|
|
27
30
|
type Limitation,
|
|
28
31
|
} from "../core/output.ts";
|
|
29
32
|
import { needsReverse, pointerQuestion, readPointer } from "../core/pointer.ts";
|
|
33
|
+
import {
|
|
34
|
+
type Action,
|
|
35
|
+
answerItemFromInput,
|
|
36
|
+
buildResultReport,
|
|
37
|
+
type Cause,
|
|
38
|
+
contextFromEvidence,
|
|
39
|
+
type Diagnostic,
|
|
40
|
+
type Item,
|
|
41
|
+
known,
|
|
42
|
+
type RawValue,
|
|
43
|
+
unknown,
|
|
44
|
+
} from "../core/result-report.ts";
|
|
30
45
|
import { type Section, sectionFile } from "../core/sections.ts";
|
|
31
46
|
import type { SyntaxParser } from "../core/units.ts";
|
|
32
47
|
import { interpolate } from "../describe.ts";
|
|
33
48
|
import type { GuideContext } from "../guide.ts";
|
|
34
|
-
import type { Judgment } from "../jev/types.ts";
|
|
35
|
-
import {
|
|
49
|
+
import type { Answer, Judgment } from "../jev/types.ts";
|
|
50
|
+
import { renderResultReport } from "../render.ts";
|
|
36
51
|
import type { ToolDependencies } from "../runtime.ts";
|
|
37
52
|
import { NOT_CONFIGURED } from "../texts/configuration.ts";
|
|
38
53
|
import { LOCATE_DESCRIPTION } from "../texts/locate.ts";
|
|
39
54
|
|
|
40
55
|
const parameters = Type.Object(
|
|
41
|
-
{
|
|
56
|
+
{
|
|
57
|
+
root: Type.Optional(Type.String()),
|
|
58
|
+
path: Type.String({ minLength: 1 }),
|
|
59
|
+
goal: Type.String({ minLength: 1 }),
|
|
60
|
+
},
|
|
42
61
|
{ additionalProperties: false },
|
|
43
62
|
);
|
|
44
63
|
type LocateArgs = Static<typeof parameters>;
|
|
@@ -75,11 +94,97 @@ export function createLocateTool(
|
|
|
75
94
|
ctx: { cwd: string } & GuideContext,
|
|
76
95
|
) {
|
|
77
96
|
const client = dependencies.client;
|
|
78
|
-
const
|
|
97
|
+
const evidence = await resolveEvidenceContext(ctx.cwd, args.root, {
|
|
98
|
+
exec: execute,
|
|
99
|
+
signal,
|
|
100
|
+
origin: dependencies.evidenceOrigin,
|
|
101
|
+
});
|
|
102
|
+
const evidenceContext = evidence.context;
|
|
103
|
+
const cwd = evidence.ok ? evidence.cwd : evidenceContext.authority.path;
|
|
79
104
|
const started = performance.now();
|
|
80
105
|
const exec = shareGitInventory(execute);
|
|
81
106
|
const results: Judgment[] = [];
|
|
82
107
|
const limitations: Limitation[] = [];
|
|
108
|
+
const context = contextFromEvidence(evidenceContext);
|
|
109
|
+
const diagnostics: Diagnostic[] = [];
|
|
110
|
+
const actions: Action[] = [];
|
|
111
|
+
const auxiliary = {
|
|
112
|
+
controls: { fresh: 0, cache: 0, notJudged: 0, static: 0 },
|
|
113
|
+
passages: { fresh: 0, cache: 0, notJudged: 0 },
|
|
114
|
+
};
|
|
115
|
+
let primary: Answer | undefined;
|
|
116
|
+
let control: Answer | undefined;
|
|
117
|
+
let requiredControl = false;
|
|
118
|
+
let failureCause: Cause = "invalid_response";
|
|
119
|
+
const raw = (answer: Answer): RawValue[] =>
|
|
120
|
+
answer.type === "choice"
|
|
121
|
+
? Object.entries(answer.probabilities).map(
|
|
122
|
+
([label, probability]) => ({
|
|
123
|
+
label,
|
|
124
|
+
value: label,
|
|
125
|
+
probability: known(probability),
|
|
126
|
+
}),
|
|
127
|
+
)
|
|
128
|
+
: [];
|
|
129
|
+
const diagnose = (cause: Cause, fact: string, members: string[] = []) => {
|
|
130
|
+
const id = `d${diagnostics.length + 1}`,
|
|
131
|
+
actionId = `a${actions.length + 1}`;
|
|
132
|
+
const code =
|
|
133
|
+
cause === "invalid_root"
|
|
134
|
+
? "correct_context"
|
|
135
|
+
: cause === "not_configured"
|
|
136
|
+
? "configure_client"
|
|
137
|
+
: cause === "transport_failure" || cause === "service_unavailable"
|
|
138
|
+
? "recover_service"
|
|
139
|
+
: "inspect_native";
|
|
140
|
+
actions.push({
|
|
141
|
+
id: actionId,
|
|
142
|
+
code,
|
|
143
|
+
target: known(args.path),
|
|
144
|
+
scope: { kind: "call" },
|
|
145
|
+
condition:
|
|
146
|
+
code === "correct_context"
|
|
147
|
+
? "If the requested root is incorrect"
|
|
148
|
+
: code === "recover_service"
|
|
149
|
+
? "Only after service recovery is observed"
|
|
150
|
+
: "If the range decision remains open",
|
|
151
|
+
instruction:
|
|
152
|
+
code === "correct_context"
|
|
153
|
+
? "Use the exact registered worktree top-level; no fallback was performed."
|
|
154
|
+
: code === "configure_client"
|
|
155
|
+
? "Configure the Jev client or inspect the file natively."
|
|
156
|
+
: "Read or search the cited file within existing permissions; revisit Jev only with decisive new evidence or materially changed context.",
|
|
157
|
+
repeatUnchanged: false,
|
|
158
|
+
});
|
|
159
|
+
diagnostics.push({
|
|
160
|
+
id,
|
|
161
|
+
cause,
|
|
162
|
+
fact,
|
|
163
|
+
target: known(
|
|
164
|
+
cause === "invalid_root" ? (args.root ?? cwd) : args.path,
|
|
165
|
+
),
|
|
166
|
+
origin:
|
|
167
|
+
cause === "invalid_root" || cause === "forbidden_path"
|
|
168
|
+
? "input"
|
|
169
|
+
: cause === "control_failure"
|
|
170
|
+
? "control"
|
|
171
|
+
: cause === "session_budget"
|
|
172
|
+
? "budget"
|
|
173
|
+
: cause === "service_unavailable" ||
|
|
174
|
+
cause === "transport_failure" ||
|
|
175
|
+
cause === "invalid_response" ||
|
|
176
|
+
cause === "provider_context_refusal" ||
|
|
177
|
+
cause === "not_configured"
|
|
178
|
+
? "provider"
|
|
179
|
+
: "collection",
|
|
180
|
+
scope: { kind: "call" },
|
|
181
|
+
effect: "blocking",
|
|
182
|
+
material: true,
|
|
183
|
+
omittedMembers: members,
|
|
184
|
+
memberCount: known(members.length),
|
|
185
|
+
actionIds: [actionId],
|
|
186
|
+
});
|
|
187
|
+
};
|
|
83
188
|
const finish = (input: Omit<EnvelopeInput, "yield">) => {
|
|
84
189
|
const usage = results.reduce(
|
|
85
190
|
(sum, result) => ({
|
|
@@ -109,24 +214,182 @@ export function createLocateTool(
|
|
|
109
214
|
elapsedMs: performance.now() - started,
|
|
110
215
|
},
|
|
111
216
|
});
|
|
217
|
+
if (input.refusal) diagnose(failureCause, input.refusal);
|
|
218
|
+
if (input.answers?.[0] && primary?.source)
|
|
219
|
+
actions.push({
|
|
220
|
+
id: `a${actions.length + 1}`,
|
|
221
|
+
code: "inspect_native",
|
|
222
|
+
target: known(
|
|
223
|
+
input.answers[0].label === "none"
|
|
224
|
+
? args.path
|
|
225
|
+
: input.answers[0].label,
|
|
226
|
+
),
|
|
227
|
+
scope: { kind: "group", groupIds: ["pointer"] },
|
|
228
|
+
condition: "If the range decision remains open",
|
|
229
|
+
instruction:
|
|
230
|
+
input.answers[0].label === "none"
|
|
231
|
+
? "Search elsewhere for the goal; read any cited alternative range if the result is unsure. Do not retry unchanged evidence."
|
|
232
|
+
: "Read the current primary and alternative ranges; retain uncertainty if no decisive new evidence is available. Do not retry unchanged evidence.",
|
|
233
|
+
repeatUnchanged: false,
|
|
234
|
+
});
|
|
235
|
+
if (
|
|
236
|
+
!input.refusal &&
|
|
237
|
+
(!primary?.source || (requiredControl && !control?.source))
|
|
238
|
+
)
|
|
239
|
+
diagnose(
|
|
240
|
+
"invalid_response",
|
|
241
|
+
"pointer decision lacks established answer provenance or its required control",
|
|
242
|
+
);
|
|
243
|
+
const common = {
|
|
244
|
+
id: "pointer",
|
|
245
|
+
kind: "pointer" as const,
|
|
246
|
+
label: args.path,
|
|
247
|
+
groupId: "pointer",
|
|
248
|
+
evidence: primary
|
|
249
|
+
? [
|
|
250
|
+
{
|
|
251
|
+
target: args.path,
|
|
252
|
+
canonicalPath: evidenceContext.effectiveRoot
|
|
253
|
+
? known(
|
|
254
|
+
`${evidenceContext.effectiveRoot.path}/${args.path}`,
|
|
255
|
+
)
|
|
256
|
+
: unknown("file admission not established"),
|
|
257
|
+
aliases: [],
|
|
258
|
+
side: "current" as const,
|
|
259
|
+
revision: context.resolvedBase,
|
|
260
|
+
},
|
|
261
|
+
]
|
|
262
|
+
: [],
|
|
263
|
+
diagnosticIds: diagnostics.map((d) => d.id),
|
|
264
|
+
actionIds: actions.map((a) => a.id),
|
|
265
|
+
};
|
|
266
|
+
if (input.answers?.[0]) common.label = input.answers[0].label;
|
|
267
|
+
const displayed = input.answers?.[0];
|
|
268
|
+
let item: Item = { ...common, treatment: "not_judged", source: "none" };
|
|
269
|
+
if (
|
|
270
|
+
displayed &&
|
|
271
|
+
primary?.type === "choice" &&
|
|
272
|
+
primary.source &&
|
|
273
|
+
(!requiredControl || (control?.type === "choice" && control.source))
|
|
274
|
+
) {
|
|
275
|
+
auxiliary.passages[primary.source]--;
|
|
276
|
+
item = answerItemFromInput(common, {
|
|
277
|
+
...displayed,
|
|
278
|
+
answer: primary,
|
|
279
|
+
publicResult: displayed.value?.head ?? primary.choice,
|
|
280
|
+
reason: [...limitations, ...(input.limitations ?? [])]
|
|
281
|
+
.map((limit) => limit.fact)
|
|
282
|
+
.join("; "),
|
|
283
|
+
rawValues: raw(primary),
|
|
284
|
+
reportControls:
|
|
285
|
+
control?.type === "choice" && control.source
|
|
286
|
+
? [
|
|
287
|
+
{
|
|
288
|
+
id: "pointer-order",
|
|
289
|
+
kind: "order",
|
|
290
|
+
source: control.source,
|
|
291
|
+
outcome: displayed.orderDependent
|
|
292
|
+
? "order-dependent"
|
|
293
|
+
: "consistent",
|
|
294
|
+
rawValues: raw(control),
|
|
295
|
+
},
|
|
296
|
+
]
|
|
297
|
+
: [],
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
const report = buildResultReport({
|
|
301
|
+
tool: "jev_locate_in_file",
|
|
302
|
+
context,
|
|
303
|
+
items: [item],
|
|
304
|
+
diagnostics,
|
|
305
|
+
actions,
|
|
306
|
+
metrics: {
|
|
307
|
+
...metrics,
|
|
308
|
+
costUsd: results.every((r) => !r.calls || r.usage !== undefined)
|
|
309
|
+
? usage.costUsd
|
|
310
|
+
: undefined,
|
|
311
|
+
elapsedMs: performance.now() - started,
|
|
312
|
+
},
|
|
313
|
+
auxiliary,
|
|
314
|
+
total: known(1),
|
|
315
|
+
refused: Boolean(
|
|
316
|
+
input.refusal &&
|
|
317
|
+
(failureCause === "invalid_root" ||
|
|
318
|
+
failureCause === "forbidden_path" ||
|
|
319
|
+
failureCause === "outside_inventory" ||
|
|
320
|
+
failureCause === "group_too_large" ||
|
|
321
|
+
failureCause === "evidence_too_large"),
|
|
322
|
+
),
|
|
323
|
+
});
|
|
112
324
|
runtime.session.record(envelope);
|
|
113
325
|
runtime.guide.deliver(ctx);
|
|
114
326
|
return {
|
|
115
|
-
content: [
|
|
116
|
-
|
|
327
|
+
content: [
|
|
328
|
+
{
|
|
329
|
+
type: "text" as const,
|
|
330
|
+
text: renderResultReport(report, {
|
|
331
|
+
details: {
|
|
332
|
+
lines: [
|
|
333
|
+
...envelope.lines,
|
|
334
|
+
{
|
|
335
|
+
type: "list" as const,
|
|
336
|
+
title: "pointer reservations",
|
|
337
|
+
items: [...limitations, ...(input.limitations ?? [])].map(
|
|
338
|
+
(limit) => limit.fact,
|
|
339
|
+
),
|
|
340
|
+
},
|
|
341
|
+
...(displayed
|
|
342
|
+
? [
|
|
343
|
+
{
|
|
344
|
+
type: "list" as const,
|
|
345
|
+
title: "read ranges",
|
|
346
|
+
items: [
|
|
347
|
+
...(displayed.label === "none"
|
|
348
|
+
? []
|
|
349
|
+
: [displayed.label]),
|
|
350
|
+
...(displayed.candidates ?? [])
|
|
351
|
+
.filter(
|
|
352
|
+
(candidate) => candidate.label !== "none",
|
|
353
|
+
)
|
|
354
|
+
.map(
|
|
355
|
+
(candidate) =>
|
|
356
|
+
`${candidate.label} (${candidate.value.p})`,
|
|
357
|
+
),
|
|
358
|
+
],
|
|
359
|
+
},
|
|
360
|
+
]
|
|
361
|
+
: []),
|
|
362
|
+
],
|
|
363
|
+
},
|
|
364
|
+
}),
|
|
365
|
+
},
|
|
366
|
+
],
|
|
367
|
+
details: { results, evidenceContext, result: report },
|
|
117
368
|
...hostUsage(
|
|
118
369
|
host.isOmp,
|
|
119
370
|
results.some((r) => r.usage) ? usage : undefined,
|
|
120
371
|
),
|
|
121
372
|
};
|
|
122
373
|
};
|
|
374
|
+
if (!evidence.ok) {
|
|
375
|
+
failureCause = evidence.cause ?? "invalid_root";
|
|
376
|
+
return finish({ refusal: evidence.error });
|
|
377
|
+
}
|
|
378
|
+
failureCause = "file_unavailable";
|
|
123
379
|
const file = await readLocateFile(cwd, args.path, signal, exec);
|
|
124
|
-
if (!file.ok)
|
|
380
|
+
if (!file.ok) {
|
|
381
|
+
failureCause = file.cause ?? "file_unavailable";
|
|
382
|
+
return finish({ refusal: file.error });
|
|
383
|
+
}
|
|
384
|
+
failureCause = "outside_inventory";
|
|
125
385
|
if (file.bytes < LOCATE_MIN_KB * 1000)
|
|
126
386
|
return finish({
|
|
127
387
|
refusal: `small file (${file.lines} lines): read it directly`,
|
|
128
388
|
});
|
|
129
|
-
if (!client)
|
|
389
|
+
if (!client) {
|
|
390
|
+
failureCause = "not_configured";
|
|
391
|
+
return finish({ refusal: NOT_CONFIGURED });
|
|
392
|
+
}
|
|
130
393
|
let sections: Section[];
|
|
131
394
|
if (file.kind === "whole") {
|
|
132
395
|
const parser = await loadParser();
|
|
@@ -169,8 +432,66 @@ export function createLocateTool(
|
|
|
169
432
|
});
|
|
170
433
|
}
|
|
171
434
|
const sectionCount = sections.length;
|
|
435
|
+
context.inventories.push({
|
|
436
|
+
id: "sections",
|
|
437
|
+
kind: "sections",
|
|
438
|
+
rules: [
|
|
439
|
+
file.kind === "whole"
|
|
440
|
+
? "declarations or heuristic sections from complete file"
|
|
441
|
+
: "bounded window outline",
|
|
442
|
+
],
|
|
443
|
+
restrictions: [
|
|
444
|
+
args.path,
|
|
445
|
+
...sections.map(
|
|
446
|
+
(section) => `${section.id}: ${section.start}-${section.end}`,
|
|
447
|
+
),
|
|
448
|
+
],
|
|
449
|
+
discovered: known(sectionCount),
|
|
450
|
+
considered: known(sectionCount),
|
|
451
|
+
scopeRestricted: true,
|
|
452
|
+
criteria: [],
|
|
453
|
+
});
|
|
454
|
+
if (!sectionCount) {
|
|
455
|
+
diagnose(
|
|
456
|
+
"collection_empty",
|
|
457
|
+
"no sections discovered; no pointer judgment",
|
|
458
|
+
);
|
|
459
|
+
return finish({});
|
|
460
|
+
}
|
|
461
|
+
failureCause = "group_too_large";
|
|
172
462
|
const judge = async (candidates: Section[], outline = false) => {
|
|
173
|
-
|
|
463
|
+
primary = undefined;
|
|
464
|
+
control = undefined;
|
|
465
|
+
requiredControl = false;
|
|
466
|
+
const inventory = context.inventories.find(
|
|
467
|
+
(inventory) => inventory.id === "pointer-candidates",
|
|
468
|
+
);
|
|
469
|
+
const restricted = {
|
|
470
|
+
id: "pointer-candidates",
|
|
471
|
+
kind: "sections" as const,
|
|
472
|
+
rules: ["actual candidates for the latest pointer stage"],
|
|
473
|
+
restrictions: [
|
|
474
|
+
args.path,
|
|
475
|
+
...candidates.map(
|
|
476
|
+
(section) => `${section.id}: ${section.start}-${section.end}`,
|
|
477
|
+
),
|
|
478
|
+
],
|
|
479
|
+
discovered: known(candidates.length),
|
|
480
|
+
considered: known(candidates.length),
|
|
481
|
+
scopeRestricted: true,
|
|
482
|
+
criteria: [],
|
|
483
|
+
};
|
|
484
|
+
if (inventory) Object.assign(inventory, restricted);
|
|
485
|
+
else context.inventories.push(restricted);
|
|
486
|
+
const state = withEvidenceContext(
|
|
487
|
+
sectionState(args.path, args.goal, candidates, outline),
|
|
488
|
+
evidenceContext,
|
|
489
|
+
);
|
|
490
|
+
if (JSON.stringify(state).length > STATE_MAX_CHARS)
|
|
491
|
+
return {
|
|
492
|
+
ok: false as const,
|
|
493
|
+
error: `required evidence exceeds STATE_MAX_CHARS=${STATE_MAX_CHARS}`,
|
|
494
|
+
};
|
|
174
495
|
const ask = async (reverse: boolean) => {
|
|
175
496
|
const result = await client.judge(
|
|
176
497
|
state,
|
|
@@ -186,12 +507,27 @@ export function createLocateTool(
|
|
|
186
507
|
signal,
|
|
187
508
|
...runtime.session.requestGate(),
|
|
188
509
|
beforeRequest: (n) => runtime.session.admit(n),
|
|
510
|
+
admissionCause: () => "session_budget",
|
|
189
511
|
onUsage: (usage) => runtime.session.recordUsage(usage),
|
|
190
512
|
},
|
|
191
513
|
);
|
|
192
514
|
results.push(result);
|
|
515
|
+
if (!result.ok) {
|
|
516
|
+
failureCause = result.failureCause ?? "invalid_response";
|
|
517
|
+
if (reverse) auxiliary.controls.notJudged++;
|
|
518
|
+
else auxiliary.passages.notJudged++;
|
|
519
|
+
}
|
|
193
520
|
if (!result.ok) return { ok: false as const, error: result.error };
|
|
194
521
|
const answer = result.answers.pointer;
|
|
522
|
+
if (reverse) control = answer;
|
|
523
|
+
else primary = answer;
|
|
524
|
+
if (answer?.type === "unjudged")
|
|
525
|
+
failureCause = answer.cause ?? "invalid_response";
|
|
526
|
+
if (answer?.type === "choice" && answer.source) {
|
|
527
|
+
if (reverse) auxiliary.controls[answer.source]++;
|
|
528
|
+
else auxiliary.passages[answer.source]++;
|
|
529
|
+
} else if (reverse) auxiliary.controls.notJudged++;
|
|
530
|
+
else auxiliary.passages.notJudged++;
|
|
195
531
|
return answer?.type === "choice"
|
|
196
532
|
? { ok: true as const, probabilities: answer.probabilities }
|
|
197
533
|
: {
|
|
@@ -206,25 +542,51 @@ export function createLocateTool(
|
|
|
206
542
|
if (!first.ok) return first;
|
|
207
543
|
let pointer = readPointer(first.probabilities);
|
|
208
544
|
if (needsReverse(first.probabilities, candidates.length)) {
|
|
545
|
+
requiredControl = true;
|
|
209
546
|
const second = await ask(true);
|
|
210
|
-
if (!second.ok)
|
|
547
|
+
if (!second.ok) {
|
|
548
|
+
if (failureCause === "invalid_response")
|
|
549
|
+
failureCause = "control_failure";
|
|
550
|
+
return second;
|
|
551
|
+
}
|
|
211
552
|
pointer = readPointer(first.probabilities, second.probabilities);
|
|
212
553
|
}
|
|
213
554
|
return { ok: true as const, ...pointer };
|
|
214
555
|
};
|
|
556
|
+
const emptyState = sectionState(args.path, args.goal, []);
|
|
557
|
+
const contextChars =
|
|
558
|
+
JSON.stringify(withEvidenceContext(emptyState, evidenceContext))
|
|
559
|
+
.length - JSON.stringify(emptyState).length;
|
|
560
|
+
const evidenceMaxChars = STATE_MAX_CHARS - contextChars;
|
|
215
561
|
const fits = (candidates: Section[]) =>
|
|
216
|
-
sectionStateFits(
|
|
562
|
+
sectionStateFits(
|
|
563
|
+
args.path,
|
|
564
|
+
args.goal,
|
|
565
|
+
candidates,
|
|
566
|
+
false,
|
|
567
|
+
evidenceMaxChars,
|
|
568
|
+
);
|
|
217
569
|
const twoStage = file.kind === "outline" || !fits(sections);
|
|
218
570
|
let planUnsure = false;
|
|
219
571
|
if (twoStage) {
|
|
220
|
-
const outline = sectionOutline(
|
|
572
|
+
const outline = sectionOutline(
|
|
573
|
+
args.path,
|
|
574
|
+
args.goal,
|
|
575
|
+
sections,
|
|
576
|
+
evidenceMaxChars,
|
|
577
|
+
);
|
|
221
578
|
if (outline.length < 2 || outline.length >= CHOICE_MAX_OPTIONS)
|
|
222
579
|
return finish({
|
|
223
580
|
refusal:
|
|
224
581
|
"No usable multi-section plan fits the budget; use grep or read directly.",
|
|
225
582
|
});
|
|
226
|
-
const evidence = outlineEvidence(
|
|
227
|
-
|
|
583
|
+
const evidence = outlineEvidence(
|
|
584
|
+
args.path,
|
|
585
|
+
args.goal,
|
|
586
|
+
outline,
|
|
587
|
+
evidenceMaxChars,
|
|
588
|
+
);
|
|
589
|
+
if (!fits(evidence))
|
|
228
590
|
return finish({
|
|
229
591
|
refusal: "Block plan exceeds the state budget; use grep.",
|
|
230
592
|
});
|
|
@@ -274,7 +636,10 @@ export function createLocateTool(
|
|
|
274
636
|
}
|
|
275
637
|
if (file.kind === "outline") {
|
|
276
638
|
const selected = await scanRange(cwd, args.path, block, signal, exec);
|
|
277
|
-
if (!selected.ok)
|
|
639
|
+
if (!selected.ok) {
|
|
640
|
+
failureCause = selected.cause ?? "file_unavailable";
|
|
641
|
+
return finish({ refusal: selected.error });
|
|
642
|
+
}
|
|
278
643
|
const lines = selected.text.split("\n");
|
|
279
644
|
sections = sections
|
|
280
645
|
.filter((s) => s.start >= block.start && s.end <= block.end)
|
|
@@ -334,7 +699,7 @@ export function createLocateTool(
|
|
|
334
699
|
);
|
|
335
700
|
if (!displayed)
|
|
336
701
|
return finish({ refusal: "Pointer probabilities missing." });
|
|
337
|
-
const { primary, band, candidates } = displayed;
|
|
702
|
+
const { primary: displayedPrimary, band, candidates } = displayed;
|
|
338
703
|
if (band === "unsure" || head.id === "none")
|
|
339
704
|
limitations.push({
|
|
340
705
|
fact:
|
|
@@ -346,7 +711,7 @@ export function createLocateTool(
|
|
|
346
711
|
return finish({
|
|
347
712
|
answers: [
|
|
348
713
|
{
|
|
349
|
-
...
|
|
714
|
+
...displayedPrimary,
|
|
350
715
|
band,
|
|
351
716
|
orderDependent: pointer.orderDependent,
|
|
352
717
|
candidates,
|
|
@@ -354,7 +719,7 @@ export function createLocateTool(
|
|
|
354
719
|
],
|
|
355
720
|
lines:
|
|
356
721
|
head.id !== "none"
|
|
357
|
-
? [{ type: "list", title: "read", items: [
|
|
722
|
+
? [{ type: "list", title: "read", items: [displayedPrimary.label] }]
|
|
358
723
|
: [],
|
|
359
724
|
limitations: [
|
|
360
725
|
{
|