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,878 @@
1
+ # Bridge & Staffing: Multi-Project Agent Orchestration
2
+
3
+ ## Overview
4
+
5
+ This document describes the **Bridge** feature - a multi-project orchestration layer that allows a single agent (the **Architect** or **Principal**) to coordinate work across multiple projects, each managed by a **Lead** who can dynamically **staff** worker agents.
6
+
7
+ This builds on the existing agent-relay infrastructure while adding:
8
+ 1. Cross-project communication via socket bridging
9
+ 2. SE role hierarchy (Architect → Lead → Engineer)
10
+ 3. Dynamic agent spawning by Leads
11
+ 4. Multi-project dashboard visibility
12
+
13
+ ---
14
+
15
+ ## Terminology
16
+
17
+ | Term | Description |
18
+ |------|-------------|
19
+ | **Bridge** | CLI command and mode for multi-project orchestration |
20
+ | **Architect** / **Principal** | SE role for the bridging agent (cross-project coordinator) |
21
+ | **Lead** | Project leader (Tech Lead) - one per project, can spawn workers |
22
+ | **Engineer** / **Worker** | Agents spawned by Leads to execute specific tasks |
23
+ | **Spawn** | Action of creating a new worker agent |
24
+ | **Standup** | Morning coordination where Architect assigns work to Leads |
25
+
26
+ ---
27
+
28
+ ## Architecture
29
+
30
+ ```
31
+ ┌─────────────────────────────────────┐
32
+ │ ARCHITECT (bridge agent) │
33
+ │ agent-relay bridge --as architect │
34
+ │ │
35
+ │ ┌─────────────────────────────┐ │
36
+ │ │ MultiProjectClient │ │
37
+ │ │ - projectSockets: Map │ │
38
+ │ │ - projectLeads: Map │ │
39
+ │ └─────────────────────────────┘ │
40
+ └──────────────┬──────────────────────┘
41
+
42
+ ┌───────────────────────┼───────────────────────┐
43
+ │ │ │
44
+ ▼ ▼ ▼
45
+ ┌────────────────┐ ┌────────────────┐ ┌────────────────┐
46
+ │ auth-service │ │ frontend │ │ api-service │
47
+ │ Project Daemon │ │ Project Daemon │ │ Project Daemon │
48
+ │ │ │ │ │ │
49
+ │ Socket: │ │ Socket: │ │ Socket: │
50
+ │ /tmp/.../auth/ │ │ /tmp/.../fe/ │ │ /tmp/.../api/ │
51
+ └───────┬────────┘ └───────┬────────┘ └───────┬────────┘
52
+ │ │ │
53
+ ▼ ▼ ▼
54
+ ┌────────────────┐ ┌────────────────┐ ┌────────────────┐
55
+ │ Lead: Alice │ │ Lead: Bob │ │ Lead: Carol │
56
+ │ (can spawn) │ │ (can spawn) │ │ (can spawn) │
57
+ └───────┬────────┘ └───────┬────────┘ └───────┬────────┘
58
+ │ │ │
59
+ ┌─────┴─────┐ ┌─────┴─────┐ │
60
+ ▼ ▼ ▼ ▼ ▼
61
+ ┌───────┐ ┌───────┐ ┌───────┐ ┌───────┐ ┌───────┐
62
+ │ Dev1 │ │ QA1 │ │ Dev1 │ │ Rev1 │ │ SRE1 │
63
+ └───────┘ └───────┘ └───────┘ └───────┘ └───────┘
64
+ ```
65
+
66
+ ### Key Design Decisions
67
+
68
+ 1. **Keep Daemons Project-Local**: Each project maintains its own daemon and socket. The Architect connects to multiple daemons simultaneously. This preserves security isolation.
69
+
70
+ 2. **Coordination at Client Layer**: The `MultiProjectClient` handles cross-project routing, not the daemons. Daemons remain simple and unchanged.
71
+
72
+ 3. **Leads Own Staffing**: Leads decide how many workers and what types to spawn based on work assigned by the Architect.
73
+
74
+ ---
75
+
76
+ ## CLI Commands
77
+
78
+ ### Design Principle
79
+
80
+ > **Tight surface area. Simple interfaces hiding complexity.**
81
+
82
+ ### Start Bridge (Architect)
83
+
84
+ ```bash
85
+ # Bridge multiple projects - just list the paths
86
+ agent-relay bridge ~/auth ~/frontend ~/api
87
+ ```
88
+
89
+ That's it. The system handles:
90
+ - Socket discovery for each project
91
+ - Multi-socket connections
92
+ - Lead auto-detection (first registered agent per project)
93
+ - Cross-project message routing
94
+
95
+ ### Start Lead
96
+
97
+ ```bash
98
+ # Be a lead - just your name
99
+ agent-relay lead Alice
100
+ ```
101
+
102
+ That's it. The system handles:
103
+ - Project detection from current directory
104
+ - Spawn capability (automatic for leads)
105
+ - Daemon registration
106
+ - Worker management
107
+
108
+ ### Workers (spawned by Leads)
109
+
110
+ Workers are created via relay messages - not CLI commands:
111
+
112
+ ```bash
113
+ ->relay:spawn Dev1 claude "Build the login page"
114
+ ->relay:release Dev1
115
+ ```
116
+
117
+ ### What's Hidden
118
+
119
+ | User types | System handles |
120
+ |------------|----------------|
121
+ | `bridge ~/auth ~/frontend` | Socket discovery, multi-connection, lead detection |
122
+ | `lead Alice` | Spawn capability, project detection, daemon registration |
123
+ | `->relay:spawn Dev1 claude "task"` | Tmux window, agent launch, task injection |
124
+
125
+ ---
126
+
127
+ ## Message Patterns
128
+
129
+ ### Cross-Project Addressing
130
+
131
+ ```bash
132
+ # Architect → specific project lead
133
+ @relay:auth-service:lead Implement OAuth flow by EOD
134
+
135
+ # Architect → all leads
136
+ @relay:*:lead Standup time - report status and blockers
137
+
138
+ # Architect → specific agent in a project
139
+ @relay:frontend:Dev1 Focus on the login form first
140
+
141
+ # Architect → broadcast to entire project
142
+ @relay:api-service:* Code freeze starts in 1 hour
143
+ ```
144
+
145
+ ### Intra-Project (existing patterns still work)
146
+
147
+ ```bash
148
+ # Lead → worker (same project)
149
+ @relay:Dev1 Start on the user endpoint
150
+
151
+ # Worker → Lead
152
+ @relay:lead Task complete, ready for next assignment
153
+
154
+ # Worker → Worker
155
+ @relay:QA1 My PR is ready for testing
156
+ ```
157
+
158
+ ### Spawn Messages
159
+
160
+ ```bash
161
+ # Lead spawns a worker
162
+ ->relay:spawn Dev1 claude "Implement login endpoint"
163
+
164
+ # Lead spawns with specific model
165
+ ->relay:spawn SeniorDev claude:opus "Design the auth architecture"
166
+ ->relay:spawn QuickFix claude:haiku "Fix typos in error messages"
167
+
168
+ # Lead releases a worker
169
+ ->relay:release Dev1
170
+
171
+ # Lead releases all workers
172
+ ->relay:release *
173
+ ```
174
+
175
+ ---
176
+
177
+ ## Spawn Protocol
178
+
179
+ ### Spawn Message Format
180
+
181
+ ```
182
+ ->relay:spawn <name> <cli>[:<model>] "<initial_task>"
183
+ ```
184
+
185
+ | Field | Required | Description |
186
+ |-------|----------|-------------|
187
+ | `name` | Yes | Worker agent name (unique within project) |
188
+ | `cli` | Yes | Agent CLI: `claude`, `codex`, `gemini` |
189
+ | `model` | No | Model variant: `opus`, `sonnet`, `haiku` |
190
+ | `initial_task` | Yes | Task injected into worker on startup |
191
+
192
+ ### Spawn Sequence
193
+
194
+ ```
195
+ ┌─────────┐ ┌─────────┐ ┌─────────┐
196
+ │ Lead │ │ Daemon │ │ Spawner │
197
+ └────┬────┘ └────┬────┘ └────┬────┘
198
+ │ │ │
199
+ │ ->relay:spawn Dev1│ │
200
+ │ claude "task" │ │
201
+ │──────────────────>│ │
202
+ │ │ │
203
+ │ │ SPAWN request │
204
+ │ │──────────────────>│
205
+ │ │ │
206
+ │ │ Create tmux │
207
+ │ │ window "Dev1" │
208
+ │ │ │
209
+ │ │ Launch: │
210
+ │ │ agent-relay │
211
+ │ │ -n Dev1 claude │
212
+ │ │ │
213
+ │ │ Inject task │
214
+ │ │ │
215
+ │ │ SPAWN_ACK │
216
+ │ │<──────────────────│
217
+ │ │ │
218
+ │ SPAWN_COMPLETE │ │
219
+ │ Dev1 ready │ │
220
+ │<──────────────────│ │
221
+ │ │ │
222
+ ```
223
+
224
+ ### Spawn Implementation
225
+
226
+ ```typescript
227
+ // src/daemon/spawner.ts
228
+
229
+ interface SpawnRequest {
230
+ name: string;
231
+ cli: string;
232
+ model?: string;
233
+ task: string;
234
+ requestedBy: string; // Lead's name
235
+ }
236
+
237
+ export class AgentSpawner {
238
+ constructor(private projectRoot: string) {}
239
+
240
+ async spawn(request: SpawnRequest): Promise<SpawnResult> {
241
+ const { name, cli, model, task, requestedBy } = request;
242
+
243
+ // 1. Create tmux window
244
+ const sessionName = `relay-${name}`;
245
+ await exec(`tmux new-window -t relay -n ${name}`);
246
+
247
+ // 2. Build command
248
+ let cmd = `agent-relay -n ${name}`;
249
+ if (model) {
250
+ cmd += ` ${cli}:${model}`;
251
+ } else {
252
+ cmd += ` ${cli}`;
253
+ }
254
+
255
+ // 3. Launch agent
256
+ await exec(`tmux send-keys -t relay:${name} '${cmd}' Enter`);
257
+
258
+ // 4. Wait for agent to register
259
+ await this.waitForAgent(name, 30_000);
260
+
261
+ // 5. Inject initial task
262
+ await exec(`tmux send-keys -t relay:${name} '${escapeTask(task)}' Enter`);
263
+
264
+ return {
265
+ success: true,
266
+ name,
267
+ window: `relay:${name}`,
268
+ spawnedBy: requestedBy,
269
+ };
270
+ }
271
+
272
+ async release(name: string): Promise<void> {
273
+ // Send graceful shutdown
274
+ await exec(`tmux send-keys -t relay:${name} '/exit' Enter`);
275
+ // Kill window after delay
276
+ setTimeout(() => {
277
+ exec(`tmux kill-window -t relay:${name}`).catch(() => {});
278
+ }, 5000);
279
+ }
280
+ }
281
+ ```
282
+
283
+ ---
284
+
285
+ ## Daily Workflow: Standup Protocol
286
+
287
+ ### Morning Standup Sequence
288
+
289
+ ```
290
+ ┌────────────────────────────────────────────────────────────────────────┐
291
+ │ MORNING STANDUP │
292
+ ├────────────────────────────────────────────────────────────────────────┤
293
+ │ │
294
+ │ 1. ARCHITECT INITIATES │
295
+ │ @relay:*:lead Standup - report status, blockers, and staffing needs│
296
+ │ │
297
+ │ 2. LEADS REPORT │
298
+ │ Alice (auth): STATUS: OAuth 60% complete │
299
+ │ BLOCKED: Need API spec from api-service │
300
+ │ STAFF: Need 2 devs + 1 QA once unblocked │
301
+ │ │
302
+ │ Bob (frontend): STATUS: Dashboard ready for review │
303
+ │ BLOCKED: None │
304
+ │ STAFF: Need 1 reviewer │
305
+ │ │
306
+ │ Carol (api): STATUS: Spec complete │
307
+ │ BLOCKED: None │
308
+ │ STAFF: Ready for 3 parallel endpoint devs │
309
+ │ │
310
+ │ 3. ARCHITECT COORDINATES CROSS-PROJECT │
311
+ │ @relay:api-service:lead Alice needs your API spec - priority │
312
+ │ @relay:auth-service:lead Unblocked - Carol sending spec now │
313
+ │ │
314
+ │ 4. ARCHITECT ASSIGNS WORK │
315
+ │ @relay:auth-service:lead APPROVED: Staff 2 devs + 1 QA. Pri: HIGH │
316
+ │ @relay:frontend:lead APPROVED: Staff 1 reviewer for dashboard │
317
+ │ @relay:api-service:lead APPROVED: Staff 3 devs for endpoints │
318
+ │ │
319
+ │ 5. LEADS STAFF THEIR TEAMS │
320
+ │ Alice: ->relay:spawn Dev1 claude "Implement OAuth token flow" │
321
+ │ ->relay:spawn Dev2 claude "Implement OAuth callback" │
322
+ │ ->relay:spawn QA1 claude "Write OAuth integration tests" │
323
+ │ │
324
+ │ Bob: ->relay:spawn Reviewer claude "Review dashboard PR #42" │
325
+ │ │
326
+ │ Carol: ->relay:spawn Dev1 claude "POST /users endpoint" │
327
+ │ ->relay:spawn Dev2 claude "GET /users/:id endpoint" │
328
+ │ ->relay:spawn Dev3 claude "DELETE /users/:id endpoint" │
329
+ │ │
330
+ └────────────────────────────────────────────────────────────────────────┘
331
+ ```
332
+
333
+ ### Staffing Strategy by Work Type
334
+
335
+ | Work Type | Recommended Agent | Rationale |
336
+ |-----------|-------------------|-----------|
337
+ | Architecture/Design | `claude:opus` | Complex reasoning needed |
338
+ | Feature Implementation | `claude:sonnet` | Balance speed/quality |
339
+ | Code Review | `claude:sonnet` | Good judgment required |
340
+ | Testing/QA | `claude:sonnet` | Thoroughness matters |
341
+ | Bug Fixes | `claude:haiku` | Fast, focused |
342
+ | Documentation | `claude:haiku` | Straightforward |
343
+ | Refactoring | `claude:sonnet` | Context-aware changes |
344
+
345
+ ### End of Day Protocol
346
+
347
+ ```bash
348
+ # Architect requests EOD status
349
+ @relay:*:lead EOD status report
350
+
351
+ # Leads report and release workers
352
+ @relay:architect STATUS: OAuth complete, PR #123 ready
353
+ RELEASING: Dev1, Dev2, QA1
354
+ ->relay:release Dev1
355
+ ->relay:release Dev2
356
+ ->relay:release QA1
357
+
358
+ # Architect summarizes for dashboard/records
359
+ @relay:*:lead Good work today. Tomorrow: auth integration testing
360
+ ```
361
+
362
+ ---
363
+
364
+ ## Multi-Project Dashboard
365
+
366
+ ### Dashboard URL
367
+
368
+ ```
369
+ http://localhost:3888/bridge?projects=auth-service,frontend,api-service
370
+ ```
371
+
372
+ ### Dashboard Layout
373
+
374
+ ```
375
+ ┌─────────────────────────────────────────────────────────────────────────┐
376
+ │ AGENT RELAY - BRIDGE DASHBOARD │
377
+ ├─────────────────────────────────────────────────────────────────────────┤
378
+ │ Orchestrator: Architect (Principal) [Refresh] │
379
+ ├───────────────┬──────────────┬──────────────┬──────────────────────────┤
380
+ │ PROJECT │ LEAD │ TEAM │ STATUS │
381
+ ├───────────────┼──────────────┼──────────────┼──────────────────────────┤
382
+ │ auth-service │ Alice ● │ 3 workers │ ✓ OAuth implementation │
383
+ │ │ │ Dev1 ● │ │
384
+ │ │ │ Dev2 ● │ │
385
+ │ │ │ QA1 ○ │ │
386
+ ├───────────────┼──────────────┼──────────────┼──────────────────────────┤
387
+ │ frontend │ Bob ● │ 1 worker │ ⚠ Blocked on API spec │
388
+ │ │ │ Reviewer ● │ │
389
+ ├───────────────┼──────────────┼──────────────┼──────────────────────────┤
390
+ │ api-service │ Carol ● │ 3 workers │ ✓ Parallel endpoints │
391
+ │ │ │ Dev1 ● │ │
392
+ │ │ │ Dev2 ● │ │
393
+ │ │ │ Dev3 ● │ │
394
+ └───────────────┴──────────────┴──────────────┴──────────────────────────┘
395
+
396
+ │ Cross-Project Message Flow [Filter: All] │
397
+ ├─────────────────────────────────────────────────────────────────────────┤
398
+ │ 09:00 Architect → *:lead: Standup time │
399
+ │ 09:01 auth:Alice → Architect: STATUS: OAuth 60%... │
400
+ │ 09:01 frontend:Bob → Architect: STATUS: Dashboard ready... │
401
+ │ 09:02 api:Carol → Architect: STATUS: Spec complete... │
402
+ │ 09:05 Architect → api:lead: Send spec to Alice │
403
+ │ 09:06 api:Carol → auth:Alice: Here's the spec... │
404
+ │ 09:10 auth:Alice: ->relay:spawn Dev1 claude "OAuth token..." │
405
+ └─────────────────────────────────────────────────────────────────────────┘
406
+
407
+ │ [Click project to drill down] [Click agent to attach] │
408
+ └─────────────────────────────────────────────────────────────────────────┘
409
+ ```
410
+
411
+ ### Drill-Down View (Single Project)
412
+
413
+ ```
414
+ ┌─────────────────────────────────────────────────────────────────────────┐
415
+ │ auth-service [← Back to Overview] │
416
+ ├─────────────────────────────────────────────────────────────────────────┤
417
+ │ Lead: Alice │
418
+ │ Status: Active - OAuth implementation │
419
+ │ Workers: 3/3 active │
420
+ ├─────────────────────────────────────────────────────────────────────────┤
421
+ │ AGENT │ STATUS │ TASK │ MESSAGES │
422
+ ├──────────────┼──────────┼─────────────────────────────┼─────────────────┤
423
+ │ Alice (Lead) │ ● active │ Coordinating OAuth impl │ 12↑ 8↓ │
424
+ │ Dev1 │ ● active │ OAuth token flow │ 5↑ 14↓ │
425
+ │ Dev2 │ ● typing │ OAuth callback handler │ 3↑ 6↓ │
426
+ │ QA1 │ ○ idle │ Waiting for impl complete │ 1↑ 2↓ │
427
+ ├─────────────────────────────────────────────────────────────────────────┤
428
+ │ Project Messages │
429
+ ├─────────────────────────────────────────────────────────────────────────┤
430
+ │ 09:15 Alice → Dev1: Start on token flow │
431
+ │ 09:16 Alice → Dev2: Handle the callback endpoint │
432
+ │ 09:30 Dev1 → Alice: Token generation done, testing... │
433
+ │ 09:45 Dev1 → QA1: Ready for your test suite │
434
+ └─────────────────────────────────────────────────────────────────────────┘
435
+ ```
436
+
437
+ ---
438
+
439
+ ## Implementation Plan
440
+
441
+ ### Phase 1: Multi-Socket Client
442
+
443
+ **File: `src/wrapper/multi-project-client.ts`**
444
+
445
+ ```typescript
446
+ export class MultiProjectClient {
447
+ private sockets: Map<string, net.Socket> = new Map();
448
+ private projectLeads: Map<string, string> = new Map();
449
+
450
+ constructor(private projects: ProjectConfig[]) {}
451
+
452
+ async connect(): Promise<void> {
453
+ for (const project of this.projects) {
454
+ const socket = await this.connectToProject(project);
455
+ this.sockets.set(project.id, socket);
456
+ if (project.lead) {
457
+ this.projectLeads.set(project.id, project.lead);
458
+ }
459
+ }
460
+ }
461
+
462
+ async send(target: string, message: string): Promise<void> {
463
+ // Parse target: "project:agent" or "project:*" or "*:lead"
464
+ const { projectId, agentName } = this.parseTarget(target);
465
+
466
+ if (projectId === '*') {
467
+ // Broadcast to all projects
468
+ for (const [pid, socket] of this.sockets) {
469
+ await this.sendToSocket(socket, agentName, message);
470
+ }
471
+ } else {
472
+ const socket = this.sockets.get(projectId);
473
+ if (socket) {
474
+ await this.sendToSocket(socket, agentName, message);
475
+ }
476
+ }
477
+ }
478
+
479
+ private parseTarget(target: string): { projectId: string; agentName: string } {
480
+ const [projectId, agentName] = target.split(':');
481
+ return {
482
+ projectId: projectId || '*',
483
+ agentName: agentName === 'lead'
484
+ ? this.projectLeads.get(projectId) || 'lead'
485
+ : agentName,
486
+ };
487
+ }
488
+ }
489
+ ```
490
+
491
+ ### Phase 2: Extended Parser
492
+
493
+ **File: `src/wrapper/parser.ts`** (modifications)
494
+
495
+ ```typescript
496
+ // Extended pattern for cross-project messaging
497
+ const CROSS_PROJECT_PATTERN = /^@relay:([a-zA-Z0-9_-]+):([a-zA-Z0-9_*]+)\s+(.+)$/;
498
+ const SPAWN_PATTERN = /^->relay:spawn\s+(\S+)\s+(\S+)\s+"([^"]+)"$/;
499
+ const RELEASE_PATTERN = /^->relay:release\s+(\S+)$/;
500
+
501
+ export function parseRelayCommand(line: string): RelayCommand | null {
502
+ // Check for spawn command
503
+ const spawnMatch = line.match(SPAWN_PATTERN);
504
+ if (spawnMatch) {
505
+ return {
506
+ type: 'spawn',
507
+ name: spawnMatch[1],
508
+ cli: spawnMatch[2],
509
+ task: spawnMatch[3],
510
+ };
511
+ }
512
+
513
+ // Check for release command
514
+ const releaseMatch = line.match(RELEASE_PATTERN);
515
+ if (releaseMatch) {
516
+ return {
517
+ type: 'release',
518
+ name: releaseMatch[1],
519
+ };
520
+ }
521
+
522
+ // Check for cross-project message
523
+ const crossMatch = line.match(CROSS_PROJECT_PATTERN);
524
+ if (crossMatch) {
525
+ return {
526
+ type: 'message',
527
+ project: crossMatch[1],
528
+ target: crossMatch[2],
529
+ body: crossMatch[3],
530
+ };
531
+ }
532
+
533
+ // Fall back to existing single-project pattern
534
+ // ...existing code...
535
+ }
536
+ ```
537
+
538
+ ### Phase 3: Bridge CLI Command
539
+
540
+ **File: `src/cli/bridge.ts`**
541
+
542
+ ```typescript
543
+ import { Command } from 'commander';
544
+ import { MultiProjectClient } from '../wrapper/multi-project-client.js';
545
+
546
+ export function bridgeCommand(program: Command): void {
547
+ program
548
+ .command('bridge')
549
+ .description('Bridge multiple projects as orchestrator')
550
+ .argument('<projects...>', 'Project paths to bridge')
551
+ .action(async (projectPaths: string[]) => {
552
+ // Resolve and validate paths
553
+ const projects = await resolveProjects(projectPaths);
554
+
555
+ // Connect to all project sockets
556
+ const client = new MultiProjectClient(projects);
557
+ await client.connect();
558
+
559
+ // Auto-detect leads (first registered agent per project)
560
+ await client.discoverLeads();
561
+
562
+ // Start wrapper with multi-project client
563
+ const wrapper = new TmuxWrapper({
564
+ agentName: 'Architect',
565
+ client,
566
+ isOrchestrator: true,
567
+ });
568
+
569
+ await wrapper.start();
570
+ });
571
+ }
572
+ ```
573
+
574
+ **File: `src/cli/lead.ts`**
575
+
576
+ ```typescript
577
+ export function leadCommand(program: Command): void {
578
+ program
579
+ .command('lead')
580
+ .description('Start as project lead with spawn capability')
581
+ .argument('<name>', 'Your name')
582
+ .action(async (name: string) => {
583
+ // Detect project from cwd
584
+ const project = await detectProject(process.cwd());
585
+
586
+ // Start as lead with spawn capability
587
+ const wrapper = new TmuxWrapper({
588
+ agentName: name,
589
+ projectRoot: project.root,
590
+ isLead: true,
591
+ canSpawn: true,
592
+ });
593
+
594
+ await wrapper.start();
595
+ });
596
+ }
597
+ ```
598
+
599
+ ### Phase 4: Spawner Service
600
+
601
+ **File: `src/daemon/spawner.ts`**
602
+
603
+ ```typescript
604
+ export class AgentSpawner {
605
+ private activeWorkers: Map<string, WorkerInfo> = new Map();
606
+
607
+ constructor(
608
+ private projectRoot: string,
609
+ private tmuxSession: string,
610
+ ) {}
611
+
612
+ async spawn(request: SpawnRequest): Promise<SpawnResult> {
613
+ const { name, cli, model, task, requestedBy } = request;
614
+
615
+ // Validate
616
+ if (this.activeWorkers.has(name)) {
617
+ throw new Error(`Worker ${name} already exists`);
618
+ }
619
+
620
+ // Create tmux window
621
+ const windowName = `${name}`;
622
+ await execAsync(`tmux new-window -t ${this.tmuxSession} -n ${windowName}`);
623
+
624
+ // Build and launch command
625
+ const cliCmd = model ? `${cli}:${model}` : cli;
626
+ const cmd = `agent-relay -n ${name} ${cliCmd}`;
627
+ await execAsync(`tmux send-keys -t ${this.tmuxSession}:${windowName} '${cmd}' Enter`);
628
+
629
+ // Wait for registration
630
+ const registered = await this.waitForAgent(name, 30_000);
631
+ if (!registered) {
632
+ await this.cleanup(windowName);
633
+ throw new Error(`Worker ${name} failed to register`);
634
+ }
635
+
636
+ // Inject initial task
637
+ await sleep(2000); // Wait for agent to be ready
638
+ await execAsync(
639
+ `tmux send-keys -t ${this.tmuxSession}:${windowName} ${escapeForTmux(task)} Enter`
640
+ );
641
+
642
+ // Track worker
643
+ this.activeWorkers.set(name, {
644
+ name,
645
+ cli: cliCmd,
646
+ task,
647
+ spawnedBy: requestedBy,
648
+ spawnedAt: Date.now(),
649
+ window: `${this.tmuxSession}:${windowName}`,
650
+ });
651
+
652
+ return { success: true, name, window: windowName };
653
+ }
654
+
655
+ async release(name: string): Promise<void> {
656
+ const worker = this.activeWorkers.get(name);
657
+ if (!worker) return;
658
+
659
+ // Send exit command
660
+ try {
661
+ await execAsync(`tmux send-keys -t ${worker.window} '/exit' Enter`);
662
+ await sleep(3000);
663
+ } catch {
664
+ // Window may already be gone
665
+ }
666
+
667
+ // Kill window
668
+ try {
669
+ await execAsync(`tmux kill-window -t ${worker.window}`);
670
+ } catch {
671
+ // Ignore
672
+ }
673
+
674
+ this.activeWorkers.delete(name);
675
+ }
676
+
677
+ async releaseAll(): Promise<void> {
678
+ for (const name of this.activeWorkers.keys()) {
679
+ await this.release(name);
680
+ }
681
+ }
682
+
683
+ getActiveWorkers(): WorkerInfo[] {
684
+ return Array.from(this.activeWorkers.values());
685
+ }
686
+ }
687
+ ```
688
+
689
+ ### Phase 5: Dashboard Enhancement
690
+
691
+ **File: `src/dashboard/bridge-view.ts`**
692
+
693
+ ```typescript
694
+ export function createBridgeRouter(projects: ProjectConnection[]): Router {
695
+ const router = Router();
696
+
697
+ router.get('/bridge', async (req, res) => {
698
+ const projectData = await Promise.all(
699
+ projects.map(async (project) => ({
700
+ id: project.id,
701
+ name: project.name,
702
+ lead: await project.getLead(),
703
+ agents: await project.getAgents(),
704
+ status: await project.getStatus(),
705
+ recentMessages: await project.getRecentMessages(10),
706
+ }))
707
+ );
708
+
709
+ res.render('bridge', {
710
+ projects: projectData,
711
+ orchestrator: req.query.as || 'Architect',
712
+ });
713
+ });
714
+
715
+ router.get('/bridge/:projectId', async (req, res) => {
716
+ const project = projects.find(p => p.id === req.params.projectId);
717
+ if (!project) {
718
+ return res.status(404).send('Project not found');
719
+ }
720
+
721
+ const data = {
722
+ ...project,
723
+ agents: await project.getAgents(),
724
+ messages: await project.getMessages(50),
725
+ workers: await project.getWorkers(),
726
+ };
727
+
728
+ res.render('bridge-project', data);
729
+ });
730
+
731
+ return router;
732
+ }
733
+ ```
734
+
735
+ ---
736
+
737
+ ## Protocol Extensions
738
+
739
+ ### New Message Types
740
+
741
+ ```typescript
742
+ // src/protocol/types.ts additions
743
+
744
+ export interface SpawnPayload {
745
+ name: string;
746
+ cli: string;
747
+ model?: string;
748
+ task: string;
749
+ }
750
+
751
+ export interface ReleasePayload {
752
+ name: string;
753
+ }
754
+
755
+ export interface SpawnResultPayload {
756
+ success: boolean;
757
+ name: string;
758
+ window?: string;
759
+ error?: string;
760
+ }
761
+
762
+ // Extended envelope for cross-project
763
+ export interface CrossProjectEnvelope<T> extends Envelope<T> {
764
+ sourceProject?: string;
765
+ targetProject?: string;
766
+ }
767
+ ```
768
+
769
+ ### Wire Format Examples
770
+
771
+ ```json
772
+ // Spawn request
773
+ {
774
+ "v": 1,
775
+ "type": "SPAWN",
776
+ "id": "spawn-123",
777
+ "payload": {
778
+ "name": "Dev1",
779
+ "cli": "claude",
780
+ "model": "sonnet",
781
+ "task": "Implement the login endpoint"
782
+ }
783
+ }
784
+
785
+ // Cross-project message
786
+ {
787
+ "v": 1,
788
+ "type": "SEND",
789
+ "id": "msg-456",
790
+ "to": "Alice",
791
+ "sourceProject": "api-service",
792
+ "targetProject": "auth-service",
793
+ "payload": {
794
+ "kind": "message",
795
+ "body": "Here's the API spec you requested"
796
+ }
797
+ }
798
+ ```
799
+
800
+ ---
801
+
802
+ ## Security Considerations
803
+
804
+ 1. **Socket Permissions**: Each project daemon maintains 0o600 permissions. The Architect must have filesystem access to all project socket paths.
805
+
806
+ 2. **Spawn Validation**: Only agents with `--can-spawn` flag can spawn workers. Daemon validates the request source.
807
+
808
+ 3. **Cross-Project Isolation**: Messages are routed through the Architect's client, not directly between daemons. This provides an audit point.
809
+
810
+ 4. **Worker Limits**: Consider adding `--max-workers` flag to prevent runaway spawning.
811
+
812
+ ---
813
+
814
+ ## Configuration
815
+
816
+ ### Project Config File (optional)
817
+
818
+ ```json
819
+ // ~/auth-service/.agent-relay/config.json
820
+ {
821
+ "projectName": "auth-service",
822
+ "defaultLead": "Alice",
823
+ "maxWorkers": 5,
824
+ "allowedClis": ["claude", "codex"],
825
+ "spawnDefaults": {
826
+ "model": "sonnet",
827
+ "timeout": 3600
828
+ }
829
+ }
830
+ ```
831
+
832
+ ### Bridge Config File (optional)
833
+
834
+ ```json
835
+ // ~/.config/agent-relay/bridge.json
836
+ {
837
+ "defaultRole": "architect",
838
+ "projects": {
839
+ "auth-service": {
840
+ "path": "~/auth-service",
841
+ "lead": "Alice"
842
+ },
843
+ "frontend": {
844
+ "path": "~/frontend",
845
+ "lead": "Bob"
846
+ }
847
+ },
848
+ "standup": {
849
+ "autoStart": true,
850
+ "time": "09:00"
851
+ }
852
+ }
853
+ ```
854
+
855
+ ---
856
+
857
+ ## Future Enhancements
858
+
859
+ 1. **Scheduled Standups**: Auto-trigger standup at configured time
860
+ 2. **Worker Health Checks**: Monitor spawned workers for crashes
861
+ 3. **Load Balancing**: Auto-distribute work across projects
862
+ 4. **Metrics/Analytics**: Track productivity across projects
863
+ 5. **Notification Integration**: Slack/Discord alerts for blockers
864
+ 6. **Replay Mode**: Re-run standups from message history
865
+
866
+ ---
867
+
868
+ ## Summary
869
+
870
+ The Bridge & Staffing feature enables:
871
+
872
+ - **Architect/Principal** as cross-project orchestrator via `agent-relay bridge`
873
+ - **Leads** who can dynamically spawn worker agents via `->relay:spawn`
874
+ - **Standup protocol** for daily work coordination
875
+ - **Multi-project dashboard** for visibility across all projects
876
+ - **SE vernacular** with familiar role hierarchy
877
+
878
+ This maintains the simplicity of `@relay:` patterns while enabling sophisticated multi-project orchestration.