@miriad-systems/backend 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.
@@ -0,0 +1,347 @@
1
+ /**
2
+ * Runtime Client
3
+ *
4
+ * Manages the WebSocket connection to the CAST backend.
5
+ * Handles the LocalRuntime protocol: authentication, message routing,
6
+ * and agent lifecycle coordination.
7
+ */
8
+ import WebSocket from 'ws';
9
+ import { AgentManager } from './agent-manager.js';
10
+ import { getMachineInfo } from './config.js';
11
+ // =============================================================================
12
+ // Runtime Client
13
+ // =============================================================================
14
+ export class RuntimeClient {
15
+ runtimeConfig;
16
+ agentManager;
17
+ ws = null;
18
+ status = 'disconnected';
19
+ reconnectAttempts = 0;
20
+ reconnectTimeout = null;
21
+ heartbeatInterval = null;
22
+ /** Heartbeat interval in milliseconds (30 seconds) */
23
+ static HEARTBEAT_INTERVAL_MS = 30000;
24
+ onConnected;
25
+ onDisconnected;
26
+ onError;
27
+ constructor(config) {
28
+ this.runtimeConfig = config.config;
29
+ this.onConnected = config.onConnected;
30
+ this.onDisconnected = config.onDisconnected;
31
+ this.onError = config.onError;
32
+ // Create agent manager
33
+ this.agentManager = new AgentManager({
34
+ workspaceBasePath: this.runtimeConfig.workspace.basePath,
35
+ onFrame: (message) => this.sendFrame(message),
36
+ onCheckin: (agentId) => this.sendCheckin(agentId),
37
+ onError: (agentId, error) => {
38
+ console.error(`[RuntimeClient] Agent ${agentId} error:`, error);
39
+ },
40
+ });
41
+ }
42
+ /**
43
+ * Get current runtime status.
44
+ */
45
+ getStatus() {
46
+ return this.status;
47
+ }
48
+ /**
49
+ * Get agent manager for status queries.
50
+ */
51
+ getAgentManager() {
52
+ return this.agentManager;
53
+ }
54
+ /**
55
+ * Connect to the backend.
56
+ */
57
+ async connect() {
58
+ if (this.ws) {
59
+ console.log('[RuntimeClient] Already connected');
60
+ return;
61
+ }
62
+ this.status = 'connecting';
63
+ const { credentials } = this.runtimeConfig;
64
+ // Use query param for protocol - works with both local dev and AWS Lambda
65
+ // (API Gateway doesn't have /runtimes/connect route, uses $connect with query param)
66
+ const url = `${credentials.wsUrl}?protocol=runtime`;
67
+ console.log(`[RuntimeClient] Connecting to ${url}`);
68
+ return new Promise((resolve, reject) => {
69
+ this.ws = new WebSocket(url, {
70
+ headers: {
71
+ Authorization: `Server ${credentials.secret}`,
72
+ },
73
+ });
74
+ this.ws.on('open', () => {
75
+ console.log('[RuntimeClient] Connected');
76
+ this.status = 'connected';
77
+ this.reconnectAttempts = 0;
78
+ this.sendRuntimeReady();
79
+ resolve();
80
+ });
81
+ this.ws.on('message', (data) => {
82
+ this.handleMessage(data.toString());
83
+ });
84
+ this.ws.on('close', (code, reason) => {
85
+ console.log(`[RuntimeClient] Disconnected: ${code} ${reason.toString()}`);
86
+ this.status = 'disconnected';
87
+ this.ws = null;
88
+ this.stopHeartbeatInterval();
89
+ this.onDisconnected?.(code, reason.toString());
90
+ this.scheduleReconnect();
91
+ });
92
+ this.ws.on('error', (error) => {
93
+ // Log connection errors cleanly without full stack traces
94
+ const errorMsg = this.formatConnectionError(error);
95
+ if (errorMsg) {
96
+ console.error(`[RuntimeClient] ${errorMsg}`);
97
+ }
98
+ else {
99
+ console.error('[RuntimeClient] WebSocket error:', error);
100
+ }
101
+ this.onError?.(error);
102
+ if (this.status === 'connecting') {
103
+ reject(error);
104
+ }
105
+ });
106
+ });
107
+ }
108
+ /**
109
+ * Disconnect from the backend.
110
+ */
111
+ async disconnect() {
112
+ if (this.reconnectTimeout) {
113
+ clearTimeout(this.reconnectTimeout);
114
+ this.reconnectTimeout = null;
115
+ }
116
+ // Stop heartbeat interval
117
+ this.stopHeartbeatInterval();
118
+ // Suspend all agents
119
+ await this.agentManager.suspendAll();
120
+ if (this.ws) {
121
+ this.ws.close(1000, 'Client disconnect');
122
+ this.ws = null;
123
+ }
124
+ this.status = 'disconnected';
125
+ }
126
+ // ===========================================================================
127
+ // Message Handling
128
+ // ===========================================================================
129
+ handleMessage(data) {
130
+ let message;
131
+ try {
132
+ message = JSON.parse(data);
133
+ }
134
+ catch {
135
+ console.error('[RuntimeClient] Invalid JSON:', data);
136
+ return;
137
+ }
138
+ switch (message.type) {
139
+ case 'runtime_connected':
140
+ console.log(`[RuntimeClient] Runtime connected: ${message.runtimeId} (protocol ${message.protocolVersion})`);
141
+ this.status = 'ready';
142
+ this.onConnected?.();
143
+ // Re-checkin all active agents on reconnect
144
+ // This ensures backend knows about agents that survived the disconnect
145
+ this.reCheckinActiveAgents();
146
+ // Start heartbeat interval for agent liveness
147
+ this.startHeartbeatInterval();
148
+ break;
149
+ case 'activate':
150
+ this.handleActivate(message);
151
+ break;
152
+ case 'message':
153
+ this.handleDeliverMessage(message);
154
+ break;
155
+ case 'suspend':
156
+ this.handleSuspend(message);
157
+ break;
158
+ case 'ping':
159
+ this.sendPong(message.timestamp);
160
+ break;
161
+ case 'error':
162
+ console.error(`[RuntimeClient] Backend error: ${message.code} - ${message.message}`);
163
+ break;
164
+ default:
165
+ console.log(`[RuntimeClient] Unknown message type: ${message.type}`);
166
+ }
167
+ }
168
+ async handleActivate(message) {
169
+ console.log(`[RuntimeClient] Activate agent: ${message.agentId}`);
170
+ await this.agentManager.activate(message);
171
+ }
172
+ async handleDeliverMessage(message) {
173
+ console.log(`[RuntimeClient] Message for agent: ${message.agentId}`);
174
+ await this.agentManager.deliverMessage(message);
175
+ }
176
+ async handleSuspend(message) {
177
+ console.log(`[RuntimeClient] Suspend agent: ${message.agentId}`);
178
+ await this.agentManager.suspend(message);
179
+ }
180
+ // ===========================================================================
181
+ // Outgoing Messages
182
+ // ===========================================================================
183
+ send(message) {
184
+ if (this.ws && this.ws.readyState === WebSocket.OPEN) {
185
+ this.ws.send(JSON.stringify(message));
186
+ }
187
+ else {
188
+ console.error('[RuntimeClient] Cannot send: WebSocket not open');
189
+ }
190
+ }
191
+ sendRuntimeReady() {
192
+ const { credentials, spaceId, name } = this.runtimeConfig;
193
+ const machineInfo = getMachineInfo();
194
+ const message = {
195
+ type: 'runtime_ready',
196
+ runtimeId: credentials.runtimeId,
197
+ spaceId,
198
+ name,
199
+ machineInfo,
200
+ };
201
+ console.log(`[RuntimeClient] Sending runtime_ready: ${name} (${credentials.runtimeId})`);
202
+ this.send(message);
203
+ }
204
+ sendCheckin(agentId) {
205
+ const message = {
206
+ type: 'agent_checkin',
207
+ agentId,
208
+ };
209
+ console.log(`[RuntimeClient] Agent checkin: ${agentId}`);
210
+ this.send(message);
211
+ }
212
+ sendFrame(frameMessage) {
213
+ this.send(frameMessage);
214
+ }
215
+ sendPong(timestamp) {
216
+ const message = {
217
+ type: 'pong',
218
+ timestamp,
219
+ };
220
+ this.send(message);
221
+ }
222
+ /**
223
+ * Re-checkin all active agents after reconnection.
224
+ * This notifies the backend about agents that survived the disconnect.
225
+ */
226
+ reCheckinActiveAgents() {
227
+ const agents = this.agentManager.getAgents();
228
+ const activeAgents = agents.filter((a) => a.status !== 'offline');
229
+ if (activeAgents.length === 0) {
230
+ console.log('[RuntimeClient] No active agents to re-checkin');
231
+ return;
232
+ }
233
+ console.log(`[RuntimeClient] Re-checking in ${activeAgents.length} active agent(s)`);
234
+ for (const agent of activeAgents) {
235
+ this.sendCheckin(agent.agentId);
236
+ }
237
+ }
238
+ // ===========================================================================
239
+ // Heartbeat
240
+ // ===========================================================================
241
+ /**
242
+ * Start periodic heartbeat for all online agents.
243
+ */
244
+ startHeartbeatInterval() {
245
+ if (this.heartbeatInterval) {
246
+ return; // Already running
247
+ }
248
+ console.log(`[RuntimeClient] Starting heartbeat interval (${RuntimeClient.HEARTBEAT_INTERVAL_MS / 1000}s)`);
249
+ this.heartbeatInterval = setInterval(() => {
250
+ this.sendHeartbeats();
251
+ }, RuntimeClient.HEARTBEAT_INTERVAL_MS);
252
+ }
253
+ /**
254
+ * Stop the heartbeat interval.
255
+ */
256
+ stopHeartbeatInterval() {
257
+ if (this.heartbeatInterval) {
258
+ clearInterval(this.heartbeatInterval);
259
+ this.heartbeatInterval = null;
260
+ console.log('[RuntimeClient] Stopped heartbeat interval');
261
+ }
262
+ }
263
+ /**
264
+ * Send heartbeats for all online agents.
265
+ */
266
+ sendHeartbeats() {
267
+ const agents = this.agentManager.getAgents();
268
+ const onlineAgents = agents.filter((a) => a.status === 'online' || a.status === 'busy');
269
+ if (onlineAgents.length === 0) {
270
+ return; // No agents to heartbeat
271
+ }
272
+ for (const agent of onlineAgents) {
273
+ const message = {
274
+ type: 'agent_heartbeat',
275
+ agentId: agent.agentId,
276
+ };
277
+ this.send(message);
278
+ }
279
+ }
280
+ // ===========================================================================
281
+ // Error Formatting
282
+ // ===========================================================================
283
+ /**
284
+ * Format common connection errors into clean single-line messages.
285
+ * Returns null for unexpected errors that should show full stack trace.
286
+ */
287
+ formatConnectionError(error) {
288
+ const message = error.message || '';
289
+ const code = error.code;
290
+ // Handle AggregateError (multiple connection attempts failed)
291
+ if (error.name === 'AggregateError' && 'errors' in error) {
292
+ const aggError = error;
293
+ if (aggError.errors.length > 0) {
294
+ const firstError = aggError.errors[0];
295
+ if (firstError.code === 'ECONNREFUSED') {
296
+ return 'Connection refused - server unavailable';
297
+ }
298
+ if (firstError.code === 'ETIMEDOUT') {
299
+ return 'Connection timed out';
300
+ }
301
+ }
302
+ }
303
+ // Handle direct error codes
304
+ if (code === 'ECONNREFUSED') {
305
+ return 'Connection refused - server unavailable';
306
+ }
307
+ if (code === 'ETIMEDOUT') {
308
+ return 'Connection timed out';
309
+ }
310
+ if (code === 'ENOTFOUND') {
311
+ return 'Server not found - check URL';
312
+ }
313
+ if (code === 'ECONNRESET') {
314
+ return 'Connection reset by server';
315
+ }
316
+ // Check message patterns
317
+ if (message.includes('ECONNREFUSED')) {
318
+ return 'Connection refused - server unavailable';
319
+ }
320
+ if (message.includes('ETIMEDOUT')) {
321
+ return 'Connection timed out';
322
+ }
323
+ return null;
324
+ }
325
+ // ===========================================================================
326
+ // Reconnection
327
+ // ===========================================================================
328
+ scheduleReconnect() {
329
+ if (this.reconnectTimeout)
330
+ return;
331
+ // Exponential backoff: 1s, 2s, 4s, 8s, 16s, max 30s
332
+ const delay = Math.min(1000 * Math.pow(2, this.reconnectAttempts), 30000);
333
+ this.reconnectAttempts++;
334
+ console.log(`[RuntimeClient] Reconnecting in ${delay / 1000}s (attempt ${this.reconnectAttempts})`);
335
+ this.reconnectTimeout = setTimeout(async () => {
336
+ this.reconnectTimeout = null;
337
+ try {
338
+ await this.connect();
339
+ }
340
+ catch {
341
+ // Error already logged by WebSocket error handler
342
+ // Will be scheduled again by close handler
343
+ }
344
+ }, delay);
345
+ }
346
+ }
347
+ //# sourceMappingURL=runtime-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime-client.js","sourceRoot":"","sources":["../src/runtime-client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Tymbal Bridge for Local Runtime
3
+ *
4
+ * Translates Claude Agent SDK messages to Tymbal protocol frames
5
+ * and sends them to the backend via the runtime's WS connection.
6
+ *
7
+ * Adapted from local-agent-engine for multi-agent support:
8
+ * - Frames are wrapped with agentId for routing
9
+ * - Uses callback instead of direct WS send (runtime manages connection)
10
+ */
11
+ import type { SDKMessage } from '@anthropic-ai/claude-agent-sdk';
12
+ import type { AgentFrameMessage } from './types.js';
13
+ export interface TymbalBridgeConfig {
14
+ /** Agent ID ({spaceId}:{channelId}:{callsign}) */
15
+ agentId: string;
16
+ /** Agent callsign for frame sender attribution */
17
+ callsign: string;
18
+ /** Callback to send frames to runtime */
19
+ onFrame: (message: AgentFrameMessage) => void;
20
+ }
21
+ export declare class TymbalBridge {
22
+ private readonly agentId;
23
+ private readonly callsign;
24
+ private readonly onFrame;
25
+ private sessionId;
26
+ private currentAssistantMsgId;
27
+ private assistantContent;
28
+ private startFrameEmitted;
29
+ constructor(config: TymbalBridgeConfig);
30
+ getSessionId(): string | null;
31
+ getCallsign(): string;
32
+ /**
33
+ * Process an SDK message and emit appropriate Tymbal frames.
34
+ */
35
+ processSDKMessage(message: SDKMessage): Promise<void>;
36
+ private handleSystem;
37
+ private handlePartialAssistant;
38
+ private handleAssistant;
39
+ private handleUser;
40
+ private handleResult;
41
+ finalize(): Promise<void>;
42
+ /**
43
+ * Emit a frame wrapped with agentId for routing.
44
+ */
45
+ private emitFrame;
46
+ }
47
+ //# sourceMappingURL=tymbal-bridge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tymbal-bridge.d.ts","sourceRoot":"","sources":["../src/tymbal-bridge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EACV,UAAU,EAMX,MAAM,gCAAgC,CAAC;AAExC,OAAO,KAAK,EAA0C,iBAAiB,EAAE,MAAM,YAAY,CAAC;AA6B5F,MAAM,WAAW,kBAAkB;IACjC,kDAAkD;IAClD,OAAO,EAAE,MAAM,CAAC;IAChB,kDAAkD;IAClD,QAAQ,EAAE,MAAM,CAAC;IACjB,yCAAyC;IACzC,OAAO,EAAE,CAAC,OAAO,EAAE,iBAAiB,KAAK,IAAI,CAAC;CAC/C;AAED,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAuC;IAG/D,OAAO,CAAC,SAAS,CAAuB;IAGxC,OAAO,CAAC,qBAAqB,CAAuB;IACpD,OAAO,CAAC,gBAAgB,CAAc;IACtC,OAAO,CAAC,iBAAiB,CAAkB;gBAE/B,MAAM,EAAE,kBAAkB;IAMtC,YAAY,IAAI,MAAM,GAAG,IAAI;IAI7B,WAAW,IAAI,MAAM;IAIrB;;OAEG;IACG,iBAAiB,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;YA+B7C,YAAY;YAOZ,sBAAsB;YA2BtB,eAAe;YA0Df,UAAU;YA2CV,YAAY;IAwFpB,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAkB/B;;OAEG;YACW,SAAS;CASxB"}