@desplega.ai/agent-swarm 1.2.0 → 1.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (123) hide show
  1. package/.claude/settings.local.json +20 -1
  2. package/.dockerignore +3 -0
  3. package/.env.docker.example +22 -1
  4. package/.env.example +17 -0
  5. package/.github/workflows/docker-publish.yml +92 -0
  6. package/CONTRIBUTING.md +270 -0
  7. package/DEPLOYMENT.md +391 -0
  8. package/Dockerfile.worker +29 -1
  9. package/FAQ.md +19 -0
  10. package/LICENSE +21 -0
  11. package/MCP.md +249 -0
  12. package/README.md +105 -185
  13. package/assets/agent-swarm-logo-orange.png +0 -0
  14. package/assets/agent-swarm-logo.png +0 -0
  15. package/assets/agent-swarm.png +0 -0
  16. package/deploy/docker-push.ts +30 -0
  17. package/docker-compose.example.yml +137 -0
  18. package/docker-entrypoint.sh +223 -7
  19. package/package.json +13 -4
  20. package/{cc-plugin → plugin}/.claude-plugin/plugin.json +1 -1
  21. package/plugin/README.md +1 -0
  22. package/plugin/agents/.gitkeep +0 -0
  23. package/plugin/agents/codebase-analyzer.md +143 -0
  24. package/plugin/agents/codebase-locator.md +122 -0
  25. package/plugin/agents/codebase-pattern-finder.md +227 -0
  26. package/plugin/agents/web-search-researcher.md +109 -0
  27. package/plugin/commands/create-plan.md +415 -0
  28. package/plugin/commands/implement-plan.md +89 -0
  29. package/plugin/commands/research.md +200 -0
  30. package/plugin/commands/start-leader.md +101 -0
  31. package/plugin/commands/start-worker.md +56 -0
  32. package/plugin/commands/swarm-chat.md +78 -0
  33. package/plugin/commands/todos.md +66 -0
  34. package/plugin/commands/work-on-task.md +44 -0
  35. package/plugin/skills/.gitkeep +0 -0
  36. package/scripts/generate-mcp-docs.ts +415 -0
  37. package/slack-manifest.json +69 -0
  38. package/src/be/db.ts +1431 -25
  39. package/src/cli.tsx +135 -11
  40. package/src/commands/lead.ts +13 -0
  41. package/src/commands/runner.ts +255 -0
  42. package/src/commands/setup.tsx +5 -5
  43. package/src/commands/worker.ts +8 -220
  44. package/src/hooks/hook.ts +108 -14
  45. package/src/http.ts +361 -5
  46. package/src/prompts/base-prompt.ts +131 -0
  47. package/src/server.ts +56 -0
  48. package/src/slack/app.ts +73 -0
  49. package/src/slack/commands.ts +88 -0
  50. package/src/slack/handlers.ts +281 -0
  51. package/src/slack/index.ts +3 -0
  52. package/src/slack/responses.ts +175 -0
  53. package/src/slack/router.ts +170 -0
  54. package/src/slack/types.ts +20 -0
  55. package/src/slack/watcher.ts +119 -0
  56. package/src/tools/create-channel.ts +80 -0
  57. package/src/tools/get-tasks.ts +54 -21
  58. package/src/tools/join-swarm.ts +28 -4
  59. package/src/tools/list-channels.ts +37 -0
  60. package/src/tools/list-services.ts +110 -0
  61. package/src/tools/poll-task.ts +47 -3
  62. package/src/tools/post-message.ts +87 -0
  63. package/src/tools/read-messages.ts +192 -0
  64. package/src/tools/register-service.ts +118 -0
  65. package/src/tools/send-task.ts +80 -7
  66. package/src/tools/store-progress.ts +9 -3
  67. package/src/tools/task-action.ts +211 -0
  68. package/src/tools/unregister-service.ts +110 -0
  69. package/src/tools/update-profile.ts +105 -0
  70. package/src/tools/update-service-status.ts +118 -0
  71. package/src/types.ts +110 -3
  72. package/src/utils/pretty-print.ts +224 -0
  73. package/thoughts/shared/plans/.gitkeep +0 -0
  74. package/thoughts/shared/plans/2025-12-18-inverse-teleport.md +1142 -0
  75. package/thoughts/shared/plans/2025-12-18-slack-integration.md +1195 -0
  76. package/thoughts/shared/plans/2025-12-19-agent-log-streaming.md +732 -0
  77. package/thoughts/shared/plans/2025-12-19-role-based-swarm-plugin.md +361 -0
  78. package/thoughts/shared/plans/2025-12-20-mobile-responsive-ui.md +501 -0
  79. package/thoughts/shared/plans/2025-12-20-startup-team-swarm.md +560 -0
  80. package/thoughts/shared/research/.gitkeep +0 -0
  81. package/thoughts/shared/research/2025-12-18-slack-integration.md +442 -0
  82. package/thoughts/shared/research/2025-12-19-agent-log-streaming.md +339 -0
  83. package/thoughts/shared/research/2025-12-19-agent-secrets-cli-research.md +390 -0
  84. package/thoughts/shared/research/2025-12-21-gemini-cli-integration.md +376 -0
  85. package/thoughts/shared/research/2025-12-22-setup-experience-improvements.md +264 -0
  86. package/tsconfig.json +3 -1
  87. package/ui/bun.lock +692 -0
  88. package/ui/index.html +22 -0
  89. package/ui/package.json +32 -0
  90. package/ui/pnpm-lock.yaml +3034 -0
  91. package/ui/postcss.config.js +6 -0
  92. package/ui/public/logo.png +0 -0
  93. package/ui/src/App.tsx +43 -0
  94. package/ui/src/components/ActivityFeed.tsx +415 -0
  95. package/ui/src/components/AgentDetailPanel.tsx +534 -0
  96. package/ui/src/components/AgentsPanel.tsx +549 -0
  97. package/ui/src/components/ChatPanel.tsx +1820 -0
  98. package/ui/src/components/ConfigModal.tsx +232 -0
  99. package/ui/src/components/Dashboard.tsx +534 -0
  100. package/ui/src/components/Header.tsx +168 -0
  101. package/ui/src/components/ServicesPanel.tsx +612 -0
  102. package/ui/src/components/StatsBar.tsx +288 -0
  103. package/ui/src/components/StatusBadge.tsx +124 -0
  104. package/ui/src/components/TaskDetailPanel.tsx +807 -0
  105. package/ui/src/components/TasksPanel.tsx +575 -0
  106. package/ui/src/hooks/queries.ts +170 -0
  107. package/ui/src/index.css +235 -0
  108. package/ui/src/lib/api.ts +161 -0
  109. package/ui/src/lib/config.ts +35 -0
  110. package/ui/src/lib/theme.ts +214 -0
  111. package/ui/src/lib/utils.ts +48 -0
  112. package/ui/src/main.tsx +32 -0
  113. package/ui/src/types/api.ts +164 -0
  114. package/ui/src/vite-env.d.ts +1 -0
  115. package/ui/tailwind.config.js +35 -0
  116. package/ui/tsconfig.json +31 -0
  117. package/ui/vite.config.ts +22 -0
  118. package/cc-plugin/README.md +0 -49
  119. package/cc-plugin/commands/setup-leader.md +0 -73
  120. package/cc-plugin/commands/start-worker.md +0 -64
  121. package/docker-compose.worker.yml +0 -35
  122. package/example-req-meta.json +0 -24
  123. /package/{cc-plugin → plugin}/hooks/hooks.json +0 -0
