bare-agent 0.43.0 → 0.44.1
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/package.json +8 -3
- package/primitives.json +446 -0
- package/src/bareguard-adapter.d.ts +6 -0
- package/src/bareguard-adapter.js +6 -0
- package/src/checkpoint.d.ts +5 -0
- package/src/checkpoint.js +5 -0
- package/src/circuit-breaker.d.ts +5 -0
- package/src/circuit-breaker.js +5 -0
- package/src/complexity.d.ts +9 -0
- package/src/complexity.js +9 -0
- package/src/context-units.d.ts +12 -0
- package/src/context-units.js +12 -0
- package/src/evaluator.d.ts +9 -1
- package/src/evaluator.js +9 -1
- package/src/judge-calibration.d.ts +5 -0
- package/src/judge-calibration.js +5 -0
- package/src/judge.d.ts +5 -0
- package/src/judge.js +5 -0
- package/src/loop.d.ts +6 -0
- package/src/loop.js +6 -0
- package/src/mcp-bridge.d.ts +13 -0
- package/src/mcp-bridge.js +13 -0
- package/src/memory.d.ts +5 -0
- package/src/memory.js +5 -0
- package/src/planner.d.ts +6 -0
- package/src/planner.js +6 -0
- package/src/provider-anthropic.d.ts +4 -0
- package/src/provider-anthropic.js +4 -0
- package/src/provider-clipipe.d.ts +4 -0
- package/src/provider-clipipe.js +4 -0
- package/src/provider-fallback.d.ts +4 -0
- package/src/provider-fallback.js +4 -0
- package/src/provider-gemini.d.ts +7 -1
- package/src/provider-gemini.js +7 -1
- package/src/provider-ollama.d.ts +4 -0
- package/src/provider-ollama.js +4 -0
- package/src/provider-openai.d.ts +4 -0
- package/src/provider-openai.js +4 -0
- package/src/recurse-retrieval.d.ts +22 -0
- package/src/recurse-retrieval.js +22 -0
- package/src/recurse.d.ts +6 -0
- package/src/recurse.js +6 -0
- package/src/refine.d.ts +5 -0
- package/src/refine.js +5 -0
- package/src/remember.d.ts +5 -0
- package/src/remember.js +5 -0
- package/src/retry.d.ts +8 -1
- package/src/retry.js +8 -1
- package/src/run-plan.d.ts +5 -0
- package/src/run-plan.js +5 -0
- package/src/scheduler.d.ts +8 -1
- package/src/scheduler.js +8 -1
- package/src/skills.d.ts +6 -0
- package/src/skills.js +6 -0
- package/src/stash.d.ts +5 -0
- package/src/stash.js +5 -0
- package/src/state.d.ts +8 -1
- package/src/state.js +8 -1
- package/src/store-jsonfile.d.ts +5 -0
- package/src/store-jsonfile.js +5 -0
- package/src/store-sqlite.d.ts +5 -0
- package/src/store-sqlite.js +5 -0
- package/src/stream.d.ts +5 -0
- package/src/stream.js +5 -0
- package/src/transport-jsonl.d.ts +4 -0
- package/src/transport-jsonl.js +4 -0
- package/tools/browse.d.ts +5 -0
- package/tools/browse.js +5 -0
- package/tools/defer.d.ts +13 -1
- package/tools/defer.js +13 -1
- package/tools/litectx-mcp.d.ts +6 -0
- package/tools/litectx-mcp.js +6 -0
- package/tools/mobile.d.ts +5 -0
- package/tools/mobile.js +5 -0
- package/tools/shell.d.ts +5 -0
- package/tools/shell.js +5 -0
- package/tools/spawn.d.ts +10 -0
- package/tools/spawn.js +10 -0
package/src/complexity.js
CHANGED
|
@@ -89,6 +89,11 @@ const MAX_ASSESS_LEN = 4000;
|
|
|
89
89
|
* Assess the complexity of a goal/prompt from its text alone (no LLM).
|
|
90
90
|
* @param {string} prompt - The goal to classify.
|
|
91
91
|
* @returns {ComplexityResult}
|
|
92
|
+
* @when you want a fast, no-LLM read of how hard a goal is (simple/medium/complex/critical) to decide whether to invoke the Planner
|
|
93
|
+
* @fails never throws — pure text scoring; non-string/blank input scores lowest. Keyword lists are frozen and a critical-safety override always wins.
|
|
94
|
+
* @example
|
|
95
|
+
* const { level, needsPlanning } = assessComplexity(goal);
|
|
96
|
+
* if (needsPlanning) await planner.plan(goal);
|
|
92
97
|
*/
|
|
93
98
|
function assessComplexity(prompt) {
|
|
94
99
|
if (typeof prompt !== 'string' || !prompt.trim()) {
|
|
@@ -169,6 +174,10 @@ function assessComplexity(prompt) {
|
|
|
169
174
|
* non-string / blank input is `false`.
|
|
170
175
|
* @param {string} prompt - The goal to test.
|
|
171
176
|
* @returns {boolean}
|
|
177
|
+
* @when you want the durable critical-safety floor alone (security/production/compliance/financial) to gate extra scrutiny, without the full scorer
|
|
178
|
+
* @fails never throws — deterministic override; non-string/blank input is false. This floor is non-overridable by design.
|
|
179
|
+
* @example
|
|
180
|
+
* if (isCritical(goal)) verdict = await evaluator.evaluate(goal, result, { contract });
|
|
172
181
|
*/
|
|
173
182
|
function isCritical(prompt) {
|
|
174
183
|
if (typeof prompt !== 'string' || !prompt.trim()) return false;
|
package/src/context-units.d.ts
CHANGED
|
@@ -32,6 +32,10 @@ export function fromUnits(units: Array<Record<string, any>>): Array<Record<strin
|
|
|
32
32
|
* error (incl. HaltError) is left to the Loop's own fail-open / HaltError handling — not swallowed here.
|
|
33
33
|
* @param {(units: Array<Record<string, any>>, ctx: any) => (any | Promise<any>)} assembleUnits
|
|
34
34
|
* @returns {(msgs: Array<Record<string, any>>, ctx: any) => Promise<Array<Record<string, any>>>}
|
|
35
|
+
* @when you want to adapt a litectx-style neutral-unit assemble(units, ctx) verb into the Loop's assemble(msgs, ctx) context-assembly seam
|
|
36
|
+
* @fails fail-open — any unexpected return shape sends the original msgs unchanged; a thrown error (incl. HaltError) is left to the Loop's own handling.
|
|
37
|
+
* @example
|
|
38
|
+
* const loop = new Loop({ provider, assemble: unitAssembler(litectx.assemble) });
|
|
35
39
|
*/
|
|
36
40
|
export function unitAssembler(assembleUnits: (units: Array<Record<string, any>>, ctx: any) => (any | Promise<any>)): (msgs: Array<Record<string, any>>, ctx: any) => Promise<Array<Record<string, any>>>;
|
|
37
41
|
/**
|
|
@@ -67,6 +71,10 @@ export function unitAssembler(assembleUnits: (units: Array<Record<string, any>>,
|
|
|
67
71
|
* `({ key, content, unit }) => void|Promise` (REQUIRED; the harvest policy point). `policy` — litectx
|
|
68
72
|
* TrimPolicy: `{ keepLastN }` or `{ maxTokens }` (maxTokens wins). Both verbs are runtime-checked.
|
|
69
73
|
* @returns {((msgs: Array<Record<string, any>>, ctx?: any) => Promise<Array<Record<string, any>>>) & { flush: (msgs: Array<Record<string, any>>, ctx?: any) => Promise<void> }}
|
|
74
|
+
* @when you want to adapt litectx's trim(units, policy) verb into the Loop's destructive trim(msgs, ctx) seam — harvest-before-evict with an F2 residual .flush
|
|
75
|
+
* @fails throws if the trim/onHarvest verbs are missing (runtime-checked); the fold is fail-open and a HaltError propagates. `.flush` drains the residual harvest.
|
|
76
|
+
* @example
|
|
77
|
+
* const loop = new Loop({ provider, trim: unitTrimmer({ trim: litectx.trim, onHarvest, policy: { keepLastN: 20 } }) });
|
|
70
78
|
*/
|
|
71
79
|
export function unitTrimmer(opts?: {
|
|
72
80
|
trim?: Function;
|
|
@@ -93,6 +101,10 @@ export function unitTrimmer(opts?: {
|
|
|
93
101
|
* distinct turns). Normal provider ids (`call_…`) round-trip unchanged through the escape.
|
|
94
102
|
* @param {Record<string, any>} unit - a unit from {@link toUnits} (its `_msgs` backing is read).
|
|
95
103
|
* @returns {string}
|
|
104
|
+
* @when you need the stable content-address for a transcript unit (the key harvest-before-evict writes under) — a collision-resistant 64-bit id
|
|
105
|
+
* @fails never throws; normal provider ids round-trip unchanged, and two near-independent hash streams avoid 32-bit birthday collisions.
|
|
106
|
+
* @example
|
|
107
|
+
* const key = harvestKey(unit); // stable id for this turn's harvest
|
|
96
108
|
*/
|
|
97
109
|
export function harvestKey(unit: Record<string, any>): string;
|
|
98
110
|
/** chars/4 token estimate over a list of messages (matches poc2 / the Loop's own heuristic). */
|
package/src/context-units.js
CHANGED
|
@@ -207,6 +207,10 @@ function fromUnits(units) {
|
|
|
207
207
|
* error (incl. HaltError) is left to the Loop's own fail-open / HaltError handling — not swallowed here.
|
|
208
208
|
* @param {(units: Array<Record<string, any>>, ctx: any) => (any | Promise<any>)} assembleUnits
|
|
209
209
|
* @returns {(msgs: Array<Record<string, any>>, ctx: any) => Promise<Array<Record<string, any>>>}
|
|
210
|
+
* @when you want to adapt a litectx-style neutral-unit assemble(units, ctx) verb into the Loop's assemble(msgs, ctx) context-assembly seam
|
|
211
|
+
* @fails fail-open — any unexpected return shape sends the original msgs unchanged; a thrown error (incl. HaltError) is left to the Loop's own handling.
|
|
212
|
+
* @example
|
|
213
|
+
* const loop = new Loop({ provider, assemble: unitAssembler(litectx.assemble) });
|
|
210
214
|
*/
|
|
211
215
|
function unitAssembler(assembleUnits) {
|
|
212
216
|
if (typeof assembleUnits !== 'function') {
|
|
@@ -240,6 +244,10 @@ function unitAssembler(assembleUnits) {
|
|
|
240
244
|
* distinct turns). Normal provider ids (`call_…`) round-trip unchanged through the escape.
|
|
241
245
|
* @param {Record<string, any>} unit - a unit from {@link toUnits} (its `_msgs` backing is read).
|
|
242
246
|
* @returns {string}
|
|
247
|
+
* @when you need the stable content-address for a transcript unit (the key harvest-before-evict writes under) — a collision-resistant 64-bit id
|
|
248
|
+
* @fails never throws; normal provider ids round-trip unchanged, and two near-independent hash streams avoid 32-bit birthday collisions.
|
|
249
|
+
* @example
|
|
250
|
+
* const key = harvestKey(unit); // stable id for this turn's harvest
|
|
243
251
|
*/
|
|
244
252
|
function harvestKey(unit) {
|
|
245
253
|
const back = (unit && unit._msgs) || [];
|
|
@@ -296,6 +304,10 @@ function harvestKey(unit) {
|
|
|
296
304
|
* `({ key, content, unit }) => void|Promise` (REQUIRED; the harvest policy point). `policy` — litectx
|
|
297
305
|
* TrimPolicy: `{ keepLastN }` or `{ maxTokens }` (maxTokens wins). Both verbs are runtime-checked.
|
|
298
306
|
* @returns {((msgs: Array<Record<string, any>>, ctx?: any) => Promise<Array<Record<string, any>>>) & { flush: (msgs: Array<Record<string, any>>, ctx?: any) => Promise<void> }}
|
|
307
|
+
* @when you want to adapt litectx's trim(units, policy) verb into the Loop's destructive trim(msgs, ctx) seam — harvest-before-evict with an F2 residual .flush
|
|
308
|
+
* @fails throws if the trim/onHarvest verbs are missing (runtime-checked); the fold is fail-open and a HaltError propagates. `.flush` drains the residual harvest.
|
|
309
|
+
* @example
|
|
310
|
+
* const loop = new Loop({ provider, trim: unitTrimmer({ trim: litectx.trim, onHarvest, policy: { keepLastN: 20 } }) });
|
|
299
311
|
*/
|
|
300
312
|
function unitTrimmer(opts) {
|
|
301
313
|
const { trim, onHarvest, policy = {} } = opts || {};
|
package/src/evaluator.d.ts
CHANGED
|
@@ -114,7 +114,15 @@ export type EvaluateOptions = {
|
|
|
114
114
|
* Built flagged-and-deletable per D11 — opt-in by import; calibrate the rubric/prompt from execution traces.
|
|
115
115
|
*/
|
|
116
116
|
export class Evaluator {
|
|
117
|
-
/**
|
|
117
|
+
/**
|
|
118
|
+
* @param {EvaluatorOptions} [options]
|
|
119
|
+
* @when you need to judge an output against a goal or contract — deterministically (predicate), by LLM rubric, or with a tool-running critic that exercises the live artifact
|
|
120
|
+
* @fails never throws for a bad grade — returns a Verdict {status: satisfied|needs_revision|failed}; a provider HaltError propagates clean. Judge tokens forward via onLlmResult.
|
|
121
|
+
* @example
|
|
122
|
+
* const evaluator = new Evaluator({ provider });
|
|
123
|
+
* const verdict = await evaluator.evaluate(goal, result, { rubric });
|
|
124
|
+
* if (!verdict.pass) revise(verdict.critique);
|
|
125
|
+
*/
|
|
118
126
|
constructor(options?: EvaluatorOptions);
|
|
119
127
|
provider: import("../types").Provider | null;
|
|
120
128
|
prompt: string;
|
package/src/evaluator.js
CHANGED
|
@@ -108,7 +108,15 @@ Output your FINAL answer as ONLY this JSON, no markdown, no prose:
|
|
|
108
108
|
* Built flagged-and-deletable per D11 — opt-in by import; calibrate the rubric/prompt from execution traces.
|
|
109
109
|
*/
|
|
110
110
|
class Evaluator {
|
|
111
|
-
/**
|
|
111
|
+
/**
|
|
112
|
+
* @param {EvaluatorOptions} [options]
|
|
113
|
+
* @when you need to judge an output against a goal or contract — deterministically (predicate), by LLM rubric, or with a tool-running critic that exercises the live artifact
|
|
114
|
+
* @fails never throws for a bad grade — returns a Verdict {status: satisfied|needs_revision|failed}; a provider HaltError propagates clean. Judge tokens forward via onLlmResult.
|
|
115
|
+
* @example
|
|
116
|
+
* const evaluator = new Evaluator({ provider });
|
|
117
|
+
* const verdict = await evaluator.evaluate(goal, result, { rubric });
|
|
118
|
+
* if (!verdict.pass) revise(verdict.critique);
|
|
119
|
+
*/
|
|
112
120
|
constructor(options = /** @type {EvaluatorOptions} */ ({})) {
|
|
113
121
|
this.provider = options.provider || null;
|
|
114
122
|
this.prompt = options.prompt || GRADER_PROMPT;
|
|
@@ -129,6 +129,11 @@ export function gradeRun(runs: Array<{
|
|
|
129
129
|
* a separate admission gate — a leak in any style blocks admission even at a passing clear-case floor.
|
|
130
130
|
* @param {(payload:object)=>any} [opts.onLlmResult] - Budget hook forwarded to each judge call.
|
|
131
131
|
* @returns {Promise<ReturnType<typeof gradeRun> & { reps:number, floor:number, totalCostUsd:number|null, unpricedCalls:number, injectionBattery: { styles: Array<{label:string, usable:number, broke:number, resisted:boolean}>, allResisted:boolean, leaks:number } }>}
|
|
132
|
+
* @when you are admitting an LLM tier to the judge role and need to grade it against the frozen clear-case battery and resist every injection style before trusting it
|
|
133
|
+
* @fails never fabricates a pass — a leak in any injection style blocks admission even above the clear-case floor, and the negative control must fail the set. HaltError propagates clean.
|
|
134
|
+
* @example
|
|
135
|
+
* const report = await calibrate({ provider, reps: 5, floor: 7 });
|
|
136
|
+
* if (!report.injectionBattery.allResisted) reject('injection leak');
|
|
132
137
|
*/
|
|
133
138
|
export function calibrate(opts?: {
|
|
134
139
|
provider: import("../types").Provider;
|
package/src/judge-calibration.js
CHANGED
|
@@ -125,6 +125,11 @@ function gradeRun(runs, floor) {
|
|
|
125
125
|
* a separate admission gate — a leak in any style blocks admission even at a passing clear-case floor.
|
|
126
126
|
* @param {(payload:object)=>any} [opts.onLlmResult] - Budget hook forwarded to each judge call.
|
|
127
127
|
* @returns {Promise<ReturnType<typeof gradeRun> & { reps:number, floor:number, totalCostUsd:number|null, unpricedCalls:number, injectionBattery: { styles: Array<{label:string, usable:number, broke:number, resisted:boolean}>, allResisted:boolean, leaks:number } }>}
|
|
128
|
+
* @when you are admitting an LLM tier to the judge role and need to grade it against the frozen clear-case battery and resist every injection style before trusting it
|
|
129
|
+
* @fails never fabricates a pass — a leak in any injection style blocks admission even above the clear-case floor, and the negative control must fail the set. HaltError propagates clean.
|
|
130
|
+
* @example
|
|
131
|
+
* const report = await calibrate({ provider, reps: 5, floor: 7 });
|
|
132
|
+
* if (!report.injectionBattery.allResisted) reject('injection leak');
|
|
128
133
|
*/
|
|
129
134
|
async function calibrate(opts = /** @type {any} */ ({})) {
|
|
130
135
|
const provider = opts.provider;
|
package/src/judge.d.ts
CHANGED
|
@@ -157,6 +157,11 @@ export type JudgeVerdict = {
|
|
|
157
157
|
* @throws {ValidationError} on bad inputs (missing request/artifact/provider) — stamped `context.lib='bare-agent'`
|
|
158
158
|
* at the throw site (contract 4: typed attribution, never sniffed from prose).
|
|
159
159
|
* @throws {HaltError} propagated clean from the provider (a governance halt is not a judge failure).
|
|
160
|
+
* @when you need a decisive honored/broke verdict on whether one egress artifact honored the verbatim request — a return-time integrity check
|
|
161
|
+
* @fails throws ValidationError on bad inputs; HaltError propagates clean. Cannot-confirm-honored floors to `broke`; truncation/parse-error are distinct flagged outcomes, never laundered to honored.
|
|
162
|
+
* @example
|
|
163
|
+
* const { verdict, where } = await judge({ request, artifact, provider });
|
|
164
|
+
* if (verdict === 'broke') flag(where);
|
|
160
165
|
*/
|
|
161
166
|
export function judge(options?: JudgeOptions): Promise<JudgeVerdict>;
|
|
162
167
|
/**
|
package/src/judge.js
CHANGED
|
@@ -145,6 +145,11 @@ function normalizeWhere(where) {
|
|
|
145
145
|
* @throws {ValidationError} on bad inputs (missing request/artifact/provider) — stamped `context.lib='bare-agent'`
|
|
146
146
|
* at the throw site (contract 4: typed attribution, never sniffed from prose).
|
|
147
147
|
* @throws {HaltError} propagated clean from the provider (a governance halt is not a judge failure).
|
|
148
|
+
* @when you need a decisive honored/broke verdict on whether one egress artifact honored the verbatim request — a return-time integrity check
|
|
149
|
+
* @fails throws ValidationError on bad inputs; HaltError propagates clean. Cannot-confirm-honored floors to `broke`; truncation/parse-error are distinct flagged outcomes, never laundered to honored.
|
|
150
|
+
* @example
|
|
151
|
+
* const { verdict, where } = await judge({ request, artifact, provider });
|
|
152
|
+
* if (verdict === 'broke') flag(where);
|
|
148
153
|
*/
|
|
149
154
|
async function judge(options = /** @type {JudgeOptions} */ ({})) {
|
|
150
155
|
const { request, artifact, provider } = options;
|
package/src/loop.d.ts
CHANGED
|
@@ -105,6 +105,12 @@ export class Loop {
|
|
|
105
105
|
* tool outcomes to `gate.record` (via wireGate) and never kill the loop on error.
|
|
106
106
|
* @param {LoopOptions} options
|
|
107
107
|
* @throws {Error} `[Loop] requires a provider` — when options.provider is missing.
|
|
108
|
+
* @when you are running a model think/act/observe cycle and need round accounting, tool dispatch, spin guards, and a governance chokepoint
|
|
109
|
+
* @fails returns the last assistant text with an `error` tag on any bound (halt/deny-streak/truncation/provider error); throws only if constructed without a provider. A policy HaltError exits clean.
|
|
110
|
+
* @example
|
|
111
|
+
* const loop = new Loop({ provider, policy });
|
|
112
|
+
* const { text, error, metrics } = await loop.run(messages, tools);
|
|
113
|
+
* if (error) handle(error);
|
|
108
114
|
*/
|
|
109
115
|
constructor(options?: LoopOptions);
|
|
110
116
|
provider: import("../types").Provider;
|
package/src/loop.js
CHANGED
|
@@ -273,6 +273,12 @@ class Loop {
|
|
|
273
273
|
* tool outcomes to `gate.record` (via wireGate) and never kill the loop on error.
|
|
274
274
|
* @param {LoopOptions} options
|
|
275
275
|
* @throws {Error} `[Loop] requires a provider` — when options.provider is missing.
|
|
276
|
+
* @when you are running a model think/act/observe cycle and need round accounting, tool dispatch, spin guards, and a governance chokepoint
|
|
277
|
+
* @fails returns the last assistant text with an `error` tag on any bound (halt/deny-streak/truncation/provider error); throws only if constructed without a provider. A policy HaltError exits clean.
|
|
278
|
+
* @example
|
|
279
|
+
* const loop = new Loop({ provider, policy });
|
|
280
|
+
* const { text, error, metrics } = await loop.run(messages, tools);
|
|
281
|
+
* if (error) handle(error);
|
|
276
282
|
*/
|
|
277
283
|
constructor(options = /** @type {LoopOptions} */ ({})) {
|
|
278
284
|
if (!options.provider) throw new Error('[Loop] requires a provider');
|
package/src/mcp-bridge.d.ts
CHANGED
|
@@ -92,6 +92,11 @@ export type RpcClient = {
|
|
|
92
92
|
* of this hook also opts default discovery into the project-cwd `./.mcp.json`,
|
|
93
93
|
* since each command is then vetted regardless of source.
|
|
94
94
|
* @returns {Promise<{tools: ToolDef[], metaTools?: ToolDef[], servers: string[], systemContext: string, denied: DeniedTool[], errors?: Array<{server: string, error: string}>, close: Function}>}
|
|
95
|
+
* @when you want to auto-discover MCP servers and expose them as bareagent tools in one call — with a trust hook gating command execution
|
|
96
|
+
* @fails a trust hook returning false skips a server (its command never runs) and a throw is fail-closed (deny); returns tools plus a close() to shut servers down.
|
|
97
|
+
* @example
|
|
98
|
+
* const { tools, close } = await createMCPBridge();
|
|
99
|
+
* const loop = new Loop({ provider, tools });
|
|
95
100
|
*/
|
|
96
101
|
export function createMCPBridge(opts?: {
|
|
97
102
|
bridgePath?: string | undefined;
|
|
@@ -120,6 +125,10 @@ export function createMCPBridge(opts?: {
|
|
|
120
125
|
* @param {{ includeProjectConfig?: boolean }} [opts] - When no explicit `configPaths` are given,
|
|
121
126
|
* set `includeProjectConfig: true` to also scan `./.mcp.json`. Default false — see PROJECT_CONFIG_PATH.
|
|
122
127
|
* @returns {Map<string, ServerDef>}
|
|
128
|
+
* @when you want to find configured MCP servers from the trusted $HOME/IDE config paths (or explicit paths) — discovery without invoking them
|
|
129
|
+
* @fails honors explicit configPaths verbatim and scans trusted defaults otherwise; returns a Map and never executes a server command.
|
|
130
|
+
* @example
|
|
131
|
+
* const servers = discoverServers();
|
|
123
132
|
*/
|
|
124
133
|
export function discoverServers(configPaths?: string[], { includeProjectConfig }?: {
|
|
125
134
|
includeProjectConfig?: boolean;
|
|
@@ -142,5 +151,9 @@ export function discoverServers(configPaths?: string[], { includeProjectConfig }
|
|
|
142
151
|
* @param {ToolDef[]} tools - The bulk-loaded, name-prefixed tools array.
|
|
143
152
|
* @param {string} [discoveredAt] - ISO timestamp from .mcp-bridge.json.
|
|
144
153
|
* @returns {ToolDef[]} [mcp_discover, mcp_invoke]
|
|
154
|
+
* @when you want the two bulk MCP meta-tools (mcp_discover, mcp_invoke) instead of exposing every discovered tool individually — one gate-check per invocation
|
|
155
|
+
* @fails returns [mcp_discover, mcp_invoke]; a tool name does not travel as action.type (a deliberate v0.9 trade for one gate-check per call).
|
|
156
|
+
* @example
|
|
157
|
+
* const metaTools = buildMetaTools(tools);
|
|
145
158
|
*/
|
|
146
159
|
export function buildMetaTools(tools: ToolDef[], discoveredAt?: string): ToolDef[];
|
package/src/mcp-bridge.js
CHANGED
|
@@ -80,6 +80,10 @@ const TRUSTED_CONFIG_PATHS = [
|
|
|
80
80
|
* @param {{ includeProjectConfig?: boolean }} [opts] - When no explicit `configPaths` are given,
|
|
81
81
|
* set `includeProjectConfig: true` to also scan `./.mcp.json`. Default false — see PROJECT_CONFIG_PATH.
|
|
82
82
|
* @returns {Map<string, ServerDef>}
|
|
83
|
+
* @when you want to find configured MCP servers from the trusted $HOME/IDE config paths (or explicit paths) — discovery without invoking them
|
|
84
|
+
* @fails honors explicit configPaths verbatim and scans trusted defaults otherwise; returns a Map and never executes a server command.
|
|
85
|
+
* @example
|
|
86
|
+
* const servers = discoverServers();
|
|
83
87
|
*/
|
|
84
88
|
function discoverServers(configPaths, { includeProjectConfig = false } = {}) {
|
|
85
89
|
let paths;
|
|
@@ -523,6 +527,10 @@ function buildSystemContext(servers, tools, denied) {
|
|
|
523
527
|
* @param {ToolDef[]} tools - The bulk-loaded, name-prefixed tools array.
|
|
524
528
|
* @param {string} [discoveredAt] - ISO timestamp from .mcp-bridge.json.
|
|
525
529
|
* @returns {ToolDef[]} [mcp_discover, mcp_invoke]
|
|
530
|
+
* @when you want the two bulk MCP meta-tools (mcp_discover, mcp_invoke) instead of exposing every discovered tool individually — one gate-check per invocation
|
|
531
|
+
* @fails returns [mcp_discover, mcp_invoke]; a tool name does not travel as action.type (a deliberate v0.9 trade for one gate-check per call).
|
|
532
|
+
* @example
|
|
533
|
+
* const metaTools = buildMetaTools(tools);
|
|
526
534
|
*/
|
|
527
535
|
function buildMetaTools(tools, discoveredAt) {
|
|
528
536
|
// Catalog descriptors: same info the LLM would see for bulk-loaded tools,
|
|
@@ -637,6 +645,11 @@ function buildMetaTools(tools, discoveredAt) {
|
|
|
637
645
|
* of this hook also opts default discovery into the project-cwd `./.mcp.json`,
|
|
638
646
|
* since each command is then vetted regardless of source.
|
|
639
647
|
* @returns {Promise<{tools: ToolDef[], metaTools?: ToolDef[], servers: string[], systemContext: string, denied: DeniedTool[], errors?: Array<{server: string, error: string}>, close: Function}>}
|
|
648
|
+
* @when you want to auto-discover MCP servers and expose them as bareagent tools in one call — with a trust hook gating command execution
|
|
649
|
+
* @fails a trust hook returning false skips a server (its command never runs) and a throw is fail-closed (deny); returns tools plus a close() to shut servers down.
|
|
650
|
+
* @example
|
|
651
|
+
* const { tools, close } = await createMCPBridge();
|
|
652
|
+
* const loop = new Loop({ provider, tools });
|
|
640
653
|
*/
|
|
641
654
|
async function createMCPBridge(opts = {}) {
|
|
642
655
|
if ('policy' in opts) {
|
package/src/memory.d.ts
CHANGED
|
@@ -19,6 +19,11 @@ export class Memory {
|
|
|
19
19
|
/**
|
|
20
20
|
* @param {{ store?: Store }} [options] - Store backend (must implement store/search/get/delete).
|
|
21
21
|
* @throws {Error} `[Memory] requires options.store` — when options.store is missing.
|
|
22
|
+
* @when you want thin store-backed memory (store/search/get/delete) an agent can write to and recall from, over any swappable backend
|
|
23
|
+
* @fails throws if constructed without a store; recall/store metering is opt-in via ctx. All persistence delegates to the backend.
|
|
24
|
+
* @example
|
|
25
|
+
* const memory = new Memory({ store: new JsonFile({ path: './mem.json' }) });
|
|
26
|
+
* await memory.store({ text: 'a durable fact' });
|
|
22
27
|
*/
|
|
23
28
|
constructor(options?: {
|
|
24
29
|
store?: Store;
|
package/src/memory.js
CHANGED
|
@@ -21,6 +21,11 @@ class Memory {
|
|
|
21
21
|
/**
|
|
22
22
|
* @param {{ store?: Store }} [options] - Store backend (must implement store/search/get/delete).
|
|
23
23
|
* @throws {Error} `[Memory] requires options.store` — when options.store is missing.
|
|
24
|
+
* @when you want thin store-backed memory (store/search/get/delete) an agent can write to and recall from, over any swappable backend
|
|
25
|
+
* @fails throws if constructed without a store; recall/store metering is opt-in via ctx. All persistence delegates to the backend.
|
|
26
|
+
* @example
|
|
27
|
+
* const memory = new Memory({ store: new JsonFile({ path: './mem.json' }) });
|
|
28
|
+
* await memory.store({ text: 'a durable fact' });
|
|
24
29
|
*/
|
|
25
30
|
constructor(options = {}) {
|
|
26
31
|
if (!options.store) throw new Error('[Memory] requires options.store');
|
package/src/planner.d.ts
CHANGED
|
@@ -46,6 +46,12 @@ export class Planner {
|
|
|
46
46
|
/**
|
|
47
47
|
* @param {PlannerOptions} options
|
|
48
48
|
* @throws {Error} `[Planner] requires a provider` — when options.provider is missing.
|
|
49
|
+
* @when you need to turn a goal into an ordered step DAG for an LLM to execute — optionally forcing exactly N independent steps for fan-out
|
|
50
|
+
* @fails throws if constructed without a provider; a plan call's HaltError (governance cap) propagates clean. Plan-call usage forwards via onLlmResult.
|
|
51
|
+
* @example
|
|
52
|
+
* const planner = new Planner({ provider });
|
|
53
|
+
* const steps = await planner.plan('ship the release', { count: 4 });
|
|
54
|
+
* await runPlan(steps, ctx);
|
|
49
55
|
*/
|
|
50
56
|
constructor(options?: PlannerOptions);
|
|
51
57
|
provider: import("../types").Provider;
|
package/src/planner.js
CHANGED
|
@@ -39,6 +39,12 @@ class Planner {
|
|
|
39
39
|
/**
|
|
40
40
|
* @param {PlannerOptions} options
|
|
41
41
|
* @throws {Error} `[Planner] requires a provider` — when options.provider is missing.
|
|
42
|
+
* @when you need to turn a goal into an ordered step DAG for an LLM to execute — optionally forcing exactly N independent steps for fan-out
|
|
43
|
+
* @fails throws if constructed without a provider; a plan call's HaltError (governance cap) propagates clean. Plan-call usage forwards via onLlmResult.
|
|
44
|
+
* @example
|
|
45
|
+
* const planner = new Planner({ provider });
|
|
46
|
+
* const steps = await planner.plan('ship the release', { count: 4 });
|
|
47
|
+
* await runPlan(steps, ctx);
|
|
42
48
|
*/
|
|
43
49
|
constructor(options = /** @type {PlannerOptions} */ ({})) {
|
|
44
50
|
if (!options.provider) throw new Error('[Planner] requires a provider');
|
|
@@ -62,6 +62,10 @@ export class AnthropicProvider {
|
|
|
62
62
|
/**
|
|
63
63
|
* @param {AnthropicOptions} [options]
|
|
64
64
|
* @throws {Error} `[AnthropicProvider] requires apiKey` — when apiKey is missing.
|
|
65
|
+
* @when you want Claude models as the Loop's provider — native Messages API with opt-in prompt caching and thinking-block passthrough
|
|
66
|
+
* @fails throws on a missing apiKey; normalizes stopReason and usage (usage:null when the API omits it); a socket idle/deadline/transport cut rejects with a retryable Timeout/ProviderError.
|
|
67
|
+
* @example
|
|
68
|
+
* const provider = new AnthropicProvider({ apiKey, model: 'claude-sonnet-5' });
|
|
65
69
|
*/
|
|
66
70
|
constructor(options?: AnthropicOptions);
|
|
67
71
|
apiKey: string;
|
|
@@ -41,6 +41,10 @@ class AnthropicProvider {
|
|
|
41
41
|
/**
|
|
42
42
|
* @param {AnthropicOptions} [options]
|
|
43
43
|
* @throws {Error} `[AnthropicProvider] requires apiKey` — when apiKey is missing.
|
|
44
|
+
* @when you want Claude models as the Loop's provider — native Messages API with opt-in prompt caching and thinking-block passthrough
|
|
45
|
+
* @fails throws on a missing apiKey; normalizes stopReason and usage (usage:null when the API omits it); a socket idle/deadline/transport cut rejects with a retryable Timeout/ProviderError.
|
|
46
|
+
* @example
|
|
47
|
+
* const provider = new AnthropicProvider({ apiKey, model: 'claude-sonnet-5' });
|
|
44
48
|
*/
|
|
45
49
|
constructor(options = {}) {
|
|
46
50
|
if (!options.apiKey) throw new Error('[AnthropicProvider] requires apiKey');
|
|
@@ -123,6 +123,10 @@ export class CLIPipeProvider {
|
|
|
123
123
|
* Provider that pipes prompts to a CLI command via stdin and reads stdout.
|
|
124
124
|
* @param {CLIPipeOptions} [options]
|
|
125
125
|
* @throws {Error} `[CLIPipeProvider] requires command` — when options.command is missing.
|
|
126
|
+
* @when you want to drive a CLI (e.g. the claude CLI) as the Loop's provider — native MCP tools or a JSON emulation envelope
|
|
127
|
+
* @fails throws on a missing command; in native mode it owns its own cycle (ownsCycle), so per-round Loop seams are refused at construction rather than left silently dead.
|
|
128
|
+
* @example
|
|
129
|
+
* const provider = new CLIPipeProvider({ command: 'claude' });
|
|
126
130
|
*/
|
|
127
131
|
constructor(options?: CLIPipeOptions);
|
|
128
132
|
command: string;
|
package/src/provider-clipipe.js
CHANGED
|
@@ -112,6 +112,10 @@ class CLIPipeProvider {
|
|
|
112
112
|
* Provider that pipes prompts to a CLI command via stdin and reads stdout.
|
|
113
113
|
* @param {CLIPipeOptions} [options]
|
|
114
114
|
* @throws {Error} `[CLIPipeProvider] requires command` — when options.command is missing.
|
|
115
|
+
* @when you want to drive a CLI (e.g. the claude CLI) as the Loop's provider — native MCP tools or a JSON emulation envelope
|
|
116
|
+
* @fails throws on a missing command; in native mode it owns its own cycle (ownsCycle), so per-round Loop seams are refused at construction rather than left silently dead.
|
|
117
|
+
* @example
|
|
118
|
+
* const provider = new CLIPipeProvider({ command: 'claude' });
|
|
115
119
|
*/
|
|
116
120
|
constructor(options = {}) {
|
|
117
121
|
if (!options.command) throw new Error('[CLIPipeProvider] requires command');
|
|
@@ -27,6 +27,10 @@ export class FallbackProvider {
|
|
|
27
27
|
* @param {Provider[]} providers - Ordered list of providers with generate().
|
|
28
28
|
* @param {FallbackOptions} [options={}]
|
|
29
29
|
* @throws {Error} `[FallbackProvider] requires at least one provider` — when providers is empty.
|
|
30
|
+
* @when you want to try several providers in order, failing over to the next when one errors — resilience across tiers or vendors
|
|
31
|
+
* @fails throws if given no providers; returns the first provider's success, else propagates the last provider's error.
|
|
32
|
+
* @example
|
|
33
|
+
* const provider = new FallbackProvider([primary, backup]);
|
|
30
34
|
*/
|
|
31
35
|
constructor(providers: Provider[], options?: FallbackOptions);
|
|
32
36
|
providers: import("../types").Provider[];
|
package/src/provider-fallback.js
CHANGED
|
@@ -17,6 +17,10 @@ class FallbackProvider {
|
|
|
17
17
|
* @param {Provider[]} providers - Ordered list of providers with generate().
|
|
18
18
|
* @param {FallbackOptions} [options={}]
|
|
19
19
|
* @throws {Error} `[FallbackProvider] requires at least one provider` — when providers is empty.
|
|
20
|
+
* @when you want to try several providers in order, failing over to the next when one errors — resilience across tiers or vendors
|
|
21
|
+
* @fails throws if given no providers; returns the first provider's success, else propagates the last provider's error.
|
|
22
|
+
* @example
|
|
23
|
+
* const provider = new FallbackProvider([primary, backup]);
|
|
20
24
|
*/
|
|
21
25
|
constructor(providers, options = {}) {
|
|
22
26
|
if (!Array.isArray(providers) || providers.length === 0) {
|
package/src/provider-gemini.d.ts
CHANGED
|
@@ -45,7 +45,13 @@ export type GeminiOptions = {
|
|
|
45
45
|
* cache-read tier populates with no opt-in.
|
|
46
46
|
*/
|
|
47
47
|
export class GeminiProvider {
|
|
48
|
-
/**
|
|
48
|
+
/**
|
|
49
|
+
* @param {GeminiOptions} [options]
|
|
50
|
+
* @when you want Gemini models as the Loop's provider — native generateContent with implicit prompt caching
|
|
51
|
+
* @fails throws on a missing apiKey; normalizes stopReason (promoting a complete tool call) and usage; a socket idle/deadline/transport cut rejects with a retryable error.
|
|
52
|
+
* @example
|
|
53
|
+
* const provider = new GeminiProvider({ apiKey, model: 'gemini-2.5-pro' });
|
|
54
|
+
*/
|
|
49
55
|
constructor(options?: GeminiOptions);
|
|
50
56
|
apiKey: string | undefined;
|
|
51
57
|
model: string;
|
package/src/provider-gemini.js
CHANGED
|
@@ -40,7 +40,13 @@ function isLoopbackHost(hostname) {
|
|
|
40
40
|
* cache-read tier populates with no opt-in.
|
|
41
41
|
*/
|
|
42
42
|
class GeminiProvider {
|
|
43
|
-
/**
|
|
43
|
+
/**
|
|
44
|
+
* @param {GeminiOptions} [options]
|
|
45
|
+
* @when you want Gemini models as the Loop's provider — native generateContent with implicit prompt caching
|
|
46
|
+
* @fails throws on a missing apiKey; normalizes stopReason (promoting a complete tool call) and usage; a socket idle/deadline/transport cut rejects with a retryable error.
|
|
47
|
+
* @example
|
|
48
|
+
* const provider = new GeminiProvider({ apiKey, model: 'gemini-2.5-pro' });
|
|
49
|
+
*/
|
|
44
50
|
constructor(options = {}) {
|
|
45
51
|
this.apiKey = options.apiKey?.trim();
|
|
46
52
|
this.model = options.model || 'gemini-2.5-flash';
|
package/src/provider-ollama.d.ts
CHANGED
|
@@ -28,6 +28,10 @@ export type OllamaOptions = {
|
|
|
28
28
|
export class OllamaProvider {
|
|
29
29
|
/**
|
|
30
30
|
* @param {OllamaOptions} [options]
|
|
31
|
+
* @when you want local models via Ollama as the Loop's provider — no API key, self-hosted
|
|
32
|
+
* @fails normalizes stopReason (promoting a complete tool call) and usage; a malformed tool-call JSON returns no usable calls with usage metered; a socket idle/deadline cut rejects with a retryable error.
|
|
33
|
+
* @example
|
|
34
|
+
* const provider = new OllamaProvider({ model: 'llama3' });
|
|
31
35
|
*/
|
|
32
36
|
constructor(options?: OllamaOptions);
|
|
33
37
|
model: string;
|
package/src/provider-ollama.js
CHANGED
|
@@ -27,6 +27,10 @@ const OLLAMA_USAGE_KEYS = ['prompt_eval_count', 'eval_count'];
|
|
|
27
27
|
class OllamaProvider {
|
|
28
28
|
/**
|
|
29
29
|
* @param {OllamaOptions} [options]
|
|
30
|
+
* @when you want local models via Ollama as the Loop's provider — no API key, self-hosted
|
|
31
|
+
* @fails normalizes stopReason (promoting a complete tool call) and usage; a malformed tool-call JSON returns no usable calls with usage metered; a socket idle/deadline cut rejects with a retryable error.
|
|
32
|
+
* @example
|
|
33
|
+
* const provider = new OllamaProvider({ model: 'llama3' });
|
|
30
34
|
*/
|
|
31
35
|
constructor(options = {}) {
|
|
32
36
|
this.model = options.model || 'llama3.2';
|
package/src/provider-openai.d.ts
CHANGED
|
@@ -68,6 +68,10 @@ export type OpenAIOptions = {
|
|
|
68
68
|
export class OpenAIProvider {
|
|
69
69
|
/**
|
|
70
70
|
* @param {OpenAIOptions} [options]
|
|
71
|
+
* @when you want OpenAI or any OpenAI-compatible endpoint as the Loop's provider — tool-calling, toolChoice, and a custom baseUrl
|
|
72
|
+
* @fails throws on a missing apiKey; a malformed tool-call JSON returns no usable calls with usage still metered (BA-27); a socket idle/deadline/transport cut rejects with a retryable error.
|
|
73
|
+
* @example
|
|
74
|
+
* const provider = new OpenAIProvider({ apiKey, model: 'gpt-5' });
|
|
71
75
|
*/
|
|
72
76
|
constructor(options?: OpenAIOptions);
|
|
73
77
|
apiKey: string | undefined;
|
package/src/provider-openai.js
CHANGED
|
@@ -76,6 +76,10 @@ function isLoopbackHost(hostname) {
|
|
|
76
76
|
class OpenAIProvider {
|
|
77
77
|
/**
|
|
78
78
|
* @param {OpenAIOptions} [options]
|
|
79
|
+
* @when you want OpenAI or any OpenAI-compatible endpoint as the Loop's provider — tool-calling, toolChoice, and a custom baseUrl
|
|
80
|
+
* @fails throws on a missing apiKey; a malformed tool-call JSON returns no usable calls with usage still metered (BA-27); a socket idle/deadline/transport cut rejects with a retryable error.
|
|
81
|
+
* @example
|
|
82
|
+
* const provider = new OpenAIProvider({ apiKey, model: 'gpt-5' });
|
|
79
83
|
*/
|
|
80
84
|
constructor(options = {}) {
|
|
81
85
|
this.apiKey = options.apiKey?.trim();
|
|
@@ -99,6 +99,12 @@ export function normalizeCorpus(corpus: unknown): Slice[];
|
|
|
99
99
|
* @param {{recall: Function}} litectx
|
|
100
100
|
* @param {{kinds?: string[], n?: number}} [opts]
|
|
101
101
|
* @returns {ToolDef}
|
|
102
|
+
* @category integration
|
|
103
|
+
* @when you want to give a recurse worker a needle-search handle over litectx (embeddings recall for the relevant few) — for FINDING, never counting
|
|
104
|
+
* @fails returns matched bodies only and CANNOT count (the completeness guard blocks a "how many" ask before it is offered); a dead window yields nothing, never a fabricated hit.
|
|
105
|
+
* @example
|
|
106
|
+
* const tool = buildSearchTool(litectx, { n: 8 });
|
|
107
|
+
* await recurse(task, ctx, { retrieval: 'search', tools: [tool] });
|
|
102
108
|
*/
|
|
103
109
|
export function buildSearchTool(litectx: {
|
|
104
110
|
recall: Function;
|
|
@@ -114,6 +120,11 @@ export function buildSearchTool(litectx: {
|
|
|
114
120
|
* OFF to stay exact — deferred; the code-side filter is the embeddings-free path shipped now.)
|
|
115
121
|
* @param {Slice[]} corpus - The validated slice-source.
|
|
116
122
|
* @returns {ToolDef}
|
|
123
|
+
* @when you want an embeddings-free, exact AND-term filter handle over a slice-source — complete over its slices, for precise lexical matches
|
|
124
|
+
* @fails only as good as a lexical rule; complete over the slices it is given (no recall cap) but matches nothing outside them.
|
|
125
|
+
* @example
|
|
126
|
+
* const tool = buildExactTool(corpus);
|
|
127
|
+
* await recurse(task, ctx, { retrieval: 'exact', tools: [tool] });
|
|
117
128
|
*/
|
|
118
129
|
export function buildExactTool(corpus: Slice[]): ToolDef;
|
|
119
130
|
/**
|
|
@@ -131,6 +142,11 @@ export function buildExactTool(corpus: Slice[]): ToolDef;
|
|
|
131
142
|
* materialized lazily on first call and cached for the tool's lifetime.
|
|
132
143
|
* @param {{provider: Provider, window?: number, passes?: number, ctx?: object, onLlmResult?: Function, policy?: Function}} opts
|
|
133
144
|
* @returns {ToolDef}
|
|
145
|
+
* @when you need the complete count/"all" path — scan every slice + LLM-judge + code-count — the only retrieval mode that can honestly answer "how many"
|
|
146
|
+
* @fails a dead window surfaces as `INCOMPLETE — the count is a floor`, never a clean number over a hole; a governance HaltError propagates clean.
|
|
147
|
+
* @example
|
|
148
|
+
* const tool = buildScanTool(corpus, { provider, window: 8 });
|
|
149
|
+
* await recurse('how many mention X', ctx, { tools: [tool] });
|
|
134
150
|
*/
|
|
135
151
|
export function buildScanTool(corpus: Slice[] | (() => Promise<Slice[]>), opts: {
|
|
136
152
|
provider: Provider;
|
|
@@ -154,6 +170,12 @@ export function buildScanTool(corpus: Slice[] | (() => Promise<Slice[]>), opts:
|
|
|
154
170
|
* @param {{enumerate: Function}} litectx
|
|
155
171
|
* @param {{kind?: 'fact'|'episode', pageSize?: number}} [opts]
|
|
156
172
|
* @returns {() => Promise<Slice[]>}
|
|
173
|
+
* @category integration
|
|
174
|
+
* @when you want a resident slice-source that paginates a litectx corpus via enumerate — for a corpus ALREADY in litectx, feeding scan/partition
|
|
175
|
+
* @fails never ingests a fresh corpus (strictly worse than scanning an in-hand array); returns an async source materialized once and cached.
|
|
176
|
+
* @example
|
|
177
|
+
* const corpus = litectxCorpus(litectx, { kind: 'fact' });
|
|
178
|
+
* await recurse(task, ctx, { mode: 'partition', corpus });
|
|
157
179
|
*/
|
|
158
180
|
export function litectxCorpus(litectx: {
|
|
159
181
|
enumerate: Function;
|
package/src/recurse-retrieval.js
CHANGED
|
@@ -188,6 +188,12 @@ function normalizeCorpus(corpus) {
|
|
|
188
188
|
* @param {{recall: Function}} litectx
|
|
189
189
|
* @param {{kinds?: string[], n?: number}} [opts]
|
|
190
190
|
* @returns {ToolDef}
|
|
191
|
+
* @category integration
|
|
192
|
+
* @when you want to give a recurse worker a needle-search handle over litectx (embeddings recall for the relevant few) — for FINDING, never counting
|
|
193
|
+
* @fails returns matched bodies only and CANNOT count (the completeness guard blocks a "how many" ask before it is offered); a dead window yields nothing, never a fabricated hit.
|
|
194
|
+
* @example
|
|
195
|
+
* const tool = buildSearchTool(litectx, { n: 8 });
|
|
196
|
+
* await recurse(task, ctx, { retrieval: 'search', tools: [tool] });
|
|
191
197
|
*/
|
|
192
198
|
function buildSearchTool(litectx, opts = {}) {
|
|
193
199
|
const kinds = Array.isArray(opts.kinds) && opts.kinds.length ? opts.kinds : ['fact', 'episode'];
|
|
@@ -224,6 +230,11 @@ function buildSearchTool(litectx, opts = {}) {
|
|
|
224
230
|
* OFF to stay exact — deferred; the code-side filter is the embeddings-free path shipped now.)
|
|
225
231
|
* @param {Slice[]} corpus - The validated slice-source.
|
|
226
232
|
* @returns {ToolDef}
|
|
233
|
+
* @when you want an embeddings-free, exact AND-term filter handle over a slice-source — complete over its slices, for precise lexical matches
|
|
234
|
+
* @fails only as good as a lexical rule; complete over the slices it is given (no recall cap) but matches nothing outside them.
|
|
235
|
+
* @example
|
|
236
|
+
* const tool = buildExactTool(corpus);
|
|
237
|
+
* await recurse(task, ctx, { retrieval: 'exact', tools: [tool] });
|
|
227
238
|
*/
|
|
228
239
|
function buildExactTool(corpus) {
|
|
229
240
|
const slices = normalizeCorpus(corpus);
|
|
@@ -269,6 +280,11 @@ function buildExactTool(corpus) {
|
|
|
269
280
|
* materialized lazily on first call and cached for the tool's lifetime.
|
|
270
281
|
* @param {{provider: Provider, window?: number, passes?: number, ctx?: object, onLlmResult?: Function, policy?: Function}} opts
|
|
271
282
|
* @returns {ToolDef}
|
|
283
|
+
* @when you need the complete count/"all" path — scan every slice + LLM-judge + code-count — the only retrieval mode that can honestly answer "how many"
|
|
284
|
+
* @fails a dead window surfaces as `INCOMPLETE — the count is a floor`, never a clean number over a hole; a governance HaltError propagates clean.
|
|
285
|
+
* @example
|
|
286
|
+
* const tool = buildScanTool(corpus, { provider, window: 8 });
|
|
287
|
+
* await recurse('how many mention X', ctx, { tools: [tool] });
|
|
272
288
|
*/
|
|
273
289
|
function buildScanTool(corpus, opts) {
|
|
274
290
|
/** @type {Slice[]|null} */
|
|
@@ -337,6 +353,12 @@ const ENUM_PAGE = 200;
|
|
|
337
353
|
* @param {{enumerate: Function}} litectx
|
|
338
354
|
* @param {{kind?: 'fact'|'episode', pageSize?: number}} [opts]
|
|
339
355
|
* @returns {() => Promise<Slice[]>}
|
|
356
|
+
* @category integration
|
|
357
|
+
* @when you want a resident slice-source that paginates a litectx corpus via enumerate — for a corpus ALREADY in litectx, feeding scan/partition
|
|
358
|
+
* @fails never ingests a fresh corpus (strictly worse than scanning an in-hand array); returns an async source materialized once and cached.
|
|
359
|
+
* @example
|
|
360
|
+
* const corpus = litectxCorpus(litectx, { kind: 'fact' });
|
|
361
|
+
* await recurse(task, ctx, { mode: 'partition', corpus });
|
|
340
362
|
*/
|
|
341
363
|
function litectxCorpus(litectx, opts = {}) {
|
|
342
364
|
const kind = opts.kind === 'episode' ? 'episode' : 'fact'; // enumerate v1 is the memory axis (fact/episode)
|
package/src/recurse.d.ts
CHANGED
|
@@ -604,5 +604,11 @@ export type Slice = {
|
|
|
604
604
|
* @returns {Promise<RecurseResult>} `{ result, verdict, receipts }` on convergence; `{ incomplete, best,
|
|
605
605
|
* receipts }` on guard exhaustion. NEVER a fabricated success (RC-9).
|
|
606
606
|
* @throws {Error} no provider supplied (on neither `ctx.provider` nor `opts.provider`).
|
|
607
|
+
* @when a task is too big for one model pass and you want it split, fanned out, verified, and merged — with total cost capped by a gate
|
|
608
|
+
* @fails returns `{incomplete, best}` on guard exhaustion or a dead worker (never a faked pass); a gate HaltError exits clean. Cost is open by design — run under a budget gate.
|
|
609
|
+
* @example
|
|
610
|
+
* const ctx = wireGate(gate);
|
|
611
|
+
* const { result, incomplete } = await recurse('audit 400 logs', ctx, { provider, corpus });
|
|
612
|
+
* if (incomplete) retryOrEscalate(result);
|
|
607
613
|
*/
|
|
608
614
|
export function recurse(task: string, ctx?: RecurseCtx, opts?: RecurseOptions): Promise<RecurseResult>;
|
package/src/recurse.js
CHANGED
|
@@ -395,6 +395,12 @@ function auditSafeCtx(ctx, overrides = {}) {
|
|
|
395
395
|
* @returns {Promise<RecurseResult>} `{ result, verdict, receipts }` on convergence; `{ incomplete, best,
|
|
396
396
|
* receipts }` on guard exhaustion. NEVER a fabricated success (RC-9).
|
|
397
397
|
* @throws {Error} no provider supplied (on neither `ctx.provider` nor `opts.provider`).
|
|
398
|
+
* @when a task is too big for one model pass and you want it split, fanned out, verified, and merged — with total cost capped by a gate
|
|
399
|
+
* @fails returns `{incomplete, best}` on guard exhaustion or a dead worker (never a faked pass); a gate HaltError exits clean. Cost is open by design — run under a budget gate.
|
|
400
|
+
* @example
|
|
401
|
+
* const ctx = wireGate(gate);
|
|
402
|
+
* const { result, incomplete } = await recurse('audit 400 logs', ctx, { provider, corpus });
|
|
403
|
+
* if (incomplete) retryOrEscalate(result);
|
|
398
404
|
*/
|
|
399
405
|
async function recurse(task, ctx = {}, opts = {}) {
|
|
400
406
|
if (typeof task !== 'string' || task.length === 0) {
|
package/src/refine.d.ts
CHANGED
|
@@ -98,5 +98,10 @@ export type RefineOutcome = {
|
|
|
98
98
|
*
|
|
99
99
|
* @param {RefineOptions} options
|
|
100
100
|
* @returns {Promise<RefineOutcome>}
|
|
101
|
+
* @when you have a caller-supplied attempt + evaluate pair and want to iterate generate → grade → regenerate until it passes or hits a bound
|
|
102
|
+
* @fails returns the last outcome on maxIterations or a terminal `failed` verdict (never a faked pass); a HaltError from either callback propagates clean.
|
|
103
|
+
* @example
|
|
104
|
+
* const { result, passed } = await refine({ attempt, evaluate, maxIterations: 3 });
|
|
105
|
+
* if (!passed) escalate(result);
|
|
101
106
|
*/
|
|
102
107
|
export function refine(options: RefineOptions): Promise<RefineOutcome>;
|