@backendkit-labs/agent-web 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.
package/README.md ADDED
@@ -0,0 +1,62 @@
1
+ # @backendkit-labs/agent-web
2
+
3
+ HTTP/WebSocket server layer for [@backendkit-labs/agent-core](https://www.npmjs.com/package/@backendkit-labs/agent-core).
4
+
5
+ Wraps `AgentEngine` in a Fastify 5 server and exposes it to browsers via WebSocket with streaming, multi-session management, and a real-time monitor bus.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npm install @backendkit-labs/agent-web @backendkit-labs/agent-core
11
+ ```
12
+
13
+ ## Quick start
14
+
15
+ ```typescript
16
+ import { AgentServer } from '@backendkit-labs/agent-web';
17
+ import type { EngineFactory } from '@backendkit-labs/agent-web';
18
+ import { AgentEngine, AgentRegistry, ToolRegistry, ProviderRegistry } from '@backendkit-labs/agent-core';
19
+
20
+ const engineFactory: EngineFactory = (sessionId, transport) =>
21
+ new AgentEngine({ transport, /* ...your config */ });
22
+
23
+ const server = new AgentServer({
24
+ port: 3000,
25
+ engineFactory,
26
+ cors: '*',
27
+ });
28
+
29
+ await server.start();
30
+ // WS /chat — chat sessions
31
+ // WS /monitor — real-time observer (read-only)
32
+ // GET /health — { status, sessions, monitors }
33
+ // GET /sessions — active session list
34
+ ```
35
+
36
+ ## WebSocket protocol
37
+
38
+ **Client → server** (`/chat`):
39
+ ```json
40
+ { "type": "input", "content": "Your message" }
41
+ { "type": "abort" }
42
+ ```
43
+
44
+ **Server → client** — standard `AgentEvent` JSON frames: `token`, `tool_call`, `tool_result`, `agent_switch`, `block_start`, `done`, `error`, etc.
45
+
46
+ ## Monitor bus
47
+
48
+ Connect to `WS /monitor` to observe all sessions in real time (read-only):
49
+
50
+ ```json
51
+ { "type": "monitor_hello", "sessionIds": ["..."] }
52
+ { "type": "monitor_session_open", "sessionId": "..." }
53
+ { "type": "monitor_session_close", "sessionId": "..." }
54
+ { "sessionId": "...", "type": "run_input", "content": "..." }
55
+ { "sessionId": "...", "type": "token", "content": "..." }
56
+ ```
57
+
58
+ Use with [`@backendkit-labs/agent-web-ui`](https://www.npmjs.com/package/@backendkit-labs/agent-web-ui) for a ready-made React chat + kanban monitor UI.
59
+
60
+ ## License
61
+
62
+ MIT
@@ -0,0 +1,418 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>BackendKit Agent</title>
7
+ <style>
8
+ * { box-sizing: border-box; margin: 0; padding: 0; }
9
+
10
+ body {
11
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
12
+ background: #0f0f0f;
13
+ color: #e8e8e8;
14
+ height: 100dvh;
15
+ display: flex;
16
+ flex-direction: column;
17
+ }
18
+
19
+ #header {
20
+ padding: 12px 20px;
21
+ border-bottom: 1px solid #2a2a2a;
22
+ display: flex;
23
+ align-items: center;
24
+ gap: 12px;
25
+ background: #161616;
26
+ }
27
+
28
+ #header h1 { font-size: 15px; font-weight: 600; }
29
+
30
+ #agent-badge {
31
+ font-size: 12px;
32
+ padding: 2px 8px;
33
+ border-radius: 999px;
34
+ background: #2a2a2a;
35
+ color: #999;
36
+ }
37
+
38
+ #status-dot {
39
+ width: 8px; height: 8px;
40
+ border-radius: 50%;
41
+ background: #555;
42
+ margin-left: auto;
43
+ }
44
+ #status-dot.connected { background: #22c55e; }
45
+ #status-dot.thinking { background: #f59e0b; animation: pulse 1s infinite; }
46
+
47
+ @keyframes pulse {
48
+ 0%, 100% { opacity: 1; }
49
+ 50% { opacity: 0.3; }
50
+ }
51
+
52
+ #messages {
53
+ flex: 1;
54
+ overflow-y: auto;
55
+ padding: 20px;
56
+ display: flex;
57
+ flex-direction: column;
58
+ gap: 16px;
59
+ }
60
+
61
+ .msg {
62
+ max-width: 720px;
63
+ width: 100%;
64
+ align-self: flex-start;
65
+ }
66
+
67
+ .msg.user {
68
+ align-self: flex-end;
69
+ }
70
+
71
+ .msg-bubble {
72
+ padding: 10px 14px;
73
+ border-radius: 12px;
74
+ font-size: 14px;
75
+ line-height: 1.6;
76
+ white-space: pre-wrap;
77
+ word-break: break-word;
78
+ }
79
+
80
+ .msg.user .msg-bubble {
81
+ background: #1d4ed8;
82
+ color: #fff;
83
+ border-bottom-right-radius: 4px;
84
+ }
85
+
86
+ .msg.assistant .msg-bubble {
87
+ background: #1e1e1e;
88
+ border: 1px solid #2a2a2a;
89
+ border-bottom-left-radius: 4px;
90
+ }
91
+
92
+ .msg-meta {
93
+ font-size: 11px;
94
+ color: #555;
95
+ margin-bottom: 4px;
96
+ padding: 0 4px;
97
+ }
98
+
99
+ .tool-badge {
100
+ display: inline-flex;
101
+ align-items: center;
102
+ gap: 6px;
103
+ font-size: 12px;
104
+ padding: 4px 10px;
105
+ border-radius: 6px;
106
+ background: #1a1a2e;
107
+ border: 1px solid #2d2d5e;
108
+ color: #818cf8;
109
+ margin: 4px 0;
110
+ }
111
+
112
+ .tool-badge.done { border-color: #166534; color: #4ade80; background: #052e16; }
113
+ .tool-badge.error { border-color: #7f1d1d; color: #f87171; background: #2d0000; }
114
+
115
+ #input-area {
116
+ padding: 16px 20px;
117
+ border-top: 1px solid #2a2a2a;
118
+ background: #161616;
119
+ display: flex;
120
+ gap: 10px;
121
+ }
122
+
123
+ #input {
124
+ flex: 1;
125
+ background: #1e1e1e;
126
+ border: 1px solid #2a2a2a;
127
+ border-radius: 8px;
128
+ padding: 10px 14px;
129
+ color: #e8e8e8;
130
+ font-size: 14px;
131
+ resize: none;
132
+ outline: none;
133
+ min-height: 44px;
134
+ max-height: 160px;
135
+ font-family: inherit;
136
+ }
137
+
138
+ #input:focus { border-color: #3b82f6; }
139
+
140
+ #send-btn {
141
+ background: #1d4ed8;
142
+ color: #fff;
143
+ border: none;
144
+ border-radius: 8px;
145
+ padding: 0 18px;
146
+ font-size: 14px;
147
+ cursor: pointer;
148
+ font-weight: 500;
149
+ height: 44px;
150
+ white-space: nowrap;
151
+ }
152
+
153
+ #send-btn:disabled { opacity: 0.4; cursor: not-allowed; }
154
+ #send-btn:hover:not(:disabled) { background: #1e40af; }
155
+
156
+ #abort-btn {
157
+ background: transparent;
158
+ color: #f87171;
159
+ border: 1px solid #7f1d1d;
160
+ border-radius: 8px;
161
+ padding: 0 14px;
162
+ font-size: 13px;
163
+ cursor: pointer;
164
+ height: 44px;
165
+ display: none;
166
+ }
167
+ #abort-btn.visible { display: block; }
168
+ #abort-btn:hover { background: #2d0000; }
169
+
170
+ #connect-bar {
171
+ padding: 10px 20px;
172
+ background: #1a1a1a;
173
+ border-bottom: 1px solid #2a2a2a;
174
+ display: flex;
175
+ gap: 8px;
176
+ align-items: center;
177
+ font-size: 13px;
178
+ }
179
+
180
+ #server-url {
181
+ flex: 1;
182
+ background: #1e1e1e;
183
+ border: 1px solid #2a2a2a;
184
+ border-radius: 6px;
185
+ padding: 6px 10px;
186
+ color: #e8e8e8;
187
+ font-size: 13px;
188
+ outline: none;
189
+ }
190
+
191
+ #connect-btn {
192
+ background: #065f46;
193
+ color: #34d399;
194
+ border: 1px solid #065f46;
195
+ border-radius: 6px;
196
+ padding: 6px 14px;
197
+ cursor: pointer;
198
+ font-size: 13px;
199
+ }
200
+ #connect-btn:hover { background: #047857; }
201
+ </style>
202
+ </head>
203
+ <body>
204
+
205
+ <div id="header">
206
+ <h1>BackendKit Agent</h1>
207
+ <span id="agent-badge">–</span>
208
+ <div id="status-dot" title="Disconnected"></div>
209
+ </div>
210
+
211
+ <div id="connect-bar">
212
+ <input id="server-url" type="text" value="ws://localhost:4000/chat" spellcheck="false" />
213
+ <button id="connect-btn" onclick="connect()">Connect</button>
214
+ </div>
215
+
216
+ <div id="messages"></div>
217
+
218
+ <div id="input-area">
219
+ <textarea id="input" placeholder="Send a message…" rows="1" disabled></textarea>
220
+ <button id="abort-btn" onclick="abort()">Stop</button>
221
+ <button id="send-btn" onclick="sendMessage()" disabled>Send</button>
222
+ </div>
223
+
224
+ <script>
225
+ let ws = null;
226
+ let sessionId = null;
227
+ let currentAssistantMsg = null;
228
+ let isRunning = false;
229
+
230
+ const messagesEl = document.getElementById('messages');
231
+ const inputEl = document.getElementById('input');
232
+ const sendBtn = document.getElementById('send-btn');
233
+ const abortBtn = document.getElementById('abort-btn');
234
+ const statusDot = document.getElementById('status-dot');
235
+ const agentBadge = document.getElementById('agent-badge');
236
+ const connectBtn = document.getElementById('connect-btn');
237
+
238
+ function connect() {
239
+ const url = document.getElementById('server-url').value.trim();
240
+ if (ws) { ws.close(); ws = null; }
241
+
242
+ addSystemMsg(`Connecting to ${url}…`);
243
+ ws = new WebSocket(url);
244
+
245
+ ws.onopen = () => {
246
+ statusDot.className = 'connected';
247
+ statusDot.title = 'Connected';
248
+ };
249
+
250
+ ws.onclose = () => {
251
+ statusDot.className = '';
252
+ statusDot.title = 'Disconnected';
253
+ inputEl.disabled = true;
254
+ sendBtn.disabled = true;
255
+ setRunning(false);
256
+ addSystemMsg('Disconnected.');
257
+ };
258
+
259
+ ws.onerror = () => addSystemMsg('WebSocket error.');
260
+
261
+ ws.onmessage = (e) => {
262
+ const evt = JSON.parse(e.data);
263
+ handleEvent(evt);
264
+ };
265
+ }
266
+
267
+ function handleEvent(evt) {
268
+ switch (evt.type) {
269
+ case 'session_created':
270
+ sessionId = evt.sessionId;
271
+ inputEl.disabled = false;
272
+ sendBtn.disabled = false;
273
+ addSystemMsg(`Session: ${sessionId.slice(0, 8)}…`);
274
+ break;
275
+
276
+ case 'ready':
277
+ setRunning(false);
278
+ currentAssistantMsg = null;
279
+ break;
280
+
281
+ case 'token':
282
+ if (!currentAssistantMsg) currentAssistantMsg = addAssistantMsg();
283
+ currentAssistantMsg.textContent += evt.content;
284
+ scrollToBottom();
285
+ break;
286
+
287
+ case 'tool_call':
288
+ currentAssistantMsg = null;
289
+ addToolBadge(`⚙ ${evt.name}${evt.args_preview ? ` — ${evt.args_preview}` : ''}`, 'pending');
290
+ break;
291
+
292
+ case 'tool_result': {
293
+ const last = messagesEl.querySelector('.tool-badge:last-child');
294
+ if (last) last.className = `tool-badge ${evt.success ? 'done' : 'error'}`;
295
+ break;
296
+ }
297
+
298
+ case 'agent_switch':
299
+ agentBadge.textContent = `${evt.to_icon ?? ''} ${evt.to_name ?? evt.to}`.trim();
300
+ currentAssistantMsg = null;
301
+ break;
302
+
303
+ case 'block_start':
304
+ agentBadge.textContent = `${evt.agent_icon ?? ''} ${evt.agent_name ?? evt.agent_id}`.trim();
305
+ break;
306
+
307
+ case 'thinking':
308
+ addSystemMsg(`💭 ${evt.label}`);
309
+ break;
310
+
311
+ case 'done':
312
+ setRunning(false);
313
+ currentAssistantMsg = null;
314
+ break;
315
+
316
+ case 'error':
317
+ setRunning(false);
318
+ addSystemMsg(`Error: ${evt.message}`, true);
319
+ currentAssistantMsg = null;
320
+ break;
321
+
322
+ case 'system':
323
+ addSystemMsg(evt.text, evt.level === 'error');
324
+ break;
325
+
326
+ case 'metrics':
327
+ addSystemMsg(`Tokens: ${evt.input_tokens} in / ${evt.output_tokens} out${evt.cost_usd != null ? ` · $${evt.cost_usd.toFixed(4)}` : ''}`);
328
+ break;
329
+ }
330
+ }
331
+
332
+ function sendMessage() {
333
+ const content = inputEl.value.trim();
334
+ if (!content || !ws || isRunning) return;
335
+
336
+ addUserMsg(content);
337
+ inputEl.value = '';
338
+ inputEl.style.height = 'auto';
339
+ setRunning(true);
340
+
341
+ ws.send(JSON.stringify({ type: 'input', content }));
342
+ }
343
+
344
+ function abort() {
345
+ if (ws) ws.send(JSON.stringify({ type: 'abort' }));
346
+ setRunning(false);
347
+ }
348
+
349
+ function setRunning(running) {
350
+ isRunning = running;
351
+ sendBtn.disabled = running;
352
+ abortBtn.className = running ? 'visible' : '';
353
+ statusDot.className = running ? 'thinking connected' : 'connected';
354
+ }
355
+
356
+ // ── DOM helpers ──────────────────────────────────────────────────────────
357
+
358
+ function addUserMsg(text) {
359
+ const div = document.createElement('div');
360
+ div.className = 'msg user';
361
+ div.innerHTML = `<div class="msg-bubble">${escHtml(text)}</div>`;
362
+ messagesEl.appendChild(div);
363
+ scrollToBottom();
364
+ }
365
+
366
+ function addAssistantMsg() {
367
+ const div = document.createElement('div');
368
+ div.className = 'msg assistant';
369
+ const meta = agentBadge.textContent !== '–' ? `<div class="msg-meta">${escHtml(agentBadge.textContent)}</div>` : '';
370
+ div.innerHTML = `${meta}<div class="msg-bubble"></div>`;
371
+ messagesEl.appendChild(div);
372
+ scrollToBottom();
373
+ return div.querySelector('.msg-bubble');
374
+ }
375
+
376
+ function addToolBadge(label, state) {
377
+ const span = document.createElement('span');
378
+ span.className = `tool-badge ${state}`;
379
+ span.textContent = label;
380
+ const wrap = document.createElement('div');
381
+ wrap.className = 'msg assistant';
382
+ wrap.appendChild(span);
383
+ messagesEl.appendChild(wrap);
384
+ scrollToBottom();
385
+ }
386
+
387
+ function addSystemMsg(text, isError = false) {
388
+ const div = document.createElement('div');
389
+ div.style.cssText = `font-size:12px;color:${isError ? '#f87171' : '#555'};padding:2px 4px;text-align:center`;
390
+ div.textContent = text;
391
+ messagesEl.appendChild(div);
392
+ scrollToBottom();
393
+ }
394
+
395
+ function scrollToBottom() {
396
+ messagesEl.scrollTop = messagesEl.scrollHeight;
397
+ }
398
+
399
+ function escHtml(s) {
400
+ return s.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
401
+ }
402
+
403
+ // ── Keyboard shortcuts ────────────────────────────────────────────────────
404
+
405
+ inputEl.addEventListener('keydown', (e) => {
406
+ if (e.key === 'Enter' && !e.shiftKey) {
407
+ e.preventDefault();
408
+ sendMessage();
409
+ }
410
+ });
411
+
412
+ inputEl.addEventListener('input', () => {
413
+ inputEl.style.height = 'auto';
414
+ inputEl.style.height = Math.min(inputEl.scrollHeight, 160) + 'px';
415
+ });
416
+ </script>
417
+ </body>
418
+ </html>
@@ -0,0 +1,9 @@
1
+ export { AgentServer } from './server/AgentServer';
2
+ export type { AgentServerOptions, AuthHook } from './server/AgentServer';
3
+ export { SessionManager } from './session/SessionManager';
4
+ export type { Session, SessionManagerOptions, EngineFactory } from './session/SessionManager';
5
+ export { WebSocketTransport } from './transport/WebSocketTransport';
6
+ export type { WsMessage } from './transport/WebSocketTransport';
7
+ export { MonitorBus } from './monitor/MonitorBus';
8
+ export type { MonitorFrame } from './monitor/MonitorBus';
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACnD,YAAY,EAAE,kBAAkB,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAEzE,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC1D,YAAY,EAAE,OAAO,EAAE,qBAAqB,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAE9F,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AACpE,YAAY,EAAE,SAAS,EAAE,MAAM,gCAAgC,CAAC;AAEhE,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAClD,YAAY,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MonitorBus = exports.WebSocketTransport = exports.SessionManager = exports.AgentServer = void 0;
4
+ var AgentServer_1 = require("./server/AgentServer");
5
+ Object.defineProperty(exports, "AgentServer", { enumerable: true, get: function () { return AgentServer_1.AgentServer; } });
6
+ var SessionManager_1 = require("./session/SessionManager");
7
+ Object.defineProperty(exports, "SessionManager", { enumerable: true, get: function () { return SessionManager_1.SessionManager; } });
8
+ var WebSocketTransport_1 = require("./transport/WebSocketTransport");
9
+ Object.defineProperty(exports, "WebSocketTransport", { enumerable: true, get: function () { return WebSocketTransport_1.WebSocketTransport; } });
10
+ var MonitorBus_1 = require("./monitor/MonitorBus");
11
+ Object.defineProperty(exports, "MonitorBus", { enumerable: true, get: function () { return MonitorBus_1.MonitorBus; } });
12
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,oDAAmD;AAA1C,0GAAA,WAAW,OAAA;AAGpB,2DAA0D;AAAjD,gHAAA,cAAc,OAAA;AAGvB,qEAAoE;AAA3D,wHAAA,kBAAkB,OAAA;AAG3B,mDAAkD;AAAzC,wGAAA,UAAU,OAAA"}
@@ -0,0 +1,31 @@
1
+ import type { AgentEvent } from '@backendkit-labs/agent-core';
2
+ import type { WebSocket } from 'ws';
3
+ export type MonitorFrame = {
4
+ type: 'monitor_hello';
5
+ sessionIds: string[];
6
+ } | {
7
+ type: 'monitor_session_open';
8
+ sessionId: string;
9
+ } | {
10
+ type: 'monitor_session_close';
11
+ sessionId: string;
12
+ } | ({
13
+ sessionId: string;
14
+ } & AgentEvent) | {
15
+ sessionId: string;
16
+ type: 'run_input';
17
+ content: string;
18
+ };
19
+ export declare class MonitorBus {
20
+ private monitors;
21
+ private activeSessions;
22
+ subscribe(socket: WebSocket): void;
23
+ notifySessionOpen(sessionId: string): void;
24
+ notifySessionClose(sessionId: string): void;
25
+ broadcast(sessionId: string, event: AgentEvent): void;
26
+ broadcastRunInput(sessionId: string, content: string): void;
27
+ private fanOut;
28
+ private send;
29
+ get size(): number;
30
+ }
31
+ //# sourceMappingURL=MonitorBus.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MonitorBus.d.ts","sourceRoot":"","sources":["../../src/monitor/MonitorBus.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AAEpC,MAAM,MAAM,YAAY,GAClB;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,UAAU,EAAE,MAAM,EAAE,CAAA;CAAE,GAC/C;IAAE,IAAI,EAAE,sBAAsB,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GACnD;IAAE,IAAI,EAAE,uBAAuB,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GACpD,CAAC;IAAE,SAAS,EAAE,MAAM,CAAA;CAAE,GAAG,UAAU,CAAC,GACpC;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,WAAW,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAEhE,qBAAa,UAAU;IACnB,OAAO,CAAC,QAAQ,CAAwB;IACxC,OAAO,CAAC,cAAc,CAAqB;IAE3C,SAAS,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI;IAUlC,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAK1C,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAK3C,SAAS,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,GAAG,IAAI;IAIrD,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI;IAI3D,OAAO,CAAC,MAAM;IASd,OAAO,CAAC,IAAI;IAMZ,IAAI,IAAI,IAAI,MAAM,CAEjB;CACJ"}
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MonitorBus = void 0;
4
+ class MonitorBus {
5
+ monitors = new Set();
6
+ activeSessions = new Set();
7
+ subscribe(socket) {
8
+ this.monitors.add(socket);
9
+ // Greet the new observer with currently open sessions
10
+ const hello = { type: 'monitor_hello', sessionIds: [...this.activeSessions] };
11
+ this.send(socket, hello);
12
+ socket.on('close', () => this.monitors.delete(socket));
13
+ }
14
+ notifySessionOpen(sessionId) {
15
+ this.activeSessions.add(sessionId);
16
+ this.fanOut({ type: 'monitor_session_open', sessionId });
17
+ }
18
+ notifySessionClose(sessionId) {
19
+ this.activeSessions.delete(sessionId);
20
+ this.fanOut({ type: 'monitor_session_close', sessionId });
21
+ }
22
+ broadcast(sessionId, event) {
23
+ this.fanOut({ sessionId, ...event });
24
+ }
25
+ broadcastRunInput(sessionId, content) {
26
+ this.fanOut({ sessionId, type: 'run_input', content });
27
+ }
28
+ fanOut(frame) {
29
+ const data = JSON.stringify(frame);
30
+ for (const ws of this.monitors) {
31
+ if (ws.readyState === 1 /* OPEN */) {
32
+ try {
33
+ ws.send(data);
34
+ }
35
+ catch { /* ignore — socket may be closing */ }
36
+ }
37
+ }
38
+ }
39
+ send(socket, frame) {
40
+ if (socket.readyState === 1) {
41
+ try {
42
+ socket.send(JSON.stringify(frame));
43
+ }
44
+ catch { /* ignore */ }
45
+ }
46
+ }
47
+ get size() {
48
+ return this.monitors.size;
49
+ }
50
+ }
51
+ exports.MonitorBus = MonitorBus;
52
+ //# sourceMappingURL=MonitorBus.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MonitorBus.js","sourceRoot":"","sources":["../../src/monitor/MonitorBus.ts"],"names":[],"mappings":";;;AAUA,MAAa,UAAU;IACX,QAAQ,GAAG,IAAI,GAAG,EAAa,CAAC;IAChC,cAAc,GAAG,IAAI,GAAG,EAAU,CAAC;IAE3C,SAAS,CAAC,MAAiB;QACvB,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAE1B,sDAAsD;QACtD,MAAM,KAAK,GAAiB,EAAE,IAAI,EAAE,eAAe,EAAE,UAAU,EAAE,CAAC,GAAG,IAAI,CAAC,cAAc,CAAC,EAAE,CAAC;QAC5F,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;QAEzB,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;IAC3D,CAAC;IAED,iBAAiB,CAAC,SAAiB;QAC/B,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QACnC,IAAI,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,sBAAsB,EAAE,SAAS,EAAE,CAAC,CAAC;IAC7D,CAAC;IAED,kBAAkB,CAAC,SAAiB;QAChC,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QACtC,IAAI,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,uBAAuB,EAAE,SAAS,EAAE,CAAC,CAAC;IAC9D,CAAC;IAED,SAAS,CAAC,SAAiB,EAAE,KAAiB;QAC1C,IAAI,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAkB,CAAC,CAAC;IACzD,CAAC;IAED,iBAAiB,CAAC,SAAiB,EAAE,OAAe;QAChD,IAAI,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,CAAC,CAAC;IAC3D,CAAC;IAEO,MAAM,CAAC,KAAmB;QAC9B,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;QACnC,KAAK,MAAM,EAAE,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAC7B,IAAI,EAAE,CAAC,UAAU,KAAK,CAAC,CAAC,UAAU,EAAE,CAAC;gBACjC,IAAI,CAAC;oBAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBAAC,CAAC;gBAAC,MAAM,CAAC,CAAC,oCAAoC,CAAC,CAAC;YACzE,CAAC;QACL,CAAC;IACL,CAAC;IAEO,IAAI,CAAC,MAAiB,EAAE,KAAmB;QAC/C,IAAI,MAAM,CAAC,UAAU,KAAK,CAAC,EAAE,CAAC;YAC1B,IAAI,CAAC;gBAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC;YAAC,CAAC;YAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC;QACtE,CAAC;IACL,CAAC;IAED,IAAI,IAAI;QACJ,OAAO,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC;IAC9B,CAAC;CACJ;AAlDD,gCAkDC"}
@@ -0,0 +1,30 @@
1
+ import type { FastifyRequest, FastifyReply } from 'fastify';
2
+ import { SessionManager } from '../session/SessionManager';
3
+ import type { SessionManagerOptions } from '../session/SessionManager';
4
+ import { MonitorBus } from '../monitor/MonitorBus';
5
+ export type AuthHook = (req: FastifyRequest, reply: FastifyReply) => Promise<void>;
6
+ export interface AgentServerOptions extends SessionManagerOptions {
7
+ /** TCP port to listen on. Default: 3000. */
8
+ port?: number;
9
+ /** Host to bind to. Default: '0.0.0.0'. */
10
+ host?: string;
11
+ /** Optional auth hook — called on every request before route handlers. */
12
+ auth?: AuthHook;
13
+ /** Fastify logger. Default: false (silent). Pass true for pretty logs in dev. */
14
+ logger?: boolean | object;
15
+ /** Allowed CORS origins. Default: '*'. */
16
+ cors?: string | string[];
17
+ }
18
+ export declare class AgentServer {
19
+ private app;
20
+ private sessions;
21
+ private readonly monitor;
22
+ private readonly port;
23
+ private readonly host;
24
+ constructor(opts: AgentServerOptions);
25
+ start(): Promise<string>;
26
+ stop(): Promise<void>;
27
+ get sessionManager(): SessionManager;
28
+ get monitorBus(): MonitorBus;
29
+ }
30
+ //# sourceMappingURL=AgentServer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AgentServer.d.ts","sourceRoot":"","sources":["../../src/server/AgentServer.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAmB,cAAc,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AACvE,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAGnD,MAAM,MAAM,QAAQ,GAAG,CAAC,GAAG,EAAE,cAAc,EAAE,KAAK,EAAE,YAAY,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;AAEnF,MAAM,WAAW,kBAAmB,SAAQ,qBAAqB;IAC7D,4CAA4C;IAC5C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,2CAA2C;IAC3C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,0EAA0E;IAC1E,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB,iFAAiF;IACjF,MAAM,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IAC1B,0CAA0C;IAC1C,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;CAC5B;AAED,qBAAa,WAAW;IACpB,OAAO,CAAC,GAAG,CAAkB;IAC7B,OAAO,CAAC,QAAQ,CAAiB;IACjC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAa;IACrC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAS;IAC9B,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAS;gBAElB,IAAI,EAAE,kBAAkB;IA4C9B,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC;IAKxB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAK3B,IAAI,cAAc,IAAI,cAAc,CAEnC;IAED,IAAI,UAAU,IAAI,UAAU,CAE3B;CACJ"}
@@ -0,0 +1,71 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.AgentServer = void 0;
7
+ const fastify_1 = __importDefault(require("fastify"));
8
+ const websocket_1 = __importDefault(require("@fastify/websocket"));
9
+ const SessionManager_1 = require("../session/SessionManager");
10
+ const MonitorBus_1 = require("../monitor/MonitorBus");
11
+ const routes_1 = require("./routes");
12
+ class AgentServer {
13
+ app;
14
+ sessions;
15
+ monitor;
16
+ port;
17
+ host;
18
+ constructor(opts) {
19
+ this.port = opts.port ?? 3000;
20
+ this.host = opts.host ?? '0.0.0.0';
21
+ this.monitor = new MonitorBus_1.MonitorBus();
22
+ this.sessions = new SessionManager_1.SessionManager({
23
+ engineFactory: opts.engineFactory,
24
+ maxIdleMs: opts.maxIdleMs,
25
+ maxSessions: opts.maxSessions,
26
+ monitorBus: this.monitor,
27
+ });
28
+ this.app = (0, fastify_1.default)({ logger: opts.logger ?? false });
29
+ // CORS headers
30
+ const origins = opts.cors ?? '*';
31
+ this.app.addHook('onSend', async (_req, reply) => {
32
+ const origin = Array.isArray(origins) ? origins.join(', ') : origins;
33
+ reply.header('Access-Control-Allow-Origin', origin);
34
+ });
35
+ this.app.options('*', async (_req, reply) => {
36
+ const origin = Array.isArray(origins) ? origins.join(', ') : origins;
37
+ reply.header('Access-Control-Allow-Origin', origin);
38
+ reply.header('Access-Control-Allow-Methods', 'GET, POST, DELETE, OPTIONS');
39
+ reply.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');
40
+ reply.code(204).send();
41
+ });
42
+ if (opts.auth) {
43
+ const authHook = opts.auth;
44
+ this.app.addHook('preHandler', authHook);
45
+ }
46
+ // Register websocket plugin + all routes inside a register() so Fastify
47
+ // resolves the plugin before wiring routes (required for WS routes in v11).
48
+ const sessions = this.sessions;
49
+ const monitor = this.monitor;
50
+ this.app.register(async (instance) => {
51
+ await instance.register(websocket_1.default);
52
+ (0, routes_1.registerRoutes)(instance, sessions, monitor);
53
+ });
54
+ }
55
+ async start() {
56
+ const address = await this.app.listen({ port: this.port, host: this.host });
57
+ return address;
58
+ }
59
+ async stop() {
60
+ this.sessions.close();
61
+ await this.app.close();
62
+ }
63
+ get sessionManager() {
64
+ return this.sessions;
65
+ }
66
+ get monitorBus() {
67
+ return this.monitor;
68
+ }
69
+ }
70
+ exports.AgentServer = AgentServer;
71
+ //# sourceMappingURL=AgentServer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AgentServer.js","sourceRoot":"","sources":["../../src/server/AgentServer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ import type { FastifyInstance } from 'fastify';
2
+ import type { SessionManager } from '../session/SessionManager';
3
+ import type { MonitorBus } from '../monitor/MonitorBus';
4
+ export declare function registerRoutes(app: FastifyInstance, sessions: SessionManager, monitorBus?: MonitorBus): void;
5
+ //# sourceMappingURL=routes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../src/server/routes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE/C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAExD,wBAAgB,cAAc,CAAC,GAAG,EAAE,eAAe,EAAE,QAAQ,EAAE,cAAc,EAAE,UAAU,CAAC,EAAE,UAAU,GAAG,IAAI,CAgF5G"}
@@ -0,0 +1,77 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerRoutes = registerRoutes;
4
+ function registerRoutes(app, sessions, monitorBus) {
5
+ // ── Health ────────────────────────────────────────────────────────────────
6
+ app.get('/health', async () => ({
7
+ status: 'ok',
8
+ sessions: sessions.size,
9
+ monitors: monitorBus?.size ?? 0,
10
+ timestamp: new Date().toISOString(),
11
+ }));
12
+ // ── Sessions ─────────────────────────────────────────────────────────────
13
+ app.get('/sessions', async () => ({ sessions: sessions.list() }));
14
+ app.delete('/sessions/:id', async (req, reply) => {
15
+ const session = sessions.get(req.params.id);
16
+ if (!session)
17
+ return reply.code(404).send({ error: 'Session not found' });
18
+ sessions.destroy(req.params.id);
19
+ return { ok: true };
20
+ });
21
+ // ── WebSocket chat ────────────────────────────────────────────────────────
22
+ // Client connects → session is created → messages flow over the socket.
23
+ // The engine sends AgentEvents as JSON frames; the client sends:
24
+ // { type: 'input', content: '...' } ← user message
25
+ // { type: 'abort' } ← cancel current run
26
+ app.get('/chat', { websocket: true }, (socket, _req) => {
27
+ let session;
28
+ try {
29
+ session = sessions.create(socket);
30
+ // Announce session id so the client can reference it
31
+ socket.send(JSON.stringify({ type: 'session_created', sessionId: session.id }));
32
+ }
33
+ catch (err) {
34
+ const message = err instanceof Error ? err.message : 'Failed to create session';
35
+ socket.send(JSON.stringify({ type: 'error', message }));
36
+ socket.close();
37
+ return;
38
+ }
39
+ socket.on('message', (raw) => {
40
+ try {
41
+ const msg = JSON.parse(raw.toString());
42
+ if (msg.type === 'input' && typeof msg.content === 'string') {
43
+ sessions.get(session.id);
44
+ // Broadcast user input to monitor observers before running
45
+ monitorBus?.broadcastRunInput(session.id, msg.content);
46
+ // Fire-and-forget: engine.run() handles its own error boundary
47
+ void session.engine.run(msg.content).catch((err) => {
48
+ const message = err instanceof Error ? err.message : 'Engine error';
49
+ socket.send(JSON.stringify({ type: 'error', message }));
50
+ });
51
+ }
52
+ if (msg.type === 'abort') {
53
+ session.engine.abort();
54
+ }
55
+ }
56
+ catch {
57
+ // ignore malformed frames
58
+ }
59
+ });
60
+ // Cleanup is handled by SessionManager's socket.on('close') hook
61
+ });
62
+ // ── WebSocket monitor ─────────────────────────────────────────────────────
63
+ // Read-only observer — receives all session events tagged with sessionId.
64
+ // Protocol (server → client):
65
+ // { type: 'monitor_hello', sessionIds: string[] }
66
+ // { type: 'monitor_session_open', sessionId }
67
+ // { type: 'monitor_session_close', sessionId }
68
+ // { sessionId, type: 'run_input', content }
69
+ // { sessionId, ...AgentEvent }
70
+ if (monitorBus) {
71
+ const bus = monitorBus;
72
+ app.get('/monitor', { websocket: true }, (socket, _req) => {
73
+ bus.subscribe(socket);
74
+ });
75
+ }
76
+ }
77
+ //# sourceMappingURL=routes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routes.js","sourceRoot":"","sources":["../../src/server/routes.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ import type { AgentEngine } from '@backendkit-labs/agent-core';
2
+ import { WebSocketTransport } from '../transport/WebSocketTransport';
3
+ import type { MonitorBus } from '../monitor/MonitorBus';
4
+ import type { WebSocket } from 'ws';
5
+ export interface Session {
6
+ id: string;
7
+ engine: AgentEngine;
8
+ transport: WebSocketTransport;
9
+ createdAt: Date;
10
+ lastActivityAt: Date;
11
+ }
12
+ export type EngineFactory = (sessionId: string, transport: WebSocketTransport) => AgentEngine;
13
+ export interface SessionManagerOptions {
14
+ /** Factory that creates a fresh AgentEngine for a session. Called once per session. */
15
+ engineFactory: EngineFactory;
16
+ /** Max idle time in ms before a session is automatically evicted. Default: 30 minutes. */
17
+ maxIdleMs?: number;
18
+ /** Max concurrent sessions. Rejects new sessions when reached. Default: 100. */
19
+ maxSessions?: number;
20
+ /** Optional monitor bus — broadcasts all session events to connected observers. */
21
+ monitorBus?: MonitorBus;
22
+ }
23
+ export declare class SessionManager {
24
+ private sessions;
25
+ private readonly maxIdleMs;
26
+ private readonly maxSessions;
27
+ private readonly engineFactory;
28
+ private readonly monitorBus;
29
+ private sweepTimer;
30
+ constructor(opts: SessionManagerOptions);
31
+ /** Create a new session and wire a WebSocket to it. */
32
+ create(socket: WebSocket): Session;
33
+ get(id: string): Session | undefined;
34
+ destroy(id: string): void;
35
+ list(): Pick<Session, 'id' | 'createdAt' | 'lastActivityAt'>[];
36
+ get size(): number;
37
+ close(): void;
38
+ private sweepIdle;
39
+ }
40
+ //# sourceMappingURL=SessionManager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SessionManager.d.ts","sourceRoot":"","sources":["../../src/session/SessionManager.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAc,MAAM,6BAA6B,CAAC;AAC3E,OAAO,EAAE,kBAAkB,EAAE,MAAM,iCAAiC,CAAC;AACrE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AAEpC,MAAM,WAAW,OAAO;IACpB,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,WAAW,CAAC;IACpB,SAAS,EAAE,kBAAkB,CAAC;IAC9B,SAAS,EAAE,IAAI,CAAC;IAChB,cAAc,EAAE,IAAI,CAAC;CACxB;AAED,MAAM,MAAM,aAAa,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,kBAAkB,KAAK,WAAW,CAAC;AAE9F,MAAM,WAAW,qBAAqB;IAClC,uFAAuF;IACvF,aAAa,EAAE,aAAa,CAAC;IAC7B,0FAA0F;IAC1F,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gFAAgF;IAChF,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,mFAAmF;IACnF,UAAU,CAAC,EAAE,UAAU,CAAC;CAC3B;AAED,qBAAa,cAAc;IACvB,OAAO,CAAC,QAAQ,CAA8B;IAC9C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAgB;IAC9C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAyB;IACpD,OAAO,CAAC,UAAU,CAA+C;gBAErD,IAAI,EAAE,qBAAqB;IAWvC,uDAAuD;IACvD,MAAM,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO;IAoClC,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS;IAMpC,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IASzB,IAAI,IAAI,IAAI,CAAC,OAAO,EAAE,IAAI,GAAG,WAAW,GAAG,gBAAgB,CAAC,EAAE;IAM9D,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,KAAK,IAAI,IAAI;IAQb,OAAO,CAAC,SAAS;CAQpB"}
@@ -0,0 +1,98 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SessionManager = void 0;
4
+ const uuid_1 = require("uuid");
5
+ const WebSocketTransport_1 = require("../transport/WebSocketTransport");
6
+ class SessionManager {
7
+ sessions = new Map();
8
+ maxIdleMs;
9
+ maxSessions;
10
+ engineFactory;
11
+ monitorBus;
12
+ sweepTimer = null;
13
+ constructor(opts) {
14
+ this.engineFactory = opts.engineFactory;
15
+ this.maxIdleMs = opts.maxIdleMs ?? 30 * 60 * 1000;
16
+ this.maxSessions = opts.maxSessions ?? 100;
17
+ this.monitorBus = opts.monitorBus;
18
+ // sweep idle sessions every minute
19
+ this.sweepTimer = setInterval(() => this.sweepIdle(), 60_000);
20
+ this.sweepTimer.unref?.();
21
+ }
22
+ /** Create a new session and wire a WebSocket to it. */
23
+ create(socket) {
24
+ if (this.sessions.size >= this.maxSessions) {
25
+ throw new Error(`Max concurrent sessions (${this.maxSessions}) reached`);
26
+ }
27
+ const id = (0, uuid_1.v4)();
28
+ const transport = new WebSocketTransport_1.WebSocketTransport(socket);
29
+ // Tap emit to fan out every AgentEvent to the monitor bus
30
+ if (this.monitorBus) {
31
+ const bus = this.monitorBus;
32
+ const origEmit = transport.emit.bind(transport);
33
+ transport.emit = (event) => {
34
+ origEmit(event);
35
+ bus.broadcast(id, event);
36
+ };
37
+ }
38
+ const engine = this.engineFactory(id, transport);
39
+ const session = {
40
+ id,
41
+ engine,
42
+ transport,
43
+ createdAt: new Date(),
44
+ lastActivityAt: new Date(),
45
+ };
46
+ this.sessions.set(id, session);
47
+ this.monitorBus?.notifySessionOpen(id);
48
+ socket.on('close', () => this.destroy(id));
49
+ return session;
50
+ }
51
+ get(id) {
52
+ const session = this.sessions.get(id);
53
+ if (session)
54
+ session.lastActivityAt = new Date();
55
+ return session;
56
+ }
57
+ destroy(id) {
58
+ const session = this.sessions.get(id);
59
+ if (!session)
60
+ return;
61
+ try {
62
+ session.engine.abort();
63
+ }
64
+ catch { /* engine may already be idle */ }
65
+ try {
66
+ session.transport.close();
67
+ }
68
+ catch { /* may already be closed */ }
69
+ this.sessions.delete(id);
70
+ this.monitorBus?.notifySessionClose(id);
71
+ }
72
+ list() {
73
+ return [...this.sessions.values()].map(({ id, createdAt, lastActivityAt }) => ({
74
+ id, createdAt, lastActivityAt,
75
+ }));
76
+ }
77
+ get size() {
78
+ return this.sessions.size;
79
+ }
80
+ close() {
81
+ if (this.sweepTimer) {
82
+ clearInterval(this.sweepTimer);
83
+ this.sweepTimer = null;
84
+ }
85
+ for (const id of [...this.sessions.keys()])
86
+ this.destroy(id);
87
+ }
88
+ sweepIdle() {
89
+ const cutoff = Date.now() - this.maxIdleMs;
90
+ for (const [id, session] of this.sessions) {
91
+ if (session.lastActivityAt.getTime() < cutoff) {
92
+ this.destroy(id);
93
+ }
94
+ }
95
+ }
96
+ }
97
+ exports.SessionManager = SessionManager;
98
+ //# sourceMappingURL=SessionManager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SessionManager.js","sourceRoot":"","sources":["../../src/session/SessionManager.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ import type { AgentEvent } from '@backendkit-labs/agent-core';
2
+ import type { Transport, Unsubscribe } from '@backendkit-labs/agent-core';
3
+ import type { WebSocket } from 'ws';
4
+ export type WsMessage = {
5
+ type: 'input';
6
+ content: string;
7
+ } | {
8
+ type: 'approval';
9
+ decision: 'approve' | 'approve_all' | 'reject';
10
+ };
11
+ export declare class WebSocketTransport implements Transport {
12
+ private readonly socket;
13
+ private inputHandlers;
14
+ private closed;
15
+ constructor(socket: WebSocket);
16
+ emit(event: AgentEvent): void;
17
+ onInput(handler: (input: string) => void): Unsubscribe;
18
+ close(): void;
19
+ get isOpen(): boolean;
20
+ }
21
+ //# sourceMappingURL=WebSocketTransport.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WebSocketTransport.d.ts","sourceRoot":"","sources":["../../src/transport/WebSocketTransport.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAC1E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AAEpC,MAAM,MAAM,SAAS,GACf;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAClC;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,QAAQ,EAAE,SAAS,GAAG,aAAa,GAAG,QAAQ,CAAA;CAAE,CAAC;AAE3E,qBAAa,kBAAmB,YAAW,SAAS;IAIpC,OAAO,CAAC,QAAQ,CAAC,MAAM;IAHnC,OAAO,CAAC,aAAa,CAAsC;IAC3D,OAAO,CAAC,MAAM,CAAS;gBAEM,MAAM,EAAE,SAAS;IAkB9C,IAAI,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI;IAS7B,OAAO,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,GAAG,WAAW;IAOtD,KAAK,IAAI,IAAI;IAUb,IAAI,MAAM,IAAI,OAAO,CAEpB;CACJ"}
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.WebSocketTransport = void 0;
4
+ class WebSocketTransport {
5
+ socket;
6
+ inputHandlers = [];
7
+ closed = false;
8
+ constructor(socket) {
9
+ this.socket = socket;
10
+ socket.on('message', (raw) => {
11
+ try {
12
+ const msg = JSON.parse(raw.toString());
13
+ if (msg.type === 'input') {
14
+ for (const h of this.inputHandlers)
15
+ h(msg.content);
16
+ }
17
+ }
18
+ catch {
19
+ // ignore malformed frames
20
+ }
21
+ });
22
+ socket.on('close', () => {
23
+ this.closed = true;
24
+ this.inputHandlers = [];
25
+ });
26
+ }
27
+ emit(event) {
28
+ if (this.closed)
29
+ return;
30
+ try {
31
+ this.socket.send(JSON.stringify(event));
32
+ }
33
+ catch {
34
+ // socket may have closed between the guard and send
35
+ }
36
+ }
37
+ onInput(handler) {
38
+ this.inputHandlers.push(handler);
39
+ return () => {
40
+ this.inputHandlers = this.inputHandlers.filter(h => h !== handler);
41
+ };
42
+ }
43
+ close() {
44
+ this.closed = true;
45
+ this.inputHandlers = [];
46
+ try {
47
+ this.socket.close();
48
+ }
49
+ catch {
50
+ // already closed
51
+ }
52
+ }
53
+ get isOpen() {
54
+ return !this.closed && this.socket.readyState === 1; // OPEN
55
+ }
56
+ }
57
+ exports.WebSocketTransport = WebSocketTransport;
58
+ //# sourceMappingURL=WebSocketTransport.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WebSocketTransport.js","sourceRoot":"","sources":["../../src/transport/WebSocketTransport.ts"],"names":[],"mappings":";;;AAQA,MAAa,kBAAkB;IAIE;IAHrB,aAAa,GAAmC,EAAE,CAAC;IACnD,MAAM,GAAG,KAAK,CAAC;IAEvB,YAA6B,MAAiB;QAAjB,WAAM,GAAN,MAAM,CAAW;QAC1C,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,GAAoB,EAAE,EAAE;YAC1C,IAAI,CAAC;gBACD,MAAM,GAAG,GAAc,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC;gBAClD,IAAI,GAAG,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;oBACvB,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,aAAa;wBAAE,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;gBACvD,CAAC;YACL,CAAC;YAAC,MAAM,CAAC;gBACL,0BAA0B;YAC9B,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;YACpB,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;YACnB,IAAI,CAAC,aAAa,GAAG,EAAE,CAAC;QAC5B,CAAC,CAAC,CAAC;IACP,CAAC;IAED,IAAI,CAAC,KAAiB;QAClB,IAAI,IAAI,CAAC,MAAM;YAAE,OAAO;QACxB,IAAI,CAAC;YACD,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC;QAC5C,CAAC;QAAC,MAAM,CAAC;YACL,oDAAoD;QACxD,CAAC;IACL,CAAC;IAED,OAAO,CAAC,OAAgC;QACpC,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACjC,OAAO,GAAG,EAAE;YACR,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC;QACvE,CAAC,CAAC;IACN,CAAC;IAED,KAAK;QACD,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;QACnB,IAAI,CAAC,aAAa,GAAG,EAAE,CAAC;QACxB,IAAI,CAAC;YACD,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;QACxB,CAAC;QAAC,MAAM,CAAC;YACL,iBAAiB;QACrB,CAAC;IACL,CAAC;IAED,IAAI,MAAM;QACN,OAAO,CAAC,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,MAAM,CAAC,UAAU,KAAK,CAAC,CAAC,CAAC,OAAO;IAChE,CAAC;CACJ;AAnDD,gDAmDC"}
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "@backendkit-labs/agent-web",
3
+ "version": "0.1.0",
4
+ "description": "HTTP/WebSocket server layer for agent-core — browser-ready streaming agent sessions",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "files": [
8
+ "dist/",
9
+ "client/",
10
+ "README.md"
11
+ ],
12
+ "scripts": {
13
+ "build": "tsc",
14
+ "dev": "tsc --watch",
15
+ "typecheck": "tsc --noEmit",
16
+ "prepublishOnly": "npm run build"
17
+ },
18
+ "peerDependencies": {
19
+ "@backendkit-labs/agent-core": ">=0.21.0"
20
+ },
21
+ "dependencies": {
22
+ "@fastify/websocket": "^11.0.0",
23
+ "fastify": "^5.0.0",
24
+ "uuid": "^10.0.0"
25
+ },
26
+ "devDependencies": {
27
+ "@backendkit-labs/agent-core": "*",
28
+ "@types/node": "^20.0.0",
29
+ "@types/uuid": "^10.0.0",
30
+ "@types/ws": "^8.0.0",
31
+ "typescript": "^5.4.0"
32
+ },
33
+ "engines": {
34
+ "node": ">=20.0.0"
35
+ },
36
+ "publishConfig": {
37
+ "access": "public"
38
+ }
39
+ }