agent-relay 1.0.7 → 1.0.9

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 (140) hide show
  1. package/README.md +176 -6
  2. package/dist/bridge/config.d.ts +41 -0
  3. package/dist/bridge/config.d.ts.map +1 -0
  4. package/dist/bridge/config.js +143 -0
  5. package/dist/bridge/config.js.map +1 -0
  6. package/dist/bridge/index.d.ts +10 -0
  7. package/dist/bridge/index.d.ts.map +1 -0
  8. package/dist/bridge/index.js +10 -0
  9. package/dist/bridge/index.js.map +1 -0
  10. package/dist/bridge/multi-project-client.d.ts +99 -0
  11. package/dist/bridge/multi-project-client.d.ts.map +1 -0
  12. package/dist/bridge/multi-project-client.js +386 -0
  13. package/dist/bridge/multi-project-client.js.map +1 -0
  14. package/dist/bridge/spawner.d.ts +46 -0
  15. package/dist/bridge/spawner.d.ts.map +1 -0
  16. package/dist/bridge/spawner.js +223 -0
  17. package/dist/bridge/spawner.js.map +1 -0
  18. package/dist/bridge/types.d.ts +55 -0
  19. package/dist/bridge/types.d.ts.map +1 -0
  20. package/dist/bridge/types.js +6 -0
  21. package/dist/bridge/types.js.map +1 -0
  22. package/dist/bridge/utils.d.ts +30 -0
  23. package/dist/bridge/utils.d.ts.map +1 -0
  24. package/dist/bridge/utils.js +54 -0
  25. package/dist/bridge/utils.js.map +1 -0
  26. package/dist/cli/index.d.ts +2 -0
  27. package/dist/cli/index.d.ts.map +1 -1
  28. package/dist/cli/index.js +906 -6
  29. package/dist/cli/index.js.map +1 -1
  30. package/dist/daemon/agent-registry.d.ts +60 -0
  31. package/dist/daemon/agent-registry.d.ts.map +1 -0
  32. package/dist/daemon/agent-registry.js +163 -0
  33. package/dist/daemon/agent-registry.js.map +1 -0
  34. package/dist/daemon/connection.d.ts +33 -1
  35. package/dist/daemon/connection.d.ts.map +1 -1
  36. package/dist/daemon/connection.js +86 -11
  37. package/dist/daemon/connection.js.map +1 -1
  38. package/dist/daemon/index.d.ts +2 -0
  39. package/dist/daemon/index.d.ts.map +1 -1
  40. package/dist/daemon/index.js +2 -0
  41. package/dist/daemon/index.js.map +1 -1
  42. package/dist/daemon/registry.d.ts +9 -0
  43. package/dist/daemon/registry.d.ts.map +1 -0
  44. package/dist/daemon/registry.js +9 -0
  45. package/dist/daemon/registry.js.map +1 -0
  46. package/dist/daemon/router.d.ts +61 -2
  47. package/dist/daemon/router.d.ts.map +1 -1
  48. package/dist/daemon/router.js +219 -4
  49. package/dist/daemon/router.js.map +1 -1
  50. package/dist/daemon/server.d.ts +9 -0
  51. package/dist/daemon/server.d.ts.map +1 -1
  52. package/dist/daemon/server.js +135 -16
  53. package/dist/daemon/server.js.map +1 -1
  54. package/dist/dashboard/metrics.d.ts +105 -0
  55. package/dist/dashboard/metrics.d.ts.map +1 -0
  56. package/dist/dashboard/metrics.js +192 -0
  57. package/dist/dashboard/metrics.js.map +1 -0
  58. package/dist/dashboard/needs-attention.d.ts +24 -0
  59. package/dist/dashboard/needs-attention.d.ts.map +1 -0
  60. package/dist/dashboard/needs-attention.js +78 -0
  61. package/dist/dashboard/needs-attention.js.map +1 -0
  62. package/dist/dashboard/public/bridge.html +1272 -0
  63. package/dist/dashboard/public/index.html +2094 -347
  64. package/dist/dashboard/public/js/app.js +184 -0
  65. package/dist/dashboard/public/js/app.js.map +7 -0
  66. package/dist/dashboard/public/metrics.html +999 -0
  67. package/dist/dashboard/server.d.ts +14 -1
  68. package/dist/dashboard/server.d.ts.map +1 -1
  69. package/dist/dashboard/server.js +689 -16
  70. package/dist/dashboard/server.js.map +1 -1
  71. package/dist/dashboard/start.js +1 -1
  72. package/dist/dashboard/start.js.map +1 -1
  73. package/dist/dashboard-v2/index.d.ts +10 -0
  74. package/dist/dashboard-v2/index.d.ts.map +1 -0
  75. package/dist/dashboard-v2/index.js +54 -0
  76. package/dist/dashboard-v2/index.js.map +1 -0
  77. package/dist/dashboard-v2/lib/api.d.ts +95 -0
  78. package/dist/dashboard-v2/lib/api.d.ts.map +1 -0
  79. package/dist/dashboard-v2/lib/api.js +270 -0
  80. package/dist/dashboard-v2/lib/api.js.map +1 -0
  81. package/dist/dashboard-v2/lib/colors.d.ts +61 -0
  82. package/dist/dashboard-v2/lib/colors.d.ts.map +1 -0
  83. package/dist/dashboard-v2/lib/colors.js +198 -0
  84. package/dist/dashboard-v2/lib/colors.js.map +1 -0
  85. package/dist/dashboard-v2/lib/hierarchy.d.ts +74 -0
  86. package/dist/dashboard-v2/lib/hierarchy.d.ts.map +1 -0
  87. package/dist/dashboard-v2/lib/hierarchy.js +196 -0
  88. package/dist/dashboard-v2/lib/hierarchy.js.map +1 -0
  89. package/dist/dashboard-v2/types/index.d.ts +154 -0
  90. package/dist/dashboard-v2/types/index.d.ts.map +1 -0
  91. package/dist/dashboard-v2/types/index.js +6 -0
  92. package/dist/dashboard-v2/types/index.js.map +1 -0
  93. package/dist/index.d.ts +1 -0
  94. package/dist/index.d.ts.map +1 -1
  95. package/dist/protocol/types.d.ts +15 -1
  96. package/dist/protocol/types.d.ts.map +1 -1
  97. package/dist/storage/adapter.d.ts +74 -1
  98. package/dist/storage/adapter.d.ts.map +1 -1
  99. package/dist/storage/adapter.js +39 -0
  100. package/dist/storage/adapter.js.map +1 -1
  101. package/dist/storage/sqlite-adapter.d.ts +92 -1
  102. package/dist/storage/sqlite-adapter.d.ts.map +1 -1
  103. package/dist/storage/sqlite-adapter.js +615 -47
  104. package/dist/storage/sqlite-adapter.js.map +1 -1
  105. package/dist/utils/agent-config.d.ts +45 -0
  106. package/dist/utils/agent-config.d.ts.map +1 -0
  107. package/dist/utils/agent-config.js +118 -0
  108. package/dist/utils/agent-config.js.map +1 -0
  109. package/dist/utils/project-namespace.d.ts.map +1 -1
  110. package/dist/utils/project-namespace.js +22 -1
  111. package/dist/utils/project-namespace.js.map +1 -1
  112. package/dist/wrapper/client.d.ts +30 -3
  113. package/dist/wrapper/client.d.ts.map +1 -1
  114. package/dist/wrapper/client.js +85 -9
  115. package/dist/wrapper/client.js.map +1 -1
  116. package/dist/wrapper/parser.d.ts +127 -4
  117. package/dist/wrapper/parser.d.ts.map +1 -1
  118. package/dist/wrapper/parser.js +622 -86
  119. package/dist/wrapper/parser.js.map +1 -1
  120. package/dist/wrapper/tmux-wrapper.d.ts +136 -10
  121. package/dist/wrapper/tmux-wrapper.d.ts.map +1 -1
  122. package/dist/wrapper/tmux-wrapper.js +599 -79
  123. package/dist/wrapper/tmux-wrapper.js.map +1 -1
  124. package/docs/AGENTS.md +132 -27
  125. package/docs/ARCHITECTURE_DECISIONS.md +175 -0
  126. package/docs/CHANGELOG.md +1 -1
  127. package/docs/COMPETITIVE_ANALYSIS.md +897 -0
  128. package/docs/DESIGN_BRIDGE_STAFFING.md +878 -0
  129. package/docs/DESIGN_V2.md +1079 -0
  130. package/docs/INTEGRATION-GUIDE.md +926 -0
  131. package/docs/MONETIZATION.md +1679 -0
  132. package/docs/PROPOSAL-trajectories.md +1582 -0
  133. package/docs/PROTOCOL.md +3 -3
  134. package/docs/SCALING_ANALYSIS.md +280 -0
  135. package/docs/TMUX_IMPLEMENTATION_NOTES.md +9 -9
  136. package/docs/TMUX_IMPROVEMENTS.md +968 -0
  137. package/docs/agent-relay-snippet.md +61 -0
  138. package/docs/competitive-analysis-mcp-agent-mail.md +389 -0
  139. package/docs/dashboard-v2-plan.md +179 -0
  140. package/package.json +10 -3
