@animalabs/agent-framework 0.5.6 → 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 (177) hide show
  1. package/dist/src/agent.d.ts +20 -4
  2. package/dist/src/agent.d.ts.map +1 -1
  3. package/dist/src/agent.js +45 -14
  4. package/dist/src/agent.js.map +1 -1
  5. package/dist/src/framework.d.ts +220 -5
  6. package/dist/src/framework.d.ts.map +1 -1
  7. package/dist/src/framework.js +1489 -69
  8. package/dist/src/framework.js.map +1 -1
  9. package/dist/src/gate/event-gate.d.ts +71 -1
  10. package/dist/src/gate/event-gate.d.ts.map +1 -1
  11. package/dist/src/gate/event-gate.js +292 -9
  12. package/dist/src/gate/event-gate.js.map +1 -1
  13. package/dist/src/gate/gate-script-worker.d.ts +2 -0
  14. package/dist/src/gate/gate-script-worker.d.ts.map +1 -0
  15. package/dist/src/gate/gate-script-worker.js +69 -0
  16. package/dist/src/gate/gate-script-worker.js.map +1 -0
  17. package/dist/src/gate/gate-script.d.ts +44 -0
  18. package/dist/src/gate/gate-script.d.ts.map +1 -0
  19. package/dist/src/gate/gate-script.js +164 -0
  20. package/dist/src/gate/gate-script.js.map +1 -0
  21. package/dist/src/gate/types.d.ts +35 -3
  22. package/dist/src/gate/types.d.ts.map +1 -1
  23. package/dist/src/index.d.ts +2 -0
  24. package/dist/src/index.d.ts.map +1 -1
  25. package/dist/src/index.js +2 -0
  26. package/dist/src/index.js.map +1 -1
  27. package/dist/src/mcpl/channel-registry.d.ts +93 -1
  28. package/dist/src/mcpl/channel-registry.d.ts.map +1 -1
  29. package/dist/src/mcpl/channel-registry.js +260 -6
  30. package/dist/src/mcpl/channel-registry.js.map +1 -1
  31. package/dist/src/mcpl/checkpoint-manager.d.ts +7 -0
  32. package/dist/src/mcpl/checkpoint-manager.d.ts.map +1 -1
  33. package/dist/src/mcpl/checkpoint-manager.js +15 -0
  34. package/dist/src/mcpl/checkpoint-manager.js.map +1 -1
  35. package/dist/src/mcpl/conversation-router.d.ts +161 -0
  36. package/dist/src/mcpl/conversation-router.d.ts.map +1 -0
  37. package/dist/src/mcpl/conversation-router.js +179 -0
  38. package/dist/src/mcpl/conversation-router.js.map +1 -0
  39. package/dist/src/mcpl/feature-set-manager.d.ts.map +1 -1
  40. package/dist/src/mcpl/feature-set-manager.js +10 -1
  41. package/dist/src/mcpl/feature-set-manager.js.map +1 -1
  42. package/dist/src/mcpl/index.d.ts +2 -0
  43. package/dist/src/mcpl/index.d.ts.map +1 -1
  44. package/dist/src/mcpl/index.js +2 -0
  45. package/dist/src/mcpl/index.js.map +1 -1
  46. package/dist/src/mcpl/push-handler.d.ts.map +1 -1
  47. package/dist/src/mcpl/push-handler.js +1 -0
  48. package/dist/src/mcpl/push-handler.js.map +1 -1
  49. package/dist/src/mcpl/server-connection.d.ts +35 -30
  50. package/dist/src/mcpl/server-connection.d.ts.map +1 -1
  51. package/dist/src/mcpl/server-connection.js +149 -206
  52. package/dist/src/mcpl/server-connection.js.map +1 -1
  53. package/dist/src/mcpl/transport.d.ts +98 -0
  54. package/dist/src/mcpl/transport.d.ts.map +1 -0
  55. package/dist/src/mcpl/transport.js +281 -0
  56. package/dist/src/mcpl/transport.js.map +1 -0
  57. package/dist/src/mcpl/types.d.ts +42 -2
  58. package/dist/src/mcpl/types.d.ts.map +1 -1
  59. package/dist/src/mcpl/types.js.map +1 -1
  60. package/dist/src/module-registry.d.ts +5 -1
  61. package/dist/src/module-registry.d.ts.map +1 -1
  62. package/dist/src/module-registry.js +16 -2
  63. package/dist/src/module-registry.js.map +1 -1
  64. package/dist/src/modules/discord/index.d.ts +10 -0
  65. package/dist/src/modules/discord/index.d.ts.map +1 -1
  66. package/dist/src/modules/discord/index.js +83 -0
  67. package/dist/src/modules/discord/index.js.map +1 -1
  68. package/dist/src/prose-segments.d.ts +18 -0
  69. package/dist/src/prose-segments.d.ts.map +1 -0
  70. package/dist/src/prose-segments.js +39 -0
  71. package/dist/src/prose-segments.js.map +1 -0
  72. package/dist/src/runtime/liveness-watchdog-worker.d.ts +2 -0
  73. package/dist/src/runtime/liveness-watchdog-worker.d.ts.map +1 -0
  74. package/dist/src/runtime/liveness-watchdog-worker.js +57 -0
  75. package/dist/src/runtime/liveness-watchdog-worker.js.map +1 -0
  76. package/dist/src/runtime/liveness-watchdog.d.ts +36 -0
  77. package/dist/src/runtime/liveness-watchdog.d.ts.map +1 -0
  78. package/dist/src/runtime/liveness-watchdog.js +106 -0
  79. package/dist/src/runtime/liveness-watchdog.js.map +1 -0
  80. package/dist/src/tool-result-history.d.ts +17 -0
  81. package/dist/src/tool-result-history.d.ts.map +1 -0
  82. package/dist/src/tool-result-history.js +60 -0
  83. package/dist/src/tool-result-history.js.map +1 -0
  84. package/dist/src/types/agent.d.ts +21 -0
  85. package/dist/src/types/agent.d.ts.map +1 -1
  86. package/dist/src/types/framework.d.ts +22 -0
  87. package/dist/src/types/framework.d.ts.map +1 -1
  88. package/dist/src/types/module.d.ts +27 -0
  89. package/dist/src/types/module.d.ts.map +1 -1
  90. package/dist/src/types/trace.d.ts +28 -0
  91. package/dist/src/types/trace.d.ts.map +1 -1
  92. package/dist/test/channel-registry-routing.test.d.ts +2 -0
  93. package/dist/test/channel-registry-routing.test.d.ts.map +1 -0
  94. package/dist/test/channel-registry-routing.test.js +182 -0
  95. package/dist/test/channel-registry-routing.test.js.map +1 -0
  96. package/dist/test/checkpoint-attribution.test.d.ts +2 -0
  97. package/dist/test/checkpoint-attribution.test.d.ts.map +1 -0
  98. package/dist/test/checkpoint-attribution.test.js +40 -0
  99. package/dist/test/checkpoint-attribution.test.js.map +1 -0
  100. package/dist/test/conversation-router.test.d.ts +2 -0
  101. package/dist/test/conversation-router.test.d.ts.map +1 -0
  102. package/dist/test/conversation-router.test.js +160 -0
  103. package/dist/test/conversation-router.test.js.map +1 -0
  104. package/dist/test/conversation-routing.test.d.ts +7 -0
  105. package/dist/test/conversation-routing.test.d.ts.map +1 -0
  106. package/dist/test/conversation-routing.test.js +203 -0
  107. package/dist/test/conversation-routing.test.js.map +1 -0
  108. package/dist/test/event-gate-policy-mutation.test.d.ts +2 -0
  109. package/dist/test/event-gate-policy-mutation.test.d.ts.map +1 -0
  110. package/dist/test/event-gate-policy-mutation.test.js +137 -0
  111. package/dist/test/event-gate-policy-mutation.test.js.map +1 -0
  112. package/dist/test/event-gate-script.test.d.ts +2 -0
  113. package/dist/test/event-gate-script.test.d.ts.map +1 -0
  114. package/dist/test/event-gate-script.test.js +95 -0
  115. package/dist/test/event-gate-script.test.js.map +1 -0
  116. package/dist/test/event-gate-tags.test.d.ts +2 -0
  117. package/dist/test/event-gate-tags.test.d.ts.map +1 -0
  118. package/dist/test/event-gate-tags.test.js +76 -0
  119. package/dist/test/event-gate-tags.test.js.map +1 -0
  120. package/dist/test/framework.test.js +73 -1
  121. package/dist/test/framework.test.js.map +1 -1
  122. package/dist/test/helpers/mock-membrane.d.ts +41 -0
  123. package/dist/test/helpers/mock-membrane.d.ts.map +1 -0
  124. package/dist/test/helpers/mock-membrane.js +161 -0
  125. package/dist/test/helpers/mock-membrane.js.map +1 -0
  126. package/dist/test/inference-failure-observability.test.d.ts +2 -0
  127. package/dist/test/inference-failure-observability.test.d.ts.map +1 -0
  128. package/dist/test/inference-failure-observability.test.js +75 -0
  129. package/dist/test/inference-failure-observability.test.js.map +1 -0
  130. package/dist/test/liveness-watchdog.test.d.ts +2 -0
  131. package/dist/test/liveness-watchdog.test.d.ts.map +1 -0
  132. package/dist/test/liveness-watchdog.test.js +78 -0
  133. package/dist/test/liveness-watchdog.test.js.map +1 -0
  134. package/dist/test/mcpl-ws-transport.test.d.ts +2 -0
  135. package/dist/test/mcpl-ws-transport.test.d.ts.map +1 -0
  136. package/dist/test/mcpl-ws-transport.test.js +155 -0
  137. package/dist/test/mcpl-ws-transport.test.js.map +1 -0
  138. package/dist/test/prose-segments.test.d.ts +2 -0
  139. package/dist/test/prose-segments.test.d.ts.map +1 -0
  140. package/dist/test/prose-segments.test.js +52 -0
  141. package/dist/test/prose-segments.test.js.map +1 -0
  142. package/dist/test/tool-result-history.test.d.ts +7 -0
  143. package/dist/test/tool-result-history.test.d.ts.map +1 -0
  144. package/dist/test/tool-result-history.test.js +77 -0
  145. package/dist/test/tool-result-history.test.js.map +1 -0
  146. package/dist/test/trunk-channel-routing.test.d.ts +19 -0
  147. package/dist/test/trunk-channel-routing.test.d.ts.map +1 -0
  148. package/dist/test/trunk-channel-routing.test.js +175 -0
  149. package/dist/test/trunk-channel-routing.test.js.map +1 -0
  150. package/dist/tsconfig.tsbuildinfo +1 -1
  151. package/package.json +3 -3
  152. package/src/agent.ts +53 -18
  153. package/src/framework.ts +1685 -71
  154. package/src/gate/event-gate.ts +297 -9
  155. package/src/gate/gate-script-worker.ts +73 -0
  156. package/src/gate/gate-script.ts +180 -0
  157. package/src/gate/types.ts +36 -3
  158. package/src/index.ts +12 -0
  159. package/src/mcpl/channel-registry.ts +331 -6
  160. package/src/mcpl/checkpoint-manager.ts +16 -0
  161. package/src/mcpl/conversation-router.ts +284 -0
  162. package/src/mcpl/feature-set-manager.ts +14 -1
  163. package/src/mcpl/index.ts +12 -0
  164. package/src/mcpl/push-handler.ts +1 -0
  165. package/src/mcpl/server-connection.ts +157 -224
  166. package/src/mcpl/transport.ts +307 -0
  167. package/src/mcpl/types.ts +49 -2
  168. package/src/module-registry.ts +25 -2
  169. package/src/modules/discord/index.ts +93 -0
  170. package/src/prose-segments.ts +42 -0
  171. package/src/runtime/liveness-watchdog-worker.ts +66 -0
  172. package/src/runtime/liveness-watchdog.ts +122 -0
  173. package/src/tool-result-history.ts +57 -0
  174. package/src/types/agent.ts +21 -0
  175. package/src/types/framework.ts +24 -0
  176. package/src/types/module.ts +29 -0
  177. package/src/types/trace.ts +36 -0
