@saadnvd1/agent-os 0.3.824 → 0.3.825
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/app/api/fork/cancel/route.ts +19 -0
- package/app/api/fork/children/route.ts +52 -0
- package/app/api/fork/result/route.ts +29 -0
- package/app/api/fork/return/route.ts +20 -0
- package/app/api/fork/route.ts +32 -0
- package/app/api/fork/wait/route.ts +27 -0
- package/app/api/load/heavy/lease/route.ts +26 -0
- package/app/api/sessions/[id]/archive/route.ts +17 -4
- package/app/api/sessions/[id]/route.ts +2 -3
- package/app/page.tsx +0 -5
- package/components/Chat/ChatPanel.tsx +33 -206
- package/components/Chat/Forks.tsx +278 -0
- package/components/Chat/ReplyingTo.tsx +24 -0
- package/components/Chat/TimelineRows.tsx +194 -0
- package/components/Pane/DesktopTabBar.tsx +0 -6
- package/components/Pane/MobileTabBar.tsx +0 -6
- package/components/Pane/MobileViewPicker.test.ts +1 -8
- package/components/Pane/MobileViewPicker.tsx +0 -11
- package/components/Pane/PaneViewToggle.test.ts +0 -2
- package/components/Pane/PaneViewToggle.tsx +0 -17
- package/components/Pane/index.tsx +0 -50
- package/components/SessionList/useDoneAction.ts +18 -3
- package/contexts/PaneContext.tsx +1 -1
- package/data/done/index.ts +26 -2
- package/data/fork/index.ts +83 -0
- package/lib/chat/commands.ts +15 -0
- package/lib/chat/runner.ts +8 -2
- package/lib/chat/worker/extras.ts +7 -1
- package/lib/chat/worker/host.ts +2 -0
- package/lib/config/client-config-types.ts +7 -0
- package/lib/config/client-config.ts +2 -0
- package/lib/db/migrations/20261102T0100_forks.ts +87 -0
- package/lib/done/archive.ts +21 -1
- package/lib/experiments/registry.ts +8 -0
- package/lib/fork/api.ts +10 -0
- package/lib/fork/brief.ts +63 -0
- package/lib/fork/conversation.ts +75 -0
- package/lib/fork/deliver.ts +91 -0
- package/lib/fork/extras.ts +53 -0
- package/lib/fork/guard.ts +323 -0
- package/lib/fork/heavy-lease.ts +104 -0
- package/lib/fork/held.ts +26 -0
- package/lib/fork/ledger.ts +324 -0
- package/lib/fork/options.ts +46 -0
- package/lib/fork/point.ts +41 -0
- package/lib/fork/scratch.ts +67 -0
- package/lib/fork/settle.ts +215 -0
- package/lib/fork/snapshot.ts +69 -0
- package/lib/fork/start.ts +273 -0
- package/lib/fork/tick.ts +54 -0
- package/lib/fork/types.ts +79 -0
- package/lib/fork/view.ts +116 -0
- package/lib/fork/waits.ts +83 -0
- package/lib/fork/worktree.ts +115 -0
- package/lib/orchestrator/shell-guard.ts +6 -3
- package/lib/orchestrator/write-guard.ts +1 -1
- package/lib/security/demo.ts +1 -2
- package/lib/sessions/end.ts +5 -0
- package/package.json +1 -1
- package/server.ts +2 -0
- package/app/api/orchestrate/spawn/route.ts +0 -54
- package/app/api/orchestrate/workers/[id]/route.ts +0 -89
- package/app/api/orchestrate/workers/route.ts +0 -31
- package/app/api/sessions/[id]/mcp-config/route.ts +0 -34
- package/components/ConductorPanel.tsx +0 -258
- package/lib/mcp-config.ts +0 -80
- package/lib/orchestration.ts +0 -463
- package/mcp/orchestration-server.ts +0 -441
|
@@ -1,441 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env npx ts-node
|
|
2
|
-
/**
|
|
3
|
-
* MCP Server for Session Orchestration
|
|
4
|
-
*
|
|
5
|
-
* Exposes tools for any Claude session to become a "conductor" that spawns
|
|
6
|
-
* and manages worker sessions. Each worker gets its own git worktree.
|
|
7
|
-
*
|
|
8
|
-
* Setup (one-time, in ~/.claude/settings.json or project .mcp.json):
|
|
9
|
-
* {
|
|
10
|
-
* "mcpServers": {
|
|
11
|
-
* "agent-os": {
|
|
12
|
-
* "command": "npx",
|
|
13
|
-
* "args": ["tsx", "/path/to/agent-os/mcp/orchestration-server.ts"],
|
|
14
|
-
* "env": {
|
|
15
|
-
* "AGENTOS_URL": "http://localhost:3011"
|
|
16
|
-
* }
|
|
17
|
-
* }
|
|
18
|
-
* }
|
|
19
|
-
* }
|
|
20
|
-
*
|
|
21
|
-
* Usage: Any session can spawn workers by calling spawn_worker with its own
|
|
22
|
-
* session ID as conductorId. The UI will show the conductor/worker hierarchy.
|
|
23
|
-
*/
|
|
24
|
-
|
|
25
|
-
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
26
|
-
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
27
|
-
import {
|
|
28
|
-
CallToolRequestSchema,
|
|
29
|
-
ListToolsRequestSchema,
|
|
30
|
-
} from "@modelcontextprotocol/sdk/types.js";
|
|
31
|
-
|
|
32
|
-
const AGENTOS_URL = process.env.AGENTOS_URL || "http://localhost:3011";
|
|
33
|
-
|
|
34
|
-
// Optional: Get conductor session ID from environment (can also be passed per-call)
|
|
35
|
-
const DEFAULT_CONDUCTOR_ID = process.env.CONDUCTOR_SESSION_ID || "";
|
|
36
|
-
|
|
37
|
-
async function apiCall(path: string, options?: RequestInit) {
|
|
38
|
-
const url = `${AGENTOS_URL}${path}`;
|
|
39
|
-
const response = await fetch(url, {
|
|
40
|
-
...options,
|
|
41
|
-
headers: {
|
|
42
|
-
"Content-Type": "application/json",
|
|
43
|
-
...(process.env.AGENTOS_TOKEN && {
|
|
44
|
-
Authorization: `Bearer ${process.env.AGENTOS_TOKEN}`,
|
|
45
|
-
}),
|
|
46
|
-
...options?.headers,
|
|
47
|
-
},
|
|
48
|
-
});
|
|
49
|
-
return response.json();
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
const server = new Server(
|
|
53
|
-
{
|
|
54
|
-
name: "agent-os-orchestration",
|
|
55
|
-
version: "1.0.0",
|
|
56
|
-
},
|
|
57
|
-
{
|
|
58
|
-
capabilities: {
|
|
59
|
-
tools: {},
|
|
60
|
-
},
|
|
61
|
-
}
|
|
62
|
-
);
|
|
63
|
-
|
|
64
|
-
// List available tools
|
|
65
|
-
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
66
|
-
return {
|
|
67
|
-
tools: [
|
|
68
|
-
{
|
|
69
|
-
name: "spawn_worker",
|
|
70
|
-
description:
|
|
71
|
-
"Spawn a new worker session to handle a task. Creates an isolated git worktree for the worker.",
|
|
72
|
-
inputSchema: {
|
|
73
|
-
type: "object" as const,
|
|
74
|
-
properties: {
|
|
75
|
-
conductorId: {
|
|
76
|
-
type: "string",
|
|
77
|
-
description:
|
|
78
|
-
"Your session ID (the conductor). Required unless CONDUCTOR_SESSION_ID env var is set.",
|
|
79
|
-
},
|
|
80
|
-
task: {
|
|
81
|
-
type: "string",
|
|
82
|
-
description: "The task/prompt to send to the worker",
|
|
83
|
-
},
|
|
84
|
-
workingDirectory: {
|
|
85
|
-
type: "string",
|
|
86
|
-
description:
|
|
87
|
-
"The git repository path for the worker to operate in",
|
|
88
|
-
},
|
|
89
|
-
branchName: {
|
|
90
|
-
type: "string",
|
|
91
|
-
description:
|
|
92
|
-
"Optional branch name for the worktree (auto-generated if not provided)",
|
|
93
|
-
},
|
|
94
|
-
useWorktree: {
|
|
95
|
-
type: "boolean",
|
|
96
|
-
description:
|
|
97
|
-
"Whether to create an isolated worktree (default: true)",
|
|
98
|
-
default: true,
|
|
99
|
-
},
|
|
100
|
-
model: {
|
|
101
|
-
type: "string",
|
|
102
|
-
description: "Model to use (opus, sonnet, fable, haiku)",
|
|
103
|
-
default: "opus",
|
|
104
|
-
},
|
|
105
|
-
},
|
|
106
|
-
required: ["task", "workingDirectory"],
|
|
107
|
-
},
|
|
108
|
-
},
|
|
109
|
-
{
|
|
110
|
-
name: "list_workers",
|
|
111
|
-
description: "List all worker sessions spawned by a conductor",
|
|
112
|
-
inputSchema: {
|
|
113
|
-
type: "object" as const,
|
|
114
|
-
properties: {
|
|
115
|
-
conductorId: {
|
|
116
|
-
type: "string",
|
|
117
|
-
description:
|
|
118
|
-
"The conductor session ID. Required unless CONDUCTOR_SESSION_ID env var is set.",
|
|
119
|
-
},
|
|
120
|
-
},
|
|
121
|
-
},
|
|
122
|
-
},
|
|
123
|
-
{
|
|
124
|
-
name: "get_worker_output",
|
|
125
|
-
description: "Get recent terminal output from a worker",
|
|
126
|
-
inputSchema: {
|
|
127
|
-
type: "object" as const,
|
|
128
|
-
properties: {
|
|
129
|
-
workerId: {
|
|
130
|
-
type: "string",
|
|
131
|
-
description: "The worker session ID",
|
|
132
|
-
},
|
|
133
|
-
lines: {
|
|
134
|
-
type: "number",
|
|
135
|
-
description: "Number of lines to retrieve (default: 50)",
|
|
136
|
-
default: 50,
|
|
137
|
-
},
|
|
138
|
-
},
|
|
139
|
-
required: ["workerId"],
|
|
140
|
-
},
|
|
141
|
-
},
|
|
142
|
-
{
|
|
143
|
-
name: "send_to_worker",
|
|
144
|
-
description: "Send a message or command to a worker",
|
|
145
|
-
inputSchema: {
|
|
146
|
-
type: "object" as const,
|
|
147
|
-
properties: {
|
|
148
|
-
workerId: {
|
|
149
|
-
type: "string",
|
|
150
|
-
description: "The worker session ID",
|
|
151
|
-
},
|
|
152
|
-
message: {
|
|
153
|
-
type: "string",
|
|
154
|
-
description: "The message to send",
|
|
155
|
-
},
|
|
156
|
-
},
|
|
157
|
-
required: ["workerId", "message"],
|
|
158
|
-
},
|
|
159
|
-
},
|
|
160
|
-
{
|
|
161
|
-
name: "complete_worker",
|
|
162
|
-
description: "Mark a worker as completed (task finished successfully)",
|
|
163
|
-
inputSchema: {
|
|
164
|
-
type: "object" as const,
|
|
165
|
-
properties: {
|
|
166
|
-
workerId: {
|
|
167
|
-
type: "string",
|
|
168
|
-
description: "The worker session ID",
|
|
169
|
-
},
|
|
170
|
-
},
|
|
171
|
-
required: ["workerId"],
|
|
172
|
-
},
|
|
173
|
-
},
|
|
174
|
-
{
|
|
175
|
-
name: "kill_worker",
|
|
176
|
-
description:
|
|
177
|
-
"Kill a worker session and optionally clean up its worktree",
|
|
178
|
-
inputSchema: {
|
|
179
|
-
type: "object" as const,
|
|
180
|
-
properties: {
|
|
181
|
-
workerId: {
|
|
182
|
-
type: "string",
|
|
183
|
-
description: "The worker session ID",
|
|
184
|
-
},
|
|
185
|
-
cleanupWorktree: {
|
|
186
|
-
type: "boolean",
|
|
187
|
-
description: "Whether to delete the worktree (default: false)",
|
|
188
|
-
default: false,
|
|
189
|
-
},
|
|
190
|
-
},
|
|
191
|
-
required: ["workerId"],
|
|
192
|
-
},
|
|
193
|
-
},
|
|
194
|
-
{
|
|
195
|
-
name: "get_workers_summary",
|
|
196
|
-
description: "Get a summary count of workers by status",
|
|
197
|
-
inputSchema: {
|
|
198
|
-
type: "object" as const,
|
|
199
|
-
properties: {
|
|
200
|
-
conductorId: {
|
|
201
|
-
type: "string",
|
|
202
|
-
description:
|
|
203
|
-
"The conductor session ID. Required unless CONDUCTOR_SESSION_ID env var is set.",
|
|
204
|
-
},
|
|
205
|
-
},
|
|
206
|
-
},
|
|
207
|
-
},
|
|
208
|
-
],
|
|
209
|
-
};
|
|
210
|
-
});
|
|
211
|
-
|
|
212
|
-
// Helper to get conductor ID from args or env
|
|
213
|
-
function getConductorId(
|
|
214
|
-
args: Record<string, unknown> | undefined
|
|
215
|
-
): string | null {
|
|
216
|
-
return (args?.conductorId as string) || DEFAULT_CONDUCTOR_ID || null;
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
// Handle tool calls
|
|
220
|
-
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
221
|
-
const { name, arguments: args } = request.params;
|
|
222
|
-
|
|
223
|
-
try {
|
|
224
|
-
switch (name) {
|
|
225
|
-
case "spawn_worker": {
|
|
226
|
-
const conductorId = getConductorId(args);
|
|
227
|
-
if (!conductorId) {
|
|
228
|
-
return {
|
|
229
|
-
content: [
|
|
230
|
-
{
|
|
231
|
-
type: "text" as const,
|
|
232
|
-
text: "Error: conductorId is required. Pass it as a parameter or set CONDUCTOR_SESSION_ID env var.",
|
|
233
|
-
},
|
|
234
|
-
],
|
|
235
|
-
};
|
|
236
|
-
}
|
|
237
|
-
const result = await apiCall("/api/orchestrate/spawn", {
|
|
238
|
-
method: "POST",
|
|
239
|
-
body: JSON.stringify({
|
|
240
|
-
conductorSessionId: conductorId,
|
|
241
|
-
task: args?.task,
|
|
242
|
-
workingDirectory: args?.workingDirectory,
|
|
243
|
-
branchName: args?.branchName,
|
|
244
|
-
useWorktree: args?.useWorktree ?? true,
|
|
245
|
-
model: args?.model || "opus",
|
|
246
|
-
}),
|
|
247
|
-
});
|
|
248
|
-
return {
|
|
249
|
-
content: [
|
|
250
|
-
{
|
|
251
|
-
type: "text" as const,
|
|
252
|
-
text: result.error
|
|
253
|
-
? `Error: ${result.error}`
|
|
254
|
-
: `Worker spawned successfully!\nID: ${result.session.id}\nName: ${result.session.name}\nWorktree: ${result.session.worktree_path || "none"}`,
|
|
255
|
-
},
|
|
256
|
-
],
|
|
257
|
-
};
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
case "list_workers": {
|
|
261
|
-
const conductorId = getConductorId(args);
|
|
262
|
-
if (!conductorId) {
|
|
263
|
-
return {
|
|
264
|
-
content: [
|
|
265
|
-
{
|
|
266
|
-
type: "text" as const,
|
|
267
|
-
text: "Error: conductorId is required. Pass it as a parameter or set CONDUCTOR_SESSION_ID env var.",
|
|
268
|
-
},
|
|
269
|
-
],
|
|
270
|
-
};
|
|
271
|
-
}
|
|
272
|
-
const result = await apiCall(
|
|
273
|
-
`/api/orchestrate/workers?conductorId=${conductorId}`
|
|
274
|
-
);
|
|
275
|
-
if (result.error) {
|
|
276
|
-
return {
|
|
277
|
-
content: [
|
|
278
|
-
{ type: "text" as const, text: `Error: ${result.error}` },
|
|
279
|
-
],
|
|
280
|
-
};
|
|
281
|
-
}
|
|
282
|
-
const workers = result.workers || [];
|
|
283
|
-
if (workers.length === 0) {
|
|
284
|
-
return {
|
|
285
|
-
content: [
|
|
286
|
-
{ type: "text" as const, text: "No workers spawned yet." },
|
|
287
|
-
],
|
|
288
|
-
};
|
|
289
|
-
}
|
|
290
|
-
const list = workers
|
|
291
|
-
.map(
|
|
292
|
-
(w: {
|
|
293
|
-
id: string;
|
|
294
|
-
name: string;
|
|
295
|
-
status: string;
|
|
296
|
-
task: string;
|
|
297
|
-
branchName: string | null;
|
|
298
|
-
}) =>
|
|
299
|
-
`- [${w.status.toUpperCase()}] ${w.name} (${w.id.slice(0, 8)})\n Task: ${w.task}\n Branch: ${w.branchName || "none"}`
|
|
300
|
-
)
|
|
301
|
-
.join("\n\n");
|
|
302
|
-
return {
|
|
303
|
-
content: [{ type: "text" as const, text: `Workers:\n\n${list}` }],
|
|
304
|
-
};
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
case "get_worker_output": {
|
|
308
|
-
const result = await apiCall(
|
|
309
|
-
`/api/orchestrate/workers/${args?.workerId}?lines=${args?.lines || 50}`
|
|
310
|
-
);
|
|
311
|
-
return {
|
|
312
|
-
content: [
|
|
313
|
-
{
|
|
314
|
-
type: "text" as const,
|
|
315
|
-
text: result.error
|
|
316
|
-
? `Error: ${result.error}`
|
|
317
|
-
: result.output || "(no output)",
|
|
318
|
-
},
|
|
319
|
-
],
|
|
320
|
-
};
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
case "send_to_worker": {
|
|
324
|
-
const result = await apiCall(
|
|
325
|
-
`/api/orchestrate/workers/${args?.workerId}`,
|
|
326
|
-
{
|
|
327
|
-
method: "POST",
|
|
328
|
-
body: JSON.stringify({
|
|
329
|
-
action: "send",
|
|
330
|
-
message: args?.message,
|
|
331
|
-
}),
|
|
332
|
-
}
|
|
333
|
-
);
|
|
334
|
-
return {
|
|
335
|
-
content: [
|
|
336
|
-
{
|
|
337
|
-
type: "text" as const,
|
|
338
|
-
text: result.error
|
|
339
|
-
? `Error: ${result.error}`
|
|
340
|
-
: "Message sent successfully.",
|
|
341
|
-
},
|
|
342
|
-
],
|
|
343
|
-
};
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
case "complete_worker": {
|
|
347
|
-
const result = await apiCall(
|
|
348
|
-
`/api/orchestrate/workers/${args?.workerId}`,
|
|
349
|
-
{
|
|
350
|
-
method: "POST",
|
|
351
|
-
body: JSON.stringify({ action: "complete" }),
|
|
352
|
-
}
|
|
353
|
-
);
|
|
354
|
-
return {
|
|
355
|
-
content: [
|
|
356
|
-
{
|
|
357
|
-
type: "text" as const,
|
|
358
|
-
text: result.error
|
|
359
|
-
? `Error: ${result.error}`
|
|
360
|
-
: "Worker marked as completed.",
|
|
361
|
-
},
|
|
362
|
-
],
|
|
363
|
-
};
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
case "kill_worker": {
|
|
367
|
-
const cleanup = args?.cleanupWorktree ? "?cleanup=true" : "";
|
|
368
|
-
const result = await apiCall(
|
|
369
|
-
`/api/orchestrate/workers/${args?.workerId}${cleanup}`,
|
|
370
|
-
{ method: "DELETE" }
|
|
371
|
-
);
|
|
372
|
-
return {
|
|
373
|
-
content: [
|
|
374
|
-
{
|
|
375
|
-
type: "text" as const,
|
|
376
|
-
text: result.error
|
|
377
|
-
? `Error: ${result.error}`
|
|
378
|
-
: "Worker killed successfully.",
|
|
379
|
-
},
|
|
380
|
-
],
|
|
381
|
-
};
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
case "get_workers_summary": {
|
|
385
|
-
const conductorId = getConductorId(args);
|
|
386
|
-
if (!conductorId) {
|
|
387
|
-
return {
|
|
388
|
-
content: [
|
|
389
|
-
{
|
|
390
|
-
type: "text" as const,
|
|
391
|
-
text: "Error: conductorId is required. Pass it as a parameter or set CONDUCTOR_SESSION_ID env var.",
|
|
392
|
-
},
|
|
393
|
-
],
|
|
394
|
-
};
|
|
395
|
-
}
|
|
396
|
-
const result = await apiCall(
|
|
397
|
-
`/api/orchestrate/workers?conductorId=${conductorId}&summary=true`
|
|
398
|
-
);
|
|
399
|
-
if (result.error) {
|
|
400
|
-
return {
|
|
401
|
-
content: [
|
|
402
|
-
{ type: "text" as const, text: `Error: ${result.error}` },
|
|
403
|
-
],
|
|
404
|
-
};
|
|
405
|
-
}
|
|
406
|
-
const s = result.summary;
|
|
407
|
-
return {
|
|
408
|
-
content: [
|
|
409
|
-
{
|
|
410
|
-
type: "text" as const,
|
|
411
|
-
text: `Workers Summary:\n- Total: ${s.total}\n- Pending: ${s.pending}\n- Running: ${s.running}\n- Waiting: ${s.waiting}\n- Completed: ${s.completed}\n- Failed: ${s.failed}`,
|
|
412
|
-
},
|
|
413
|
-
],
|
|
414
|
-
};
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
default:
|
|
418
|
-
return {
|
|
419
|
-
content: [{ type: "text" as const, text: `Unknown tool: ${name}` }],
|
|
420
|
-
};
|
|
421
|
-
}
|
|
422
|
-
} catch (error) {
|
|
423
|
-
return {
|
|
424
|
-
content: [
|
|
425
|
-
{
|
|
426
|
-
type: "text" as const,
|
|
427
|
-
text: `Error: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
428
|
-
},
|
|
429
|
-
],
|
|
430
|
-
};
|
|
431
|
-
}
|
|
432
|
-
});
|
|
433
|
-
|
|
434
|
-
// Start the server
|
|
435
|
-
async function main() {
|
|
436
|
-
const transport = new StdioServerTransport();
|
|
437
|
-
await server.connect(transport);
|
|
438
|
-
console.error("Agent-OS Orchestration MCP Server started");
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
main().catch(console.error);
|