@animalabs/agent-framework 0.26.2 → 0.27.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 +56 -6
- package/dist/src/agent.d.ts +263 -22
- package/dist/src/agent.d.ts.map +1 -1
- package/dist/src/agent.js +531 -54
- package/dist/src/agent.js.map +1 -1
- package/dist/src/api/server.d.ts.map +1 -1
- package/dist/src/api/server.js +1 -2
- package/dist/src/api/server.js.map +1 -1
- package/dist/src/auto-continue.d.ts +33 -0
- package/dist/src/auto-continue.d.ts.map +1 -0
- package/dist/src/auto-continue.js +35 -0
- package/dist/src/auto-continue.js.map +1 -0
- package/dist/src/code-execution/isolate/scripts.d.ts +11 -0
- package/dist/src/code-execution/isolate/scripts.d.ts.map +1 -1
- package/dist/src/code-execution/isolate/scripts.js +5 -1
- package/dist/src/code-execution/isolate/scripts.js.map +1 -1
- package/dist/src/framework.d.ts +759 -129
- package/dist/src/framework.d.ts.map +1 -1
- package/dist/src/framework.js +3482 -928
- package/dist/src/framework.js.map +1 -1
- package/dist/src/gate/event-gate.d.ts +309 -46
- package/dist/src/gate/event-gate.d.ts.map +1 -1
- package/dist/src/gate/event-gate.js +2117 -395
- package/dist/src/gate/event-gate.js.map +1 -1
- package/dist/src/gate/index.d.ts +2 -2
- package/dist/src/gate/index.d.ts.map +1 -1
- package/dist/src/gate/index.js +1 -1
- package/dist/src/gate/index.js.map +1 -1
- package/dist/src/gate/recipe-reconcile.d.ts +103 -0
- package/dist/src/gate/recipe-reconcile.d.ts.map +1 -0
- package/dist/src/gate/recipe-reconcile.js +325 -0
- package/dist/src/gate/recipe-reconcile.js.map +1 -0
- package/dist/src/gate/types.d.ts +68 -8
- package/dist/src/gate/types.d.ts.map +1 -1
- package/dist/src/index.d.ts +6 -4
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +5 -2
- package/dist/src/index.js.map +1 -1
- package/dist/src/mcpl/channel-registry.d.ts +433 -113
- package/dist/src/mcpl/channel-registry.d.ts.map +1 -1
- package/dist/src/mcpl/channel-registry.js +1152 -434
- package/dist/src/mcpl/channel-registry.js.map +1 -1
- package/dist/src/mcpl/conversation-router.d.ts +16 -8
- package/dist/src/mcpl/conversation-router.d.ts.map +1 -1
- package/dist/src/mcpl/conversation-router.js +29 -15
- package/dist/src/mcpl/conversation-router.js.map +1 -1
- package/dist/src/mcpl/convert-block.d.ts.map +1 -1
- package/dist/src/mcpl/convert-block.js +14 -2
- package/dist/src/mcpl/convert-block.js.map +1 -1
- package/dist/src/mcpl/hook-orchestrator.d.ts +7 -1
- package/dist/src/mcpl/hook-orchestrator.d.ts.map +1 -1
- package/dist/src/mcpl/hook-orchestrator.js +32 -7
- package/dist/src/mcpl/hook-orchestrator.js.map +1 -1
- package/dist/src/mcpl/index.d.ts +5 -1
- package/dist/src/mcpl/index.d.ts.map +1 -1
- package/dist/src/mcpl/index.js +4 -0
- package/dist/src/mcpl/index.js.map +1 -1
- package/dist/src/mcpl/inference-router.d.ts +14 -2
- package/dist/src/mcpl/inference-router.d.ts.map +1 -1
- package/dist/src/mcpl/inference-router.js +73 -28
- package/dist/src/mcpl/inference-router.js.map +1 -1
- package/dist/src/mcpl/prose-grammar.d.ts +4 -0
- package/dist/src/mcpl/prose-grammar.d.ts.map +1 -1
- package/dist/src/mcpl/prose-grammar.js +6 -0
- package/dist/src/mcpl/prose-grammar.js.map +1 -1
- package/dist/src/mcpl/protocol-family.d.ts.map +1 -1
- package/dist/src/mcpl/protocol-family.js +3 -0
- package/dist/src/mcpl/protocol-family.js.map +1 -1
- package/dist/src/mcpl/push-coalescer.d.ts +20 -2
- package/dist/src/mcpl/push-coalescer.d.ts.map +1 -1
- package/dist/src/mcpl/push-coalescer.js +33 -11
- package/dist/src/mcpl/push-coalescer.js.map +1 -1
- package/dist/src/mcpl/reference-fetcher.d.ts +63 -42
- package/dist/src/mcpl/reference-fetcher.d.ts.map +1 -1
- package/dist/src/mcpl/reference-fetcher.js +177 -145
- package/dist/src/mcpl/reference-fetcher.js.map +1 -1
- package/dist/src/mcpl/references.d.ts +380 -46
- package/dist/src/mcpl/references.d.ts.map +1 -1
- package/dist/src/mcpl/references.js +736 -77
- package/dist/src/mcpl/references.js.map +1 -1
- package/dist/src/mcpl/server-connection.d.ts +82 -3
- package/dist/src/mcpl/server-connection.d.ts.map +1 -1
- package/dist/src/mcpl/server-connection.js +132 -7
- package/dist/src/mcpl/server-connection.js.map +1 -1
- package/dist/src/mcpl/tool-lifecycle.d.ts +11 -0
- package/dist/src/mcpl/tool-lifecycle.d.ts.map +1 -1
- package/dist/src/mcpl/tool-lifecycle.js +21 -0
- package/dist/src/mcpl/tool-lifecycle.js.map +1 -1
- package/dist/src/mcpl/tool-presence.d.ts +259 -0
- package/dist/src/mcpl/tool-presence.d.ts.map +1 -0
- package/dist/src/mcpl/tool-presence.js +628 -0
- package/dist/src/mcpl/tool-presence.js.map +1 -0
- package/dist/src/mcpl/tool-result-normalize.d.ts +9 -3
- package/dist/src/mcpl/tool-result-normalize.d.ts.map +1 -1
- package/dist/src/mcpl/tool-result-normalize.js +14 -6
- package/dist/src/mcpl/tool-result-normalize.js.map +1 -1
- package/dist/src/mcpl/tool-signature.d.ts +39 -0
- package/dist/src/mcpl/tool-signature.d.ts.map +1 -0
- package/dist/src/mcpl/tool-signature.js +292 -0
- package/dist/src/mcpl/tool-signature.js.map +1 -0
- package/dist/src/mcpl/toolset-host.d.ts +200 -0
- package/dist/src/mcpl/toolset-host.d.ts.map +1 -0
- package/dist/src/mcpl/toolset-host.js +621 -0
- package/dist/src/mcpl/toolset-host.js.map +1 -0
- package/dist/src/mcpl/toolsets-module.d.ts +80 -0
- package/dist/src/mcpl/toolsets-module.d.ts.map +1 -0
- package/dist/src/mcpl/toolsets-module.js +431 -0
- package/dist/src/mcpl/toolsets-module.js.map +1 -0
- package/dist/src/mcpl/transport.d.ts +2 -2
- package/dist/src/mcpl/transport.d.ts.map +1 -1
- package/dist/src/mcpl/transport.js +3 -2
- package/dist/src/mcpl/transport.js.map +1 -1
- package/dist/src/mcpl/types.d.ts +21 -1
- package/dist/src/mcpl/types.d.ts.map +1 -1
- package/dist/src/mcpl/types.js.map +1 -1
- package/dist/src/mcpl/visible-content.d.ts +7 -9
- package/dist/src/mcpl/visible-content.d.ts.map +1 -1
- package/dist/src/mcpl/visible-content.js +21 -9
- package/dist/src/mcpl/visible-content.js.map +1 -1
- package/dist/src/module-registry.d.ts +23 -8
- package/dist/src/module-registry.d.ts.map +1 -1
- package/dist/src/module-registry.js +68 -15
- package/dist/src/module-registry.js.map +1 -1
- package/dist/src/modules/discord/index.js +1 -1
- package/dist/src/modules/discord/index.js.map +1 -1
- package/dist/src/modules/history/index.d.ts +2 -1
- package/dist/src/modules/history/index.d.ts.map +1 -1
- package/dist/src/modules/history/index.js +51 -17
- package/dist/src/modules/history/index.js.map +1 -1
- package/dist/src/modules/history/semantic.d.ts +108 -6
- package/dist/src/modules/history/semantic.d.ts.map +1 -1
- package/dist/src/modules/history/semantic.js +273 -57
- package/dist/src/modules/history/semantic.js.map +1 -1
- package/dist/src/modules/workspace/direct-write.d.ts +181 -0
- package/dist/src/modules/workspace/direct-write.d.ts.map +1 -0
- package/dist/src/modules/workspace/direct-write.js +384 -0
- package/dist/src/modules/workspace/direct-write.js.map +1 -0
- package/dist/src/modules/workspace/edit-text.d.ts +26 -12
- package/dist/src/modules/workspace/edit-text.d.ts.map +1 -1
- package/dist/src/modules/workspace/edit-text.js +58 -28
- package/dist/src/modules/workspace/edit-text.js.map +1 -1
- package/dist/src/modules/workspace/effects.d.ts +10 -0
- package/dist/src/modules/workspace/effects.d.ts.map +1 -1
- package/dist/src/modules/workspace/effects.js +1 -1
- package/dist/src/modules/workspace/effects.js.map +1 -1
- package/dist/src/modules/workspace/index.d.ts +246 -27
- package/dist/src/modules/workspace/index.d.ts.map +1 -1
- package/dist/src/modules/workspace/index.js +1216 -236
- package/dist/src/modules/workspace/index.js.map +1 -1
- package/dist/src/modules/workspace/read-page.d.ts +41 -0
- package/dist/src/modules/workspace/read-page.d.ts.map +1 -0
- package/dist/src/modules/workspace/read-page.js +213 -0
- package/dist/src/modules/workspace/read-page.js.map +1 -0
- package/dist/src/modules/workspace/result-text.d.ts +67 -0
- package/dist/src/modules/workspace/result-text.d.ts.map +1 -0
- package/dist/src/modules/workspace/result-text.js +94 -0
- package/dist/src/modules/workspace/result-text.js.map +1 -0
- package/dist/src/modules/workspace/text.d.ts +29 -12
- package/dist/src/modules/workspace/text.d.ts.map +1 -1
- package/dist/src/modules/workspace/text.js +35 -33
- package/dist/src/modules/workspace/text.js.map +1 -1
- package/dist/src/modules/workspace/types.d.ts +41 -17
- package/dist/src/modules/workspace/types.d.ts.map +1 -1
- package/dist/src/modules/workspace/types.js.map +1 -1
- package/dist/src/sent-rows.d.ts +59 -0
- package/dist/src/sent-rows.d.ts.map +1 -0
- package/dist/src/sent-rows.js +145 -0
- package/dist/src/sent-rows.js.map +1 -0
- package/dist/src/silent-heartbeat.d.ts +17 -10
- package/dist/src/silent-heartbeat.d.ts.map +1 -1
- package/dist/src/silent-heartbeat.js.map +1 -1
- package/dist/src/stable-hash.d.ts +3 -0
- package/dist/src/stable-hash.d.ts.map +1 -0
- package/dist/src/stable-hash.js +15 -0
- package/dist/src/stable-hash.js.map +1 -0
- package/dist/src/stored-injections.d.ts +99 -0
- package/dist/src/stored-injections.d.ts.map +1 -0
- package/dist/src/stored-injections.js +106 -0
- package/dist/src/stored-injections.js.map +1 -0
- package/dist/src/text-as-stored.d.ts +4 -3
- package/dist/src/text-as-stored.d.ts.map +1 -1
- package/dist/src/text-as-stored.js +4 -3
- package/dist/src/text-as-stored.js.map +1 -1
- package/dist/src/thinking-drops.d.ts +81 -0
- package/dist/src/thinking-drops.d.ts.map +1 -0
- package/dist/src/thinking-drops.js +103 -0
- package/dist/src/thinking-drops.js.map +1 -0
- package/dist/src/tool-image-ledger.d.ts +9 -8
- package/dist/src/tool-image-ledger.d.ts.map +1 -1
- package/dist/src/tool-image-ledger.js +9 -8
- package/dist/src/tool-image-ledger.js.map +1 -1
- package/dist/src/tool-presentation.d.ts.map +1 -1
- package/dist/src/tool-presentation.js +3 -1
- package/dist/src/tool-presentation.js.map +1 -1
- package/dist/src/tool-result-guard.d.ts +4 -2
- package/dist/src/tool-result-guard.d.ts.map +1 -1
- package/dist/src/tool-result-guard.js +21 -6
- package/dist/src/tool-result-guard.js.map +1 -1
- package/dist/src/tool-result-history.d.ts +176 -24
- package/dist/src/tool-result-history.d.ts.map +1 -1
- package/dist/src/tool-result-history.js +411 -91
- package/dist/src/tool-result-history.js.map +1 -1
- package/dist/src/tune-out/coordinator.d.ts +17 -9
- package/dist/src/tune-out/coordinator.d.ts.map +1 -1
- package/dist/src/tune-out/coordinator.js +84 -37
- package/dist/src/tune-out/coordinator.js.map +1 -1
- package/dist/src/types/agent.d.ts +186 -7
- package/dist/src/types/agent.d.ts.map +1 -1
- package/dist/src/types/events.d.ts +15 -1
- package/dist/src/types/events.d.ts.map +1 -1
- package/dist/src/types/framework.d.ts +24 -3
- package/dist/src/types/framework.d.ts.map +1 -1
- package/dist/src/types/module.d.ts +90 -7
- package/dist/src/types/module.d.ts.map +1 -1
- package/dist/src/types/trace.d.ts +57 -2
- package/dist/src/types/trace.d.ts.map +1 -1
- package/package.json +4 -3
- package/src/agent.ts +593 -60
- package/src/api/server.ts +1 -2
- package/src/auto-continue.ts +42 -0
- package/src/code-execution/isolate/scripts.ts +11 -3
- package/src/framework.ts +3601 -967
- package/src/gate/event-gate.ts +2315 -389
- package/src/gate/index.ts +2 -1
- package/src/gate/recipe-reconcile.ts +355 -0
- package/src/gate/types.ts +70 -8
- package/src/index.ts +7 -4
- package/src/mcpl/channel-registry.ts +1220 -467
- package/src/mcpl/conversation-router.ts +34 -16
- package/src/mcpl/convert-block.ts +14 -1
- package/src/mcpl/hook-orchestrator.ts +33 -6
- package/src/mcpl/index.ts +6 -1
- package/src/mcpl/inference-router.ts +79 -34
- package/src/mcpl/prose-grammar.ts +7 -0
- package/src/mcpl/protocol-family.ts +2 -0
- package/src/mcpl/push-coalescer.ts +51 -13
- package/src/mcpl/reference-fetcher.ts +201 -162
- package/src/mcpl/references.ts +851 -77
- package/src/mcpl/server-connection.ts +145 -7
- package/src/mcpl/tool-lifecycle.ts +21 -0
- package/src/mcpl/tool-presence.ts +650 -0
- package/src/mcpl/tool-result-normalize.ts +18 -6
- package/src/mcpl/tool-signature.ts +285 -0
- package/src/mcpl/toolset-host.ts +637 -0
- package/src/mcpl/toolsets-module.ts +451 -0
- package/src/mcpl/transport.ts +3 -2
- package/src/mcpl/types.ts +22 -1
- package/src/mcpl/visible-content.ts +22 -9
- package/src/module-registry.ts +77 -24
- package/src/modules/discord/index.ts +1 -1
- package/src/modules/history/index.ts +48 -18
- package/src/modules/history/semantic.ts +308 -54
- package/src/modules/workspace/direct-write.ts +425 -0
- package/src/modules/workspace/edit-text.ts +82 -35
- package/src/modules/workspace/effects.ts +1 -1
- package/src/modules/workspace/index.ts +1274 -241
- package/src/modules/workspace/read-page.ts +222 -0
- package/src/modules/workspace/result-text.ts +119 -0
- package/src/modules/workspace/text.ts +58 -32
- package/src/modules/workspace/types.ts +41 -17
- package/src/sent-rows.ts +162 -0
- package/src/silent-heartbeat.ts +17 -10
- package/src/stable-hash.ts +14 -0
- package/src/stored-injections.ts +131 -0
- package/src/text-as-stored.ts +4 -3
- package/src/thinking-drops.ts +141 -0
- package/src/tool-image-ledger.ts +9 -8
- package/src/tool-presentation.ts +3 -1
- package/src/tool-result-guard.ts +19 -6
- package/src/tool-result-history.ts +471 -90
- package/src/tune-out/coordinator.ts +80 -37
- package/src/types/agent.ts +189 -7
- package/src/types/events.ts +15 -1
- package/src/types/framework.ts +21 -3
- package/src/types/module.ts +86 -7
- package/src/types/trace.ts +58 -2
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
import { randomUUID } from 'node:crypto';
|
|
19
19
|
import type { ContentBlock } from '@animalabs/membrane';
|
|
20
20
|
import type { StoredMessage } from '@animalabs/context-manager';
|
|
21
|
-
import type { ChannelRegistry, TuneOutParams } from '../mcpl/channel-registry.js';
|
|
21
|
+
import type { ChannelRef, ChannelRegistry, TuneOutParams } from '../mcpl/channel-registry.js';
|
|
22
22
|
import type { McplServerRegistry } from '../mcpl/server-registry.js';
|
|
23
23
|
import { CapabilityGrant } from '../mcpl/capability-grant.js';
|
|
24
24
|
import { armAfter, armAt } from '../deadline-timer.js';
|
|
@@ -45,7 +45,7 @@ const CUT_MARK = " [cut to fit; the whole message is in the channel's history]";
|
|
|
45
45
|
* its transport is now where the repeat tried to open it again (`transport`,
|
|
46
46
|
* from the framework's dispatch), then how to change the settings.
|
|
47
47
|
*/
|
|
48
|
-
export function alreadyTunedOutText(
|
|
48
|
+
export function alreadyTunedOutText(shown: string, p: TuneOutParams, transport?: string): string {
|
|
49
49
|
// A duration so long its end is past what a Date can hold has no time to show.
|
|
50
50
|
const expires = p.expiresAtMs !== undefined ? new Date(p.expiresAtMs) : undefined;
|
|
51
51
|
const until = expires && Number.isFinite(expires.getTime())
|
|
@@ -53,7 +53,7 @@ export function alreadyTunedOutText(channelId: string, p: TuneOutParams, transpo
|
|
|
53
53
|
: '';
|
|
54
54
|
// A thrown error's words may end without a stop.
|
|
55
55
|
const now = transport === undefined ? '' : /[.!?]$/.test(transport) ? `${transport} ` : `${transport}. `;
|
|
56
|
-
return `Nothing changed${transport !== undefined ? ' in the tune-out' : ''}: ${JSON.stringify(
|
|
56
|
+
return `Nothing changed${transport !== undefined ? ' in the tune-out' : ''}: ${JSON.stringify(shown)} is already `
|
|
57
57
|
+ `tuned out, with cadenceSeconds ${p.cadenceSeconds}, backlogCap ${p.backlogCap} and maxWakes ${p.maxWakes}${until}. `
|
|
58
58
|
+ now
|
|
59
59
|
+ 'To change those, cancel it first with tune_out mode "cancel", which delivers its backlog to you now, then call '
|
|
@@ -113,7 +113,7 @@ export interface TuneOutFrameworkHooks {
|
|
|
113
113
|
/** Move the subconscious window anchor (never forward past active epochs). */
|
|
114
114
|
setSubconsciousAnchor: (sequence: number) => void;
|
|
115
115
|
/** True when a channel is bound to a conversation-fork agent. */
|
|
116
|
-
isForkBound: (
|
|
116
|
+
isForkBound: (channel: ChannelRef) => boolean;
|
|
117
117
|
/** Gate privileged-users list (#77: privileged authors wake, like mentions). */
|
|
118
118
|
isPrivilegedAuthor: (authorId: string | null | undefined) => boolean;
|
|
119
119
|
/** speak_in_channel master switch (recipe-side, canary-gated). */
|
|
@@ -144,6 +144,15 @@ export class TuneOutCoordinator {
|
|
|
144
144
|
return `${serverId}\0${channelId}`;
|
|
145
145
|
}
|
|
146
146
|
|
|
147
|
+
/** A channel as the resident and the subconscious are shown it
|
|
148
|
+
* (ChannelRegistry.nameShown): its id, or, where another server registers
|
|
149
|
+
* the same id, its server-qualified name, which tune_out and
|
|
150
|
+
* cancel_tuneout take back; or, for a channel no string picks alone, its
|
|
151
|
+
* id and its server. */
|
|
152
|
+
private shown(serverId: string, channelId: string): string {
|
|
153
|
+
return this.channelRegistry.nameShown({ serverId, channelId });
|
|
154
|
+
}
|
|
155
|
+
|
|
147
156
|
/** Restart cadence + expiry timers and re-anchor after a process restart. */
|
|
148
157
|
resumeActiveEpochs(): void {
|
|
149
158
|
let oldestAnchor: number | null = null;
|
|
@@ -232,17 +241,17 @@ export class TuneOutCoordinator {
|
|
|
232
241
|
+ 'whoever runs this host.',
|
|
233
242
|
};
|
|
234
243
|
}
|
|
235
|
-
if (this.hooks.isForkBound(channelId)) {
|
|
244
|
+
if (this.hooks.isForkBound({ serverId, channelId })) {
|
|
236
245
|
return {
|
|
237
246
|
ok: false,
|
|
238
|
-
error: `Nothing changed: a conversation is bound to ${JSON.stringify(channelId)}, so its messages go to that `
|
|
247
|
+
error: `Nothing changed: a conversation is bound to ${JSON.stringify(this.shown(serverId, channelId))}, so its messages go to that `
|
|
239
248
|
+ "conversation, and tune_out won't divert a channel while one is. Calling tune_out on it again won't change "
|
|
240
249
|
+ 'that until the conversation ends, which happens only once the channel has been quiet for a while. Tune '
|
|
241
250
|
+ 'out a different channel, or ask whoever runs this host if this one should be diverted.',
|
|
242
251
|
};
|
|
243
252
|
}
|
|
244
253
|
const current = this.channelRegistry.getTuneOutState(serverId, channelId);
|
|
245
|
-
if (current) return { ok: false, error: alreadyTunedOutText(channelId, current.params), alreadyTunedOut: true };
|
|
254
|
+
if (current) return { ok: false, error: alreadyTunedOutText(this.shown(serverId, channelId), current.params), alreadyTunedOut: true };
|
|
246
255
|
// A tool's input reaches here as the model wrote it, and the store can't
|
|
247
256
|
// keep a NaN or an Infinity: its append would throw after the registry
|
|
248
257
|
// had marked the channel tuned out. So each option is read as a number
|
|
@@ -328,7 +337,7 @@ export class TuneOutCoordinator {
|
|
|
328
337
|
const ended = this.channelRegistry.cancelTuneOut(serverId, channelId, 'open', source);
|
|
329
338
|
if (!ended) {
|
|
330
339
|
// Its reader is the resident's tune_out: cancel_tuneout finds the channel first.
|
|
331
|
-
return { ok: false, error: this.notTunedOut(channelId, 'you') };
|
|
340
|
+
return { ok: false, error: this.notTunedOut(this.shown(serverId, channelId), 'you') };
|
|
332
341
|
}
|
|
333
342
|
this.clearTimers(serverId, channelId);
|
|
334
343
|
this.divertedSinceInvocation.delete(this.key(serverId, channelId));
|
|
@@ -359,10 +368,12 @@ export class TuneOutCoordinator {
|
|
|
359
368
|
.join('\n');
|
|
360
369
|
return `${authorOf(m)}: ${text}`;
|
|
361
370
|
};
|
|
362
|
-
|
|
371
|
+
// The channel as the resident and the subconscious are shown it.
|
|
372
|
+
const name = this.shown(serverId, channelId);
|
|
373
|
+
const lifecycle = `[Tune-out: ${name} cancelled — ${reason}, ${ended.wakeCount} wake${ended.wakeCount === 1 ? '' : 's'}]`;
|
|
363
374
|
const render = (lines: string[], truncated: number): string => {
|
|
364
375
|
const attrs = [
|
|
365
|
-
`channel="${
|
|
376
|
+
`channel="${name}"`,
|
|
366
377
|
`messages=${backlog.length}`,
|
|
367
378
|
...(truncated > 0 ? [`truncated=${truncated} (oldest; fetch_history covers the rest)`] : []),
|
|
368
379
|
];
|
|
@@ -441,7 +452,9 @@ export class TuneOutCoordinator {
|
|
|
441
452
|
this.hooks.addMessage(
|
|
442
453
|
subName,
|
|
443
454
|
[{ type: 'text', text: subconsciousNote }],
|
|
444
|
-
|
|
455
|
+
// With its server: a conversational row whose channel the framework
|
|
456
|
+
// reads (repinChannel), where another server's channel can share the id.
|
|
457
|
+
{ kind: 'subconscious-note', channelId, serverId, epochId },
|
|
445
458
|
);
|
|
446
459
|
}
|
|
447
460
|
const primary = this.hooks.primaryName();
|
|
@@ -467,7 +480,7 @@ export class TuneOutCoordinator {
|
|
|
467
480
|
: '';
|
|
468
481
|
this.hooks.addMessage(
|
|
469
482
|
'user',
|
|
470
|
-
[{ type: 'text', text: `[Tune-out cancelled: ${channelId} — ${reason}.${truncNote}${heldNote}]` }],
|
|
483
|
+
[{ type: 'text', text: `[Tune-out cancelled: ${this.shown(serverId, channelId)} — ${reason}.${truncNote}${heldNote}]` }],
|
|
471
484
|
{ system: true, kind: 'tune-out-cancel-report', channelId },
|
|
472
485
|
subName,
|
|
473
486
|
);
|
|
@@ -481,7 +494,7 @@ export class TuneOutCoordinator {
|
|
|
481
494
|
subName,
|
|
482
495
|
);
|
|
483
496
|
}
|
|
484
|
-
this.hooks.requestInference(subName, `tune-out cancelled: ${
|
|
497
|
+
this.hooks.requestInference(subName, `tune-out cancelled: ${name}`, 'tune-out');
|
|
485
498
|
}
|
|
486
499
|
this.hooks.emitTrace({ type: 'tune-out:cancelled', serverId, channelId, epochId, reason, wakeCount: ended.wakeCount });
|
|
487
500
|
return { ok: true };
|
|
@@ -649,16 +662,17 @@ export class TuneOutCoordinator {
|
|
|
649
662
|
if (!subName) return;
|
|
650
663
|
const key = this.key(serverId, channelId);
|
|
651
664
|
|
|
665
|
+
const shown = this.shown(serverId, channelId);
|
|
652
666
|
const notice = trigger === 'wake'
|
|
653
|
-
? `[Tune-out wake: ${
|
|
654
|
-
: `[Tune-out cadence: ${
|
|
667
|
+
? `[Tune-out wake: ${shown} — ${count} addressed message${count === 1 ? '' : 's'}]`
|
|
668
|
+
: `[Tune-out cadence: ${shown} — ${count} diverted message${count === 1 ? '' : 's'} since your last look]`;
|
|
655
669
|
this.hooks.addMessage(
|
|
656
670
|
'user',
|
|
657
671
|
[{ type: 'text', text: notice }],
|
|
658
672
|
{ system: true, kind: `tune-out-${trigger}`, channelId },
|
|
659
673
|
subName,
|
|
660
674
|
);
|
|
661
|
-
this.hooks.requestInference(subName, `tune-out ${trigger}: ${
|
|
675
|
+
this.hooks.requestInference(subName, `tune-out ${trigger}: ${shown}`, 'tune-out');
|
|
662
676
|
// Counted from here only once the turn has been asked for, so a turn that
|
|
663
677
|
// throws leaves its messages for the next period's turn.
|
|
664
678
|
this.divertedSinceInvocation.set(key, 0);
|
|
@@ -740,6 +754,8 @@ export class TuneOutCoordinator {
|
|
|
740
754
|
async handleSubconsciousTool(
|
|
741
755
|
name: string,
|
|
742
756
|
input: Record<string, unknown>,
|
|
757
|
+
/** The calling turn's own channels, for a call made inside it (the framework's callOwnChannels). */
|
|
758
|
+
own: readonly ChannelRef[] = [],
|
|
743
759
|
): Promise<{ success: boolean; data?: unknown; error?: string; isError?: boolean }> {
|
|
744
760
|
switch (name) {
|
|
745
761
|
case 'deliver_summary': {
|
|
@@ -755,19 +771,48 @@ export class TuneOutCoordinator {
|
|
|
755
771
|
}
|
|
756
772
|
const subName = this.hooks.subconsciousName();
|
|
757
773
|
if (!subName) return { success: false, error: 'subconscious not configured', isError: true };
|
|
758
|
-
// Verbatim, under its own participant name, into the resident's window
|
|
774
|
+
// Verbatim, under its own participant name, into the resident's window,
|
|
775
|
+
// tagged with the channel the name means and its server, since the
|
|
776
|
+
// framework reads a conversational row's channel (repinChannel). The
|
|
777
|
+
// name means what it means to every channel tool: inside the calling
|
|
778
|
+
// turn, the id of one of the turn's own channels means that channel,
|
|
779
|
+
// refused where its server no longer registers it or the turn has
|
|
780
|
+
// channels with that id on two servers (ownChannelFor); otherwise a
|
|
781
|
+
// name several registered channels answer to is refused with them
|
|
782
|
+
// (sharedNameRefusalFor). So no row is left for a re-pin to read as
|
|
783
|
+
// another server's channel. A name no registered channel answers to
|
|
784
|
+
// is kept as given, with no server, which a re-pin reads by name when
|
|
785
|
+
// it weighs the row, as it reads any message without its server.
|
|
786
|
+
const mine = this.channelRegistry.ownChannelFor(channelId, own, 'deliver');
|
|
787
|
+
if (mine && 'error' in mine) return { success: false, error: mine.error, isError: true };
|
|
788
|
+
const shared = mine ? undefined : this.channelRegistry.sharedNameRefusalFor(channelId, 'nothing was delivered');
|
|
789
|
+
if (shared) return { success: false, error: shared, isError: true };
|
|
790
|
+
const [named] = mine ? [mine.channel] : this.channelRegistry.channelsNamed(channelId);
|
|
759
791
|
this.hooks.addMessage(subName, [{ type: 'text', text }], {
|
|
760
792
|
kind: 'subconscious-summary',
|
|
761
|
-
channelId,
|
|
793
|
+
...(named ? { channelId: named.channelId, serverId: named.serverId } : { channelId }),
|
|
762
794
|
});
|
|
763
795
|
return { success: true, data: { delivered: true } };
|
|
764
796
|
}
|
|
765
797
|
case 'cancel_tuneout': {
|
|
766
|
-
const
|
|
767
|
-
|
|
768
|
-
|
|
798
|
+
const name = String(input.channelId ?? '');
|
|
799
|
+
// The name means what it means to every channel tool, the
|
|
800
|
+
// resident's tune_out included: inside the calling turn, the id of
|
|
801
|
+
// one of the turn's own channels means that channel (ownChannelFor);
|
|
802
|
+
// otherwise a name several registered channels answer to is refused
|
|
803
|
+
// (channelsNamed), whichever of them is tuned out, never taken to be
|
|
804
|
+
// the tuned-out one.
|
|
805
|
+
const mine = this.channelRegistry.ownChannelFor(name, own);
|
|
806
|
+
if (mine && 'error' in mine) return { success: false, error: mine.error, isError: true };
|
|
807
|
+
const named = mine ? [mine.channel] : this.channelRegistry.channelsNamed(name);
|
|
808
|
+
const shared = mine ? undefined : this.channelRegistry.sharedNameRefusalFor(name);
|
|
809
|
+
if (shared) return { success: false, error: shared, isError: true };
|
|
810
|
+
const channel = named[0];
|
|
811
|
+
if (!channel || !this.channelRegistry.getTuneOutState(channel.serverId, channel.channelId)) {
|
|
812
|
+
return { success: false, error: this.notTunedOut(name, 'the resident'), isError: true };
|
|
813
|
+
}
|
|
769
814
|
const note = typeof input.text === 'string' && input.text.trim() ? input.text.trim() : undefined;
|
|
770
|
-
const result = this.cancel(
|
|
815
|
+
const result = this.cancel(channel.serverId, channel.channelId, 'subconscious', 'subconscious judgment', note);
|
|
771
816
|
return result.ok
|
|
772
817
|
? { success: true, data: { cancelled: true } }
|
|
773
818
|
: { success: false, error: result.error, isError: true };
|
|
@@ -817,7 +862,11 @@ export class TuneOutCoordinator {
|
|
|
817
862
|
if (!text) {
|
|
818
863
|
return { success: false, error: 'Nothing was posted: speak_in_channel needs the message in "text".', isError: true };
|
|
819
864
|
}
|
|
820
|
-
|
|
865
|
+
// Inside the calling turn, the id of one of the turn's own channels
|
|
866
|
+
// means that channel, as for every channel tool (ownChannelFor).
|
|
867
|
+
const mine = this.channelRegistry.ownChannelFor(channelId, own, 'publish');
|
|
868
|
+
if (mine && 'error' in mine) return { success: false, error: mine.error, isError: true };
|
|
869
|
+
return this.channelRegistry.publishForAgent(mine ? mine.channel : channelId, text, this.hooks.subconsciousName() ?? 'Subconscious');
|
|
821
870
|
}
|
|
822
871
|
default:
|
|
823
872
|
return { success: false, error: `unknown subconscious tool: ${name}`, isError: true };
|
|
@@ -825,32 +874,26 @@ export class TuneOutCoordinator {
|
|
|
825
874
|
}
|
|
826
875
|
|
|
827
876
|
/**
|
|
828
|
-
* A cancel of a channel that isn't tuned out
|
|
829
|
-
*
|
|
877
|
+
* A cancel of a channel that isn't tuned out, by the name the caller gave
|
|
878
|
+
* or knows it by: which channels are, so the next call can name one, and
|
|
879
|
+
* how a tune-out ends without one. `backlogTo`
|
|
830
880
|
* names the resident as the message's reader knows it: "you" to the
|
|
831
881
|
* resident, "the resident" to the subconscious. It speaks of connected
|
|
832
882
|
* channels because listActive reads the registered ones: removing a server
|
|
833
883
|
* drops its channels and keeps their tune-outs, so a tune-out there is in
|
|
834
|
-
* neither this list nor
|
|
835
|
-
* about it.
|
|
884
|
+
* neither this list nor the name's lookup (channelsNamed, which reads the
|
|
885
|
+
* registered channels too), and the text claims nothing about it.
|
|
836
886
|
*/
|
|
837
|
-
private notTunedOut(
|
|
838
|
-
const active = this.listActive().map((e) => JSON.stringify(e.channelId));
|
|
887
|
+
private notTunedOut(name: string, backlogTo: 'you' | 'the resident'): string {
|
|
888
|
+
const active = this.listActive().map((e) => JSON.stringify(this.shown(e.serverId, e.channelId)));
|
|
839
889
|
const opening = active.length === 0
|
|
840
890
|
? 'no connected channel is tuned out now.'
|
|
841
|
-
: `no connected channel ${JSON.stringify(
|
|
891
|
+
: `no connected channel ${JSON.stringify(name)} is tuned out. Connected channels tuned out now: `
|
|
842
892
|
+ `${active.join(', ')}.`;
|
|
843
893
|
return `Nothing was cancelled: ${opening} A tune-out also ends by itself when its duration runs out, or at the `
|
|
844
894
|
+ `first wake past its wake budget, and its backlog goes to ${backlogTo} then.`;
|
|
845
895
|
}
|
|
846
896
|
|
|
847
|
-
private findByChannel(channelId: string): { serverId: string } | null {
|
|
848
|
-
for (const entry of this.listActive()) {
|
|
849
|
-
if (entry.channelId === channelId) return { serverId: entry.serverId };
|
|
850
|
-
}
|
|
851
|
-
return null;
|
|
852
|
-
}
|
|
853
|
-
|
|
854
897
|
/** Stop all timers (framework shutdown). */
|
|
855
898
|
stop(): void {
|
|
856
899
|
for (const timer of this.cadenceTimers.values()) clearTimeout(timer);
|
package/src/types/agent.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type { ContentBlock, YieldingStream } from '@animalabs/membrane';
|
|
|
2
2
|
import type { ContextStrategy } from '@animalabs/context-manager';
|
|
3
3
|
import type { ToolCallId, ToolResult, ToolCall } from './events.js';
|
|
4
4
|
import type { SilentHeartbeatTick } from '../silent-heartbeat.js';
|
|
5
|
+
import type { WakeLine } from '../gate/types.js';
|
|
5
6
|
|
|
6
7
|
export type SameRoundThinkTextPolicy = 'public' | 'private';
|
|
7
8
|
export type SameRoundThinkTextPolicySource =
|
|
@@ -9,6 +10,24 @@ export type SameRoundThinkTextPolicySource =
|
|
|
9
10
|
| 'recipe'
|
|
10
11
|
| 'compatibility_default';
|
|
11
12
|
|
|
13
|
+
/** What a continuation's `[continue]` row says (InferenceRequest.autoContinueRun). */
|
|
14
|
+
export interface AutoContinueRun {
|
|
15
|
+
/** How the agent stops, as the row puts it ("call skip_reply"). */
|
|
16
|
+
stop: string;
|
|
17
|
+
/** The turn before ended in words that were held, not sent, because it
|
|
18
|
+
* had already sent with a send tool. */
|
|
19
|
+
heldAfterSend?: boolean;
|
|
20
|
+
/** Set by the scheduler: the continuation is the turn's only cause. */
|
|
21
|
+
sole?: boolean;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** `AgentConfig.autoContinue` as an object (`true` means `{}`). */
|
|
25
|
+
export interface AutoContinueConfig {
|
|
26
|
+
/** Continuations in a row before the agent waits for the next event:
|
|
27
|
+
* a whole number, 1 to 20. Default 3. */
|
|
28
|
+
maxConsecutive?: number;
|
|
29
|
+
}
|
|
30
|
+
|
|
12
31
|
/**
|
|
13
32
|
* Configuration for an agent.
|
|
14
33
|
*/
|
|
@@ -62,7 +81,11 @@ export interface AgentConfig {
|
|
|
62
81
|
temperature?: number;
|
|
63
82
|
|
|
64
83
|
/** Max input tokens before framework breaks a yielding stream and
|
|
65
|
-
* restarts with recompiled (compressed) context. Default: 150000.
|
|
84
|
+
* restarts with recompiled (compressed) context. Default: 150000. Not
|
|
85
|
+
* checked mid-turn for an agent whose model binds its thinking
|
|
86
|
+
* (`thinkingBinding.binds`) with `physicalWindowTokens` set: a new stream
|
|
87
|
+
* there would take the turn's own earlier reasoning out of the request, and
|
|
88
|
+
* the window projection still guards the provider's hard cap. */
|
|
66
89
|
maxStreamTokens?: number;
|
|
67
90
|
|
|
68
91
|
/**
|
|
@@ -75,7 +98,9 @@ export interface AgentConfig {
|
|
|
75
98
|
* request the provider will 400 (issue #92: a legal compile can walk past
|
|
76
99
|
* the physical window mid-turn). The projection runs once a call on the
|
|
77
100
|
* stream has reported its prompt. Unset → no projection; only the
|
|
78
|
-
* maxStreamTokens restart applies.
|
|
101
|
+
* maxStreamTokens restart applies. A value is converted to a number as a
|
|
102
|
+
* comparison converts it, so a string such as "200000" reads as its number;
|
|
103
|
+
* one whose number isn't finite, such as null, reads as unset.
|
|
79
104
|
*/
|
|
80
105
|
physicalWindowTokens?: number;
|
|
81
106
|
|
|
@@ -133,7 +158,9 @@ export interface AgentConfig {
|
|
|
133
158
|
* Extended thinking config. When `enabled: true`, the agent runs with
|
|
134
159
|
* Anthropic's native extended thinking; responses include `thinking` blocks
|
|
135
160
|
* with cryptographic signatures, and the API enforces `temperature: 1`
|
|
136
|
-
* (Membrane handles this). Omit or set `enabled: false` to disable
|
|
161
|
+
* (Membrane handles this). Omit or set `enabled: false` to disable, except
|
|
162
|
+
* on a model whose thinking is always on (Fable, Mythos), which thinks
|
|
163
|
+
* whatever this says.
|
|
137
164
|
*/
|
|
138
165
|
thinking?: {
|
|
139
166
|
enabled: boolean;
|
|
@@ -143,10 +170,98 @@ export interface AgentConfig {
|
|
|
143
170
|
/**
|
|
144
171
|
* How thinking content is returned: 'summarized' (readable summary) or
|
|
145
172
|
* 'omitted' (empty thinking field, signature only). Fable 5 / Opus 4.7+
|
|
146
|
-
* default to 'omitted' — set 'summarized' to receive thinking text.
|
|
173
|
+
* default to 'omitted' — set 'summarized' to receive thinking text. A
|
|
174
|
+
* model whose thinking is always on (Fable, Mythos) is sent a thinking
|
|
175
|
+
* config only to carry `thinkingBinding.dropBlock`, so this reaches it
|
|
176
|
+
* only with that set.
|
|
147
177
|
*/
|
|
148
178
|
display?: 'summarized' | 'omitted';
|
|
149
179
|
};
|
|
180
|
+
/**
|
|
181
|
+
* How the provider binds this agent's signed thinking to the conversation
|
|
182
|
+
* before it. A host sets it from what it knows of the agent's model and
|
|
183
|
+
* route.
|
|
184
|
+
*/
|
|
185
|
+
thinkingBinding?: {
|
|
186
|
+
/**
|
|
187
|
+
* Ask the provider to drop a signed thinking block whose earlier
|
|
188
|
+
* conversation has changed, as a fold changes it, rather than refuse the
|
|
189
|
+
* request. An account that enforces the binding otherwise answers such a
|
|
190
|
+
* request with a 400. One that doesn't decides for itself: the four
|
|
191
|
+
* accounts tried on 2026-10-18 dropped such blocks silently, so there the
|
|
192
|
+
* model sees nothing different and the report is what's new, and one
|
|
193
|
+
* that would show such a block to the model is asked to drop it instead.
|
|
194
|
+
*
|
|
195
|
+
* It's sent as `thinking.block_binding.prefix_mismatch_behavior:
|
|
196
|
+
* "drop_block"` with the `thinking-binding-controls-2026-08-01` beta,
|
|
197
|
+
* which membrane's Anthropic and Bedrock adapters carry (Bedrock's
|
|
198
|
+
* acceptance of the beta hasn't been tried). On any other adapter it does
|
|
199
|
+
* nothing, so a host should refuse it there. A thinking config the host
|
|
200
|
+
* sends itself, through `request.extra` or as `thinking` in
|
|
201
|
+
* `providerParams`, replaces the one membrane builds, control included.
|
|
202
|
+
*
|
|
203
|
+
* On a model whose thinking is always on (Fable, Mythos), membrane
|
|
204
|
+
* carries it in an adaptive thinking config whatever `thinking.enabled`
|
|
205
|
+
* says, with `thinking.display` when that's set, so such an agent's
|
|
206
|
+
* `display` takes effect only with this setting. On any other model, it
|
|
207
|
+
* goes only in the thinking config membrane sends while thinking is on.
|
|
208
|
+
* Membrane tells the first kind by its id, which names `fable` or
|
|
209
|
+
* `mythos`. Opus 5.5, Haiku 5.5, Sonnet 5.5, Sonnet 5, Fable 5 and Fable
|
|
210
|
+
* 5.1 accepted the config on 2026-10-18. Mythos hasn't been tried, and
|
|
211
|
+
* membrane notes that the family has refused thinking configs with a 400
|
|
212
|
+
* before, so for a Mythos model the setting may fail every request.
|
|
213
|
+
*
|
|
214
|
+
* On a framework turn, each call's report of the blocks the provider
|
|
215
|
+
* dropped goes to its `inference:usage` trace, and neither that call nor
|
|
216
|
+
* its stream's later calls feed the context strategy's calibration, since
|
|
217
|
+
* the provider doesn't bill a block it dropped and the strategy still
|
|
218
|
+
* counts it. After a fold, each block after the stretch it rewrote is
|
|
219
|
+
* bound to a conversation the request no longer holds. Where the context
|
|
220
|
+
* strategy still sends such a block, the provider drops it on every call
|
|
221
|
+
* until a later fold takes it out of the context, and while such blocks
|
|
222
|
+
* are sent, no stream gives a sample and the calibration keeps the value
|
|
223
|
+
* it had. A strategy that leaves that thinking out at the fold sends none
|
|
224
|
+
* to drop. The direct API carries the control too, but reads no report: it
|
|
225
|
+
* traces no usage and feeds no calibration.
|
|
226
|
+
*
|
|
227
|
+
* Default false: nothing is sent, and the account's default applies.
|
|
228
|
+
*/
|
|
229
|
+
dropBlock?: boolean;
|
|
230
|
+
/**
|
|
231
|
+
* Whether this agent's model binds its signed thinking to the
|
|
232
|
+
* conversation before it, as the host knows from its own model records.
|
|
233
|
+
* A context manager that keeps a record of what each branch sent can
|
|
234
|
+
* then leave out thinking whose earlier conversation changed before the
|
|
235
|
+
* provider sees it. Set:
|
|
236
|
+
* - each compile the agent sends tells the context manager so
|
|
237
|
+
* (`thinkingBinding`: `binds`, `dropBlock` as set here, and the agent's
|
|
238
|
+
* name as the `participant` whose replies are its own), with digests
|
|
239
|
+
* of what the request carries ahead of its messages (`identity`: the
|
|
240
|
+
* system text with its system-position injections, the tools in the
|
|
241
|
+
* order sent, the model, and the thinking config, `maxTokens`,
|
|
242
|
+
* `providerParams` and `prefillUserMessage`);
|
|
243
|
+
* - each framework stream tells the context manager which stored rows
|
|
244
|
+
* its later rounds and its last reply carried as stored
|
|
245
|
+
* (`noteRequestSent`), naming the compile it was built from, so their
|
|
246
|
+
* thinking is kept at the next compile. The direct API
|
|
247
|
+
* (`runInference`) notes nothing, so each of its replies' thinking is
|
|
248
|
+
* left out at the compile after it's stored;
|
|
249
|
+
* - with `physicalWindowTokens` set too, `maxStreamTokens` no longer
|
|
250
|
+
* starts a new stream mid-turn, since the new stream's compile can take
|
|
251
|
+
* the turn's earlier reasoning out of the request;
|
|
252
|
+
* - with `dropBlock` set too, where a compile reports where the branch's
|
|
253
|
+
* record began (`thinkingRecordedAfter`), a block the provider drops
|
|
254
|
+
* that the record doesn't account for raises the
|
|
255
|
+
* `thinking-binding-loss` ops alert, once per incident and again if a
|
|
256
|
+
* call drops more. The provider reports drops only on a request that
|
|
257
|
+
* carries the drop control; without it, such a block is refused.
|
|
258
|
+
* The record and the notes
|
|
259
|
+
* vouch for requests as agent-framework hands them to membrane, so a
|
|
260
|
+
* membrane `beforeRequest` hook that changes a request must change every
|
|
261
|
+
* request alike. Default false: compiles and streams are as they were.
|
|
262
|
+
*/
|
|
263
|
+
binds?: boolean;
|
|
264
|
+
};
|
|
150
265
|
/**
|
|
151
266
|
* How to handle a model content-policy refusal (`stop_reason: refusal`).
|
|
152
267
|
* When `autoRewind` is on, a refused turn triggers an automatic rewind: the
|
|
@@ -219,6 +334,34 @@ export interface AgentConfig {
|
|
|
219
334
|
*/
|
|
220
335
|
toolWrapperProseGuard?: boolean;
|
|
221
336
|
|
|
337
|
+
/**
|
|
338
|
+
* Continue on the agent's own turn ending (off when left out): a turn the
|
|
339
|
+
* model ends itself (stop reason `end_turn` or `stop_sequence`), whether
|
|
340
|
+
* its last response is prose or empty, is followed by another turn,
|
|
341
|
+
* instead of a pause until the next event. A turn ended through
|
|
342
|
+
* `skip_reply` (or another tool that ends the turn, such as `sleep`), or
|
|
343
|
+
* whose last prose envelope is `>>skip_reply` (explicit) or `>>>skip_reply`
|
|
344
|
+
* (hybrid), pauses as usual. So does one whose closing prose bounced (the
|
|
345
|
+
* bounce and its cap answer it), and an agent offered no way to stop
|
|
346
|
+
* (locus or disabled mode without an offered `skip_reply`).
|
|
347
|
+
*
|
|
348
|
+
* As the next turn starts, a short `[continue]` system row naming the agent
|
|
349
|
+
* joins the window: another turn starts, how to stop, and that unmarked
|
|
350
|
+
* words are sent as usual; on the last continuation of a run, that the
|
|
351
|
+
* agent waits for the next event after it. It's written only when the
|
|
352
|
+
* continuation is that turn's whole cause: a message batched with it makes
|
|
353
|
+
* it that message's turn, with no row, in the message's channel. Otherwise
|
|
354
|
+
* the next turn keeps the turn's locus (locus and hybrid modes). At most
|
|
355
|
+
* `maxConsecutive` (default 3) continuations run in a row: the count
|
|
356
|
+
* starts again when a turn starts with a cause of its own (a message, a
|
|
357
|
+
* timer, a heartbeat that isn't a silent tick) or a tool ends one. Silent
|
|
358
|
+
* heartbeat ticks, refusals, `max_tokens` cut-offs, the stall guard's
|
|
359
|
+
* ends, ephemeral runs, conversation forks and a quiesce never continue. A
|
|
360
|
+
* ` !` continuation the turn asked for runs instead, and counts toward
|
|
361
|
+
* nothing. See docs/auto-continue.md.
|
|
362
|
+
*/
|
|
363
|
+
autoContinue?: boolean | AutoContinueConfig;
|
|
364
|
+
|
|
222
365
|
/**
|
|
223
366
|
* Clock rows (off when left out): when a boundary of this interval on the
|
|
224
367
|
* agent's local wall clock (counted from local midnight, in the
|
|
@@ -399,9 +542,13 @@ export interface InferenceRequest {
|
|
|
399
542
|
* all of them without materializing content, the turn has no cause left
|
|
400
543
|
* and does not run. */
|
|
401
544
|
coalescingBatchSubjects?: string[];
|
|
402
|
-
/** Host-owned: this wake's sole cause is
|
|
403
|
-
* coalesced render placed in its
|
|
404
|
-
*
|
|
545
|
+
/** Host-owned: this wake's sole cause is this write, deferred for its
|
|
546
|
+
* agent while a turn was alive: a coalesced render placed in its queue
|
|
547
|
+
* (wakeToRead), or a message, notice or event whose wake was queued with
|
|
548
|
+
* it. Once a request that carried the write was answered and, for a wake
|
|
549
|
+
* that carries a channel, the agent has since published into that
|
|
550
|
+
* channel having read it, or once the write is in neither the agent's
|
|
551
|
+
* queue nor its store's current branch, the wake starts no turn. */
|
|
405
552
|
readsDeferredWrite?: string;
|
|
406
553
|
agentName: string;
|
|
407
554
|
reason: string;
|
|
@@ -418,6 +565,25 @@ export interface InferenceRequest {
|
|
|
418
565
|
* ephemeral run's (a subagent's), which takes no global fallback.
|
|
419
566
|
*/
|
|
420
567
|
channelId?: string;
|
|
568
|
+
/**
|
|
569
|
+
* The server of `channelId`'s channel. MCPL requires a channel id to be
|
|
570
|
+
* unique only within one server's connection (§14.2), so where two
|
|
571
|
+
* servers register the same id only the pair names one channel. The
|
|
572
|
+
* framework's own wakes set it beside `channelId`. A host's request that
|
|
573
|
+
* gives `channelId` alone names the one channel that id, or its
|
|
574
|
+
* server-qualified name (`serverId:channelId`), means; one that two
|
|
575
|
+
* servers' channels answer to names none, and the turn's speech has no
|
|
576
|
+
* channel of its own.
|
|
577
|
+
*/
|
|
578
|
+
channelServerId?: string;
|
|
579
|
+
/**
|
|
580
|
+
* Framework-set: this wake carries an autoContinue continuation
|
|
581
|
+
* (AgentConfig.autoContinue), with what its `[continue]` row says. The
|
|
582
|
+
* scheduler keeps the mark on the turn's trigger only when the
|
|
583
|
+
* continuation is that turn's whole cause, and the turn writes the row as
|
|
584
|
+
* it starts. Hosts don't set it.
|
|
585
|
+
*/
|
|
586
|
+
autoContinueRun?: AutoContinueRun;
|
|
421
587
|
/**
|
|
422
588
|
* True when the triggering message explicitly addressed the agent (mention,
|
|
423
589
|
* reply-to-bot, DM — `chat:addressed` in MCPL RFC-001 terms). When a wake
|
|
@@ -426,6 +592,16 @@ export interface InferenceRequest {
|
|
|
426
592
|
* not capture the agent's voice just by being newest.
|
|
427
593
|
*/
|
|
428
594
|
addressed?: boolean;
|
|
595
|
+
/**
|
|
596
|
+
* Host-owned: true when the triggering message's server marked it ambient
|
|
597
|
+
* (`chat:ambient`). A coalesced push's wake carries it only to read a
|
|
598
|
+
* render, and only when every notice the render stands for was. Such a
|
|
599
|
+
* message asked nothing of the agent, so a wake for it that carries
|
|
600
|
+
* `readsDeferredWrite` is dropped once a request that carried the message
|
|
601
|
+
* was answered, with no publish asked of the agent. A message with neither
|
|
602
|
+
* tag is in doubt, and is judged as an addressed one.
|
|
603
|
+
*/
|
|
604
|
+
ambient?: true;
|
|
429
605
|
/**
|
|
430
606
|
* Who spoke the message that triggered this inference, as an adapter-
|
|
431
607
|
* namespaced opaque id (`<serverId>:user:<authorId>`) — never a display
|
|
@@ -446,6 +622,12 @@ export interface InferenceRequest {
|
|
|
446
622
|
wakeChannelId?: string;
|
|
447
623
|
/** Timestamp of the event the wake provenance was taken from (ms). */
|
|
448
624
|
wakeAt?: number;
|
|
625
|
+
/** Batched-wake lines the gate handed to this wake rather than wrote
|
|
626
|
+
* (WakeLine), decided by a fresh start of its turn as WakeLine says: each
|
|
627
|
+
* written once into the window it names, or left for a later fresh start
|
|
628
|
+
* of the same wake. A retry, or a start that resumes a logical turn,
|
|
629
|
+
* decides none. */
|
|
630
|
+
wakeLines?: WakeLine[];
|
|
449
631
|
/** Suppress every automatic plain-prose delivery for this logical turn.
|
|
450
632
|
* Explicit tool calls remain available. Used by authenticated silent wakes. */
|
|
451
633
|
suppressProse?: boolean;
|
package/src/types/events.ts
CHANGED
|
@@ -194,6 +194,18 @@ export interface ToolResult {
|
|
|
194
194
|
* it reads the result's error text, if any.
|
|
195
195
|
*/
|
|
196
196
|
data?: unknown;
|
|
197
|
+
/**
|
|
198
|
+
* Whether `data` is a protocol result's content: an array in which every
|
|
199
|
+
* element is a content block, of a type this host can show or not. The
|
|
200
|
+
* framework sets it on an MCPL tool's result, and a module that returns a
|
|
201
|
+
* content array can set it too. When it's set, each element reaches the
|
|
202
|
+
* model as itself, as a reference's stub, or as a marker saying what it was
|
|
203
|
+
* and why it isn't shown, never as JSON. When it isn't, an array counts as
|
|
204
|
+
* content only when every element is a block of one of MCP's types (text,
|
|
205
|
+
* image, audio, resource, resource_link), and any other array is data,
|
|
206
|
+
* written as JSON.
|
|
207
|
+
*/
|
|
208
|
+
dataIsContent?: boolean;
|
|
197
209
|
/** Error message (if failure) */
|
|
198
210
|
error?: string;
|
|
199
211
|
/** Whether this was an error (for LLM) */
|
|
@@ -311,7 +323,9 @@ export interface ToolCall {
|
|
|
311
323
|
* read_image tool passes a model's call to the workspace without it; and a
|
|
312
324
|
* utility run through `utils` carries what its `utils` call carried.
|
|
313
325
|
* Without it, a module holds its answer to a bound of its own, if any: the
|
|
314
|
-
* workspace's
|
|
326
|
+
* workspace's `edit` and `edit_many` answers hold to the house default,
|
|
327
|
+
* 24,000, and a `read` holds its page to its line limit alone, with every
|
|
328
|
+
* line whole, since such a caller takes the data itself.
|
|
315
329
|
*/
|
|
316
330
|
resultMaxChars?: number;
|
|
317
331
|
}
|
package/src/types/framework.ts
CHANGED
|
@@ -16,6 +16,13 @@ export type { TraceEvent, TraceEventListener } from './trace.js';
|
|
|
16
16
|
export interface ModuleProcessResponse {
|
|
17
17
|
moduleName: string;
|
|
18
18
|
response: EventResponse;
|
|
19
|
+
/**
|
|
20
|
+
* Set when applying the response threw: the operation it threw at, such
|
|
21
|
+
* as `the edit of <id>`, and the error's message. What the response asked
|
|
22
|
+
* for before that was applied, that operation may have taken effect in
|
|
23
|
+
* part or whole, and the rest of it wasn't (EventResponse).
|
|
24
|
+
*/
|
|
25
|
+
threw?: { at: string; error: string };
|
|
19
26
|
}
|
|
20
27
|
|
|
21
28
|
/**
|
|
@@ -241,8 +248,9 @@ export interface FrameworkConfig {
|
|
|
241
248
|
* workspace file under `tool-results/` (deterministic `<date>-<callId>.txt`
|
|
242
249
|
* name, overwritten on collision, retained until the workspace owner
|
|
243
250
|
* deletes it — never auto-GC'd) and replaced inline by a bounded preview
|
|
244
|
-
* plus the file reference; with no writable mount without an include
|
|
245
|
-
*
|
|
251
|
+
* plus the file reference; with no writable mount without an include that
|
|
252
|
+
* keeps its files in the store (not configured direct), the fallback is
|
|
253
|
+
* explicit plain truncation. Default 24000 (house-safe; issue #89). Must be
|
|
246
254
|
* >= 1000. A resident's own agent_settings value
|
|
247
255
|
* `tool_result_inline_max_chars` (durable, persisted in framework state)
|
|
248
256
|
* takes precedence over this for that agent; the EFFECTIVE cap for every
|
|
@@ -276,7 +284,17 @@ export interface FrameworkConfig {
|
|
|
276
284
|
*/
|
|
277
285
|
hostToolClasses?: Record<string, string[]>;
|
|
278
286
|
|
|
279
|
-
/**
|
|
287
|
+
/**
|
|
288
|
+
* Inference routing policy for server-initiated inference (optional): the
|
|
289
|
+
* model an MCPL server's inference/request runs on. Without one, each such
|
|
290
|
+
* request is refused with -32603 and a message naming this setting; the
|
|
291
|
+
* framework doesn't choose a model for a server. A server's effective
|
|
292
|
+
* grant can still list `inferenceRequest` then, since the grant comes from
|
|
293
|
+
* what the server advertises and `enabledCapabilities`, not from this
|
|
294
|
+
* policy. A policy that names no model for a request's feature set (no
|
|
295
|
+
* override, byFeature or wildcards entry that names one, and an empty
|
|
296
|
+
* `default`) refuses that request the same way, naming the feature set.
|
|
297
|
+
*/
|
|
280
298
|
inferenceRouting?: InferenceRoutingPolicy;
|
|
281
299
|
|
|
282
300
|
/** EventGate config. If omitted, all events trigger inference (unchanged default). */
|