@@ -0,0 +1,160 @@
1
+ import { test } from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { ConversationRouter } from '../src/mcpl/conversation-router.js';
4
+ const T0 = 1_750_000_000_000;
5
+ function makeRouter(overrides = {}) {
6
+ return new ConversationRouter({ templateAgent: 'trunk', ...overrides });
7
+ }
8
+ // ---------------------------------------------------------------------------
9
+ // Bind policy
10
+ // ---------------------------------------------------------------------------
11
+ test('DM message binds without a mention (default dm bind: always)', () => {
12
+ const router = makeRouter();
13
+ const decision = router.route({ channelId: 'slack:D1', mentioned: false, kind: 'dm', now: T0 });
14
+ assert.equal(decision.kind, 'spawn');
15
+ if (decision.kind === 'spawn') {
16
+ assert.equal(decision.generation, 1);
17
+ assert.match(decision.agentName, /^conversation-slack-D1-g1$/);
18
+ assert.equal(decision.trigger, true);
19
+ }
20
+ });
21
+ test('channel message without mention stays unbound (default channel bind: mention)', () => {
22
+ const router = makeRouter();
23
+ const decision = router.route({ channelId: 'slack:C1', mentioned: false, kind: 'channel', now: T0 });
24
+ assert.equal(decision.kind, 'unbound');
25
+ });
26
+ test('channel mention spawns a fork', () => {
27
+ const router = makeRouter();
28
+ const decision = router.route({ channelId: 'slack:C1', mentioned: true, kind: 'channel', now: T0 });
29
+ assert.equal(decision.kind, 'spawn');
30
+ });
31
+ test('bind rule never keeps DMs unbound', () => {
32
+ const router = makeRouter({ bind: { dm: 'never' } });
33
+ const decision = router.route({ channelId: 'slack:D1', mentioned: true, kind: 'dm', now: T0 });
34
+ assert.equal(decision.kind, 'unbound');
35
+ });
36
+ // ---------------------------------------------------------------------------
37
+ // Spawn is two-phase: route() proposes, bind() commits
38
+ // ---------------------------------------------------------------------------
39
+ test('spawn decision does not create the binding until bind() is called', () => {
40
+ const router = makeRouter();
41
+ const d1 = router.route({ channelId: 'slack:C1', mentioned: true, kind: 'channel', now: T0 });
42
+ assert.equal(d1.kind, 'spawn');
43
+ assert.equal(router.getBinding('slack:C1'), undefined);
44
+ // Framework failed to spawn → next mention proposes the same generation.
45
+ const d2 = router.route({ channelId: 'slack:C1', mentioned: true, kind: 'channel', now: T0 + 1 });
46
+ assert.equal(d2.kind, 'spawn');
47
+ if (d2.kind === 'spawn')
48
+ assert.equal(d2.generation, 1);
49
+ if (d2.kind === 'spawn')
50
+ router.bind('slack:C1', d2.agentName, d2.generation, T0 + 1);
51
+ const d3 = router.route({ channelId: 'slack:C1', mentioned: false, kind: 'channel', now: T0 + 2 });
52
+ assert.equal(d3.kind, 'existing');
53
+ });
54
+ // ---------------------------------------------------------------------------
55
+ // Trigger policy on bound channels
56
+ // ---------------------------------------------------------------------------
57
+ test('bound channel: non-mentions land without triggering, mentions trigger', () => {
58
+ const router = makeRouter();
59
+ router.bind('slack:C1', 'conversation-slack-C1-g1', 1, T0);
60
+ const ambient = router.route({ channelId: 'slack:C1', mentioned: false, kind: 'channel', now: T0 + 1 });
61
+ assert.equal(ambient.kind, 'existing');
62
+ if (ambient.kind === 'existing')
63
+ assert.equal(ambient.trigger, false);
64
+ const mention = router.route({ channelId: 'slack:C1', mentioned: true, kind: 'channel', now: T0 + 2 });
65
+ assert.equal(mention.kind, 'existing');
66
+ if (mention.kind === 'existing')
67
+ assert.equal(mention.trigger, true);
68
+ });
69
+ test('bound DM: every message triggers', () => {
70
+ const router = makeRouter();
71
+ router.bind('slack:D1', 'conversation-slack-D1-g1', 1, T0);
72
+ const decision = router.route({ channelId: 'slack:D1', mentioned: false, kind: 'dm', now: T0 + 1 });
73
+ assert.equal(decision.kind, 'existing');
74
+ if (decision.kind === 'existing')
75
+ assert.equal(decision.trigger, true);
76
+ });
77
+ // ---------------------------------------------------------------------------
78
+ // TTL + generations
79
+ // ---------------------------------------------------------------------------
80
+ test('expired() respects lastActivity refresh from route()', () => {
81
+ const router = makeRouter({ idleTtlMs: 1000 });
82
+ router.bind('slack:C1', 'conversation-slack-C1-g1', 1, T0);
83
+ // Ambient traffic keeps the binding alive even without triggering.
84
+ router.route({ channelId: 'slack:C1', mentioned: false, kind: 'channel', now: T0 + 900 });
85
+ assert.deepEqual(router.expired(T0 + 1500), []);
86
+ assert.equal(router.expired(T0 + 1901).length, 1);
87
+ });
88
+ test('rebind after expiry gets a fresh generation and agent name', () => {
89
+ const router = makeRouter({ idleTtlMs: 1000 });
90
+ const d1 = router.route({ channelId: 'slack:C1', mentioned: true, kind: 'channel', now: T0 });
91
+ if (d1.kind !== 'spawn')
92
+ assert.fail('expected spawn');
93
+ router.bind('slack:C1', d1.agentName, d1.generation, T0);
94
+ router.unbind('slack:C1'); // framework does this after the closure turn
95
+ const d2 = router.route({ channelId: 'slack:C1', mentioned: true, kind: 'channel', now: T0 + 5000 });
96
+ assert.equal(d2.kind, 'spawn');
97
+ if (d2.kind === 'spawn') {
98
+ assert.equal(d2.generation, 2);
99
+ assert.match(d2.agentName, /-g2$/);
100
+ assert.notEqual(d2.agentName, d1.agentName);
101
+ }
102
+ });
103
+ test('channelForAgent reverse lookup', () => {
104
+ const router = makeRouter();
105
+ router.bind('slack:C1', 'fork-a', 1, T0);
106
+ assert.equal(router.channelForAgent('fork-a'), 'slack:C1');
107
+ assert.equal(router.channelForAgent('fork-b'), undefined);
108
+ });
109
+ // ---------------------------------------------------------------------------
110
+ // Channel classification
111
+ // ---------------------------------------------------------------------------
112
+ test('classifyChannel reads descriptor metadata and message channel_type', () => {
113
+ const im = {
114
+ id: 'slack:D1', type: 'slack', label: 'DM: @alice', direction: 'bidirectional',
115
+ address: {}, metadata: { is_im: true },
116
+ };
117
+ const mpim = {
118
+ id: 'slack:G1', type: 'slack', label: 'Group DM', direction: 'bidirectional',
119
+ address: {}, metadata: { is_mpim: true },
120
+ };
121
+ const channel = {
122
+ id: 'slack:C1', type: 'slack', label: '#general', direction: 'bidirectional',
123
+ address: {}, metadata: { is_member: true },
124
+ };
125
+ assert.equal(ConversationRouter.classifyChannel(im), 'dm');
126
+ assert.equal(ConversationRouter.classifyChannel(mpim), 'groupDm');
127
+ assert.equal(ConversationRouter.classifyChannel(channel), 'channel');
128
+ assert.equal(ConversationRouter.classifyChannel(undefined, { channel_type: 'im' }), 'dm');
129
+ assert.equal(ConversationRouter.classifyChannel(undefined, { channel_type: 'mpim' }), 'groupDm');
130
+ assert.equal(ConversationRouter.classifyChannel(undefined, { channel_type: 'channel' }), 'channel');
131
+ assert.equal(ConversationRouter.classifyChannel(undefined, undefined), 'channel');
132
+ });
133
+ test('group DMs bind on any message but trigger only on mention by default', () => {
134
+ const router = new ConversationRouter({ templateAgent: 'main' });
135
+ const ambient = router.route({ channelId: 'slack:G1', mentioned: false, kind: 'groupDm', now: T0 });
136
+ assert.equal(ambient.kind, 'spawn', 'group DM binds without a mention');
137
+ assert.equal(ambient.trigger, false, 'no firehose replies in group DMs');
138
+ const mention = router.route({ channelId: 'slack:G2', mentioned: true, kind: 'groupDm', now: T0 });
139
+ assert.equal(mention.kind, 'spawn');
140
+ assert.equal(mention.trigger, true, 'mention triggers');
141
+ });
142
+ test('generation counters export/hydrate round-trips and never regresses', () => {
143
+ const router = new ConversationRouter({ templateAgent: 'main' });
144
+ router.bind('slack:C1', 'conversation-slack-C1-g3', 3, T0);
145
+ router.bind('slack:D1', 'conversation-slack-D1-g1', 1, T0);
146
+ const exported = router.exportGenerations();
147
+ assert.deepEqual(exported, { 'slack:C1': 3, 'slack:D1': 1 });
148
+ const restored = new ConversationRouter({ templateAgent: 'main' });
149
+ restored.hydrateGenerations(exported);
150
+ const decision = restored.route({ channelId: 'slack:C1', mentioned: true, kind: 'channel', now: T0 });
151
+ assert.equal(decision.kind, 'spawn');
152
+ assert.equal(decision.generation, 4, 'restart must not reuse generation names');
153
+ // Hydration never regresses a counter that advanced in the meantime.
154
+ restored.bind('slack:D1', 'conversation-slack-D1-g5', 5, T0);
155
+ restored.hydrateGenerations(exported);
156
+ const d1 = restored.route({ channelId: 'slack:D2', mentioned: false, kind: 'dm', now: T0 });
157
+ assert.equal(d1.kind, 'spawn', 'sanity');
158
+ assert.equal(restored.exportGenerations()['slack:D1'], 5);
159
+ });
160
+ //# sourceMappingURL=conversation-router.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conversation-router.test.js","sourceRoot":"","sources":["../../test/conversation-router.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Integration tests for per-channel conversation routing: incoming MCPL
3
+ * channel messages spawn/route to fork agents instead of the primary
4
+ * conversation when FrameworkConfig.conversations is set.
5
+ */
6
+ export {};
7
+ //# sourceMappingURL=conversation-routing.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conversation-routing.test.d.ts","sourceRoot":"","sources":["../../test/conversation-routing.test.ts"],"names":[],"mappings":"AAAA;;;;GAIG"}
@@ -0,0 +1,203 @@
1
+ /**
2
+ * Integration tests for per-channel conversation routing: incoming MCPL
3
+ * channel messages spawn/route to fork agents instead of the primary
4
+ * conversation when FrameworkConfig.conversations is set.
5
+ */
6
+ import { describe, it, beforeEach, afterEach } from 'node:test';
7
+ import assert from 'node:assert/strict';
8
+ import { tmpdir } from 'node:os';
9
+ import { mkdtempSync, rmSync } from 'node:fs';
10
+ import { join } from 'node:path';
11
+ import { AgentFramework } from '../src/index.js';
12
+ import { MockMembrane, createMockResponse } from './helpers/mock-membrane.js';
13
+ function incomingEvent(overrides) {
14
+ return {
15
+ type: 'mcpl:channel-incoming',
16
+ serverId: 'srv',
17
+ channelId: overrides.channelId,
18
+ messageId: overrides.messageId ?? `m-${Math.random().toString(36).slice(2)}`,
19
+ author: { id: 'U1', name: 'alice' },
20
+ content: [{ type: 'text', text: overrides.text }],
21
+ timestamp: new Date().toISOString(),
22
+ metadata: {
23
+ mentioned: overrides.mentioned ?? false,
24
+ ...(overrides.channelType ? { channel_type: overrides.channelType } : {}),
25
+ },
26
+ triggerInference: true,
27
+ };
28
+ }
29
+ describe('Conversation routing', () => {
30
+ let tempDir;
31
+ let membrane;
32
+ beforeEach(() => {
33
+ tempDir = mkdtempSync(join(tmpdir(), 'conv-routing-test-'));
34
+ membrane = new MockMembrane();
35
+ });
36
+ afterEach(() => {
37
+ rmSync(tempDir, { recursive: true, force: true });
38
+ });
39
+ async function makeFramework(conversations = {}, storeFile = 'test.chronicle') {
40
+ return AgentFramework.create({
41
+ storePath: join(tempDir, storeFile),
42
+ membrane: membrane.asMembrane(),
43
+ agents: [
44
+ { name: 'trunk', model: 'test-model', systemPrompt: 'You are the trunk.' },
45
+ ],
46
+ modules: [],
47
+ conversations: { templateAgent: 'trunk', ...conversations },
48
+ });
49
+ }
50
+ /** Make a binding look idle past the TTL and re-arm the sweep throttle —
51
+ * deterministic expiry instead of racing a 1ms TTL against the wall clock. */
52
+ function forceExpiry(framework, channelId, idleTtlMs) {
53
+ const binding = framework.getConversationRouter().getBinding(channelId);
54
+ assert.ok(binding, `binding for ${channelId} should exist before forcing expiry`);
55
+ binding.lastActivity = Date.now() - idleTtlMs - 1_000;
56
+ framework.lastConversationSweep = 0;
57
+ }
58
+ it('rejects an unknown template agent at creation', async () => {
59
+ await assert.rejects(() => AgentFramework.create({
60
+ storePath: join(tempDir, 'test.chronicle'),
61
+ membrane: membrane.asMembrane(),
62
+ agents: [{ name: 'trunk', model: 'test-model', systemPrompt: 'x' }],
63
+ modules: [],
64
+ conversations: { templateAgent: 'nope' },
65
+ }), /templateAgent "nope"/);
66
+ });
67
+ it('DM message spawns a fork, routes the message there, and triggers inference', async () => {
68
+ membrane.pushResponse(createMockResponse([{ type: 'text', text: 'Hi alice!' }]));
69
+ const framework = await makeFramework();
70
+ framework.pushEvent(incomingEvent({
71
+ channelId: 'slack:D1', text: 'hello there', channelType: 'im',
72
+ }));
73
+ await framework.runUntilIdle();
74
+ const fork = framework.getAgent('conversation-slack-D1-g1');
75
+ assert.ok(fork, 'fork agent should exist');
76
+ // Message landed in the fork's context, not the trunk's.
77
+ const { messages: forkMessages } = await fork.getContextManager().compile();
78
+ assert.ok(forkMessages.some((m) => JSON.stringify(m.content).includes('hello there')), 'fork context should contain the incoming message');
79
+ const trunk = framework.getAgent('trunk');
80
+ const { messages: trunkMessages } = await trunk.getContextManager().compile();
81
+ assert.ok(!trunkMessages.some((m) => JSON.stringify(m.content).includes('hello there')), 'trunk context must not receive routed messages');
82
+ // Inference ran for the fork.
83
+ assert.ok(membrane.calls.length >= 1, 'inference should have been triggered');
84
+ // Binding is live.
85
+ const router = framework.getConversationRouter();
86
+ assert.equal(router.getBinding('slack:D1')?.agentName, 'conversation-slack-D1-g1');
87
+ await framework.stop();
88
+ });
89
+ it('channel message without mention is dropped; mention spawns', async () => {
90
+ membrane.pushResponse(createMockResponse([{ type: 'text', text: 'On it.' }]));
91
+ const framework = await makeFramework();
92
+ framework.pushEvent(incomingEvent({ channelId: 'slack:C1', text: 'just chatting' }));
93
+ await framework.runUntilIdle();
94
+ assert.equal(framework.getAgent('conversation-slack-C1-g1'), null, 'no fork without mention');
95
+ assert.equal(membrane.calls.length, 0, 'no inference for unrouted messages');
96
+ framework.pushEvent(incomingEvent({ channelId: 'slack:C1', text: 'bot, help', mentioned: true }));
97
+ await framework.runUntilIdle();
98
+ assert.ok(framework.getAgent('conversation-slack-C1-g1'), 'mention spawns fork');
99
+ await framework.stop();
100
+ });
101
+ it('non-mention on a bound channel lands in context without triggering', async () => {
102
+ membrane.pushResponse(createMockResponse([{ type: 'text', text: 'ack' }]));
103
+ const framework = await makeFramework();
104
+ framework.pushEvent(incomingEvent({ channelId: 'slack:C1', text: 'first', mentioned: true }));
105
+ await framework.runUntilIdle();
106
+ const callsAfterSpawn = membrane.calls.length;
107
+ framework.pushEvent(incomingEvent({ channelId: 'slack:C1', text: 'ambient detail' }));
108
+ await framework.runUntilIdle();
109
+ const fork = framework.getAgent('conversation-slack-C1-g1');
110
+ const { messages } = await fork.getContextManager().compile();
111
+ assert.ok(messages.some((m) => JSON.stringify(m.content).includes('ambient detail')), 'ambient message should land in fork context');
112
+ assert.equal(membrane.calls.length, callsAfterSpawn, 'ambient message must not trigger inference');
113
+ await framework.stop();
114
+ });
115
+ it('fork inherits the template context', async () => {
116
+ membrane.pushResponse(createMockResponse([{ type: 'text', text: 'ok' }]));
117
+ const framework = await makeFramework();
118
+ // Seed the trunk (primary agent) with handbook-like content.
119
+ framework.getAgent('trunk').getContextManager().addMessage('user', [
120
+ { type: 'text', text: 'HANDBOOK: always check the logs first.' },
121
+ ]);
122
+ framework.pushEvent(incomingEvent({ channelId: 'slack:D2', text: 'hi', channelType: 'im' }));
123
+ await framework.runUntilIdle();
124
+ const fork = framework.getAgent('conversation-slack-D2-g1');
125
+ const { messages } = await fork.getContextManager().compile();
126
+ assert.ok(messages.some((m) => JSON.stringify(m.content).includes('HANDBOOK')), 'fork should inherit trunk context');
127
+ await framework.stop();
128
+ });
129
+ it('idle TTL runs a closure turn, unbinds, disposes the fork, and the next message spawns g2', async () => {
130
+ const IDLE_TTL_MS = 60_000;
131
+ membrane.pushResponse(createMockResponse([{ type: 'text', text: 'hello!' }]));
132
+ const framework = await makeFramework({ idleTtlMs: IDLE_TTL_MS });
133
+ framework.pushEvent(incomingEvent({ channelId: 'slack:D3', text: 'hi', channelType: 'im' }));
134
+ await framework.runUntilIdle();
135
+ const router = framework.getConversationRouter();
136
+ assert.ok(router.getBinding('slack:D3'), 'spawn should leave a live binding');
137
+ const g1 = framework.getAgent('conversation-slack-D3-g1');
138
+ assert.ok(g1, 'g1 fork should exist after spawn');
139
+ // Force expiry deterministically and provide the closure-turn response.
140
+ forceExpiry(framework, 'slack:D3', IDLE_TTL_MS);
141
+ membrane.pushResponse(createMockResponse([{ type: 'text', text: 'Final report posted.' }]));
142
+ // Nudge the loop so the sweep runs.
143
+ framework.pushEvent({ type: 'external-message', source: 'test', content: 'tick', metadata: {} });
144
+ await framework.runUntilIdle();
145
+ assert.equal(router.getBinding('slack:D3'), undefined, 'binding should be gone after TTL');
146
+ const { messages } = await g1.getContextManager().compile();
147
+ assert.ok(messages.some((m) => JSON.stringify(m.content).includes('engagement is closing')), 'closure prompt should be in the fork context');
148
+ assert.equal(framework.getAgent('conversation-slack-D3-g1'), null, 'closed fork should be disposed once its closure turn finished');
149
+ // Next DM spawns a fresh generation.
150
+ membrane.pushResponse(createMockResponse([{ type: 'text', text: 'hi again' }]));
151
+ framework.pushEvent(incomingEvent({ channelId: 'slack:D3', text: 'back again', channelType: 'im' }));
152
+ await framework.runUntilIdle();
153
+ assert.ok(framework.getAgent('conversation-slack-D3-g2'), 'rebind spawns generation 2');
154
+ await framework.stop();
155
+ });
156
+ it('restart does not reuse generation names or re-seed an existing namespace', async () => {
157
+ membrane.pushResponse(createMockResponse([{ type: 'text', text: 'noted' }]));
158
+ const fw1 = await makeFramework({}, 'restart.chronicle');
159
+ fw1.getAgent('trunk').getContextManager().addMessage('user', [
160
+ { type: 'text', text: 'HANDBOOK-V1: always check the logs first.' },
161
+ ]);
162
+ fw1.pushEvent(incomingEvent({ channelId: 'slack:D9', text: 'case one', channelType: 'im' }));
163
+ await fw1.runUntilIdle();
164
+ assert.ok(fw1.getAgent('conversation-slack-D9-g1'), 'first engagement spawns g1');
165
+ await fw1.stop();
166
+ // Restart: same store, fresh framework. The generation counter must come
167
+ // back from Chronicle so the next engagement is g2 in a fresh namespace.
168
+ membrane.pushResponse(createMockResponse([{ type: 'text', text: 'fresh' }]));
169
+ const fw2 = await makeFramework({}, 'restart.chronicle');
170
+ fw2.pushEvent(incomingEvent({ channelId: 'slack:D9', text: 'case two', channelType: 'im' }));
171
+ await fw2.runUntilIdle();
172
+ assert.equal(fw2.getAgent('conversation-slack-D9-g1'), null, 'g1 must not be resurrected');
173
+ const g2 = fw2.getAgent('conversation-slack-D9-g2');
174
+ assert.ok(g2, 'restart spawns the next generation, not generation 1 again');
175
+ const { messages } = await g2.getContextManager().compile();
176
+ const handbookCopies = messages.filter((m) => JSON.stringify(m.content).includes('HANDBOOK-V1')).length;
177
+ assert.equal(handbookCopies, 1, 'fresh namespace is seeded with exactly one template copy');
178
+ assert.ok(!messages.some((m) => JSON.stringify(m.content).includes('case one')), 'a new generation must not inherit the previous engagement history');
179
+ await fw2.stop();
180
+ });
181
+ it('forks cannot open channels or close foreign ones', async () => {
182
+ membrane.pushResponse(createMockResponse([{ type: 'text', text: 'hi' }]));
183
+ const framework = await makeFramework();
184
+ framework.pushEvent(incomingEvent({ channelId: 'slack:C7', text: 'bot, help', mentioned: true }));
185
+ await framework.runUntilIdle();
186
+ const forkName = 'conversation-slack-C7-g1';
187
+ assert.ok(framework.getAgent(forkName), 'fork should exist');
188
+ const failures = [];
189
+ framework.onTrace((e) => {
190
+ if (e.type === 'tool:failed') {
191
+ failures.push({ tool: e.tool, error: e.error });
192
+ }
193
+ });
194
+ const fw = framework;
195
+ fw.dispatchChannelToolCall(forkName, { id: 't1', name: 'channel_open', input: { channelId: 'slack:C8' } });
196
+ fw.dispatchChannelToolCall(forkName, { id: 't2', name: 'channel_close', input: { channelId: 'slack:C8' } });
197
+ assert.equal(failures.length, 2, 'both foreign channel operations should be rejected');
198
+ assert.ok(failures[0].error.includes('cannot open channels'), 'channel_open is rejected outright');
199
+ assert.ok(failures[1].error.includes('closing slack:C8 is not allowed'), 'foreign channel_close is rejected');
200
+ await framework.stop();
201
+ });
202
+ });
203
+ //# sourceMappingURL=conversation-routing.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conversation-routing.test.js","sourceRoot":"","sources":["../../test/conversation-routing.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=event-gate-policy-mutation.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-gate-policy-mutation.test.d.ts","sourceRoot":"","sources":["../../test/event-gate-policy-mutation.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,137 @@
1
+ import { describe, it, beforeEach, afterEach } from 'node:test';
2
+ import assert from 'node:assert';
3
+ import { existsSync, mkdirSync, writeFileSync, readFileSync, rmSync } from 'node:fs';
4
+ import { join } from 'node:path';
5
+ import { EventGate } from '../src/gate/event-gate.js';
6
+ const TMP_DIR = join(import.meta.dirname, '../.test-tmp-gate-mutation');
7
+ const CONFIG_PATH = join(TMP_DIR, 'gate.json');
8
+ function makeGate(config, onInference) {
9
+ mkdirSync(TMP_DIR, { recursive: true });
10
+ writeFileSync(CONFIG_PATH, JSON.stringify(config));
11
+ return new EventGate({
12
+ configPath: CONFIG_PATH,
13
+ emitTrace: () => { },
14
+ addMessage: () => '',
15
+ requestInference: (agentName, reason, source) => onInference?.({ agentName, reason, source }),
16
+ getAgentNames: () => ['agent'],
17
+ });
18
+ }
19
+ function readConfig() {
20
+ return JSON.parse(readFileSync(CONFIG_PATH, 'utf-8'));
21
+ }
22
+ function ev(tags, overrides) {
23
+ return { content: 'x', eventType: 'mcpl:channel-incoming', serverId: 'discord', channelId: 'c1', tags, ...overrides };
24
+ }
25
+ beforeEach(() => mkdirSync(TMP_DIR, { recursive: true }));
26
+ afterEach(() => { if (existsSync(TMP_DIR))
27
+ rmSync(TMP_DIR, { recursive: true, force: true }); });
28
+ describe('EventGate.addPolicy / removePolicy (wake_add_rule / wake_remove_rule)', () => {
29
+ it('adds a policy that takes effect immediately and is persisted to gate.json', () => {
30
+ const gate = makeGate({ policies: [], default: 'defer' });
31
+ // Before: an ambient event falls through to the "defer" default.
32
+ assert.strictEqual(gate.evaluate(ev(['chat:ambient'])).trigger, false);
33
+ gate.addPolicy({ name: 'mentions', match: { tagsAny: ['chat:mention'] }, behavior: 'always' });
34
+ // Live effect without any reload.
35
+ assert.strictEqual(gate.evaluate(ev(['chat:mention'])).trigger, true);
36
+ // Persisted to disk in canonical shape.
37
+ const onDisk = readConfig();
38
+ assert.deepStrictEqual(onDisk.policies.map(p => p.name), ['mentions']);
39
+ assert.strictEqual(onDisk.policies[0].behavior, 'always');
40
+ });
41
+ it('upserts (replaces in place) when a policy of the same name already exists', () => {
42
+ const gate = makeGate({
43
+ policies: [
44
+ { name: 'a', match: { tagsAny: ['chat:dm'] }, behavior: 'always' },
45
+ { name: 'busy', match: { channel: 'c1' }, behavior: 'always' },
46
+ { name: 'b', match: { tagsAny: ['chat:mention'] }, behavior: 'always' },
47
+ ],
48
+ default: 'defer',
49
+ });
50
+ gate.addPolicy({ name: 'busy', match: { channel: 'c1' }, behavior: { debounce: 5000 } });
51
+ const names = readConfig().policies.map(p => p.name);
52
+ assert.deepStrictEqual(names, ['a', 'busy', 'b'], 'order preserved, no duplicate');
53
+ const busy = readConfig().policies.find(p => p.name === 'busy');
54
+ assert.deepStrictEqual(busy.behavior, { debounce: 5000 }, 'behavior replaced');
55
+ });
56
+ it('prepend places a wake rule ahead of a broad rule (first match wins)', () => {
57
+ const gate = makeGate({
58
+ policies: [{ name: 'firehose', match: { channel: 'c1' }, behavior: 'defer' }],
59
+ default: 'defer',
60
+ });
61
+ // Without the wake rule, a mention in c1 is deferred by the broad rule.
62
+ assert.strictEqual(gate.evaluate(ev(['chat:mention'])).trigger, false);
63
+ gate.addPolicy({ name: 'mentions', match: { tagsAny: ['chat:mention'] }, behavior: 'always' }, { position: 'prepend' });
64
+ const decision = gate.evaluate(ev(['chat:mention']));
65
+ assert.strictEqual(decision.trigger, true);
66
+ assert.strictEqual(decision.policyName, 'mentions');
67
+ assert.deepStrictEqual(readConfig().policies.map(p => p.name), ['mentions', 'firehose']);
68
+ });
69
+ it('rejects an invalid policy and writes nothing', () => {
70
+ const gate = makeGate({ policies: [{ name: 'keep', match: {}, behavior: 'always' }], default: 'defer' });
71
+ assert.throws(() => gate.addPolicy({ name: 'bad', behavior: { debounce: -5 } }), /debounce/);
72
+ assert.throws(() => gate.addPolicy({ match: {}, behavior: 'always' }), /name/);
73
+ // Original config untouched.
74
+ assert.deepStrictEqual(readConfig().policies.map(p => p.name), ['keep']);
75
+ });
76
+ it('removePolicy removes and persists; returns false for an unknown name', () => {
77
+ const gate = makeGate({
78
+ policies: [
79
+ { name: 'x', match: { tagsAny: ['chat:mention'] }, behavior: 'always' },
80
+ { name: 'y', match: {}, behavior: 'defer' },
81
+ ],
82
+ default: 'defer',
83
+ });
84
+ assert.strictEqual(gate.removePolicy('x'), true);
85
+ assert.strictEqual(gate.evaluate(ev(['chat:mention'])).trigger, false, 'rule no longer applies');
86
+ assert.deepStrictEqual(readConfig().policies.map(p => p.name), ['y']);
87
+ assert.strictEqual(gate.removePolicy('nope'), false);
88
+ assert.deepStrictEqual(readConfig().policies.map(p => p.name), ['y'], 'unchanged on miss');
89
+ });
90
+ it('persisted config is valid and reloadable by a fresh gate', () => {
91
+ const gate = makeGate({ policies: [], default: 'defer' });
92
+ gate.addPolicy({ name: 'r', match: { source: 'discord', channel: 'c1', tagsAny: ['chat:ambient'] }, behavior: { debounce: 1000 } });
93
+ const fresh = new EventGate({
94
+ configPath: CONFIG_PATH,
95
+ emitTrace: () => { },
96
+ addMessage: () => '',
97
+ requestInference: () => { },
98
+ getAgentNames: () => ['agent'],
99
+ });
100
+ assert.deepStrictEqual(fresh.getStatus().policies.map(p => p.name), ['r']);
101
+ });
102
+ it('listPolicyNames reflects the freshest on-disk view', () => {
103
+ const gate = makeGate({ policies: [{ name: 'a', match: {}, behavior: 'always' }], default: 'defer' });
104
+ gate.addPolicy({ name: 'b', match: {}, behavior: 'defer' });
105
+ assert.deepStrictEqual(gate.listPolicyNames(), ['a', 'b']);
106
+ });
107
+ });
108
+ describe('Item 5 gate mechanism: every-message-debounced on chat:ambient', () => {
109
+ it('debounces ambient messages in a channel and fires one batched wake after the window', async () => {
110
+ const inferences = [];
111
+ const gate = makeGate({ policies: [], default: 'defer' }, (c) => inferences.push(c));
112
+ // The gate half of item 5's "debounced mode": a per-channel debounce policy
113
+ // keyed on the ambient tag. (subscribe_channel + subscription-gc "off" are
114
+ // the host-side other two thirds — see the B2 report.)
115
+ gate.addPolicy({
116
+ name: 'ambient-debounced:c1',
117
+ match: { source: 'discord', channel: 'c1', tagsAny: ['chat:ambient'] },
118
+ behavior: { debounce: 120 }, // just above MIN_DEBOUNCE_MS for a fast test
119
+ });
120
+ // A burst of ambient messages: each matches, none triggers immediately.
121
+ for (let i = 0; i < 3; i++) {
122
+ const d = gate.evaluate(ev(['chat:ambient'], { content: `msg ${i}` }));
123
+ assert.strictEqual(d.trigger, false, 'ambient is batched, not an immediate wake');
124
+ assert.strictEqual(d.policyName, 'ambient-debounced:c1');
125
+ }
126
+ // A mention in the same channel is NOT ambient → falls through to default (defer here).
127
+ assert.strictEqual(gate.evaluate(ev(['chat:mention'])).trigger, false);
128
+ assert.strictEqual(inferences.length, 0, 'no wake yet — window still open');
129
+ await new Promise((r) => setTimeout(r, 200));
130
+ assert.strictEqual(inferences.length, 1, 'exactly one batched wake after the window settles');
131
+ // Status shows the debounce policy with its match count.
132
+ const status = gate.getStatus();
133
+ const p = status.policies.find(s => s.name === 'ambient-debounced:c1');
134
+ assert.strictEqual(p.matchCount, 3);
135
+ });
136
+ });
137
+ //# sourceMappingURL=event-gate-policy-mutation.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-gate-policy-mutation.test.js","sourceRoot":"","sources":["../../test/event-gate-policy-mutation.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=event-gate-script.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-gate-script.test.d.ts","sourceRoot":"","sources":["../../test/event-gate-script.test.ts"],"names":[],"mappings":""}