toolnexus 0.26.0 → 0.28.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -2
- package/dist/acp.d.ts +16 -2
- package/dist/acp.js +74 -21
- package/dist/agents/home.d.ts +85 -4
- package/dist/agents/home.js +347 -52
- package/dist/classifier.d.ts +21 -3
- package/dist/classifier.js +175 -9
- package/dist/client.d.ts +109 -0
- package/dist/client.js +380 -75
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/mcp.js +29 -7
- package/dist/mcp2026/source.d.ts +5 -0
- package/dist/mcp2026/source.js +11 -2
- package/dist/mcp2026/stdio.d.ts +43 -0
- package/dist/mcp2026/stdio.js +144 -20
- package/dist/mcphandler.js +2 -4
- package/dist/mcpserve.js +2 -1
- package/dist/nodefetch.d.ts +1 -0
- package/dist/nodefetch.js +156 -0
- package/dist/serve.js +5 -1
- package/dist/skill.d.ts +14 -0
- package/dist/skill.js +33 -2
- package/dist/transcript_search.d.ts +27 -0
- package/dist/transcript_search.js +165 -0
- package/package.json +2 -2
- package/src/acp.ts +101 -22
- package/src/agents/home.ts +381 -53
- package/src/classifier.ts +168 -12
- package/src/client.ts +368 -57
- package/src/index.ts +1 -0
- package/src/mcp.ts +36 -7
- package/src/mcp2026/source.ts +14 -4
- package/src/mcp2026/stdio.ts +148 -23
- package/src/mcphandler.ts +2 -3
- package/src/mcpserve.ts +2 -1
- package/src/nodefetch.ts +143 -0
- package/src/serve.ts +6 -2
- package/src/skill.ts +51 -2
- package/src/transcript_search.ts +172 -0
package/README.md
CHANGED
|
@@ -92,6 +92,9 @@ tool. Runnable: `npm run example:prompt-cache` ([`examples/prompt_cache.ts`](exa
|
|
|
92
92
|
`after`; `read` / `grep` report truncation in metadata.
|
|
93
93
|
- `agents.compactor({ onCompact })`, `agent.asToolWith(ctx => ({ hooks, waitFor }))`, runtime
|
|
94
94
|
`waitFor`, `ctx.callId` on tools, `listModels(opts)`.
|
|
95
|
+
- `compactOnOverflow: agents.compactor({ maxTokens: tighter })` — when the provider rejects a prompt as too
|
|
96
|
+
long (`isContextOverflow(status, body)`), compact and re-send the same turn; three rapid
|
|
97
|
+
compactions in a row fail with `ContextOverflowError` (SPEC §7F.1).
|
|
95
98
|
|
|
96
99
|
An in-process model streams for real when it is given as `generateStream(request, emit)`; a plain
|
|
97
100
|
`generate` still refuses `stream()`.
|
|
@@ -152,7 +155,8 @@ const rs = await c.states("a", "b").evaluate(judge.noul("q", "…")) // [{
|
|
|
152
155
|
|
|
153
156
|
- `backend`: `typesafe` (default) · `openrouter` (`typesafe/jev-1.14`) · `cloudflare` (`/ai/run`
|
|
154
157
|
envelope; `${CLOUDFLARE_ACCOUNT_ID}` in the URL expands at call time, errors name it unexpanded)
|
|
155
|
-
· `vercel` · `
|
|
158
|
+
· `vercel` · `openai` (OpenAI Decisions API, `POST {baseUrl}/decisions`, `gpt-6-luna`,
|
|
159
|
+
`OPENAI_API_KEY`; raw questions are unsupported, `calibrated` is false) · `custom` (requires `baseUrl` and `supports`). The table is data:
|
|
156
160
|
`CLASSIFIER_BACKENDS`; `supports: { images }` overrides it per classifier.
|
|
157
161
|
- Images (style `systemone` only): image parts in the state are lifted into `assets` (cloudflare:
|
|
158
162
|
data URLs). PNG/JPEG, 280–1568 px a side, ≤ 16 MiB each and 32 MiB per request — never resized.
|
|
@@ -317,7 +321,7 @@ Everything else — the full surface, with runnable examples — lives on the do
|
|
|
317
321
|
| **Start here** | [Quickstart](https://muthuishere.github.io/toolnexus/quickstart/) · [Concepts](https://muthuishere.github.io/toolnexus/concepts/) · [Install](https://muthuishere.github.io/toolnexus/install/) |
|
|
318
322
|
| **Tool sources** | [MCP](https://muthuishere.github.io/toolnexus/mcp/) · [Skills](https://muthuishere.github.io/toolnexus/skills/) · [Native](https://muthuishere.github.io/toolnexus/native/) · [HTTP](https://muthuishere.github.io/toolnexus/http/) · [Built-ins](https://muthuishere.github.io/toolnexus/builtins/) · [A2A](https://muthuishere.github.io/toolnexus/a2a/) |
|
|
319
323
|
| **The loop** | [Streaming](https://muthuishere.github.io/toolnexus/streaming/) · [Memory](https://muthuishere.github.io/toolnexus/memory/) · [Suspension](https://muthuishere.github.io/toolnexus/suspension/) · [Resilience](https://muthuishere.github.io/toolnexus/resilience/) · [Observability](https://muthuishere.github.io/toolnexus/observability/) |
|
|
320
|
-
| **Agents** | [Sub-agents & teams](https://muthuishere.github.io/toolnexus/subagents/) · [Personas](https://muthuishere.github.io/toolnexus/persona-agents/) · [Typed decisions](https://muthuishere.github.io/toolnexus/judge/) |
|
|
324
|
+
| **Agents** | [Sub-agents & teams](https://muthuishere.github.io/toolnexus/subagents/) · [Personas](https://muthuishere.github.io/toolnexus/persona-agents/) · [Dreaming](https://muthuishere.github.io/toolnexus/dreaming/) · [Typed decisions](https://muthuishere.github.io/toolnexus/judge/) |
|
|
321
325
|
| **API reference** | [JavaScript](https://muthuishere.github.io/toolnexus/api/javascript/) |
|
|
322
326
|
| **Cookbook** | [Zero to agent](https://muthuishere.github.io/toolnexus/cookbook/zero-to-agent/) · [MCP servers](https://muthuishere.github.io/toolnexus/cookbook/mcp-servers/) · [Agent skills](https://muthuishere.github.io/toolnexus/cookbook/agent-skills/) · [Judge](https://muthuishere.github.io/toolnexus/cookbook/judge/) |
|
|
323
327
|
|
package/dist/acp.d.ts
CHANGED
|
@@ -48,15 +48,29 @@ export interface ACPOptions {
|
|
|
48
48
|
/** One warm ACP connection: one child process, one session, alive across many
|
|
49
49
|
* turns. `.generate` has exactly the shape {@link InProcessOptions.generate}
|
|
50
50
|
* wants, so it plugs straight into {@link createInProcessClient}. */
|
|
51
|
+
/** Per-call options for {@link ACPClient.generate} / {@link ACPClient.generateStream}. */
|
|
52
|
+
export interface ACPGenerateOptions {
|
|
53
|
+
/** Cancels the turn: when it aborts mid-turn the `session/cancel` notification is sent,
|
|
54
|
+
* the turn's reply is awaited (bounded by `requestTimeoutMs`, default 30s) so the next
|
|
55
|
+
* turn never lands on a busy session, and the call rejects with an `AbortError`. The
|
|
56
|
+
* process and session stay warm. */
|
|
57
|
+
signal?: AbortSignal;
|
|
58
|
+
}
|
|
51
59
|
export interface ACPClient {
|
|
52
|
-
generate: (request: InProcessRequest) => Promise<InProcessResponse>;
|
|
60
|
+
generate: (request: InProcessRequest, opts?: ACPGenerateOptions) => Promise<InProcessResponse>;
|
|
53
61
|
/** {@link generate}, streaming: `emit` fires as each `session/update` arrives —
|
|
54
62
|
* agent_message_chunk → `text`, agent_thought_chunk → `thought` (via "acp"),
|
|
55
63
|
* tool_call → `tool_call` and a completed/failed tool_call_update → `tool_result`
|
|
56
64
|
* (both via "acp": the agent's own tools, reported, never executed by the loop).
|
|
57
65
|
* Shaped for {@link InProcessOptions.generateStream}. The returned content is the
|
|
58
66
|
* message text only. */
|
|
59
|
-
generateStream: (request: InProcessRequest, emit: (event: StreamEvent) => void) => Promise<InProcessResponse>;
|
|
67
|
+
generateStream: (request: InProcessRequest, emit: (event: StreamEvent) => void, opts?: ACPGenerateOptions) => Promise<InProcessResponse>;
|
|
68
|
+
/** Sends `session/set_mode {sessionId, modeId}` on the running session (no restart). The
|
|
69
|
+
* agent's error rejects this call only; the client stays usable. */
|
|
70
|
+
setMode: (mode: string) => Promise<void>;
|
|
71
|
+
/** Changes the model on the running session exactly as the `model` option does:
|
|
72
|
+
* `session/set_config_option {configId:"model"}`, then `session/set_model`; both failing rejects. */
|
|
73
|
+
setModel: (model: string) => Promise<void>;
|
|
60
74
|
/** Terminates the agent's process group. Idempotent — a second call is a no-op. */
|
|
61
75
|
close: () => Promise<void>;
|
|
62
76
|
/** The agent's raw `initialize` result. */
|
package/dist/acp.js
CHANGED
|
@@ -276,29 +276,32 @@ export async function loadACP(opts) {
|
|
|
276
276
|
if (opts.mode) {
|
|
277
277
|
await step("session/set_mode", () => call("session/set_mode", { sessionId, modeId: opts.mode }));
|
|
278
278
|
}
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
279
|
+
// Passed through unchanged. Stable ACP spelling first, then the older session/set_model.
|
|
280
|
+
const sendModel = async (model) => {
|
|
281
|
+
try {
|
|
282
|
+
await call("session/set_config_option", { sessionId, configId: "model", value: model });
|
|
283
|
+
}
|
|
284
|
+
catch (e1) {
|
|
283
285
|
try {
|
|
284
|
-
await call("session/
|
|
286
|
+
await call("session/set_model", { sessionId, modelId: model });
|
|
285
287
|
}
|
|
286
|
-
catch (
|
|
287
|
-
|
|
288
|
-
await call("session/set_model", { sessionId, modelId: model });
|
|
289
|
-
}
|
|
290
|
-
catch (e2) {
|
|
291
|
-
throw new Error(`${e1.message}; ${e2.message}`);
|
|
292
|
-
}
|
|
288
|
+
catch (e2) {
|
|
289
|
+
throw new Error(`${e1.message}; ${e2.message}`);
|
|
293
290
|
}
|
|
294
|
-
}
|
|
291
|
+
}
|
|
292
|
+
};
|
|
293
|
+
if (opts.model) {
|
|
294
|
+
const model = opts.model;
|
|
295
|
+
await step(`set model ${JSON.stringify(model)}`, () => sendModel(model));
|
|
295
296
|
}
|
|
296
297
|
// Serialises turns: one ACP session is one conversation, so concurrent
|
|
297
298
|
// prompts must not interleave into it. Idiomatic promise-chaining queue —
|
|
298
299
|
// each generate() call attaches after whatever is already queued, and the
|
|
299
300
|
// queue itself never rejects (a failed turn does not poison later ones).
|
|
300
301
|
let turnQueue = Promise.resolve();
|
|
301
|
-
async function promptOnce(request, emit) {
|
|
302
|
+
async function promptOnce(request, emit, signal) {
|
|
303
|
+
if (signal?.aborted)
|
|
304
|
+
throw acpAbortError();
|
|
302
305
|
const text = renderPrompt(request);
|
|
303
306
|
const id = `c-${++nextId}`;
|
|
304
307
|
let content = "";
|
|
@@ -338,12 +341,20 @@ export async function loadACP(opts) {
|
|
|
338
341
|
break;
|
|
339
342
|
}
|
|
340
343
|
case "tool_call_update": {
|
|
344
|
+
const callId = u.toolCallId ?? "";
|
|
345
|
+
// A non-empty title is remembered as the call's name for every later event (#188).
|
|
346
|
+
if (u.title)
|
|
347
|
+
toolNames.set(callId, u.title);
|
|
348
|
+
const name = toolNames.get(callId) || "";
|
|
349
|
+
const raw = u.rawInput;
|
|
350
|
+
if (raw && typeof raw === "object" && !Array.isArray(raw) && Object.keys(raw).length > 0) {
|
|
351
|
+
send({ type: "tool_call_args", id: callId, name, args: raw, via: "acp" });
|
|
352
|
+
}
|
|
341
353
|
if (u.status !== "completed" && u.status !== "failed")
|
|
342
354
|
break;
|
|
343
|
-
const name = u.title || toolNames.get(u.toolCallId ?? "") || "";
|
|
344
355
|
send({
|
|
345
356
|
type: "tool_result",
|
|
346
|
-
id:
|
|
357
|
+
id: callId,
|
|
347
358
|
name,
|
|
348
359
|
output: acpToolContentText(u.content),
|
|
349
360
|
isError: u.status === "failed",
|
|
@@ -361,8 +372,30 @@ export async function loadACP(opts) {
|
|
|
361
372
|
const timeout = new Promise((_, reject) => {
|
|
362
373
|
timer = setTimeout(() => reject(new Error(`toolnexus: ACP turn timed out after ${permissionTimeoutMs}ms`)), permissionTimeoutMs);
|
|
363
374
|
});
|
|
375
|
+
let onAbort;
|
|
376
|
+
const cancelled = new Promise((resolve) => {
|
|
377
|
+
if (!signal)
|
|
378
|
+
return;
|
|
379
|
+
onAbort = () => resolve("cancelled");
|
|
380
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
381
|
+
});
|
|
364
382
|
try {
|
|
365
|
-
const msg = await Promise.race([replyPromise, timeout]);
|
|
383
|
+
const msg = await Promise.race([replyPromise, timeout, cancelled]);
|
|
384
|
+
if (msg === "cancelled") {
|
|
385
|
+
// ACP: session/cancel is a notification (no id); the agent then ends the turn with
|
|
386
|
+
// stopReason "cancelled". Wait (bounded) for that reply so the session is idle again.
|
|
387
|
+
write({ jsonrpc: "2.0", method: "session/cancel", params: { sessionId } });
|
|
388
|
+
const waitMs = opts.requestTimeoutMs && opts.requestTimeoutMs > 0 ? opts.requestTimeoutMs : 30000;
|
|
389
|
+
let wait;
|
|
390
|
+
await Promise.race([
|
|
391
|
+
replyPromise,
|
|
392
|
+
new Promise((r) => {
|
|
393
|
+
wait = setTimeout(r, waitMs);
|
|
394
|
+
}),
|
|
395
|
+
]);
|
|
396
|
+
clearTimeout(wait);
|
|
397
|
+
throw acpAbortError();
|
|
398
|
+
}
|
|
366
399
|
if (msg.error && msg.error.code === 0)
|
|
367
400
|
throw new Error(msg.error.message);
|
|
368
401
|
if (msg.error)
|
|
@@ -371,14 +404,16 @@ export async function loadACP(opts) {
|
|
|
371
404
|
}
|
|
372
405
|
finally {
|
|
373
406
|
clearTimeout(timer);
|
|
407
|
+
if (onAbort)
|
|
408
|
+
signal.removeEventListener("abort", onAbort);
|
|
374
409
|
pending.delete(id);
|
|
375
410
|
updateSink = null;
|
|
376
411
|
}
|
|
377
412
|
}
|
|
378
|
-
const generateStream = (request, emit) => {
|
|
413
|
+
const generateStream = (request, emit, callOpts) => {
|
|
379
414
|
if (closed)
|
|
380
415
|
return Promise.reject(new Error("toolnexus: ACP client is closed"));
|
|
381
|
-
const turn = turnQueue.then(() => promptOnce(request, emit));
|
|
416
|
+
const turn = turnQueue.then(() => promptOnce(request, emit, callOpts?.signal));
|
|
382
417
|
turnQueue = turn.then(() => undefined, () => undefined);
|
|
383
418
|
return turn;
|
|
384
419
|
};
|
|
@@ -400,13 +435,31 @@ export async function loadACP(opts) {
|
|
|
400
435
|
if (t === "late")
|
|
401
436
|
await Promise.race([childGone, new Promise((r) => setTimeout(r, 1000).unref())]);
|
|
402
437
|
}
|
|
403
|
-
const generate = (request) => generateStream(request);
|
|
404
|
-
|
|
438
|
+
const generate = (request, callOpts) => generateStream(request, undefined, callOpts);
|
|
439
|
+
// SPEC §8C (#197): sent at once on the running session — no restart, not queued behind a turn.
|
|
440
|
+
const liveCall = async (label, fn) => {
|
|
441
|
+
if (closed)
|
|
442
|
+
throw new Error("toolnexus: ACP client is closed");
|
|
443
|
+
try {
|
|
444
|
+
await fn();
|
|
445
|
+
}
|
|
446
|
+
catch (e) {
|
|
447
|
+
throw new Error(`toolnexus: acp: ${label}: ${e instanceof Error ? e.message : String(e)}`);
|
|
448
|
+
}
|
|
449
|
+
};
|
|
450
|
+
const setMode = (mode) => liveCall("session/set_mode", () => call("session/set_mode", { sessionId, modeId: mode }));
|
|
451
|
+
const setModel = (model) => liveCall(`set model ${JSON.stringify(model)}`, () => sendModel(model));
|
|
452
|
+
const client = { generate, generateStream, setMode, setModel, close, initializeResult };
|
|
405
453
|
// Non-enumerable, undocumented — internal test hook only, so the public
|
|
406
454
|
// ACPClient surface stays exactly {generate, generateStream, close}.
|
|
407
455
|
Object.defineProperty(client, "_pid", { value: child.pid, enumerable: false });
|
|
408
456
|
return client;
|
|
409
457
|
}
|
|
458
|
+
function acpAbortError() {
|
|
459
|
+
const e = new Error("toolnexus: ACP turn cancelled");
|
|
460
|
+
e.name = "AbortError";
|
|
461
|
+
return e;
|
|
462
|
+
}
|
|
410
463
|
/** Signals the child's whole process group (POSIX), else the child itself. Never throws. */
|
|
411
464
|
function killGroup(pid, signal) {
|
|
412
465
|
if (process.platform !== "win32") {
|
package/dist/agents/home.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Tool } from "../types.js";
|
|
2
2
|
import { Agent, type AgentRunOptions } from "./agent.js";
|
|
3
3
|
import { AgentRuntime } from "./runtime.js";
|
|
4
|
+
import type { ConversationStore } from "../client.js";
|
|
4
5
|
/**
|
|
5
6
|
* Bootstrap discovery order (§7E): identity first, durable memory last. Each present
|
|
6
7
|
* file is injected into the soul as a `## <filename>` section, in exactly this order.
|
|
@@ -16,29 +17,72 @@ export declare const HEARTBEAT_OK = "HEARTBEAT_OK";
|
|
|
16
17
|
* model knows the silent-reply contract, and the word "Heartbeat" so a HEARTBEAT.md
|
|
17
18
|
* that keys off it can recognise the trigger. */
|
|
18
19
|
export declare const HEARTBEAT_PROMPT: string;
|
|
20
|
+
/** The preset index cap (§7E "Memory hierarchy"): Claude Code's 200 lines / 25 KB. */
|
|
21
|
+
export declare const MEMORY_INDEX_CAP_PRESET: {
|
|
22
|
+
readonly maxLines: 200;
|
|
23
|
+
readonly maxBytes: 25600;
|
|
24
|
+
};
|
|
25
|
+
/** `memoryIndexCap` option: `true` ⇒ the preset; an object's missing field ⇒ the preset's. */
|
|
26
|
+
export type MemoryIndexCap = boolean | {
|
|
27
|
+
maxLines?: number;
|
|
28
|
+
maxBytes?: number;
|
|
29
|
+
};
|
|
30
|
+
/** Options for {@link composeSoul}. Absent ⇒ byte-identical to the pre-hierarchy soul. */
|
|
31
|
+
export interface ComposeSoulOptions {
|
|
32
|
+
/** Second, smaller injection cap for `MEMORY.md` only (whole lines). Off by default. */
|
|
33
|
+
memoryIndexCap?: MemoryIndexCap;
|
|
34
|
+
/** Notes tier on — selects the notice wording that points at `note:<slug>`. */
|
|
35
|
+
notes?: boolean;
|
|
36
|
+
}
|
|
37
|
+
/** The exact notice appended to a capped `MEMORY.md` (§7E "Memory hierarchy"). */
|
|
38
|
+
export declare function memoryIndexNotice(maxLines: number, maxBytes: number, notes: boolean): string;
|
|
39
|
+
/**
|
|
40
|
+
* Apply the index cap to `MEMORY.md`'s (already 2 MB-capped) content. Lines are split on
|
|
41
|
+
* "\n"; one trailing "\n" does not start a line. Within both bounds ⇒ returned unchanged.
|
|
42
|
+
* Otherwise keep the first `maxLines` lines, drop trailing whole lines until the "\n"-joined
|
|
43
|
+
* UTF-8 length is <= `maxBytes` (a lone over-long first line is cut at a character
|
|
44
|
+
* boundary), and append {@link memoryIndexNotice}.
|
|
45
|
+
*/
|
|
46
|
+
export declare function applyIndexCap(content: string, cap: {
|
|
47
|
+
maxLines: number;
|
|
48
|
+
maxBytes: number;
|
|
49
|
+
}, notes?: boolean): string;
|
|
19
50
|
/**
|
|
20
51
|
* Compose the bootstrap files present in `dir` into one soul string — the frozen
|
|
21
52
|
* snapshot injected as the system prompt for the whole run. Only present files
|
|
22
53
|
* appear, always in {@link BOOTSTRAP_ORDER}, each as a `## <filename>` section.
|
|
23
54
|
*/
|
|
24
|
-
export declare function composeSoul(dir: string): {
|
|
55
|
+
export declare function composeSoul(dir: string, opts?: ComposeSoulOptions): {
|
|
25
56
|
soul: string;
|
|
26
57
|
found: string[];
|
|
27
58
|
};
|
|
59
|
+
/** A valid note slug (§7E "Memory hierarchy"): lowercase, no path separators, no `..`. */
|
|
60
|
+
export declare const NOTE_SLUG_RE: RegExp;
|
|
61
|
+
/** Options for {@link memoryTool}. */
|
|
62
|
+
export interface MemoryToolOptions {
|
|
63
|
+
/** Turn on the notes tier: `note:<slug>` targets plus `read`/`list`. Off ⇒ the tool is
|
|
64
|
+
* byte-identical to the pre-hierarchy tool (schema, description, behavior). */
|
|
65
|
+
notes?: boolean;
|
|
66
|
+
}
|
|
67
|
+
/** The notes-on description (§7E): says what memory is FOR, not rules about what to record. */
|
|
68
|
+
export declare const MEMORY_NOTES_DESCRIPTION: string;
|
|
28
69
|
/**
|
|
29
70
|
* The `memory` builtin (§7E, file-backed, opt-in). One tool, three actions over
|
|
30
71
|
* `MEMORY.md` (the agent's own notes) and `USER.md` (its model of the user):
|
|
31
72
|
*
|
|
32
73
|
* - `add` — append an entry
|
|
33
|
-
* - `replace` — swap an existing substring for `with`
|
|
34
|
-
* - `remove` — delete an existing substring
|
|
74
|
+
* - `replace` — swap the first occurrence of an existing substring for `with`
|
|
75
|
+
* - `remove` — delete the first occurrence of an existing substring
|
|
76
|
+
*
|
|
77
|
+
* With `{notes: true}` it also takes `target: "note:<slug>"` (`<dir>/memory/<slug>.md`,
|
|
78
|
+
* never injected into the soul) and two read actions, `read` and `list`.
|
|
35
79
|
*
|
|
36
80
|
* Every action writes to **disk**. It does **not** touch the live session's system
|
|
37
81
|
* prompt: the frozen-snapshot rule means the edit loads only at the START of the
|
|
38
82
|
* next session (keeping a long-lived persona cache-stable). A `replace`/`remove`
|
|
39
83
|
* whose substring is absent is a loud `isError` — never a silent no-op.
|
|
40
84
|
*/
|
|
41
|
-
export declare function memoryTool(dir: string): Tool;
|
|
85
|
+
export declare function memoryTool(dir: string, opts?: MemoryToolOptions): Tool;
|
|
42
86
|
/** Options for {@link fromDir}. */
|
|
43
87
|
export interface FromDirOptions {
|
|
44
88
|
/** Routing description a delegating model sees; default derived from the dir. */
|
|
@@ -51,6 +95,17 @@ export interface FromDirOptions {
|
|
|
51
95
|
tools?: Tool[];
|
|
52
96
|
/** Set `false` to omit the memory tool (a read-only persona). */
|
|
53
97
|
memory?: boolean;
|
|
98
|
+
/** Cap the injected `MEMORY.md` (whole lines; `true` ⇒ 200 lines / 25600 bytes). Off by default. */
|
|
99
|
+
memoryIndexCap?: MemoryIndexCap;
|
|
100
|
+
/** Turn on the notes tier of the memory tool (`note:<slug>`, `read`, `list`). Off by default. */
|
|
101
|
+
notes?: boolean;
|
|
102
|
+
/** Dream on a schedule: when this persona is started with {@link startAgent}, a
|
|
103
|
+
* {@link dreamer} over the same dir runs on the same runtime every `everyMs`. */
|
|
104
|
+
dream?: {
|
|
105
|
+
everyMs: number;
|
|
106
|
+
store: ConversationStore;
|
|
107
|
+
instructions?: string;
|
|
108
|
+
};
|
|
54
109
|
}
|
|
55
110
|
/**
|
|
56
111
|
* The directory IS the agent (§7E). Discovers the bootstrap files in `dir`, composes
|
|
@@ -59,6 +114,32 @@ export interface FromDirOptions {
|
|
|
59
114
|
* all apply.
|
|
60
115
|
*/
|
|
61
116
|
export declare function fromDir(dir: string, opts?: FromDirOptions): Agent;
|
|
117
|
+
/**
|
|
118
|
+
* What a dream pass is FOR and the order it works in (§7E "Dreaming"). Byte-identical in
|
|
119
|
+
* every port. It becomes the dreamer's `## HEARTBEAT.md` section, so the ordinary
|
|
120
|
+
* {@link HEARTBEAT_PROMPT} wake drives a pass and `HEARTBEAT_OK` keeps an idle pass silent.
|
|
121
|
+
*/
|
|
122
|
+
export declare const DREAM_PROMPT: string;
|
|
123
|
+
/** Options for {@link dreamer}. */
|
|
124
|
+
export interface DreamerOptions {
|
|
125
|
+
/** The transcripts to verify memory against — must implement `list()`. Required. */
|
|
126
|
+
store: ConversationStore;
|
|
127
|
+
/** Host prose appended after {@link DREAM_PROMPT} (and after `DREAM.md`, if present). */
|
|
128
|
+
instructions?: string;
|
|
129
|
+
/** Agent name; default `<basename(dir)>-dreamer`. */
|
|
130
|
+
name?: string;
|
|
131
|
+
/** Model id; default `"inherit"` — the persona's client (use a cheap model there). */
|
|
132
|
+
model?: string;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* The built-in dreamer preset (§7E "Dreaming"): a plain {@link Agent} whose tools are
|
|
136
|
+
* EXACTLY `memory` (notes on, over `dir`) and `transcript_search` (over `store`) — never
|
|
137
|
+
* the persona's shell/file tools. Its soul is one `## HEARTBEAT.md` section holding
|
|
138
|
+
* {@link DREAM_PROMPT}, then `DREAM.md` from `dir` (trimmed, if non-empty), then
|
|
139
|
+
* `instructions` (trimmed, if non-empty), joined by blank lines. Schedule it with
|
|
140
|
+
* {@link startAgent} or run one pass with `run(HEARTBEAT_PROMPT, …)`.
|
|
141
|
+
*/
|
|
142
|
+
export declare function dreamer(dir: string, opts: DreamerOptions): Agent;
|
|
62
143
|
/** Heartbeat configuration for {@link startAgent}. */
|
|
63
144
|
export interface HeartbeatOptions {
|
|
64
145
|
/** Interval between beats, in ms (measured on the runtime's clock). */
|