@animalabs/agent-framework 0.1.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 (212) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +233 -0
  3. package/dist/src/agent.d.ts +130 -0
  4. package/dist/src/agent.d.ts.map +1 -0
  5. package/dist/src/agent.js +406 -0
  6. package/dist/src/agent.js.map +1 -0
  7. package/dist/src/api/mcp-server.d.ts +37 -0
  8. package/dist/src/api/mcp-server.d.ts.map +1 -0
  9. package/dist/src/api/mcp-server.js +598 -0
  10. package/dist/src/api/mcp-server.js.map +1 -0
  11. package/dist/src/api/server.d.ts +83 -0
  12. package/dist/src/api/server.d.ts.map +1 -0
  13. package/dist/src/api/server.js +785 -0
  14. package/dist/src/api/server.js.map +1 -0
  15. package/dist/src/api/types.d.ts +395 -0
  16. package/dist/src/api/types.d.ts.map +1 -0
  17. package/dist/src/api/types.js +5 -0
  18. package/dist/src/api/types.js.map +1 -0
  19. package/dist/src/framework.d.ts +309 -0
  20. package/dist/src/framework.d.ts.map +1 -0
  21. package/dist/src/framework.js +2309 -0
  22. package/dist/src/framework.js.map +1 -0
  23. package/dist/src/gate/event-gate.d.ts +75 -0
  24. package/dist/src/gate/event-gate.d.ts.map +1 -0
  25. package/dist/src/gate/event-gate.js +503 -0
  26. package/dist/src/gate/event-gate.js.map +1 -0
  27. package/dist/src/gate/index.d.ts +3 -0
  28. package/dist/src/gate/index.d.ts.map +1 -0
  29. package/dist/src/gate/index.js +2 -0
  30. package/dist/src/gate/index.js.map +1 -0
  31. package/dist/src/gate/types.d.ts +82 -0
  32. package/dist/src/gate/types.d.ts.map +1 -0
  33. package/dist/src/gate/types.js +9 -0
  34. package/dist/src/gate/types.js.map +1 -0
  35. package/dist/src/index.d.ts +17 -0
  36. package/dist/src/index.d.ts.map +1 -0
  37. package/dist/src/index.js +18 -0
  38. package/dist/src/index.js.map +1 -0
  39. package/dist/src/mcpl/channel-registry.d.ts +141 -0
  40. package/dist/src/mcpl/channel-registry.d.ts.map +1 -0
  41. package/dist/src/mcpl/channel-registry.js +647 -0
  42. package/dist/src/mcpl/channel-registry.js.map +1 -0
  43. package/dist/src/mcpl/checkpoint-manager.d.ts +79 -0
  44. package/dist/src/mcpl/checkpoint-manager.d.ts.map +1 -0
  45. package/dist/src/mcpl/checkpoint-manager.js +430 -0
  46. package/dist/src/mcpl/checkpoint-manager.js.map +1 -0
  47. package/dist/src/mcpl/errors.d.ts +52 -0
  48. package/dist/src/mcpl/errors.d.ts.map +1 -0
  49. package/dist/src/mcpl/errors.js +93 -0
  50. package/dist/src/mcpl/errors.js.map +1 -0
  51. package/dist/src/mcpl/feature-set-manager.d.ts +91 -0
  52. package/dist/src/mcpl/feature-set-manager.d.ts.map +1 -0
  53. package/dist/src/mcpl/feature-set-manager.js +250 -0
  54. package/dist/src/mcpl/feature-set-manager.js.map +1 -0
  55. package/dist/src/mcpl/hook-orchestrator.d.ts +51 -0
  56. package/dist/src/mcpl/hook-orchestrator.d.ts.map +1 -0
  57. package/dist/src/mcpl/hook-orchestrator.js +217 -0
  58. package/dist/src/mcpl/hook-orchestrator.js.map +1 -0
  59. package/dist/src/mcpl/index.d.ts +14 -0
  60. package/dist/src/mcpl/index.d.ts.map +1 -0
  61. package/dist/src/mcpl/index.js +21 -0
  62. package/dist/src/mcpl/index.js.map +1 -0
  63. package/dist/src/mcpl/inference-router.d.ts +67 -0
  64. package/dist/src/mcpl/inference-router.d.ts.map +1 -0
  65. package/dist/src/mcpl/inference-router.js +249 -0
  66. package/dist/src/mcpl/inference-router.js.map +1 -0
  67. package/dist/src/mcpl/push-handler.d.ts +60 -0
  68. package/dist/src/mcpl/push-handler.d.ts.map +1 -0
  69. package/dist/src/mcpl/push-handler.js +162 -0
  70. package/dist/src/mcpl/push-handler.js.map +1 -0
  71. package/dist/src/mcpl/scope-manager.d.ts +76 -0
  72. package/dist/src/mcpl/scope-manager.d.ts.map +1 -0
  73. package/dist/src/mcpl/scope-manager.js +213 -0
  74. package/dist/src/mcpl/scope-manager.js.map +1 -0
  75. package/dist/src/mcpl/server-connection.d.ts +174 -0
  76. package/dist/src/mcpl/server-connection.d.ts.map +1 -0
  77. package/dist/src/mcpl/server-connection.js +559 -0
  78. package/dist/src/mcpl/server-connection.js.map +1 -0
  79. package/dist/src/mcpl/server-registry.d.ts +63 -0
  80. package/dist/src/mcpl/server-registry.d.ts.map +1 -0
  81. package/dist/src/mcpl/server-registry.js +119 -0
  82. package/dist/src/mcpl/server-registry.js.map +1 -0
  83. package/dist/src/mcpl/types.d.ts +755 -0
  84. package/dist/src/mcpl/types.d.ts.map +1 -0
  85. package/dist/src/mcpl/types.js +48 -0
  86. package/dist/src/mcpl/types.js.map +1 -0
  87. package/dist/src/module-registry.d.ts +93 -0
  88. package/dist/src/module-registry.d.ts.map +1 -0
  89. package/dist/src/module-registry.js +386 -0
  90. package/dist/src/module-registry.js.map +1 -0
  91. package/dist/src/modules/api/index.d.ts +22 -0
  92. package/dist/src/modules/api/index.d.ts.map +1 -0
  93. package/dist/src/modules/api/index.js +63 -0
  94. package/dist/src/modules/api/index.js.map +1 -0
  95. package/dist/src/modules/discord/discord-js-client.d.ts +58 -0
  96. package/dist/src/modules/discord/discord-js-client.d.ts.map +1 -0
  97. package/dist/src/modules/discord/discord-js-client.js +273 -0
  98. package/dist/src/modules/discord/discord-js-client.js.map +1 -0
  99. package/dist/src/modules/discord/index.d.ts +82 -0
  100. package/dist/src/modules/discord/index.d.ts.map +1 -0
  101. package/dist/src/modules/discord/index.js +959 -0
  102. package/dist/src/modules/discord/index.js.map +1 -0
  103. package/dist/src/modules/discord/types.d.ts +327 -0
  104. package/dist/src/modules/discord/types.d.ts.map +1 -0
  105. package/dist/src/modules/discord/types.js +5 -0
  106. package/dist/src/modules/discord/types.js.map +1 -0
  107. package/dist/src/modules/index.d.ts +11 -0
  108. package/dist/src/modules/index.d.ts.map +1 -0
  109. package/dist/src/modules/index.js +7 -0
  110. package/dist/src/modules/index.js.map +1 -0
  111. package/dist/src/modules/workspace/index.d.ts +59 -0
  112. package/dist/src/modules/workspace/index.d.ts.map +1 -0
  113. package/dist/src/modules/workspace/index.js +831 -0
  114. package/dist/src/modules/workspace/index.js.map +1 -0
  115. package/dist/src/modules/workspace/sync.d.ts +48 -0
  116. package/dist/src/modules/workspace/sync.d.ts.map +1 -0
  117. package/dist/src/modules/workspace/sync.js +253 -0
  118. package/dist/src/modules/workspace/sync.js.map +1 -0
  119. package/dist/src/modules/workspace/types.d.ts +166 -0
  120. package/dist/src/modules/workspace/types.d.ts.map +1 -0
  121. package/dist/src/modules/workspace/types.js +5 -0
  122. package/dist/src/modules/workspace/types.js.map +1 -0
  123. package/dist/src/modules/workspace/watcher.d.ts +43 -0
  124. package/dist/src/modules/workspace/watcher.d.ts.map +1 -0
  125. package/dist/src/modules/workspace/watcher.js +120 -0
  126. package/dist/src/modules/workspace/watcher.js.map +1 -0
  127. package/dist/src/queue.d.ts +50 -0
  128. package/dist/src/queue.d.ts.map +1 -0
  129. package/dist/src/queue.js +95 -0
  130. package/dist/src/queue.js.map +1 -0
  131. package/dist/src/safe-slice.d.ts +8 -0
  132. package/dist/src/safe-slice.d.ts.map +1 -0
  133. package/dist/src/safe-slice.js +18 -0
  134. package/dist/src/safe-slice.js.map +1 -0
  135. package/dist/src/types/agent.d.ts +118 -0
  136. package/dist/src/types/agent.d.ts.map +1 -0
  137. package/dist/src/types/agent.js +2 -0
  138. package/dist/src/types/agent.js.map +1 -0
  139. package/dist/src/types/events.d.ts +220 -0
  140. package/dist/src/types/events.d.ts.map +1 -0
  141. package/dist/src/types/events.js +2 -0
  142. package/dist/src/types/events.js.map +1 -0
  143. package/dist/src/types/framework.d.ts +184 -0
  144. package/dist/src/types/framework.d.ts.map +1 -0
  145. package/dist/src/types/framework.js +2 -0
  146. package/dist/src/types/framework.js.map +1 -0
  147. package/dist/src/types/index.d.ts +7 -0
  148. package/dist/src/types/index.d.ts.map +1 -0
  149. package/dist/src/types/index.js +7 -0
  150. package/dist/src/types/index.js.map +1 -0
  151. package/dist/src/types/inference-log.d.ts +64 -0
  152. package/dist/src/types/inference-log.d.ts.map +1 -0
  153. package/dist/src/types/inference-log.js +5 -0
  154. package/dist/src/types/inference-log.js.map +1 -0
  155. package/dist/src/types/module.d.ts +302 -0
  156. package/dist/src/types/module.d.ts.map +1 -0
  157. package/dist/src/types/module.js +2 -0
  158. package/dist/src/types/module.js.map +1 -0
  159. package/dist/src/types/trace.d.ts +173 -0
  160. package/dist/src/types/trace.d.ts.map +1 -0
  161. package/dist/src/types/trace.js +2 -0
  162. package/dist/src/types/trace.js.map +1 -0
  163. package/dist/test/event-gate.test.d.ts +2 -0
  164. package/dist/test/event-gate.test.d.ts.map +1 -0
  165. package/dist/test/event-gate.test.js +535 -0
  166. package/dist/test/event-gate.test.js.map +1 -0
  167. package/dist/test/framework.test.d.ts +5 -0
  168. package/dist/test/framework.test.d.ts.map +1 -0
  169. package/dist/test/framework.test.js +738 -0
  170. package/dist/test/framework.test.js.map +1 -0
  171. package/dist/tsconfig.tsbuildinfo +1 -0
  172. package/package.json +58 -0
  173. package/src/agent.ts +512 -0
  174. package/src/api/mcp-server.ts +687 -0
  175. package/src/api/server.ts +1023 -0
  176. package/src/api/types.ts +542 -0
  177. package/src/framework.ts +2784 -0
  178. package/src/gate/event-gate.ts +623 -0
  179. package/src/gate/index.ts +12 -0
  180. package/src/gate/types.ts +112 -0
  181. package/src/index.ts +63 -0
  182. package/src/mcpl/channel-registry.ts +824 -0
  183. package/src/mcpl/checkpoint-manager.ts +545 -0
  184. package/src/mcpl/errors.ts +114 -0
  185. package/src/mcpl/feature-set-manager.ts +333 -0
  186. package/src/mcpl/hook-orchestrator.ts +281 -0
  187. package/src/mcpl/index.ts +134 -0
  188. package/src/mcpl/inference-router.ts +335 -0
  189. package/src/mcpl/push-handler.ts +232 -0
  190. package/src/mcpl/scope-manager.ts +262 -0
  191. package/src/mcpl/server-connection.ts +694 -0
  192. package/src/mcpl/server-registry.ts +144 -0
  193. package/src/mcpl/types.ts +991 -0
  194. package/src/module-registry.ts +547 -0
  195. package/src/modules/api/index.ts +94 -0
  196. package/src/modules/discord/discord-js-client.ts +353 -0
  197. package/src/modules/discord/index.ts +1146 -0
  198. package/src/modules/discord/types.ts +465 -0
  199. package/src/modules/index.ts +43 -0
  200. package/src/modules/workspace/index.ts +969 -0
  201. package/src/modules/workspace/sync.ts +314 -0
  202. package/src/modules/workspace/types.ts +202 -0
  203. package/src/modules/workspace/watcher.ts +142 -0
  204. package/src/queue.ts +106 -0
  205. package/src/safe-slice.ts +18 -0
  206. package/src/types/agent.ts +119 -0
  207. package/src/types/events.ts +252 -0
  208. package/src/types/framework.ts +211 -0
  209. package/src/types/index.ts +6 -0
  210. package/src/types/inference-log.ts +64 -0
  211. package/src/types/module.ts +367 -0
  212. package/src/types/trace.ts +196 -0
