@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.
Files changed (70) hide show
  1. package/app/api/fork/cancel/route.ts +19 -0
  2. package/app/api/fork/children/route.ts +52 -0
  3. package/app/api/fork/result/route.ts +29 -0
  4. package/app/api/fork/return/route.ts +20 -0
  5. package/app/api/fork/route.ts +32 -0
  6. package/app/api/fork/wait/route.ts +27 -0
  7. package/app/api/load/heavy/lease/route.ts +26 -0
  8. package/app/api/sessions/[id]/archive/route.ts +17 -4
  9. package/app/api/sessions/[id]/route.ts +2 -3
  10. package/app/page.tsx +0 -5
  11. package/components/Chat/ChatPanel.tsx +33 -206
  12. package/components/Chat/Forks.tsx +278 -0
  13. package/components/Chat/ReplyingTo.tsx +24 -0
  14. package/components/Chat/TimelineRows.tsx +194 -0
  15. package/components/Pane/DesktopTabBar.tsx +0 -6
  16. package/components/Pane/MobileTabBar.tsx +0 -6
  17. package/components/Pane/MobileViewPicker.test.ts +1 -8
  18. package/components/Pane/MobileViewPicker.tsx +0 -11
  19. package/components/Pane/PaneViewToggle.test.ts +0 -2
  20. package/components/Pane/PaneViewToggle.tsx +0 -17
  21. package/components/Pane/index.tsx +0 -50
  22. package/components/SessionList/useDoneAction.ts +18 -3
  23. package/content/whats-new.json +16 -0
  24. package/contexts/PaneContext.tsx +1 -1
  25. package/data/done/index.ts +26 -2
  26. package/data/fork/index.ts +83 -0
  27. package/lib/chat/commands.ts +15 -0
  28. package/lib/chat/drivers/copilot-ask.ts +38 -5
  29. package/lib/chat/runner.ts +8 -2
  30. package/lib/chat/worker/extras.ts +7 -1
  31. package/lib/chat/worker/host.ts +2 -0
  32. package/lib/config/client-config-types.ts +7 -0
  33. package/lib/config/client-config.ts +2 -0
  34. package/lib/db/migrations/20261102T0100_forks.ts +87 -0
  35. package/lib/done/archive.ts +21 -1
  36. package/lib/experiments/registry.ts +8 -0
  37. package/lib/fork/api.ts +10 -0
  38. package/lib/fork/brief.ts +63 -0
  39. package/lib/fork/conversation.ts +75 -0
  40. package/lib/fork/deliver.ts +91 -0
  41. package/lib/fork/extras.ts +53 -0
  42. package/lib/fork/guard.ts +323 -0
  43. package/lib/fork/heavy-lease.ts +104 -0
  44. package/lib/fork/held.ts +26 -0
  45. package/lib/fork/ledger.ts +324 -0
  46. package/lib/fork/options.ts +46 -0
  47. package/lib/fork/point.ts +41 -0
  48. package/lib/fork/scratch.ts +67 -0
  49. package/lib/fork/settle.ts +215 -0
  50. package/lib/fork/snapshot.ts +69 -0
  51. package/lib/fork/start.ts +273 -0
  52. package/lib/fork/tick.ts +54 -0
  53. package/lib/fork/types.ts +79 -0
  54. package/lib/fork/view.ts +116 -0
  55. package/lib/fork/waits.ts +83 -0
  56. package/lib/fork/worktree.ts +115 -0
  57. package/lib/orchestrator/shell-guard.ts +6 -3
  58. package/lib/orchestrator/write-guard.ts +1 -1
  59. package/lib/security/demo.ts +1 -2
  60. package/lib/sessions/end.ts +5 -0
  61. package/package.json +1 -1
  62. package/server.ts +2 -0
  63. package/app/api/orchestrate/spawn/route.ts +0 -54
  64. package/app/api/orchestrate/workers/[id]/route.ts +0 -89
  65. package/app/api/orchestrate/workers/route.ts +0 -31
  66. package/app/api/sessions/[id]/mcp-config/route.ts +0 -34
  67. package/components/ConductorPanel.tsx +0 -258
  68. package/lib/mcp-config.ts +0 -80
  69. package/lib/orchestration.ts +0 -463
  70. 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&apos;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
- }