@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.
Files changed (116) hide show
  1. package/dist/src/framework.d.ts +67 -0
  2. package/dist/src/framework.d.ts.map +1 -1
  3. package/dist/src/framework.js +469 -26
  4. package/dist/src/framework.js.map +1 -1
  5. package/dist/src/gate/event-gate.d.ts +44 -1
  6. package/dist/src/gate/event-gate.d.ts.map +1 -1
  7. package/dist/src/gate/event-gate.js +152 -8
  8. package/dist/src/gate/event-gate.js.map +1 -1
  9. package/dist/src/gate/gate-script-worker.d.ts +2 -0
  10. package/dist/src/gate/gate-script-worker.d.ts.map +1 -0
  11. package/dist/src/gate/gate-script-worker.js +69 -0
  12. package/dist/src/gate/gate-script-worker.js.map +1 -0
  13. package/dist/src/gate/gate-script.d.ts +44 -0
  14. package/dist/src/gate/gate-script.d.ts.map +1 -0
  15. package/dist/src/gate/gate-script.js +164 -0
  16. package/dist/src/gate/gate-script.js.map +1 -0
  17. package/dist/src/gate/types.d.ts +18 -3
  18. package/dist/src/gate/types.d.ts.map +1 -1
  19. package/dist/src/mcpl/channel-registry.d.ts +43 -1
  20. package/dist/src/mcpl/channel-registry.d.ts.map +1 -1
  21. package/dist/src/mcpl/channel-registry.js +82 -8
  22. package/dist/src/mcpl/channel-registry.js.map +1 -1
  23. package/dist/src/mcpl/checkpoint-manager.d.ts +7 -0
  24. package/dist/src/mcpl/checkpoint-manager.d.ts.map +1 -1
  25. package/dist/src/mcpl/checkpoint-manager.js +15 -0
  26. package/dist/src/mcpl/checkpoint-manager.js.map +1 -1
  27. package/dist/src/mcpl/push-handler.d.ts.map +1 -1
  28. package/dist/src/mcpl/push-handler.js +1 -0
  29. package/dist/src/mcpl/push-handler.js.map +1 -1
  30. package/dist/src/mcpl/server-connection.d.ts +35 -30
  31. package/dist/src/mcpl/server-connection.d.ts.map +1 -1
  32. package/dist/src/mcpl/server-connection.js +145 -206
  33. package/dist/src/mcpl/server-connection.js.map +1 -1
  34. package/dist/src/mcpl/transport.d.ts +98 -0
  35. package/dist/src/mcpl/transport.d.ts.map +1 -0
  36. package/dist/src/mcpl/transport.js +281 -0
  37. package/dist/src/mcpl/transport.js.map +1 -0
  38. package/dist/src/mcpl/types.d.ts +42 -2
  39. package/dist/src/mcpl/types.d.ts.map +1 -1
  40. package/dist/src/mcpl/types.js.map +1 -1
  41. package/dist/src/module-registry.d.ts +2 -0
  42. package/dist/src/module-registry.d.ts.map +1 -1
  43. package/dist/src/module-registry.js +9 -2
  44. package/dist/src/module-registry.js.map +1 -1
  45. package/dist/src/prose-segments.d.ts +18 -0
  46. package/dist/src/prose-segments.d.ts.map +1 -0
  47. package/dist/src/prose-segments.js +39 -0
  48. package/dist/src/prose-segments.js.map +1 -0
  49. package/dist/src/runtime/liveness-watchdog-worker.d.ts +2 -0
  50. package/dist/src/runtime/liveness-watchdog-worker.d.ts.map +1 -0
  51. package/dist/src/runtime/liveness-watchdog-worker.js +57 -0
  52. package/dist/src/runtime/liveness-watchdog-worker.js.map +1 -0
  53. package/dist/src/runtime/liveness-watchdog.d.ts +36 -0
  54. package/dist/src/runtime/liveness-watchdog.d.ts.map +1 -0
  55. package/dist/src/runtime/liveness-watchdog.js +106 -0
  56. package/dist/src/runtime/liveness-watchdog.js.map +1 -0
  57. package/dist/src/types/agent.d.ts +10 -0
  58. package/dist/src/types/agent.d.ts.map +1 -1
  59. package/dist/src/types/framework.d.ts +14 -0
  60. package/dist/src/types/framework.d.ts.map +1 -1
  61. package/dist/src/types/module.d.ts +12 -0
  62. package/dist/src/types/module.d.ts.map +1 -1
  63. package/dist/test/channel-registry-routing.test.js +108 -1
  64. package/dist/test/channel-registry-routing.test.js.map +1 -1
  65. package/dist/test/checkpoint-attribution.test.d.ts +2 -0
  66. package/dist/test/checkpoint-attribution.test.d.ts.map +1 -0
  67. package/dist/test/checkpoint-attribution.test.js +40 -0
  68. package/dist/test/checkpoint-attribution.test.js.map +1 -0
  69. package/dist/test/event-gate-policy-mutation.test.d.ts +2 -0
  70. package/dist/test/event-gate-policy-mutation.test.d.ts.map +1 -0
  71. package/dist/test/event-gate-policy-mutation.test.js +137 -0
  72. package/dist/test/event-gate-policy-mutation.test.js.map +1 -0
  73. package/dist/test/event-gate-script.test.d.ts +2 -0
  74. package/dist/test/event-gate-script.test.d.ts.map +1 -0
  75. package/dist/test/event-gate-script.test.js +95 -0
  76. package/dist/test/event-gate-script.test.js.map +1 -0
  77. package/dist/test/event-gate-tags.test.d.ts +2 -0
  78. package/dist/test/event-gate-tags.test.d.ts.map +1 -0
  79. package/dist/test/event-gate-tags.test.js +76 -0
  80. package/dist/test/event-gate-tags.test.js.map +1 -0
  81. package/dist/test/liveness-watchdog.test.d.ts +2 -0
  82. package/dist/test/liveness-watchdog.test.d.ts.map +1 -0
  83. package/dist/test/liveness-watchdog.test.js +78 -0
  84. package/dist/test/liveness-watchdog.test.js.map +1 -0
  85. package/dist/test/mcpl-ws-transport.test.d.ts +2 -0
  86. package/dist/test/mcpl-ws-transport.test.d.ts.map +1 -0
  87. package/dist/test/mcpl-ws-transport.test.js +155 -0
  88. package/dist/test/mcpl-ws-transport.test.js.map +1 -0
  89. package/dist/test/prose-segments.test.d.ts +2 -0
  90. package/dist/test/prose-segments.test.d.ts.map +1 -0
  91. package/dist/test/prose-segments.test.js +52 -0
  92. package/dist/test/prose-segments.test.js.map +1 -0
  93. package/dist/test/trunk-channel-routing.test.d.ts +19 -0
  94. package/dist/test/trunk-channel-routing.test.d.ts.map +1 -0
  95. package/dist/test/trunk-channel-routing.test.js +175 -0
  96. package/dist/test/trunk-channel-routing.test.js.map +1 -0
  97. package/dist/tsconfig.tsbuildinfo +1 -1
  98. package/package.json +2 -2
  99. package/src/framework.ts +509 -24
  100. package/src/gate/event-gate.ts +153 -8
  101. package/src/gate/gate-script-worker.ts +73 -0
  102. package/src/gate/gate-script.ts +180 -0
  103. package/src/gate/types.ts +19 -3
  104. package/src/mcpl/channel-registry.ts +112 -8
  105. package/src/mcpl/checkpoint-manager.ts +16 -0
  106. package/src/mcpl/push-handler.ts +1 -0
  107. package/src/mcpl/server-connection.ts +153 -224
  108. package/src/mcpl/transport.ts +307 -0
  109. package/src/mcpl/types.ts +49 -2
  110. package/src/module-registry.ts +13 -2
  111. package/src/prose-segments.ts +42 -0
  112. package/src/runtime/liveness-watchdog-worker.ts +66 -0
  113. package/src/runtime/liveness-watchdog.ts +122 -0
  114. package/src/types/agent.ts +10 -0
  115. package/src/types/framework.ts +15 -0
  116. 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
- if (openChannels.length === 0 && !this.defaultPublishChannel) {
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: from defaultPublishChannel if set
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: same as incoming (reply to last channel)
721
- if (this.defaultPublishChannel) {
807
+ // Default outgoing: the resolved outbound locus (home channel for forks).
808
+ if (outgoing) {
722
809
  context.defaultOutgoing = {
723
- channelId: this.defaultPublishChannel,
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
- const channelId = this.defaultPublishChannel;
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
- return fail(null, 'no locus (defaultPublishChannel null)');
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
  // ==========================================================================
@@ -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
  }