@@ -0,0 +1,386 @@
1
+ /**
2
+ * MultiProjectClient
3
+ * Connects to multiple project daemons simultaneously for cross-project orchestration.
4
+ */
5
+ import net from 'node:net';
6
+ import fs from 'node:fs';
7
+ import { v4 as uuid } from 'uuid';
8
+ import { PROTOCOL_VERSION, } from '../protocol/types.js';
9
+ import { encodeFrame, FrameParser } from '../protocol/framing.js';
10
+ export class MultiProjectClient {
11
+ projects;
12
+ connections = new Map();
13
+ leads = new Map();
14
+ options;
15
+ shuttingDown = false;
16
+ /** Handler for incoming messages */
17
+ onMessage;
18
+ /** Handler for connection state changes */
19
+ onProjectStateChange;
20
+ constructor(projects, options = {}) {
21
+ this.projects = projects;
22
+ this.options = {
23
+ agentName: options.agentName ?? '__BridgeClient',
24
+ reconnect: options.reconnect ?? true,
25
+ reconnectDelay: options.reconnectDelay ?? 1000,
26
+ maxReconnectDelay: options.maxReconnectDelay ?? 30000,
27
+ maxReconnectAttempts: options.maxReconnectAttempts ?? Infinity,
28
+ };
29
+ }
30
+ /**
31
+ * Connect to all project daemons
32
+ */
33
+ async connect() {
34
+ const connectPromises = this.projects.map((project) => this.connectToProject(project));
35
+ await Promise.all(connectPromises);
36
+ }
37
+ /**
38
+ * Connect to a single project daemon
39
+ */
40
+ connectToProject(project) {
41
+ return new Promise((resolve, reject) => {
42
+ // Check socket exists
43
+ if (!fs.existsSync(project.socketPath)) {
44
+ console.error(`[bridge] No daemon running for ${project.id} (${project.path})`);
45
+ console.error(`[bridge] Start with: cd ${project.path} && agent-relay up`);
46
+ reject(new Error(`No daemon for ${project.id}`));
47
+ return;
48
+ }
49
+ const socket = net.createConnection(project.socketPath, () => {
50
+ this.sendHello(conn);
51
+ });
52
+ const conn = {
53
+ config: project,
54
+ socket,
55
+ parser: new FrameParser(),
56
+ ready: false,
57
+ };
58
+ socket.on('data', (data) => this.handleData(conn, data));
59
+ socket.on('close', () => {
60
+ const wasReady = conn.ready;
61
+ conn.ready = false;
62
+ this.onProjectStateChange?.(project.id, false);
63
+ // Attempt reconnection if enabled and not shutting down
64
+ if (wasReady && this.options.reconnect && !this.shuttingDown) {
65
+ this.scheduleReconnect(conn);
66
+ }
67
+ });
68
+ socket.on('error', (err) => {
69
+ console.error(`[bridge] Connection error for ${project.id}:`, err.message);
70
+ if (!conn.ready) {
71
+ reject(err);
72
+ }
73
+ });
74
+ this.connections.set(project.id, conn);
75
+ // Wait for ready
76
+ const checkReady = setInterval(() => {
77
+ if (conn.ready) {
78
+ clearInterval(checkReady);
79
+ clearTimeout(timeout);
80
+ resolve();
81
+ }
82
+ }, 10);
83
+ const timeout = setTimeout(() => {
84
+ if (!conn.ready) {
85
+ clearInterval(checkReady);
86
+ socket.destroy();
87
+ reject(new Error(`Connection timeout for ${project.id}`));
88
+ }
89
+ }, 5000);
90
+ });
91
+ }
92
+ /**
93
+ * Send HELLO to a project daemon
94
+ */
95
+ sendHello(conn) {
96
+ const hello = {
97
+ v: PROTOCOL_VERSION,
98
+ type: 'HELLO',
99
+ id: uuid(),
100
+ ts: Date.now(),
101
+ payload: {
102
+ agent: this.options.agentName,
103
+ cli: 'bridge',
104
+ capabilities: {
105
+ ack: true,
106
+ resume: false,
107
+ max_inflight: 256,
108
+ supports_topics: true,
109
+ },
110
+ },
111
+ };
112
+ this.send(conn, hello);
113
+ }
114
+ /**
115
+ * Handle incoming data from a project connection
116
+ */
117
+ handleData(conn, data) {
118
+ try {
119
+ const frames = conn.parser.push(data);
120
+ for (const frame of frames) {
121
+ this.processFrame(conn, frame);
122
+ }
123
+ }
124
+ catch (err) {
125
+ console.error(`[bridge] Parse error for ${conn.config.id}:`, err);
126
+ }
127
+ }
128
+ /**
129
+ * Process a frame from a project daemon
130
+ */
131
+ processFrame(conn, envelope) {
132
+ switch (envelope.type) {
133
+ case 'WELCOME':
134
+ conn.ready = true;
135
+ console.log(`[bridge] Connected to ${conn.config.id}`);
136
+ this.onProjectStateChange?.(conn.config.id, true);
137
+ break;
138
+ case 'DELIVER':
139
+ this.handleDeliver(conn, envelope);
140
+ break;
141
+ case 'PING':
142
+ this.send(conn, {
143
+ v: PROTOCOL_VERSION,
144
+ type: 'PONG',
145
+ id: uuid(),
146
+ ts: Date.now(),
147
+ payload: envelope.payload ?? {},
148
+ });
149
+ break;
150
+ }
151
+ }
152
+ /**
153
+ * Handle delivered message from a project
154
+ */
155
+ handleDeliver(conn, envelope) {
156
+ // Send ACK
157
+ this.send(conn, {
158
+ v: PROTOCOL_VERSION,
159
+ type: 'ACK',
160
+ id: uuid(),
161
+ ts: Date.now(),
162
+ payload: {
163
+ ack_id: envelope.id,
164
+ seq: envelope.delivery.seq,
165
+ },
166
+ });
167
+ // Notify handler
168
+ if (this.onMessage && envelope.from) {
169
+ this.onMessage(conn.config.id, envelope.from, envelope.payload, envelope.id);
170
+ }
171
+ }
172
+ /**
173
+ * Send envelope to a project daemon
174
+ */
175
+ send(conn, envelope) {
176
+ if (!conn.socket)
177
+ return false;
178
+ try {
179
+ const frame = encodeFrame(envelope);
180
+ conn.socket.write(frame);
181
+ return true;
182
+ }
183
+ catch (err) {
184
+ console.error(`[bridge] Send error for ${conn.config.id}:`, err);
185
+ return false;
186
+ }
187
+ }
188
+ /**
189
+ * Send message to a project
190
+ * @param projectId - Target project ID
191
+ * @param to - Agent name within the project (or '*' for broadcast, 'lead' for project lead)
192
+ * @param body - Message body
193
+ */
194
+ sendToProject(projectId, to, body) {
195
+ const conn = this.connections.get(projectId);
196
+ if (!conn?.ready) {
197
+ console.error(`[bridge] Cannot send to ${projectId}: not connected`);
198
+ return false;
199
+ }
200
+ // Resolve 'lead' to actual lead name
201
+ let targetAgent = to;
202
+ if (to === 'lead') {
203
+ const lead = this.leads.get(projectId);
204
+ if (lead) {
205
+ targetAgent = lead.name;
206
+ }
207
+ else {
208
+ // Fallback to configured lead name
209
+ targetAgent = conn.config.leadName;
210
+ }
211
+ }
212
+ const envelope = {
213
+ v: PROTOCOL_VERSION,
214
+ type: 'SEND',
215
+ id: uuid(),
216
+ ts: Date.now(),
217
+ to: targetAgent,
218
+ payload: {
219
+ kind: 'message',
220
+ body,
221
+ },
222
+ };
223
+ return this.send(conn, envelope);
224
+ }
225
+ /**
226
+ * Broadcast to all leads
227
+ */
228
+ broadcastToLeads(body) {
229
+ for (const [projectId] of this.connections) {
230
+ this.sendToProject(projectId, 'lead', body);
231
+ }
232
+ }
233
+ /**
234
+ * Broadcast to all agents in all projects
235
+ */
236
+ broadcastAll(body) {
237
+ for (const [projectId, conn] of this.connections) {
238
+ if (conn.ready) {
239
+ const envelope = {
240
+ v: PROTOCOL_VERSION,
241
+ type: 'SEND',
242
+ id: uuid(),
243
+ ts: Date.now(),
244
+ to: '*',
245
+ payload: {
246
+ kind: 'message',
247
+ body,
248
+ },
249
+ };
250
+ this.send(conn, envelope);
251
+ }
252
+ }
253
+ }
254
+ /**
255
+ * Register a lead for a project
256
+ */
257
+ registerLead(projectId, leadName) {
258
+ this.leads.set(projectId, {
259
+ name: leadName,
260
+ projectId,
261
+ connected: true,
262
+ });
263
+ }
264
+ /**
265
+ * Get all connected projects
266
+ */
267
+ getConnectedProjects() {
268
+ return Array.from(this.connections.entries())
269
+ .filter(([_, conn]) => conn.ready)
270
+ .map(([id]) => id);
271
+ }
272
+ /**
273
+ * Get project connection info
274
+ */
275
+ getProject(projectId) {
276
+ return this.connections.get(projectId)?.config;
277
+ }
278
+ /**
279
+ * Schedule a reconnection attempt with exponential backoff
280
+ */
281
+ scheduleReconnect(conn) {
282
+ if (conn.reconnecting || this.shuttingDown)
283
+ return;
284
+ const attempts = conn.reconnectAttempts ?? 0;
285
+ if (attempts >= this.options.maxReconnectAttempts) {
286
+ console.error(`[bridge] Max reconnection attempts reached for ${conn.config.id}`);
287
+ return;
288
+ }
289
+ conn.reconnecting = true;
290
+ conn.reconnectAttempts = attempts + 1;
291
+ // Calculate delay with exponential backoff
292
+ const delay = Math.min(this.options.reconnectDelay * Math.pow(2, attempts), this.options.maxReconnectDelay);
293
+ console.log(`[bridge] Reconnecting to ${conn.config.id} in ${delay}ms (attempt ${conn.reconnectAttempts})`);
294
+ conn.reconnectTimer = setTimeout(() => {
295
+ this.attemptReconnect(conn);
296
+ }, delay);
297
+ }
298
+ /**
299
+ * Attempt to reconnect to a project daemon
300
+ */
301
+ attemptReconnect(conn) {
302
+ if (this.shuttingDown) {
303
+ conn.reconnecting = false;
304
+ return;
305
+ }
306
+ // Check socket exists
307
+ if (!fs.existsSync(conn.config.socketPath)) {
308
+ console.error(`[bridge] No daemon running for ${conn.config.id}, will retry`);
309
+ conn.reconnecting = false;
310
+ this.scheduleReconnect(conn);
311
+ return;
312
+ }
313
+ const socket = net.createConnection(conn.config.socketPath, () => {
314
+ this.sendHello(conn);
315
+ });
316
+ // Update connection with new socket
317
+ conn.socket = socket;
318
+ conn.parser = new FrameParser();
319
+ socket.on('data', (data) => this.handleData(conn, data));
320
+ socket.on('close', () => {
321
+ const wasReady = conn.ready;
322
+ conn.ready = false;
323
+ this.onProjectStateChange?.(conn.config.id, false);
324
+ if (wasReady && this.options.reconnect && !this.shuttingDown) {
325
+ this.scheduleReconnect(conn);
326
+ }
327
+ });
328
+ socket.on('error', (err) => {
329
+ console.error(`[bridge] Reconnection error for ${conn.config.id}:`, err.message);
330
+ conn.reconnecting = false;
331
+ if (!this.shuttingDown) {
332
+ this.scheduleReconnect(conn);
333
+ }
334
+ });
335
+ // Reset reconnection state on successful connect
336
+ const originalReady = conn.ready;
337
+ const checkReady = setInterval(() => {
338
+ if (conn.ready && !originalReady) {
339
+ clearInterval(checkReady);
340
+ clearTimeout(timeout);
341
+ conn.reconnecting = false;
342
+ conn.reconnectAttempts = 0;
343
+ console.log(`[bridge] Reconnected to ${conn.config.id}`);
344
+ }
345
+ }, 10);
346
+ const timeout = setTimeout(() => {
347
+ if (!conn.ready) {
348
+ clearInterval(checkReady);
349
+ socket.destroy();
350
+ conn.reconnecting = false;
351
+ console.error(`[bridge] Reconnection timeout for ${conn.config.id}`);
352
+ if (!this.shuttingDown) {
353
+ this.scheduleReconnect(conn);
354
+ }
355
+ }
356
+ }, 5000);
357
+ }
358
+ /**
359
+ * Disconnect from all projects
360
+ */
361
+ disconnect() {
362
+ this.shuttingDown = true;
363
+ for (const [_, conn] of this.connections) {
364
+ // Clear any pending reconnection timers
365
+ if (conn.reconnectTimer) {
366
+ clearTimeout(conn.reconnectTimer);
367
+ }
368
+ try {
369
+ this.send(conn, {
370
+ v: PROTOCOL_VERSION,
371
+ type: 'BYE',
372
+ id: uuid(),
373
+ ts: Date.now(),
374
+ payload: {},
375
+ });
376
+ conn.socket.end();
377
+ }
378
+ catch {
379
+ // Ignore
380
+ }
381
+ }
382
+ this.connections.clear();
383
+ this.leads.clear();
384
+ }
385
+ }
386
+ //# sourceMappingURL=multi-project-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"multi-project-client.js","sourceRoot":"","sources":["../../src/bridge/multi-project-client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Agent Spawner
3
+ * Handles spawning and releasing worker agents via tmux.
4
+ */
5
+ import type { SpawnRequest, SpawnResult, WorkerInfo } from './types.js';
6
+ export declare class AgentSpawner {
7
+ private activeWorkers;
8
+ private tmuxSession;
9
+ private agentsPath;
10
+ constructor(projectRoot: string, tmuxSession?: string);
11
+ private projectRoot;
12
+ /**
13
+ * Ensure the worker tmux session exists
14
+ */
15
+ ensureSession(): Promise<void>;
16
+ /**
17
+ * Spawn a new worker agent
18
+ */
19
+ spawn(request: SpawnRequest): Promise<SpawnResult>;
20
+ /**
21
+ * Release (terminate) a worker
22
+ */
23
+ release(name: string): Promise<boolean>;
24
+ /**
25
+ * Release all workers
26
+ */
27
+ releaseAll(): Promise<void>;
28
+ /**
29
+ * Get all active workers
30
+ */
31
+ getActiveWorkers(): WorkerInfo[];
32
+ /**
33
+ * Check if a worker exists
34
+ */
35
+ hasWorker(name: string): boolean;
36
+ /**
37
+ * Get worker info
38
+ */
39
+ getWorker(name: string): WorkerInfo | undefined;
40
+ /**
41
+ * Wait for an agent to appear in the registry (agents.json)
42
+ */
43
+ private waitForAgentRegistration;
44
+ private isAgentRegistered;
45
+ }
46
+ //# sourceMappingURL=spawner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spawner.d.ts","sourceRoot":"","sources":["../../src/bridge/spawner.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAExE,qBAAa,YAAY;IACvB,OAAO,CAAC,aAAa,CAAsC;IAC3D,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,UAAU,CAAS;gBAGzB,WAAW,EAAE,MAAM,EACnB,WAAW,CAAC,EAAE,MAAM;IAStB,OAAO,CAAC,WAAW,CAAS;IAE5B;;OAEG;IACG,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC;IAYpC;;OAEG;IACG,KAAK,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,WAAW,CAAC;IAyGxD;;OAEG;IACG,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAiC7C;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAOjC;;OAEG;IACH,gBAAgB,IAAI,UAAU,EAAE;IAIhC;;OAEG;IACH,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAIhC;;OAEG;IACH,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS;IAI/C;;OAEG;YACW,wBAAwB;IAkBtC,OAAO,CAAC,iBAAiB;CAkB1B"}
@@ -0,0 +1,223 @@
1
+ /**
2
+ * Agent Spawner
3
+ * Handles spawning and releasing worker agents via tmux.
4
+ */
5
+ import fs from 'node:fs';
6
+ import path from 'node:path';
7
+ import { execAsync, sleep, escapeForTmux } from './utils.js';
8
+ import { getProjectPaths } from '../utils/project-namespace.js';
9
+ export class AgentSpawner {
10
+ activeWorkers = new Map();
11
+ tmuxSession;
12
+ agentsPath;
13
+ constructor(projectRoot, tmuxSession) {
14
+ const paths = getProjectPaths(projectRoot);
15
+ this.projectRoot = paths.projectRoot;
16
+ this.agentsPath = path.join(paths.teamDir, 'agents.json');
17
+ // Default session name based on project
18
+ this.tmuxSession = tmuxSession || 'relay-workers';
19
+ }
20
+ projectRoot;
21
+ /**
22
+ * Ensure the worker tmux session exists
23
+ */
24
+ async ensureSession() {
25
+ try {
26
+ await execAsync(`tmux has-session -t ${this.tmuxSession} 2>/dev/null`);
27
+ }
28
+ catch {
29
+ // Session doesn't exist, create it
30
+ await execAsync(`tmux new-session -d -s ${this.tmuxSession} -c "${this.projectRoot}"`);
31
+ console.log(`[spawner] Created session ${this.tmuxSession}`);
32
+ }
33
+ }
34
+ /**
35
+ * Spawn a new worker agent
36
+ */
37
+ async spawn(request) {
38
+ const { name, cli, task, requestedBy } = request;
39
+ const debug = process.env.DEBUG_SPAWN === '1';
40
+ // Check if worker already exists
41
+ if (this.activeWorkers.has(name)) {
42
+ return {
43
+ success: false,
44
+ name,
45
+ error: `Worker ${name} already exists`,
46
+ };
47
+ }
48
+ try {
49
+ await this.ensureSession();
50
+ if (debug)
51
+ console.log(`[spawner:debug] Session ${this.tmuxSession} ready`);
52
+ // Create new window for worker
53
+ const windowName = name;
54
+ const newWindowCmd = `tmux new-window -t ${this.tmuxSession} -n ${windowName} -c "${this.projectRoot}"`;
55
+ if (debug)
56
+ console.log(`[spawner:debug] Creating window: ${newWindowCmd}`);
57
+ await execAsync(newWindowCmd);
58
+ // Build the agent-relay command
59
+ // Unset TMUX to allow Claude to run inside tmux (it refuses to nest by default)
60
+ // Use full path to agent-relay to avoid PATH issues with nvm/shell init
61
+ let agentRelayPath;
62
+ try {
63
+ const { stdout } = await execAsync('which agent-relay');
64
+ agentRelayPath = stdout.trim();
65
+ if (debug)
66
+ console.log(`[spawner:debug] Found agent-relay at: ${agentRelayPath}`);
67
+ }
68
+ catch {
69
+ // Fallback to npx if which fails
70
+ agentRelayPath = 'npx agent-relay';
71
+ if (debug)
72
+ console.log(`[spawner:debug] Using npx fallback`);
73
+ }
74
+ // Add --dangerously-skip-permissions for Claude agents to avoid permission dialogs
75
+ // Use -- to separate agent-relay options from the wrapped command
76
+ const isClaudeCli = cli.startsWith('claude');
77
+ const cliWithFlags = isClaudeCli ? `${cli} --dangerously-skip-permissions` : cli;
78
+ const cmd = `unset TMUX && ${agentRelayPath} -n ${name} -- ${cliWithFlags}`;
79
+ if (debug)
80
+ console.log(`[spawner:debug] Agent command: ${cmd}`);
81
+ // Send the command
82
+ const sendCmd = `tmux send-keys -t ${this.tmuxSession}:${windowName} '${cmd}' Enter`;
83
+ if (debug)
84
+ console.log(`[spawner:debug] Sending: ${sendCmd}`);
85
+ await execAsync(sendCmd);
86
+ // Wait for the agent to register with the daemon before injecting tasks
87
+ const registered = await this.waitForAgentRegistration(name, 30_000, 500);
88
+ if (!registered) {
89
+ const error = `Worker ${name} failed to register within 30s`;
90
+ console.error(`[spawner] ${error}`);
91
+ // Clean up the tmux window to avoid orphaned workers
92
+ await execAsync(`tmux kill-window -t ${this.tmuxSession}:${windowName}`).catch(() => { });
93
+ return {
94
+ success: false,
95
+ name,
96
+ error,
97
+ };
98
+ }
99
+ // Inject the initial task if provided
100
+ if (task && task.trim()) {
101
+ const escapedTask = escapeForTmux(task);
102
+ if (debug)
103
+ console.log(`[spawner:debug] Injecting task: ${escapedTask.substring(0, 50)}...`);
104
+ await execAsync(`tmux send-keys -t ${this.tmuxSession}:${windowName} -l "${escapedTask}"`);
105
+ await sleep(100);
106
+ await execAsync(`tmux send-keys -t ${this.tmuxSession}:${windowName} Enter`);
107
+ }
108
+ // Track the worker
109
+ const workerInfo = {
110
+ name,
111
+ cli,
112
+ task,
113
+ spawnedBy: requestedBy,
114
+ spawnedAt: Date.now(),
115
+ window: `${this.tmuxSession}:${windowName}`,
116
+ };
117
+ this.activeWorkers.set(name, workerInfo);
118
+ console.log(`[spawner] Spawned ${name} (${cli}) for ${requestedBy}`);
119
+ return {
120
+ success: true,
121
+ name,
122
+ window: workerInfo.window,
123
+ };
124
+ }
125
+ catch (err) {
126
+ console.error(`[spawner] Failed to spawn ${name}:`, err.message);
127
+ if (debug)
128
+ console.error(`[spawner:debug] Full error:`, err);
129
+ return {
130
+ success: false,
131
+ name,
132
+ error: err.message,
133
+ };
134
+ }
135
+ }
136
+ /**
137
+ * Release (terminate) a worker
138
+ */
139
+ async release(name) {
140
+ const worker = this.activeWorkers.get(name);
141
+ if (!worker) {
142
+ console.log(`[spawner] Worker ${name} not found`);
143
+ return false;
144
+ }
145
+ try {
146
+ // Send exit command gracefully
147
+ await execAsync(`tmux send-keys -t ${worker.window} '/exit' Enter`).catch(() => { });
148
+ // Wait a bit for graceful shutdown
149
+ await sleep(2000);
150
+ // Kill the window
151
+ await execAsync(`tmux kill-window -t ${worker.window}`).catch(() => { });
152
+ this.activeWorkers.delete(name);
153
+ console.log(`[spawner] Released ${name}`);
154
+ return true;
155
+ }
156
+ catch (err) {
157
+ console.error(`[spawner] Failed to release ${name}:`, err.message);
158
+ // Still remove from tracking
159
+ this.activeWorkers.delete(name);
160
+ return false;
161
+ }
162
+ }
163
+ /**
164
+ * Release all workers
165
+ */
166
+ async releaseAll() {
167
+ const workers = Array.from(this.activeWorkers.keys());
168
+ for (const name of workers) {
169
+ await this.release(name);
170
+ }
171
+ }
172
+ /**
173
+ * Get all active workers
174
+ */
175
+ getActiveWorkers() {
176
+ return Array.from(this.activeWorkers.values());
177
+ }
178
+ /**
179
+ * Check if a worker exists
180
+ */
181
+ hasWorker(name) {
182
+ return this.activeWorkers.has(name);
183
+ }
184
+ /**
185
+ * Get worker info
186
+ */
187
+ getWorker(name) {
188
+ return this.activeWorkers.get(name);
189
+ }
190
+ /**
191
+ * Wait for an agent to appear in the registry (agents.json)
192
+ */
193
+ async waitForAgentRegistration(name, timeoutMs = 30_000, pollIntervalMs = 500) {
194
+ const deadline = Date.now() + timeoutMs;
195
+ while (Date.now() < deadline) {
196
+ if (this.isAgentRegistered(name)) {
197
+ return true;
198
+ }
199
+ await sleep(pollIntervalMs);
200
+ }
201
+ return false;
202
+ }
203
+ isAgentRegistered(name) {
204
+ if (!this.agentsPath)
205
+ return false;
206
+ if (!fs.existsSync(this.agentsPath))
207
+ return false;
208
+ try {
209
+ const raw = JSON.parse(fs.readFileSync(this.agentsPath, 'utf-8'));
210
+ const agents = Array.isArray(raw?.agents)
211
+ ? raw.agents
212
+ : raw?.agents && typeof raw.agents === 'object'
213
+ ? Object.values(raw.agents)
214
+ : [];
215
+ return agents.some((a) => a?.name === name);
216
+ }
217
+ catch (err) {
218
+ console.error('[spawner] Failed to read agents registry:', err.message);
219
+ return false;
220
+ }
221
+ }
222
+ }
223
+ //# sourceMappingURL=spawner.js.map