@cydm/magic-shell-agent-node 0.1.1 → 0.1.3

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.
@@ -0,0 +1,180 @@
1
+ (function (global) {
2
+ function hasUsablePrimaryAgent(primaryAgent) {
3
+ return !!primaryAgent && primaryAgent.status !== "disabled" && primaryAgent.status !== "missing";
4
+ }
5
+
6
+ function applyPrimaryReply(payload, ctx) {
7
+ ctx.pendingNodeReply = false;
8
+ ctx.pushNodeTranscript("assistant", payload?.text || "Primary agent did not return a message.", {
9
+ actionLabel: payload?.actionLabel,
10
+ actionType: payload?.actionType,
11
+ actionTaskSummary: payload?.actionTaskSummary,
12
+ actionSessionId: payload?.actionSessionId,
13
+ });
14
+ }
15
+
16
+ function applyRuntimeSnapshot(snapshot, ctx) {
17
+ const previousWorkers = ctx.workers || [];
18
+ ctx.workers = snapshot.workers || [];
19
+ ctx.runtimeSummary = snapshot.runtime || null;
20
+ ctx.runtimeFocus = snapshot.focus || [];
21
+ ctx.primaryAgent = snapshot.primary || null;
22
+ ctx.syncNodeTranscriptFromServer(snapshot.nodeHistory);
23
+
24
+ const liveSessionIds = new Set(ctx.workers.map((worker) => worker.sessionId));
25
+ const currentWorker = ctx.sessionId
26
+ ? ctx.workers.find((worker) => worker.sessionId === ctx.sessionId)
27
+ : null;
28
+
29
+ for (const previousWorker of previousWorkers) {
30
+ if (!liveSessionIds.has(previousWorker.sessionId)) {
31
+ ctx.destroyTerminalView(previousWorker.sessionId);
32
+ }
33
+ }
34
+
35
+ if (ctx.pendingSpawnSessionId && ctx.workers.some((worker) => worker.sessionId === ctx.pendingSpawnSessionId)) {
36
+ if (ctx.pendingSpawnAutoAttach) {
37
+ // Once the worker is visible in runtime state, perform a real attach so
38
+ // the browser connection is routed to the new session via the relay.
39
+ ctx.attachWorker(ctx.pendingSpawnSessionId, true);
40
+ } else {
41
+ ctx.pushNodeTranscript(
42
+ "assistant",
43
+ "A worker is ready in the sidebar.\nSession · S:" + ctx.pendingSpawnSessionId.slice(-6),
44
+ {
45
+ actionSessionId: ctx.pendingSpawnSessionId,
46
+ actionLabel: "ATTACH",
47
+ actionType: "attach",
48
+ },
49
+ );
50
+ }
51
+ ctx.pendingSpawnSessionId = null;
52
+ ctx.pendingSpawnAutoAttach = true;
53
+ ctx.resetSpawnButton();
54
+ }
55
+
56
+ if (ctx.pendingStopSessionId) {
57
+ const pendingStopWorker = ctx.workers.find((worker) => worker.sessionId === ctx.pendingStopSessionId);
58
+ if (!pendingStopWorker || pendingStopWorker.status === "stopped" || pendingStopWorker.status === "failed") {
59
+ ctx.pendingStopSessionId = null;
60
+ }
61
+ }
62
+
63
+ if (ctx.pendingRestartSessionId) {
64
+ const pendingRestartWorker = ctx.workers.find((worker) => worker.sessionId === ctx.pendingRestartSessionId);
65
+ if (pendingRestartWorker && pendingRestartWorker.status === "running") {
66
+ ctx.pendingRestartSessionId = null;
67
+ }
68
+ }
69
+
70
+ if (ctx.sessionId && !ctx.workers.some((worker) => worker.sessionId === ctx.sessionId)) {
71
+ const closedSessionId = ctx.sessionId;
72
+ ctx.writeSystemNotice("WORKER CLOSED: " + closedSessionId.slice(-8));
73
+ ctx.clearPreferredSession();
74
+ ctx.returnToPrimaryChat({ clearSession: true });
75
+ }
76
+
77
+ if (
78
+ currentWorker
79
+ && ctx.pendingRestartSessionId !== currentWorker.sessionId
80
+ && (currentWorker.status === "stopped" || currentWorker.status === "failed")
81
+ ) {
82
+ ctx.writeSystemNotice("WORKER INACTIVE: " + currentWorker.sessionId.slice(-8));
83
+ if (ctx.preferredSessionId === currentWorker.sessionId) {
84
+ ctx.clearPreferredSession();
85
+ }
86
+ ctx.returnToPrimaryChat({ clearSession: true });
87
+ }
88
+
89
+ ctx.renderWorkers();
90
+
91
+ if (ctx.sessionId && ctx.workerDetailOpen && ctx.workerDetail && ctx.workerDetail.worker.sessionId === ctx.sessionId) {
92
+ ctx.refreshCurrentWorkerDetail();
93
+ }
94
+
95
+ if (!ctx.sessionId && ctx.workers.length) {
96
+ const preferredWorker = ctx.preferredSessionId
97
+ ? ctx.workers.find((worker) => worker.sessionId === ctx.preferredSessionId)
98
+ : null;
99
+ const firstAttachable = ctx.workers.find((worker) => worker.status !== "stopped" && worker.status !== "failed");
100
+ const targetWorker = preferredWorker || firstAttachable || ctx.workers[0];
101
+ if (!ctx.isNodeViewSelected() && targetWorker.status !== "stopped" && targetWorker.status !== "failed") {
102
+ ctx.attachWorker(targetWorker.sessionId, true);
103
+ }
104
+ }
105
+ }
106
+
107
+ function handleControlMessage(msg, ctx) {
108
+ if (msg.controlKind === "result" && msg.controlName === "get_primary_agent" && msg.payload && msg.payload.primary) {
109
+ ctx.primaryAgent = msg.payload.primary || null;
110
+ ctx.renderNodeHome();
111
+ return true;
112
+ }
113
+
114
+ if (msg.controlKind === "result" && msg.controlName === "send_primary_message") {
115
+ if (msg.ok === false) {
116
+ ctx.pendingNodeReply = false;
117
+ ctx.pushNodeTranscript(
118
+ "assistant",
119
+ msg.error || msg.payload?.error || "Primary agent request failed before it produced a reply.",
120
+ );
121
+ return true;
122
+ }
123
+ if (msg.payload) {
124
+ applyPrimaryReply(msg.payload, ctx);
125
+ return true;
126
+ }
127
+ ctx.pendingNodeReply = false;
128
+ ctx.pushNodeTranscript("assistant", "Primary agent did not return a usable reply.");
129
+ return true;
130
+ }
131
+
132
+ if (msg.controlKind === "result" && (msg.controlName === "get_runtime_summary" || msg.controlName === "list_workers") && msg.payload) {
133
+ applyRuntimeSnapshot(msg.payload, ctx);
134
+ return true;
135
+ }
136
+
137
+ if (msg.controlKind === "result" && msg.controlName === "get_worker_detail" && msg.payload && msg.payload.worker) {
138
+ ctx.workerDetail = msg.payload.worker || null;
139
+ ctx.renderWorkerDetailPanel();
140
+ return true;
141
+ }
142
+
143
+ if (msg.controlKind === "result" && msg.controlName === "browse_directories" && msg.payload) {
144
+ ctx.dirBrowserPath = msg.payload.path || null;
145
+ ctx.dirBrowserParentPath = msg.payload.parentPath || null;
146
+ ctx.dirBrowserRepoRoot = msg.payload.repoRoot || null;
147
+ ctx.dirBrowserEntries = msg.payload.entries || [];
148
+ ctx.renderDirBrowser();
149
+ return true;
150
+ }
151
+
152
+ if (msg.controlKind === "event" && msg.controlName === "runtime.snapshot" && msg.payload) {
153
+ applyRuntimeSnapshot(msg.payload, ctx);
154
+ return true;
155
+ }
156
+
157
+ if (msg.controlKind === "event" && msg.controlName === "worker.detail" && msg.payload && msg.payload.worker) {
158
+ ctx.workerDetail = msg.payload.worker || null;
159
+ ctx.renderWorkerDetailPanel();
160
+ return true;
161
+ }
162
+
163
+ if (msg.controlKind === "event" && msg.controlName === "directory.list" && msg.payload) {
164
+ ctx.dirBrowserPath = msg.payload.path || null;
165
+ ctx.dirBrowserParentPath = msg.payload.parentPath || null;
166
+ ctx.dirBrowserRepoRoot = msg.payload.repoRoot || null;
167
+ ctx.dirBrowserEntries = msg.payload.entries || [];
168
+ ctx.renderDirBrowser();
169
+ return true;
170
+ }
171
+
172
+ return false;
173
+ }
174
+
175
+ global.MagicShellRuntimeControl = {
176
+ hasUsablePrimaryAgent,
177
+ applyRuntimeSnapshot,
178
+ handleControlMessage,
179
+ };
180
+ })(window);
@@ -0,0 +1,80 @@
1
+ (function (global) {
2
+ function handleWorkbenchMessage(msg, ctx) {
3
+ switch (msg.type) {
4
+ case "auth_success":
5
+ ctx.writeTerminalLine("__system__", "\r\n[ SYSTEM ONLINE ]");
6
+ ctx.sessionId = null;
7
+ ctx.setSessionDisplay("----");
8
+ ctx.syncWorkerPluginPreference?.();
9
+ ctx.requestWorkerList();
10
+ return true;
11
+
12
+ case "output":
13
+ if (msg.sessionId) {
14
+ ctx.writeTerminal(msg.sessionId, msg.data);
15
+ } else {
16
+ ctx.writeTerminal("__system__", msg.data);
17
+ }
18
+ return true;
19
+
20
+ case "error":
21
+ ctx.writeTerminalLine(ctx.activeTerminalId || "__system__", `\r\n[ ERROR: ${msg.error} ]\r\n`);
22
+ return true;
23
+
24
+ case "session":
25
+ ctx.requestWorkerList();
26
+ return true;
27
+
28
+ case "dir_list":
29
+ ctx.dirBrowserPath = msg.path || null;
30
+ ctx.dirBrowserParentPath = msg.parentPath || null;
31
+ ctx.dirBrowserRepoRoot = msg.repoRoot || null;
32
+ ctx.dirBrowserEntries = msg.entries || [];
33
+ ctx.renderDirBrowser();
34
+ return true;
35
+
36
+ case "worker_list":
37
+ global.MagicShellRuntimeControl.applyRuntimeSnapshot({
38
+ workers: msg.workers || [],
39
+ runtime: msg.runtime || null,
40
+ focus: msg.focus || [],
41
+ primary: msg.primary || null,
42
+ nodeHistory: msg.nodeHistory || [],
43
+ }, ctx.getRuntimeControlContext());
44
+ return true;
45
+
46
+ case "worker_detail":
47
+ ctx.workerDetail = msg.worker || null;
48
+ ctx.renderWorkerDetailPanel();
49
+ return true;
50
+
51
+ case "node_reply":
52
+ ctx.pendingNodeReply = false;
53
+ ctx.pushNodeTranscript("assistant", msg.text || "Primary agent did not return a message.", {
54
+ actionLabel: msg.actionLabel,
55
+ actionType: msg.actionType,
56
+ actionTaskSummary: msg.actionTaskSummary,
57
+ actionSessionId: msg.actionSessionId,
58
+ });
59
+ return true;
60
+
61
+ case "attached":
62
+ if (msg.sessionId) {
63
+ ctx.sessionId = msg.sessionId;
64
+ ctx.setSessionDisplay(msg.sessionId.slice(-8));
65
+ ctx.renderWorkers();
66
+ }
67
+ return true;
68
+
69
+ case "control":
70
+ return global.MagicShellRuntimeControl.handleControlMessage(msg, ctx.getRuntimeControlContext());
71
+
72
+ default:
73
+ return false;
74
+ }
75
+ }
76
+
77
+ global.MagicShellRuntimeMessages = {
78
+ handleWorkbenchMessage,
79
+ };
80
+ })(window);
@@ -0,0 +1,102 @@
1
+ (function (global) {
2
+ function renderNodeDetailPanel(ctx) {
3
+ const panel = ctx.getPanelNode();
4
+ const toggle = ctx.getToggleNode();
5
+ panel.classList.toggle("active", ctx.nodeDetailOpen && ctx.isNodeViewSelected());
6
+ toggle.textContent = ctx.nodeDetailOpen ? "HIDE" : "OVERVIEW";
7
+ }
8
+
9
+ function getPrimaryTitle(primaryAgent) {
10
+ return "Agent";
11
+ }
12
+
13
+ function renderNodeHome(ctx) {
14
+ const thread = ctx.getThreadNode();
15
+ const coordination = ctx.getCoordinationNode();
16
+ const coordinationText = ctx.getCoordinationTextNode();
17
+ const input = ctx.getInputNode();
18
+ const workers = ctx.workers;
19
+ const summary = ctx.runtimeSummary || {
20
+ liveWorkers: workers.filter((worker) => worker.status !== "stopped" && worker.status !== "failed").length,
21
+ waitingWorkers: workers.filter((worker) => worker.activityState === "ready" && worker.status !== "stopped" && worker.status !== "failed").length,
22
+ busyWorkers: workers.filter((worker) => worker.activityState === "busy" && worker.status !== "stopped" && worker.status !== "failed").length,
23
+ };
24
+ const primaryTitle = getPrimaryTitle(ctx.primaryAgent);
25
+ const primaryLabel = ctx.primaryAgent
26
+ ? `${ctx.primaryAgent.pluginName} · ${ctx.primaryAgent.status}`
27
+ : "no primary";
28
+ ctx.setNodeNavTitle(primaryTitle);
29
+ ctx.setNodeHomeTitle(primaryTitle);
30
+ ctx.setNodeNavMeta(`${primaryLabel} · ${summary.liveWorkers || 0} live · ${summary.waitingWorkers || 0} waiting · ${summary.busyWorkers || 0} busy`);
31
+ ctx.setNodeNavBadge(summary.busyWorkers ? `${summary.busyWorkers} busy` : "idle");
32
+ ctx.setNodeHomeBadge(summary.liveWorkers ? `${summary.liveWorkers} live` : "idle");
33
+ const primaryReady = global.MagicShellRuntimeControl.hasUsablePrimaryAgent(ctx.primaryAgent);
34
+ input.disabled = ctx.pendingNodeReply || !ctx.isConnected() || !primaryReady;
35
+ input.placeholder = ctx.pendingNodeReply
36
+ ? "Primary agent is thinking..."
37
+ : primaryReady
38
+ ? "Ask the primary agent to coordinate work."
39
+ : "Primary agent unavailable. Use the worker controls directly.";
40
+
41
+ if (!primaryReady) {
42
+ coordination.className = "node-coordination-state";
43
+ coordinationText.textContent = "Primary agent unavailable. Use the runtime controls directly.";
44
+ } else if (ctx.pendingNodeReply) {
45
+ coordination.className = `node-coordination-state active ${summary.busyWorkers > 0 ? "waiting" : ""}`.trim();
46
+ coordinationText.textContent = summary.busyWorkers > 0
47
+ ? "Primary is delegating work and waiting for a worker result."
48
+ : "Primary is reviewing the request.";
49
+ } else if (summary.busyWorkers > 0) {
50
+ coordination.className = "node-coordination-state active waiting";
51
+ coordinationText.textContent = `Runtime has ${summary.busyWorkers} busy worker${summary.busyWorkers > 1 ? "s" : ""}. Open the worker view to inspect live execution.`;
52
+ } else if (summary.waitingWorkers > 0) {
53
+ coordination.className = "node-coordination-state active";
54
+ coordinationText.textContent = `Runtime has ${summary.waitingWorkers} worker${summary.waitingWorkers > 1 ? "s" : ""} ready for follow-up.`;
55
+ } else {
56
+ coordination.className = "node-coordination-state";
57
+ coordinationText.textContent = "Primary coordination idle.";
58
+ }
59
+
60
+ const merged = [...ctx.nodeTranscript].slice(-18);
61
+ if (!primaryReady) {
62
+ merged.push({
63
+ id: "node-primary-unavailable",
64
+ role: "assistant",
65
+ text: "Primary agent unavailable. Magic Shell is still usable as a runtime workbench: spawn workers, inspect runtime state, browse directories, and attach directly.",
66
+ timestamp: Date.now(),
67
+ });
68
+ }
69
+
70
+ if (!merged.length) {
71
+ thread.innerHTML = '<div class="node-chat-empty">Primary agent is ready.</div>';
72
+ return;
73
+ }
74
+
75
+ const previousScrollTop = thread.scrollTop;
76
+ const distanceFromBottom = thread.scrollHeight - thread.scrollTop - thread.clientHeight;
77
+ const shouldStickToBottom = distanceFromBottom < 24;
78
+
79
+ thread.innerHTML = merged.map((entry) => `
80
+ <div class="node-turn ${entry.role} ${entry.pending ? "pending" : ""}">
81
+ <div>${ctx.escapeHtml(entry.text)}</div>
82
+ ${(entry.actionSessionId || entry.actionType === "spawn") ? `
83
+ <div class="node-turn-action-row">
84
+ <button class="node-turn-action" onclick="runNodeAction('${entry.id}')">${ctx.escapeHtml(entry.actionLabel || "ATTACH")}</button>
85
+ </div>
86
+ ` : ""}
87
+ ${entry.pending ? '<div class="node-turn-status">Working...</div>' : ""}
88
+ <div class="node-turn-meta">${ctx.escapeHtml(ctx.formatRelativeTime(entry.timestamp))}</div>
89
+ </div>
90
+ `).join("");
91
+ if (shouldStickToBottom) {
92
+ thread.scrollTop = thread.scrollHeight;
93
+ } else {
94
+ thread.scrollTop = previousScrollTop;
95
+ }
96
+ }
97
+
98
+ global.MagicShellRuntimeNodeUI = {
99
+ renderNodeDetailPanel,
100
+ renderNodeHome,
101
+ };
102
+ })(window);
@@ -0,0 +1,180 @@
1
+ (function (global) {
2
+ function renderRuntimeSummary(ctx) {
3
+ const node = ctx.getRuntimeSummaryNode();
4
+ const workers = ctx.workers;
5
+ const summary = ctx.runtimeSummary || {
6
+ liveWorkers: workers.filter((worker) => worker.status !== "stopped" && worker.status !== "failed").length,
7
+ closedWorkers: workers.filter((worker) => worker.status === "stopped" || worker.status === "failed").length,
8
+ busyWorkers: workers.filter((worker) => worker.activityState === "busy" && worker.status !== "stopped" && worker.status !== "failed").length,
9
+ waitingWorkers: workers.filter((worker) => worker.activityState === "ready" && worker.status !== "stopped" && worker.status !== "failed").length,
10
+ attentionWorkers: workers.filter((worker) => worker.phase === "attention" || ctx.workerNeedsAttention(worker)).length,
11
+ quietWorkers: workers.filter((worker) => worker.phase === "quiet").length,
12
+ failedWorkers: workers.filter((worker) => worker.status === "failed").length,
13
+ watchWorkers: workers.filter((worker) => worker.interventionLevel === "watch").length,
14
+ suggestedWorkers: workers.filter((worker) => worker.interventionLevel === "suggested").length,
15
+ requiredWorkers: workers.filter((worker) => worker.interventionLevel === "required").length,
16
+ observeWorkers: workers.filter((worker) => worker.recommendedAction === "observe").length,
17
+ attachWorkers: workers.filter((worker) => worker.recommendedAction === "attach").length,
18
+ restartWorkers: workers.filter((worker) => worker.recommendedAction === "restart").length,
19
+ closeWorkers: workers.filter((worker) => worker.recommendedAction === "close").length,
20
+ totalRestarts: workers.reduce((sum, worker) => sum + (worker.restartCount || 0), 0),
21
+ totalAttaches: workers.reduce((sum, worker) => sum + (worker.attachCount || 0), 0),
22
+ totalOutputChars: workers.reduce((sum, worker) => sum + (worker.outputCharCount || 0), 0),
23
+ };
24
+ node.innerHTML = [
25
+ ["LIVE", summary.liveWorkers],
26
+ ["BUSY", summary.busyWorkers],
27
+ ["WAITING", summary.waitingWorkers],
28
+ ["QUIET", summary.quietWorkers],
29
+ ["ATTN", summary.attentionWorkers],
30
+ ["FAILED", summary.failedWorkers],
31
+ ["WATCH", summary.watchWorkers],
32
+ ["SUGGEST", summary.suggestedWorkers],
33
+ ["REQUIRED", summary.requiredWorkers],
34
+ ["OBSERVE", summary.observeWorkers],
35
+ ["ATTACH", summary.attachWorkers],
36
+ ["RESTART", summary.restartWorkers],
37
+ ["CLOSE", summary.closeWorkers],
38
+ ["ATTACHES", summary.totalAttaches],
39
+ ["RESTARTS", summary.totalRestarts],
40
+ ["OUTPUT", ctx.formatBytes(summary.totalOutputChars)],
41
+ ["CLOSED", summary.closedWorkers],
42
+ ].map(([label, value]) => `
43
+ <div class="runtime-summary-stat">
44
+ <div class="runtime-summary-label">${ctx.escapeHtml(label)}</div>
45
+ <div class="runtime-summary-value">${ctx.escapeHtml(value)}</div>
46
+ </div>
47
+ `).join("");
48
+ }
49
+
50
+ function renderRuntimeFocus(ctx) {
51
+ const node = ctx.getRuntimeFocusNode();
52
+ const focusItems = ctx.runtimeFocus || [];
53
+ if (!focusItems.length) {
54
+ node.className = "runtime-focus-empty";
55
+ node.textContent = "No immediate actions.";
56
+ return;
57
+ }
58
+ node.className = "runtime-focus-list";
59
+ node.innerHTML = focusItems.map((item) => `
60
+ <div class="runtime-focus-item" onclick="attachWorker('${item.sessionId}')">
61
+ <div class="runtime-focus-head">
62
+ <span>${ctx.escapeHtml(ctx.formatFocusAction(item))} · ${ctx.escapeHtml(item.agentType)} · ${ctx.escapeHtml(item.title)}</span>
63
+ <span>${ctx.escapeHtml(ctx.formatRelativeTime(item.updatedAt))}</span>
64
+ </div>
65
+ <div class="runtime-focus-body">${ctx.escapeHtml(item.reason || `${item.phase || "worker"} needs attention`)}</div>
66
+ </div>
67
+ `).join("");
68
+ }
69
+
70
+ function renderRuntimeFeed(ctx) {
71
+ const node = ctx.getRuntimeFeedNode();
72
+ const recent = [...ctx.workers]
73
+ .filter((worker) => worker.lastEventAt && worker.lastEventSummary && !ctx.isControlNoise(worker.lastEventSummary))
74
+ .sort((a, b) => (b.lastEventAt || 0) - (a.lastEventAt || 0))
75
+ .slice(0, 6);
76
+ if (!recent.length) {
77
+ node.className = "runtime-feed-empty";
78
+ node.innerHTML = "No worker activity yet.";
79
+ return;
80
+ }
81
+ node.className = "runtime-feed-list";
82
+ node.innerHTML = recent.map((worker) => {
83
+ const label = worker.displayName || worker.taskSummary || `S:${worker.sessionId.slice(-6)}`;
84
+ const eventLevel = worker.lastEventLevel ? `<span class="runtime-level ${worker.lastEventLevel}">${ctx.escapeHtml(worker.lastEventLevel)}</span>` : "";
85
+ return `
86
+ <div class="runtime-feed-item">
87
+ <div class="runtime-feed-head">
88
+ <span>${ctx.escapeHtml(worker.agentType)} · ${ctx.escapeHtml(label)} ${eventLevel}</span>
89
+ <span>${ctx.escapeHtml(ctx.formatRelativeTime(worker.lastEventAt))}</span>
90
+ </div>
91
+ <div class="runtime-feed-body">${ctx.escapeHtml(worker.lastEventSummary || "")}</div>
92
+ </div>
93
+ `;
94
+ }).join("");
95
+ }
96
+
97
+ function renderWorkers(ctx) {
98
+ const list = ctx.getWorkerListNode();
99
+ const nodeCard = ctx.getNodeNavCard();
100
+ if (ctx.selectedViewId !== "node" && !ctx.workers.some((worker) => worker.sessionId === ctx.selectedViewId)) {
101
+ ctx.selectNodeHome();
102
+ return;
103
+ }
104
+ renderRuntimeSummary(ctx);
105
+ renderRuntimeFocus(ctx);
106
+ renderRuntimeFeed(ctx);
107
+ ctx.renderNodeHome();
108
+ nodeCard.classList.toggle("active", ctx.isNodeViewSelected());
109
+ if (!ctx.workers.length) {
110
+ list.innerHTML = '<div class="worker-empty">No workers yet.</div>';
111
+ ctx.updateWorkspaceView();
112
+ return;
113
+ }
114
+
115
+ const orderedWorkers = [...ctx.workers].sort((a, b) => (b.createdAt || 0) - (a.createdAt || 0));
116
+ const liveWorkers = orderedWorkers.filter((worker) => worker.status !== "stopped" && worker.status !== "failed");
117
+ const closedWorkers = orderedWorkers.filter((worker) => worker.status === "stopped" || worker.status === "failed");
118
+
119
+ const renderWorkerCard = (worker) => {
120
+ const active = ctx.selectedViewId === worker.sessionId ? "active" : "";
121
+ const summary = worker.taskSummary || "";
122
+ const displayName = worker.displayName || `S:${worker.sessionId.slice(-6)}`;
123
+ const cwd = worker.cwd || ".";
124
+ const recent = ctx.formatLastOutput(worker.lastOutputAt);
125
+ const recentEvent = ctx.getWorkerPreviewLine(worker);
126
+ const recentEventAt = worker.lastEventAt ? ` · ${ctx.formatRelativeTime(worker.lastEventAt)}` : "";
127
+ const clickable = worker.status !== "stopped" && worker.status !== "failed";
128
+ const inactive = clickable ? "" : "inactive";
129
+ const badge = ctx.formatWorkerBadge(worker);
130
+ const attention = ctx.workerNeedsAttention(worker) ? "needs-attention" : "";
131
+ const location = ctx.pathBaseName(cwd);
132
+ const label = worker.agentSessionId
133
+ ? `PIE:${worker.agentSessionId.slice(-6)}`
134
+ : `S:${worker.sessionId.slice(-6)}`;
135
+ const action = ctx.formatRecommendedAction(worker);
136
+ return `
137
+ <div class="worker-item ${active} ${inactive} ${attention} ${worker.status}" data-session-id="${worker.sessionId}" ${clickable ? `onclick="attachWorker('${worker.sessionId}')"` : ""}>
138
+ <div class="worker-title-row">
139
+ <div class="worker-type">${ctx.escapeHtml(displayName)}</div>
140
+ <div class="worker-label">${label}</div>
141
+ </div>
142
+ <div class="worker-meta">${ctx.escapeHtml(location)}${summary ? ` · ${ctx.escapeHtml(summary)}` : ""}</div>
143
+ <div class="worker-event">${ctx.escapeHtml(recentEvent)}${ctx.escapeHtml(recentEventAt)}</div>
144
+ <div class="worker-badge">${badge}${action ? ` · ${ctx.escapeHtml(action)}` : ""}</div>
145
+ <div class="worker-session">${worker.sessionId.slice(-8)} · ${recent}</div>
146
+ </div>
147
+ `;
148
+ };
149
+
150
+ const liveMarkup = liveWorkers.length
151
+ ? `<div class="worker-section-list">${liveWorkers.map(renderWorkerCard).join("")}</div>`
152
+ : '<div class="worker-empty">No live workers.</div>';
153
+ const closedToggle = closedWorkers.length
154
+ ? `
155
+ <button class="worker-section-toggle" onclick="toggleClosedWorkers()">
156
+ <span>${ctx.showClosedWorkers ? "HIDE" : "SHOW"} CLOSED</span>
157
+ <span>${closedWorkers.length}</span>
158
+ </button>
159
+ `
160
+ : "";
161
+ const closedMarkup = closedWorkers.length && ctx.showClosedWorkers
162
+ ? `<div class="worker-section-list">${closedWorkers.map(renderWorkerCard).join("")}</div>`
163
+ : "";
164
+
165
+ list.innerHTML = `
166
+ <div class="worker-section">${liveMarkup}</div>
167
+ ${closedToggle ? `<div class="worker-section">${closedToggle}${closedMarkup}</div>` : ""}
168
+ `;
169
+ ctx.setWorkerHeaderLabel(`SESSIONS ${liveWorkers.length}${closedWorkers.length ? ` · ${closedWorkers.length} CLOSED` : ""}`);
170
+ ctx.renderCwdShortcuts();
171
+ ctx.updateWorkspaceView();
172
+ }
173
+
174
+ global.MagicShellRuntimeRender = {
175
+ renderRuntimeSummary,
176
+ renderRuntimeFocus,
177
+ renderRuntimeFeed,
178
+ renderWorkers,
179
+ };
180
+ })(window);