@saadnvd1/agent-os 0.3.821 → 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/content/whats-new.json +16 -0
- 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/drivers/copilot-ask.ts +38 -5
- 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,258 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import { useState, useEffect, useCallback } from "react";
|
|
4
|
-
import { WorkerCard, type WorkerInfo } from "./WorkerCard";
|
|
5
|
-
import { Button } from "./ui/button";
|
|
6
|
-
import {
|
|
7
|
-
RefreshCw,
|
|
8
|
-
Users,
|
|
9
|
-
CheckCircle,
|
|
10
|
-
Loader2,
|
|
11
|
-
AlertCircle,
|
|
12
|
-
XCircle,
|
|
13
|
-
} from "lucide-react";
|
|
14
|
-
import { cn } from "@/lib/utils";
|
|
15
|
-
|
|
16
|
-
interface WorkersSummary {
|
|
17
|
-
total: number;
|
|
18
|
-
pending: number;
|
|
19
|
-
running: number;
|
|
20
|
-
waiting: number;
|
|
21
|
-
completed: number;
|
|
22
|
-
failed: number;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
interface ConductorPanelProps {
|
|
26
|
-
conductorSessionId: string;
|
|
27
|
-
onAttachToWorker?: (workerId: string) => void;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export function ConductorPanel({
|
|
31
|
-
conductorSessionId,
|
|
32
|
-
onAttachToWorker,
|
|
33
|
-
}: ConductorPanelProps) {
|
|
34
|
-
const [workers, setWorkers] = useState<WorkerInfo[]>([]);
|
|
35
|
-
const [summary, setSummary] = useState<WorkersSummary | null>(null);
|
|
36
|
-
const [expandedWorkers, setExpandedWorkers] = useState<Set<string>>(
|
|
37
|
-
new Set()
|
|
38
|
-
);
|
|
39
|
-
const [workerOutputs, setWorkerOutputs] = useState<Record<string, string>>(
|
|
40
|
-
{}
|
|
41
|
-
);
|
|
42
|
-
const [loading, setLoading] = useState(true);
|
|
43
|
-
const [refreshing, setRefreshing] = useState(false);
|
|
44
|
-
|
|
45
|
-
const fetchWorkers = useCallback(async () => {
|
|
46
|
-
try {
|
|
47
|
-
const res = await fetch(
|
|
48
|
-
`/api/orchestrate/workers?conductorId=${conductorSessionId}`
|
|
49
|
-
);
|
|
50
|
-
const data = await res.json();
|
|
51
|
-
if (data.workers) {
|
|
52
|
-
setWorkers(data.workers);
|
|
53
|
-
}
|
|
54
|
-
} catch (error) {
|
|
55
|
-
console.error("Failed to fetch workers:", error);
|
|
56
|
-
}
|
|
57
|
-
}, [conductorSessionId]);
|
|
58
|
-
|
|
59
|
-
const fetchSummary = useCallback(async () => {
|
|
60
|
-
try {
|
|
61
|
-
const res = await fetch(
|
|
62
|
-
`/api/orchestrate/workers?conductorId=${conductorSessionId}&summary=true`
|
|
63
|
-
);
|
|
64
|
-
const data = await res.json();
|
|
65
|
-
if (data.summary) {
|
|
66
|
-
setSummary(data.summary);
|
|
67
|
-
}
|
|
68
|
-
} catch (error) {
|
|
69
|
-
console.error("Failed to fetch summary:", error);
|
|
70
|
-
}
|
|
71
|
-
}, [conductorSessionId]);
|
|
72
|
-
|
|
73
|
-
const fetchWorkerOutput = useCallback(async (workerId: string) => {
|
|
74
|
-
try {
|
|
75
|
-
const res = await fetch(`/api/orchestrate/workers/${workerId}?lines=30`);
|
|
76
|
-
const data = await res.json();
|
|
77
|
-
if (data.output) {
|
|
78
|
-
setWorkerOutputs((prev) => ({ ...prev, [workerId]: data.output }));
|
|
79
|
-
}
|
|
80
|
-
} catch (error) {
|
|
81
|
-
console.error("Failed to fetch worker output:", error);
|
|
82
|
-
}
|
|
83
|
-
}, []);
|
|
84
|
-
|
|
85
|
-
const refresh = useCallback(async () => {
|
|
86
|
-
setRefreshing(true);
|
|
87
|
-
await Promise.all([fetchWorkers(), fetchSummary()]);
|
|
88
|
-
setRefreshing(false);
|
|
89
|
-
}, [fetchWorkers, fetchSummary]);
|
|
90
|
-
|
|
91
|
-
// Initial load
|
|
92
|
-
useEffect(() => {
|
|
93
|
-
const load = async () => {
|
|
94
|
-
setLoading(true);
|
|
95
|
-
await Promise.all([fetchWorkers(), fetchSummary()]);
|
|
96
|
-
setLoading(false);
|
|
97
|
-
};
|
|
98
|
-
load();
|
|
99
|
-
}, [fetchWorkers, fetchSummary]);
|
|
100
|
-
|
|
101
|
-
// Poll for updates every 5 seconds
|
|
102
|
-
useEffect(() => {
|
|
103
|
-
const interval = setInterval(refresh, 5000);
|
|
104
|
-
return () => clearInterval(interval);
|
|
105
|
-
}, [refresh]);
|
|
106
|
-
|
|
107
|
-
// Fetch output for expanded workers
|
|
108
|
-
useEffect(() => {
|
|
109
|
-
expandedWorkers.forEach((workerId) => {
|
|
110
|
-
if (!workerOutputs[workerId]) {
|
|
111
|
-
fetchWorkerOutput(workerId);
|
|
112
|
-
}
|
|
113
|
-
});
|
|
114
|
-
}, [expandedWorkers, workerOutputs, fetchWorkerOutput]);
|
|
115
|
-
|
|
116
|
-
const toggleExpand = (workerId: string) => {
|
|
117
|
-
setExpandedWorkers((prev) => {
|
|
118
|
-
const next = new Set(prev);
|
|
119
|
-
if (next.has(workerId)) {
|
|
120
|
-
next.delete(workerId);
|
|
121
|
-
} else {
|
|
122
|
-
next.add(workerId);
|
|
123
|
-
// Fetch output when expanding
|
|
124
|
-
fetchWorkerOutput(workerId);
|
|
125
|
-
}
|
|
126
|
-
return next;
|
|
127
|
-
});
|
|
128
|
-
};
|
|
129
|
-
|
|
130
|
-
const handleSendMessage = async (workerId: string, message: string) => {
|
|
131
|
-
try {
|
|
132
|
-
await fetch(`/api/orchestrate/workers/${workerId}`, {
|
|
133
|
-
method: "POST",
|
|
134
|
-
headers: { "Content-Type": "application/json" },
|
|
135
|
-
body: JSON.stringify({ action: "send", message }),
|
|
136
|
-
});
|
|
137
|
-
// Refresh output after sending
|
|
138
|
-
setTimeout(() => fetchWorkerOutput(workerId), 1000);
|
|
139
|
-
} catch (error) {
|
|
140
|
-
console.error("Failed to send message:", error);
|
|
141
|
-
}
|
|
142
|
-
};
|
|
143
|
-
|
|
144
|
-
const handleKillWorker = async (workerId: string) => {
|
|
145
|
-
if (!confirm("Kill this worker?")) return;
|
|
146
|
-
try {
|
|
147
|
-
await fetch(`/api/orchestrate/workers/${workerId}`, {
|
|
148
|
-
method: "DELETE",
|
|
149
|
-
});
|
|
150
|
-
await refresh();
|
|
151
|
-
} catch (error) {
|
|
152
|
-
console.error("Failed to kill worker:", error);
|
|
153
|
-
}
|
|
154
|
-
};
|
|
155
|
-
|
|
156
|
-
if (loading) {
|
|
157
|
-
return (
|
|
158
|
-
<div className="flex h-full items-center justify-center">
|
|
159
|
-
<Loader2 className="text-muted-foreground h-6 w-6 animate-spin" />
|
|
160
|
-
</div>
|
|
161
|
-
);
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
if (workers.length === 0) {
|
|
165
|
-
return (
|
|
166
|
-
<div className="text-muted-foreground flex h-full flex-col items-center justify-center">
|
|
167
|
-
<Users className="mb-4 h-12 w-12 opacity-50" />
|
|
168
|
-
<p className="text-lg font-medium">No workers yet</p>
|
|
169
|
-
<p className="text-sm">
|
|
170
|
-
This conductor hasn't spawned any workers.
|
|
171
|
-
</p>
|
|
172
|
-
<p className="mt-4 max-w-md text-center text-xs">
|
|
173
|
-
Use the MCP tools or API to spawn workers. The conductor can delegate
|
|
174
|
-
tasks to parallel worker sessions.
|
|
175
|
-
</p>
|
|
176
|
-
</div>
|
|
177
|
-
);
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
return (
|
|
181
|
-
<div className="flex h-full flex-col">
|
|
182
|
-
{/* Header with summary */}
|
|
183
|
-
<div className="flex items-center justify-between border-b p-4">
|
|
184
|
-
<div className="flex items-center gap-4">
|
|
185
|
-
<div className="flex items-center gap-2">
|
|
186
|
-
<Users className="text-primary h-5 w-5" />
|
|
187
|
-
<span className="font-semibold">Workers</span>
|
|
188
|
-
<span className="text-muted-foreground">
|
|
189
|
-
({summary?.total || workers.length})
|
|
190
|
-
</span>
|
|
191
|
-
</div>
|
|
192
|
-
|
|
193
|
-
{summary && (
|
|
194
|
-
<div className="flex items-center gap-3 text-sm">
|
|
195
|
-
{summary.running > 0 && (
|
|
196
|
-
<div className="flex items-center gap-1 text-green-500">
|
|
197
|
-
<Loader2 className="h-3 w-3 animate-spin" />
|
|
198
|
-
<span>{summary.running} running</span>
|
|
199
|
-
</div>
|
|
200
|
-
)}
|
|
201
|
-
{summary.waiting > 0 && (
|
|
202
|
-
<div className="flex items-center gap-1 text-yellow-500">
|
|
203
|
-
<AlertCircle className="h-3 w-3" />
|
|
204
|
-
<span>{summary.waiting} waiting</span>
|
|
205
|
-
</div>
|
|
206
|
-
)}
|
|
207
|
-
{summary.completed > 0 && (
|
|
208
|
-
<div className="flex items-center gap-1 text-green-500">
|
|
209
|
-
<CheckCircle className="h-3 w-3" />
|
|
210
|
-
<span>{summary.completed} done</span>
|
|
211
|
-
</div>
|
|
212
|
-
)}
|
|
213
|
-
{summary.failed > 0 && (
|
|
214
|
-
<div className="flex items-center gap-1 text-red-500">
|
|
215
|
-
<XCircle className="h-3 w-3" />
|
|
216
|
-
<span>{summary.failed} failed</span>
|
|
217
|
-
</div>
|
|
218
|
-
)}
|
|
219
|
-
</div>
|
|
220
|
-
)}
|
|
221
|
-
</div>
|
|
222
|
-
|
|
223
|
-
<Button
|
|
224
|
-
variant="ghost"
|
|
225
|
-
size="icon-sm"
|
|
226
|
-
onClick={refresh}
|
|
227
|
-
disabled={refreshing}
|
|
228
|
-
>
|
|
229
|
-
<RefreshCw className={cn("h-4 w-4", refreshing && "animate-spin")} />
|
|
230
|
-
</Button>
|
|
231
|
-
</div>
|
|
232
|
-
|
|
233
|
-
{/* Workers grid */}
|
|
234
|
-
<div className="flex-1 overflow-auto p-4">
|
|
235
|
-
<div className="grid grid-cols-1 gap-3 lg:grid-cols-2">
|
|
236
|
-
{workers.map((worker) => (
|
|
237
|
-
<WorkerCard
|
|
238
|
-
key={worker.id}
|
|
239
|
-
worker={worker}
|
|
240
|
-
isExpanded={expandedWorkers.has(worker.id)}
|
|
241
|
-
output={workerOutputs[worker.id]}
|
|
242
|
-
onToggleExpand={() => toggleExpand(worker.id)}
|
|
243
|
-
onViewOutput={() => {
|
|
244
|
-
toggleExpand(worker.id);
|
|
245
|
-
fetchWorkerOutput(worker.id);
|
|
246
|
-
}}
|
|
247
|
-
onSendMessage={(msg) => handleSendMessage(worker.id, msg)}
|
|
248
|
-
onKill={() => handleKillWorker(worker.id)}
|
|
249
|
-
onAttach={
|
|
250
|
-
onAttachToWorker ? () => onAttachToWorker(worker.id) : undefined
|
|
251
|
-
}
|
|
252
|
-
/>
|
|
253
|
-
))}
|
|
254
|
-
</div>
|
|
255
|
-
</div>
|
|
256
|
-
</div>
|
|
257
|
-
);
|
|
258
|
-
}
|
package/lib/mcp-config.ts
DELETED
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* MCP Config Auto-Generation
|
|
3
|
-
*
|
|
4
|
-
* Writes a .mcp.json file to the session's working directory so Claude
|
|
5
|
-
* automatically picks up the orchestration tools with the session ID baked in.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { writeFileSync, existsSync, readFileSync } from "fs";
|
|
9
|
-
import path from "path";
|
|
10
|
-
import { selfUrl } from "./agents/self-env";
|
|
11
|
-
|
|
12
|
-
interface McpConfig {
|
|
13
|
-
mcpServers: Record<
|
|
14
|
-
string,
|
|
15
|
-
{
|
|
16
|
-
command: string;
|
|
17
|
-
args: string[];
|
|
18
|
-
env?: Record<string, string>;
|
|
19
|
-
}
|
|
20
|
-
>;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Write or update .mcp.json in the working directory with orchestration server config
|
|
25
|
-
*/
|
|
26
|
-
export function ensureMcpConfig(
|
|
27
|
-
workingDirectory: string,
|
|
28
|
-
sessionId: string
|
|
29
|
-
): void {
|
|
30
|
-
const configPath = path.join(workingDirectory, ".mcp.json");
|
|
31
|
-
const orchestrationServerPath = path.join(
|
|
32
|
-
process.cwd(),
|
|
33
|
-
"mcp",
|
|
34
|
-
"orchestration-server.ts"
|
|
35
|
-
);
|
|
36
|
-
|
|
37
|
-
let config: McpConfig = { mcpServers: {} };
|
|
38
|
-
|
|
39
|
-
// Read existing config if present
|
|
40
|
-
if (existsSync(configPath)) {
|
|
41
|
-
try {
|
|
42
|
-
const existing = readFileSync(configPath, "utf-8");
|
|
43
|
-
config = JSON.parse(existing);
|
|
44
|
-
if (!config.mcpServers) {
|
|
45
|
-
config.mcpServers = {};
|
|
46
|
-
}
|
|
47
|
-
} catch {
|
|
48
|
-
// Invalid JSON, start fresh
|
|
49
|
-
config = { mcpServers: {} };
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
// Add/update agent-os orchestration server
|
|
54
|
-
config.mcpServers["agent-os"] = {
|
|
55
|
-
command: "npx",
|
|
56
|
-
args: ["tsx", orchestrationServerPath],
|
|
57
|
-
env: {
|
|
58
|
-
AGENTOS_URL: selfUrl(),
|
|
59
|
-
CONDUCTOR_SESSION_ID: sessionId,
|
|
60
|
-
},
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
// Write config
|
|
64
|
-
writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* Check if .mcp.json exists and has agent-os configured
|
|
69
|
-
*/
|
|
70
|
-
export function hasMcpConfig(workingDirectory: string): boolean {
|
|
71
|
-
const configPath = path.join(workingDirectory, ".mcp.json");
|
|
72
|
-
if (!existsSync(configPath)) return false;
|
|
73
|
-
|
|
74
|
-
try {
|
|
75
|
-
const config = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
76
|
-
return !!config.mcpServers?.["agent-os"];
|
|
77
|
-
} catch {
|
|
78
|
-
return false;
|
|
79
|
-
}
|
|
80
|
-
}
|