@animalabs/agent-framework 0.29.0 → 0.31.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 +71 -8
- package/dist/src/admission/admit.d.ts +90 -0
- package/dist/src/admission/admit.d.ts.map +1 -0
- package/dist/src/admission/admit.js +43 -0
- package/dist/src/admission/admit.js.map +1 -0
- package/dist/src/agent.d.ts +50 -9
- package/dist/src/agent.d.ts.map +1 -1
- package/dist/src/agent.js +125 -23
- package/dist/src/agent.js.map +1 -1
- package/dist/src/api/mcp-server-core.d.ts.map +1 -1
- package/dist/src/api/mcp-server-core.js +10 -3
- package/dist/src/api/mcp-server-core.js.map +1 -1
- package/dist/src/author-presentation.d.ts +50 -0
- package/dist/src/author-presentation.d.ts.map +1 -0
- package/dist/src/author-presentation.js +58 -0
- package/dist/src/author-presentation.js.map +1 -0
- package/dist/src/code-execution/isolate/render.d.ts.map +1 -1
- package/dist/src/code-execution/isolate/render.js +7 -6
- package/dist/src/code-execution/isolate/render.js.map +1 -1
- package/dist/src/code-execution/isolate/scripts.d.ts.map +1 -1
- package/dist/src/code-execution/isolate/scripts.js +5 -4
- package/dist/src/code-execution/isolate/scripts.js.map +1 -1
- package/dist/src/dendrite/index.d.ts +6 -0
- package/dist/src/dendrite/index.d.ts.map +1 -0
- package/dist/src/dendrite/index.js +3 -0
- package/dist/src/dendrite/index.js.map +1 -0
- package/dist/src/dendrite/presets.d.ts +60 -0
- package/dist/src/dendrite/presets.d.ts.map +1 -0
- package/dist/src/dendrite/presets.js +117 -0
- package/dist/src/dendrite/presets.js.map +1 -0
- package/dist/src/dendrite/registry.d.ts +260 -0
- package/dist/src/dendrite/registry.d.ts.map +1 -0
- package/dist/src/dendrite/registry.js +742 -0
- package/dist/src/dendrite/registry.js.map +1 -0
- package/dist/src/dendrite/types.d.ts +392 -0
- package/dist/src/dendrite/types.d.ts.map +1 -0
- package/dist/src/dendrite/types.js +24 -0
- package/dist/src/dendrite/types.js.map +1 -0
- package/dist/src/framework.d.ts +806 -118
- package/dist/src/framework.d.ts.map +1 -1
- package/dist/src/framework.js +4180 -1121
- package/dist/src/framework.js.map +1 -1
- package/dist/src/gate/event-gate.d.ts +36 -21
- package/dist/src/gate/event-gate.d.ts.map +1 -1
- package/dist/src/gate/event-gate.js +223 -149
- package/dist/src/gate/event-gate.js.map +1 -1
- package/dist/src/gate/recipe-reconcile.d.ts.map +1 -1
- package/dist/src/gate/recipe-reconcile.js +6 -5
- package/dist/src/gate/recipe-reconcile.js.map +1 -1
- package/dist/src/host-presentation.d.ts +4 -0
- package/dist/src/host-presentation.d.ts.map +1 -1
- package/dist/src/host-presentation.js +12 -10
- package/dist/src/host-presentation.js.map +1 -1
- package/dist/src/index.d.ts +6 -2
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +7 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/inference-bounds.d.ts +37 -0
- package/dist/src/inference-bounds.d.ts.map +1 -0
- package/dist/src/inference-bounds.js +84 -0
- package/dist/src/inference-bounds.js.map +1 -0
- package/dist/src/line-reader.d.ts +7 -1
- package/dist/src/line-reader.d.ts.map +1 -1
- package/dist/src/line-reader.js +17 -6
- package/dist/src/line-reader.js.map +1 -1
- package/dist/src/mcpl/channel-registry.d.ts +84 -9
- package/dist/src/mcpl/channel-registry.d.ts.map +1 -1
- package/dist/src/mcpl/channel-registry.js +136 -58
- package/dist/src/mcpl/channel-registry.js.map +1 -1
- package/dist/src/mcpl/errors.d.ts +1 -0
- package/dist/src/mcpl/errors.d.ts.map +1 -1
- package/dist/src/mcpl/errors.js +3 -0
- package/dist/src/mcpl/errors.js.map +1 -1
- package/dist/src/mcpl/inbound-source.d.ts +2 -2
- package/dist/src/mcpl/inbound-source.js +2 -2
- package/dist/src/mcpl/inference-router.d.ts +11 -1
- package/dist/src/mcpl/inference-router.d.ts.map +1 -1
- package/dist/src/mcpl/inference-router.js +65 -9
- package/dist/src/mcpl/inference-router.js.map +1 -1
- package/dist/src/mcpl/modern-connection.d.ts +10 -7
- package/dist/src/mcpl/modern-connection.d.ts.map +1 -1
- package/dist/src/mcpl/modern-connection.js +85 -74
- package/dist/src/mcpl/modern-connection.js.map +1 -1
- package/dist/src/mcpl/protocol-family.d.ts +1 -1
- package/dist/src/mcpl/protocol-family.d.ts.map +1 -1
- package/dist/src/mcpl/protocol-family.js +45 -3
- package/dist/src/mcpl/protocol-family.js.map +1 -1
- package/dist/src/mcpl/push-coalescer.d.ts +11 -7
- package/dist/src/mcpl/push-coalescer.d.ts.map +1 -1
- package/dist/src/mcpl/push-coalescer.js +21 -0
- package/dist/src/mcpl/push-coalescer.js.map +1 -1
- package/dist/src/mcpl/references.d.ts +1 -2
- package/dist/src/mcpl/references.d.ts.map +1 -1
- package/dist/src/mcpl/references.js +3 -3
- package/dist/src/mcpl/references.js.map +1 -1
- package/dist/src/mcpl/server-connection.d.ts +14 -0
- package/dist/src/mcpl/server-connection.d.ts.map +1 -1
- package/dist/src/mcpl/server-connection.js +26 -22
- package/dist/src/mcpl/server-connection.js.map +1 -1
- package/dist/src/mcpl/tool-signature.d.ts +1 -0
- package/dist/src/mcpl/tool-signature.d.ts.map +1 -1
- package/dist/src/mcpl/tool-signature.js +16 -11
- package/dist/src/mcpl/tool-signature.js.map +1 -1
- package/dist/src/mcpl/toolsets-module.d.ts.map +1 -1
- package/dist/src/mcpl/toolsets-module.js +7 -0
- package/dist/src/mcpl/toolsets-module.js.map +1 -1
- package/dist/src/mcpl/transport.d.ts +44 -21
- package/dist/src/mcpl/transport.d.ts.map +1 -1
- package/dist/src/mcpl/transport.js +283 -64
- package/dist/src/mcpl/transport.js.map +1 -1
- package/dist/src/mcpl/types.d.ts +17 -1
- package/dist/src/mcpl/types.d.ts.map +1 -1
- package/dist/src/mcpl/types.js.map +1 -1
- package/dist/src/module-registry.d.ts +32 -4
- package/dist/src/module-registry.d.ts.map +1 -1
- package/dist/src/module-registry.js +189 -25
- package/dist/src/module-registry.js.map +1 -1
- package/dist/src/modules/discord/discord-js-client.d.ts.map +1 -1
- package/dist/src/modules/discord/discord-js-client.js +24 -16
- package/dist/src/modules/discord/discord-js-client.js.map +1 -1
- package/dist/src/modules/sender-name.d.ts +5 -4
- package/dist/src/modules/sender-name.d.ts.map +1 -1
- package/dist/src/modules/sender-name.js +7 -5
- package/dist/src/modules/sender-name.js.map +1 -1
- package/dist/src/modules/workspace/disk-agreement.d.ts +16 -8
- package/dist/src/modules/workspace/disk-agreement.d.ts.map +1 -1
- package/dist/src/modules/workspace/disk-agreement.js +33 -12
- package/dist/src/modules/workspace/disk-agreement.js.map +1 -1
- package/dist/src/modules/workspace/index.d.ts +6 -3
- package/dist/src/modules/workspace/index.d.ts.map +1 -1
- package/dist/src/modules/workspace/index.js +61 -15
- package/dist/src/modules/workspace/index.js.map +1 -1
- package/dist/src/modules/workspace/observe.d.ts +9 -1
- package/dist/src/modules/workspace/observe.d.ts.map +1 -1
- package/dist/src/modules/workspace/observe.js +50 -10
- package/dist/src/modules/workspace/observe.js.map +1 -1
- package/dist/src/modules/workspace/reconcile.d.ts +11 -1
- package/dist/src/modules/workspace/reconcile.d.ts.map +1 -1
- package/dist/src/modules/workspace/reconcile.js +19 -8
- package/dist/src/modules/workspace/reconcile.js.map +1 -1
- package/dist/src/modules/workspace/types.d.ts +8 -0
- package/dist/src/modules/workspace/types.d.ts.map +1 -1
- package/dist/src/modules/workspace/types.js.map +1 -1
- package/dist/src/modules/workspace/watcher.d.ts +7 -0
- package/dist/src/modules/workspace/watcher.d.ts.map +1 -1
- package/dist/src/modules/workspace/watcher.js +7 -5
- package/dist/src/modules/workspace/watcher.js.map +1 -1
- package/dist/src/notice-tags.d.ts +2 -0
- package/dist/src/notice-tags.d.ts.map +1 -1
- package/dist/src/notice-tags.js +2 -0
- package/dist/src/notice-tags.js.map +1 -1
- package/dist/src/recovery/discord-awareness-outbox.d.ts.map +1 -1
- package/dist/src/recovery/discord-awareness-outbox.js +1 -9
- package/dist/src/recovery/discord-awareness-outbox.js.map +1 -1
- package/dist/src/silent-heartbeat.d.ts +4 -2
- package/dist/src/silent-heartbeat.d.ts.map +1 -1
- package/dist/src/silent-heartbeat.js +7 -4
- package/dist/src/silent-heartbeat.js.map +1 -1
- package/dist/src/stored-injections.d.ts +4 -0
- package/dist/src/stored-injections.d.ts.map +1 -1
- package/dist/src/stored-injections.js +6 -0
- package/dist/src/stored-injections.js.map +1 -1
- package/dist/src/supplied-text.d.ts +2 -0
- package/dist/src/supplied-text.d.ts.map +1 -1
- package/dist/src/supplied-text.js +16 -7
- package/dist/src/supplied-text.js.map +1 -1
- package/dist/src/tool-result-guard.js +1 -1
- package/dist/src/tool-result-guard.js.map +1 -1
- package/dist/src/tune-out/coordinator.d.ts +368 -16
- package/dist/src/tune-out/coordinator.d.ts.map +1 -1
- package/dist/src/tune-out/coordinator.js +1224 -136
- package/dist/src/tune-out/coordinator.js.map +1 -1
- package/dist/src/tune-out/tools.d.ts +115 -7
- package/dist/src/tune-out/tools.d.ts.map +1 -1
- package/dist/src/tune-out/tools.js +55 -18
- package/dist/src/tune-out/tools.js.map +1 -1
- package/dist/src/types/agent.d.ts +27 -1
- package/dist/src/types/agent.d.ts.map +1 -1
- package/dist/src/types/framework.d.ts +153 -15
- package/dist/src/types/framework.d.ts.map +1 -1
- package/dist/src/types/module.d.ts +42 -3
- package/dist/src/types/module.d.ts.map +1 -1
- package/dist/src/types/trace.d.ts +140 -3
- package/dist/src/types/trace.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/admission/admit.ts +113 -0
- package/src/agent.ts +138 -31
- package/src/api/mcp-server-core.ts +10 -3
- package/src/author-presentation.ts +81 -0
- package/src/code-execution/isolate/render.ts +7 -6
- package/src/code-execution/isolate/scripts.ts +5 -4
- package/src/dendrite/index.ts +32 -0
- package/src/dendrite/presets.ts +149 -0
- package/src/dendrite/registry.ts +808 -0
- package/src/dendrite/types.ts +397 -0
- package/src/framework.ts +4344 -1052
- package/src/gate/event-gate.ts +251 -150
- package/src/gate/recipe-reconcile.ts +7 -5
- package/src/host-presentation.ts +14 -10
- package/src/index.ts +16 -2
- package/src/inference-bounds.ts +111 -0
- package/src/line-reader.ts +22 -5
- package/src/mcpl/channel-registry.ts +170 -60
- package/src/mcpl/errors.ts +4 -0
- package/src/mcpl/inbound-source.ts +2 -2
- package/src/mcpl/inference-router.ts +67 -10
- package/src/mcpl/modern-connection.ts +83 -68
- package/src/mcpl/protocol-family.ts +37 -4
- package/src/mcpl/push-coalescer.ts +21 -7
- package/src/mcpl/references.ts +3 -4
- package/src/mcpl/server-connection.ts +30 -21
- package/src/mcpl/tool-signature.ts +18 -11
- package/src/mcpl/toolsets-module.ts +7 -0
- package/src/mcpl/transport.ts +261 -60
- package/src/mcpl/types.ts +18 -1
- package/src/module-registry.ts +178 -25
- package/src/modules/discord/discord-js-client.ts +33 -29
- package/src/modules/sender-name.ts +8 -5
- package/src/modules/workspace/disk-agreement.ts +39 -15
- package/src/modules/workspace/index.ts +65 -14
- package/src/modules/workspace/observe.ts +51 -10
- package/src/modules/workspace/reconcile.ts +22 -8
- package/src/modules/workspace/types.ts +5 -0
- package/src/modules/workspace/watcher.ts +8 -6
- package/src/notice-tags.ts +2 -0
- package/src/recovery/discord-awareness-outbox.ts +1 -11
- package/src/silent-heartbeat.ts +7 -4
- package/src/stored-injections.ts +7 -0
- package/src/supplied-text.ts +17 -6
- package/src/tool-result-guard.ts +1 -1
- package/src/tune-out/coordinator.ts +1374 -141
- package/src/tune-out/tools.ts +166 -13
- package/src/types/agent.ts +30 -1
- package/src/types/framework.ts +157 -15
- package/src/types/module.ts +42 -3
- package/src/types/trace.ts +154 -3
|
@@ -40,6 +40,8 @@
|
|
|
40
40
|
* rule with no name is never merged: the merge keys rules by name.
|
|
41
41
|
*/
|
|
42
42
|
|
|
43
|
+
import { quotedValue } from '../supplied-text.js';
|
|
44
|
+
|
|
43
45
|
/** What a start changed, by rule name, for its trace and its log line. */
|
|
44
46
|
export interface RecipeChanges {
|
|
45
47
|
added: string[];
|
|
@@ -173,11 +175,11 @@ export function reconcileWithRecipe(
|
|
|
173
175
|
const agree = (a: unknown, b: unknown): boolean => meaningOf(a) === meaningOf(b);
|
|
174
176
|
const liveDuplicate = duplicateName(live.rules);
|
|
175
177
|
if (liveDuplicate !== undefined) {
|
|
176
|
-
throw new Error(`gate.json names the rule
|
|
178
|
+
throw new Error(`gate.json names the rule ${quotedValue(liveDuplicate)} twice`);
|
|
177
179
|
}
|
|
178
180
|
const recipeDuplicate = duplicateName(recipe.rules);
|
|
179
181
|
if (recipeDuplicate !== undefined) {
|
|
180
|
-
throw new Error(`the recipe names the rule
|
|
182
|
+
throw new Error(`the recipe names the rule ${quotedValue(recipeDuplicate)} twice`);
|
|
181
183
|
}
|
|
182
184
|
|
|
183
185
|
const changes = noChanges();
|
|
@@ -322,7 +324,7 @@ export function reconcileLine(changes: RecipeChanges): string | null {
|
|
|
322
324
|
parts.push(`the recorded recipe couldn't be read (${changes.recordUnreadable}), so this start removed and redefined nothing`);
|
|
323
325
|
}
|
|
324
326
|
const list = (label: string, names: string[]) => {
|
|
325
|
-
if (names.length > 0) parts.push(`${label} ${names.join(', ')}`);
|
|
327
|
+
if (names.length > 0) parts.push(`${label} ${names.map((name) => quotedValue(name)).join(', ')}`);
|
|
326
328
|
};
|
|
327
329
|
list('added', changes.added);
|
|
328
330
|
list('removed', changes.removed);
|
|
@@ -332,13 +334,13 @@ export function reconcileLine(changes: RecipeChanges): string | null {
|
|
|
332
334
|
list('kept as edited, though the recipe changed or dropped it:', changes.keptEdited);
|
|
333
335
|
if (changes.differsFromRecipe.length > 0) {
|
|
334
336
|
parts.push(
|
|
335
|
-
`kept, though the recipe defines it otherwise: ${changes.differsFromRecipe.join(', ')} `
|
|
337
|
+
`kept, though the recipe defines it otherwise: ${changes.differsFromRecipe.map((name) => quotedValue(name)).join(', ')} `
|
|
336
338
|
+ '(remove such a rule, and the next start restores it from the recipe)',
|
|
337
339
|
);
|
|
338
340
|
}
|
|
339
341
|
if (changes.outsideRecipe.length > 0) {
|
|
340
342
|
parts.push(
|
|
341
|
-
`kept, not in the recipe: ${changes.outsideRecipe.join(', ')} `
|
|
343
|
+
`kept, not in the recipe: ${changes.outsideRecipe.map((name) => quotedValue(name)).join(', ')} `
|
|
342
344
|
+ "(a stale one can be removed, and no start brings it back)",
|
|
343
345
|
);
|
|
344
346
|
}
|
package/src/host-presentation.ts
CHANGED
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
|
|
30
30
|
import { dirname } from 'node:path';
|
|
31
31
|
import type { ToolDefinition } from './types/events.js';
|
|
32
|
-
import { checkArgs,
|
|
32
|
+
import { checkArgs, renderToolDefinition, toolLine } from './mcpl/tool-signature.js';
|
|
33
33
|
import { META_TOOL_NAMES } from './mcpl/toolset-host.js';
|
|
34
34
|
|
|
35
35
|
/** The built-in toolset's name, as agents address it (`toolsets--open {toolset: "settings"}`). */
|
|
@@ -72,6 +72,8 @@ export interface HostPresentationDeps {
|
|
|
72
72
|
isUtility(toolName: string): boolean;
|
|
73
73
|
/** The resident agents of this host (no forks, ephemeral runs or the subconscious). */
|
|
74
74
|
residents(): string[];
|
|
75
|
+
/** The agent whose presentation this one is shown: a derived agent that shows its parent's tool list, its parent; anyone else, itself. */
|
|
76
|
+
ownerOf?(agentName: string): string;
|
|
75
77
|
}
|
|
76
78
|
|
|
77
79
|
interface AgentState {
|
|
@@ -198,10 +200,15 @@ export class HostPresentation {
|
|
|
198
200
|
return server?.configured === 'closed-tab' && !server.connected;
|
|
199
201
|
}
|
|
200
202
|
|
|
203
|
+
/** The agent whose presentation `agentName` is shown (HostPresentationDeps.ownerOf). */
|
|
204
|
+
ownerOf(agentName: string): string {
|
|
205
|
+
return this.deps.ownerOf?.(agentName) ?? agentName;
|
|
206
|
+
}
|
|
207
|
+
|
|
201
208
|
/** How a server is presented to an agent now. */
|
|
202
209
|
presentation(agentName: string, serverId: string): ServerPresentation {
|
|
203
210
|
if (this.isClosedTab(serverId)) return 'closed-tab';
|
|
204
|
-
const chosen = this.doc.agents[agentName]?.servers[serverId];
|
|
211
|
+
const chosen = this.doc.agents[this.ownerOf(agentName)]?.servers[serverId];
|
|
205
212
|
if (chosen) return chosen;
|
|
206
213
|
const configured = this.server(serverId)?.configured ?? 'full';
|
|
207
214
|
return configured === 'closed-tab' ? 'tab' : configured;
|
|
@@ -218,7 +225,7 @@ export class HostPresentation {
|
|
|
218
225
|
}
|
|
219
226
|
|
|
220
227
|
isOpen(agentName: string, key: string): boolean {
|
|
221
|
-
return this.doc.agents[agentName]?.open.includes(key) ?? false;
|
|
228
|
+
return this.doc.agents[this.ownerOf(agentName)]?.open.includes(key) ?? false;
|
|
222
229
|
}
|
|
223
230
|
|
|
224
231
|
private setOpen(agentName: string, key: string, open: boolean): void {
|
|
@@ -267,10 +274,7 @@ export class HostPresentation {
|
|
|
267
274
|
private announce(label: string, tools: ToolDefinition[], closeHint: string): string {
|
|
268
275
|
const lines = [`${label} open: call these with ${META_TOOL_NAMES.act} {tool, args}; they aren't in your tool list.`];
|
|
269
276
|
if (!tools.length) lines.push(' (none right now)');
|
|
270
|
-
for (const t of tools)
|
|
271
|
-
const sentence = firstSentence(t.description);
|
|
272
|
-
lines.push(` ${signature(t)}${sentence ? ` — ${sentence}` : ''}`);
|
|
273
|
-
}
|
|
277
|
+
for (const t of tools) lines.push(toolLine(t));
|
|
274
278
|
lines.push(`${closeHint} closes it.`);
|
|
275
279
|
return lines.join('\n');
|
|
276
280
|
}
|
|
@@ -311,7 +315,7 @@ export class HostPresentation {
|
|
|
311
315
|
const open = this.isOpen(agentName, BUILTIN_TOOLSET);
|
|
312
316
|
return [
|
|
313
317
|
`${this.builtinLabel} ${open ? 'open' : 'closed'}${open ? '' : ` — ${META_TOOL_NAMES.open} {toolset:"${BUILTIN_TOOLSET}"} opens it`}`,
|
|
314
|
-
...tools.map((t) =>
|
|
318
|
+
...tools.map((t) => toolLine(t)),
|
|
315
319
|
].join('\n');
|
|
316
320
|
}
|
|
317
321
|
const { serverId } = which;
|
|
@@ -322,7 +326,7 @@ export class HostPresentation {
|
|
|
322
326
|
const head = mode === 'full'
|
|
323
327
|
? `${this.tabLabel(serverId)} in your tool list${tools.length ? `; its hidden tools (${open ? 'open' : 'closed'}):` : '.'}`
|
|
324
328
|
: `${this.tabLabel(serverId)} a tab, connected, ${open ? 'open' : `hidden — ${META_TOOL_NAMES.open} {server:"${serverId}"} opens it`}:`;
|
|
325
|
-
return [head, ...tools.map((t) =>
|
|
329
|
+
return [head, ...tools.map((t) => toolLine(t))].join('\n');
|
|
326
330
|
}
|
|
327
331
|
|
|
328
332
|
/** `toolsets--look {tool}` for a host toolset's tool, or null when no host toolset has it. */
|
|
@@ -578,7 +582,7 @@ export class HostPresentation {
|
|
|
578
582
|
openMarkers(agentName: string): Array<{ marker: string; reminder: () => string; dueWhenAbsent: true }> {
|
|
579
583
|
if (!this.active(agentName)) return [];
|
|
580
584
|
const out: Array<{ marker: string; reminder: () => string; dueWhenAbsent: true }> = [];
|
|
581
|
-
for (const key of this.doc.agents[agentName]?.open ?? []) {
|
|
585
|
+
for (const key of this.doc.agents[this.ownerOf(agentName)]?.open ?? []) {
|
|
582
586
|
if (key === BUILTIN_TOOLSET) {
|
|
583
587
|
out.push({
|
|
584
588
|
marker: `${this.builtinLabel} open`,
|
package/src/index.ts
CHANGED
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
export * from './types/index.js';
|
|
3
3
|
|
|
4
4
|
// Core classes
|
|
5
|
-
export { AgentFramework, BudgetPreflightError, DefaultErrorPolicy, ProviderWaitNotSleptError, QuiesceBlockedError, ResumeBlockedError } from './framework.js';
|
|
6
|
-
export type { RuntimeSettingsPreview, HostModeStatus } from './framework.js';
|
|
5
|
+
export { AgentFramework, AgentStoppedError, BudgetPreflightError, DefaultErrorPolicy, ProviderWaitNotSleptError, QuiesceBlockedError, ResumeBlockedError } from './framework.js';
|
|
6
|
+
export type { RuntimeSettingsPreview, HostModeStatus, DeriveAgentOptions } from './framework.js';
|
|
7
7
|
export { Agent } from './agent.js';
|
|
8
|
+
export { FLAT_MINIMUM_MS, type InferenceBoundsOptions } from './inference-bounds.js';
|
|
8
9
|
export { storedInjectionMarkOf, type StoredInjectionMark } from './stored-injections.js';
|
|
9
10
|
export type { StartStreamResult } from './agent.js';
|
|
10
11
|
export { ProcessQueueImpl } from './queue.js';
|
|
@@ -15,6 +16,9 @@ export { CLOCK_ROW_KIND, resolveClockConfig } from './clock-rows.js';
|
|
|
15
16
|
export type { ClockConfig, ClockStamp, ResolvedClockConfig } from './clock-rows.js';
|
|
16
17
|
export { REFUSAL_REACTIONS, REFUSAL_REACTION_FALLBACK, REFUSAL_REACTION_BASELINE } from './refusal-reactions.js';
|
|
17
18
|
|
|
19
|
+
// Dendrite: agent registry, lifecycle and presets
|
|
20
|
+
export * from './dendrite/index.js';
|
|
21
|
+
|
|
18
22
|
// Built-in modules
|
|
19
23
|
export * from './modules/index.js';
|
|
20
24
|
|
|
@@ -196,6 +200,16 @@ export type {
|
|
|
196
200
|
} from './recovery/offline-branch.js';
|
|
197
201
|
|
|
198
202
|
export * from "./tool-presentation.js";
|
|
203
|
+
|
|
204
|
+
// Tune-out: the subconscious configuration and the default reader-fork framing.
|
|
205
|
+
export { presentAuthor, authorLine, authorNamed, ANONYMOUS_PARTICIPANT } from './author-presentation.js';
|
|
206
|
+
export {
|
|
207
|
+
defaultReaderFraming,
|
|
208
|
+
type SubconsciousConfig,
|
|
209
|
+
type PersistentReaderConfig,
|
|
210
|
+
type ForkReaderConfig,
|
|
211
|
+
type ReaderFramingContext,
|
|
212
|
+
} from './tune-out/tools.js';
|
|
199
213
|
export {
|
|
200
214
|
SILENT_HEARTBEAT_SEPARATOR,
|
|
201
215
|
silentHeartbeatOf,
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { MembraneError } from '@animalabs/membrane';
|
|
2
|
+
import { PROGRESS_BOUNDS, resolveProgressBounds, type ProgressBounds } from '@animalabs/membrane/progress-bounds';
|
|
3
|
+
|
|
4
|
+
/** FrameworkConfig.inferenceBounds: how long the framework lets a model call
|
|
5
|
+
* go without progress. */
|
|
6
|
+
export interface InferenceBoundsOptions {
|
|
7
|
+
/** Membrane's phase bounds for a stream whose adapter reports content
|
|
8
|
+
* (Anthropic, Bedrock), and the values the flat and whole-call bounds
|
|
9
|
+
* derive from: the larger of `beforeContentMs` and `betweenEventsMs`,
|
|
10
|
+
* plus `max_tokens` at `thinkTokensPerSecond`. Each one given replaces
|
|
11
|
+
* membrane's default (PROGRESS_BOUNDS). */
|
|
12
|
+
progressBounds?: Partial<ProgressBounds>;
|
|
13
|
+
/** The least the flat bound on a stream can be, from its last provider
|
|
14
|
+
* event. Default FLAT_MINIMUM_MS. */
|
|
15
|
+
flatMinimumMs?: number;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const FLAT_MINIMUM_MS = 1_862_000;
|
|
19
|
+
|
|
20
|
+
export interface InferenceBounds {
|
|
21
|
+
readonly progressBounds: Readonly<ProgressBounds>;
|
|
22
|
+
readonly flatMinimumMs: number;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function resolveInferenceBounds(option: false | InferenceBoundsOptions | undefined): InferenceBounds | null {
|
|
26
|
+
if (option === false) return null;
|
|
27
|
+
if (option !== undefined && (typeof option !== 'object' || option === null)) {
|
|
28
|
+
throw new TypeError(`[agent-framework] inferenceBounds must be false or an object, got ${option === null ? 'null' : typeof option}`);
|
|
29
|
+
}
|
|
30
|
+
const given = option?.progressBounds;
|
|
31
|
+
if (given !== undefined && (typeof given !== 'object' || given === null)) {
|
|
32
|
+
throw new TypeError(`[agent-framework] inferenceBounds.progressBounds must be an object of bounds, got ${given === null ? 'null' : typeof given}`);
|
|
33
|
+
}
|
|
34
|
+
let progressBounds: ProgressBounds;
|
|
35
|
+
try {
|
|
36
|
+
progressBounds = resolveProgressBounds(given ?? true) ?? { ...PROGRESS_BOUNDS };
|
|
37
|
+
} catch (refused) {
|
|
38
|
+
const why = refused instanceof Error ? refused.message.replace(/^\[membrane\] /, '') : String(refused);
|
|
39
|
+
throw new (refused instanceof TypeError ? TypeError : RangeError)(`[agent-framework] inferenceBounds.${why}`);
|
|
40
|
+
}
|
|
41
|
+
const flatMinimumMs: unknown = option?.flatMinimumMs === undefined ? FLAT_MINIMUM_MS : option.flatMinimumMs;
|
|
42
|
+
if (typeof flatMinimumMs !== 'number' || !Number.isFinite(flatMinimumMs) || flatMinimumMs <= 0) {
|
|
43
|
+
throw new RangeError(`[agent-framework] inferenceBounds.flatMinimumMs must be a finite number above 0, got ${String(flatMinimumMs)}`);
|
|
44
|
+
}
|
|
45
|
+
return Object.freeze({ progressBounds: Object.freeze(progressBounds), flatMinimumMs });
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function allowanceMs(bounds: Readonly<ProgressBounds>, maxTokens: number | undefined): number {
|
|
49
|
+
return typeof maxTokens === 'number' && Number.isFinite(maxTokens) && maxTokens > 0
|
|
50
|
+
? Math.ceil(maxTokens / bounds.thinkTokensPerSecond) * 1000
|
|
51
|
+
: 0;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function setupMs(bounds: Readonly<ProgressBounds>): number {
|
|
55
|
+
return Math.max(bounds.beforeContentMs, bounds.betweenEventsMs);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function streamBounds(
|
|
59
|
+
bounds: InferenceBounds | null,
|
|
60
|
+
maxTokens: number | undefined,
|
|
61
|
+
): { progressBounds?: Readonly<ProgressBounds>; progressTimeoutMs?: number } {
|
|
62
|
+
if (bounds === null) return {};
|
|
63
|
+
const phases = bounds.progressBounds;
|
|
64
|
+
return {
|
|
65
|
+
progressBounds: phases,
|
|
66
|
+
progressTimeoutMs: Math.max(setupMs(phases) + allowanceMs(phases, maxTokens), bounds.flatMinimumMs),
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function wholeCallMs(bounds: InferenceBounds, maxTokens: number | undefined): number {
|
|
71
|
+
if (!(typeof maxTokens === 'number' && Number.isFinite(maxTokens) && maxTokens > 0)) return Math.max(setupMs(bounds.progressBounds), bounds.flatMinimumMs);
|
|
72
|
+
return setupMs(bounds.progressBounds) + allowanceMs(bounds.progressBounds, maxTokens);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// membrane 0.8.0 marks a deadline it reports as run out with this registered
|
|
76
|
+
// symbol (on a sent request, or the Anthropic SDK's timeout, which covers its
|
|
77
|
+
// connect timeout too) and exports no reader or marker for it. The one reader:
|
|
78
|
+
// the deadline class here and the transient-failure check of an exhausted
|
|
79
|
+
// turn's re-arm both use it.
|
|
80
|
+
const DEADLINE_EXPIRY = Symbol.for('membrane.deadlineExpiry');
|
|
81
|
+
|
|
82
|
+
export function isDeadlineExpiry(error: unknown): boolean {
|
|
83
|
+
return typeof error === 'object' && error !== null && (error as Record<symbol, unknown>)[DEADLINE_EXPIRY] === true;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Names the host setting for the bound a stream cut hit, read from the opening
|
|
88
|
+
* of membrane 0.8.0's message for it (its progress-bounds.ts and
|
|
89
|
+
* providers/utils.ts), so the reason an operator and the agent see names the
|
|
90
|
+
* host's setting for that bound, as the README's table maps them. A message it
|
|
91
|
+
* doesn't know is left as it is.
|
|
92
|
+
*/
|
|
93
|
+
export function withHostSetting(message: string): string {
|
|
94
|
+
const setting = message.includes('of a thinking block\'s start') ? 'progressBounds.thinkTokensPerSecond'
|
|
95
|
+
: message.startsWith('No content within ') ? 'progressBounds.beforeContentMs'
|
|
96
|
+
: message.startsWith('No provider event within ') ? 'progressBounds.betweenEventsMs'
|
|
97
|
+
: message.startsWith('No provider progress within ') ? 'flatMinimumMs'
|
|
98
|
+
: undefined;
|
|
99
|
+
return setting === undefined ? message : `${message} (the host's setting for this bound: \`inferenceBounds.${setting}\`)`;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export function deadlineCut(message: string | undefined): MembraneError {
|
|
103
|
+
const error = new MembraneError({
|
|
104
|
+
type: 'timeout',
|
|
105
|
+
retryable: false,
|
|
106
|
+
message: message === undefined ? 'The provider made no progress within the call\'s bound' : withHostSetting(message),
|
|
107
|
+
rawError: undefined,
|
|
108
|
+
});
|
|
109
|
+
Object.defineProperty(error, DEADLINE_EXPIRY, { value: true });
|
|
110
|
+
return error;
|
|
111
|
+
}
|
package/src/line-reader.ts
CHANGED
|
@@ -4,12 +4,26 @@ import { StringDecoder } from 'node:string_decoder';
|
|
|
4
4
|
const LF = 0x0a;
|
|
5
5
|
const CR = 0x0d;
|
|
6
6
|
|
|
7
|
-
export
|
|
7
|
+
export const MAX_LINE_CHARS = 100 * 1024 * 1024;
|
|
8
|
+
|
|
9
|
+
export interface LineHandlers {
|
|
10
|
+
line: (line: string) => void;
|
|
11
|
+
end?: () => void;
|
|
12
|
+
tooLong: (maxChars: number) => void;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function readLines(input: Readable, on: LineHandlers, maxChars = MAX_LINE_CHARS): () => void {
|
|
8
16
|
const decoder = new StringDecoder('utf8');
|
|
9
17
|
let partial = '';
|
|
10
18
|
let lineEndedAtCr = false;
|
|
11
19
|
let stopped = false;
|
|
12
20
|
|
|
21
|
+
const refuse = () => {
|
|
22
|
+
stop();
|
|
23
|
+
partial = '';
|
|
24
|
+
on.tooLong(maxChars);
|
|
25
|
+
};
|
|
26
|
+
|
|
13
27
|
const take = (text: string) => {
|
|
14
28
|
let from = 0;
|
|
15
29
|
if (lineEndedAtCr && text.length > 0) {
|
|
@@ -19,6 +33,7 @@ export function readLines(input: Readable, onLine: (line: string) => void, onEnd
|
|
|
19
33
|
for (let i = from; i < text.length && !stopped; i++) {
|
|
20
34
|
const code = text.charCodeAt(i);
|
|
21
35
|
if (code !== LF && code !== CR) continue;
|
|
36
|
+
if (partial.length + (i - from) > maxChars) return refuse();
|
|
22
37
|
const line = partial + text.slice(from, i);
|
|
23
38
|
partial = '';
|
|
24
39
|
if (code === CR) {
|
|
@@ -26,9 +41,11 @@ export function readLines(input: Readable, onLine: (line: string) => void, onEnd
|
|
|
26
41
|
else if (text.charCodeAt(i + 1) === LF) i++;
|
|
27
42
|
}
|
|
28
43
|
from = i + 1;
|
|
29
|
-
|
|
44
|
+
on.line(line);
|
|
30
45
|
}
|
|
31
|
-
if (
|
|
46
|
+
if (stopped) return;
|
|
47
|
+
if (partial.length + (text.length - from) > maxChars) return refuse();
|
|
48
|
+
partial += text.slice(from);
|
|
32
49
|
};
|
|
33
50
|
|
|
34
51
|
const onData = (chunk: Buffer | string) => take(decoder.write(chunk));
|
|
@@ -37,11 +54,11 @@ export function readLines(input: Readable, onLine: (line: string) => void, onEnd
|
|
|
37
54
|
if (!stopped && partial.length > 0) {
|
|
38
55
|
const last = partial;
|
|
39
56
|
partial = '';
|
|
40
|
-
|
|
57
|
+
on.line(last);
|
|
41
58
|
}
|
|
42
59
|
if (stopped) return;
|
|
43
60
|
stop();
|
|
44
|
-
|
|
61
|
+
on.end?.();
|
|
45
62
|
};
|
|
46
63
|
const stop = () => {
|
|
47
64
|
stopped = true;
|