@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
package/src/tune-out/tools.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import type { ContentBlock } from '@animalabs/membrane';
|
|
2
|
+
import { authorLine } from '../author-presentation.js';
|
|
3
|
+
import type { StoredMessage } from '@animalabs/context-manager';
|
|
1
4
|
/**
|
|
2
5
|
* The subconscious's tool surface (issue #77).
|
|
3
6
|
*
|
|
@@ -11,7 +14,7 @@
|
|
|
11
14
|
* never leak bare prose to the default locus.
|
|
12
15
|
*/
|
|
13
16
|
|
|
14
|
-
import type { ToolDefinition } from '../types/index.js';
|
|
17
|
+
import type { AgentConfig, ToolDefinition } from '../types/index.js';
|
|
15
18
|
|
|
16
19
|
export const SUBCONSCIOUS_TOOL_NAMES = [
|
|
17
20
|
'deliver_summary',
|
|
@@ -51,11 +54,13 @@ export const SUBCONSCIOUS_TOOLS: ToolDefinition[] = [
|
|
|
51
54
|
'End the tune-out on a channel now — use when something needs the ' +
|
|
52
55
|
'resident\'s full attention. The diverted backlog is delivered to ' +
|
|
53
56
|
'them (capped), and normal attention resumes. Add your own note in ' +
|
|
54
|
-
'`text`; it arrives alongside the backlog, in your voice.'
|
|
57
|
+
'`text`; it arrives alongside the backlog, in your voice. If you were ' +
|
|
58
|
+
'started to read one hold, you can end only that hold.',
|
|
55
59
|
inputSchema: {
|
|
56
60
|
type: 'object',
|
|
57
61
|
properties: {
|
|
58
|
-
channelId: { type: 'string' },
|
|
62
|
+
channelId: { type: 'string', description: 'A tuned-out channel, or a channel held by the all-except hold (which ends that hold, unless scope is "channel").' },
|
|
63
|
+
scope: { type: 'string', enum: ['channel', 'all-except'], description: '"all-except" ends the all-except hold outright; "channel" ends only the channel\'s own tune-out.' },
|
|
59
64
|
text: {
|
|
60
65
|
type: 'string',
|
|
61
66
|
description: 'Your accompanying note to the resident (optional).',
|
|
@@ -86,10 +91,14 @@ export const SUBCONSCIOUS_TOOLS: ToolDefinition[] = [
|
|
|
86
91
|
{
|
|
87
92
|
name: 'speak_in_channel',
|
|
88
93
|
description:
|
|
89
|
-
'Post a message into a tuned-out channel,
|
|
90
|
-
'as the resident)
|
|
91
|
-
'
|
|
92
|
-
'
|
|
94
|
+
'Post a message into a tuned-out channel, in your own words and ' +
|
|
95
|
+
'clearly as yourself (not as the resident); it goes out through the ' +
|
|
96
|
+
'resident\'s publish path, so the channel sees it under their account. ' +
|
|
97
|
+
'Use sparingly — e.g. to tell someone the resident is tuned out and ' +
|
|
98
|
+
'when to expect them. If you were started to read one hold, you can ' +
|
|
99
|
+
'post only into a channel that hold still holds, and a post made once ' +
|
|
100
|
+
'it has ended is refused; your report can say it instead. Disabled unless ' +
|
|
101
|
+
'the resident\'s configuration allows it.',
|
|
93
102
|
inputSchema: {
|
|
94
103
|
type: 'object',
|
|
95
104
|
properties: {
|
|
@@ -102,11 +111,53 @@ export const SUBCONSCIOUS_TOOLS: ToolDefinition[] = [
|
|
|
102
111
|
];
|
|
103
112
|
|
|
104
113
|
/**
|
|
105
|
-
* Configuration for the subconscious
|
|
114
|
+
* Configuration for the subconscious (FrameworkConfig.subconscious). Two
|
|
115
|
+
* shapes, by `reader`:
|
|
116
|
+
*
|
|
117
|
+
* - `'persistent'` (default): one persistent side-agent with an isolated
|
|
118
|
+
* slot and a windowed read-only view of the residents' timeline, as
|
|
119
|
+
* issue #77 built it. Its `systemPrompt` is a system prompt.
|
|
120
|
+
* - `'forks'`: a succession of forks of the resident (Dendrite). Each
|
|
121
|
+
* cadence tick, coalesced wake and cancel derives a short-lived fork at
|
|
122
|
+
* the resident's head — the resident's whole prefix, its refusals
|
|
123
|
+
* included, ending with it — hands it the traffic held since the last
|
|
124
|
+
* look as the first message after the fork point, and lets it report
|
|
125
|
+
* back as attributed mail. No agent persists between invocations;
|
|
126
|
+
* dispositions do, in the coordinator's state. Its `voice` is framing,
|
|
127
|
+
* not a system prompt, and residents are shown the reader's four tools
|
|
128
|
+
* in their tool block (refused at dispatch), so the fork's request is
|
|
129
|
+
* the resident's up to the fork point.
|
|
106
130
|
*/
|
|
107
|
-
export
|
|
131
|
+
export type SubconsciousConfig = PersistentReaderConfig | ForkReaderConfig;
|
|
132
|
+
|
|
133
|
+
interface SubconsciousConfigBase {
|
|
108
134
|
/** Master switch. */
|
|
109
135
|
enabled: boolean;
|
|
136
|
+
/**
|
|
137
|
+
* Allow speak_in_channel. Default false until the voice block has passed
|
|
138
|
+
* its canary round. A reader's post goes out through the resident's own
|
|
139
|
+
* publish path, so the channel sees it under the resident's account, in
|
|
140
|
+
* the reader's words; nothing in the request names the reader. With it
|
|
141
|
+
* off, a reader fork can post nowhere: every tool but its own is refused
|
|
142
|
+
* at dispatch (the block it is shown stays the resident's, for the
|
|
143
|
+
* shared prefix).
|
|
144
|
+
*/
|
|
145
|
+
allowChannelSpeech?: boolean;
|
|
146
|
+
/** The reader's model's context window, as AgentConfig.physicalWindowTokens,
|
|
147
|
+
* in place of the primary's. Left out or null, a reader on the primary's
|
|
148
|
+
* model has the primary's; on another model, the persistent reader has
|
|
149
|
+
* none and a fork has the primary's. */
|
|
150
|
+
physicalWindowTokens?: AgentConfig['physicalWindowTokens'];
|
|
151
|
+
/** The reader's model's thinking binding, as AgentConfig.thinkingBinding,
|
|
152
|
+
* in place of the primary's. Left out, null or false, a reader on the
|
|
153
|
+
* primary's model has the primary's; on another model, the persistent
|
|
154
|
+
* reader has none and a fork has the primary's. `{ binds: false }` gives
|
|
155
|
+
* a reader none. */
|
|
156
|
+
thinkingBinding?: AgentConfig['thinkingBinding'];
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export interface PersistentReaderConfig extends SubconsciousConfigBase {
|
|
160
|
+
reader?: 'persistent';
|
|
110
161
|
/**
|
|
111
162
|
* Registry + participant name. Default 'Subconscious' — following the
|
|
112
163
|
* Context Manager precedent: a title-case functional voice, not an
|
|
@@ -116,7 +167,8 @@ export interface SubconsciousConfig {
|
|
|
116
167
|
/** Model id; defaults to the primary agent's (same-model side-process).
|
|
117
168
|
* Another id, compared exactly, makes the subconscious leave out the
|
|
118
169
|
* fields that describe the primary's model, its window and its thinking
|
|
119
|
-
* binding (SUBCONSCIOUS_MODEL_FIELDS);
|
|
170
|
+
* binding (SUBCONSCIOUS_MODEL_FIELDS); `physicalWindowTokens` and
|
|
171
|
+
* `thinkingBinding` set its own. */
|
|
120
172
|
model?: string;
|
|
121
173
|
/**
|
|
122
174
|
* The voice/criteria mode block — recipe-side and co-authored with the
|
|
@@ -124,9 +176,110 @@ export interface SubconsciousConfig {
|
|
|
124
176
|
* person toward the resident. Canary before fleet use (issue #77).
|
|
125
177
|
*/
|
|
126
178
|
systemPrompt: string;
|
|
127
|
-
/** Allow speak_in_channel. Default false until the voice block has
|
|
128
|
-
* passed its canary round. */
|
|
129
|
-
allowChannelSpeech?: boolean;
|
|
130
179
|
/** WindowedPassthroughStrategy re-anchor fraction (default 0.5). */
|
|
131
180
|
reAnchorFraction?: number;
|
|
132
181
|
}
|
|
182
|
+
|
|
183
|
+
export interface ForkReaderConfig extends SubconsciousConfigBase {
|
|
184
|
+
reader: 'forks';
|
|
185
|
+
/**
|
|
186
|
+
* Whose weights run the fork. Required, never defaulted: a fork holds the
|
|
187
|
+
* resident's whole prefix, and which model reads it is the one fact the
|
|
188
|
+
* resident's consent names. The resident's own model is the copy; another
|
|
189
|
+
* model is allowed when stated here. `create` refuses the omission.
|
|
190
|
+
*/
|
|
191
|
+
model: string;
|
|
192
|
+
/**
|
|
193
|
+
* The reader's voice block — report-shaped, second person toward the
|
|
194
|
+
* resident, co-authored with them. Delivered as the first message after
|
|
195
|
+
* the fork point, with the notice, the standing dispositions and the held
|
|
196
|
+
* traffic; never a system prompt.
|
|
197
|
+
*/
|
|
198
|
+
voice: string;
|
|
199
|
+
/**
|
|
200
|
+
* A fresh context strategy instance for each fork, same class and
|
|
201
|
+
* configuration as the resident's, so the fork reuses the resident's fold
|
|
202
|
+
* state and rendering. Required unless the resident runs a passthrough
|
|
203
|
+
* strategy.
|
|
204
|
+
*/
|
|
205
|
+
strategyFactory?: () => import('@animalabs/context-manager').ContextStrategy;
|
|
206
|
+
/** Idle timeout for one reader fork (default 10 minutes). */
|
|
207
|
+
forkIdleTimeoutMs?: number;
|
|
208
|
+
/**
|
|
209
|
+
* How a reader fork is prompted: the content of the one message it
|
|
210
|
+
* receives after the fork point. Default `defaultReaderFraming`. The
|
|
211
|
+
* framework stores whatever this returns as a user message with
|
|
212
|
+
* `metadata.kind = 'tune-out-reader-framing'`; the notice, voice,
|
|
213
|
+
* dispositions and held traffic are in the context, and nothing else
|
|
214
|
+
* about the fork's prompt is fixed by the path.
|
|
215
|
+
*/
|
|
216
|
+
framing?: (context: ReaderFramingContext) => ContentBlock[];
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/** What a reader fork's framing is built from. */
|
|
220
|
+
export interface ReaderFramingContext {
|
|
221
|
+
/** The resident the fork was derived from. */
|
|
222
|
+
resident: string;
|
|
223
|
+
/** The fork's own name. */
|
|
224
|
+
fork: string;
|
|
225
|
+
/** `ForkReaderConfig.voice`. */
|
|
226
|
+
voice: string;
|
|
227
|
+
channelId: string;
|
|
228
|
+
epochId: string;
|
|
229
|
+
trigger: 'cadence' | 'wake' | 'cancel';
|
|
230
|
+
/** The coordinator's notice for this invocation (`[Tune-out wake: …]` etc.). */
|
|
231
|
+
notice: string;
|
|
232
|
+
/** Standing dispositions recorded on the epoch, or null. */
|
|
233
|
+
dispositions: string | null;
|
|
234
|
+
/**
|
|
235
|
+
* The traffic held since the last look, oldest first; empty when nothing
|
|
236
|
+
* arrived. It includes the messages still queued behind the resident's
|
|
237
|
+
* turn (admitted, not yet stored), last: their ids are held handles and
|
|
238
|
+
* their sequences provisional, as a module's held rows are.
|
|
239
|
+
*/
|
|
240
|
+
backlog: StoredMessage[];
|
|
241
|
+
/** How many of `backlog`'s last entries are still queued behind the resident's turn. Absent is none. */
|
|
242
|
+
queuedCount?: number;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* The default reader-fork framing: a header naming the channel, epoch and
|
|
247
|
+
* trigger; the notice; the voice block; standing dispositions if any; then
|
|
248
|
+
* the held traffic as `author: text` lines with non-text blocks (images,
|
|
249
|
+
* documents) preserved in order as the source supplied them.
|
|
250
|
+
*/
|
|
251
|
+
export function defaultReaderFraming(ctx: ReaderFramingContext): ContentBlock[] {
|
|
252
|
+
const header =
|
|
253
|
+
`[Tune-out reader — ${ctx.channelId}, epoch ${ctx.epochId.slice(0, 8)}, ${ctx.trigger}]\n` +
|
|
254
|
+
`${ctx.notice}\n\n${ctx.voice}` +
|
|
255
|
+
(ctx.dispositions ? `\n\n[Standing dispositions]\n${ctx.dispositions}` : '');
|
|
256
|
+
const content: ContentBlock[] = [{ type: 'text', text: header }];
|
|
257
|
+
if (ctx.backlog.length === 0) {
|
|
258
|
+
content.push({ type: 'text', text: '\n[Nothing new has been held since your last look.]' });
|
|
259
|
+
return content;
|
|
260
|
+
}
|
|
261
|
+
const n = ctx.backlog.length;
|
|
262
|
+
const q = Math.min(Math.max(0, Math.floor(ctx.queuedCount ?? 0)), n);
|
|
263
|
+
const lines: string[] = [`\n[Held in ${ctx.channelId} since your last look: ${n} message${n === 1 ? '' : 's'}]`];
|
|
264
|
+
const flush = () => {
|
|
265
|
+
if (lines.length > 0) content.push({ type: 'text', text: lines.splice(0).join('\n') });
|
|
266
|
+
};
|
|
267
|
+
const which = q === n ? (q === 1 ? 'It' : 'They') : (q === 1 ? 'The last one' : `The last ${q}`);
|
|
268
|
+
const queuedNote = `[${which} arrived while ${ctx.resident}'s turn was running, so ${q === 1 ? 'it is' : 'they are'} still queued, not yet stored.]`;
|
|
269
|
+
for (const [i, message] of ctx.backlog.entries()) {
|
|
270
|
+
if (q > 0 && i === n - q) lines.push(queuedNote);
|
|
271
|
+
const author = (message.metadata as { author?: { name?: string } } | undefined)?.author?.name ?? message.participant;
|
|
272
|
+
const text = message.content
|
|
273
|
+
.filter((b): b is Extract<ContentBlock, { type: 'text' }> => b.type === 'text')
|
|
274
|
+
.map((b) => b.text)
|
|
275
|
+
.join('\n');
|
|
276
|
+
lines.push(authorLine(author, text));
|
|
277
|
+
const media = message.content.filter((b) => b.type !== 'text');
|
|
278
|
+
if (media.length > 0) {
|
|
279
|
+
flush();
|
|
280
|
+
content.push(...media);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
flush();
|
|
284
|
+
return content;
|
|
285
|
+
}
|
package/src/types/agent.ts
CHANGED
|
@@ -340,6 +340,34 @@ export interface AgentConfig {
|
|
|
340
340
|
*/
|
|
341
341
|
proseRouting?: 'locus' | 'explicit' | 'hybrid' | 'disabled';
|
|
342
342
|
|
|
343
|
+
/**
|
|
344
|
+
* Stored participants that are this agent's own voice, in addition to its
|
|
345
|
+
* name: their turns are presented to the model as this agent's assistant
|
|
346
|
+
* turns. A derived agent sets this to the agent it inherited from, so the
|
|
347
|
+
* parent's turns read as its own history. Model-facing role assignment
|
|
348
|
+
* only, applied at request assembly — stored authorship is never rewritten.
|
|
349
|
+
*/
|
|
350
|
+
selfParticipants?: string[];
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* The participant name this agent's own turns are presented under at
|
|
354
|
+
* request assembly. Defaults to `name`. A derived agent that should share
|
|
355
|
+
* its parent's provider prefix sets the parent's name, so the two
|
|
356
|
+
* requests are identical in formatters that render participant names as
|
|
357
|
+
* well as in those that only assign roles. New turns are still STORED
|
|
358
|
+
* under the agent's own name.
|
|
359
|
+
*/
|
|
360
|
+
presentAs?: string;
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* Tools refused when this agent calls them, although they stay advertised.
|
|
364
|
+
* Restricts a derived agent without changing the tool block at the front
|
|
365
|
+
* of its request — removing a tool from `allowedTools` instead would
|
|
366
|
+
* change those bytes and forfeit the provider prefix it shares with its
|
|
367
|
+
* parent.
|
|
368
|
+
*/
|
|
369
|
+
denyToolsAtDispatch?: string[];
|
|
370
|
+
|
|
343
371
|
/**
|
|
344
372
|
* Fail-closed containment for a text response whose entire visible prose is
|
|
345
373
|
* an invocation-shaped wrapper naming a tool registered on that exact turn.
|
|
@@ -511,7 +539,8 @@ export interface InferenceOptions {
|
|
|
511
539
|
* `supportsInjectedMessages` is what the stream declared with the round's
|
|
512
540
|
* tool calls (membrane's `ToolContext.supportsInjectedMessages`): whether it
|
|
513
541
|
* can carry messages injected with that round's results. It's absent when
|
|
514
|
-
* the stream doesn't say, as
|
|
542
|
+
* the stream doesn't say, which reads as a stream that can't, and when there
|
|
543
|
+
* is no stream, as for the direct API.
|
|
515
544
|
*/
|
|
516
545
|
export type AgentState =
|
|
517
546
|
| { status: 'idle' }
|
package/src/types/framework.ts
CHANGED
|
@@ -6,6 +6,7 @@ import type { ProcessEvent } from './events.js';
|
|
|
6
6
|
import type { McplServerConfig, InferenceRoutingPolicy } from '../mcpl/types.js';
|
|
7
7
|
import type { ConversationRouterConfig } from '../mcpl/conversation-router.js';
|
|
8
8
|
import type { GateOptions } from '../gate/types.js';
|
|
9
|
+
import type { InferenceBoundsOptions } from '../inference-bounds.js';
|
|
9
10
|
|
|
10
11
|
// Re-export trace types
|
|
11
12
|
export type { TraceEvent, TraceEventListener } from './trace.js';
|
|
@@ -116,6 +117,48 @@ export type ProviderHoldHook = (
|
|
|
116
117
|
) => ProviderHold | undefined;
|
|
117
118
|
|
|
118
119
|
export interface FrameworkConfig {
|
|
120
|
+
/**
|
|
121
|
+
* Dendrite agent lifecycle options.
|
|
122
|
+
*/
|
|
123
|
+
dendrite?: {
|
|
124
|
+
/**
|
|
125
|
+
* When an agent ends while task work it spawned is still running, the
|
|
126
|
+
* orphan is always told. With this on (the default), the first agent
|
|
127
|
+
* that could adopt it — its nearest live ancestor, else the primary —
|
|
128
|
+
* also receives a factual notice, which is what makes the reparenting
|
|
129
|
+
* offer visible to a model. Turn it off when the host makes adoption
|
|
130
|
+
* decisions itself from the `dendrite:agent-orphaned` trace.
|
|
131
|
+
*/
|
|
132
|
+
announceOrphans?: boolean;
|
|
133
|
+
/**
|
|
134
|
+
* By default every derivation tells the agent it was made from, in one
|
|
135
|
+
* host-framed line. After this many derivations of one kind from one
|
|
136
|
+
* agent, that agent hears a count line every so many instead of a line
|
|
137
|
+
* per fork, so a reader loop that forks a hundred times a session doesn't
|
|
138
|
+
* fill its source's context with stamps; every derivation still emits its
|
|
139
|
+
* `dendrite:agent-created` trace. Default 10; 0 never collapses.
|
|
140
|
+
*/
|
|
141
|
+
collapseDerivationNoticesAfter?: number;
|
|
142
|
+
/**
|
|
143
|
+
* A host's say over each derivation, ephemeral spawn and resume, before the
|
|
144
|
+
* agent or its new incarnation exists, given the spec of what is about to
|
|
145
|
+
* exist (name, kind, spawner, route, inheritance, metadata). A shared
|
|
146
|
+
* derivation's record then adds the child's branch, and names the
|
|
147
|
+
* parent's namespace as its own. Return nothing to allow, or `{ refuse }`
|
|
148
|
+
* with the reason, which the caller gets as the error. The framework
|
|
149
|
+
* attaches no meaning to the decision: consent, budget, hours, a mind's
|
|
150
|
+
* standing instruction, a use not yet imagined are all the host's.
|
|
151
|
+
*/
|
|
152
|
+
beforeDerive?: (spec: import('../dendrite/types.js').AgentSpec) => void | { refuse: string };
|
|
153
|
+
/**
|
|
154
|
+
* What an agent is told when something is derived from it. Given the
|
|
155
|
+
* record, the agent it was derived from and the framework's default line,
|
|
156
|
+
* return the text to add, or null to add nothing. Absent, the default
|
|
157
|
+
* line is used. The `dendrite:agent-created` trace is emitted either way.
|
|
158
|
+
*/
|
|
159
|
+
derivationNotice?: (record: import('../dendrite/types.js').AgentRecord, from: string, defaultText: string) => string | null;
|
|
160
|
+
};
|
|
161
|
+
|
|
119
162
|
/**
|
|
120
163
|
* The subconscious resident (issue #77): a persistent side-agent that
|
|
121
164
|
* receives diverted traffic from tuned-out channels and reports to the
|
|
@@ -123,6 +166,46 @@ export interface FrameworkConfig {
|
|
|
123
166
|
*/
|
|
124
167
|
subconscious?: import('../tune-out/tools.js').SubconsciousConfig;
|
|
125
168
|
|
|
169
|
+
/**
|
|
170
|
+
* Tune-out without a reader (issue #232 §3.3). The hold machinery always
|
|
171
|
+
* runs when MCPL channels exist, so a persisted epoch is resumed and
|
|
172
|
+
* released whatever the configuration; this switch only decides whether
|
|
173
|
+
* residents are offered `tune_out` when no reader is configured. Without
|
|
174
|
+
* a reader a tune-out is a silent hold: no cadence, no wake budget,
|
|
175
|
+
* mentions are acknowledged, the backlog is released on cancel.
|
|
176
|
+
*/
|
|
177
|
+
tuneOut?: {
|
|
178
|
+
/** Offer `tune_out` without a subconscious. Default: only with one. */
|
|
179
|
+
enabled?: boolean;
|
|
180
|
+
/**
|
|
181
|
+
* Announce the all-except scope in the excepted channel: one bracketed
|
|
182
|
+
* line under the resident's name when it opens (who, for how long) and
|
|
183
|
+
* one when it closes. Default true — a hold the room cannot see is the
|
|
184
|
+
* failure the scope exists to prevent. `false` keeps both quiet.
|
|
185
|
+
*/
|
|
186
|
+
announce?: boolean;
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Where a turn's plain prose lands when nothing names a destination
|
|
191
|
+
* (architecture channel, 2026-10-06: the §3 carve-out accepted by the
|
|
192
|
+
* residents).
|
|
193
|
+
*
|
|
194
|
+
* - `'delivery'` (default): the locus comes only from the delivery that
|
|
195
|
+
* woke the turn, a conversation fork's home channel, or a channel the
|
|
196
|
+
* agent opened during the turn. Incoming traffic never moves it — not
|
|
197
|
+
* between turns, not mid-turn. A turn with no such locus keeps its
|
|
198
|
+
* prose private, and says so in a `[routing]` notice.
|
|
199
|
+
* - `'latest-inbound'`: the previous rule. The most recent delivered
|
|
200
|
+
* inbound message is a global fallback locus for turns without one, and
|
|
201
|
+
* an addressed message injected mid-turn re-pins the turn's locus.
|
|
202
|
+
*
|
|
203
|
+
* Held traffic never moves the locus under either rule.
|
|
204
|
+
*/
|
|
205
|
+
speech?: {
|
|
206
|
+
locus?: 'delivery' | 'latest-inbound';
|
|
207
|
+
};
|
|
208
|
+
|
|
126
209
|
/**
|
|
127
210
|
* IANA zone of the agent's wall clock: it renders wall-clock times for the
|
|
128
211
|
* agent, and it sets where clock rows' boundaries fall (AgentConfig.clock,
|
|
@@ -160,6 +243,26 @@ export interface FrameworkConfig {
|
|
|
160
243
|
*/
|
|
161
244
|
deferredWritesPath?: string;
|
|
162
245
|
|
|
246
|
+
/**
|
|
247
|
+
* The Dendrite agent registry's recovery file: its live records and the
|
|
248
|
+
* results it holds for delivery, written atomically and fsynced, and read
|
|
249
|
+
* first at boot. Defaults to `<storePath>/recovery/dendrite-registry.json`.
|
|
250
|
+
* Outside the chronicle for the reason `hostModePath` is: a branch switch
|
|
251
|
+
* or historical rollback would otherwise take back held results. Set
|
|
252
|
+
* explicitly when providing an app-owned `store` without `storePath`;
|
|
253
|
+
* without either, the registry persists only in the branch-local store,
|
|
254
|
+
* with a warning at create.
|
|
255
|
+
*/
|
|
256
|
+
agentRegistryPath?: string;
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* The registry's ended-record log recovery file (its newest records, as
|
|
260
|
+
* many as a restart reads). Defaults to
|
|
261
|
+
* `<storePath>/recovery/dendrite-ended.json`; same rationale and fallback
|
|
262
|
+
* as `agentRegistryPath`.
|
|
263
|
+
*/
|
|
264
|
+
agentRegistryEndedPath?: string;
|
|
265
|
+
|
|
163
266
|
/** Emoji used for branch-awareness markers (default: 💤). */
|
|
164
267
|
discordAwarenessEmoji?: string;
|
|
165
268
|
|
|
@@ -197,6 +300,30 @@ export interface FrameworkConfig {
|
|
|
197
300
|
/** Custom error policy */
|
|
198
301
|
errorPolicy?: ErrorPolicy;
|
|
199
302
|
|
|
303
|
+
/**
|
|
304
|
+
* How long a model call may go without progress before it is cut. A
|
|
305
|
+
* resident's streamed call, and an MCPL server's, gets membrane's progress
|
|
306
|
+
* bounds where its adapter's stream reports content (Anthropic, Bedrock),
|
|
307
|
+
* and on every adapter a flat bound from its last provider event: the
|
|
308
|
+
* larger of a setup term (the larger of `beforeContentMs` and
|
|
309
|
+
* `betweenEventsMs`) plus the request's `maxTokens` at
|
|
310
|
+
* `thinkTokensPerSecond`, and `flatMinimumMs`. An auxiliary call
|
|
311
|
+
* (compression, the reply judge) and an MCPL server's non-streamed call get
|
|
312
|
+
* a bound on the whole call from its send, the setup term plus that
|
|
313
|
+
* allowance (the larger of the setup term and `flatMinimumMs` when it
|
|
314
|
+
* names no `maxTokens`), unless its
|
|
315
|
+
* caller passed a `progressTimeoutMs` of its own; where an adapter's
|
|
316
|
+
* `complete()` streams under the hood, membrane renews it at each parsed
|
|
317
|
+
* provider event. A call a bound cuts fails
|
|
318
|
+
* with type `timeout`, and on an agent's turn DefaultErrorPolicy gives each
|
|
319
|
+
* such call one fresh call, except in a turn that re-arms an exhausted one,
|
|
320
|
+
* where `exhaustedTurnRearmMs` says what a cut does. One setting covers
|
|
321
|
+
* every agent. `false` sets
|
|
322
|
+
* none of these bounds. Default: membrane's PROGRESS_BOUNDS and
|
|
323
|
+
* FLAT_MINIMUM_MS.
|
|
324
|
+
*/
|
|
325
|
+
inferenceBounds?: false | InferenceBoundsOptions;
|
|
326
|
+
|
|
200
327
|
/**
|
|
201
328
|
* Host verdict on a failed inference: "this cannot succeed until later".
|
|
202
329
|
* Consulted before the error policy and before the built-in
|
|
@@ -235,20 +362,32 @@ export interface FrameworkConfig {
|
|
|
235
362
|
* channel isn't sent again: one whose turn was woken for a channel id that
|
|
236
363
|
* names no one registered channel and never found one, and whose request
|
|
237
364
|
* names none of its own. A failure that states a wait is held for that wait
|
|
238
|
-
* first, as any turn's is, and a
|
|
239
|
-
*
|
|
240
|
-
*
|
|
241
|
-
*
|
|
242
|
-
* of the re-armed turn's rounds has run
|
|
243
|
-
* failure gets no retries from the error
|
|
244
|
-
* failure the next delay follows, until the
|
|
245
|
-
* any other kind ends the re-arming.
|
|
246
|
-
*
|
|
247
|
-
*
|
|
248
|
-
*
|
|
249
|
-
*
|
|
250
|
-
*
|
|
251
|
-
*
|
|
365
|
+
* first, as any turn's is, and a turn that ends on a deadline, whether the
|
|
366
|
+
* host set it or membrane marks one as run out (the runtime's or undici's
|
|
367
|
+
* on a sent request, the Anthropic adapter's watchdogs, or the Anthropic
|
|
368
|
+
* SDK's timeout, which covers its connect timeout too), isn't re-armed,
|
|
369
|
+
* save the exception below. Until one of the re-armed turn's rounds has run
|
|
370
|
+
* or had its tool calls cut off, its failure gets no retries from the error
|
|
371
|
+
* policy: on another transient failure the next delay follows, until the
|
|
372
|
+
* list runs out, and a failure of any other kind ends the re-arming. The
|
|
373
|
+
* one exception is a re-armed turn's send that fails with a deadline marked
|
|
374
|
+
* as run out, membrane's or a bound the host sets on the call
|
|
375
|
+
* (`inferenceBounds`), as when a stream stalls. Such a cut counts as a
|
|
376
|
+
* transient failure, so a turn that ends on it is re-armed as for a
|
|
377
|
+
* transient failure, unless it follows another such cut with no transient
|
|
378
|
+
* failure, answered round or message said to the agent between them: two
|
|
379
|
+
* such cuts in a row end the re-arming, and the second gets no retry from
|
|
380
|
+
* the error policy, even in a re-armed turn that got partway. A transient
|
|
381
|
+
* failure or an answered round in any of the agent's turns clears an
|
|
382
|
+
* earlier cut, and so does a message said to the agent, or the agent's end.
|
|
383
|
+
* Every such cut counts, including one the error policy retries or a host's
|
|
384
|
+
* provider hold takes. A wake for the agent while it waits starts its turn
|
|
385
|
+
* at once, with the re-armed trigger merged in. That turn is the
|
|
386
|
+
* wake's: the error policy retries its failures. A completed turn starts the
|
|
387
|
+
* count again, and so does a message said to the agent: not a system message
|
|
388
|
+
* or notice; not an edit, reaction or deletion, unless an edit is the first
|
|
389
|
+
* version of a message the agent sees; not a message its own view leaves
|
|
390
|
+
* out, such as one a tune-out holds from it; and not the agent's own
|
|
252
391
|
* schedule, the heartbeat's tick or reminder, a self-wake timer it set, or a
|
|
253
392
|
* script's wake. A notice a module or an MCPL server stores as a message
|
|
254
393
|
* counts. A message in the residents' shared slot counts for each resident
|
|
@@ -456,7 +595,10 @@ export interface InferencePolicy {
|
|
|
456
595
|
*/
|
|
457
596
|
export interface ErrorPolicy {
|
|
458
597
|
/**
|
|
459
|
-
* Handle an inference error.
|
|
598
|
+
* Handle an inference error. `attempt` is how many times the turn's
|
|
599
|
+
* request has failed before this one; for a deadline expiry (a progress
|
|
600
|
+
* bound's cut, or one membrane marks), how many times that call's request
|
|
601
|
+
* has expired in a row before this one.
|
|
460
602
|
*/
|
|
461
603
|
onInferenceError(
|
|
462
604
|
error: Error,
|
package/src/types/module.ts
CHANGED
|
@@ -120,7 +120,10 @@ export interface Module {
|
|
|
120
120
|
|
|
121
121
|
/**
|
|
122
122
|
* Start the module.
|
|
123
|
-
* Called when the
|
|
123
|
+
* Called when the module is added: create() adds the modules it is given,
|
|
124
|
+
* so for those it runs before create() returns. Its own removal and the
|
|
125
|
+
* framework's stop() wait for it to settle before the module's stop()
|
|
126
|
+
* runs, so it mustn't await either.
|
|
124
127
|
*/
|
|
125
128
|
start(ctx: ModuleContext): Promise<void>;
|
|
126
129
|
|
|
@@ -132,6 +135,8 @@ export interface Module {
|
|
|
132
135
|
* a host wires up after create() returns, such as a WorkspaceModule's
|
|
133
136
|
* initStore(). A stop() that saves state can therefore run before that
|
|
134
137
|
* wiring, and shouldn't then save over state it was handed and hasn't used.
|
|
138
|
+
* It isn't called before the module's start() has settled, so it can end
|
|
139
|
+
* what start() set up.
|
|
135
140
|
*/
|
|
136
141
|
stop(): Promise<void>;
|
|
137
142
|
|
|
@@ -259,6 +264,41 @@ export interface AgentSettingsExtension {
|
|
|
259
264
|
|
|
260
265
|
/**
|
|
261
266
|
* Context provided to modules for interacting with the framework.
|
|
267
|
+
*
|
|
268
|
+
* Once the module's `stop()` has completed, through `removeModule()` or the
|
|
269
|
+
* framework's `stop()`, its context has no effect on the framework:
|
|
270
|
+
* - each push is dropped with a log line, as a push after the framework's
|
|
271
|
+
* `stop()` is;
|
|
272
|
+
* - `setState`, `registerLogState`, `appendToLog`, `editLogItem`,
|
|
273
|
+
* `editMessage`, `removeMessage`, `notifyOps`, `registerSpeechHandler` and
|
|
274
|
+
* `unregisterSpeechHandler` do nothing; `addMessage` returns `''` and adds
|
|
275
|
+
* nothing; `callTool` resolves with an error result and runs nothing;
|
|
276
|
+
* `getModule` returns `null`; and `onTrace` subscribes nothing, while the
|
|
277
|
+
* subscriptions made through the context end. None of these throws, and
|
|
278
|
+
* the first such call of each method logs one line naming the module and
|
|
279
|
+
* the method;
|
|
280
|
+
* - `findMessageByExternalId` answers without updating its mapping, and logs
|
|
281
|
+
* nothing.
|
|
282
|
+
*
|
|
283
|
+
* Its reads (`getState`, `getLog`, `getLogLength`, `getMessage`,
|
|
284
|
+
* `queryMessages`, `getAgents`, `getActiveTools`, `findMessageByExternalId`)
|
|
285
|
+
* answer, and fail, as before; one that meets a held write may record that
|
|
286
|
+
* the write has landed, as any reader's does. The framework calls none of the
|
|
287
|
+
* module's `onProcess`, `onAgentSpeech` and `onToolBatchComplete` again; it
|
|
288
|
+
* drops whole a response of its that hadn't been applied by then, with a line
|
|
289
|
+
* naming what the response asked for if it asked for anything; and it leaves
|
|
290
|
+
* its `gatherContext` answer out of a turn whose answers it hadn't merged by
|
|
291
|
+
* then, with a line if it held injections. After `removeModule()`, the result
|
|
292
|
+
* its `handleToolCall` returns for a call it was running when its `stop()`
|
|
293
|
+
* completed is still delivered while the framework runs. The framework's
|
|
294
|
+
* `stop()` closes its event queue before it stops any module, so through the
|
|
295
|
+
* framework's `stop()` that result is dropped, with the line any tool result
|
|
296
|
+
* arriving after the framework's `stop()` gets. A result it pushes through
|
|
297
|
+
* its context after its `stop()` completes is dropped, as its other pushes
|
|
298
|
+
* are. A module whose `stop()` fails stays registered, and its context keeps
|
|
299
|
+
* working.
|
|
300
|
+
* References the module already holds, to other modules or to its own
|
|
301
|
+
* timers, stay as they are.
|
|
262
302
|
*/
|
|
263
303
|
export interface ModuleContext {
|
|
264
304
|
/**
|
|
@@ -366,8 +406,7 @@ export interface ModuleContext {
|
|
|
366
406
|
* and a tool round moves when it injects messages into a stream that
|
|
367
407
|
* declares it carries them. So one that waited for its target's end (a
|
|
368
408
|
* run's end, or a fork's last turn), one stored while it was idle and
|
|
369
|
-
* never sent, one stored at a tool round that ended the turn
|
|
370
|
-
* injected into a stream that didn't declare it carries injections go to
|
|
409
|
+
* never sent, and one stored at a tool round that ended the turn go to
|
|
371
410
|
* the primary. The framework's own
|
|
372
411
|
* writes for a run or a fork are unchanged: they stay in its store,
|
|
373
412
|
* unless they're notices the primary already takes in their place (a
|