@@ -0,0 +1,1142 @@
1
+ # Inverse Teleport Implementation Plan
2
+
3
+ ## Overview
4
+
5
+ Implement "teleport-out" feature: a local Claude Code session can transfer its context to a distributed worker agent to continue the work. This is the inverse of Claude Code Web's teleport feature (which brings web sessions to local CLI).
6
+
7
+ ## Current State Analysis
8
+
9
+ The Agent Swarm MCP currently provides:
10
+ - HTTP server with REST API and MCP transport (`src/http.ts`)
11
+ - SQLite database with `agents`, `agent_tasks`, and `agent_log` tables (`src/be/db.ts`)
12
+ - 8 MCP tools for agent coordination (`src/tools/*.ts`)
13
+ - CLI runners for lead/worker agents (`src/commands/*.ts`)
14
+ - Hook system for session lifecycle events (`src/hooks/hook.ts`)
15
+
16
+ **What's Missing:**
17
+ - No mechanism for local sessions to export context
18
+ - Tasks are simple text strings, not rich context packages
19
+ - Workers have no way to receive session context beyond task descriptions
20
+ - No teleport request tracking or lifecycle management
21
+
22
+ ### Key Discoveries:
23
+ - Tasks flow through `send-task` tool with simple string payload (`src/tools/send-task.ts`)
24
+ - Workers poll for tasks via `poll-task` with 60-second timeout (`src/tools/poll-task.ts`)
25
+ - Hook system can provide guidance to workers on session start (`src/hooks/hook.ts:44-76`)
26
+ - Database uses transactions for atomic operations (`src/be/db.ts`)
27
+
28
+ ## Desired End State
29
+
30
+ After implementation:
31
+ 1. Local Claude Code session calls `teleport-out` with summary and context
32
+ 2. Teleport request is stored in database with rich context package
33
+ 3. Worker polls for teleports via `poll-teleport` and claims one atomically
34
+ 4. Worker continues the work with full context understanding
35
+ 5. Teleport lifecycle is tracked (pending → claimed → started → completed/failed)
36
+
37
+ ### Verification:
38
+ - Local session can call `teleport-out` and receive teleport ID
39
+ - Worker receives rich context package via `poll-teleport`
40
+ - Teleport status visible in dashboard/API
41
+ - Worker completes task with proper tracking
42
+
43
+ ## What We're NOT Doing
44
+
45
+ - **Full conversation history transfer** - Summary + context is sufficient
46
+ - **Claude CLI session file sync** - Workers are distributed (no shared filesystem)
47
+ - **Native `--resume` integration** - Would require session file transfer
48
+ - **Multi-teleport fan-out** - One teleport goes to one worker
49
+ - **Teleport cancellation UI** - Can be added later
50
+ - **File content sync** - Workers must have access to the same repo/codebase
51
+
52
+ ## Implementation Approach
53
+
54
+ Add a new `teleport_requests` table with rich context schema. Create 5 new MCP tools for the teleport lifecycle. Workers check for teleports before regular tasks. Context is provided as initial prompt material.
55
+
56
+ ---
57
+
58
+ ## Phase 1: Database Schema Updates
59
+
60
+ ### Overview
61
+ Add teleport request storage and tracking.
62
+
63
+ ### Changes Required:
64
+
65
+ #### 1. Types (`src/types.ts`)
66
+
67
+ Add teleport request schema after line 66 (after AgentLogSchema):
68
+
69
+ ```typescript
70
+ // Teleport Request Types
71
+ export const TeleportRequestStatusSchema = z.enum([
72
+ "pending", // Created, waiting for worker
73
+ "claimed", // Worker claimed it
74
+ "started", // Worker began work
75
+ "completed", // Work finished successfully
76
+ "failed" // Work failed
77
+ ]);
78
+ export type TeleportRequestStatus = z.infer<typeof TeleportRequestStatusSchema>;
79
+
80
+ export const RelevantFileSchema = z.object({
81
+ path: z.string(),
82
+ summary: z.string().optional(),
83
+ content: z.string().optional(),
84
+ });
85
+ export type RelevantFile = z.infer<typeof RelevantFileSchema>;
86
+
87
+ export const TeleportRequestSchema = z.object({
88
+ id: z.string().uuid(),
89
+ sourceAgentId: z.string().optional(), // Who sent it (may be null for non-swarm sessions)
90
+ targetAgentId: z.string().optional(), // Specific worker or null for any
91
+ status: TeleportRequestStatusSchema,
92
+
93
+ // Context Package
94
+ summary: z.string().min(1), // Required: AI summary of session
95
+ currentGoal: z.string().optional(), // What to accomplish
96
+ relevantFiles: z.string().optional(), // JSON array of RelevantFile
97
+ contextNotes: z.string().optional(), // Additional context
98
+ workingDirectory: z.string().optional(), // CWD of original session
99
+ projectPath: z.string().optional(), // Project root
100
+
101
+ // Timestamps
102
+ createdAt: z.string(),
103
+ claimedAt: z.string().optional(),
104
+ claimedBy: z.string().optional(), // Agent ID that claimed it
105
+ startedAt: z.string().optional(),
106
+ finishedAt: z.string().optional(),
107
+
108
+ // Result
109
+ resultTaskId: z.string().optional(), // Links to agent_tasks when work begins
110
+ output: z.string().optional(),
111
+ failureReason: z.string().optional(),
112
+ });
113
+ export type TeleportRequest = z.infer<typeof TeleportRequestSchema>;
114
+ ```
115
+
116
+ #### 2. Database Schema (`src/be/db.ts`)
117
+
118
+ Add table creation after `agent_log` table (around line 60):
119
+
120
+ ```sql
121
+ CREATE TABLE IF NOT EXISTS teleport_requests (
122
+ id TEXT PRIMARY KEY,
123
+ sourceAgentId TEXT,
124
+ targetAgentId TEXT,
125
+ status TEXT NOT NULL CHECK(status IN ('pending', 'claimed', 'started', 'completed', 'failed')),
126
+
127
+ -- Context Package
128
+ summary TEXT NOT NULL,
129
+ currentGoal TEXT,
130
+ relevantFiles TEXT,
131
+ contextNotes TEXT,
132
+ workingDirectory TEXT,
133
+ projectPath TEXT,
134
+
135
+ -- Timestamps
136
+ createdAt TEXT NOT NULL,
137
+ claimedAt TEXT,
138
+ claimedBy TEXT,
139
+ startedAt TEXT,
140
+ finishedAt TEXT,
141
+
142
+ -- Result
143
+ resultTaskId TEXT,
144
+ output TEXT,
145
+ failureReason TEXT,
146
+
147
+ FOREIGN KEY (sourceAgentId) REFERENCES agents(id) ON DELETE SET NULL,
148
+ FOREIGN KEY (targetAgentId) REFERENCES agents(id) ON DELETE SET NULL,
149
+ FOREIGN KEY (claimedBy) REFERENCES agents(id) ON DELETE SET NULL,
150
+ FOREIGN KEY (resultTaskId) REFERENCES agent_tasks(id) ON DELETE SET NULL
151
+ );
152
+
153
+ CREATE INDEX IF NOT EXISTS idx_teleport_requests_status ON teleport_requests(status);
154
+ CREATE INDEX IF NOT EXISTS idx_teleport_requests_targetAgentId ON teleport_requests(targetAgentId);
155
+ CREATE INDEX IF NOT EXISTS idx_teleport_requests_claimedBy ON teleport_requests(claimedBy);
156
+ ```
157
+
158
+ #### 3. Database Functions (`src/be/db.ts`)
159
+
160
+ Add type and CRUD functions after existing task functions:
161
+
162
+ ```typescript
163
+ // --- Teleport Request Types ---
164
+ type TeleportRequestRow = {
165
+ id: string;
166
+ sourceAgentId: string | null;
167
+ targetAgentId: string | null;
168
+ status: TeleportRequestStatus;
169
+ summary: string;
170
+ currentGoal: string | null;
171
+ relevantFiles: string | null;
172
+ contextNotes: string | null;
173
+ workingDirectory: string | null;
174
+ projectPath: string | null;
175
+ createdAt: string;
176
+ claimedAt: string | null;
177
+ claimedBy: string | null;
178
+ startedAt: string | null;
179
+ finishedAt: string | null;
180
+ resultTaskId: string | null;
181
+ output: string | null;
182
+ failureReason: string | null;
183
+ };
184
+
185
+ function rowToTeleportRequest(row: TeleportRequestRow): TeleportRequest {
186
+ return {
187
+ id: row.id,
188
+ sourceAgentId: row.sourceAgentId ?? undefined,
189
+ targetAgentId: row.targetAgentId ?? undefined,
190
+ status: row.status,
191
+ summary: row.summary,
192
+ currentGoal: row.currentGoal ?? undefined,
193
+ relevantFiles: row.relevantFiles ?? undefined,
194
+ contextNotes: row.contextNotes ?? undefined,
195
+ workingDirectory: row.workingDirectory ?? undefined,
196
+ projectPath: row.projectPath ?? undefined,
197
+ createdAt: row.createdAt,
198
+ claimedAt: row.claimedAt ?? undefined,
199
+ claimedBy: row.claimedBy ?? undefined,
200
+ startedAt: row.startedAt ?? undefined,
201
+ finishedAt: row.finishedAt ?? undefined,
202
+ resultTaskId: row.resultTaskId ?? undefined,
203
+ output: row.output ?? undefined,
204
+ failureReason: row.failureReason ?? undefined,
205
+ };
206
+ }
207
+
208
+ // --- Teleport Request CRUD ---
209
+ export function createTeleportRequest(data: {
210
+ summary: string;
211
+ currentGoal?: string;
212
+ relevantFiles?: string;
213
+ contextNotes?: string;
214
+ workingDirectory?: string;
215
+ projectPath?: string;
216
+ sourceAgentId?: string;
217
+ targetAgentId?: string;
218
+ }): TeleportRequest {
219
+ const id = crypto.randomUUID();
220
+ const row = getDb()
221
+ .prepare<TeleportRequestRow, [string, string | null, string | null, string, string, string | null, string | null, string | null, string | null, string | null]>(
222
+ `INSERT INTO teleport_requests
223
+ (id, sourceAgentId, targetAgentId, status, summary, currentGoal, relevantFiles, contextNotes, workingDirectory, projectPath, createdAt)
224
+ VALUES (?, ?, ?, 'pending', ?, ?, ?, ?, ?, ?, strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))
225
+ RETURNING *`
226
+ )
227
+ .get(
228
+ id,
229
+ data.sourceAgentId ?? null,
230
+ data.targetAgentId ?? null,
231
+ data.summary,
232
+ data.currentGoal ?? null,
233
+ data.relevantFiles ?? null,
234
+ data.contextNotes ?? null,
235
+ data.workingDirectory ?? null,
236
+ data.projectPath ?? null
237
+ );
238
+ if (!row) throw new Error("Failed to create teleport request");
239
+ return rowToTeleportRequest(row);
240
+ }
241
+
242
+ export function getTeleportRequestById(id: string): TeleportRequest | null {
243
+ const row = getDb()
244
+ .prepare<TeleportRequestRow, [string]>("SELECT * FROM teleport_requests WHERE id = ?")
245
+ .get(id);
246
+ return row ? rowToTeleportRequest(row) : null;
247
+ }
248
+
249
+ export function getPendingTeleportForAgent(agentId: string): TeleportRequest | null {
250
+ // Find pending teleport targeted to this agent OR any unassigned pending teleport
251
+ const row = getDb()
252
+ .prepare<TeleportRequestRow, [string]>(
253
+ `SELECT * FROM teleport_requests
254
+ WHERE status = 'pending'
255
+ AND (targetAgentId = ? OR targetAgentId IS NULL)
256
+ ORDER BY createdAt ASC
257
+ LIMIT 1`
258
+ )
259
+ .get(agentId);
260
+ return row ? rowToTeleportRequest(row) : null;
261
+ }
262
+
263
+ export function claimTeleportRequest(teleportId: string, agentId: string): TeleportRequest | null {
264
+ const row = getDb()
265
+ .prepare<TeleportRequestRow, [string, string]>(
266
+ `UPDATE teleport_requests
267
+ SET status = 'claimed', claimedAt = strftime('%Y-%m-%dT%H:%M:%fZ', 'now'), claimedBy = ?
268
+ WHERE id = ? AND status = 'pending'
269
+ RETURNING *`
270
+ )
271
+ .get(agentId, teleportId);
272
+ return row ? rowToTeleportRequest(row) : null;
273
+ }
274
+
275
+ export function startTeleportRequest(teleportId: string, taskId?: string): TeleportRequest | null {
276
+ const row = getDb()
277
+ .prepare<TeleportRequestRow, [string | null, string]>(
278
+ `UPDATE teleport_requests
279
+ SET status = 'started', startedAt = strftime('%Y-%m-%dT%H:%M:%fZ', 'now'), resultTaskId = ?
280
+ WHERE id = ? AND status = 'claimed'
281
+ RETURNING *`
282
+ )
283
+ .get(taskId ?? null, teleportId);
284
+ return row ? rowToTeleportRequest(row) : null;
285
+ }
286
+
287
+ export function completeTeleportRequest(teleportId: string, output?: string): TeleportRequest | null {
288
+ const row = getDb()
289
+ .prepare<TeleportRequestRow, [string | null, string]>(
290
+ `UPDATE teleport_requests
291
+ SET status = 'completed', finishedAt = strftime('%Y-%m-%dT%H:%M:%fZ', 'now'), output = ?
292
+ WHERE id = ? AND status IN ('claimed', 'started')
293
+ RETURNING *`
294
+ )
295
+ .get(output ?? null, teleportId);
296
+ return row ? rowToTeleportRequest(row) : null;
297
+ }
298
+
299
+ export function failTeleportRequest(teleportId: string, failureReason: string): TeleportRequest | null {
300
+ const row = getDb()
301
+ .prepare<TeleportRequestRow, [string, string]>(
302
+ `UPDATE teleport_requests
303
+ SET status = 'failed', finishedAt = strftime('%Y-%m-%dT%H:%M:%fZ', 'now'), failureReason = ?
304
+ WHERE id = ? AND status IN ('claimed', 'started')
305
+ RETURNING *`
306
+ )
307
+ .get(failureReason, teleportId);
308
+ return row ? rowToTeleportRequest(row) : null;
309
+ }
310
+
311
+ export function getAllTeleportRequests(options?: { status?: TeleportRequestStatus }): TeleportRequest[] {
312
+ if (options?.status) {
313
+ return getDb()
314
+ .prepare<TeleportRequestRow, [string]>(
315
+ "SELECT * FROM teleport_requests WHERE status = ? ORDER BY createdAt DESC"
316
+ )
317
+ .all(options.status)
318
+ .map(rowToTeleportRequest);
319
+ }
320
+ return getDb()
321
+ .prepare<TeleportRequestRow, []>("SELECT * FROM teleport_requests ORDER BY createdAt DESC")
322
+ .all()
323
+ .map(rowToTeleportRequest);
324
+ }
325
+ ```
326
+
327
+ ### Success Criteria:
328
+
329
+ #### Automated Verification:
330
+ - [ ] TypeScript compiles: `bun run tsc:check`
331
+ - [ ] Server starts without errors: `bun run dev:http`
332
+ - [ ] Database table created (check with sqlite3 CLI)
333
+
334
+ #### Manual Verification:
335
+ - [ ] Can manually insert/query teleport_requests table
336
+
337
+ **Implementation Note**: After completing this phase and all automated verification passes, pause here for manual confirmation that the database changes work correctly before proceeding to the next phase.
338
+
339
+ ---
340
+
341
+ ## Phase 2: Teleport MCP Tools
342
+
343
+ ### Overview
344
+ Add 5 new MCP tools for teleport lifecycle management.
345
+
346
+ ### Changes Required:
347
+
348
+ #### 1. teleport-out Tool (`src/tools/teleport-out.ts`)
349
+
350
+ **File**: `src/tools/teleport-out.ts` (NEW)
351
+
352
+ ```typescript
353
+ import { z } from "zod/v4";
354
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
355
+ import { createToolRegistrar, type RequestInfo } from "./utils";
356
+ import {
357
+ createTeleportRequest,
358
+ getAgentById,
359
+ getAllAgents,
360
+ } from "../be/db";
361
+ import { RelevantFileSchema } from "../types";
362
+
363
+ const inputSchema = z.object({
364
+ summary: z.string().min(1).describe("AI-generated summary of current session and work accomplished"),
365
+ currentGoal: z.string().optional().describe("What you're trying to accomplish"),
366
+ relevantFiles: z.array(RelevantFileSchema).optional().describe("Files relevant to the task"),
367
+ contextNotes: z.string().optional().describe("Additional context for the receiving agent"),
368
+ targetAgentId: z.string().uuid().optional().describe("Specific worker agent ID, or omit for any available worker"),
369
+ workingDirectory: z.string().optional().describe("Current working directory"),
370
+ projectPath: z.string().optional().describe("Project root path"),
371
+ });
372
+
373
+ const outputSchema = z.object({
374
+ success: z.boolean(),
375
+ teleportId: z.string().uuid().optional(),
376
+ message: z.string(),
377
+ targetAgent: z.object({
378
+ id: z.string(),
379
+ name: z.string(),
380
+ status: z.string(),
381
+ }).optional(),
382
+ });
383
+
384
+ export function registerTeleportOut(server: McpServer): void {
385
+ createToolRegistrar(server)(
386
+ "teleport-out",
387
+ {
388
+ description: "Transfer current session context to a worker agent to continue the work",
389
+ inputSchema,
390
+ outputSchema,
391
+ },
392
+ async (args, requestInfo: RequestInfo) => {
393
+ // Validate target agent if specified
394
+ if (args.targetAgentId) {
395
+ const targetAgent = getAgentById(args.targetAgentId);
396
+ if (!targetAgent) {
397
+ return {
398
+ success: false,
399
+ message: `Target agent ${args.targetAgentId} not found`,
400
+ };
401
+ }
402
+ if (targetAgent.isLead) {
403
+ return {
404
+ success: false,
405
+ message: "Cannot teleport to lead agent. Choose a worker agent.",
406
+ };
407
+ }
408
+ if (targetAgent.status === "offline") {
409
+ return {
410
+ success: false,
411
+ message: `Target agent "${targetAgent.name}" is offline`,
412
+ };
413
+ }
414
+ } else {
415
+ // Check if any workers are available
416
+ const workers = getAllAgents().filter(a => !a.isLead && a.status !== "offline");
417
+ if (workers.length === 0) {
418
+ return {
419
+ success: false,
420
+ message: "No worker agents available to receive teleport",
421
+ };
422
+ }
423
+ }
424
+
425
+ // Serialize relevantFiles if provided
426
+ const relevantFilesJson = args.relevantFiles
427
+ ? JSON.stringify(args.relevantFiles)
428
+ : undefined;
429
+
430
+ // Create teleport request
431
+ const teleport = createTeleportRequest({
432
+ summary: args.summary,
433
+ currentGoal: args.currentGoal,
434
+ relevantFiles: relevantFilesJson,
435
+ contextNotes: args.contextNotes,
436
+ workingDirectory: args.workingDirectory,
437
+ projectPath: args.projectPath,
438
+ sourceAgentId: requestInfo.agentId,
439
+ targetAgentId: args.targetAgentId,
440
+ });
441
+
442
+ const targetAgent = args.targetAgentId
443
+ ? getAgentById(args.targetAgentId)
444
+ : undefined;
445
+
446
+ return {
447
+ success: true,
448
+ teleportId: teleport.id,
449
+ message: args.targetAgentId
450
+ ? `Session teleported to ${targetAgent?.name}. Teleport ID: ${teleport.id}`
451
+ : `Session teleported to swarm. Any available worker will pick it up. Teleport ID: ${teleport.id}`,
452
+ targetAgent: targetAgent
453
+ ? { id: targetAgent.id, name: targetAgent.name, status: targetAgent.status }
454
+ : undefined,
455
+ };
456
+ }
457
+ );
458
+ }
459
+ ```
460
+
461
+ #### 2. poll-teleport Tool (`src/tools/poll-teleport.ts`)
462
+
463
+ **File**: `src/tools/poll-teleport.ts` (NEW)
464
+
465
+ ```typescript
466
+ import { z } from "zod/v4";
467
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
468
+ import { createToolRegistrar, type RequestInfo } from "./utils";
469
+ import {
470
+ getPendingTeleportForAgent,
471
+ claimTeleportRequest,
472
+ getAgentById,
473
+ updateAgentStatus,
474
+ } from "../be/db";
475
+ import { TeleportRequestSchema } from "../types";
476
+
477
+ const inputSchema = z.object({});
478
+
479
+ const outputSchema = z.object({
480
+ success: z.boolean(),
481
+ message: z.string(),
482
+ teleport: TeleportRequestSchema.optional(),
483
+ waitedForSeconds: z.number(),
484
+ });
485
+
486
+ const POLL_TIMEOUT_MS = 30_000; // 30 seconds (shorter than poll-task)
487
+ const POLL_INTERVAL_MS = 2_000; // 2 seconds
488
+
489
+ export function registerPollTeleport(server: McpServer): void {
490
+ createToolRegistrar(server)(
491
+ "poll-teleport",
492
+ {
493
+ description: "Poll for pending teleport requests to continue another session's work",
494
+ inputSchema,
495
+ outputSchema,
496
+ },
497
+ async (args, requestInfo: RequestInfo) => {
498
+ const agentId = requestInfo.agentId;
499
+ if (!agentId) {
500
+ return {
501
+ success: false,
502
+ message: "Agent ID required. Set X-Agent-ID header.",
503
+ waitedForSeconds: 0,
504
+ };
505
+ }
506
+
507
+ const agent = getAgentById(agentId);
508
+ if (!agent) {
509
+ return {
510
+ success: false,
511
+ message: `Agent ${agentId} not found`,
512
+ waitedForSeconds: 0,
513
+ };
514
+ }
515
+
516
+ if (agent.isLead) {
517
+ return {
518
+ success: false,
519
+ message: "Lead agents cannot poll for teleports",
520
+ waitedForSeconds: 0,
521
+ };
522
+ }
523
+
524
+ const startTime = Date.now();
525
+ let elapsedMs = 0;
526
+
527
+ while (elapsedMs < POLL_TIMEOUT_MS) {
528
+ // Try to claim a pending teleport atomically
529
+ const pending = getPendingTeleportForAgent(agentId);
530
+
531
+ if (pending) {
532
+ const claimed = claimTeleportRequest(pending.id, agentId);
533
+
534
+ if (claimed) {
535
+ // Update agent status to busy
536
+ updateAgentStatus(agentId, "busy");
537
+
538
+ return {
539
+ success: true,
540
+ message: `Claimed teleport ${claimed.id}. Context follows.`,
541
+ teleport: claimed,
542
+ waitedForSeconds: Math.round(elapsedMs / 1000),
543
+ };
544
+ }
545
+ }
546
+
547
+ // Wait before next poll
548
+ await new Promise(resolve => setTimeout(resolve, POLL_INTERVAL_MS));
549
+ elapsedMs = Date.now() - startTime;
550
+ }
551
+
552
+ return {
553
+ success: false,
554
+ message: "No teleport requests available. Try poll-task for regular tasks.",
555
+ waitedForSeconds: Math.round(POLL_TIMEOUT_MS / 1000),
556
+ };
557
+ }
558
+ );
559
+ }
560
+ ```
561
+
562
+ #### 3. start-teleport Tool (`src/tools/start-teleport.ts`)
563
+
564
+ **File**: `src/tools/start-teleport.ts` (NEW)
565
+
566
+ ```typescript
567
+ import { z } from "zod/v4";
568
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
569
+ import { createToolRegistrar, type RequestInfo } from "./utils";
570
+ import {
571
+ startTeleportRequest,
572
+ getTeleportRequestById,
573
+ createTask,
574
+ } from "../be/db";
575
+
576
+ const inputSchema = z.object({
577
+ teleportId: z.string().uuid().describe("The teleport request ID to start working on"),
578
+ });
579
+
580
+ const outputSchema = z.object({
581
+ success: z.boolean(),
582
+ message: z.string(),
583
+ taskId: z.string().uuid().optional(),
584
+ });
585
+
586
+ export function registerStartTeleport(server: McpServer): void {
587
+ createToolRegistrar(server)(
588
+ "start-teleport",
589
+ {
590
+ description: "Mark a teleport request as started and optionally create a tracking task",
591
+ inputSchema,
592
+ outputSchema,
593
+ },
594
+ async (args, requestInfo: RequestInfo) => {
595
+ const agentId = requestInfo.agentId;
596
+ if (!agentId) {
597
+ return {
598
+ success: false,
599
+ message: "Agent ID required",
600
+ };
601
+ }
602
+
603
+ const teleport = getTeleportRequestById(args.teleportId);
604
+ if (!teleport) {
605
+ return {
606
+ success: false,
607
+ message: `Teleport ${args.teleportId} not found`,
608
+ };
609
+ }
610
+
611
+ if (teleport.claimedBy !== agentId) {
612
+ return {
613
+ success: false,
614
+ message: "You did not claim this teleport",
615
+ };
616
+ }
617
+
618
+ if (teleport.status !== "claimed") {
619
+ return {
620
+ success: false,
621
+ message: `Teleport is ${teleport.status}, not claimed`,
622
+ };
623
+ }
624
+
625
+ // Create a tracking task with teleport context
626
+ const taskDescription = teleport.currentGoal
627
+ ? `[Teleport] ${teleport.currentGoal}`
628
+ : `[Teleport] Continue: ${teleport.summary.slice(0, 100)}...`;
629
+
630
+ const task = createTask(agentId, taskDescription, {
631
+ source: "mcp",
632
+ });
633
+
634
+ // Mark teleport as started
635
+ const started = startTeleportRequest(args.teleportId, task.id);
636
+
637
+ if (!started) {
638
+ return {
639
+ success: false,
640
+ message: "Failed to start teleport",
641
+ };
642
+ }
643
+
644
+ return {
645
+ success: true,
646
+ message: `Teleport started. Task ${task.id} created for tracking.`,
647
+ taskId: task.id,
648
+ };
649
+ }
650
+ );
651
+ }
652
+ ```
653
+
654
+ #### 4. complete-teleport Tool (`src/tools/complete-teleport.ts`)
655
+
656
+ **File**: `src/tools/complete-teleport.ts` (NEW)
657
+
658
+ ```typescript
659
+ import { z } from "zod/v4";
660
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
661
+ import { createToolRegistrar, type RequestInfo } from "./utils";
662
+ import {
663
+ completeTeleportRequest,
664
+ failTeleportRequest,
665
+ getTeleportRequestById,
666
+ updateAgentStatus,
667
+ completeTask,
668
+ failTask,
669
+ } from "../be/db";
670
+
671
+ const inputSchema = z.object({
672
+ teleportId: z.string().uuid().describe("The teleport request ID"),
673
+ status: z.enum(["completed", "failed"]).describe("Final status"),
674
+ output: z.string().optional().describe("Result/output of the work (for completed)"),
675
+ failureReason: z.string().optional().describe("Reason for failure (for failed)"),
676
+ });
677
+
678
+ const outputSchema = z.object({
679
+ success: z.boolean(),
680
+ message: z.string(),
681
+ });
682
+
683
+ export function registerCompleteTeleport(server: McpServer): void {
684
+ createToolRegistrar(server)(
685
+ "complete-teleport",
686
+ {
687
+ description: "Mark a teleport request as completed or failed",
688
+ inputSchema,
689
+ outputSchema,
690
+ },
691
+ async (args, requestInfo: RequestInfo) => {
692
+ const agentId = requestInfo.agentId;
693
+ if (!agentId) {
694
+ return {
695
+ success: false,
696
+ message: "Agent ID required",
697
+ };
698
+ }
699
+
700
+ const teleport = getTeleportRequestById(args.teleportId);
701
+ if (!teleport) {
702
+ return {
703
+ success: false,
704
+ message: `Teleport ${args.teleportId} not found`,
705
+ };
706
+ }
707
+
708
+ if (teleport.claimedBy !== agentId) {
709
+ return {
710
+ success: false,
711
+ message: "You did not claim this teleport",
712
+ };
713
+ }
714
+
715
+ let result;
716
+ if (args.status === "completed") {
717
+ result = completeTeleportRequest(args.teleportId, args.output);
718
+ // Also complete the tracking task if it exists
719
+ if (teleport.resultTaskId) {
720
+ completeTask(teleport.resultTaskId, args.output);
721
+ }
722
+ } else {
723
+ result = failTeleportRequest(args.teleportId, args.failureReason || "Unknown error");
724
+ if (teleport.resultTaskId) {
725
+ failTask(teleport.resultTaskId, args.failureReason || "Unknown error");
726
+ }
727
+ }
728
+
729
+ if (!result) {
730
+ return {
731
+ success: false,
732
+ message: "Failed to update teleport status",
733
+ };
734
+ }
735
+
736
+ // Set agent back to idle
737
+ updateAgentStatus(agentId, "idle");
738
+
739
+ return {
740
+ success: true,
741
+ message: `Teleport ${args.status}`,
742
+ };
743
+ }
744
+ );
745
+ }
746
+ ```
747
+
748
+ #### 5. get-teleport-details Tool (`src/tools/get-teleport-details.ts`)
749
+
750
+ **File**: `src/tools/get-teleport-details.ts` (NEW)
751
+
752
+ ```typescript
753
+ import { z } from "zod/v4";
754
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
755
+ import { createToolRegistrar, type RequestInfo } from "./utils";
756
+ import { getTeleportRequestById, getAgentById } from "../be/db";
757
+ import { TeleportRequestSchema } from "../types";
758
+
759
+ const inputSchema = z.object({
760
+ teleportId: z.string().uuid().describe("The teleport request ID to get details for"),
761
+ });
762
+
763
+ const outputSchema = z.object({
764
+ success: z.boolean(),
765
+ message: z.string(),
766
+ teleport: TeleportRequestSchema.optional(),
767
+ sourceAgent: z.object({
768
+ id: z.string(),
769
+ name: z.string(),
770
+ }).optional(),
771
+ claimedByAgent: z.object({
772
+ id: z.string(),
773
+ name: z.string(),
774
+ }).optional(),
775
+ });
776
+
777
+ export function registerGetTeleportDetails(server: McpServer): void {
778
+ createToolRegistrar(server)(
779
+ "get-teleport-details",
780
+ {
781
+ description: "Get details of a teleport request",
782
+ inputSchema,
783
+ outputSchema,
784
+ },
785
+ async (args, requestInfo: RequestInfo) => {
786
+ const teleport = getTeleportRequestById(args.teleportId);
787
+
788
+ if (!teleport) {
789
+ return {
790
+ success: false,
791
+ message: `Teleport ${args.teleportId} not found`,
792
+ };
793
+ }
794
+
795
+ const sourceAgent = teleport.sourceAgentId
796
+ ? getAgentById(teleport.sourceAgentId)
797
+ : undefined;
798
+
799
+ const claimedByAgent = teleport.claimedBy
800
+ ? getAgentById(teleport.claimedBy)
801
+ : undefined;
802
+
803
+ return {
804
+ success: true,
805
+ message: `Teleport ${teleport.id} is ${teleport.status}`,
806
+ teleport,
807
+ sourceAgent: sourceAgent
808
+ ? { id: sourceAgent.id, name: sourceAgent.name }
809
+ : undefined,
810
+ claimedByAgent: claimedByAgent
811
+ ? { id: claimedByAgent.id, name: claimedByAgent.name }
812
+ : undefined,
813
+ };
814
+ }
815
+ );
816
+ }
817
+ ```
818
+
819
+ ### Success Criteria:
820
+
821
+ #### Automated Verification:
822
+ - [ ] TypeScript compiles: `bun run tsc:check`
823
+ - [ ] Linting passes: `bun run lint`
824
+ - [ ] Server starts: `bun run dev:http`
825
+
826
+ #### Manual Verification:
827
+ - [ ] Can call `teleport-out` and receive teleport ID
828
+ - [ ] Can call `poll-teleport` and receive pending teleport
829
+ - [ ] Teleport lifecycle flows correctly
830
+
831
+ **Implementation Note**: After completing this phase and all automated verification passes, pause here for manual confirmation before proceeding to the next phase.
832
+
833
+ ---
834
+
835
+ ## Phase 3: Register Tools in Server
836
+
837
+ ### Overview
838
+ Wire up the new teleport tools in the MCP server.
839
+
840
+ ### Changes Required:
841
+
842
+ #### 1. Update Server (`src/server.ts`)
843
+
844
+ Add imports and registrations:
845
+
846
+ ```typescript
847
+ // Add imports after line 10
848
+ import { registerTeleportOut } from "./tools/teleport-out";
849
+ import { registerPollTeleport } from "./tools/poll-teleport";
850
+ import { registerStartTeleport } from "./tools/start-teleport";
851
+ import { registerCompleteTeleport } from "./tools/complete-teleport";
852
+ import { registerGetTeleportDetails } from "./tools/get-teleport-details";
853
+
854
+ // Add registrations after line 33 (after existing tool registrations)
855
+ registerTeleportOut(server);
856
+ registerPollTeleport(server);
857
+ registerStartTeleport(server);
858
+ registerCompleteTeleport(server);
859
+ registerGetTeleportDetails(server);
860
+ ```
861
+
862
+ ### Success Criteria:
863
+
864
+ #### Automated Verification:
865
+ - [ ] TypeScript compiles: `bun run tsc:check`
866
+ - [ ] Server starts and lists teleport tools
867
+
868
+ #### Manual Verification:
869
+ - [ ] Tools appear in MCP tool list
870
+
871
+ ---
872
+
873
+ ## Phase 4: Worker Integration
874
+
875
+ ### Overview
876
+ Update worker hooks to check for teleports before regular tasks.
877
+
878
+ ### Changes Required:
879
+
880
+ #### 1. Update Hook Messages (`src/hooks/hook.ts`)
881
+
882
+ Modify the SessionStart case for workers (around line 60):
883
+
884
+ ```typescript
885
+ } else {
886
+ console.log(
887
+ `${agentInfo.name} is registered as a WORKER agent (status: ${agentInfo.status}) as of ${new Date().toISOString()}.`
888
+ );
889
+ console.log(
890
+ `As a worker agent, FIRST check for teleport requests using the poll-teleport tool.
891
+ Teleport requests contain rich context from sessions that need continuation - summary, goal, relevant files, and notes.
892
+ If no teleport requests are pending, fall back to poll-task for regular task assignments.
893
+
894
+ Priority:
895
+ 1. poll-teleport - Check for session continuations with rich context
896
+ 2. poll-task - Check for regular task assignments
897
+
898
+ When you receive a teleport:
899
+ - Read the summary and context carefully
900
+ - Call start-teleport to begin tracking
901
+ - Continue the work as described
902
+ - Call complete-teleport when done`
903
+ );
904
+ }
905
+ ```
906
+
907
+ ### Success Criteria:
908
+
909
+ #### Automated Verification:
910
+ - [ ] TypeScript compiles: `bun run tsc:check`
911
+ - [ ] Hook message includes teleport guidance
912
+
913
+ #### Manual Verification:
914
+ - [ ] Worker receives teleport-first guidance on session start
915
+
916
+ ---
917
+
918
+ ## Phase 5: HTTP API Endpoints
919
+
920
+ ### Overview
921
+ Add REST endpoints for dashboard visibility.
922
+
923
+ ### Changes Required:
924
+
925
+ #### 1. Update HTTP Server (`src/http.ts`)
926
+
927
+ Add teleport endpoints after existing API routes:
928
+
929
+ ```typescript
930
+ // GET /api/teleports - List teleport requests
931
+ if (req.method === "GET" && url.pathname === "/api/teleports") {
932
+ const statusFilter = url.searchParams.get("status") as TeleportRequestStatus | null;
933
+ const teleports = getAllTeleportRequests(
934
+ statusFilter ? { status: statusFilter } : undefined
935
+ );
936
+ return Response.json({ teleports });
937
+ }
938
+
939
+ // GET /api/teleports/:id - Get teleport details
940
+ if (req.method === "GET" && url.pathname.match(/^\/api\/teleports\/[a-f0-9-]+$/)) {
941
+ const teleportId = url.pathname.split("/").pop()!;
942
+ const teleport = getTeleportRequestById(teleportId);
943
+ if (!teleport) {
944
+ return Response.json({ error: "Teleport not found" }, { status: 404 });
945
+ }
946
+
947
+ const sourceAgent = teleport.sourceAgentId
948
+ ? getAgentById(teleport.sourceAgentId)
949
+ : undefined;
950
+ const claimedByAgent = teleport.claimedBy
951
+ ? getAgentById(teleport.claimedBy)
952
+ : undefined;
953
+
954
+ return Response.json({
955
+ teleport,
956
+ sourceAgent,
957
+ claimedByAgent,
958
+ });
959
+ }
960
+ ```
961
+
962
+ Update `/api/stats` to include teleport counts:
963
+
964
+ ```typescript
965
+ // Add to stats response
966
+ const teleportCounts = {
967
+ pending: getAllTeleportRequests({ status: "pending" }).length,
968
+ claimed: getAllTeleportRequests({ status: "claimed" }).length,
969
+ started: getAllTeleportRequests({ status: "started" }).length,
970
+ completed: getAllTeleportRequests({ status: "completed" }).length,
971
+ failed: getAllTeleportRequests({ status: "failed" }).length,
972
+ };
973
+
974
+ // Include in response
975
+ return Response.json({
976
+ agents: { ... },
977
+ tasks: { ... },
978
+ teleports: teleportCounts, // Add this
979
+ });
980
+ ```
981
+
982
+ ### Success Criteria:
983
+
984
+ #### Automated Verification:
985
+ - [ ] TypeScript compiles: `bun run tsc:check`
986
+ - [ ] Server starts without errors
987
+
988
+ #### Manual Verification:
989
+ - [ ] `/api/teleports` returns list
990
+ - [ ] `/api/teleports/:id` returns details
991
+ - [ ] `/api/stats` includes teleport counts
992
+
993
+ ---
994
+
995
+ ## User Experience Flow
996
+
997
+ ### Sending (Local Session)
998
+
999
+ ```
1000
+ User: "Send this to a worker to continue"
1001
+
1002
+ Claude: I'll package the current context and teleport it to an available worker.
1003
+
1004
+ [Calls teleport-out with:
1005
+ summary: "Implementing user authentication. Created login form, connected to API.
1006
+ Currently debugging token refresh - getting 401 intermittently."
1007
+ currentGoal: "Fix token refresh to prevent session expiration"
1008
+ relevantFiles: [
1009
+ { path: "src/components/LoginForm.tsx", summary: "Login UI" },
1010
+ { path: "src/api/auth.ts", summary: "Auth client with refresh logic" }
1011
+ ]
1012
+ contextNotes: "The refresh endpoint seems to fail after ~15 minutes of inactivity"
1013
+ ]
1014
+
1015
+ Claude: Session teleported! Teleport ID: abc-12345
1016
+ A worker will pick this up and continue the debugging.
1017
+ ```
1018
+
1019
+ ### Receiving (Worker)
1020
+
1021
+ ```
1022
+ [Worker calls poll-teleport, receives:]
1023
+
1024
+ {
1025
+ "success": true,
1026
+ "teleport": {
1027
+ "id": "abc-12345",
1028
+ "summary": "Implementing user authentication...",
1029
+ "currentGoal": "Fix token refresh to prevent session expiration",
1030
+ "relevantFiles": "[{...}]",
1031
+ "contextNotes": "The refresh endpoint seems to fail after ~15 minutes..."
1032
+ }
1033
+ }
1034
+
1035
+ Claude: Received teleport abc-12345!
1036
+
1037
+ I'm continuing work from another session. Here's the context:
1038
+ - **Summary**: Implementing user authentication. Login form created, API connected.
1039
+ - **Current Goal**: Fix token refresh to prevent session expiration
1040
+ - **Key Files**: src/components/LoginForm.tsx, src/api/auth.ts
1041
+ - **Notes**: Refresh fails after ~15 minutes of inactivity
1042
+
1043
+ Let me start by reading the auth.ts file to understand the refresh logic...
1044
+
1045
+ [Calls start-teleport to begin tracking]
1046
+ [Does the work]
1047
+ [Calls complete-teleport when done]
1048
+ ```
1049
+
1050
+ ---
1051
+
1052
+ ## Testing Strategy
1053
+
1054
+ ### Unit Tests
1055
+
1056
+ Create `src/tools/teleport-out.test.ts`:
1057
+
1058
+ ```typescript
1059
+ import { test, expect, describe, beforeEach, mock } from "bun:test";
1060
+
1061
+ // Mock database functions
1062
+ mock.module("../be/db", () => ({
1063
+ createTeleportRequest: mock(() => ({
1064
+ id: "test-uuid",
1065
+ status: "pending",
1066
+ summary: "Test summary",
1067
+ createdAt: new Date().toISOString(),
1068
+ })),
1069
+ getAgentById: mock((id: string) => {
1070
+ if (id === "worker-1") return { id: "worker-1", name: "Worker", isLead: false, status: "idle" };
1071
+ if (id === "lead-1") return { id: "lead-1", name: "Lead", isLead: true, status: "idle" };
1072
+ return null;
1073
+ }),
1074
+ getAllAgents: mock(() => [
1075
+ { id: "worker-1", name: "Worker", isLead: false, status: "idle" },
1076
+ ]),
1077
+ }));
1078
+
1079
+ describe("teleport-out", () => {
1080
+ test("creates teleport request with summary", async () => {
1081
+ // Test implementation
1082
+ });
1083
+
1084
+ test("rejects teleport to lead agent", async () => {
1085
+ // Test implementation
1086
+ });
1087
+
1088
+ test("rejects when no workers available", async () => {
1089
+ // Test implementation
1090
+ });
1091
+ });
1092
+ ```
1093
+
1094
+ ### Integration Tests
1095
+
1096
+ Manual testing checklist:
1097
+
1098
+ 1. **Teleport Creation**
1099
+ - Call `teleport-out` from local session
1100
+ - Verify teleport appears in `/api/teleports`
1101
+ - Verify status is "pending"
1102
+
1103
+ 2. **Teleport Claiming**
1104
+ - Start a worker
1105
+ - Worker calls `poll-teleport`
1106
+ - Verify teleport status changes to "claimed"
1107
+
1108
+ 3. **Teleport Completion**
1109
+ - Worker calls `start-teleport`
1110
+ - Worker calls `complete-teleport`
1111
+ - Verify status is "completed"
1112
+
1113
+ 4. **Error Cases**
1114
+ - Teleport to offline agent - verify rejection
1115
+ - Teleport to lead agent - verify rejection
1116
+ - Double-claim - verify only one succeeds
1117
+
1118
+ ---
1119
+
1120
+ ## Files Summary
1121
+
1122
+ | File | Action | Purpose |
1123
+ |------|--------|---------|
1124
+ | `src/types.ts` | Edit | Add TeleportRequest types |
1125
+ | `src/be/db.ts` | Edit | Add teleport_requests table + CRUD |
1126
+ | `src/tools/teleport-out.ts` | Create | Send context to swarm |
1127
+ | `src/tools/poll-teleport.ts` | Create | Worker claims teleport |
1128
+ | `src/tools/start-teleport.ts` | Create | Worker starts work |
1129
+ | `src/tools/complete-teleport.ts` | Create | Worker completes |
1130
+ | `src/tools/get-teleport-details.ts` | Create | View status |
1131
+ | `src/server.ts` | Edit | Register new tools |
1132
+ | `src/hooks/hook.ts` | Edit | Worker teleport awareness |
1133
+ | `src/http.ts` | Edit | REST API endpoints |
1134
+
1135
+ ---
1136
+
1137
+ ## References
1138
+
1139
+ - Claude Code Web teleport: `claude --teleport <session_id>`
1140
+ - Session storage: `~/.claude/projects/`
1141
+ - Existing task flow: `src/tools/send-task.ts`, `src/tools/poll-task.ts`
1142
+ - Hook system: `src/hooks/hook.ts`