@animalabs/agent-framework 0.5.7 → 0.6.2

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 (179) hide show
  1. package/dist/src/agent.d.ts +4 -0
  2. package/dist/src/agent.d.ts.map +1 -1
  3. package/dist/src/agent.js +15 -0
  4. package/dist/src/agent.js.map +1 -1
  5. package/dist/src/framework.d.ts +243 -0
  6. package/dist/src/framework.d.ts.map +1 -1
  7. package/dist/src/framework.js +1378 -114
  8. package/dist/src/framework.js.map +1 -1
  9. package/dist/src/gate/event-gate.d.ts +64 -2
  10. package/dist/src/gate/event-gate.d.ts.map +1 -1
  11. package/dist/src/gate/event-gate.js +212 -12
  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 +18 -3
  22. package/dist/src/gate/types.d.ts.map +1 -1
  23. package/dist/src/index.d.ts +3 -0
  24. package/dist/src/index.d.ts.map +1 -1
  25. package/dist/src/index.js +4 -0
  26. package/dist/src/index.js.map +1 -1
  27. package/dist/src/mcpl/channel-registry.d.ts +60 -2
  28. package/dist/src/mcpl/channel-registry.d.ts.map +1 -1
  29. package/dist/src/mcpl/channel-registry.js +114 -9
  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/push-handler.d.ts.map +1 -1
  36. package/dist/src/mcpl/push-handler.js +5 -0
  37. package/dist/src/mcpl/push-handler.js.map +1 -1
  38. package/dist/src/mcpl/server-connection.d.ts +64 -30
  39. package/dist/src/mcpl/server-connection.d.ts.map +1 -1
  40. package/dist/src/mcpl/server-connection.js +247 -210
  41. package/dist/src/mcpl/server-connection.js.map +1 -1
  42. package/dist/src/mcpl/transport.d.ts +98 -0
  43. package/dist/src/mcpl/transport.d.ts.map +1 -0
  44. package/dist/src/mcpl/transport.js +281 -0
  45. package/dist/src/mcpl/transport.js.map +1 -0
  46. package/dist/src/mcpl/types.d.ts +59 -3
  47. package/dist/src/mcpl/types.d.ts.map +1 -1
  48. package/dist/src/mcpl/types.js.map +1 -1
  49. package/dist/src/module-registry.d.ts +17 -1
  50. package/dist/src/module-registry.d.ts.map +1 -1
  51. package/dist/src/module-registry.js +101 -6
  52. package/dist/src/module-registry.js.map +1 -1
  53. package/dist/src/prose-segments.d.ts +18 -0
  54. package/dist/src/prose-segments.d.ts.map +1 -0
  55. package/dist/src/prose-segments.js +39 -0
  56. package/dist/src/prose-segments.js.map +1 -0
  57. package/dist/src/runtime/liveness-watchdog-worker.d.ts +2 -0
  58. package/dist/src/runtime/liveness-watchdog-worker.d.ts.map +1 -0
  59. package/dist/src/runtime/liveness-watchdog-worker.js +57 -0
  60. package/dist/src/runtime/liveness-watchdog-worker.js.map +1 -0
  61. package/dist/src/runtime/liveness-watchdog.d.ts +36 -0
  62. package/dist/src/runtime/liveness-watchdog.d.ts.map +1 -0
  63. package/dist/src/runtime/liveness-watchdog.js +106 -0
  64. package/dist/src/runtime/liveness-watchdog.js.map +1 -0
  65. package/dist/src/types/agent.d.ts +47 -0
  66. package/dist/src/types/agent.d.ts.map +1 -1
  67. package/dist/src/types/framework.d.ts +14 -0
  68. package/dist/src/types/framework.d.ts.map +1 -1
  69. package/dist/src/types/module.d.ts +58 -0
  70. package/dist/src/types/module.d.ts.map +1 -1
  71. package/dist/src/types/trace.d.ts +42 -0
  72. package/dist/src/types/trace.d.ts.map +1 -1
  73. package/dist/test/agent-cache-ttl.test.d.ts +2 -0
  74. package/dist/test/agent-cache-ttl.test.d.ts.map +1 -0
  75. package/dist/test/agent-cache-ttl.test.js +25 -0
  76. package/dist/test/agent-cache-ttl.test.js.map +1 -0
  77. package/dist/test/channel-registry-routing.test.js +108 -1
  78. package/dist/test/channel-registry-routing.test.js.map +1 -1
  79. package/dist/test/checkpoint-attribution.test.d.ts +2 -0
  80. package/dist/test/checkpoint-attribution.test.d.ts.map +1 -0
  81. package/dist/test/checkpoint-attribution.test.js +40 -0
  82. package/dist/test/checkpoint-attribution.test.js.map +1 -0
  83. package/dist/test/conversation-fork-injection-scoping.test.d.ts +2 -0
  84. package/dist/test/conversation-fork-injection-scoping.test.d.ts.map +1 -0
  85. package/dist/test/conversation-fork-injection-scoping.test.js +184 -0
  86. package/dist/test/conversation-fork-injection-scoping.test.js.map +1 -0
  87. package/dist/test/event-gate-policy-mutation.test.d.ts +2 -0
  88. package/dist/test/event-gate-policy-mutation.test.d.ts.map +1 -0
  89. package/dist/test/event-gate-policy-mutation.test.js +137 -0
  90. package/dist/test/event-gate-policy-mutation.test.js.map +1 -0
  91. package/dist/test/event-gate-script.test.d.ts +2 -0
  92. package/dist/test/event-gate-script.test.d.ts.map +1 -0
  93. package/dist/test/event-gate-script.test.js +95 -0
  94. package/dist/test/event-gate-script.test.js.map +1 -0
  95. package/dist/test/event-gate-tags.test.d.ts +2 -0
  96. package/dist/test/event-gate-tags.test.d.ts.map +1 -0
  97. package/dist/test/event-gate-tags.test.js +76 -0
  98. package/dist/test/event-gate-tags.test.js.map +1 -0
  99. package/dist/test/gather-context-timeout.test.d.ts +2 -0
  100. package/dist/test/gather-context-timeout.test.d.ts.map +1 -0
  101. package/dist/test/gather-context-timeout.test.js +120 -0
  102. package/dist/test/gather-context-timeout.test.js.map +1 -0
  103. package/dist/test/inference-exhaustion-rewind.test.d.ts +2 -0
  104. package/dist/test/inference-exhaustion-rewind.test.d.ts.map +1 -0
  105. package/dist/test/inference-exhaustion-rewind.test.js +181 -0
  106. package/dist/test/inference-exhaustion-rewind.test.js.map +1 -0
  107. package/dist/test/inference-failure-observability.test.js +4 -0
  108. package/dist/test/inference-failure-observability.test.js.map +1 -1
  109. package/dist/test/liveness-watchdog.test.d.ts +2 -0
  110. package/dist/test/liveness-watchdog.test.d.ts.map +1 -0
  111. package/dist/test/liveness-watchdog.test.js +78 -0
  112. package/dist/test/liveness-watchdog.test.js.map +1 -0
  113. package/dist/test/mcpl-reconnect-events.test.d.ts +2 -0
  114. package/dist/test/mcpl-reconnect-events.test.d.ts.map +1 -0
  115. package/dist/test/mcpl-reconnect-events.test.js +119 -0
  116. package/dist/test/mcpl-reconnect-events.test.js.map +1 -0
  117. package/dist/test/mcpl-reconnect-reregistration.test.d.ts +2 -0
  118. package/dist/test/mcpl-reconnect-reregistration.test.d.ts.map +1 -0
  119. package/dist/test/mcpl-reconnect-reregistration.test.js +181 -0
  120. package/dist/test/mcpl-reconnect-reregistration.test.js.map +1 -0
  121. package/dist/test/mcpl-request-timeout.test.d.ts +2 -0
  122. package/dist/test/mcpl-request-timeout.test.d.ts.map +1 -0
  123. package/dist/test/mcpl-request-timeout.test.js +123 -0
  124. package/dist/test/mcpl-request-timeout.test.js.map +1 -0
  125. package/dist/test/mcpl-runtime-lifecycle.test.d.ts +7 -0
  126. package/dist/test/mcpl-runtime-lifecycle.test.d.ts.map +1 -0
  127. package/dist/test/mcpl-runtime-lifecycle.test.js +142 -0
  128. package/dist/test/mcpl-runtime-lifecycle.test.js.map +1 -0
  129. package/dist/test/mcpl-ws-transport.test.d.ts +2 -0
  130. package/dist/test/mcpl-ws-transport.test.d.ts.map +1 -0
  131. package/dist/test/mcpl-ws-transport.test.js +155 -0
  132. package/dist/test/mcpl-ws-transport.test.js.map +1 -0
  133. package/dist/test/prose-segments.test.d.ts +2 -0
  134. package/dist/test/prose-segments.test.d.ts.map +1 -0
  135. package/dist/test/prose-segments.test.js +52 -0
  136. package/dist/test/prose-segments.test.js.map +1 -0
  137. package/dist/test/refusal-rewind.test.d.ts +2 -0
  138. package/dist/test/refusal-rewind.test.d.ts.map +1 -0
  139. package/dist/test/refusal-rewind.test.js +108 -0
  140. package/dist/test/refusal-rewind.test.js.map +1 -0
  141. package/dist/test/state-scaling-e2e.test.d.ts +22 -0
  142. package/dist/test/state-scaling-e2e.test.d.ts.map +1 -0
  143. package/dist/test/state-scaling-e2e.test.js +200 -0
  144. package/dist/test/state-scaling-e2e.test.js.map +1 -0
  145. package/dist/test/state-scaling.test.d.ts +12 -0
  146. package/dist/test/state-scaling.test.d.ts.map +1 -0
  147. package/dist/test/state-scaling.test.js +243 -0
  148. package/dist/test/state-scaling.test.js.map +1 -0
  149. package/dist/test/tool-result-content.test.d.ts +2 -0
  150. package/dist/test/tool-result-content.test.d.ts.map +1 -0
  151. package/dist/test/tool-result-content.test.js +37 -0
  152. package/dist/test/tool-result-content.test.js.map +1 -0
  153. package/dist/test/trunk-channel-routing.test.d.ts +19 -0
  154. package/dist/test/trunk-channel-routing.test.d.ts.map +1 -0
  155. package/dist/test/trunk-channel-routing.test.js +175 -0
  156. package/dist/test/trunk-channel-routing.test.js.map +1 -0
  157. package/dist/tsconfig.tsbuildinfo +1 -1
  158. package/package.json +4 -3
  159. package/src/agent.ts +16 -0
  160. package/src/framework.ts +1576 -121
  161. package/src/gate/event-gate.ts +209 -12
  162. package/src/gate/gate-script-worker.ts +73 -0
  163. package/src/gate/gate-script.ts +180 -0
  164. package/src/gate/types.ts +19 -3
  165. package/src/index.ts +10 -0
  166. package/src/mcpl/channel-registry.ts +143 -8
  167. package/src/mcpl/checkpoint-manager.ts +16 -0
  168. package/src/mcpl/push-handler.ts +5 -0
  169. package/src/mcpl/server-connection.ts +262 -227
  170. package/src/mcpl/transport.ts +307 -0
  171. package/src/mcpl/types.ts +68 -3
  172. package/src/module-registry.ts +126 -6
  173. package/src/prose-segments.ts +42 -0
  174. package/src/runtime/liveness-watchdog-worker.ts +66 -0
  175. package/src/runtime/liveness-watchdog.ts +122 -0
  176. package/src/types/agent.ts +48 -0
  177. package/src/types/framework.ts +15 -0
  178. package/src/types/module.ts +65 -0
  179. package/src/types/trace.ts +51 -0
