loadtoagent 1.1.0 → 1.3.2
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 +14 -5
- package/README.md +7 -0
- package/README.zh-CN.md +7 -0
- package/docs/PROVIDER-CONTRACTS.md +25 -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 +157 -38
- package/package.json +13 -4
- package/preload.js +13 -2
- package/renderer/app-agent-actions.js +125 -53
- package/renderer/app-bootstrap.js +54 -19
- package/renderer/app-dashboard.js +196 -55
- package/renderer/app-drawer-content.js +48 -44
- package/renderer/app-drawer-data.js +26 -13
- package/renderer/app-drawer.js +72 -54
- package/renderer/app-events-dialogs.js +146 -37
- package/renderer/app-events-filters.js +172 -13
- package/renderer/app-events-navigation.js +77 -30
- package/renderer/app-events-sessions.js +156 -9
- package/renderer/app-events.js +2 -1
- package/renderer/app-graph-model.js +20 -38
- package/renderer/app-graph-orchestration.js +15 -13
- package/renderer/app-graph-view.js +228 -113
- package/renderer/app-management.js +257 -0
- package/renderer/app-provider-visibility.js +124 -0
- package/renderer/app-quality.js +568 -0
- package/renderer/app-run-modal.js +104 -34
- package/renderer/app-runtime-overview.js +312 -0
- package/renderer/app-session-render.js +94 -37
- package/renderer/app-tmux-render.js +52 -44
- package/renderer/app.js +153 -75
- package/renderer/i18n-messages.js +867 -37
- package/renderer/i18n.js +104 -0
- package/renderer/index.html +154 -67
- package/renderer/shared.js +17 -0
- package/renderer/styles-agent-map.css +16 -10
- package/renderer/styles-cards.css +44 -18
- package/renderer/styles-collaboration.css +23 -23
- package/renderer/styles-components.css +122 -45
- package/renderer/styles-management.css +364 -0
- package/renderer/styles-onboarding.css +8 -8
- package/renderer/styles-overlays.css +13 -7
- package/renderer/styles-product.css +48 -40
- package/renderer/styles-quality.css +312 -0
- package/renderer/styles-readability.css +1040 -0
- package/renderer/styles-responsive-product.css +84 -12
- package/renderer/styles-responsive-runtime.css +25 -17
- package/renderer/styles-responsive-shell.css +44 -48
- package/renderer/styles-responsive-workflows.css +39 -5
- package/renderer/styles-run-composer.css +28 -23
- package/renderer/styles-runtime-overview.css +285 -0
- package/renderer/styles-settings.css +176 -16
- package/renderer/styles-terminal.css +374 -37
- package/renderer/styles-tmux.css +49 -49
- package/renderer/styles-workflow-map.css +362 -15
- package/renderer/styles-workflows.css +116 -49
- package/renderer/styles.css +34 -19
- package/renderer/terminal-agent.js +17 -13
- package/renderer/terminal-events.js +253 -37
- package/renderer/terminal-workbench.js +208 -89
- package/renderer/terminal.js +350 -35
- 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 +14 -10
- 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 -14
- package/src/macUpdateHelper.js +210 -0
- package/src/monitorWorker.js +65 -6
- package/src/platformPath.js +80 -0
- package/src/processMonitor.js +80 -22
- package/src/providerVisibilityStore.js +45 -0
- package/src/sessionIntelligence.js +261 -0
- package/src/terminalHost.js +453 -0
- package/src/terminalHostDaemon.js +61 -0
- package/src/terminalManager.js +218 -6
- 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("ui.waiting_for_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 || pane.dead) 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,64 +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 fallbackTmuxLanes = lanesFor(fallbackTmuxRoots);
|
|
277
|
-
const tmuxDisplayCount = tmuxEntries.length + fallbackTmuxRoots.length;
|
|
315
|
+
const tmuxLanes = lanesFor(tmuxRoots);
|
|
278
316
|
const standardHtml = standardLanes.length
|
|
279
317
|
? `<div class="agent-flow-overview">${standardLanes.map((item) => providerFlowLane(item.providerId, item.roots, model)).join("")}</div>`
|
|
280
|
-
:
|
|
281
|
-
const
|
|
282
|
-
|
|
283
|
-
? `${tmuxEntries.length ? `<div class="live-tmux-grid">${tmuxEntries.map(liveTmuxPaneCard).join("")}</div>` : ""}
|
|
284
|
-
${
|
|
285
|
-
fallbackTmuxLanes.length
|
|
286
|
-
? `<div class="agent-flow-overview live-tmux-fallback">
|
|
287
|
-
${fallbackTmuxLanes.map((item) => providerFlowLane(item.providerId, item.roots, model)).join("")}
|
|
288
|
-
</div>`
|
|
289
|
-
: ""
|
|
290
|
-
}`
|
|
291
|
-
: '<div class="runtime-segment-empty tmux"><b>TMUX에서 실행 중인 AI가 없습니다</b><span>TMUX AI 프로세스가 감지되면 일반 실행과 분리해 여기에 표시합니다.</span></div>';
|
|
292
|
-
return `<div class="agent-runtime-split" data-runtime-split="true">
|
|
293
|
-
<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">
|
|
294
321
|
<header>
|
|
295
322
|
<span class="runtime-segment-icon">▦</span>
|
|
296
|
-
<span><small
|
|
297
|
-
<strong>${
|
|
298
|
-
<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>
|
|
299
326
|
</header>
|
|
300
|
-
|
|
301
|
-
</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}
|
|
302
332
|
<section class="runtime-segment standard-runtime" data-runtime-segment="standard">
|
|
303
333
|
<header>
|
|
304
334
|
<span class="runtime-segment-icon">›_</span>
|
|
305
|
-
<span><small
|
|
306
|
-
<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>
|
|
307
337
|
</header>
|
|
308
338
|
${standardHtml}
|
|
309
339
|
</section>
|
|
@@ -341,24 +371,24 @@ window.LoadToAgentAppFactories.createGraphView = function createGraphView(contex
|
|
|
341
371
|
.join("");
|
|
342
372
|
const metrics = workflowMetrics(session, children);
|
|
343
373
|
const capacity = metrics.simultaneousCapacity > 0 ? metrics.simultaneousCapacity : "--";
|
|
344
|
-
const retained = metrics.retainedCount == null ? "
|
|
345
|
-
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");
|
|
346
376
|
return `<div class="agent-workflow-summary" data-collaboration-summary="true">
|
|
347
377
|
<div class="workflow-metric-grid">
|
|
348
378
|
<span data-collaboration-metric="created">
|
|
349
|
-
<small
|
|
379
|
+
<small>${esc(t("graph.created_in_task"))}</small><b>${esc(metrics.cumulativeCreated)}</b><em>${window.LoadToAgentI18n.t("ui.items")}</em>
|
|
350
380
|
</span>
|
|
351
381
|
<span data-collaboration-metric="capacity">
|
|
352
|
-
<small
|
|
382
|
+
<small>${esc(t("graph.simultaneous_capacity"))}</small><b>${esc(capacity)}</b><em>${capacity === "--" ? "" : window.LoadToAgentI18n.t("ui.items")}</em>
|
|
353
383
|
</span>
|
|
354
384
|
<span data-collaboration-metric="running">
|
|
355
|
-
<small
|
|
385
|
+
<small>${esc(t("graph.currently_running"))}</small><b>${esc(metrics.currentlyRunning)}</b><em>${window.LoadToAgentI18n.t("ui.items")}</em>
|
|
356
386
|
</span>
|
|
357
387
|
<span data-collaboration-metric="completed">
|
|
358
|
-
<small
|
|
388
|
+
<small>${esc(t("graph.completed_records"))}</small><b>${esc(metrics.completedRecords)}</b><em>${window.LoadToAgentI18n.t("ui.items")}</em>
|
|
359
389
|
</span>
|
|
360
390
|
</div>
|
|
361
|
-
<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>
|
|
362
392
|
<div class="workflow-summary-providers">${providers}</div>
|
|
363
393
|
</div>`;
|
|
364
394
|
}
|
|
@@ -379,10 +409,10 @@ window.LoadToAgentAppFactories.createGraphView = function createGraphView(contex
|
|
|
379
409
|
return `<div class="completed-subagent-disclosure ${expanded ? "expanded" : ""}" data-completed-subagent-section>
|
|
380
410
|
<button type="button" data-subagent-completed-toggle="${esc(ownerId)}" aria-expanded="${expanded ? "true" : "false"}">
|
|
381
411
|
<span class="completed-disclosure-icon">✓</span>
|
|
382
|
-
<span><b
|
|
383
|
-
<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>
|
|
384
414
|
</span>
|
|
385
|
-
<i>${expanded ? "
|
|
415
|
+
<i>${esc(expanded ? t("graph.collapse") : t("graph.expand"))}</i>
|
|
386
416
|
</button>
|
|
387
417
|
</div>`;
|
|
388
418
|
}
|
|
@@ -400,12 +430,12 @@ window.LoadToAgentAppFactories.createGraphView = function createGraphView(contex
|
|
|
400
430
|
|
|
401
431
|
function communicationEndpoint(value, owner, model) {
|
|
402
432
|
const path = String(value || "");
|
|
403
|
-
if (!path) return "
|
|
404
|
-
if (path === "Codex 런타임") return
|
|
405
|
-
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");
|
|
406
436
|
const taskName = agentPathTaskName(path);
|
|
407
437
|
const session = model.nodes.find((node) => node.agentPath === path || node.taskName === taskName);
|
|
408
|
-
if (session) return `${session.agentName || "
|
|
438
|
+
if (session) return `${session.agentName || t("graph.sub_ai")}${taskName ? ` · ${taskName}` : ""}`;
|
|
409
439
|
return taskName || path;
|
|
410
440
|
}
|
|
411
441
|
|
|
@@ -418,11 +448,11 @@ window.LoadToAgentAppFactories.createGraphView = function createGraphView(contex
|
|
|
418
448
|
return `<section class="agent-communication-panel empty" data-collaboration-communications="0">
|
|
419
449
|
<header>
|
|
420
450
|
<span>
|
|
421
|
-
<b
|
|
422
|
-
<small
|
|
451
|
+
<b>${esc(t("graph.communication_title"))}</b>
|
|
452
|
+
<small>${esc(t("graph.communication_short_description"))}</small>
|
|
423
453
|
</span>
|
|
424
454
|
</header>
|
|
425
|
-
<p
|
|
455
|
+
<p>${esc(t("graph.no_communication_events"))}</p>
|
|
426
456
|
</section>`;
|
|
427
457
|
}
|
|
428
458
|
const rows = events
|
|
@@ -430,32 +460,34 @@ window.LoadToAgentAppFactories.createGraphView = function createGraphView(contex
|
|
|
430
460
|
const text =
|
|
431
461
|
event.text ||
|
|
432
462
|
(event.protected
|
|
433
|
-
?
|
|
463
|
+
? t("graph.assigned_to_subagent", { task: event.taskName || t("graph.this_task") })
|
|
434
464
|
: event.kind === "started"
|
|
435
|
-
? "
|
|
436
|
-
: "
|
|
437
|
-
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")}` : "";
|
|
438
468
|
return `<article class="agent-communication-event ${esc(event.kind)}" data-communication-kind="${esc(event.kind)}">
|
|
439
469
|
<span class="communication-route">
|
|
440
470
|
<b>${esc(communicationEndpoint(event.from, owner, model))}</b><i>→</i>
|
|
441
471
|
<b>${esc(communicationEndpoint(event.to, owner, model))}</b>
|
|
442
472
|
</span>
|
|
443
473
|
<span class="communication-copy">
|
|
444
|
-
<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>
|
|
445
475
|
<strong>${esc(text)}</strong>
|
|
446
476
|
</span>
|
|
447
477
|
<time>${esc(timeOnly(event.timestamp))}</time>
|
|
448
478
|
</article>`;
|
|
449
479
|
})
|
|
450
480
|
.join("");
|
|
451
|
-
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 });
|
|
452
484
|
return `<section class="agent-communication-panel"
|
|
453
485
|
data-collaboration-communications="${events.length}"
|
|
454
486
|
data-collaboration-communications-total="${relevant.length}">
|
|
455
487
|
<header>
|
|
456
488
|
<span>
|
|
457
|
-
<b
|
|
458
|
-
<small
|
|
489
|
+
<b>${esc(t("graph.communication_title"))}</b>
|
|
490
|
+
<small>${esc(t("graph.communication_description"))}</small>
|
|
459
491
|
</span>
|
|
460
492
|
<em>${countLabel}</em>
|
|
461
493
|
</header>
|
|
@@ -463,20 +495,99 @@ window.LoadToAgentAppFactories.createGraphView = function createGraphView(contex
|
|
|
463
495
|
</section>`;
|
|
464
496
|
}
|
|
465
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
|
+
|
|
466
576
|
function focusedGraph(focus, model, motionKind = "refresh") {
|
|
467
577
|
const parent = focus.parentId ? model.byId.get(focus.parentId) : null;
|
|
468
578
|
const children = graphChildren(focus, model);
|
|
579
|
+
const executionCount = (focus.executions || []).length;
|
|
469
580
|
const { ongoing, completed } = splitSubagents(children);
|
|
470
581
|
const completedExpanded = state.expandedCompletedSubagents.has(focus.id);
|
|
471
582
|
const shownChildren = completedExpanded ? [...ongoing, ...completed] : ongoing;
|
|
472
583
|
const metrics = workflowMetrics(focus, children);
|
|
473
584
|
const upstream = parent
|
|
474
|
-
? 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"))
|
|
475
586
|
: `<div class="agent-workflow-origin">
|
|
476
587
|
<span class="workflow-origin-icon">◎</span>
|
|
477
588
|
<span>
|
|
478
|
-
<b
|
|
479
|
-
<small
|
|
589
|
+
<b>${esc(t("graph.user_request"))}</b>
|
|
590
|
+
<small>${esc(t("graph.task_origin"))}</small>
|
|
480
591
|
</span>
|
|
481
592
|
<span class="agent-workflow-port output" data-workflow-port="upstream-output" aria-hidden="true">
|
|
482
593
|
</span>
|
|
@@ -484,8 +595,10 @@ window.LoadToAgentAppFactories.createGraphView = function createGraphView(contex
|
|
|
484
595
|
const ongoingRows = ongoing.length
|
|
485
596
|
? ongoing.map((child) => workflowCompactNode(child, model, "downstream", agentRoleLabel(child.agentRole))).join("")
|
|
486
597
|
: children.length
|
|
487
|
-
?
|
|
488
|
-
:
|
|
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>`;
|
|
489
602
|
const completedRows = completedExpanded
|
|
490
603
|
? `<div class="completed-subagent-list" data-completed-subagent-list>
|
|
491
604
|
${completed.map((child) => workflowCompactNode(child, model, "downstream", agentRoleLabel(child.agentRole))).join("")}
|
|
@@ -493,13 +606,13 @@ window.LoadToAgentAppFactories.createGraphView = function createGraphView(contex
|
|
|
493
606
|
: "";
|
|
494
607
|
const downstream = `${ongoingRows}${completedSubagentDisclosure(focus.id, completed, completedExpanded)}${completedRows}`;
|
|
495
608
|
const connectMotion = ["focus", "focus-back", "view"].includes(motionKind) ? "motion-connect" : "";
|
|
496
|
-
const childGroupPort = shownChildren.length
|
|
609
|
+
const childGroupPort = shownChildren.length || executionCount
|
|
497
610
|
? '<span class="agent-workflow-port input group-input" data-workflow-port="children-group-input" aria-hidden="true"></span>'
|
|
498
611
|
: "";
|
|
499
612
|
return `<div class="agent-workflow-canvas ${connectMotion}" data-workflow-focus="${esc(focus.id)}">
|
|
500
|
-
<svg class="agent-workflow-edges" role="img" aria-label="
|
|
501
|
-
<title
|
|
502
|
-
<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>
|
|
503
616
|
<defs><marker id="workflowArrow" markerWidth="8" markerHeight="8" refX="7" refY="4" orient="auto" markerUnits="strokeWidth">
|
|
504
617
|
<path d="M0,0 L8,4 L0,8 Z" fill="context-stroke"></path>
|
|
505
618
|
</marker></defs>
|
|
@@ -507,28 +620,30 @@ window.LoadToAgentAppFactories.createGraphView = function createGraphView(contex
|
|
|
507
620
|
</svg>
|
|
508
621
|
<div class="agent-workflow-grid">
|
|
509
622
|
<section class="agent-workflow-column upstream-column">
|
|
510
|
-
<header><b>${parent ? "
|
|
511
|
-
<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>
|
|
512
625
|
</header>
|
|
513
626
|
<div class="agent-workflow-stack">${upstream}</div>
|
|
514
627
|
</section>
|
|
515
628
|
<section class="agent-workflow-column selected-column">
|
|
516
|
-
<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>
|
|
517
630
|
<div class="agent-workflow-selected-stack">
|
|
518
631
|
<div class="agent-workflow-selected">
|
|
519
632
|
<span class="agent-workflow-port input" data-workflow-port="focus-input" aria-hidden="true"></span>
|
|
520
633
|
${graphNode(focus, { focus: true })}
|
|
521
634
|
</div>
|
|
522
635
|
${context.agentCommandComposer(focus)}
|
|
523
|
-
${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>' : ""}
|
|
524
637
|
</div>
|
|
525
638
|
</section>
|
|
526
639
|
<section class="agent-workflow-column downstream-column"
|
|
527
640
|
data-workflow-child-count="${children.length}" data-workflow-visible-child-count="${shownChildren.length}">
|
|
528
641
|
${childGroupPort}
|
|
529
|
-
<header><b
|
|
530
|
-
<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>
|
|
531
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>` : ""}
|
|
532
647
|
${workflowChildrenSummary(focus, children)}
|
|
533
648
|
<div class="agent-workflow-stack downstream-stack ${shownChildren.length > 3 ? "density-many" : ""}">${downstream}</div>
|
|
534
649
|
</section>
|
|
@@ -540,6 +655,6 @@ window.LoadToAgentAppFactories.createGraphView = function createGraphView(contex
|
|
|
540
655
|
return {
|
|
541
656
|
graphNode, compactGraphNode, providerFlowLane, workflowCompactNode, liveTmuxEntries, liveTmuxPaneCard, runtimeSeparatedOverview,
|
|
542
657
|
workflowMetrics, workflowChildrenSummary, splitSubagents, completedSubagentDisclosure, agentPathTaskName, communicationEndpoint,
|
|
543
|
-
workflowCommunicationPanel, focusedGraph,
|
|
658
|
+
workflowCommunicationPanel, executionActivityLabel, executionActivityStatus, executionActivityCard, executionActivityPanel, focusedGraph,
|
|
544
659
|
};
|
|
545
660
|
};
|