loadtoagent 1.0.0 → 1.3.1
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/README.ko.md +7 -0
- package/README.md +7 -0
- package/README.zh-CN.md +7 -0
- package/docs/PROVIDER-CONTRACTS.md +23 -1
- package/docs/UI-AUDIT-100.md +118 -0
- package/docs/UI-AUDIT-101-200.md +120 -0
- package/docs/UI-AUDIT-201-300.md +120 -0
- package/main.js +183 -37
- package/package.json +15 -4
- package/preload.js +11 -0
- package/renderer/app-agent-actions.js +125 -53
- package/renderer/app-bootstrap.js +54 -9
- package/renderer/app-dashboard.js +197 -58
- package/renderer/app-drawer-content.js +103 -86
- package/renderer/app-drawer-data.js +26 -13
- package/renderer/app-drawer.js +79 -55
- package/renderer/app-events-dialogs.js +146 -37
- package/renderer/app-events-filters.js +172 -9
- package/renderer/app-events-navigation.js +78 -31
- package/renderer/app-events-sessions.js +171 -4
- package/renderer/app-events.js +2 -1
- package/renderer/app-graph-model.js +32 -4
- package/renderer/app-graph-orchestration.js +16 -13
- package/renderer/app-graph-view.js +228 -112
- package/renderer/app-management.js +211 -0
- package/renderer/app-provider-visibility.js +124 -0
- package/renderer/app-quality.js +568 -0
- package/renderer/app-run-modal.js +103 -33
- package/renderer/app-runtime-overview.js +312 -0
- package/renderer/app-session-render.js +94 -37
- package/renderer/app-tmux-render.js +119 -38
- package/renderer/app.js +155 -76
- package/renderer/i18n-messages.js +833 -15
- package/renderer/i18n.js +104 -0
- package/renderer/index.html +162 -75
- package/renderer/shared.js +17 -0
- package/renderer/styles-agent-map.css +50 -1
- package/renderer/styles-cards.css +26 -0
- package/renderer/styles-collaboration.css +18 -88
- package/renderer/styles-components.css +126 -14
- package/renderer/styles-management.css +355 -0
- package/renderer/styles-overlays.css +13 -2
- package/renderer/styles-product.css +24 -16
- package/renderer/styles-quality.css +312 -0
- package/renderer/styles-readability.css +862 -0
- package/renderer/styles-responsive-product.css +84 -12
- package/renderer/styles-responsive-runtime.css +31 -14
- package/renderer/styles-responsive-shell.css +44 -48
- package/renderer/styles-responsive-workflows.css +37 -17
- package/renderer/styles-run-composer.css +5 -0
- package/renderer/styles-runtime-overview.css +285 -0
- package/renderer/styles-settings.css +160 -0
- package/renderer/styles-terminal.css +339 -2
- package/renderer/styles-tmux.css +154 -0
- package/renderer/styles-workflow-map.css +362 -15
- package/renderer/styles-workflows.css +69 -2
- package/renderer/styles.css +27 -12
- package/renderer/terminal-agent.js +17 -13
- package/renderer/terminal-events.js +233 -40
- package/renderer/terminal-workbench.js +176 -78
- package/renderer/terminal.js +350 -37
- package/src/agentMonitor/claudeParser.js +96 -7
- package/src/agentMonitor/codexParser.js +59 -4
- package/src/agentMonitor/executionActivity.js +282 -0
- package/src/agentMonitor/genericParser.js +56 -13
- package/src/agentMonitor/hierarchy.js +17 -0
- package/src/agentMonitor/responseIntent.js +51 -0
- package/src/agentMonitor.js +21 -3
- package/src/agentRunner.js +66 -1
- package/src/attentionNotifier.js +71 -0
- package/src/automationMonitor.js +258 -0
- package/src/contracts.js +10 -1
- package/src/ipc/registerAgentIpc.js +9 -3
- package/src/ipc/registerAppIpc.js +4 -1
- package/src/ipc/registerTerminalIpc.js +12 -6
- package/src/macUpdateHelper.js +210 -0
- package/src/monitorWorker.js +65 -6
- package/src/platformPath.js +80 -0
- package/src/processMonitor.js +81 -23
- package/src/providerVisibilityStore.js +45 -0
- package/src/sessionIntelligence.js +247 -0
- package/src/terminalHost.js +381 -0
- package/src/terminalHostDaemon.js +60 -0
- package/src/terminalManager.js +158 -3
- package/src/tmuxMonitor.js +2 -2
- package/src/updateInstaller.js +175 -0
|
@@ -14,6 +14,8 @@ window.LoadToAgentAppFactories.createGraphView = function createGraphView(contex
|
|
|
14
14
|
timeOnly,
|
|
15
15
|
providerInfo,
|
|
16
16
|
providerStyle,
|
|
17
|
+
isProviderVisible = () => true,
|
|
18
|
+
visibleProviders = () => state.providers,
|
|
17
19
|
agentRoleLabel,
|
|
18
20
|
statusClass,
|
|
19
21
|
currentActivity,
|
|
@@ -27,7 +29,13 @@ window.LoadToAgentAppFactories.createGraphView = function createGraphView(contex
|
|
|
27
29
|
agentExecutionMode,
|
|
28
30
|
executionModeBadge,
|
|
29
31
|
graphDescendantCount,
|
|
32
|
+
sessionWorkspaceLabel,
|
|
30
33
|
} = context;
|
|
34
|
+
const t = (key, params) => window.LoadToAgentI18n.t(key, params);
|
|
35
|
+
const statusLabel = (status) => ({
|
|
36
|
+
starting: t("ui.preparing"), running: t("ui.working"), waiting: t("app.nav.needs_review"), idle: t("ui.idle"),
|
|
37
|
+
completed: t("ui.completed"), failed: t("ui.problem"), cancelled: t("ui.stopped"),
|
|
38
|
+
})[status] || STATUS[status] || status;
|
|
31
39
|
|
|
32
40
|
function graphNode(session, options = {}) {
|
|
33
41
|
const provider = providerInfo(session.provider);
|
|
@@ -50,31 +58,34 @@ window.LoadToAgentAppFactories.createGraphView = function createGraphView(contex
|
|
|
50
58
|
const currentWork = latestWorkCopy(session);
|
|
51
59
|
const currentPreview = readablePreview(currentWork, options.focus ? 132 : 108);
|
|
52
60
|
const role = session.parentId
|
|
53
|
-
?
|
|
54
|
-
|
|
61
|
+
? t("graph.helper_ai_identity", {
|
|
62
|
+
name: session.agentName ? ` · ${session.agentName}` : "",
|
|
63
|
+
role: session.agentRole ? ` / ${agentRoleLabel(session.agentRole)}` : "",
|
|
64
|
+
})
|
|
65
|
+
: t("graph.assigned_ai");
|
|
55
66
|
return `<article class="agent-node ${running ? "running" : ""} ${session.parentId ? "child-agent" : "root-agent"} ${options.focus ? "is-focus" : ""}"
|
|
56
67
|
data-motion-key="agent:${esc(session.id)}"
|
|
57
68
|
data-motion-value="${esc(session.updatedAt || "")}:${usage.total || 0}:${esc(session.status || "")}"
|
|
58
69
|
style="${providerStyle(session.provider)}">
|
|
59
|
-
<button class="agent-node-main" type="button" data-graph-focus="${esc(session.id)}" aria-label="${esc(role)}
|
|
70
|
+
<button class="agent-node-main" type="button" data-graph-focus="${esc(session.id)}" aria-label="${esc(t("graph.focus_relationships", { role }))}">
|
|
60
71
|
<span class="agent-node-top">
|
|
61
72
|
<span class="provider-mark">${esc(provider.mark)}</span>
|
|
62
|
-
<span class="agent-identity"><b>${esc(role)}</b><small>${esc(provider.label)} · ${esc(session.model || "
|
|
73
|
+
<span class="agent-identity"><b>${esc(role)}</b><small>${esc(provider.label)} · ${esc(session.model || t("graph.model_unknown"))}</small></span>
|
|
63
74
|
${executionModeBadge(session, true)}
|
|
64
|
-
<span class="status-pill ${statusClass(session.status)}">${esc(
|
|
75
|
+
<span class="status-pill ${statusClass(session.status)}">${esc(statusLabel(session.status))}</span>
|
|
65
76
|
</span>
|
|
66
77
|
<span class="agent-task-label">
|
|
67
|
-
${session.parentId ?
|
|
78
|
+
${session.parentId ? t("graph.assigned_task", { source: delegation.assignmentSource === "parent-narration" ? t("graph.main_ai_explanation_suffix") : "" }) : t("graph.current_goal")}
|
|
68
79
|
</span>
|
|
69
80
|
<strong class="agent-task" title="${esc(goalPreview.full)}">${esc(goalPreview.text)}</strong>
|
|
70
|
-
${goalPreview.truncated ?
|
|
81
|
+
${goalPreview.truncated ? `<span class="agent-goal-note">${esc(t("graph.summary_shown"))}</span>` : ""}
|
|
71
82
|
<span class="agent-current">
|
|
72
|
-
<span><i>${statusIcon(activity.type)}</i><b
|
|
83
|
+
<span><i>${statusIcon(activity.type)}</i><b>${esc(t("graph.current_work"))}</b></span>
|
|
73
84
|
<strong title="${esc(currentPreview.full)}">${esc(currentPreview.text)}</strong>
|
|
74
85
|
</span>
|
|
75
86
|
<span class="agent-node-metrics">
|
|
76
87
|
<span>
|
|
77
|
-
<small
|
|
88
|
+
<small>${esc(t("graph.memory_usage"))}</small>
|
|
78
89
|
<b>${context.window ? `${percent.toFixed(1)}%` : "--"}</b>
|
|
79
90
|
</span>
|
|
80
91
|
<span>
|
|
@@ -82,15 +93,17 @@ window.LoadToAgentAppFactories.createGraphView = function createGraphView(contex
|
|
|
82
93
|
<b>${compact(usage.total)}</b>
|
|
83
94
|
</span>
|
|
84
95
|
<span>
|
|
85
|
-
<small
|
|
96
|
+
<small>${esc(t("graph.last_activity"))}</small>
|
|
86
97
|
<b>${esc(timeAgo(session.updatedAt))}</b>
|
|
87
98
|
</span>
|
|
88
99
|
</span>
|
|
89
100
|
<span class="agent-node-gauge"><i style="width:${percent}%"></i></span>
|
|
90
101
|
</button>
|
|
91
102
|
<footer class="agent-node-footer">
|
|
92
|
-
<span>${
|
|
93
|
-
|
|
103
|
+
<span>${session.parentId
|
|
104
|
+
? (cumulativeChildren ? t("graph.subagents_created", { count: cumulativeChildren }) : t("graph.helper_ai"))
|
|
105
|
+
: t("project.origin_named", { name: sessionWorkspaceLabel(session) })}</span>
|
|
106
|
+
<button type="button" data-open-session="${esc(session.id)}">${esc(t("graph.view_conversation"))} <b>↗</b>
|
|
94
107
|
</button>
|
|
95
108
|
</footer>
|
|
96
109
|
</article>`;
|
|
@@ -100,7 +113,9 @@ window.LoadToAgentAppFactories.createGraphView = function createGraphView(contex
|
|
|
100
113
|
const provider = providerInfo(session.provider);
|
|
101
114
|
const usage = session.usage || {};
|
|
102
115
|
const directChildren = graphChildren(session, model).length;
|
|
103
|
-
const identity = session.parentId
|
|
116
|
+
const identity = session.parentId
|
|
117
|
+
? t("graph.helper_ai_named", { name: session.agentName || agentRoleLabel(session.agentRole) })
|
|
118
|
+
: t("project.origin_named", { name: sessionWorkspaceLabel(session) });
|
|
104
119
|
const delegation = session.delegation || {};
|
|
105
120
|
const taskName = delegation.taskName || session.taskName || "";
|
|
106
121
|
const assignedWork = delegation.assignmentObserved && delegation.assignment ? delegation.assignment : taskName || session.title;
|
|
@@ -108,16 +123,16 @@ window.LoadToAgentAppFactories.createGraphView = function createGraphView(contex
|
|
|
108
123
|
const outcome = delegation.result || session.result || "";
|
|
109
124
|
const outcomeText = outcome || latestWorkCopy(session);
|
|
110
125
|
const assignedWorkPreview = readablePreview(assignedWork, session.parentId ? 110 : 104);
|
|
111
|
-
const taskLabel = session.parentId ? `${label || agentRoleLabel(session.agentRole)}${taskName ?
|
|
126
|
+
const taskLabel = session.parentId ? `${label || agentRoleLabel(session.agentRole)}${taskName ? t("graph.assigned_name_suffix", { name: taskName }) : ""}` : label;
|
|
112
127
|
const assignmentSourceNote =
|
|
113
128
|
session.parentId && delegation.assignmentSource === "parent-narration"
|
|
114
|
-
?
|
|
129
|
+
? `<span class="agent-flow-assignment-source">${esc(t("graph.main_ai_prestart_explanation"))}</span>`
|
|
115
130
|
: "";
|
|
116
131
|
const sharedGoalCopy =
|
|
117
|
-
session.parentId && sharedGoal && sharedGoal !== assignedWork ? `<span class="agent-flow-shared"
|
|
132
|
+
session.parentId && sharedGoal && sharedGoal !== assignedWork ? `<span class="agent-flow-shared">${esc(t("graph.shared_goal"))} · ${esc(sharedGoal)}</span>` : "";
|
|
118
133
|
const outcomeCopy = session.parentId
|
|
119
134
|
? `<span class="agent-flow-outcome ${session.status === "completed" ? "done" : ""}">
|
|
120
|
-
<b>${session.status === "completed" ? "
|
|
135
|
+
<b>${esc(session.status === "completed" ? t("graph.completed_result") : t("graph.current_work"))}</b>
|
|
121
136
|
<span class="agent-flow-outcome-copy" title="${esc(outcomeText)}">${esc(outcomeText)}</span>
|
|
122
137
|
</span>`
|
|
123
138
|
: "";
|
|
@@ -125,13 +140,13 @@ window.LoadToAgentAppFactories.createGraphView = function createGraphView(contex
|
|
|
125
140
|
const primaryTask = taskName || assignedWork || session.title;
|
|
126
141
|
const assignmentCopy =
|
|
127
142
|
assignedWork && assignedWork !== primaryTask
|
|
128
|
-
? `<span class="agent-flow-assignment"><small
|
|
143
|
+
? `<span class="agent-flow-assignment"><small>${esc(t("graph.assignment_details"))}</small><strong title="${esc(assignedWork)}">${esc(assignedWork)}</strong></span>`
|
|
129
144
|
: "";
|
|
130
145
|
const workState = subagentWorkState(session);
|
|
131
146
|
const interaction = directChildren
|
|
132
|
-
? `data-graph-focus="${esc(session.id)}" aria-label="${esc(
|
|
133
|
-
: `data-open-subagent-chat="${esc(session.id)}" aria-label="${esc(
|
|
134
|
-
const action = directChildren ?
|
|
147
|
+
? `data-graph-focus="${esc(session.id)}" aria-label="${esc(t("graph.view_child_flow", { task: primaryTask }))}"`
|
|
148
|
+
: `data-open-subagent-chat="${esc(session.id)}" aria-label="${esc(t("graph.view_main_ai_conversation_for_task", { task: primaryTask }))}"`;
|
|
149
|
+
const action = directChildren ? t("graph.view_child_subagents", { count: directChildren }) : t("graph.view_main_ai_conversation");
|
|
135
150
|
return `<button type="button" class="agent-flow-row child-session work-${workState} ${statusClass(session.status)}"
|
|
136
151
|
${interaction}
|
|
137
152
|
data-motion-key="agent:${esc(session.id)}"
|
|
@@ -140,13 +155,13 @@ window.LoadToAgentAppFactories.createGraphView = function createGraphView(contex
|
|
|
140
155
|
<span class="agent-flow-state" aria-hidden="true"></span>
|
|
141
156
|
<span class="agent-flow-copy">
|
|
142
157
|
<span class="agent-flow-kicker">
|
|
143
|
-
<small>${esc(label || agentRoleLabel(session.agentRole))}
|
|
158
|
+
<small>${esc(t("graph.named_session", { name: label || agentRoleLabel(session.agentRole) }))}</small>
|
|
144
159
|
<time>${esc(timeAgo(session.updatedAt))}</time>
|
|
145
160
|
</span>
|
|
146
161
|
<b class="agent-flow-session-title" title="${esc(primaryTask)}">${esc(primaryTask)}</b>
|
|
147
162
|
<span class="agent-flow-agent">
|
|
148
163
|
<i>${esc(provider.mark)}</i>
|
|
149
|
-
<strong>${esc(session.agentName || "
|
|
164
|
+
<strong>${esc(session.agentName || t("graph.name_unknown"))}</strong>
|
|
150
165
|
<small>${esc(provider.label)}${session.model ? ` · ${esc(session.model)}` : ""}</small>
|
|
151
166
|
</span>
|
|
152
167
|
${assignmentCopy}${assignmentSourceNote}${outcomeCopy}<span class="agent-flow-child-action">${esc(action)}</span>
|
|
@@ -154,7 +169,7 @@ window.LoadToAgentAppFactories.createGraphView = function createGraphView(contex
|
|
|
154
169
|
<span class="agent-flow-provider">
|
|
155
170
|
${executionModeBadge(session, true)}
|
|
156
171
|
<small class="status-pill work-${workState}">${esc(subagentWorkLabel(session))}</small>
|
|
157
|
-
${session.status === "completed" ? "
|
|
172
|
+
${session.status === "completed" ? `<em>${esc(t("graph.recent_work_completed"))}</em>` : ""}
|
|
158
173
|
</span>
|
|
159
174
|
</button>`;
|
|
160
175
|
}
|
|
@@ -167,10 +182,10 @@ window.LoadToAgentAppFactories.createGraphView = function createGraphView(contex
|
|
|
167
182
|
<span class="agent-flow-copy">
|
|
168
183
|
${taskLabel ? `<small>${esc(taskLabel)}</small>` : ""}
|
|
169
184
|
<b title="${esc(assignedWorkPreview.full)}">${esc(assignedWorkPreview.text)}</b>
|
|
170
|
-
<em>${esc(identity)} · ${directChildren ?
|
|
185
|
+
<em>${esc(identity)} · ${directChildren ? `${t("graph.helper_ai_count", { count: directChildren })} · ` : ""}${esc(timeAgo(session.updatedAt))}</em>
|
|
171
186
|
${assignmentSourceNote}${sharedGoalCopy}${outcomeCopy}
|
|
172
187
|
</span>
|
|
173
|
-
<span class="agent-flow-provider"><i>${esc(provider.mark)}</i><small>${esc(
|
|
188
|
+
<span class="agent-flow-provider"><i>${esc(provider.mark)}</i><small>${esc(statusLabel(session.status))}</small></span>
|
|
174
189
|
</button>`;
|
|
175
190
|
}
|
|
176
191
|
|
|
@@ -180,19 +195,30 @@ window.LoadToAgentAppFactories.createGraphView = function createGraphView(contex
|
|
|
180
195
|
const expanded = state.graphExpandedProviders.has(providerId);
|
|
181
196
|
const shown = expanded ? ordered : ordered.slice(0, 6);
|
|
182
197
|
const hidden = Math.max(0, ordered.length - shown.length);
|
|
183
|
-
const
|
|
198
|
+
const helperIds = new Set();
|
|
199
|
+
const queue = ordered.flatMap((root) => root.childIds || []);
|
|
200
|
+
while (queue.length) {
|
|
201
|
+
const id = queue.shift();
|
|
202
|
+
if (!id || helperIds.has(id)) continue;
|
|
203
|
+
const helper = model.byId.get(id);
|
|
204
|
+
if (!helper) continue;
|
|
205
|
+
helperIds.add(id);
|
|
206
|
+
queue.push(...(helper.childIds || []));
|
|
207
|
+
}
|
|
208
|
+
const helpers = [...helperIds].map((id) => model.byId.get(id)).filter(Boolean);
|
|
209
|
+
const activeHelpers = helpers.filter(isLiveSession).length;
|
|
184
210
|
return `<section class="agent-flow-lane" style="${providerStyle(providerId)}">
|
|
185
211
|
<header class="agent-flow-lane-head">
|
|
186
212
|
<span class="provider-mark">${esc(provider.mark)}</span>
|
|
187
|
-
<span><b>${esc(provider.label)}</b><small>${ordered.length
|
|
188
|
-
<em>${ordered.filter(isLiveSession).length}
|
|
213
|
+
<span><b>${esc(provider.label)}</b><small>${esc(t("graph.major_tasks_and_agents", { tasks: ordered.length, active: activeHelpers, records: helpers.length }))}</small></span>
|
|
214
|
+
<em>${esc(t("graph.running_count", { count: ordered.filter(isLiveSession).length }))}</em>
|
|
189
215
|
</header>
|
|
190
216
|
<div class="agent-flow-list">${shown.map((root) => compactGraphNode(root, model)).join("")}</div>
|
|
191
217
|
${
|
|
192
218
|
hidden
|
|
193
|
-
? `<button type="button" class="agent-flow-more" data-graph-provider-more="${esc(providerId)}"
|
|
219
|
+
? `<button type="button" class="agent-flow-more" data-graph-provider-more="${esc(providerId)}">${esc(t("graph.show_remaining_tasks", { count: hidden }))}</button>`
|
|
194
220
|
: expanded && ordered.length > 6
|
|
195
|
-
? `<button type="button" class="agent-flow-more" data-graph-provider-less="${esc(providerId)}"
|
|
221
|
+
? `<button type="button" class="agent-flow-more" data-graph-provider-less="${esc(providerId)}">${esc(t("graph.show_compact"))}</button>`
|
|
196
222
|
: ""
|
|
197
223
|
}
|
|
198
224
|
</section>`;
|
|
@@ -209,7 +235,7 @@ window.LoadToAgentAppFactories.createGraphView = function createGraphView(contex
|
|
|
209
235
|
for (const tmuxSession of distro.sessions || []) {
|
|
210
236
|
for (const window of tmuxSession.windows || []) {
|
|
211
237
|
for (const pane of window.panes || []) {
|
|
212
|
-
if (!pane.agent) continue;
|
|
238
|
+
if (!pane.agent || pane.dead || !isProviderVisible(pane.agent.provider)) continue;
|
|
213
239
|
entries.push({ distro, tmuxSession, window, pane, agent: pane.agent });
|
|
214
240
|
}
|
|
215
241
|
}
|
|
@@ -227,15 +253,15 @@ window.LoadToAgentAppFactories.createGraphView = function createGraphView(contex
|
|
|
227
253
|
const { distro, tmuxSession, window, pane, agent } = entry;
|
|
228
254
|
const provider = providerInfo(agent.provider);
|
|
229
255
|
const linked = agent.linkedSessionId ? (state.snapshot?.sessions || []).find((session) => session.id === agent.linkedSessionId) || null : null;
|
|
230
|
-
const title = linked ? linked.title : pane.title ||
|
|
231
|
-
const stateLabel = pane.dead ? "
|
|
256
|
+
const title = linked ? linked.title : pane.title || t("graph.tmux_task", { provider: provider.label });
|
|
257
|
+
const stateLabel = pane.dead ? t("graph.ended") : pane.active ? t("graph.selected_pane") : t("graph.background_running");
|
|
232
258
|
return `<article class="live-tmux-card ${pane.active ? "active" : ""} ${pane.dead ? "dead" : ""}"
|
|
233
259
|
style="${providerStyle(agent.provider)}"
|
|
234
260
|
data-motion-key="live-tmux:${esc(pane.id)}"
|
|
235
261
|
data-motion-value="${esc(agent.updatedAt || "")}:${pane.pid || 0}">
|
|
236
|
-
<button type="button" class="live-tmux-pane" data-tmux-type="pane" data-tmux-id="${esc(pane.id)}" aria-label="${esc(tmuxSession.name)}
|
|
262
|
+
<button type="button" class="live-tmux-pane" data-tmux-type="pane" data-tmux-id="${esc(pane.id)}" aria-label="${esc(t("graph.open_tmux_pane", { session: tmuxSession.name }))}">
|
|
237
263
|
<span class="live-tmux-card-head">
|
|
238
|
-
<span class="live-tmux-symbol">▦</span><span><small
|
|
264
|
+
<span class="live-tmux-symbol">▦</span><span><small>${esc(t("graph.tmux_session"))}</small><b>${esc(tmuxSession.name)}</b></span>
|
|
239
265
|
<em>${esc(stateLabel)}</em>
|
|
240
266
|
</span>
|
|
241
267
|
<strong class="live-tmux-title">${esc(title)}</strong>
|
|
@@ -246,63 +272,68 @@ window.LoadToAgentAppFactories.createGraphView = function createGraphView(contex
|
|
|
246
272
|
<span class="live-tmux-location">
|
|
247
273
|
<b>${esc(distro.name)}</b>
|
|
248
274
|
<i>›</i>
|
|
249
|
-
<span>${esc(window.name ||
|
|
275
|
+
<span>${esc(window.name || t("graph.window_number", { number: window.index + 1 }))}</span>
|
|
250
276
|
<i>›</i>
|
|
251
|
-
<span
|
|
277
|
+
<span>${esc(t("graph.pane_number", { number: pane.index + 1 }))} · ${esc(pane.nativeId || pane.id)}</span>
|
|
252
278
|
</span>
|
|
253
|
-
<span class="live-tmux-cwd" title="${esc(pane.cwd || "")}">${esc(pane.cwd || "
|
|
279
|
+
<span class="live-tmux-cwd" title="${esc(pane.cwd || "")}">${esc(pane.cwd || t("graph.workspace_unknown"))}</span>
|
|
254
280
|
</button>
|
|
255
281
|
<footer>
|
|
256
|
-
<span>${linked ? "
|
|
282
|
+
<span>${esc(linked ? t("graph.linked_to_conversation") : t("graph.detected_from_tmux"))}</span>
|
|
257
283
|
<span>
|
|
258
|
-
${linked ? `<button type="button" data-graph-focus="${esc(linked.id)}"
|
|
259
|
-
<button type="button" class="live-tmux-pane" data-tmux-type="pane" data-tmux-id="${esc(pane.id)}"
|
|
284
|
+
${linked ? `<button type="button" data-graph-focus="${esc(linked.id)}">${esc(t("graph.view_ai_flow"))}</button>` : ""}
|
|
285
|
+
<button type="button" class="live-tmux-pane" data-tmux-type="pane" data-tmux-id="${esc(pane.id)}">${esc(t("graph.open_in_tmux"))}</button>
|
|
260
286
|
</span>
|
|
261
287
|
</footer>
|
|
262
288
|
</article>`;
|
|
263
289
|
}
|
|
264
290
|
|
|
265
291
|
function runtimeSeparatedOverview(roots, model) {
|
|
266
|
-
const
|
|
267
|
-
const
|
|
268
|
-
const
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
292
|
+
const tmuxEntries = liveTmuxEntries(state.snapshot && state.snapshot.tmux);
|
|
293
|
+
const tmuxLinkedIds = new Set(tmuxEntries.map((entry) => String(entry.agent && entry.agent.linkedSessionId || "")).filter(Boolean));
|
|
294
|
+
const workflowUsesTmux = root => {
|
|
295
|
+
const queue = [...(root.childIds || [])];
|
|
296
|
+
const visited = new Set();
|
|
297
|
+
while (queue.length) {
|
|
298
|
+
const id = queue.shift();
|
|
299
|
+
if (!id || visited.has(id)) continue;
|
|
300
|
+
visited.add(id);
|
|
301
|
+
const child = model.byId.get(id);
|
|
302
|
+
if (!child) continue;
|
|
303
|
+
if (agentExecutionMode(child).kind === "tmux" || tmuxLinkedIds.has(child.id)) return true;
|
|
304
|
+
queue.push(...(child.childIds || []));
|
|
305
|
+
}
|
|
306
|
+
return false;
|
|
307
|
+
};
|
|
308
|
+
const tmuxRoots = roots.filter((root) => agentExecutionMode(root).kind === "tmux" || tmuxLinkedIds.has(root.id) || workflowUsesTmux(root));
|
|
309
|
+
const tmuxRootIds = new Set(tmuxRoots.map((root) => root.id));
|
|
310
|
+
const standardRoots = roots.filter((root) => !tmuxRootIds.has(root.id));
|
|
311
|
+
const providerOrder = [...new Set([...visibleProviders().map((item) => item.id), ...roots.map((item) => item.provider).filter(isProviderVisible)])];
|
|
272
312
|
const lanesFor = (items) =>
|
|
273
313
|
providerOrder.map((providerId) => ({ providerId, roots: items.filter((root) => root.provider === providerId) })).filter((item) => item.roots.length);
|
|
274
314
|
const standardLanes = lanesFor(standardRoots);
|
|
275
|
-
const
|
|
276
|
-
const summary = tmux.summary || {};
|
|
315
|
+
const tmuxLanes = lanesFor(tmuxRoots);
|
|
277
316
|
const standardHtml = standardLanes.length
|
|
278
317
|
? `<div class="agent-flow-overview">${standardLanes.map((item) => providerFlowLane(item.providerId, item.roots, model)).join("")}</div>`
|
|
279
|
-
:
|
|
280
|
-
const
|
|
281
|
-
|
|
282
|
-
? `${tmuxEntries.length ? `<div class="live-tmux-grid">${tmuxEntries.map(liveTmuxPaneCard).join("")}</div>` : ""}
|
|
283
|
-
${
|
|
284
|
-
fallbackTmuxLanes.length
|
|
285
|
-
? `<div class="agent-flow-overview live-tmux-fallback">
|
|
286
|
-
${fallbackTmuxLanes.map((item) => providerFlowLane(item.providerId, item.roots, model)).join("")}
|
|
287
|
-
</div>`
|
|
288
|
-
: ""
|
|
289
|
-
}`
|
|
290
|
-
: '<div class="runtime-segment-empty tmux"><b>TMUX에서 실행 중인 AI가 없습니다</b><span>TMUX AI 프로세스가 감지되면 일반 실행과 분리해 여기에 표시합니다.</span></div>';
|
|
291
|
-
return `<div class="agent-runtime-split" data-runtime-split="true">
|
|
292
|
-
<section class="runtime-segment tmux-runtime" data-runtime-segment="tmux">
|
|
318
|
+
: `<div class="runtime-segment-empty"><b>${esc(t("graph.no_standard_ai"))}</b><span>${esc(t("graph.all_detected_in_tmux"))}</span></div>`;
|
|
319
|
+
const tmuxSection = tmuxLanes.length
|
|
320
|
+
? `<section class="runtime-segment tmux-runtime" data-runtime-segment="tmux">
|
|
293
321
|
<header>
|
|
294
322
|
<span class="runtime-segment-icon">▦</span>
|
|
295
|
-
<span><small
|
|
296
|
-
<strong>${
|
|
297
|
-
<button type="button" class="live-tmux-overview-open"
|
|
323
|
+
<span><small>${esc(t("graph.tmux_only"))}</small><b>${esc(t("graph.tmux_sessions"))}</b><em>${esc(t("graph.tmux_runtime_description"))}</em></span>
|
|
324
|
+
<strong>${esc(t("common.count", { count: tmuxRoots.length }))}</strong>
|
|
325
|
+
<button type="button" class="live-tmux-overview-open">${esc(t("graph.open_tmux_overview"))}</button>
|
|
298
326
|
</header>
|
|
299
|
-
|
|
300
|
-
</section
|
|
327
|
+
<div class="agent-flow-overview">${tmuxLanes.map((item) => providerFlowLane(item.providerId, item.roots, model)).join("")}</div>
|
|
328
|
+
</section>`
|
|
329
|
+
: "";
|
|
330
|
+
return `<div class="agent-runtime-split" data-runtime-split="true">
|
|
331
|
+
${tmuxSection}
|
|
301
332
|
<section class="runtime-segment standard-runtime" data-runtime-segment="standard">
|
|
302
333
|
<header>
|
|
303
334
|
<span class="runtime-segment-icon">›_</span>
|
|
304
|
-
<span><small
|
|
305
|
-
<strong>${standardRoots.length}
|
|
335
|
+
<span><small>${esc(t("graph.without_tmux"))}</small><b>${esc(t("graph.standard_sessions"))}</b><em>${esc(t("graph.standard_runtime_description"))}</em></span>
|
|
336
|
+
<strong>${esc(t("common.count", { count: standardRoots.length }))}</strong>
|
|
306
337
|
</header>
|
|
307
338
|
${standardHtml}
|
|
308
339
|
</section>
|
|
@@ -340,24 +371,24 @@ window.LoadToAgentAppFactories.createGraphView = function createGraphView(contex
|
|
|
340
371
|
.join("");
|
|
341
372
|
const metrics = workflowMetrics(session, children);
|
|
342
373
|
const capacity = metrics.simultaneousCapacity > 0 ? metrics.simultaneousCapacity : "--";
|
|
343
|
-
const retained = metrics.retainedCount == null ? "
|
|
344
|
-
const source = metrics.capacitySource === "runtime-instruction" ? "
|
|
374
|
+
const retained = metrics.retainedCount == null ? t("graph.retained_count_unobserved") : t("graph.retained_count", { count: metrics.retainedCount });
|
|
375
|
+
const source = metrics.capacitySource === "runtime-instruction" ? t("graph.session_runtime_limit") : t("graph.capacity_source_unknown");
|
|
345
376
|
return `<div class="agent-workflow-summary" data-collaboration-summary="true">
|
|
346
377
|
<div class="workflow-metric-grid">
|
|
347
378
|
<span data-collaboration-metric="created">
|
|
348
|
-
<small
|
|
379
|
+
<small>${esc(t("graph.created_in_task"))}</small><b>${esc(metrics.cumulativeCreated)}</b><em>${window.LoadToAgentI18n.t("ui.items")}</em>
|
|
349
380
|
</span>
|
|
350
381
|
<span data-collaboration-metric="capacity">
|
|
351
|
-
<small
|
|
382
|
+
<small>${esc(t("graph.simultaneous_capacity"))}</small><b>${esc(capacity)}</b><em>${capacity === "--" ? "" : window.LoadToAgentI18n.t("ui.items")}</em>
|
|
352
383
|
</span>
|
|
353
384
|
<span data-collaboration-metric="running">
|
|
354
|
-
<small
|
|
385
|
+
<small>${esc(t("graph.currently_running"))}</small><b>${esc(metrics.currentlyRunning)}</b><em>${window.LoadToAgentI18n.t("ui.items")}</em>
|
|
355
386
|
</span>
|
|
356
387
|
<span data-collaboration-metric="completed">
|
|
357
|
-
<small
|
|
388
|
+
<small>${esc(t("graph.completed_records"))}</small><b>${esc(metrics.completedRecords)}</b><em>${window.LoadToAgentI18n.t("ui.items")}</em>
|
|
358
389
|
</span>
|
|
359
390
|
</div>
|
|
360
|
-
<div class="workflow-summary-evidence"><span>${esc(retained)} ·
|
|
391
|
+
<div class="workflow-summary-evidence"><span>${esc(retained)} · ${esc(t("graph.completed_records_collapsed"))}</span><small>${esc(source)} · ${esc(t("graph.event_basis"))}</small></div>
|
|
361
392
|
<div class="workflow-summary-providers">${providers}</div>
|
|
362
393
|
</div>`;
|
|
363
394
|
}
|
|
@@ -378,10 +409,10 @@ window.LoadToAgentAppFactories.createGraphView = function createGraphView(contex
|
|
|
378
409
|
return `<div class="completed-subagent-disclosure ${expanded ? "expanded" : ""}" data-completed-subagent-section>
|
|
379
410
|
<button type="button" data-subagent-completed-toggle="${esc(ownerId)}" aria-expanded="${expanded ? "true" : "false"}">
|
|
380
411
|
<span class="completed-disclosure-icon">✓</span>
|
|
381
|
-
<span><b
|
|
382
|
-
<small>${expanded ? "
|
|
412
|
+
<span><b>${esc(t("graph.completed_subagents", { count: completed.length }))}</b>
|
|
413
|
+
<small>${esc(expanded ? t("graph.completed_expanded") : t("graph.completed_collapsed_hint"))}</small>
|
|
383
414
|
</span>
|
|
384
|
-
<i>${expanded ? "
|
|
415
|
+
<i>${esc(expanded ? t("graph.collapse") : t("graph.expand"))}</i>
|
|
385
416
|
</button>
|
|
386
417
|
</div>`;
|
|
387
418
|
}
|
|
@@ -399,12 +430,12 @@ window.LoadToAgentAppFactories.createGraphView = function createGraphView(contex
|
|
|
399
430
|
|
|
400
431
|
function communicationEndpoint(value, owner, model) {
|
|
401
432
|
const path = String(value || "");
|
|
402
|
-
if (!path) return "
|
|
403
|
-
if (path === "Codex 런타임") return
|
|
404
|
-
if (path === "/root" || path === owner.agentPath || (!owner.agentPath && path === owner.id)) return "
|
|
433
|
+
if (!path) return t("graph.target_unknown");
|
|
434
|
+
if (path === "Codex 런타임") return t("graph.codex_runtime");
|
|
435
|
+
if (path === "/root" || path === owner.agentPath || (!owner.agentPath && path === owner.id)) return t("graph.main_ai");
|
|
405
436
|
const taskName = agentPathTaskName(path);
|
|
406
437
|
const session = model.nodes.find((node) => node.agentPath === path || node.taskName === taskName);
|
|
407
|
-
if (session) return `${session.agentName || "
|
|
438
|
+
if (session) return `${session.agentName || t("graph.sub_ai")}${taskName ? ` · ${taskName}` : ""}`;
|
|
408
439
|
return taskName || path;
|
|
409
440
|
}
|
|
410
441
|
|
|
@@ -417,11 +448,11 @@ window.LoadToAgentAppFactories.createGraphView = function createGraphView(contex
|
|
|
417
448
|
return `<section class="agent-communication-panel empty" data-collaboration-communications="0">
|
|
418
449
|
<header>
|
|
419
450
|
<span>
|
|
420
|
-
<b
|
|
421
|
-
<small
|
|
451
|
+
<b>${esc(t("graph.communication_title"))}</b>
|
|
452
|
+
<small>${esc(t("graph.communication_short_description"))}</small>
|
|
422
453
|
</span>
|
|
423
454
|
</header>
|
|
424
|
-
<p
|
|
455
|
+
<p>${esc(t("graph.no_communication_events"))}</p>
|
|
425
456
|
</section>`;
|
|
426
457
|
}
|
|
427
458
|
const rows = events
|
|
@@ -429,32 +460,34 @@ window.LoadToAgentAppFactories.createGraphView = function createGraphView(contex
|
|
|
429
460
|
const text =
|
|
430
461
|
event.text ||
|
|
431
462
|
(event.protected
|
|
432
|
-
?
|
|
463
|
+
? t("graph.assigned_to_subagent", { task: event.taskName || t("graph.this_task") })
|
|
433
464
|
: event.kind === "started"
|
|
434
|
-
? "
|
|
435
|
-
: "
|
|
436
|
-
const sourceLabel = event.assignmentSource === "parent-narration" ?
|
|
465
|
+
? t("graph.runtime_start_confirmed")
|
|
466
|
+
: t("graph.status_only_recorded"));
|
|
467
|
+
const sourceLabel = event.assignmentSource === "parent-narration" ? ` · ${t("graph.main_ai_prestart_short")}` : "";
|
|
437
468
|
return `<article class="agent-communication-event ${esc(event.kind)}" data-communication-kind="${esc(event.kind)}">
|
|
438
469
|
<span class="communication-route">
|
|
439
470
|
<b>${esc(communicationEndpoint(event.from, owner, model))}</b><i>→</i>
|
|
440
471
|
<b>${esc(communicationEndpoint(event.to, owner, model))}</b>
|
|
441
472
|
</span>
|
|
442
473
|
<span class="communication-copy">
|
|
443
|
-
<small>${esc(event.label)}${event.taskName ? ` · ${esc(event.taskName)}` : ""}${sourceLabel}</small>
|
|
474
|
+
<small>${esc(window.LoadToAgentI18n.observedText(event.label))}${event.taskName ? ` · ${esc(event.taskName)}` : ""}${sourceLabel}</small>
|
|
444
475
|
<strong>${esc(text)}</strong>
|
|
445
476
|
</span>
|
|
446
477
|
<time>${esc(timeOnly(event.timestamp))}</time>
|
|
447
478
|
</article>`;
|
|
448
479
|
})
|
|
449
480
|
.join("");
|
|
450
|
-
const countLabel = relevant.length > events.length
|
|
481
|
+
const countLabel = relevant.length > events.length
|
|
482
|
+
? t("graph.recent_of_total_events", { recent: events.length, total: relevant.length })
|
|
483
|
+
: t("common.events", { count: events.length });
|
|
451
484
|
return `<section class="agent-communication-panel"
|
|
452
485
|
data-collaboration-communications="${events.length}"
|
|
453
486
|
data-collaboration-communications-total="${relevant.length}">
|
|
454
487
|
<header>
|
|
455
488
|
<span>
|
|
456
|
-
<b
|
|
457
|
-
<small
|
|
489
|
+
<b>${esc(t("graph.communication_title"))}</b>
|
|
490
|
+
<small>${esc(t("graph.communication_description"))}</small>
|
|
458
491
|
</span>
|
|
459
492
|
<em>${countLabel}</em>
|
|
460
493
|
</header>
|
|
@@ -462,20 +495,99 @@ window.LoadToAgentAppFactories.createGraphView = function createGraphView(contex
|
|
|
462
495
|
</section>`;
|
|
463
496
|
}
|
|
464
497
|
|
|
498
|
+
function executionActivityLabel(activity) {
|
|
499
|
+
if (activity.kind === "background") return t("graph.background_task");
|
|
500
|
+
return activity.mode === "background" ? t("graph.shell_background") : t("graph.shell_foreground");
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
function executionActivityStatus(activity) {
|
|
504
|
+
return ({
|
|
505
|
+
running: t("graph.execution_running"),
|
|
506
|
+
completed: t("graph.execution_completed"),
|
|
507
|
+
failed: t("graph.execution_failed"),
|
|
508
|
+
cancelled: t("ui.stopped"),
|
|
509
|
+
})[activity.status] || activity.status;
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
function executionActivityCard(activity, ownerId = "") {
|
|
513
|
+
const label = executionActivityLabel(activity);
|
|
514
|
+
const command = readablePreview(activity.command || activity.label || label, 150);
|
|
515
|
+
const handle = activity.backgroundId
|
|
516
|
+
? `${activity.backgroundIdType || t("graph.execution_handle")} · ${activity.backgroundId}`
|
|
517
|
+
: "";
|
|
518
|
+
const runtime = activity.runtime || activity.tool || t("graph.runtime_unknown");
|
|
519
|
+
return `<details class="execution-activity-card ${esc(activity.kind || "background")} ${esc(activity.mode || "foreground")} ${esc(activity.status || "completed")}"
|
|
520
|
+
data-disclosure-key="${esc(`graph:execution:${ownerId}:${activity.id}`)}"
|
|
521
|
+
data-execution-activity="${esc(activity.id)}"
|
|
522
|
+
data-execution-kind="${esc(activity.kind || "")}" data-execution-mode="${esc(activity.mode || "")}" data-execution-status="${esc(activity.status || "")}">
|
|
523
|
+
<summary>
|
|
524
|
+
<span class="execution-activity-icon" aria-hidden="true">${activity.kind === "shell" ? "›_" : "◌"}</span>
|
|
525
|
+
<span class="execution-activity-copy">
|
|
526
|
+
<span class="execution-activity-kicker"><b>${esc(label)}</b><small>${esc(runtime)}</small></span>
|
|
527
|
+
<strong title="${esc(command.full)}">${esc(activity.label || command.text)}</strong>
|
|
528
|
+
${activity.command ? `<code title="${esc(activity.command)}">${esc(command.text)}</code>` : ""}
|
|
529
|
+
<span class="execution-activity-meta">
|
|
530
|
+
${activity.cwd ? `<small title="${esc(activity.cwd)}">${esc(t("graph.execution_workdir"))} · ${esc(activity.cwd)}</small>` : ""}
|
|
531
|
+
${handle ? `<small>${esc(handle)}</small>` : ""}
|
|
532
|
+
</span>
|
|
533
|
+
</span>
|
|
534
|
+
<span class="execution-activity-state"><span><i aria-hidden="true"></i><b>${esc(executionActivityStatus(activity))}</b></span><small>${esc(activity.statusDetail || timeAgo(activity.updatedAt || activity.startedAt))}</small><span class="execution-activity-disclosure"><b class="open-label">${esc(t("graph.execution_details"))}</b><b class="close-label">${esc(t("graph.execution_details_close"))}</b><i aria-hidden="true">⌄</i></span></span>
|
|
535
|
+
</summary>
|
|
536
|
+
<div class="execution-activity-detail">
|
|
537
|
+
<div class="execution-detail-command"><header><span>${esc(t("graph.execution_command"))}</span><button type="button" data-copy-text="${esc(activity.command || activity.label || command.full)}">${esc(t("graph.copy_command"))}</button></header><code>${esc(activity.command || activity.label || command.full)}</code></div>
|
|
538
|
+
<dl>
|
|
539
|
+
<div><dt>${esc(t("graph.execution_status_label"))}</dt><dd>${esc(executionActivityStatus(activity))}${activity.statusDetail ? ` · ${esc(activity.statusDetail)}` : ""}</dd></div>
|
|
540
|
+
<div><dt>${esc(t("graph.execution_runtime"))}</dt><dd>${esc(runtime)}</dd></div>
|
|
541
|
+
${activity.cwd ? `<div><dt>${esc(t("graph.execution_workdir"))}</dt><dd title="${esc(activity.cwd)}">${esc(activity.cwd)}</dd></div>` : ""}
|
|
542
|
+
${handle ? `<div><dt>${esc(t("graph.execution_handle"))}</dt><dd>${esc(handle)}</dd></div>` : ""}
|
|
543
|
+
${activity.startedAt ? `<div><dt>${esc(t("graph.execution_started"))}</dt><dd title="${esc(activity.startedAt)}">${esc(timeAgo(activity.startedAt))}</dd></div>` : ""}
|
|
544
|
+
${activity.updatedAt ? `<div><dt>${esc(t("graph.execution_updated"))}</dt><dd title="${esc(activity.updatedAt)}">${esc(timeAgo(activity.updatedAt))}</dd></div>` : ""}
|
|
545
|
+
</dl>
|
|
546
|
+
<div class="execution-detail-output"><header><span>${esc(t("graph.execution_output"))}</span>${activity.output ? `<button type="button" data-copy-text="${esc(activity.output)}">${esc(t("graph.copy_output"))}</button>` : ""}</header>${activity.output ? `<pre>${esc(activity.output)}</pre>` : `<p>${esc(t("graph.execution_output_unavailable"))}</p>`}</div>
|
|
547
|
+
</div>
|
|
548
|
+
</details>`;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
function executionActivityPanel(session) {
|
|
552
|
+
const activities = [...(session.executions || [])].sort((a, b) => {
|
|
553
|
+
const activeA = a.status === "running" ? 1 : 0;
|
|
554
|
+
const activeB = b.status === "running" ? 1 : 0;
|
|
555
|
+
return activeB - activeA || Date.parse(b.updatedAt || b.startedAt || 0) - Date.parse(a.updatedAt || a.startedAt || 0);
|
|
556
|
+
});
|
|
557
|
+
if (!activities.length) return "";
|
|
558
|
+
const running = activities.filter((activity) => activity.status === "running");
|
|
559
|
+
const completed = activities.filter((activity) => activity.status !== "running");
|
|
560
|
+
const expanded = state.expandedExecutionSessions.has(session.id);
|
|
561
|
+
const shown = expanded ? activities : [...running, ...completed.slice(0, Math.max(0, 6 - running.length))];
|
|
562
|
+
return `<section class="execution-activity-panel" data-execution-activities="${activities.length}" data-running-executions="${running.length}">
|
|
563
|
+
<header>
|
|
564
|
+
<span><b>${esc(t("graph.execution_activity"))}</b><small>${esc(t("graph.execution_activity_description"))}</small></span>
|
|
565
|
+
<em>${esc(t("graph.execution_activity_summary", { running: running.length, total: activities.length }))}</em>
|
|
566
|
+
</header>
|
|
567
|
+
<div class="execution-activity-list">${shown.map(activity => executionActivityCard(activity, session.id)).join("")}</div>
|
|
568
|
+
${shown.length < activities.length
|
|
569
|
+
? `<button type="button" class="execution-activity-retained" data-execution-history-toggle="${esc(session.id)}" aria-expanded="false">${esc(t("graph.execution_show_older", { count: activities.length - shown.length }))}</button>`
|
|
570
|
+
: expanded && activities.length > 6
|
|
571
|
+
? `<button type="button" class="execution-activity-retained" data-execution-history-toggle="${esc(session.id)}" aria-expanded="true">${esc(t("graph.execution_collapse_older"))}</button>`
|
|
572
|
+
: ""}
|
|
573
|
+
</section>`;
|
|
574
|
+
}
|
|
575
|
+
|
|
465
576
|
function focusedGraph(focus, model, motionKind = "refresh") {
|
|
466
577
|
const parent = focus.parentId ? model.byId.get(focus.parentId) : null;
|
|
467
578
|
const children = graphChildren(focus, model);
|
|
579
|
+
const executionCount = (focus.executions || []).length;
|
|
468
580
|
const { ongoing, completed } = splitSubagents(children);
|
|
469
581
|
const completedExpanded = state.expandedCompletedSubagents.has(focus.id);
|
|
470
582
|
const shownChildren = completedExpanded ? [...ongoing, ...completed] : ongoing;
|
|
471
583
|
const metrics = workflowMetrics(focus, children);
|
|
472
584
|
const upstream = parent
|
|
473
|
-
? workflowCompactNode(parent, model, "upstream", parent.parentId ? "
|
|
585
|
+
? workflowCompactNode(parent, model, "upstream", parent.parentId ? t("graph.back_to_previous_ai") : t("graph.back_to_main_ai"))
|
|
474
586
|
: `<div class="agent-workflow-origin">
|
|
475
587
|
<span class="workflow-origin-icon">◎</span>
|
|
476
588
|
<span>
|
|
477
|
-
<b
|
|
478
|
-
<small
|
|
589
|
+
<b>${esc(t("graph.user_request"))}</b>
|
|
590
|
+
<small>${esc(t("graph.task_origin"))}</small>
|
|
479
591
|
</span>
|
|
480
592
|
<span class="agent-workflow-port output" data-workflow-port="upstream-output" aria-hidden="true">
|
|
481
593
|
</span>
|
|
@@ -483,8 +595,10 @@ window.LoadToAgentAppFactories.createGraphView = function createGraphView(contex
|
|
|
483
595
|
const ongoingRows = ongoing.length
|
|
484
596
|
? ongoing.map((child) => workflowCompactNode(child, model, "downstream", agentRoleLabel(child.agentRole))).join("")
|
|
485
597
|
: children.length
|
|
486
|
-
?
|
|
487
|
-
:
|
|
598
|
+
? `<div class="agent-workflow-empty current-clear"><b>${esc(t("graph.no_active_subagents"))}</b><span>${esc(t("graph.completed_available_below"))}</span></div>`
|
|
599
|
+
: executionCount
|
|
600
|
+
? ""
|
|
601
|
+
: `<div class="agent-workflow-empty">${esc(t("graph.no_delegated_tasks"))}</div>`;
|
|
488
602
|
const completedRows = completedExpanded
|
|
489
603
|
? `<div class="completed-subagent-list" data-completed-subagent-list>
|
|
490
604
|
${completed.map((child) => workflowCompactNode(child, model, "downstream", agentRoleLabel(child.agentRole))).join("")}
|
|
@@ -492,13 +606,13 @@ window.LoadToAgentAppFactories.createGraphView = function createGraphView(contex
|
|
|
492
606
|
: "";
|
|
493
607
|
const downstream = `${ongoingRows}${completedSubagentDisclosure(focus.id, completed, completedExpanded)}${completedRows}`;
|
|
494
608
|
const connectMotion = ["focus", "focus-back", "view"].includes(motionKind) ? "motion-connect" : "";
|
|
495
|
-
const childGroupPort = shownChildren.length
|
|
609
|
+
const childGroupPort = shownChildren.length || executionCount
|
|
496
610
|
? '<span class="agent-workflow-port input group-input" data-workflow-port="children-group-input" aria-hidden="true"></span>'
|
|
497
611
|
: "";
|
|
498
612
|
return `<div class="agent-workflow-canvas ${connectMotion}" data-workflow-focus="${esc(focus.id)}">
|
|
499
|
-
<svg class="agent-workflow-edges" role="img" aria-label="
|
|
500
|
-
<title
|
|
501
|
-
<desc
|
|
613
|
+
<svg class="agent-workflow-edges" role="img" aria-label="${esc(t("graph.workflow_aria"))}">
|
|
614
|
+
<title>${esc(t("graph.workflow_title"))}</title>
|
|
615
|
+
<desc>${esc(t("graph.workflow_description"))}</desc>
|
|
502
616
|
<defs><marker id="workflowArrow" markerWidth="8" markerHeight="8" refX="7" refY="4" orient="auto" markerUnits="strokeWidth">
|
|
503
617
|
<path d="M0,0 L8,4 L0,8 Z" fill="context-stroke"></path>
|
|
504
618
|
</marker></defs>
|
|
@@ -506,28 +620,30 @@ window.LoadToAgentAppFactories.createGraphView = function createGraphView(contex
|
|
|
506
620
|
</svg>
|
|
507
621
|
<div class="agent-workflow-grid">
|
|
508
622
|
<section class="agent-workflow-column upstream-column">
|
|
509
|
-
<header><b>${parent ? "
|
|
510
|
-
<span>${parent ? "
|
|
623
|
+
<header><b>${esc(parent ? t("graph.assigning_ai") : t("graph.starting_point"))}</b>
|
|
624
|
+
<span>${esc(parent ? t("graph.click_left_to_go_back") : t("graph.initial_user_task"))}</span>
|
|
511
625
|
</header>
|
|
512
626
|
<div class="agent-workflow-stack">${upstream}</div>
|
|
513
627
|
</section>
|
|
514
628
|
<section class="agent-workflow-column selected-column">
|
|
515
|
-
<header><b
|
|
629
|
+
<header><b>${esc(t("graph.selected_ai"))}</b><span>${esc(focus.parentId ? t("graph.delegated_helper_ai") : t("graph.main_ai_in_charge"))}</span></header>
|
|
516
630
|
<div class="agent-workflow-selected-stack">
|
|
517
631
|
<div class="agent-workflow-selected">
|
|
518
632
|
<span class="agent-workflow-port input" data-workflow-port="focus-input" aria-hidden="true"></span>
|
|
519
633
|
${graphNode(focus, { focus: true })}
|
|
520
634
|
</div>
|
|
521
635
|
${context.agentCommandComposer(focus)}
|
|
522
|
-
${shownChildren.length ? '<span class="agent-workflow-port output" data-workflow-port="focus-output" aria-hidden="true"></span>' : ""}
|
|
636
|
+
${shownChildren.length || executionCount ? '<span class="agent-workflow-port output" data-workflow-port="focus-output" aria-hidden="true"></span>' : ""}
|
|
523
637
|
</div>
|
|
524
638
|
</section>
|
|
525
639
|
<section class="agent-workflow-column downstream-column"
|
|
526
640
|
data-workflow-child-count="${children.length}" data-workflow-visible-child-count="${shownChildren.length}">
|
|
527
641
|
${childGroupPort}
|
|
528
|
-
<header><b
|
|
529
|
-
<span
|
|
642
|
+
<header><b>${esc(t("graph.observed_execution_units"))}</b>
|
|
643
|
+
<span>${esc(t("graph.execution_unit_summary", { subagents: children.length, executions: executionCount }))}</span>
|
|
530
644
|
</header>
|
|
645
|
+
${executionActivityPanel(focus)}
|
|
646
|
+
${children.length ? `<div class="subagent-execution-heading"><b>${esc(t("graph.subagent_sessions"))}</b><span>${esc(t("graph.subagent_visibility_summary", { ongoing: ongoing.length, completed: completed.length }))}</span></div>` : ""}
|
|
531
647
|
${workflowChildrenSummary(focus, children)}
|
|
532
648
|
<div class="agent-workflow-stack downstream-stack ${shownChildren.length > 3 ? "density-many" : ""}">${downstream}</div>
|
|
533
649
|
</section>
|
|
@@ -539,6 +655,6 @@ window.LoadToAgentAppFactories.createGraphView = function createGraphView(contex
|
|
|
539
655
|
return {
|
|
540
656
|
graphNode, compactGraphNode, providerFlowLane, workflowCompactNode, liveTmuxEntries, liveTmuxPaneCard, runtimeSeparatedOverview,
|
|
541
657
|
workflowMetrics, workflowChildrenSummary, splitSubagents, completedSubagentDisclosure, agentPathTaskName, communicationEndpoint,
|
|
542
|
-
workflowCommunicationPanel, focusedGraph,
|
|
658
|
+
workflowCommunicationPanel, executionActivityLabel, executionActivityStatus, executionActivityCard, executionActivityPanel, focusedGraph,
|
|
543
659
|
};
|
|
544
660
|
};
|