@@ -0,0 +1,785 @@
1
+ /**
2
+ * WebSocket API Server for the agent framework
3
+ *
4
+ * Provides real-time bidirectional communication:
5
+ * - Commands from clients (send messages, branch, inspect)
6
+ * - Events from framework (inference, tools, speech)
7
+ */
8
+ import { WebSocketServer, WebSocket } from 'ws';
9
+ import { createServer } from 'node:http';
10
+ export * from './types.js';
11
+ const DEFAULT_CONFIG = {
12
+ port: 8765,
13
+ host: 'localhost',
14
+ path: '/ws',
15
+ enableHttp: true,
16
+ };
17
+ /** Polling interval for subscriptions (ms) */
18
+ const SUBSCRIPTION_POLL_INTERVAL = 50;
19
+ /**
20
+ * API Server for the agent framework.
21
+ */
22
+ export class ApiServer {
23
+ config;
24
+ framework;
25
+ wss = null;
26
+ httpServer = null;
27
+ clients = new Set();
28
+ currentBranch = 'main';
29
+ /** Map from client to their subscription IDs */
30
+ clientSubscriptions = new Map();
31
+ /** Subscription poll interval timer */
32
+ subscriptionPollInterval = null;
33
+ constructor(framework, config = {}) {
34
+ this.framework = framework;
35
+ this.config = { ...DEFAULT_CONFIG, ...config };
36
+ }
37
+ /**
38
+ * Start the API server.
39
+ */
40
+ async start() {
41
+ // Create HTTP server for both WebSocket upgrade and REST endpoints
42
+ this.httpServer = createServer((req, res) => {
43
+ if (this.config.enableHttp) {
44
+ this.handleHttpRequest(req, res);
45
+ }
46
+ else {
47
+ res.writeHead(404);
48
+ res.end();
49
+ }
50
+ });
51
+ // Create WebSocket server
52
+ this.wss = new WebSocketServer({
53
+ server: this.httpServer,
54
+ path: this.config.path,
55
+ });
56
+ this.wss.on('connection', (ws) => {
57
+ this.handleConnection(ws);
58
+ });
59
+ // Subscribe to trace events
60
+ this.framework.onTrace((event) => {
61
+ this.handleTraceEvent(event);
62
+ });
63
+ // Start listening
64
+ await new Promise((resolve) => {
65
+ this.httpServer.listen(this.config.port, this.config.host, () => {
66
+ console.log(`API server listening on ws://${this.config.host}:${this.config.port}${this.config.path}`);
67
+ resolve();
68
+ });
69
+ });
70
+ // Start subscription polling
71
+ this.subscriptionPollInterval = setInterval(() => {
72
+ this.pollSubscriptions();
73
+ }, SUBSCRIPTION_POLL_INTERVAL);
74
+ }
75
+ /**
76
+ * Stop the API server.
77
+ */
78
+ async stop() {
79
+ // Stop subscription polling
80
+ if (this.subscriptionPollInterval) {
81
+ clearInterval(this.subscriptionPollInterval);
82
+ this.subscriptionPollInterval = null;
83
+ }
84
+ // Cleanup all subscriptions
85
+ const store = this.framework.getStore();
86
+ for (const [_, subscriptionIds] of this.clientSubscriptions) {
87
+ for (const subId of subscriptionIds) {
88
+ store.unsubscribe(subId);
89
+ }
90
+ }
91
+ this.clientSubscriptions.clear();
92
+ // Close all client connections
93
+ for (const client of this.clients) {
94
+ client.close();
95
+ }
96
+ this.clients.clear();
97
+ // Close servers
98
+ if (this.wss) {
99
+ this.wss.close();
100
+ this.wss = null;
101
+ }
102
+ if (this.httpServer) {
103
+ await new Promise((resolve) => {
104
+ this.httpServer.close(() => resolve());
105
+ });
106
+ this.httpServer = null;
107
+ }
108
+ }
109
+ /**
110
+ * Get the server URL.
111
+ */
112
+ get url() {
113
+ return `ws://${this.config.host}:${this.config.port}${this.config.path}`;
114
+ }
115
+ // ==========================================================================
116
+ // WebSocket Handling
117
+ // ==========================================================================
118
+ handleConnection(ws) {
119
+ this.clients.add(ws);
120
+ ws.on('message', (data) => {
121
+ try {
122
+ const message = JSON.parse(data.toString());
123
+ this.handleClientMessage(ws, message);
124
+ }
125
+ catch (error) {
126
+ this.sendResponse(ws, {
127
+ type: 'response',
128
+ success: false,
129
+ error: 'Invalid JSON',
130
+ });
131
+ }
132
+ });
133
+ ws.on('close', () => {
134
+ this.cleanupClientSubscriptions(ws);
135
+ this.clients.delete(ws);
136
+ });
137
+ ws.on('error', (error) => {
138
+ console.error('WebSocket error:', error);
139
+ this.cleanupClientSubscriptions(ws);
140
+ this.clients.delete(ws);
141
+ });
142
+ // Send welcome event
143
+ this.sendEvent(ws, 'connected', {
144
+ currentBranch: this.currentBranch,
145
+ agents: this.framework.getAllAgents().map((a) => a.name),
146
+ });
147
+ }
148
+ async handleClientMessage(ws, message) {
149
+ if (message.type !== 'request') {
150
+ this.sendResponse(ws, {
151
+ type: 'response',
152
+ id: message.id,
153
+ success: false,
154
+ error: 'Expected request message',
155
+ });
156
+ return;
157
+ }
158
+ try {
159
+ const result = await this.executeCommand(ws, message.command, message.params);
160
+ this.sendResponse(ws, {
161
+ type: 'response',
162
+ id: message.id,
163
+ success: true,
164
+ data: result,
165
+ });
166
+ }
167
+ catch (error) {
168
+ this.sendResponse(ws, {
169
+ type: 'response',
170
+ id: message.id,
171
+ success: false,
172
+ error: error instanceof Error ? error.message : String(error),
173
+ });
174
+ }
175
+ }
176
+ sendResponse(ws, response) {
177
+ if (ws.readyState === WebSocket.OPEN) {
178
+ ws.send(JSON.stringify(response));
179
+ }
180
+ }
181
+ sendEvent(ws, event, data) {
182
+ if (ws.readyState === WebSocket.OPEN) {
183
+ const message = { type: 'event', event, data };
184
+ ws.send(JSON.stringify(message));
185
+ }
186
+ }
187
+ broadcast(event, data) {
188
+ const message = { type: 'event', event, data };
189
+ const json = JSON.stringify(message);
190
+ for (const client of this.clients) {
191
+ if (client.readyState === WebSocket.OPEN) {
192
+ client.send(json);
193
+ }
194
+ }
195
+ }
196
+ // ==========================================================================
197
+ // Trace Event Handling
198
+ // ==========================================================================
199
+ handleTraceEvent(event) {
200
+ // Skip process:received — clients don't need raw input events
201
+ if (event.type === 'process:received') {
202
+ return;
203
+ }
204
+ // Broadcast all other trace events directly — no translation needed
205
+ // TraceEvents are already serializable (error is string, not Error)
206
+ this.broadcast(event.type, event);
207
+ }
208
+ // ==========================================================================
209
+ // Command Execution
210
+ // ==========================================================================
211
+ async executeCommand(ws, command, params) {
212
+ switch (command) {
213
+ // Conversation
214
+ case 'message.send':
215
+ return this.cmdMessageSend(params);
216
+ case 'message.list':
217
+ return this.cmdMessageList(params);
218
+ case 'inference.request':
219
+ return this.cmdInferenceRequest(params);
220
+ case 'inference.abort':
221
+ return this.cmdInferenceAbort(params);
222
+ // Branching
223
+ case 'branch.list':
224
+ return this.cmdBranchList();
225
+ case 'branch.create':
226
+ return this.cmdBranchCreate(params);
227
+ case 'branch.switch':
228
+ return this.cmdBranchSwitch(params);
229
+ case 'branch.current':
230
+ return this.cmdBranchCurrent();
231
+ case 'branch.delete':
232
+ return this.cmdBranchDelete(params);
233
+ // Undo/Redo
234
+ case 'undo':
235
+ return this.cmdUndo(params);
236
+ case 'redo':
237
+ return this.cmdRedo(params);
238
+ case 'undo.state':
239
+ return this.cmdUndoState(params);
240
+ // Inspection
241
+ case 'agent.list':
242
+ return this.cmdAgentList();
243
+ case 'agent.context':
244
+ return this.cmdAgentContext(params);
245
+ case 'module.list':
246
+ return this.cmdModuleList();
247
+ case 'module.state':
248
+ return this.cmdModuleState(params);
249
+ case 'store.states':
250
+ return this.cmdStoreStates(params);
251
+ case 'store.inspect':
252
+ return this.cmdStoreInspect(params);
253
+ case 'store.search':
254
+ return this.cmdStoreSearch(params);
255
+ // Subscriptions
256
+ case 'store.subscribe':
257
+ return this.cmdStoreSubscribe(ws, params);
258
+ case 'store.unsubscribe':
259
+ return this.cmdStoreUnsubscribe(ws, params);
260
+ // Inference logs
261
+ case 'inference.tail':
262
+ return this.cmdInferenceTail(params);
263
+ case 'inference.inspect':
264
+ return this.cmdInferenceInspect(params);
265
+ case 'inference.search':
266
+ return this.cmdInferenceSearch(params);
267
+ // Event logs
268
+ case 'events.tail':
269
+ return this.cmdEventsTail(params);
270
+ case 'events.inspect':
271
+ return this.cmdEventsInspect(params);
272
+ case 'events.search':
273
+ return this.cmdEventsSearch(params);
274
+ case 'events.subscribe':
275
+ return this.cmdEventsSubscribe(params);
276
+ default:
277
+ throw new Error(`Unknown command: ${command}`);
278
+ }
279
+ }
280
+ // ==========================================================================
281
+ // Conversation Commands
282
+ // ==========================================================================
283
+ async cmdMessageSend(params) {
284
+ if (!params.participant || !params.content) {
285
+ throw new Error('participant and content are required');
286
+ }
287
+ // Push api:message event for the ApiModule to handle
288
+ this.framework.pushEvent({
289
+ type: 'api:message',
290
+ participant: params.participant,
291
+ content: params.content,
292
+ triggerInference: params.triggerInference ?? true,
293
+ targetAgents: params.targetAgents,
294
+ });
295
+ return { messageId: 'pending' }; // TODO: return actual ID
296
+ }
297
+ async cmdMessageList(params) {
298
+ // Get messages from first agent's context manager
299
+ const agent = this.framework.getAllAgents()[0];
300
+ if (!agent) {
301
+ return { messages: [] };
302
+ }
303
+ const contextManager = agent.getContextManager();
304
+ const { messages } = await contextManager.compile();
305
+ const limit = params?.limit ?? 50;
306
+ const offset = params?.offset ?? 0;
307
+ return {
308
+ messages: messages.slice(-(limit + offset), offset ? -offset : undefined),
309
+ };
310
+ }
311
+ async cmdInferenceRequest(params) {
312
+ // Push api:inference-request event for the ApiModule to handle
313
+ this.framework.pushEvent({
314
+ type: 'api:inference-request',
315
+ agentName: params?.agentName,
316
+ reason: params?.reason,
317
+ });
318
+ return { queued: true };
319
+ }
320
+ async cmdInferenceAbort(params) {
321
+ if (!params?.agentName) {
322
+ throw new Error('agentName is required');
323
+ }
324
+ const aborted = this.framework.abortInference(params.agentName, params.reason);
325
+ return { aborted };
326
+ }
327
+ // ==========================================================================
328
+ // Branch Commands
329
+ // ==========================================================================
330
+ async cmdBranchList() {
331
+ const store = this.framework.getStore();
332
+ const branches = store.listBranches();
333
+ return {
334
+ branches: branches.map((branch) => ({
335
+ name: branch.name,
336
+ isCurrent: branch.name === this.currentBranch,
337
+ })),
338
+ };
339
+ }
340
+ async cmdBranchCreate(params) {
341
+ if (!params.name) {
342
+ throw new Error('name is required');
343
+ }
344
+ const store = this.framework.getStore();
345
+ store.createBranch(params.name);
346
+ this.broadcast('branch:created', {
347
+ name: params.name,
348
+ parent: this.currentBranch,
349
+ });
350
+ if (params.switchTo) {
351
+ await this.cmdBranchSwitch({ name: params.name });
352
+ }
353
+ return { name: params.name };
354
+ }
355
+ async cmdBranchSwitch(params) {
356
+ if (!params.name) {
357
+ throw new Error('name is required');
358
+ }
359
+ const store = this.framework.getStore();
360
+ const previousBranch = this.currentBranch;
361
+ store.switchBranch(params.name);
362
+ this.currentBranch = params.name;
363
+ // Materialize config files from the new branch
364
+ const ws = this.framework.getModule('workspace');
365
+ if (ws && 'materializeMount' in ws) {
366
+ await ws.materializeMount('_config');
367
+ }
368
+ this.broadcast('branch:switched', {
369
+ from: previousBranch,
370
+ to: params.name,
371
+ });
372
+ return { switched: true };
373
+ }
374
+ async cmdBranchCurrent() {
375
+ return { name: this.currentBranch };
376
+ }
377
+ async cmdBranchDelete(params) {
378
+ if (!params.name) {
379
+ throw new Error('name is required');
380
+ }
381
+ if (params.name === this.currentBranch) {
382
+ throw new Error('Cannot delete current branch');
383
+ }
384
+ if (params.name === 'main') {
385
+ throw new Error('Cannot delete main branch');
386
+ }
387
+ const store = this.framework.getStore();
388
+ store.deleteBranch(params.name);
389
+ this.broadcast('branch:deleted', { name: params.name });
390
+ return { deleted: true };
391
+ }
392
+ // ==========================================================================
393
+ // Undo/Redo Commands
394
+ // ==========================================================================
395
+ cmdUndo(params) {
396
+ if (!params.agentName) {
397
+ throw new Error('agentName is required');
398
+ }
399
+ const result = this.framework.undoLastTurn(params.agentName);
400
+ if (result.undone) {
401
+ this.currentBranch = result.toBranch;
402
+ this.broadcast('branch:switched', {
403
+ from: result.fromBranch,
404
+ to: result.toBranch,
405
+ reason: 'undo',
406
+ });
407
+ }
408
+ return result;
409
+ }
410
+ cmdRedo(params) {
411
+ if (!params.agentName) {
412
+ throw new Error('agentName is required');
413
+ }
414
+ const result = this.framework.redo(params.agentName);
415
+ if (result.redone) {
416
+ this.currentBranch = result.toBranch;
417
+ this.broadcast('branch:switched', {
418
+ from: result.fromBranch,
419
+ to: result.toBranch,
420
+ reason: 'redo',
421
+ });
422
+ }
423
+ return result;
424
+ }
425
+ cmdUndoState(params) {
426
+ if (!params.agentName) {
427
+ throw new Error('agentName is required');
428
+ }
429
+ return this.framework.getUndoRedoState(params.agentName);
430
+ }
431
+ // ==========================================================================
432
+ // Inspection Commands
433
+ // ==========================================================================
434
+ async cmdAgentList() {
435
+ const agents = this.framework.getAllAgents();
436
+ return {
437
+ agents: agents.map((a) => ({
438
+ name: a.name,
439
+ model: a.model,
440
+ status: a.state.status,
441
+ systemPromptPreview: a.systemPrompt.substring(0, 200) + '...',
442
+ allowedTools: a.allowedTools,
443
+ })),
444
+ };
445
+ }
446
+ async cmdAgentContext(params) {
447
+ if (!params.agentName) {
448
+ throw new Error('agentName is required');
449
+ }
450
+ const agent = this.framework.getAgent(params.agentName);
451
+ if (!agent) {
452
+ throw new Error(`Agent not found: ${params.agentName}`);
453
+ }
454
+ const contextManager = agent.getContextManager();
455
+ const { messages } = await contextManager.compile(params.maxTokens
456
+ ? { maxTokens: params.maxTokens, reserveForResponse: 4096 }
457
+ : undefined);
458
+ return { context: messages };
459
+ }
460
+ async cmdModuleList() {
461
+ const modules = this.framework.getAllModules();
462
+ const allTools = this.framework.getAllTools();
463
+ return {
464
+ modules: modules.map((module) => ({
465
+ name: module.name,
466
+ tools: allTools
467
+ .filter((t) => t.name.startsWith(`${module.name}:`))
468
+ .map((t) => t.name),
469
+ })),
470
+ };
471
+ }
472
+ async cmdModuleState(params) {
473
+ if (!params.moduleName) {
474
+ throw new Error('moduleName is required');
475
+ }
476
+ const store = this.framework.getStore();
477
+ const stateId = `modules/${params.moduleName}/state`;
478
+ const state = store.getStateJson(stateId);
479
+ return { state };
480
+ }
481
+ async cmdStoreStates(params) {
482
+ const store = this.framework.getStore();
483
+ let stateInfos = store.listStates();
484
+ // Filter by namespace
485
+ if (params?.namespace) {
486
+ stateInfos = stateInfos.filter((info) => info.id.startsWith(params.namespace));
487
+ }
488
+ // Apply limit (default 50)
489
+ const limit = params?.limit ?? 50;
490
+ stateInfos = stateInfos.slice(-limit);
491
+ return {
492
+ states: stateInfos.map((info) => ({
493
+ id: info.id,
494
+ strategy: info.strategy,
495
+ hasData: store.getStateJson(info.id) !== null,
496
+ })),
497
+ };
498
+ }
499
+ async cmdStoreSearch(params) {
500
+ const store = this.framework.getStore();
501
+ let stateInfos = store.listStates();
502
+ // Filter by namespace
503
+ if (params?.namespace) {
504
+ stateInfos = stateInfos.filter((info) => info.id.startsWith(params.namespace));
505
+ }
506
+ const limit = params?.limit ?? 20;
507
+ const offset = params?.offset ?? 0;
508
+ const previewLength = params?.previewLength ?? 300;
509
+ const pattern = params?.contentPattern ? new RegExp(params.contentPattern, 'i') : null;
510
+ // Collect all matching results first (for accurate total count)
511
+ const allMatches = [];
512
+ for (const info of stateInfos) {
513
+ const data = store.getStateJson(info.id);
514
+ if (data === null)
515
+ continue;
516
+ const content = JSON.stringify(data);
517
+ // If pattern specified, filter by it
518
+ if (pattern && !pattern.test(content))
519
+ continue;
520
+ allMatches.push({ stateId: info.id, content });
521
+ }
522
+ // Apply pagination
523
+ const paged = allMatches.slice(offset, offset + limit);
524
+ const results = paged.map(({ stateId, content }) => ({
525
+ stateId,
526
+ preview: content.length > previewLength ? content.slice(0, previewLength) + '...' : content,
527
+ }));
528
+ return {
529
+ results,
530
+ total: allMatches.length,
531
+ hasMore: offset + limit < allMatches.length,
532
+ };
533
+ }
534
+ async cmdStoreInspect(params) {
535
+ if (!params.stateId) {
536
+ throw new Error('stateId is required');
537
+ }
538
+ const store = this.framework.getStore();
539
+ const data = store.getStateJson(params.stateId);
540
+ return { data };
541
+ }
542
+ // ==========================================================================
543
+ // Inference Log Commands
544
+ // ==========================================================================
545
+ async cmdInferenceTail(params) {
546
+ const entries = this.framework.tailInferenceLogs(params?.count ?? 10, params?.agentName);
547
+ return { entries };
548
+ }
549
+ async cmdInferenceInspect(params) {
550
+ if (params.sequence === undefined || params.sequence === null) {
551
+ throw new Error('sequence is required');
552
+ }
553
+ const entry = this.framework.getInferenceLog(params.sequence);
554
+ if (!entry) {
555
+ throw new Error(`Inference log not found: ${params.sequence}`);
556
+ }
557
+ return { entry };
558
+ }
559
+ async cmdInferenceSearch(params) {
560
+ const result = this.framework.queryInferenceLogs({
561
+ agentName: params?.agentName,
562
+ limit: params?.limit,
563
+ offset: params?.offset,
564
+ pattern: params?.pattern,
565
+ errorsOnly: params?.errorsOnly,
566
+ });
567
+ return result;
568
+ }
569
+ // ==========================================================================
570
+ // Event Log Commands
571
+ // ==========================================================================
572
+ async cmdEventsTail(params) {
573
+ const entries = this.framework.tailProcessLogs(params?.count ?? 10, params?.eventType);
574
+ return { entries };
575
+ }
576
+ async cmdEventsInspect(params) {
577
+ if (params.sequence === undefined || params.sequence === null) {
578
+ throw new Error('sequence is required');
579
+ }
580
+ const entry = this.framework.getProcessLog(params.sequence);
581
+ if (!entry) {
582
+ throw new Error(`Process log not found: ${params.sequence}`);
583
+ }
584
+ return { entry };
585
+ }
586
+ async cmdEventsSearch(params) {
587
+ const result = this.framework.queryProcessLogs({
588
+ eventType: params?.eventType,
589
+ moduleName: params?.moduleName,
590
+ limit: params?.limit,
591
+ offset: params?.offset,
592
+ pattern: params?.pattern,
593
+ });
594
+ return result;
595
+ }
596
+ async cmdEventsSubscribe(params) {
597
+ const types = params?.types ?? ['*'];
598
+ const limit = params?.limit ?? 100;
599
+ // Get historical events from the process log
600
+ const processEntries = this.framework.tailProcessLogs(limit);
601
+ // Convert ProcessLogEntry to PersistedEvent format
602
+ const history = processEntries.map((item) => {
603
+ const { sequence, entry } = item;
604
+ const event = entry.processEvent;
605
+ const timestamp = entry.timestamp;
606
+ // Extract agent/module info from event if available
607
+ const agentName = event.agentName;
608
+ const moduleName = event.moduleName;
609
+ return {
610
+ id: `evt-${sequence}`,
611
+ sequence,
612
+ timestamp,
613
+ type: event?.type ?? 'unknown',
614
+ payload: event,
615
+ source: 'chronicle',
616
+ agentName,
617
+ moduleName,
618
+ };
619
+ });
620
+ // Filter by types if not wildcard
621
+ const filteredHistory = types.includes('*')
622
+ ? history
623
+ : history.filter((evt) => types.some((pattern) => matchEventType(pattern, evt.type)));
624
+ return {
625
+ subscribed: types,
626
+ history: filteredHistory,
627
+ };
628
+ }
629
+ // ==========================================================================
630
+ // HTTP Handling (optional REST endpoints)
631
+ // ==========================================================================
632
+ handleHttpRequest(req, res) {
633
+ // CORS headers
634
+ res.setHeader('Access-Control-Allow-Origin', '*');
635
+ res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
636
+ res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
637
+ if (req.method === 'OPTIONS') {
638
+ res.writeHead(204);
639
+ res.end();
640
+ return;
641
+ }
642
+ const url = new URL(req.url ?? '/', `http://${req.headers.host}`);
643
+ // Health check
644
+ if (url.pathname === '/health') {
645
+ res.writeHead(200, { 'Content-Type': 'application/json' });
646
+ res.end(JSON.stringify({ status: 'ok', branch: this.currentBranch }));
647
+ return;
648
+ }
649
+ // Status
650
+ if (url.pathname === '/status') {
651
+ const agents = this.framework.getAllAgents();
652
+ res.writeHead(200, { 'Content-Type': 'application/json' });
653
+ res.end(JSON.stringify({
654
+ branch: this.currentBranch,
655
+ agents: agents.map((a) => ({ name: a.name, status: a.state.status })),
656
+ clients: this.clients.size,
657
+ queueDepth: this.framework.getQueueDepth(),
658
+ }));
659
+ return;
660
+ }
661
+ res.writeHead(404, { 'Content-Type': 'application/json' });
662
+ res.end(JSON.stringify({ error: 'Not found' }));
663
+ }
664
+ // ==========================================================================
665
+ // Subscription Commands
666
+ // ==========================================================================
667
+ async cmdStoreSubscribe(ws, params) {
668
+ const store = this.framework.getStore();
669
+ // Build subscription config
670
+ const config = {};
671
+ if (params?.bufferSize !== undefined) {
672
+ config.bufferSize = params.bufferSize;
673
+ }
674
+ if (params?.maxSnapshotBytes !== undefined) {
675
+ config.maxSnapshotBytes = params.maxSnapshotBytes;
676
+ }
677
+ if (params?.fromSequence !== undefined) {
678
+ config.fromSequence = params.fromSequence;
679
+ }
680
+ if (params?.filter) {
681
+ config.filter = {
682
+ recordTypes: params.filter.recordTypes,
683
+ branch: params.filter.branch,
684
+ stateIds: params.filter.stateIds,
685
+ includeRecords: params.filter.includeRecords ?? false,
686
+ includeStateChanges: params.filter.includeStateChanges ?? false,
687
+ includeBranchEvents: params.filter.includeBranchEvents ?? false,
688
+ };
689
+ }
690
+ // Create subscription
691
+ const subscriptionId = store.subscribe(config);
692
+ // Track this subscription for the client
693
+ if (!this.clientSubscriptions.has(ws)) {
694
+ this.clientSubscriptions.set(ws, new Set());
695
+ }
696
+ this.clientSubscriptions.get(ws).add(subscriptionId);
697
+ // If fromSequence is set, perform catch-up
698
+ if (params?.fromSequence !== undefined) {
699
+ store.catchUpSubscription(subscriptionId);
700
+ }
701
+ else {
702
+ // Mark as caught up immediately for live-only subscriptions
703
+ // The store will send the CaughtUp event
704
+ store.catchUpSubscription(subscriptionId);
705
+ }
706
+ return { subscriptionId };
707
+ }
708
+ async cmdStoreUnsubscribe(ws, params) {
709
+ if (!params.subscriptionId) {
710
+ throw new Error('subscriptionId is required');
711
+ }
712
+ const store = this.framework.getStore();
713
+ // Remove from client tracking
714
+ const clientSubs = this.clientSubscriptions.get(ws);
715
+ if (clientSubs) {
716
+ clientSubs.delete(params.subscriptionId);
717
+ }
718
+ // Unsubscribe from store
719
+ store.unsubscribe(params.subscriptionId);
720
+ return { success: true };
721
+ }
722
+ // ==========================================================================
723
+ // Subscription Helpers
724
+ // ==========================================================================
725
+ /**
726
+ * Cleanup all subscriptions for a disconnected client.
727
+ */
728
+ cleanupClientSubscriptions(ws) {
729
+ const subscriptionIds = this.clientSubscriptions.get(ws);
730
+ if (!subscriptionIds)
731
+ return;
732
+ const store = this.framework.getStore();
733
+ for (const subId of subscriptionIds) {
734
+ store.unsubscribe(subId);
735
+ }
736
+ this.clientSubscriptions.delete(ws);
737
+ }
738
+ /**
739
+ * Poll all active subscriptions and push events to clients.
740
+ */
741
+ pollSubscriptions() {
742
+ const store = this.framework.getStore();
743
+ for (const [ws, subscriptionIds] of this.clientSubscriptions) {
744
+ if (ws.readyState !== WebSocket.OPEN)
745
+ continue;
746
+ for (const subId of subscriptionIds) {
747
+ // Poll events from this subscription (non-blocking)
748
+ let event = store.pollSubscription(subId);
749
+ let eventCount = 0;
750
+ const maxEventsPerPoll = 100; // Prevent blocking too long
751
+ while (event && eventCount < maxEventsPerPoll) {
752
+ // Parse the event data and send to client
753
+ try {
754
+ const eventData = JSON.parse(event.data);
755
+ const eventType = `store:${event.eventType}`;
756
+ this.sendEvent(ws, eventType, {
757
+ subscriptionId: subId,
758
+ ...eventData,
759
+ });
760
+ }
761
+ catch (error) {
762
+ console.error('Failed to parse subscription event:', error);
763
+ }
764
+ // Check for more events
765
+ event = store.pollSubscription(subId);
766
+ eventCount++;
767
+ }
768
+ }
769
+ }
770
+ }
771
+ }
772
+ /**
773
+ * Check if an event type matches a pattern.
774
+ * Supports wildcards like 'inference:*' or exact matches.
775
+ */
776
+ function matchEventType(pattern, eventType) {
777
+ if (pattern === '*')
778
+ return true;
779
+ if (pattern.endsWith(':*')) {
780
+ const prefix = pattern.slice(0, -1);
781
+ return eventType.startsWith(prefix);
782
+ }
783
+ return pattern === eventType;
784
+ }
785
+ //# sourceMappingURL=server.js.map