bare-agent 0.19.0 → 0.20.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/README.md +22 -1
- package/bareagent.context.md +1 -1
- package/index.d.ts +6 -1
- package/index.js +7 -0
- package/package.json +4 -4
- package/src/planner.d.ts +21 -1
- package/src/planner.js +24 -4
- package/src/recurse-prompts.d.ts +22 -0
- package/src/recurse-prompts.js +71 -0
- package/src/recurse-retrieval.d.ts +174 -0
- package/src/recurse-retrieval.js +372 -0
- package/src/recurse-synthesize.d.ts +53 -0
- package/src/recurse-synthesize.js +97 -0
- package/src/recurse.d.ts +361 -0
- package/src/recurse.js +886 -0
package/src/recurse.d.ts
ADDED
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The per-run runtime blob — the wiring, threaded down the whole recursion tree (and forwarded to the worker
|
|
3
|
+
* Loop's `policy`/governance via `options.ctx`). Distinct from `opts` (the policy knobs).
|
|
4
|
+
*/
|
|
5
|
+
export type RecurseCtx = {
|
|
6
|
+
/**
|
|
7
|
+
* - The model the workers call. Required (here or on `opts.provider`).
|
|
8
|
+
*/
|
|
9
|
+
provider?: import("../types").Provider | undefined;
|
|
10
|
+
/**
|
|
11
|
+
* - bareguard `policy(tool, args, ctx)` — the gate. Sees `ctx.depth` so it can
|
|
12
|
+
* enforce `limits.maxDepth`/budget/calls. recurse adds NO second guard layer (§6).
|
|
13
|
+
*/
|
|
14
|
+
policy?: Function | undefined;
|
|
15
|
+
/**
|
|
16
|
+
* - Budget hook forwarded to every worker Loop AND the verifier — judge
|
|
17
|
+
* and worker tokens are all real spend (BA1: never invisible).
|
|
18
|
+
*/
|
|
19
|
+
onLlmResult?: Function | undefined;
|
|
20
|
+
/**
|
|
21
|
+
* - The current recursion depth (0 at the top). Incremented on each self-call;
|
|
22
|
+
* threaded into `policy`. Callers normally omit it (defaults to 0).
|
|
23
|
+
*/
|
|
24
|
+
depth?: number | undefined;
|
|
25
|
+
/**
|
|
26
|
+
* - Optional event stream forwarded to each worker Loop (receipts substrate).
|
|
27
|
+
*/
|
|
28
|
+
stream?: object;
|
|
29
|
+
/**
|
|
30
|
+
* - Optional litectx handle (RC-5, §10 step 7). Backs the `search`
|
|
31
|
+
* retrieval mode (`recall`). NOT used by `scan` — litectx has no exhaustive enumerate verb today; scan reads
|
|
32
|
+
* the generic array slice-source `opts.corpus` instead (the litectx-resident scan case waits on the litectx
|
|
33
|
+
* `enumerate` verb and drops in behind the same socket).
|
|
34
|
+
*/
|
|
35
|
+
litectx?: {
|
|
36
|
+
recall: Function;
|
|
37
|
+
} | undefined;
|
|
38
|
+
};
|
|
39
|
+
export type RecurseOptions = {
|
|
40
|
+
/**
|
|
41
|
+
* - Fallback provider if `ctx.provider` is absent.
|
|
42
|
+
*/
|
|
43
|
+
provider?: import("../types").Provider | undefined;
|
|
44
|
+
/**
|
|
45
|
+
* - Open topology ceiling (§1): the depth past which the `spawn_child` tool is
|
|
46
|
+
* no longer offered (`maxDepth=1` ⇒ flat fan-out, no nesting). NOT the safety halt — that is bareguard's,
|
|
47
|
+
* and actual depth is always ≤ `limits.maxDepth`.
|
|
48
|
+
*/
|
|
49
|
+
maxDepth?: number | undefined;
|
|
50
|
+
/**
|
|
51
|
+
* - Handle tools offered to EVERY worker (RC-5 pull-default: litectx
|
|
52
|
+
* `recall`/`get`, wired at build step 7). Workers query on demand; never the whole corpus.
|
|
53
|
+
*/
|
|
54
|
+
tools?: import("../types").ToolDef[] | undefined;
|
|
55
|
+
/**
|
|
56
|
+
* - Definition of done (A3). When present, the verifier grades against THIS,
|
|
57
|
+
* not the loose task, and verification always runs.
|
|
58
|
+
*/
|
|
59
|
+
contract?: string | undefined;
|
|
60
|
+
/**
|
|
61
|
+
* Override the verifier (fills `recurse()`'s verify slot, §7.1). Default = an `Evaluator` rubric pass.
|
|
62
|
+
*/
|
|
63
|
+
evaluate?: ((result: any, ctx: {
|
|
64
|
+
contract: string | null;
|
|
65
|
+
task: string;
|
|
66
|
+
}) => (Verdict | Promise<Verdict>)) | undefined;
|
|
67
|
+
/**
|
|
68
|
+
* Override synthesis/reduce (NB-3). A FUNCTION is a deterministic code-reduce over the child `results` — the
|
|
69
|
+
* §9.1 aggregation path (LLM arithmetic over partials carried ~10–15% error). A STRATEGY string runs the
|
|
70
|
+
* built-in reducer: `'concat'` (lossless no-LLM join) or `'merge'` (an isolated Loop-driven subjective
|
|
71
|
+
* merge); a string is ignored when no child ran. Default (unset) = the worker's own final text (Family A:
|
|
72
|
+
* the parent model already combined the children's results in its closing turn).
|
|
73
|
+
*/
|
|
74
|
+
synthesize?: "concat" | "merge" | ((args: {
|
|
75
|
+
task: string;
|
|
76
|
+
text: string | null;
|
|
77
|
+
results: any[];
|
|
78
|
+
children: object[];
|
|
79
|
+
ctx: RecurseCtx;
|
|
80
|
+
}) => any) | undefined;
|
|
81
|
+
/**
|
|
82
|
+
* - (Opt-in, NB-2 / Family B) FORCED fan-out: decompose into exactly this many
|
|
83
|
+
* independent parallel workers via `Planner`→`runPlan`, then reduce. A positive integer here is the count;
|
|
84
|
+
* it OVERRIDES the tier→count map. Setting it (or `mode:'fanout'`) takes the deterministic-parallelism path
|
|
85
|
+
* instead of the model-driven Family-A default. For known-parallel tasks where the caller wants guaranteed
|
|
86
|
+
* fan-out, not the model's adaptive choice.
|
|
87
|
+
*/
|
|
88
|
+
count?: number | undefined;
|
|
89
|
+
/**
|
|
90
|
+
* - (Opt-in, NB-2 / Family B) `'fanout'` = forced semantic fan-out
|
|
91
|
+
* WITHOUT a fixed count — derived from `assessComplexity`'s tier via the calibrated map (medium/complex/
|
|
92
|
+
* critical → 2/4/6; simple → 1); `opts.count` takes precedence. `'partition'` = the DATA-DRIVEN WIDTH path
|
|
93
|
+
* (§11): measure `opts.corpus` and partition it into `max(opts.count floor, ⌈size/workerBudget⌉)` parallel
|
|
94
|
+
* scan-workers (capped by the guards), CODE-reducing the per-chunk counts. Distinct from `'fanout'`: a data
|
|
95
|
+
* partition, not a `Planner` semantic split.
|
|
96
|
+
*/
|
|
97
|
+
mode?: "fanout" | "partition" | undefined;
|
|
98
|
+
/**
|
|
99
|
+
* - (`mode:'partition'`) items per worker; width = `⌈corpus.length /
|
|
100
|
+
* workerBudget⌉` (default 100). A calibratable knob (the §9.1 algorithm), not a discovered constant.
|
|
101
|
+
*/
|
|
102
|
+
workerBudget?: number | undefined;
|
|
103
|
+
/**
|
|
104
|
+
* - (Family B) max workers run at once per wave (default 4). The wave
|
|
105
|
+
* structure is `runPlan`'s; bareguard still bounds the family rate independently.
|
|
106
|
+
*/
|
|
107
|
+
concurrency?: number | undefined;
|
|
108
|
+
/**
|
|
109
|
+
* - (§10 step 7) the retrieval shape for a task OVER A
|
|
110
|
+
* CORPUS, routed by question shape (§9.2.1). `'scan'` (the default WHEN `opts.corpus` is present) = process
|
|
111
|
+
* every slice + LLM-judge + CODE-count — the only COMPLETE path (for "how many / all"). `'search'` = litectx
|
|
112
|
+
* `recall` handle tool offered to the worker (needle; CANNOT count; requires `ctx.litectx`). `'exact'` = a
|
|
113
|
+
* deterministic code-side AND-term filter tool over `opts.corpus`. `'tools'` = the PER-QUERY Family-A face:
|
|
114
|
+
* offer the worker `scan_count` (over `opts.corpus`) + `search_memory` (when `ctx.litectx`) + `exact_match`
|
|
115
|
+
* (array corpus) ALL AT ONCE, and let it pick the shape PER SUB-QUERY — the routing lives in the tool
|
|
116
|
+
* descriptions (scan says "use for how many / all / count"; search says "never count"), so a mixed task gets
|
|
117
|
+
* needle-search AND complete-count without per-sub-query adopter declaration. The completeness guard upgrades a
|
|
118
|
+
* `'search'` on a "how many / all" ask to `'scan'` (UPGRADE-only, never a silent downgrade); it does NOT fire
|
|
119
|
+
* for `'tools'` (the complete `scan_count` is always offered there, so a mixed task keeps its search tool).
|
|
120
|
+
* Absent `corpus` AND `retrieval`, behaviour is unchanged (Family A / single-shot) — fully backward-compatible.
|
|
121
|
+
*/
|
|
122
|
+
retrieval?: "tools" | "search" | "scan" | "exact" | undefined;
|
|
123
|
+
/**
|
|
124
|
+
* - (§10 step 7) the generic slice-source scan/partition
|
|
125
|
+
* reads: an in-hand `{id, text}[]` array, OR an async `() => Promise<Slice[]>` (e.g. `litectxCorpus(litectx,
|
|
126
|
+
* {kind})` materializing a litectx-resident corpus via `enumerate`). recurse depends on this SHAPE, never on
|
|
127
|
+
* litectx. Malformed entries are dropped, never miscounted.
|
|
128
|
+
*/
|
|
129
|
+
corpus?: Slice[] | (() => Promise<Slice[]>) | undefined;
|
|
130
|
+
/**
|
|
131
|
+
* - (scan) items per judge window. Default 8 (§9.2.1 recall knee — the one
|
|
132
|
+
* calibrated number; per-model).
|
|
133
|
+
*/
|
|
134
|
+
window?: number | undefined;
|
|
135
|
+
/**
|
|
136
|
+
* - (scan) shuffled-boundary passes unioned for recall. Default 2 (~0.91 recall).
|
|
137
|
+
*/
|
|
138
|
+
passes?: number | undefined;
|
|
139
|
+
};
|
|
140
|
+
/**
|
|
141
|
+
* One audit/receipts node (RC-10) — the recursion tree reconstructs from these alone: parent→child lineage
|
|
142
|
+
* (`spawned`), each subgoal (`task`), each gap report (`verdict`), cost per node (`tokens`).
|
|
143
|
+
*/
|
|
144
|
+
export type RecurseNode = {
|
|
145
|
+
task: string;
|
|
146
|
+
depth: number;
|
|
147
|
+
complexity: {
|
|
148
|
+
level: string;
|
|
149
|
+
score: number;
|
|
150
|
+
};
|
|
151
|
+
critical: boolean;
|
|
152
|
+
/**
|
|
153
|
+
* - Child nodes (lineage).
|
|
154
|
+
*/
|
|
155
|
+
spawned: RecurseNode[];
|
|
156
|
+
verdict: Verdict | null;
|
|
157
|
+
incomplete: boolean;
|
|
158
|
+
halted: boolean;
|
|
159
|
+
/**
|
|
160
|
+
* - The worker Loop's `metrics.tokens`.
|
|
161
|
+
*/
|
|
162
|
+
tokens: object | null;
|
|
163
|
+
model: string | null;
|
|
164
|
+
/**
|
|
165
|
+
* - (§10 step 7) the retrieval mode this node ran (`scan`/`search`/`exact`),
|
|
166
|
+
* or null/absent for a plain reasoning node.
|
|
167
|
+
*/
|
|
168
|
+
retrieval?: string | null | undefined;
|
|
169
|
+
/**
|
|
170
|
+
* - set when the completeness guard upgraded the mode (e.g.
|
|
171
|
+
* `'search→scan (completeness)'`) — the audit trail for RC-9-applied-to-retrieval.
|
|
172
|
+
*/
|
|
173
|
+
retrievalUpgraded?: string | undefined;
|
|
174
|
+
/**
|
|
175
|
+
* - (scan) the scan
|
|
176
|
+
* shape: window/passes used, slices scanned, ids matched (CODE-counted).
|
|
177
|
+
*/
|
|
178
|
+
scan?: {
|
|
179
|
+
window: number;
|
|
180
|
+
passes: number;
|
|
181
|
+
scanned: number;
|
|
182
|
+
matched: number;
|
|
183
|
+
} | undefined;
|
|
184
|
+
/**
|
|
185
|
+
* - (`mode:'partition'`) the data-driven width audit: corpus size, the budget knob, the count floor, the
|
|
186
|
+
* data-derived width `⌈size/budget⌉`, the chosen `width = max(floor, dataWidth)`, and matched count.
|
|
187
|
+
*/
|
|
188
|
+
partition?: {
|
|
189
|
+
size: number;
|
|
190
|
+
workerBudget: number;
|
|
191
|
+
floor: number;
|
|
192
|
+
dataWidth: number;
|
|
193
|
+
width: number;
|
|
194
|
+
matched?: number;
|
|
195
|
+
} | undefined;
|
|
196
|
+
};
|
|
197
|
+
export type RecurseResult = {
|
|
198
|
+
/**
|
|
199
|
+
* - The synthesized answer (on convergence).
|
|
200
|
+
*/
|
|
201
|
+
result?: any;
|
|
202
|
+
/**
|
|
203
|
+
* - The verifier's gap report (null when verification did not run).
|
|
204
|
+
*/
|
|
205
|
+
verdict?: import("./evaluator").Verdict | null | undefined;
|
|
206
|
+
/**
|
|
207
|
+
* - true on guard exhaustion / a dead worker / an incomplete child (RC-9) —
|
|
208
|
+
* never a faked pass.
|
|
209
|
+
*/
|
|
210
|
+
incomplete?: boolean | undefined;
|
|
211
|
+
/**
|
|
212
|
+
* - The best partial answer when `incomplete` (RC-9).
|
|
213
|
+
*/
|
|
214
|
+
best?: any;
|
|
215
|
+
/**
|
|
216
|
+
* - When `incomplete` because a child failed: the sub-task(s) that came
|
|
217
|
+
* back incomplete (§9 scenario 1) — the anti-survivor-sum signal, not a quiet undercount.
|
|
218
|
+
*/
|
|
219
|
+
missingSlices?: string[] | undefined;
|
|
220
|
+
/**
|
|
221
|
+
* - The audit node for this call (RC-10).
|
|
222
|
+
*/
|
|
223
|
+
receipts: RecurseNode;
|
|
224
|
+
};
|
|
225
|
+
export type Provider = import("../types").Provider;
|
|
226
|
+
export type ToolDef = import("../types").ToolDef;
|
|
227
|
+
export type Verdict = import("./evaluator").Verdict;
|
|
228
|
+
export type Slice = {
|
|
229
|
+
id: string;
|
|
230
|
+
text: string;
|
|
231
|
+
};
|
|
232
|
+
/**
|
|
233
|
+
* @typedef {object} RecurseCtx
|
|
234
|
+
* The per-run runtime blob — the wiring, threaded down the whole recursion tree (and forwarded to the worker
|
|
235
|
+
* Loop's `policy`/governance via `options.ctx`). Distinct from `opts` (the policy knobs).
|
|
236
|
+
* @property {Provider} [provider] - The model the workers call. Required (here or on `opts.provider`).
|
|
237
|
+
* @property {Function} [policy] - bareguard `policy(tool, args, ctx)` — the gate. Sees `ctx.depth` so it can
|
|
238
|
+
* enforce `limits.maxDepth`/budget/calls. recurse adds NO second guard layer (§6).
|
|
239
|
+
* @property {Function} [onLlmResult] - Budget hook forwarded to every worker Loop AND the verifier — judge
|
|
240
|
+
* and worker tokens are all real spend (BA1: never invisible).
|
|
241
|
+
* @property {number} [depth] - The current recursion depth (0 at the top). Incremented on each self-call;
|
|
242
|
+
* threaded into `policy`. Callers normally omit it (defaults to 0).
|
|
243
|
+
* @property {object} [stream] - Optional event stream forwarded to each worker Loop (receipts substrate).
|
|
244
|
+
* @property {{recall: Function}} [litectx] - Optional litectx handle (RC-5, §10 step 7). Backs the `search`
|
|
245
|
+
* retrieval mode (`recall`). NOT used by `scan` — litectx has no exhaustive enumerate verb today; scan reads
|
|
246
|
+
* the generic array slice-source `opts.corpus` instead (the litectx-resident scan case waits on the litectx
|
|
247
|
+
* `enumerate` verb and drops in behind the same socket).
|
|
248
|
+
*/
|
|
249
|
+
/**
|
|
250
|
+
* @typedef {object} RecurseOptions
|
|
251
|
+
* @property {Provider} [provider] - Fallback provider if `ctx.provider` is absent.
|
|
252
|
+
* @property {number} [maxDepth=3] - Open topology ceiling (§1): the depth past which the `spawn_child` tool is
|
|
253
|
+
* no longer offered (`maxDepth=1` ⇒ flat fan-out, no nesting). NOT the safety halt — that is bareguard's,
|
|
254
|
+
* and actual depth is always ≤ `limits.maxDepth`.
|
|
255
|
+
* @property {ToolDef[]} [tools] - Handle tools offered to EVERY worker (RC-5 pull-default: litectx
|
|
256
|
+
* `recall`/`get`, wired at build step 7). Workers query on demand; never the whole corpus.
|
|
257
|
+
* @property {string} [contract] - Definition of done (A3). When present, the verifier grades against THIS,
|
|
258
|
+
* not the loose task, and verification always runs.
|
|
259
|
+
* @property {(result: any, ctx: {contract: string|null, task: string}) => (Verdict|Promise<Verdict>)} [evaluate]
|
|
260
|
+
* Override the verifier (fills `recurse()`'s verify slot, §7.1). Default = an `Evaluator` rubric pass.
|
|
261
|
+
* @property {((args: {task: string, text: string|null, results: any[], children: object[], ctx: RecurseCtx}) => any) | 'concat' | 'merge'} [synthesize]
|
|
262
|
+
* Override synthesis/reduce (NB-3). A FUNCTION is a deterministic code-reduce over the child `results` — the
|
|
263
|
+
* §9.1 aggregation path (LLM arithmetic over partials carried ~10–15% error). A STRATEGY string runs the
|
|
264
|
+
* built-in reducer: `'concat'` (lossless no-LLM join) or `'merge'` (an isolated Loop-driven subjective
|
|
265
|
+
* merge); a string is ignored when no child ran. Default (unset) = the worker's own final text (Family A:
|
|
266
|
+
* the parent model already combined the children's results in its closing turn).
|
|
267
|
+
* @property {number} [count] - (Opt-in, NB-2 / Family B) FORCED fan-out: decompose into exactly this many
|
|
268
|
+
* independent parallel workers via `Planner`→`runPlan`, then reduce. A positive integer here is the count;
|
|
269
|
+
* it OVERRIDES the tier→count map. Setting it (or `mode:'fanout'`) takes the deterministic-parallelism path
|
|
270
|
+
* instead of the model-driven Family-A default. For known-parallel tasks where the caller wants guaranteed
|
|
271
|
+
* fan-out, not the model's adaptive choice.
|
|
272
|
+
* @property {'fanout'|'partition'} [mode] - (Opt-in, NB-2 / Family B) `'fanout'` = forced semantic fan-out
|
|
273
|
+
* WITHOUT a fixed count — derived from `assessComplexity`'s tier via the calibrated map (medium/complex/
|
|
274
|
+
* critical → 2/4/6; simple → 1); `opts.count` takes precedence. `'partition'` = the DATA-DRIVEN WIDTH path
|
|
275
|
+
* (§11): measure `opts.corpus` and partition it into `max(opts.count floor, ⌈size/workerBudget⌉)` parallel
|
|
276
|
+
* scan-workers (capped by the guards), CODE-reducing the per-chunk counts. Distinct from `'fanout'`: a data
|
|
277
|
+
* partition, not a `Planner` semantic split.
|
|
278
|
+
* @property {number} [workerBudget] - (`mode:'partition'`) items per worker; width = `⌈corpus.length /
|
|
279
|
+
* workerBudget⌉` (default 100). A calibratable knob (the §9.1 algorithm), not a discovered constant.
|
|
280
|
+
* @property {number} [concurrency] - (Family B) max workers run at once per wave (default 4). The wave
|
|
281
|
+
* structure is `runPlan`'s; bareguard still bounds the family rate independently.
|
|
282
|
+
* @property {'scan'|'search'|'exact'|'tools'} [retrieval] - (§10 step 7) the retrieval shape for a task OVER A
|
|
283
|
+
* CORPUS, routed by question shape (§9.2.1). `'scan'` (the default WHEN `opts.corpus` is present) = process
|
|
284
|
+
* every slice + LLM-judge + CODE-count — the only COMPLETE path (for "how many / all"). `'search'` = litectx
|
|
285
|
+
* `recall` handle tool offered to the worker (needle; CANNOT count; requires `ctx.litectx`). `'exact'` = a
|
|
286
|
+
* deterministic code-side AND-term filter tool over `opts.corpus`. `'tools'` = the PER-QUERY Family-A face:
|
|
287
|
+
* offer the worker `scan_count` (over `opts.corpus`) + `search_memory` (when `ctx.litectx`) + `exact_match`
|
|
288
|
+
* (array corpus) ALL AT ONCE, and let it pick the shape PER SUB-QUERY — the routing lives in the tool
|
|
289
|
+
* descriptions (scan says "use for how many / all / count"; search says "never count"), so a mixed task gets
|
|
290
|
+
* needle-search AND complete-count without per-sub-query adopter declaration. The completeness guard upgrades a
|
|
291
|
+
* `'search'` on a "how many / all" ask to `'scan'` (UPGRADE-only, never a silent downgrade); it does NOT fire
|
|
292
|
+
* for `'tools'` (the complete `scan_count` is always offered there, so a mixed task keeps its search tool).
|
|
293
|
+
* Absent `corpus` AND `retrieval`, behaviour is unchanged (Family A / single-shot) — fully backward-compatible.
|
|
294
|
+
* @property {Slice[] | (() => Promise<Slice[]>)} [corpus] - (§10 step 7) the generic slice-source scan/partition
|
|
295
|
+
* reads: an in-hand `{id, text}[]` array, OR an async `() => Promise<Slice[]>` (e.g. `litectxCorpus(litectx,
|
|
296
|
+
* {kind})` materializing a litectx-resident corpus via `enumerate`). recurse depends on this SHAPE, never on
|
|
297
|
+
* litectx. Malformed entries are dropped, never miscounted.
|
|
298
|
+
* @property {number} [window] - (scan) items per judge window. Default 8 (§9.2.1 recall knee — the one
|
|
299
|
+
* calibrated number; per-model).
|
|
300
|
+
* @property {number} [passes] - (scan) shuffled-boundary passes unioned for recall. Default 2 (~0.91 recall).
|
|
301
|
+
*/
|
|
302
|
+
/**
|
|
303
|
+
* @typedef {object} RecurseNode
|
|
304
|
+
* One audit/receipts node (RC-10) — the recursion tree reconstructs from these alone: parent→child lineage
|
|
305
|
+
* (`spawned`), each subgoal (`task`), each gap report (`verdict`), cost per node (`tokens`).
|
|
306
|
+
* @property {string} task
|
|
307
|
+
* @property {number} depth
|
|
308
|
+
* @property {{level: string, score: number}} complexity
|
|
309
|
+
* @property {boolean} critical
|
|
310
|
+
* @property {RecurseNode[]} spawned - Child nodes (lineage).
|
|
311
|
+
* @property {Verdict|null} verdict
|
|
312
|
+
* @property {boolean} incomplete
|
|
313
|
+
* @property {boolean} halted
|
|
314
|
+
* @property {object|null} tokens - The worker Loop's `metrics.tokens`.
|
|
315
|
+
* @property {string|null} model
|
|
316
|
+
* @property {string|null} [retrieval] - (§10 step 7) the retrieval mode this node ran (`scan`/`search`/`exact`),
|
|
317
|
+
* or null/absent for a plain reasoning node.
|
|
318
|
+
* @property {string} [retrievalUpgraded] - set when the completeness guard upgraded the mode (e.g.
|
|
319
|
+
* `'search→scan (completeness)'`) — the audit trail for RC-9-applied-to-retrieval.
|
|
320
|
+
* @property {{window: number, passes: number, scanned: number, matched: number}} [scan] - (scan) the scan
|
|
321
|
+
* shape: window/passes used, slices scanned, ids matched (CODE-counted).
|
|
322
|
+
* @property {{size: number, workerBudget: number, floor: number, dataWidth: number, width: number, matched?: number}} [partition]
|
|
323
|
+
* - (`mode:'partition'`) the data-driven width audit: corpus size, the budget knob, the count floor, the
|
|
324
|
+
* data-derived width `⌈size/budget⌉`, the chosen `width = max(floor, dataWidth)`, and matched count.
|
|
325
|
+
*/
|
|
326
|
+
/**
|
|
327
|
+
* @typedef {object} RecurseResult
|
|
328
|
+
* @property {any} [result] - The synthesized answer (on convergence).
|
|
329
|
+
* @property {Verdict|null} [verdict] - The verifier's gap report (null when verification did not run).
|
|
330
|
+
* @property {boolean} [incomplete] - true on guard exhaustion / a dead worker / an incomplete child (RC-9) —
|
|
331
|
+
* never a faked pass.
|
|
332
|
+
* @property {any} [best] - The best partial answer when `incomplete` (RC-9).
|
|
333
|
+
* @property {string[]} [missingSlices] - When `incomplete` because a child failed: the sub-task(s) that came
|
|
334
|
+
* back incomplete (§9 scenario 1) — the anti-survivor-sum signal, not a quiet undercount.
|
|
335
|
+
* @property {RecurseNode} receipts - The audit node for this call (RC-10).
|
|
336
|
+
*/
|
|
337
|
+
/**
|
|
338
|
+
* Decompose a task into fresh-context workers, verify against a setpoint, and synthesize one result —
|
|
339
|
+
* assembled from existing primitives, not reimplemented (G1/G6).
|
|
340
|
+
*
|
|
341
|
+
* ⚠️ RESOURCE BOUNDS ARE bareguard's, not recurse()'s — OPEN BY DESIGN (§6, "no second guard layer"), and the
|
|
342
|
+
* one thing to know before running it. `recurse()` adds NO intrinsic total-work cap. The **Family-A default**
|
|
343
|
+
* (model-driven `spawn_child`) lets a node spawn UP TO each Loop's `HARD_ROUND_LIMIT` (100) children PER LEVEL,
|
|
344
|
+
* each recursing to `opts.maxDepth` (default 3) — so node count, and therefore TOKEN + $ SPEND, compounds
|
|
345
|
+
* multiplicatively and is **not capped by recurse itself**. (The forced paths — `mode:'fanout'`/`'partition'` —
|
|
346
|
+
* ARE bounded: a deterministic `count` + a `concurrency` cap. The uncapped path is the model-driven default.)
|
|
347
|
+
* This is real, not theoretical: a live POC (`poc/rlm-defer2-history-overflow.mjs`) showed a weak model
|
|
348
|
+
* over-decomposing into 40–117 calls on a single run. **So: running WITHOUT bareguard — or without ANY
|
|
349
|
+
* token/cost cap — CAN BURN TOKENS / $ unboundedly (up to ~100×depth nodes).** **WIRE bareguard**
|
|
350
|
+
* (`ctx.policy` via `wireGate`) for any non-trivial or untrusted run — it enforces depth/budget/call caps and
|
|
351
|
+
* the pre-wave fan-out checkpoint, turning a runaway into a clean `{incomplete}`. With no gate available, the
|
|
352
|
+
* only local brakes are `opts.maxDepth: 1` (flat — no nesting) and the provider/key's own usage limits.
|
|
353
|
+
*
|
|
354
|
+
* @param {string} task - The goal.
|
|
355
|
+
* @param {RecurseCtx} [ctx] - The runtime wiring (provider, policy, depth, …). Threaded down the tree.
|
|
356
|
+
* @param {RecurseOptions} [opts] - The policy knobs.
|
|
357
|
+
* @returns {Promise<RecurseResult>} `{ result, verdict, receipts }` on convergence; `{ incomplete, best,
|
|
358
|
+
* receipts }` on guard exhaustion. NEVER a fabricated success (RC-9).
|
|
359
|
+
* @throws {Error} no provider supplied (on neither `ctx.provider` nor `opts.provider`).
|
|
360
|
+
*/
|
|
361
|
+
export function recurse(task: string, ctx?: RecurseCtx, opts?: RecurseOptions): Promise<RecurseResult>;
|