bare-agent 0.42.0 → 0.44.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/bareagent.context.md +1 -1
- package/package.json +8 -3
- package/primitives.json +447 -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 +16 -2
- package/src/loop.js +29 -12
- 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 +11 -2
- package/src/provider-openai.d.ts +4 -0
- package/src/provider-openai.js +31 -3
- package/src/provider-toolcalls.d.ts +29 -0
- package/src/provider-toolcalls.js +45 -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/types/index.d.ts +10 -0
package/src/provider-ollama.js
CHANGED
|
@@ -6,6 +6,7 @@ const { requestWithTemperatureFallback } = require('./provider-temperature');
|
|
|
6
6
|
const { normalizeStopReason } = require('./provider-stop-reason');
|
|
7
7
|
const { resolveTimeoutMs, applyRequestBounds, guardResponseSettles } = require('./provider-http');
|
|
8
8
|
const { hasUsageSignal } = require('./provider-usage');
|
|
9
|
+
const { parseToolCalls } = require('./provider-toolcalls');
|
|
9
10
|
|
|
10
11
|
// BA-24: raw Ollama usage fields. Any present (even 0) ⇒ a usage signal; none ⇒ null (unpriceable).
|
|
11
12
|
const OLLAMA_USAGE_KEYS = ['prompt_eval_count', 'eval_count'];
|
|
@@ -26,6 +27,10 @@ const OLLAMA_USAGE_KEYS = ['prompt_eval_count', 'eval_count'];
|
|
|
26
27
|
class OllamaProvider {
|
|
27
28
|
/**
|
|
28
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' });
|
|
29
34
|
*/
|
|
30
35
|
constructor(options = {}) {
|
|
31
36
|
this.model = options.model || 'llama3.2';
|
|
@@ -85,8 +90,11 @@ class OllamaProvider {
|
|
|
85
90
|
});
|
|
86
91
|
const msg = data.message || {};
|
|
87
92
|
|
|
88
|
-
|
|
89
|
-
|
|
93
|
+
// BA-27: Ollama returns `function.arguments` as an OBJECT for well-formed calls, but some builds
|
|
94
|
+
// pass it through as a model-generated STRING — a malformed one must not throw here (the round
|
|
95
|
+
// already billed; a throw loses usage + hangs metering). Mirror the OpenAI path: no usable calls
|
|
96
|
+
// + a marker, never repair. The object case is untouched (JSON.parse only runs on a string).
|
|
97
|
+
const { toolCalls, malformedToolCall } = parseToolCalls(msg.tool_calls, (/** @type {any} */ tc) => ({
|
|
90
98
|
id: tc.id || `call_${Date.now()}`,
|
|
91
99
|
name: tc.function.name,
|
|
92
100
|
arguments: typeof tc.function.arguments === 'string'
|
|
@@ -97,6 +105,7 @@ class OllamaProvider {
|
|
|
97
105
|
return {
|
|
98
106
|
text: msg.content || '',
|
|
99
107
|
toolCalls,
|
|
108
|
+
...(malformedToolCall && { malformedToolCall }),
|
|
100
109
|
model: data.model || this.model,
|
|
101
110
|
// BA-6: `length` ⇒ cut off at num_predict. VERIFIED LIVE on qwen2.5:0.5b
|
|
102
111
|
// (`poc/ba6-stop-reason-gemini-ollama.mjs`): stop→end_turn, length→max_tokens. Lifecycle values
|
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
|
@@ -7,6 +7,7 @@ const { requestWithTemperatureFallback } = require('./provider-temperature');
|
|
|
7
7
|
const { normalizeStopReason } = require('./provider-stop-reason');
|
|
8
8
|
const { resolveTimeoutMs, applyRequestBounds, guardResponseSettles } = require('./provider-http');
|
|
9
9
|
const { hasUsageSignal } = require('./provider-usage');
|
|
10
|
+
const { parseToolCalls } = require('./provider-toolcalls');
|
|
10
11
|
|
|
11
12
|
// BA-24: raw OpenAI usage fields. Any present (even 0) ⇒ a usage signal; none ⇒ null (unpriceable).
|
|
12
13
|
const OPENAI_USAGE_KEYS = ['prompt_tokens', 'completion_tokens', 'prompt_tokens_details'];
|
|
@@ -30,7 +31,13 @@ function toOpenAIToolChoice(choice) {
|
|
|
30
31
|
if (typeof choice === 'object' && typeof choice.name === 'string' && choice.name) {
|
|
31
32
|
return { type: 'function', function: { name: choice.name } };
|
|
32
33
|
}
|
|
33
|
-
|
|
34
|
+
let describedChoice;
|
|
35
|
+
try {
|
|
36
|
+
describedChoice = JSON.stringify(choice);
|
|
37
|
+
} catch {
|
|
38
|
+
describedChoice = '<unserializable>';
|
|
39
|
+
}
|
|
40
|
+
throw new ProviderError(`[OpenAIProvider] invalid toolChoice: expected 'auto', 'required', or { name }, got ${describedChoice}`);
|
|
34
41
|
}
|
|
35
42
|
|
|
36
43
|
/** @param {string} hostname @returns {boolean} */
|
|
@@ -69,6 +76,10 @@ function isLoopbackHost(hostname) {
|
|
|
69
76
|
class OpenAIProvider {
|
|
70
77
|
/**
|
|
71
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' });
|
|
72
83
|
*/
|
|
73
84
|
constructor(options = {}) {
|
|
74
85
|
this.apiKey = options.apiKey?.trim();
|
|
@@ -125,11 +136,27 @@ class OpenAIProvider {
|
|
|
125
136
|
stripTemperature: () => { delete body.temperature; },
|
|
126
137
|
warnOnce: () => this._warnTemperatureDropped(),
|
|
127
138
|
});
|
|
139
|
+
// BA-27: a successful 200 whose body carries no `choices` (some OpenAI-compat servers return a
|
|
140
|
+
// 4xx-shaped error object with HTTP 200) reached `data.choices[0]` as a bare TypeError with no
|
|
141
|
+
// context. Throw a ProviderError carrying the first ~300 bytes of the body so it can be told apart.
|
|
142
|
+
if (!Array.isArray(data.choices) || data.choices.length === 0) {
|
|
143
|
+
// The `context.bound:'no-choices'` marker ALWAYS distinguishes a 4xx-in-200 from other failures.
|
|
144
|
+
// The raw body snippet is gated behind `exposeErrorBody` (default off) like every other error path
|
|
145
|
+
// here — an unexpected field in a compat server's error body must not leak into logs/audit rows
|
|
146
|
+
// (err.message flows into Loop.run().error) unless the caller opts in.
|
|
147
|
+
throw new ProviderError(
|
|
148
|
+
`[OpenAIProvider] response has no choices` +
|
|
149
|
+
(this.exposeErrorBody ? `: ${JSON.stringify(data).slice(0, 300)}` : ''),
|
|
150
|
+
/** @type {any} */ ({ context: { bound: 'no-choices' }, body: this.exposeErrorBody ? data : undefined })
|
|
151
|
+
);
|
|
152
|
+
}
|
|
128
153
|
const choice = data.choices[0];
|
|
129
154
|
const msg = choice.message;
|
|
130
155
|
|
|
131
|
-
|
|
132
|
-
|
|
156
|
+
// BA-27: `function.arguments` is a model-generated JSON STRING — a malformed one (extra brace,
|
|
157
|
+
// truncated object) must NOT throw here (the round already billed; a throw loses usage + hangs
|
|
158
|
+
// metering). parseToolCalls returns no usable calls + a marker; usage/model still flow below.
|
|
159
|
+
const { toolCalls, malformedToolCall } = parseToolCalls(msg.tool_calls, (/** @type {any} */ tc) => ({
|
|
133
160
|
id: tc.id,
|
|
134
161
|
name: tc.function.name,
|
|
135
162
|
arguments: JSON.parse(tc.function.arguments),
|
|
@@ -138,6 +165,7 @@ class OpenAIProvider {
|
|
|
138
165
|
return {
|
|
139
166
|
text: msg.content || '',
|
|
140
167
|
toolCalls,
|
|
168
|
+
...(malformedToolCall && { malformedToolCall }),
|
|
141
169
|
model: data.model || this.model,
|
|
142
170
|
// BA-6: `length` ⇒ cut off at the output cap (normalized to 'max_tokens'). Note OpenAI refuses to
|
|
143
171
|
// emit a tool call it cannot finish — it 400s instead — so a truncated round here carries no
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export type ToolCall = import("../types").ToolCall;
|
|
2
|
+
/** @typedef {import('../types').ToolCall} ToolCall */
|
|
3
|
+
/**
|
|
4
|
+
* BA-27 — parse a round's raw tool calls into the neutral {@link ToolCall} shape WITHOUT throwing on
|
|
5
|
+
* malformed arguments.
|
|
6
|
+
*
|
|
7
|
+
* OpenAI-compatible providers return `function.arguments` as a JSON STRING the model generated, so a
|
|
8
|
+
* model that emits syntactically-broken JSON (an extra brace, a truncated object — seen live on
|
|
9
|
+
* deepseek-flash and other compat servers) makes a bare `JSON.parse` throw a `SyntaxError`. That throw
|
|
10
|
+
* lands AFTER the HTTP round already succeeded and `usage` came back, so it loses the billed round and
|
|
11
|
+
* hangs any metering, and no caller can tell "the model emitted bad arguments" from a transport fault.
|
|
12
|
+
*
|
|
13
|
+
* Instead: on the FIRST unparseable call, return NO usable tool calls (`toolCalls: []`) plus a marker
|
|
14
|
+
* `{ name, error }`. The caller treats it as "no usable tool call" and retries; usage/model still flow
|
|
15
|
+
* so the round is metered. We NEVER repair the JSON — a guessed brace could execute the wrong action.
|
|
16
|
+
* All-or-nothing (mirrors BA-4's refusal to execute a truncated round's calls): a partial set risks
|
|
17
|
+
* running half a decomposed intent, so one bad call voids the whole round's calls.
|
|
18
|
+
*
|
|
19
|
+
* @param {any[]} rawToolCalls - provider-native tool-call entries (may be undefined/empty)
|
|
20
|
+
* @param {(tc: any) => ToolCall} mapOne - maps one raw entry to a ToolCall; MAY throw on bad arguments
|
|
21
|
+
* @returns {{ toolCalls: ToolCall[], malformedToolCall?: { name: string|undefined, error: string } }}
|
|
22
|
+
*/
|
|
23
|
+
export function parseToolCalls(rawToolCalls: any[], mapOne: (tc: any) => ToolCall): {
|
|
24
|
+
toolCalls: ToolCall[];
|
|
25
|
+
malformedToolCall?: {
|
|
26
|
+
name: string | undefined;
|
|
27
|
+
error: string;
|
|
28
|
+
};
|
|
29
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/** @typedef {import('../types').ToolCall} ToolCall */
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* BA-27 — parse a round's raw tool calls into the neutral {@link ToolCall} shape WITHOUT throwing on
|
|
7
|
+
* malformed arguments.
|
|
8
|
+
*
|
|
9
|
+
* OpenAI-compatible providers return `function.arguments` as a JSON STRING the model generated, so a
|
|
10
|
+
* model that emits syntactically-broken JSON (an extra brace, a truncated object — seen live on
|
|
11
|
+
* deepseek-flash and other compat servers) makes a bare `JSON.parse` throw a `SyntaxError`. That throw
|
|
12
|
+
* lands AFTER the HTTP round already succeeded and `usage` came back, so it loses the billed round and
|
|
13
|
+
* hangs any metering, and no caller can tell "the model emitted bad arguments" from a transport fault.
|
|
14
|
+
*
|
|
15
|
+
* Instead: on the FIRST unparseable call, return NO usable tool calls (`toolCalls: []`) plus a marker
|
|
16
|
+
* `{ name, error }`. The caller treats it as "no usable tool call" and retries; usage/model still flow
|
|
17
|
+
* so the round is metered. We NEVER repair the JSON — a guessed brace could execute the wrong action.
|
|
18
|
+
* All-or-nothing (mirrors BA-4's refusal to execute a truncated round's calls): a partial set risks
|
|
19
|
+
* running half a decomposed intent, so one bad call voids the whole round's calls.
|
|
20
|
+
*
|
|
21
|
+
* @param {any[]} rawToolCalls - provider-native tool-call entries (may be undefined/empty)
|
|
22
|
+
* @param {(tc: any) => ToolCall} mapOne - maps one raw entry to a ToolCall; MAY throw on bad arguments
|
|
23
|
+
* @returns {{ toolCalls: ToolCall[], malformedToolCall?: { name: string|undefined, error: string } }}
|
|
24
|
+
*/
|
|
25
|
+
function parseToolCalls(rawToolCalls, mapOne) {
|
|
26
|
+
const raw = rawToolCalls || [];
|
|
27
|
+
/** @type {ToolCall[]} */
|
|
28
|
+
const toolCalls = [];
|
|
29
|
+
for (const tc of raw) {
|
|
30
|
+
try {
|
|
31
|
+
toolCalls.push(mapOne(tc));
|
|
32
|
+
} catch (e) {
|
|
33
|
+
return {
|
|
34
|
+
toolCalls: [],
|
|
35
|
+
malformedToolCall: {
|
|
36
|
+
name: tc && tc.function ? tc.function.name : undefined,
|
|
37
|
+
error: e instanceof Error ? e.message : String(e),
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return { toolCalls };
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
module.exports = { parseToolCalls };
|
|
@@ -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>;
|
package/src/refine.js
CHANGED
|
@@ -38,6 +38,11 @@
|
|
|
38
38
|
*
|
|
39
39
|
* @param {RefineOptions} options
|
|
40
40
|
* @returns {Promise<RefineOutcome>}
|
|
41
|
+
* @when you have a caller-supplied attempt + evaluate pair and want to iterate generate → grade → regenerate until it passes or hits a bound
|
|
42
|
+
* @fails returns the last outcome on maxIterations or a terminal `failed` verdict (never a faked pass); a HaltError from either callback propagates clean.
|
|
43
|
+
* @example
|
|
44
|
+
* const { result, passed } = await refine({ attempt, evaluate, maxIterations: 3 });
|
|
45
|
+
* if (!passed) escalate(result);
|
|
41
46
|
*/
|
|
42
47
|
async function refine(options) {
|
|
43
48
|
const { attempt, evaluate } = options;
|
package/src/remember.d.ts
CHANGED
|
@@ -84,6 +84,11 @@ export type Store = import("../types").Store;
|
|
|
84
84
|
* Each is a transcript chunk — a raw string, or an object with `content`/`text`. Empty/blank spans are skipped.
|
|
85
85
|
* @param {RememberOptions} options
|
|
86
86
|
* @returns {Promise<RememberOutcome>}
|
|
87
|
+
* @when you want to distill durable facts from harvested transcript spans and persist them through a Store socket (the consolidation pass)
|
|
88
|
+
* @fails skips empty spans and never fabricates; a provider HaltError propagates clean. Each pass forwards usage via onLlmResult; a fact counts once via ctx.recordMemoryOp.
|
|
89
|
+
* @example
|
|
90
|
+
* const { facts } = await remember(spans, { provider, store });
|
|
91
|
+
* console.log(`consolidated ${facts.length} durable facts`);
|
|
87
92
|
*/
|
|
88
93
|
export function remember(spans: Array<string | {
|
|
89
94
|
content?: string;
|
package/src/remember.js
CHANGED
|
@@ -73,6 +73,11 @@ const DISTILL_PROMPT = [
|
|
|
73
73
|
* Each is a transcript chunk — a raw string, or an object with `content`/`text`. Empty/blank spans are skipped.
|
|
74
74
|
* @param {RememberOptions} options
|
|
75
75
|
* @returns {Promise<RememberOutcome>}
|
|
76
|
+
* @when you want to distill durable facts from harvested transcript spans and persist them through a Store socket (the consolidation pass)
|
|
77
|
+
* @fails skips empty spans and never fabricates; a provider HaltError propagates clean. Each pass forwards usage via onLlmResult; a fact counts once via ctx.recordMemoryOp.
|
|
78
|
+
* @example
|
|
79
|
+
* const { facts } = await remember(spans, { provider, store });
|
|
80
|
+
* console.log(`consolidated ${facts.length} durable facts`);
|
|
76
81
|
*/
|
|
77
82
|
async function remember(spans, options = /** @type {RememberOptions} */ ({})) {
|
|
78
83
|
if (!Array.isArray(spans)) throw new Error('[remember] spans must be an array');
|
package/src/retry.d.ts
CHANGED
|
@@ -21,7 +21,14 @@ export type RetryOptions = {
|
|
|
21
21
|
jitter?: number | boolean | "full" | "equal" | undefined;
|
|
22
22
|
};
|
|
23
23
|
export class Retry {
|
|
24
|
-
/**
|
|
24
|
+
/**
|
|
25
|
+
* @param {RetryOptions} [options={}]
|
|
26
|
+
* @when you want backoff-with-jitter around a flaky async call (a provider request, a plan step) — the retry seam Loop and runPlan wrap providers with
|
|
27
|
+
* @fails rethrows the last error once attempts are exhausted; by default only transient errors (429/5xx/ECONNRESET/ETIMEDOUT) are retried.
|
|
28
|
+
* @example
|
|
29
|
+
* const retry = new Retry({ maxAttempts: 3, jitter: true });
|
|
30
|
+
* const res = await retry.call(() => provider.generate(msgs));
|
|
31
|
+
*/
|
|
25
32
|
constructor(options?: RetryOptions);
|
|
26
33
|
maxAttempts: number;
|
|
27
34
|
backoff: number | "linear" | "exponential";
|
package/src/retry.js
CHANGED
|
@@ -23,7 +23,14 @@ const DEFAULT_RETRY_ON = (err) => {
|
|
|
23
23
|
};
|
|
24
24
|
|
|
25
25
|
class Retry {
|
|
26
|
-
/**
|
|
26
|
+
/**
|
|
27
|
+
* @param {RetryOptions} [options={}]
|
|
28
|
+
* @when you want backoff-with-jitter around a flaky async call (a provider request, a plan step) — the retry seam Loop and runPlan wrap providers with
|
|
29
|
+
* @fails rethrows the last error once attempts are exhausted; by default only transient errors (429/5xx/ECONNRESET/ETIMEDOUT) are retried.
|
|
30
|
+
* @example
|
|
31
|
+
* const retry = new Retry({ maxAttempts: 3, jitter: true });
|
|
32
|
+
* const res = await retry.call(() => provider.generate(msgs));
|
|
33
|
+
*/
|
|
27
34
|
constructor(options = {}) {
|
|
28
35
|
this.maxAttempts = options.maxAttempts !== undefined ? options.maxAttempts : 3;
|
|
29
36
|
this.backoff = options.backoff || 'exponential';
|
package/src/run-plan.d.ts
CHANGED
|
@@ -122,5 +122,10 @@ export type StepResult = {
|
|
|
122
122
|
* @throws {Error} `[runPlan] executeFn must be a function` — when executeFn is not a function.
|
|
123
123
|
* @throws {Error} `[runPlan] duplicate step id: "X"` — when two steps share an id.
|
|
124
124
|
* @throws {Error} `[runPlan] step "X" depends on unknown step "Y"` — when dependsOn references missing id.
|
|
125
|
+
* @when you have a step DAG from the Planner and want to execute it with wave-based parallelism (independent steps run concurrently)
|
|
126
|
+
* @fails throws on a malformed DAG (empty steps, non-function executeFn, duplicate ids, unknown dependency); a step's own error surfaces per StepResult, never crashing the wave.
|
|
127
|
+
* @example
|
|
128
|
+
* const steps = await planner.plan(goal);
|
|
129
|
+
* const results = await runPlan(steps, step => execute(step));
|
|
125
130
|
*/
|
|
126
131
|
export function runPlan(steps: Step[], executeFn: (step: Step) => any, options?: RunPlanOptions): Promise<StepResult[]>;
|
package/src/run-plan.js
CHANGED
|
@@ -47,6 +47,11 @@
|
|
|
47
47
|
* @throws {Error} `[runPlan] executeFn must be a function` — when executeFn is not a function.
|
|
48
48
|
* @throws {Error} `[runPlan] duplicate step id: "X"` — when two steps share an id.
|
|
49
49
|
* @throws {Error} `[runPlan] step "X" depends on unknown step "Y"` — when dependsOn references missing id.
|
|
50
|
+
* @when you have a step DAG from the Planner and want to execute it with wave-based parallelism (independent steps run concurrently)
|
|
51
|
+
* @fails throws on a malformed DAG (empty steps, non-function executeFn, duplicate ids, unknown dependency); a step's own error surfaces per StepResult, never crashing the wave.
|
|
52
|
+
* @example
|
|
53
|
+
* const steps = await planner.plan(goal);
|
|
54
|
+
* const results = await runPlan(steps, step => execute(step));
|
|
50
55
|
*/
|
|
51
56
|
async function runPlan(steps, executeFn, options = {}) {
|
|
52
57
|
if (!Array.isArray(steps) || steps.length === 0) {
|
package/src/scheduler.d.ts
CHANGED
|
@@ -48,7 +48,14 @@ export type SchedulerOptions = {
|
|
|
48
48
|
* @property {((err: any, job: Job) => void)|null} [onError] - Handler errors callback.
|
|
49
49
|
*/
|
|
50
50
|
export class Scheduler {
|
|
51
|
-
/**
|
|
51
|
+
/**
|
|
52
|
+
* @param {SchedulerOptions} [options={}]
|
|
53
|
+
* @when you need to fire agent turns on a schedule — cron expressions or relative intervals — driven by a periodic tick
|
|
54
|
+
* @fails an errored job routes to the onError handler and never crashes the tick loop; a malformed cron/interval is rejected when the job is added.
|
|
55
|
+
* @example
|
|
56
|
+
* const sched = new Scheduler({ interval: 60000 });
|
|
57
|
+
* sched.add({ id: 'poll', cron: '0 * * * *', run });
|
|
58
|
+
*/
|
|
52
59
|
constructor(options?: SchedulerOptions);
|
|
53
60
|
_file: string | null;
|
|
54
61
|
_interval: number;
|
package/src/scheduler.js
CHANGED
|
@@ -32,7 +32,14 @@ const { readFileSync, writeFileSync, existsSync } = require('node:fs');
|
|
|
32
32
|
*/
|
|
33
33
|
|
|
34
34
|
class Scheduler {
|
|
35
|
-
/**
|
|
35
|
+
/**
|
|
36
|
+
* @param {SchedulerOptions} [options={}]
|
|
37
|
+
* @when you need to fire agent turns on a schedule — cron expressions or relative intervals — driven by a periodic tick
|
|
38
|
+
* @fails an errored job routes to the onError handler and never crashes the tick loop; a malformed cron/interval is rejected when the job is added.
|
|
39
|
+
* @example
|
|
40
|
+
* const sched = new Scheduler({ interval: 60000 });
|
|
41
|
+
* sched.add({ id: 'poll', cron: '0 * * * *', run });
|
|
42
|
+
*/
|
|
36
43
|
constructor(options = {}) {
|
|
37
44
|
this._file = options.file || null;
|
|
38
45
|
this._interval = options.interval || 60000;
|
package/src/skills.d.ts
CHANGED
|
@@ -24,6 +24,12 @@ export class SkillRegistry {
|
|
|
24
24
|
* skill tool that would collide with one is rejected at `register` time. Tool names are globally unique
|
|
25
25
|
* for DISPATCH (PRD §2.6, D6) — this is the collision check across native + MCP + skills, not security.
|
|
26
26
|
* @param {string} [options.metaToolName='skill_use'] - Override the meta-tool name if `skill_use` is taken.
|
|
27
|
+
* @when you want to expose operator-registered skill bundles to a model by progressive disclosure — one meta-tool whose catalog unlocks a skill's tools on demand
|
|
28
|
+
* @fails register() rejects an unsafe or colliding name fail-fast and commits nothing on failure; governance is unchanged — discovery never authorizes.
|
|
29
|
+
* @example
|
|
30
|
+
* const skills = new SkillRegistry();
|
|
31
|
+
* skills.register({ name: 'deploy', description: 'ship a release', instructions: '...', tools: [] });
|
|
32
|
+
* const loop = new Loop({ provider, tools: skills.activeTools });
|
|
27
33
|
*/
|
|
28
34
|
constructor(options?: {
|
|
29
35
|
reserved?: Iterable<string> | undefined;
|
package/src/skills.js
CHANGED
|
@@ -51,6 +51,12 @@ class SkillRegistry {
|
|
|
51
51
|
* skill tool that would collide with one is rejected at `register` time. Tool names are globally unique
|
|
52
52
|
* for DISPATCH (PRD §2.6, D6) — this is the collision check across native + MCP + skills, not security.
|
|
53
53
|
* @param {string} [options.metaToolName='skill_use'] - Override the meta-tool name if `skill_use` is taken.
|
|
54
|
+
* @when you want to expose operator-registered skill bundles to a model by progressive disclosure — one meta-tool whose catalog unlocks a skill's tools on demand
|
|
55
|
+
* @fails register() rejects an unsafe or colliding name fail-fast and commits nothing on failure; governance is unchanged — discovery never authorizes.
|
|
56
|
+
* @example
|
|
57
|
+
* const skills = new SkillRegistry();
|
|
58
|
+
* skills.register({ name: 'deploy', description: 'ship a release', instructions: '...', tools: [] });
|
|
59
|
+
* const loop = new Loop({ provider, tools: skills.activeTools });
|
|
54
60
|
*/
|
|
55
61
|
constructor(options = {}) {
|
|
56
62
|
/** @type {Map<string, {name: string, description: string, instructions: string, tools: ToolDef[]}>} */
|
package/src/stash.d.ts
CHANGED
|
@@ -19,6 +19,11 @@ export type ToolDef = import("../types").ToolDef;
|
|
|
19
19
|
* @param {number} [options.compaction.keepRecentTurns=3] - Recent turns to keep at the END (live working set).
|
|
20
20
|
* @param {(msg: string) => void} [options.onNote=console.warn] - Sink for the loud one-time/backstop notes.
|
|
21
21
|
* @returns {{ skill: { name: string, description: string, instructions: string, tools: ToolDef[] }, trim: (msgs: any[], ctx: any) => Promise<any[]>, restoreHandles: () => string[] }}
|
|
22
|
+
* @when you need compaction-first context hygiene — a registrable skill whose checkpoint/compact/restore tools fold the live transcript at round boundaries
|
|
23
|
+
* @fails never throws for a fold; degrades LOUDLY to a lossless park when summarize is unwired. Preserves tool-pairing and role alternation by construction.
|
|
24
|
+
* @example
|
|
25
|
+
* const { skill, trim } = createStashSkill({ compaction: { ceilingTokens: 100000 } });
|
|
26
|
+
* const loop = new Loop({ provider, trim });
|
|
22
27
|
*/
|
|
23
28
|
export function createStashSkill(options?: {
|
|
24
29
|
defaultStrategy?: "summarize" | "stash" | undefined;
|
package/src/stash.js
CHANGED
|
@@ -82,6 +82,11 @@ const INSTRUCTIONS =
|
|
|
82
82
|
* @param {number} [options.compaction.keepRecentTurns=3] - Recent turns to keep at the END (live working set).
|
|
83
83
|
* @param {(msg: string) => void} [options.onNote=console.warn] - Sink for the loud one-time/backstop notes.
|
|
84
84
|
* @returns {{ skill: { name: string, description: string, instructions: string, tools: ToolDef[] }, trim: (msgs: any[], ctx: any) => Promise<any[]>, restoreHandles: () => string[] }}
|
|
85
|
+
* @when you need compaction-first context hygiene — a registrable skill whose checkpoint/compact/restore tools fold the live transcript at round boundaries
|
|
86
|
+
* @fails never throws for a fold; degrades LOUDLY to a lossless park when summarize is unwired. Preserves tool-pairing and role alternation by construction.
|
|
87
|
+
* @example
|
|
88
|
+
* const { skill, trim } = createStashSkill({ compaction: { ceilingTokens: 100000 } });
|
|
89
|
+
* const loop = new Loop({ provider, trim });
|
|
85
90
|
*/
|
|
86
91
|
function createStashSkill(options = {}) {
|
|
87
92
|
const {
|
package/src/state.d.ts
CHANGED
|
@@ -12,7 +12,14 @@ export type Task = {
|
|
|
12
12
|
* @property {string} updatedAt
|
|
13
13
|
*/
|
|
14
14
|
export class StateMachine extends EventEmitter<[never]> {
|
|
15
|
-
/**
|
|
15
|
+
/**
|
|
16
|
+
* @param {{ file?: string|null }} [options={}]
|
|
17
|
+
* @when you need to track task lifecycle (pending/running/done/failed/waiting/cancelled) with enforced transitions and optional file persistence
|
|
18
|
+
* @fails rejects an illegal state transition and never throws on a valid one; state changes are emitted as events.
|
|
19
|
+
* @example
|
|
20
|
+
* const sm = new StateMachine();
|
|
21
|
+
* sm.create('t1'); sm.transition('t1', 'running');
|
|
22
|
+
*/
|
|
16
23
|
constructor(options?: {
|
|
17
24
|
file?: string | null;
|
|
18
25
|
});
|
package/src/state.js
CHANGED
|
@@ -21,7 +21,14 @@ const TRANSITIONS = {
|
|
|
21
21
|
*/
|
|
22
22
|
|
|
23
23
|
class StateMachine extends EventEmitter {
|
|
24
|
-
/**
|
|
24
|
+
/**
|
|
25
|
+
* @param {{ file?: string|null }} [options={}]
|
|
26
|
+
* @when you need to track task lifecycle (pending/running/done/failed/waiting/cancelled) with enforced transitions and optional file persistence
|
|
27
|
+
* @fails rejects an illegal state transition and never throws on a valid one; state changes are emitted as events.
|
|
28
|
+
* @example
|
|
29
|
+
* const sm = new StateMachine();
|
|
30
|
+
* sm.create('t1'); sm.transition('t1', 'running');
|
|
31
|
+
*/
|
|
25
32
|
constructor(options = {}) {
|
|
26
33
|
super();
|
|
27
34
|
this.file = options.file || null;
|
package/src/store-jsonfile.d.ts
CHANGED
|
@@ -42,6 +42,11 @@ export class JsonFileStore {
|
|
|
42
42
|
/**
|
|
43
43
|
* @param {{ path?: string }} [options]
|
|
44
44
|
* @throws {Error} `[JsonFileStore] requires options.path` — when path is missing.
|
|
45
|
+
* @name JsonFile
|
|
46
|
+
* @when you want zero-dependency JSON-file storage for Memory (store/search/get/delete) — the simplest durable backend, no native deps
|
|
47
|
+
* @fails throws on a missing path; implements the four-verb Store socket over a plain JSON file.
|
|
48
|
+
* @example
|
|
49
|
+
* const store = new JsonFile({ path: './agent.json' });
|
|
45
50
|
*/
|
|
46
51
|
constructor(options?: {
|
|
47
52
|
path?: string;
|
package/src/store-jsonfile.js
CHANGED
|
@@ -31,6 +31,11 @@ class JsonFileStore {
|
|
|
31
31
|
/**
|
|
32
32
|
* @param {{ path?: string }} [options]
|
|
33
33
|
* @throws {Error} `[JsonFileStore] requires options.path` — when path is missing.
|
|
34
|
+
* @name JsonFile
|
|
35
|
+
* @when you want zero-dependency JSON-file storage for Memory (store/search/get/delete) — the simplest durable backend, no native deps
|
|
36
|
+
* @fails throws on a missing path; implements the four-verb Store socket over a plain JSON file.
|
|
37
|
+
* @example
|
|
38
|
+
* const store = new JsonFile({ path: './agent.json' });
|
|
34
39
|
*/
|
|
35
40
|
constructor(options = {}) {
|
|
36
41
|
if (!options.path) throw new Error('[JsonFileStore] requires options.path');
|
package/src/store-sqlite.d.ts
CHANGED
|
@@ -43,6 +43,11 @@ export class SQLiteStore {
|
|
|
43
43
|
* @param {{ path?: string }} [options]
|
|
44
44
|
* @throws {Error} `[SQLiteStore] requires options.path` — when path is missing.
|
|
45
45
|
* @throws {Error} `[SQLiteStore] requires better-sqlite3` — when peer dep is not installed.
|
|
46
|
+
* @name SQLite
|
|
47
|
+
* @when you want durable, queryable SQLite-backed storage for Memory (store/search/get/delete) persisted on disk
|
|
48
|
+
* @fails throws on a missing path or an absent better-sqlite3 peer dep; implements the four-verb Store socket.
|
|
49
|
+
* @example
|
|
50
|
+
* const store = new SQLite({ path: './agent.db' });
|
|
46
51
|
*/
|
|
47
52
|
constructor(options?: {
|
|
48
53
|
path?: string;
|
package/src/store-sqlite.js
CHANGED
|
@@ -23,6 +23,11 @@ class SQLiteStore {
|
|
|
23
23
|
* @param {{ path?: string }} [options]
|
|
24
24
|
* @throws {Error} `[SQLiteStore] requires options.path` — when path is missing.
|
|
25
25
|
* @throws {Error} `[SQLiteStore] requires better-sqlite3` — when peer dep is not installed.
|
|
26
|
+
* @name SQLite
|
|
27
|
+
* @when you want durable, queryable SQLite-backed storage for Memory (store/search/get/delete) persisted on disk
|
|
28
|
+
* @fails throws on a missing path or an absent better-sqlite3 peer dep; implements the four-verb Store socket.
|
|
29
|
+
* @example
|
|
30
|
+
* const store = new SQLite({ path: './agent.db' });
|
|
26
31
|
*/
|
|
27
32
|
constructor(options = {}) {
|
|
28
33
|
if (!options.path) throw new Error('[SQLiteStore] requires options.path');
|
package/src/stream.d.ts
CHANGED
|
@@ -60,6 +60,11 @@ export type StreamOptions = {
|
|
|
60
60
|
export class Stream {
|
|
61
61
|
/**
|
|
62
62
|
* @param {StreamOptions} [options={}]
|
|
63
|
+
* @when you want a structured event emitter for loop/tool/governance events, optionally piped to a transport sink
|
|
64
|
+
* @fails never throws on emit; a transport write error is isolated and does not interrupt the run.
|
|
65
|
+
* @example
|
|
66
|
+
* const stream = new Stream({ transport });
|
|
67
|
+
* stream.emit('loop:round', { n: 1 });
|
|
63
68
|
*/
|
|
64
69
|
constructor(options?: StreamOptions);
|
|
65
70
|
/** @type {Transport|null} */
|