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/locate.ts
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import type { ToolDefinition } from "@earendil-works/pi-coding-agent";
|
|
2
2
|
import { type Static, Type } from "@sinclair/typebox";
|
|
3
|
+
import {
|
|
4
|
+
resolveEvidenceContext,
|
|
5
|
+
withEvidenceContext,
|
|
6
|
+
} from "../adapters/evidence-context.ts";
|
|
3
7
|
import { shareGitInventory } from "../adapters/git-inventory.ts";
|
|
4
8
|
import { readLocateFile, scanRange } from "../adapters/locate-file.ts";
|
|
5
9
|
import { loadSyntaxParser } from "../adapters/syntax.ts";
|
|
@@ -26,18 +30,34 @@ import {
|
|
|
26
30
|
type Limitation,
|
|
27
31
|
} from "../core/output.ts";
|
|
28
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";
|
|
29
45
|
import { type Section, sectionFile } from "../core/sections.ts";
|
|
30
46
|
import type { SyntaxParser } from "../core/units.ts";
|
|
31
47
|
import { interpolate } from "../describe.ts";
|
|
32
48
|
import type { GuideContext } from "../guide.ts";
|
|
33
|
-
import type { Judgment } from "../jev/types.ts";
|
|
34
|
-
import {
|
|
49
|
+
import type { Answer, Judgment } from "../jev/types.ts";
|
|
50
|
+
import { renderResultReport } from "../render.ts";
|
|
35
51
|
import type { ToolDependencies } from "../runtime.ts";
|
|
36
52
|
import { NOT_CONFIGURED } from "../texts/configuration.ts";
|
|
37
53
|
import { LOCATE_DESCRIPTION } from "../texts/locate.ts";
|
|
38
54
|
|
|
39
55
|
const parameters = Type.Object(
|
|
40
|
-
{
|
|
56
|
+
{
|
|
57
|
+
root: Type.Optional(Type.String()),
|
|
58
|
+
path: Type.String({ minLength: 1 }),
|
|
59
|
+
goal: Type.String({ minLength: 1 }),
|
|
60
|
+
},
|
|
41
61
|
{ additionalProperties: false },
|
|
42
62
|
);
|
|
43
63
|
type LocateArgs = Static<typeof parameters>;
|
|
@@ -74,10 +94,97 @@ export function createLocateTool(
|
|
|
74
94
|
ctx: { cwd: string } & GuideContext,
|
|
75
95
|
) {
|
|
76
96
|
const client = dependencies.client;
|
|
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;
|
|
77
104
|
const started = performance.now();
|
|
78
105
|
const exec = shareGitInventory(execute);
|
|
79
106
|
const results: Judgment[] = [];
|
|
80
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
|
+
};
|
|
81
188
|
const finish = (input: Omit<EnvelopeInput, "yield">) => {
|
|
82
189
|
const usage = results.reduce(
|
|
83
190
|
(sum, result) => ({
|
|
@@ -107,24 +214,182 @@ export function createLocateTool(
|
|
|
107
214
|
elapsedMs: performance.now() - started,
|
|
108
215
|
},
|
|
109
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
|
+
});
|
|
110
324
|
runtime.session.record(envelope);
|
|
111
325
|
runtime.guide.deliver(ctx);
|
|
112
326
|
return {
|
|
113
|
-
content: [
|
|
114
|
-
|
|
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 },
|
|
115
368
|
...hostUsage(
|
|
116
369
|
host.isOmp,
|
|
117
370
|
results.some((r) => r.usage) ? usage : undefined,
|
|
118
371
|
),
|
|
119
372
|
};
|
|
120
373
|
};
|
|
121
|
-
|
|
122
|
-
|
|
374
|
+
if (!evidence.ok) {
|
|
375
|
+
failureCause = evidence.cause ?? "invalid_root";
|
|
376
|
+
return finish({ refusal: evidence.error });
|
|
377
|
+
}
|
|
378
|
+
failureCause = "file_unavailable";
|
|
379
|
+
const file = await readLocateFile(cwd, args.path, signal, exec);
|
|
380
|
+
if (!file.ok) {
|
|
381
|
+
failureCause = file.cause ?? "file_unavailable";
|
|
382
|
+
return finish({ refusal: file.error });
|
|
383
|
+
}
|
|
384
|
+
failureCause = "outside_inventory";
|
|
123
385
|
if (file.bytes < LOCATE_MIN_KB * 1000)
|
|
124
386
|
return finish({
|
|
125
387
|
refusal: `small file (${file.lines} lines): read it directly`,
|
|
126
388
|
});
|
|
127
|
-
if (!client)
|
|
389
|
+
if (!client) {
|
|
390
|
+
failureCause = "not_configured";
|
|
391
|
+
return finish({ refusal: NOT_CONFIGURED });
|
|
392
|
+
}
|
|
128
393
|
let sections: Section[];
|
|
129
394
|
if (file.kind === "whole") {
|
|
130
395
|
const parser = await loadParser();
|
|
@@ -167,8 +432,66 @@ export function createLocateTool(
|
|
|
167
432
|
});
|
|
168
433
|
}
|
|
169
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";
|
|
170
462
|
const judge = async (candidates: Section[], outline = false) => {
|
|
171
|
-
|
|
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
|
+
};
|
|
172
495
|
const ask = async (reverse: boolean) => {
|
|
173
496
|
const result = await client.judge(
|
|
174
497
|
state,
|
|
@@ -182,13 +505,29 @@ export function createLocateTool(
|
|
|
182
505
|
},
|
|
183
506
|
{
|
|
184
507
|
signal,
|
|
508
|
+
...runtime.session.requestGate(),
|
|
185
509
|
beforeRequest: (n) => runtime.session.admit(n),
|
|
510
|
+
admissionCause: () => "session_budget",
|
|
186
511
|
onUsage: (usage) => runtime.session.recordUsage(usage),
|
|
187
512
|
},
|
|
188
513
|
);
|
|
189
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
|
+
}
|
|
190
520
|
if (!result.ok) return { ok: false as const, error: result.error };
|
|
191
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++;
|
|
192
531
|
return answer?.type === "choice"
|
|
193
532
|
? { ok: true as const, probabilities: answer.probabilities }
|
|
194
533
|
: {
|
|
@@ -203,25 +542,51 @@ export function createLocateTool(
|
|
|
203
542
|
if (!first.ok) return first;
|
|
204
543
|
let pointer = readPointer(first.probabilities);
|
|
205
544
|
if (needsReverse(first.probabilities, candidates.length)) {
|
|
545
|
+
requiredControl = true;
|
|
206
546
|
const second = await ask(true);
|
|
207
|
-
if (!second.ok)
|
|
547
|
+
if (!second.ok) {
|
|
548
|
+
if (failureCause === "invalid_response")
|
|
549
|
+
failureCause = "control_failure";
|
|
550
|
+
return second;
|
|
551
|
+
}
|
|
208
552
|
pointer = readPointer(first.probabilities, second.probabilities);
|
|
209
553
|
}
|
|
210
554
|
return { ok: true as const, ...pointer };
|
|
211
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;
|
|
212
561
|
const fits = (candidates: Section[]) =>
|
|
213
|
-
sectionStateFits(
|
|
562
|
+
sectionStateFits(
|
|
563
|
+
args.path,
|
|
564
|
+
args.goal,
|
|
565
|
+
candidates,
|
|
566
|
+
false,
|
|
567
|
+
evidenceMaxChars,
|
|
568
|
+
);
|
|
214
569
|
const twoStage = file.kind === "outline" || !fits(sections);
|
|
215
570
|
let planUnsure = false;
|
|
216
571
|
if (twoStage) {
|
|
217
|
-
const outline = sectionOutline(
|
|
572
|
+
const outline = sectionOutline(
|
|
573
|
+
args.path,
|
|
574
|
+
args.goal,
|
|
575
|
+
sections,
|
|
576
|
+
evidenceMaxChars,
|
|
577
|
+
);
|
|
218
578
|
if (outline.length < 2 || outline.length >= CHOICE_MAX_OPTIONS)
|
|
219
579
|
return finish({
|
|
220
580
|
refusal:
|
|
221
581
|
"No usable multi-section plan fits the budget; use grep or read directly.",
|
|
222
582
|
});
|
|
223
|
-
const evidence = outlineEvidence(
|
|
224
|
-
|
|
583
|
+
const evidence = outlineEvidence(
|
|
584
|
+
args.path,
|
|
585
|
+
args.goal,
|
|
586
|
+
outline,
|
|
587
|
+
evidenceMaxChars,
|
|
588
|
+
);
|
|
589
|
+
if (!fits(evidence))
|
|
225
590
|
return finish({
|
|
226
591
|
refusal: "Block plan exceeds the state budget; use grep.",
|
|
227
592
|
});
|
|
@@ -270,14 +635,11 @@ export function createLocateTool(
|
|
|
270
635
|
});
|
|
271
636
|
}
|
|
272
637
|
if (file.kind === "outline") {
|
|
273
|
-
const selected = await scanRange(
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
exec,
|
|
279
|
-
);
|
|
280
|
-
if (!selected.ok) return finish({ refusal: selected.error });
|
|
638
|
+
const selected = await scanRange(cwd, args.path, block, signal, exec);
|
|
639
|
+
if (!selected.ok) {
|
|
640
|
+
failureCause = selected.cause ?? "file_unavailable";
|
|
641
|
+
return finish({ refusal: selected.error });
|
|
642
|
+
}
|
|
281
643
|
const lines = selected.text.split("\n");
|
|
282
644
|
sections = sections
|
|
283
645
|
.filter((s) => s.start >= block.start && s.end <= block.end)
|
|
@@ -337,7 +699,7 @@ export function createLocateTool(
|
|
|
337
699
|
);
|
|
338
700
|
if (!displayed)
|
|
339
701
|
return finish({ refusal: "Pointer probabilities missing." });
|
|
340
|
-
const { primary, band, candidates } = displayed;
|
|
702
|
+
const { primary: displayedPrimary, band, candidates } = displayed;
|
|
341
703
|
if (band === "unsure" || head.id === "none")
|
|
342
704
|
limitations.push({
|
|
343
705
|
fact:
|
|
@@ -349,7 +711,7 @@ export function createLocateTool(
|
|
|
349
711
|
return finish({
|
|
350
712
|
answers: [
|
|
351
713
|
{
|
|
352
|
-
...
|
|
714
|
+
...displayedPrimary,
|
|
353
715
|
band,
|
|
354
716
|
orderDependent: pointer.orderDependent,
|
|
355
717
|
candidates,
|
|
@@ -357,7 +719,7 @@ export function createLocateTool(
|
|
|
357
719
|
],
|
|
358
720
|
lines:
|
|
359
721
|
head.id !== "none"
|
|
360
|
-
? [{ type: "list", title: "read", items: [
|
|
722
|
+
? [{ type: "list", title: "read", items: [displayedPrimary.label] }]
|
|
361
723
|
: [],
|
|
362
724
|
limitations: [
|
|
363
725
|
{
|