@animalabs/agent-framework 0.5.7 → 0.6.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/dist/src/framework.d.ts +67 -0
- package/dist/src/framework.d.ts.map +1 -1
- package/dist/src/framework.js +469 -26
- package/dist/src/framework.js.map +1 -1
- package/dist/src/gate/event-gate.d.ts +44 -1
- package/dist/src/gate/event-gate.d.ts.map +1 -1
- package/dist/src/gate/event-gate.js +152 -8
- package/dist/src/gate/event-gate.js.map +1 -1
- package/dist/src/gate/gate-script-worker.d.ts +2 -0
- package/dist/src/gate/gate-script-worker.d.ts.map +1 -0
- package/dist/src/gate/gate-script-worker.js +69 -0
- package/dist/src/gate/gate-script-worker.js.map +1 -0
- package/dist/src/gate/gate-script.d.ts +44 -0
- package/dist/src/gate/gate-script.d.ts.map +1 -0
- package/dist/src/gate/gate-script.js +164 -0
- package/dist/src/gate/gate-script.js.map +1 -0
- package/dist/src/gate/types.d.ts +18 -3
- package/dist/src/gate/types.d.ts.map +1 -1
- package/dist/src/mcpl/channel-registry.d.ts +43 -1
- package/dist/src/mcpl/channel-registry.d.ts.map +1 -1
- package/dist/src/mcpl/channel-registry.js +82 -8
- package/dist/src/mcpl/channel-registry.js.map +1 -1
- package/dist/src/mcpl/checkpoint-manager.d.ts +7 -0
- package/dist/src/mcpl/checkpoint-manager.d.ts.map +1 -1
- package/dist/src/mcpl/checkpoint-manager.js +15 -0
- package/dist/src/mcpl/checkpoint-manager.js.map +1 -1
- package/dist/src/mcpl/push-handler.d.ts.map +1 -1
- package/dist/src/mcpl/push-handler.js +1 -0
- package/dist/src/mcpl/push-handler.js.map +1 -1
- package/dist/src/mcpl/server-connection.d.ts +35 -30
- package/dist/src/mcpl/server-connection.d.ts.map +1 -1
- package/dist/src/mcpl/server-connection.js +145 -206
- package/dist/src/mcpl/server-connection.js.map +1 -1
- package/dist/src/mcpl/transport.d.ts +98 -0
- package/dist/src/mcpl/transport.d.ts.map +1 -0
- package/dist/src/mcpl/transport.js +281 -0
- package/dist/src/mcpl/transport.js.map +1 -0
- package/dist/src/mcpl/types.d.ts +42 -2
- 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 +2 -0
- package/dist/src/module-registry.d.ts.map +1 -1
- package/dist/src/module-registry.js +9 -2
- package/dist/src/module-registry.js.map +1 -1
- package/dist/src/prose-segments.d.ts +18 -0
- package/dist/src/prose-segments.d.ts.map +1 -0
- package/dist/src/prose-segments.js +39 -0
- package/dist/src/prose-segments.js.map +1 -0
- package/dist/src/runtime/liveness-watchdog-worker.d.ts +2 -0
- package/dist/src/runtime/liveness-watchdog-worker.d.ts.map +1 -0
- package/dist/src/runtime/liveness-watchdog-worker.js +57 -0
- package/dist/src/runtime/liveness-watchdog-worker.js.map +1 -0
- package/dist/src/runtime/liveness-watchdog.d.ts +36 -0
- package/dist/src/runtime/liveness-watchdog.d.ts.map +1 -0
- package/dist/src/runtime/liveness-watchdog.js +106 -0
- package/dist/src/runtime/liveness-watchdog.js.map +1 -0
- package/dist/src/types/agent.d.ts +10 -0
- package/dist/src/types/agent.d.ts.map +1 -1
- package/dist/src/types/framework.d.ts +14 -0
- package/dist/src/types/framework.d.ts.map +1 -1
- package/dist/src/types/module.d.ts +12 -0
- package/dist/src/types/module.d.ts.map +1 -1
- package/dist/test/channel-registry-routing.test.js +108 -1
- package/dist/test/channel-registry-routing.test.js.map +1 -1
- package/dist/test/checkpoint-attribution.test.d.ts +2 -0
- package/dist/test/checkpoint-attribution.test.d.ts.map +1 -0
- package/dist/test/checkpoint-attribution.test.js +40 -0
- package/dist/test/checkpoint-attribution.test.js.map +1 -0
- package/dist/test/event-gate-policy-mutation.test.d.ts +2 -0
- package/dist/test/event-gate-policy-mutation.test.d.ts.map +1 -0
- package/dist/test/event-gate-policy-mutation.test.js +137 -0
- package/dist/test/event-gate-policy-mutation.test.js.map +1 -0
- package/dist/test/event-gate-script.test.d.ts +2 -0
- package/dist/test/event-gate-script.test.d.ts.map +1 -0
- package/dist/test/event-gate-script.test.js +95 -0
- package/dist/test/event-gate-script.test.js.map +1 -0
- package/dist/test/event-gate-tags.test.d.ts +2 -0
- package/dist/test/event-gate-tags.test.d.ts.map +1 -0
- package/dist/test/event-gate-tags.test.js +76 -0
- package/dist/test/event-gate-tags.test.js.map +1 -0
- package/dist/test/liveness-watchdog.test.d.ts +2 -0
- package/dist/test/liveness-watchdog.test.d.ts.map +1 -0
- package/dist/test/liveness-watchdog.test.js +78 -0
- package/dist/test/liveness-watchdog.test.js.map +1 -0
- package/dist/test/mcpl-ws-transport.test.d.ts +2 -0
- package/dist/test/mcpl-ws-transport.test.d.ts.map +1 -0
- package/dist/test/mcpl-ws-transport.test.js +155 -0
- package/dist/test/mcpl-ws-transport.test.js.map +1 -0
- package/dist/test/prose-segments.test.d.ts +2 -0
- package/dist/test/prose-segments.test.d.ts.map +1 -0
- package/dist/test/prose-segments.test.js +52 -0
- package/dist/test/prose-segments.test.js.map +1 -0
- package/dist/test/trunk-channel-routing.test.d.ts +19 -0
- package/dist/test/trunk-channel-routing.test.d.ts.map +1 -0
- package/dist/test/trunk-channel-routing.test.js +175 -0
- package/dist/test/trunk-channel-routing.test.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/src/framework.ts +509 -24
- package/src/gate/event-gate.ts +153 -8
- package/src/gate/gate-script-worker.ts +73 -0
- package/src/gate/gate-script.ts +180 -0
- package/src/gate/types.ts +19 -3
- package/src/mcpl/channel-registry.ts +112 -8
- package/src/mcpl/checkpoint-manager.ts +16 -0
- package/src/mcpl/push-handler.ts +1 -0
- package/src/mcpl/server-connection.ts +153 -224
- package/src/mcpl/transport.ts +307 -0
- package/src/mcpl/types.ts +49 -2
- package/src/module-registry.ts +13 -2
- package/src/prose-segments.ts +42 -0
- package/src/runtime/liveness-watchdog-worker.ts +66 -0
- package/src/runtime/liveness-watchdog.ts +122 -0
- package/src/types/agent.ts +10 -0
- package/src/types/framework.ts +15 -0
- package/src/types/module.ts +13 -0
|
@@ -234,6 +234,31 @@ interface ChannelRegistryOptions {
|
|
|
234
234
|
reason: string;
|
|
235
235
|
textLen: number;
|
|
236
236
|
}) => void;
|
|
237
|
+
/**
|
|
238
|
+
* Resolve a conversation fork's HOME channel from its agent name. Conversation
|
|
239
|
+
* forks are spawned bound to a single channel (the framework tracks this in
|
|
240
|
+
* `conversationAgentHomes` / `ConversationRouter.channelForAgent`); their
|
|
241
|
+
* plain-text speech must route THERE. The process-global `defaultPublishChannel`
|
|
242
|
+
* tracks only the most-recent inbound across ALL channels, so with one fork per
|
|
243
|
+
* channel running concurrently it misroutes a fork's reply to whichever channel
|
|
244
|
+
* last spoke (item 3). Returns undefined for the trunk/primary agent, which has
|
|
245
|
+
* no home and correctly falls back to the global locus (heartbeats, etc.).
|
|
246
|
+
*/
|
|
247
|
+
homeChannelResolver?: (agentName: string) => string | undefined;
|
|
248
|
+
/**
|
|
249
|
+
* Resolve the channel that triggered an agent's CURRENT inference turn, by
|
|
250
|
+
* agent name. This is the fix for single-TRUNK agents (the only mode
|
|
251
|
+
* connectome-host runs — it never exposes conversation forks). A trunk agent
|
|
252
|
+
* has no fork home, so without this its plain-text speech falls back to the
|
|
253
|
+
* process-global `defaultPublishChannel`, which tracks the most-recent inbound
|
|
254
|
+
* across ALL channels and misroutes a reply to whichever channel last spoke
|
|
255
|
+
* under concurrency (item-3 redux). The framework tracks the triggering
|
|
256
|
+
* channel of the live turn per-agent and exposes it here; resolves to
|
|
257
|
+
* undefined for a heartbeat / no-trigger turn, which correctly keeps the
|
|
258
|
+
* global fallback. Consulted AFTER `homeChannelResolver` (a fork's home always
|
|
259
|
+
* wins), BEFORE `defaultPublishChannel`.
|
|
260
|
+
*/
|
|
261
|
+
activeChannelResolver?: (agentName: string) => string | undefined;
|
|
237
262
|
}
|
|
238
263
|
|
|
239
264
|
// ============================================================================
|
|
@@ -258,6 +283,8 @@ export class ChannelRegistry {
|
|
|
258
283
|
reason: string;
|
|
259
284
|
textLen: number;
|
|
260
285
|
}) => void;
|
|
286
|
+
private homeChannelResolver?: (agentName: string) => string | undefined;
|
|
287
|
+
private activeChannelResolver?: (agentName: string) => string | undefined;
|
|
261
288
|
|
|
262
289
|
/** Registered channels, keyed by `{serverId}:{channelId}`. */
|
|
263
290
|
private channels = new Map<string, ChannelEntry>();
|
|
@@ -304,6 +331,8 @@ export class ChannelRegistry {
|
|
|
304
331
|
this.sendTypingFn = options?.sendTypingFn;
|
|
305
332
|
this.shouldTriggerInference = options?.shouldTriggerInference;
|
|
306
333
|
this.onRouteFailure = options?.onRouteFailure;
|
|
334
|
+
this.homeChannelResolver = options?.homeChannelResolver;
|
|
335
|
+
this.activeChannelResolver = options?.activeChannelResolver;
|
|
307
336
|
}
|
|
308
337
|
|
|
309
338
|
/**
|
|
@@ -484,6 +513,7 @@ export class ChannelRegistry {
|
|
|
484
513
|
messageId: message.messageId,
|
|
485
514
|
threadId: message.threadId,
|
|
486
515
|
author: message.author,
|
|
516
|
+
...(message.tags ? { tags: message.tags } : {}),
|
|
487
517
|
},
|
|
488
518
|
);
|
|
489
519
|
}
|
|
@@ -525,6 +555,50 @@ export class ChannelRegistry {
|
|
|
525
555
|
});
|
|
526
556
|
}
|
|
527
557
|
|
|
558
|
+
/**
|
|
559
|
+
* Ensure a channel is registered AND open so it can serve as an outbound
|
|
560
|
+
* routing locus, lazy-registering it if unseen and opening it if closed.
|
|
561
|
+
*
|
|
562
|
+
* Mirrors the lazy-registration inside handleIncoming(), but for channels
|
|
563
|
+
* that only ever arrive as push/events rather than channels/incoming — the
|
|
564
|
+
* motivating case is Discord DMs, which discord-mcpl forwards via push/event
|
|
565
|
+
* with the channel closed (`channelIsOpen:false`). Such a channel is never
|
|
566
|
+
* registered, so routeSpeech() can't resolve it and the agent's reply is
|
|
567
|
+
* silently dropped even though the inbound message proves the channel is
|
|
568
|
+
* reachable (item-3 redux, DM sub-case). Idempotent: a later authoritative
|
|
569
|
+
* channels/register or channels/changed overwrites this descriptor with the
|
|
570
|
+
* richer one; re-opening an already-open channel is a no-op.
|
|
571
|
+
*/
|
|
572
|
+
ensureChannelRegistered(serverId: string, channelId: string, label?: string): void {
|
|
573
|
+
const existing = this.findChannelEntry(channelId);
|
|
574
|
+
if (existing) {
|
|
575
|
+
if (!existing.open) {
|
|
576
|
+
existing.open = true;
|
|
577
|
+
this.emitTraceFn({ type: 'mcpl:channel-opened', serverId: existing.serverId, channelId });
|
|
578
|
+
}
|
|
579
|
+
return;
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
const key = `${serverId}:${channelId}`;
|
|
583
|
+
this.channels.set(key, {
|
|
584
|
+
serverId,
|
|
585
|
+
descriptor: {
|
|
586
|
+
id: channelId,
|
|
587
|
+
type: serverId,
|
|
588
|
+
label: label ?? channelId,
|
|
589
|
+
direction: 'bidirectional',
|
|
590
|
+
metadata: { lazyRegistered: true },
|
|
591
|
+
},
|
|
592
|
+
open: true,
|
|
593
|
+
});
|
|
594
|
+
this.emitTraceFn({
|
|
595
|
+
type: 'mcpl:channel-lazy-registered',
|
|
596
|
+
serverId,
|
|
597
|
+
channelId,
|
|
598
|
+
label: label ?? channelId,
|
|
599
|
+
});
|
|
600
|
+
}
|
|
601
|
+
|
|
528
602
|
// ==========================================================================
|
|
529
603
|
// Typing Indicator Management
|
|
530
604
|
// ==========================================================================
|
|
@@ -700,15 +774,28 @@ export class ChannelRegistry {
|
|
|
700
774
|
*
|
|
701
775
|
* Returns undefined if no channels are active.
|
|
702
776
|
*/
|
|
703
|
-
buildChannelContext(): ChannelContext | undefined {
|
|
777
|
+
buildChannelContext(agentName?: string): ChannelContext | undefined {
|
|
704
778
|
const openChannels = this.getOpenChannels();
|
|
705
|
-
|
|
779
|
+
|
|
780
|
+
// Resolve the outbound locus the SAME way routeSpeech does, so the agent is
|
|
781
|
+
// told the channel its speech will actually land in: a conversation fork's
|
|
782
|
+
// home channel, else this turn's triggering channel (single trunk agent —
|
|
783
|
+
// item-3 redux), else the global default (heartbeats). Without this, a fork
|
|
784
|
+
// was advertised the global locus but published somewhere else; and a trunk
|
|
785
|
+
// agent was told the wrong channel under concurrency.
|
|
786
|
+
const home = agentName ? this.homeChannelResolver?.(agentName) : undefined;
|
|
787
|
+
const active = agentName ? this.activeChannelResolver?.(agentName) : undefined;
|
|
788
|
+
const outgoing = home ?? active ?? this.defaultPublishChannel;
|
|
789
|
+
|
|
790
|
+
if (openChannels.length === 0 && !outgoing) {
|
|
706
791
|
return undefined;
|
|
707
792
|
}
|
|
708
793
|
|
|
709
794
|
const context: ChannelContext = {};
|
|
710
795
|
|
|
711
|
-
// Incoming:
|
|
796
|
+
// Incoming: the most-recent inbound message (what the agent is replying to).
|
|
797
|
+
// Left process-global — per-channel inbound tracking (the right messageId for
|
|
798
|
+
// a fork's own channel) is a separate concern from the outbound routing fix.
|
|
712
799
|
if (this.defaultPublishChannel && this.defaultPublishMessageId) {
|
|
713
800
|
context.incoming = {
|
|
714
801
|
channelId: this.defaultPublishChannel,
|
|
@@ -717,10 +804,10 @@ export class ChannelRegistry {
|
|
|
717
804
|
};
|
|
718
805
|
}
|
|
719
806
|
|
|
720
|
-
// Default outgoing:
|
|
721
|
-
if (
|
|
807
|
+
// Default outgoing: the resolved outbound locus (home channel for forks).
|
|
808
|
+
if (outgoing) {
|
|
722
809
|
context.defaultOutgoing = {
|
|
723
|
-
channelId:
|
|
810
|
+
channelId: outgoing,
|
|
724
811
|
};
|
|
725
812
|
}
|
|
726
813
|
|
|
@@ -1053,9 +1140,26 @@ export class ChannelRegistry {
|
|
|
1053
1140
|
return null;
|
|
1054
1141
|
};
|
|
1055
1142
|
|
|
1056
|
-
|
|
1143
|
+
// Resolve the outbound locus, in precedence order:
|
|
1144
|
+
// 1. fork HOME channel — a conversation fork always replies in the channel
|
|
1145
|
+
// it was spawned to serve (item 3, forks).
|
|
1146
|
+
// 2. this turn's TRIGGERING channel — a single trunk agent (connectome-
|
|
1147
|
+
// host's only mode) replies in the channel whose message woke THIS
|
|
1148
|
+
// inference, so a concurrent inbound elsewhere can't hijack the reply
|
|
1149
|
+
// (item-3 redux, trunk agents). Also carries DM channels, which arrive
|
|
1150
|
+
// as push/events and never touch `defaultPublishChannel`.
|
|
1151
|
+
// 3. the process-global `defaultPublishChannel` — last resort for turns
|
|
1152
|
+
// with no triggering channel (heartbeats, timers).
|
|
1153
|
+
// Using the global for a fork or a concurrent trunk turn is the item-3 bug:
|
|
1154
|
+
// it tracks the most-recent inbound across ALL channels, so a reply lands
|
|
1155
|
+
// wherever a message last happened to arrive rather than where it belongs.
|
|
1156
|
+
const home = this.homeChannelResolver?.(conversationId);
|
|
1157
|
+
const channelId =
|
|
1158
|
+
home ?? this.activeChannelResolver?.(conversationId) ?? this.defaultPublishChannel;
|
|
1057
1159
|
if (!channelId) {
|
|
1058
|
-
|
|
1160
|
+
// Reached only when the agent has no home, no active triggering channel,
|
|
1161
|
+
// AND no global inbound was ever seen.
|
|
1162
|
+
return fail(null, 'no locus (no home/active channel, defaultPublishChannel null)');
|
|
1059
1163
|
}
|
|
1060
1164
|
|
|
1061
1165
|
const entry = this.findChannelEntry(channelId);
|
|
@@ -368,6 +368,22 @@ export class CheckpointManager {
|
|
|
368
368
|
return null;
|
|
369
369
|
}
|
|
370
370
|
|
|
371
|
+
/**
|
|
372
|
+
* Get the server's host-managed (hostState) feature set. Host-managed state is
|
|
373
|
+
* single-valued by protocol (one unkeyed `state` per tool call), so there is at
|
|
374
|
+
* most one such set — return it so its state is threaded correctly even when the
|
|
375
|
+
* server also declares server-managed (rollback) sets. Returns null if none.
|
|
376
|
+
*/
|
|
377
|
+
getHostManagedFeatureSet(serverId: string): string | null {
|
|
378
|
+
const prefix = `${serverId}:`;
|
|
379
|
+
for (const [key, tree] of this.trees) {
|
|
380
|
+
if (key.startsWith(prefix) && tree.hostState) {
|
|
381
|
+
return key.slice(prefix.length);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
return null;
|
|
385
|
+
}
|
|
386
|
+
|
|
371
387
|
// ==========================================================================
|
|
372
388
|
// Rollback
|
|
373
389
|
// ==========================================================================
|
package/src/mcpl/push-handler.ts
CHANGED
|
@@ -197,6 +197,7 @@ export class PushHandler {
|
|
|
197
197
|
eventId: params.eventId,
|
|
198
198
|
eventType: 'mcpl:push-event',
|
|
199
199
|
...(params.origin ?? {}),
|
|
200
|
+
...(params.tags ? { tags: params.tags } : {}),
|
|
200
201
|
};
|
|
201
202
|
triggerInference = this.shouldTriggerInference(textContent, metadata);
|
|
202
203
|
}
|