@@ -0,0 +1,184 @@
1
+ import { describe, it, beforeEach, afterEach } from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { tmpdir } from 'node:os';
4
+ import { mkdtempSync, rmSync } from 'node:fs';
5
+ import { join } from 'node:path';
6
+ import { AgentFramework } from '../src/index.js';
7
+ import { ChannelRegistry } from '../src/mcpl/channel-registry.js';
8
+ import { MockMembrane, createMockResponse } from './helpers/mock-membrane.js';
9
+ const FORK_AGENT = 'conversation-chanA-g1';
10
+ class InjectionModule {
11
+ injections;
12
+ name = 'injection';
13
+ triggerAgents;
14
+ constructor(injections, triggerAgents = []) {
15
+ this.injections = injections;
16
+ this.triggerAgents = triggerAgents;
17
+ }
18
+ async start(_ctx) { }
19
+ async stop() { }
20
+ getTools() { return []; }
21
+ async handleToolCall(_call) {
22
+ return { success: false, error: 'No tools', isError: true };
23
+ }
24
+ async onProcess(_event) {
25
+ return this.triggerAgents.length > 0
26
+ ? { requestInference: this.triggerAgents }
27
+ : {};
28
+ }
29
+ async gatherContext(_agentName) {
30
+ return this.injections;
31
+ }
32
+ }
33
+ function systemInjection(text, namespace) {
34
+ const injection = {
35
+ position: 'system',
36
+ content: [{ type: 'text', text }],
37
+ };
38
+ if (namespace !== undefined) {
39
+ injection.namespace = namespace;
40
+ }
41
+ return injection;
42
+ }
43
+ function frameworkInternals(framework) {
44
+ return framework;
45
+ }
46
+ function installChannelRegistry(framework, channelIds = ['chanA', 'chanB']) {
47
+ const internals = frameworkInternals(framework);
48
+ const registry = new ChannelRegistry({ getServer: () => null }, {}, () => { }, () => { }, {
49
+ homeChannelResolver: (agentName) => internals.conversationAgentHomes.get(agentName),
50
+ });
51
+ for (const channelId of channelIds) {
52
+ registry.ensureChannelRegistered('srv', channelId);
53
+ }
54
+ internals.channelRegistry = registry;
55
+ }
56
+ function bindForkHome(framework, agentName = FORK_AGENT, channelId = 'chanA') {
57
+ frameworkInternals(framework).conversationAgentHomes.set(agentName, channelId);
58
+ }
59
+ function installHookInjections(framework, injections) {
60
+ frameworkInternals(framework).hookOrchestrator = {
61
+ beforeInference: async () => injections,
62
+ };
63
+ }
64
+ function triggerEvent() {
65
+ return {
66
+ type: 'external-message',
67
+ source: 'test',
68
+ content: 'trigger',
69
+ metadata: {},
70
+ };
71
+ }
72
+ function assertContains(text, needle) {
73
+ assert.ok(text?.includes(needle), `expected system prompt to include ${needle}`);
74
+ }
75
+ function assertOmits(text, needle) {
76
+ assert.ok(!text?.includes(needle), `expected system prompt to omit ${needle}`);
77
+ }
78
+ describe('conversation fork injection scoping', () => {
79
+ let tempDir;
80
+ let membrane;
81
+ let framework;
82
+ beforeEach(() => {
83
+ tempDir = mkdtempSync(join(tmpdir(), 'conv-injection-scope-test-'));
84
+ membrane = new MockMembrane();
85
+ framework = null;
86
+ });
87
+ afterEach(async () => {
88
+ await framework?.stop();
89
+ rmSync(tempDir, { recursive: true, force: true });
90
+ });
91
+ async function makeFramework(moduleInjections, triggerAgents) {
92
+ framework = await AgentFramework.create({
93
+ storePath: join(tempDir, 'test.chronicle'),
94
+ membrane: membrane.asMembrane(),
95
+ agents: [
96
+ { name: 'assistant', model: 'test-model', systemPrompt: 'Assistant system.' },
97
+ { name: FORK_AGENT, model: 'test-model', systemPrompt: 'Fork system.' },
98
+ ],
99
+ modules: [new InjectionModule(moduleInjections, triggerAgents)],
100
+ });
101
+ return framework;
102
+ }
103
+ it('scopes module gatherContext injections for conversation forks and keeps hook scoping', async () => {
104
+ membrane.pushResponse(createMockResponse([]));
105
+ const framework = await makeFramework([
106
+ systemInjection('module chanA context', 'chanA'),
107
+ systemInjection('module chanB context', 'chanB'),
108
+ systemInjection('module retrieval context', 'retrieval'),
109
+ systemInjection('module unnamespaced context'),
110
+ ], [FORK_AGENT]);
111
+ installChannelRegistry(framework);
112
+ bindForkHome(framework);
113
+ installHookInjections(framework, [
114
+ systemInjection('hook chanA context', 'chanA'),
115
+ systemInjection('hook chanB context', 'chanB'),
116
+ systemInjection('hook retrieval context', 'retrieval'),
117
+ ]);
118
+ framework.pushEvent(triggerEvent());
119
+ await framework.runUntilIdle();
120
+ assert.equal(membrane.calls.length, 1);
121
+ const system = membrane.calls[0].system;
122
+ assertContains(system, 'module chanA context');
123
+ assertOmits(system, 'module chanB context');
124
+ assertContains(system, 'module retrieval context');
125
+ assertContains(system, 'module unnamespaced context');
126
+ assertContains(system, 'hook chanA context');
127
+ assertOmits(system, 'hook chanB context');
128
+ assertContains(system, 'hook retrieval context');
129
+ });
130
+ it('passes module injections through for non-conversation agents', async () => {
131
+ membrane.pushResponse(createMockResponse([]));
132
+ const framework = await makeFramework([
133
+ systemInjection('module chanA context', 'chanA'),
134
+ systemInjection('module chanB context', 'chanB'),
135
+ ], ['assistant']);
136
+ installChannelRegistry(framework);
137
+ framework.pushEvent(triggerEvent());
138
+ await framework.runUntilIdle();
139
+ assert.equal(membrane.calls.length, 1);
140
+ const system = membrane.calls[0].system;
141
+ assertContains(system, 'module chanA context');
142
+ assertContains(system, 'module chanB context');
143
+ });
144
+ it('scopes full-fidelity previewActivation module injections like real inference', async () => {
145
+ const framework = await makeFramework([
146
+ systemInjection('preview module chanA context', 'chanA'),
147
+ systemInjection('preview module chanB context', 'chanB'),
148
+ ], []);
149
+ installChannelRegistry(framework);
150
+ bindForkHome(framework);
151
+ installHookInjections(framework, [
152
+ systemInjection('preview hook chanA context', 'chanA'),
153
+ systemInjection('preview hook chanB context', 'chanB'),
154
+ ]);
155
+ const request = await framework.previewActivation(FORK_AGENT, { injections: true });
156
+ assertContains(request.system, 'preview module chanA context');
157
+ assertOmits(request.system, 'preview module chanB context');
158
+ assertContains(request.system, 'preview hook chanA context');
159
+ assertOmits(request.system, 'preview hook chanB context');
160
+ assert.equal(membrane.calls.length, 0, 'previewActivation must not call the membrane');
161
+ });
162
+ it('fails open when the channel registry is absent', async () => {
163
+ membrane.pushResponse(createMockResponse([]));
164
+ const framework = await makeFramework([
165
+ systemInjection('module chanB context', 'chanB'),
166
+ ], [FORK_AGENT]);
167
+ bindForkHome(framework);
168
+ framework.pushEvent(triggerEvent());
169
+ await framework.runUntilIdle();
170
+ assert.equal(membrane.calls.length, 1);
171
+ assertContains(membrane.calls[0].system, 'module chanB context');
172
+ });
173
+ it('handles empty module injection lists without throwing', async () => {
174
+ membrane.pushResponse(createMockResponse([]));
175
+ const framework = await makeFramework([], [FORK_AGENT]);
176
+ installChannelRegistry(framework);
177
+ bindForkHome(framework);
178
+ framework.pushEvent(triggerEvent());
179
+ await framework.runUntilIdle();
180
+ assert.equal(membrane.calls.length, 1);
181
+ assert.equal(membrane.calls[0].system, 'Fork system.');
182
+ });
183
+ });
184
+ //# sourceMappingURL=conversation-fork-injection-scoping.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conversation-fork-injection-scoping.test.js","sourceRoot":"","sources":["../../test/conversation-fork-injection-scoping.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":""}
@@ -0,0 +1,95 @@
1
+ import { describe, it, beforeEach, afterEach } from 'node:test';
2
+ import assert from 'node:assert';
3
+ import { existsSync, mkdirSync, writeFileSync, 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-script');
7
+ function makeGate(scriptSrc, config, scriptTimeoutMs = 200) {
8
+ mkdirSync(TMP_DIR, { recursive: true });
9
+ const configPath = join(TMP_DIR, 'gate.json');
10
+ writeFileSync(configPath, JSON.stringify(config));
11
+ if (scriptSrc !== null)
12
+ writeFileSync(join(TMP_DIR, 'gate.js'), scriptSrc);
13
+ return new EventGate({
14
+ configPath,
15
+ scriptTimeoutMs,
16
+ emitTrace: () => { },
17
+ addMessage: () => '',
18
+ requestInference: () => { },
19
+ getAgentNames: () => ['agent'],
20
+ });
21
+ }
22
+ function ev(over) {
23
+ return { content: 'x', eventType: 'mcpl:push-event', serverId: 'discord', channelId: 'c1', tags: [], ...over };
24
+ }
25
+ // The worker imports gate.js asynchronously; wait until the gate reports ready.
26
+ async function waitReady(gate) {
27
+ for (let i = 0; i < 100; i++) {
28
+ if (gate.getStatus().script.ready)
29
+ return;
30
+ await new Promise((r) => setTimeout(r, 10));
31
+ }
32
+ throw new Error('gate.js worker never became ready');
33
+ }
34
+ beforeEach(() => mkdirSync(TMP_DIR, { recursive: true }));
35
+ afterEach(() => { if (existsSync(TMP_DIR))
36
+ rmSync(TMP_DIR, { recursive: true, force: true }); });
37
+ describe('gate.js programmable gate', () => {
38
+ it('script decision takes precedence; null falls through to declarative', async () => {
39
+ const gate = makeGate(`export default (e) => e.channelId === 'vip' ? 'always' : (e.tags.includes('chat:ambient') ? { debounce: 1000 } : null);`, { policies: [{ name: 'mention', match: { tagsAny: ['chat:mention'] }, behavior: 'always' }], default: 'defer' });
40
+ try {
41
+ await waitReady(gate);
42
+ // script says always for vip
43
+ assert.strictEqual(gate.evaluate(ev({ channelId: 'vip' })).policyName, 'gate.js');
44
+ assert.strictEqual(gate.evaluate(ev({ channelId: 'vip' })).trigger, true);
45
+ // script debounces ambient
46
+ const amb = gate.evaluate(ev({ tags: ['chat:ambient'] }));
47
+ assert.strictEqual(amb.policyName, 'gate.js');
48
+ assert.strictEqual(amb.trigger, false);
49
+ // script returns null → declarative 'mention' policy applies
50
+ const men = gate.evaluate(ev({ tags: ['chat:mention'] }));
51
+ assert.strictEqual(men.policyName, 'mention');
52
+ assert.strictEqual(men.trigger, true);
53
+ // script null, no policy → default defer
54
+ assert.strictEqual(gate.evaluate(ev({ tags: [] })).trigger, false);
55
+ }
56
+ finally {
57
+ gate.dispose();
58
+ }
59
+ });
60
+ it('a throwing script falls through and surfaces the error', async () => {
61
+ const gate = makeGate(`export default () => { throw new Error('boom'); };`, { policies: [], default: 'always' });
62
+ try {
63
+ await waitReady(gate);
64
+ const d = gate.evaluate(ev());
65
+ assert.strictEqual(d.trigger, true); // fell through to default 'always'
66
+ assert.match(gate.getStatus().script.lastError ?? '', /boom/);
67
+ }
68
+ finally {
69
+ gate.dispose();
70
+ }
71
+ });
72
+ it('an infinite-loop script times out, falls through, and is recorded', async () => {
73
+ const gate = makeGate(`export default () => { while (true) {} };`, { policies: [], default: 'always' }, 80);
74
+ try {
75
+ await waitReady(gate);
76
+ const d = gate.evaluate(ev());
77
+ assert.strictEqual(d.trigger, true); // timed out → fell through to default
78
+ assert.ok(gate.getStatus().script.timeouts >= 1);
79
+ }
80
+ finally {
81
+ gate.dispose();
82
+ }
83
+ });
84
+ it('no gate.js → script inactive, declarative gate works normally', () => {
85
+ const gate = makeGate(null, { policies: [], default: 'defer' });
86
+ try {
87
+ assert.strictEqual(gate.getStatus().script.active, false);
88
+ assert.strictEqual(gate.evaluate(ev()).trigger, false);
89
+ }
90
+ finally {
91
+ gate.dispose();
92
+ }
93
+ });
94
+ });
95
+ //# sourceMappingURL=event-gate-script.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-gate-script.test.js","sourceRoot":"","sources":["../../test/event-gate-script.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=event-gate-tags.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-gate-tags.test.d.ts","sourceRoot":"","sources":["../../test/event-gate-tags.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,76 @@
1
+ import { describe, it, beforeEach, afterEach } from 'node:test';
2
+ import assert from 'node:assert';
3
+ import { existsSync, mkdirSync, writeFileSync, rmSync } from 'node:fs';
4
+ import { join } from 'node:path';
5
+ import { EventGate } from '../src/gate/event-gate.js';
6
+ // Note: the gate matches the event's *already-expanded* tag set. Implication
7
+ // expansion (expandTags) is the host's job and is tested in mcpl-core-ts.
8
+ const TMP_DIR = join(import.meta.dirname, '../.test-tmp-gate-tags');
9
+ function makeGate(config) {
10
+ mkdirSync(TMP_DIR, { recursive: true });
11
+ const configPath = join(TMP_DIR, 'gate.json');
12
+ writeFileSync(configPath, JSON.stringify(config));
13
+ return new EventGate({
14
+ configPath,
15
+ emitTrace: () => { },
16
+ addMessage: () => '',
17
+ requestInference: () => { },
18
+ getAgentNames: () => ['agent'],
19
+ });
20
+ }
21
+ function ev(tags, overrides) {
22
+ return { content: 'x', eventType: 'mcpl:push-event', serverId: 'portal', channelId: 'c1', tags, ...overrides };
23
+ }
24
+ beforeEach(() => mkdirSync(TMP_DIR, { recursive: true }));
25
+ afterEach(() => { if (existsSync(TMP_DIR))
26
+ rmSync(TMP_DIR, { recursive: true, force: true }); });
27
+ describe('EventGate tag matching', () => {
28
+ it('tagsAny matches if any listed tag present', () => {
29
+ const gate = makeGate({
30
+ policies: [{ name: 'wake', match: { tagsAny: ['chat:mention', 'chat:dm'] }, behavior: 'always' }],
31
+ default: 'skip',
32
+ });
33
+ assert.strictEqual(gate.evaluate(ev(['chat:mention'])).trigger, true);
34
+ assert.strictEqual(gate.evaluate(ev(['chat:dm', 'chat:from-human'])).trigger, true);
35
+ assert.strictEqual(gate.evaluate(ev(['chat:ambient'])).trigger, false);
36
+ assert.strictEqual(gate.evaluate(ev([])).trigger, false);
37
+ });
38
+ it('tagsAll requires every listed tag', () => {
39
+ const gate = makeGate({
40
+ policies: [{ name: 'r', match: { tagsAll: ['chat:reaction', 'chat:to-self'] }, behavior: 'always' }],
41
+ default: 'skip',
42
+ });
43
+ assert.strictEqual(gate.evaluate(ev(['chat:reaction', 'chat:to-self'])).trigger, true);
44
+ assert.strictEqual(gate.evaluate(ev(['chat:reaction'])).trigger, false);
45
+ });
46
+ it('tagsNone excludes', () => {
47
+ const gate = makeGate({
48
+ policies: [{ name: 'amb', match: { tagsAll: ['chat:ambient'], tagsNone: ['chat:from-self'] }, behavior: 'always' }],
49
+ default: 'skip',
50
+ });
51
+ assert.strictEqual(gate.evaluate(ev(['chat:ambient'])).trigger, true);
52
+ assert.strictEqual(gate.evaluate(ev(['chat:ambient', 'chat:from-self'])).trigger, false);
53
+ });
54
+ it('glob patterns match a namespace', () => {
55
+ const gate = makeGate({
56
+ policies: [{ name: 'tg', match: { tagsAny: ['telegram:*'] }, behavior: 'always' }],
57
+ default: 'skip',
58
+ });
59
+ assert.strictEqual(gate.evaluate(ev(['telegram:callback'])).trigger, true);
60
+ assert.strictEqual(gate.evaluate(ev(['discord:slash'])).trigger, false);
61
+ });
62
+ it('first-match-wins: addressed before ambient-debounce', () => {
63
+ const gate = makeGate({
64
+ policies: [
65
+ { name: 'addressed', match: { tagsAny: ['chat:addressed'] }, behavior: 'always' },
66
+ { name: 'ambient', match: { tagsAll: ['chat:ambient'] }, behavior: { debounce: 1000 } },
67
+ ],
68
+ default: 'skip',
69
+ });
70
+ // host pre-expands a mention event to include chat:addressed
71
+ const d = gate.evaluate(ev(['chat:mention', 'chat:addressed']));
72
+ assert.strictEqual(d.policyName, 'addressed');
73
+ assert.strictEqual(d.trigger, true);
74
+ });
75
+ });
76
+ //# sourceMappingURL=event-gate-tags.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-gate-tags.test.js","sourceRoot":"","sources":["../../test/event-gate-tags.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=gather-context-timeout.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gather-context-timeout.test.d.ts","sourceRoot":"","sources":["../../test/gather-context-timeout.test.ts"],"names":[],"mappings":""}