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
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
window.LoadToAgentAppFactories = window.LoadToAgentAppFactories || {};
|
|
4
4
|
|
|
5
5
|
window.LoadToAgentAppFactories.createDashboard = function createDashboard(context = {}) {
|
|
6
|
+
const t = (key, params) => window.LoadToAgentI18n.t(key, params);
|
|
6
7
|
const {
|
|
7
8
|
$,
|
|
8
9
|
esc,
|
|
@@ -11,10 +12,20 @@ window.LoadToAgentAppFactories.createDashboard = function createDashboard(contex
|
|
|
11
12
|
state,
|
|
12
13
|
compact,
|
|
13
14
|
providerStyle,
|
|
15
|
+
visibleProviders = () => state.providers,
|
|
16
|
+
visibleSessions = () => ((state.snapshot && state.snapshot.sessions) || []),
|
|
17
|
+
isProviderVisible = () => true,
|
|
18
|
+
isRuntimeLoopSession = () => false,
|
|
14
19
|
} = context;
|
|
15
20
|
|
|
21
|
+
function displaySessions() {
|
|
22
|
+
return visibleSessions().filter((session) => (
|
|
23
|
+
typeof context.isRecentSession !== "function" || context.isRecentSession(session)
|
|
24
|
+
));
|
|
25
|
+
}
|
|
26
|
+
|
|
16
27
|
function renderProviderRail() {
|
|
17
|
-
$("#providerRail").innerHTML =
|
|
28
|
+
$("#providerRail").innerHTML = visibleProviders()
|
|
18
29
|
.map((provider) => {
|
|
19
30
|
const available = !!state.availability[provider.id];
|
|
20
31
|
return `<div class="provider-rail-item ${available ? "connected" : ""}" style="${providerStyle(provider.id)}">
|
|
@@ -27,34 +38,95 @@ window.LoadToAgentAppFactories.createDashboard = function createDashboard(contex
|
|
|
27
38
|
}
|
|
28
39
|
|
|
29
40
|
function isProjectlessSession(session) {
|
|
30
|
-
|
|
41
|
+
const cwd = session && (session.originCwd || session.cwd);
|
|
42
|
+
if (!cwd) return true;
|
|
31
43
|
if (typeof session.projectless === "boolean") return session.projectless;
|
|
32
|
-
const normalized = String(
|
|
44
|
+
const normalized = String(cwd).replace(/\\/g, "/").replace(/\/+$/, "");
|
|
33
45
|
return session.provider === "codex" && session.clientKind === "codex-desktop" && /(?:^|\/)Documents\/Codex\/\d{4}-\d{2}-\d{2}\/new-chat$/i.test(normalized);
|
|
34
46
|
}
|
|
35
47
|
|
|
48
|
+
function sessionOriginPath(session) {
|
|
49
|
+
return String(session && (session.originCwd || session.cwd) || "").trim();
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function normalizedProjectPath(value) {
|
|
53
|
+
return String(value || "").trim().replace(/\\/g, "/").replace(/\/+$/, "").toLocaleLowerCase();
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function projectContainsPath(projectPath, candidatePath) {
|
|
57
|
+
const project = normalizedProjectPath(projectPath);
|
|
58
|
+
const candidate = normalizedProjectPath(candidatePath);
|
|
59
|
+
return Boolean(project && candidate && (candidate === project || candidate.startsWith(`${project}/`)));
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function projectName(projectPath) {
|
|
63
|
+
const normalized = String(projectPath || "").replace(/\\/g, "/").replace(/\/+$/, "");
|
|
64
|
+
return normalized.split("/").filter(Boolean).pop() || t("workspace.unknown");
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function observedProjects() {
|
|
68
|
+
const projects = new Map();
|
|
69
|
+
const saved = state.workspaces.map((item) => ({ ...item, key: normalizedProjectPath(item.path) }));
|
|
70
|
+
saved.forEach((item) => projects.set(item.key, { path: item.path, name: item.name || projectName(item.path), saved: true, count: 0 }));
|
|
71
|
+
displaySessions().filter((session) => !session.parentId && !isProjectlessSession(session)).forEach((session) => {
|
|
72
|
+
const originPath = sessionOriginPath(session);
|
|
73
|
+
if (!originPath) return;
|
|
74
|
+
const owner = saved
|
|
75
|
+
.filter((item) => projectContainsPath(item.path, originPath))
|
|
76
|
+
.sort((a, b) => b.key.length - a.key.length)[0];
|
|
77
|
+
const path = owner ? owner.path : originPath;
|
|
78
|
+
const key = normalizedProjectPath(path);
|
|
79
|
+
const project = projects.get(key) || { path, name: projectName(path), saved: false, count: 0 };
|
|
80
|
+
project.count += 1;
|
|
81
|
+
project.lastActivityAt = !project.lastActivityAt || Date.parse(session.updatedAt || 0) > Date.parse(project.lastActivityAt || 0)
|
|
82
|
+
? session.updatedAt
|
|
83
|
+
: project.lastActivityAt;
|
|
84
|
+
projects.set(key, project);
|
|
85
|
+
});
|
|
86
|
+
const items = [...projects.values()];
|
|
87
|
+
const duplicateNames = new Map();
|
|
88
|
+
items.forEach((item) => duplicateNames.set(item.name.toLocaleLowerCase(), (duplicateNames.get(item.name.toLocaleLowerCase()) || 0) + 1));
|
|
89
|
+
items.forEach((item) => {
|
|
90
|
+
if (duplicateNames.get(item.name.toLocaleLowerCase()) < 2) return;
|
|
91
|
+
const parts = String(item.path).replace(/\\/g, "/").replace(/\/+$/, "").split("/").filter(Boolean);
|
|
92
|
+
item.name = parts.slice(-2).join("/") || item.name;
|
|
93
|
+
});
|
|
94
|
+
return items.sort((a, b) => Number(b.count || 0) - Number(a.count || 0)
|
|
95
|
+
|| Number(b.saved) - Number(a.saved)
|
|
96
|
+
|| String(a.name).localeCompare(String(b.name), uiLocale()));
|
|
97
|
+
}
|
|
98
|
+
|
|
36
99
|
function sessionWorkspaceLabel(session) {
|
|
37
|
-
return isProjectlessSession(session)
|
|
100
|
+
return isProjectlessSession(session)
|
|
101
|
+
? t("ui.no_project")
|
|
102
|
+
: (session && session.workspace) || projectName(sessionOriginPath(session));
|
|
38
103
|
}
|
|
39
104
|
|
|
40
105
|
function matchesWorkspaceFilter(session) {
|
|
41
106
|
if (state.workspace === "all") return true;
|
|
42
107
|
if (state.workspace === PROJECTLESS_WORKSPACE) return isProjectlessSession(session);
|
|
43
|
-
return (
|
|
44
|
-
!isProjectlessSession(session) &&
|
|
45
|
-
String((session && session.cwd) || "")
|
|
46
|
-
.toLowerCase()
|
|
47
|
-
.startsWith(state.workspace.toLowerCase())
|
|
48
|
-
);
|
|
108
|
+
return !isProjectlessSession(session) && projectContainsPath(state.workspace, sessionOriginPath(session));
|
|
49
109
|
}
|
|
50
110
|
|
|
51
111
|
function renderWorkspaces() {
|
|
52
|
-
const
|
|
53
|
-
const
|
|
54
|
-
|
|
112
|
+
const lists = [$("#workspaceList"), $("#mobileWorkspaceList")].filter(Boolean);
|
|
113
|
+
const rootSessions = displaySessions().filter((session) => !session.parentId);
|
|
114
|
+
const projects = observedProjects();
|
|
115
|
+
const projectlessCount = rootSessions.filter(isProjectlessSession).length;
|
|
116
|
+
const savedWorkspaceExists = state.workspace === "all"
|
|
117
|
+
|| (state.workspace === PROJECTLESS_WORKSPACE && projectlessCount > 0)
|
|
118
|
+
|| projects.some((project) => normalizedProjectPath(project.path) === normalizedProjectPath(state.workspace));
|
|
119
|
+
if (!savedWorkspaceExists) state.workspace = "all";
|
|
120
|
+
const projectButton = (item) => `<button type="button" class="workspace-item observed-project ${state.workspace === item.path ? "selected" : ""}"
|
|
121
|
+
data-workspace="${esc(item.path)}" title="${esc(item.path)}"
|
|
122
|
+
aria-label="${esc(t("project.filter_named", { name: item.name, count: item.count }))}"
|
|
123
|
+
aria-pressed="${state.workspace === item.path ? "true" : "false"}">
|
|
124
|
+
<strong>${esc(item.name)}</strong><small>${Number(item.count || 0)}</small>
|
|
125
|
+
</button>`;
|
|
126
|
+
const html =
|
|
55
127
|
`<button type="button" class="workspace-item ${state.workspace === "all" ? "selected" : ""}"
|
|
56
128
|
data-workspace="all" aria-pressed="${state.workspace === "all" ? "true" : "false"}">
|
|
57
|
-
<strong>${window.LoadToAgentI18n.t("
|
|
129
|
+
<strong>${window.LoadToAgentI18n.t("project.all")}</strong><small>${rootSessions.length}</small>
|
|
58
130
|
</button>` +
|
|
59
131
|
(projectlessCount
|
|
60
132
|
? `<button type="button" class="workspace-item projectless ${state.workspace === PROJECTLESS_WORKSPACE ? "selected" : ""}"
|
|
@@ -65,34 +137,39 @@ window.LoadToAgentAppFactories.createDashboard = function createDashboard(contex
|
|
|
65
137
|
<small>${projectlessCount}</small>
|
|
66
138
|
</button>`
|
|
67
139
|
: "") +
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
(item) => `<div class="workspace-row">
|
|
71
|
-
<button type="button" class="workspace-item ${state.workspace === item.path ? "selected" : ""}"
|
|
72
|
-
data-workspace="${esc(item.path)}" title="${esc(item.path)}"
|
|
73
|
-
aria-pressed="${state.workspace === item.path ? "true" : "false"}">
|
|
74
|
-
<strong>${esc(item.name)}</strong>
|
|
75
|
-
</button>
|
|
140
|
+
projects.map((item) => item.saved ? `<div class="workspace-row">
|
|
141
|
+
${projectButton(item)}
|
|
76
142
|
<button type="button" class="workspace-remove" data-remove-workspace="${esc(item.path)}"
|
|
77
|
-
aria-label="${esc(item.name)}
|
|
143
|
+
aria-label="${esc(t("workspace.remove_named", { name: item.name }))}"
|
|
78
144
|
title="${esc(window.LoadToAgentI18n.t("ui.remove_from_list"))}">×</button>
|
|
79
|
-
</div
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
145
|
+
</div>` : projectButton(item)).join("") +
|
|
146
|
+
(!projects.length && !projectlessCount ? `<div class="workspace-empty">${window.LoadToAgentI18n.t("project.empty")}</div>` : "");
|
|
147
|
+
lists.forEach((list) => { list.innerHTML = html; });
|
|
148
|
+
const selectedProject = projects.find((project) => normalizedProjectPath(project.path) === normalizedProjectPath(state.workspace));
|
|
149
|
+
const mobileSummary = $("#mobileWorkspaceSummary");
|
|
150
|
+
if (mobileSummary) mobileSummary.textContent = state.workspace === "all"
|
|
151
|
+
? t("project.all")
|
|
152
|
+
: state.workspace === PROJECTLESS_WORKSPACE
|
|
153
|
+
? t("ui.no_project")
|
|
154
|
+
: selectedProject?.name || projectName(state.workspace);
|
|
83
155
|
}
|
|
84
156
|
|
|
85
157
|
function renderGlobalStats() {
|
|
86
|
-
const
|
|
87
|
-
const
|
|
158
|
+
const sessions = displaySessions();
|
|
159
|
+
const totals = {
|
|
160
|
+
active: sessions.filter((session) => session.status === "running" || session.status === "starting").length,
|
|
161
|
+
waiting: sessions.filter((session) => context.matchesManagementFilter?.(session, "attention")).length,
|
|
162
|
+
usage: { total: sessions.reduce((sum, session) => sum + Number(session.usage && session.usage.total || 0), 0) },
|
|
163
|
+
};
|
|
88
164
|
const rootCount = sessions.filter((session) => !session.parentId).length;
|
|
89
|
-
const
|
|
165
|
+
const criticalCount = sessions.filter((session) => context.matchesManagementFilter?.(session, "critical")).length;
|
|
166
|
+
const riskCount = sessions.filter((session) => context.matchesManagementFilter?.(session, "warning")).length;
|
|
90
167
|
const items = [
|
|
91
168
|
[window.LoadToAgentI18n.t("ui.all_tasks"), rootCount, window.LoadToAgentI18n.t("ui.items"), ""],
|
|
92
169
|
[window.LoadToAgentI18n.t("ui.ai_working_now"), totals.active || 0, window.LoadToAgentI18n.t("ui.items"), "live"],
|
|
93
|
-
[window.LoadToAgentI18n.t("
|
|
94
|
-
[window.LoadToAgentI18n.t("
|
|
95
|
-
[window.LoadToAgentI18n.t("
|
|
170
|
+
[window.LoadToAgentI18n.t("management.action_required"), totals.waiting || 0, window.LoadToAgentI18n.t("ui.items"), "alert"],
|
|
171
|
+
[window.LoadToAgentI18n.t("management.health.critical"), criticalCount, window.LoadToAgentI18n.t("ui.items"), "critical"],
|
|
172
|
+
[window.LoadToAgentI18n.t("management.risk_total"), riskCount, window.LoadToAgentI18n.t("ui.items"), "warning"],
|
|
96
173
|
];
|
|
97
174
|
$("#globalStats").innerHTML = items
|
|
98
175
|
.map(
|
|
@@ -104,9 +181,46 @@ window.LoadToAgentAppFactories.createDashboard = function createDashboard(contex
|
|
|
104
181
|
)
|
|
105
182
|
.join("");
|
|
106
183
|
$("#navAllCount").textContent = rootCount;
|
|
107
|
-
|
|
108
|
-
$("#
|
|
109
|
-
|
|
184
|
+
const activeRootCount = sessions.filter((session) => !session.parentId && ["running", "starting"].includes(session.status)).length;
|
|
185
|
+
$("#navActiveCount").textContent = activeRootCount;
|
|
186
|
+
const reviewCount = sessions.filter((session) => context.needsManagementReview?.(session)).length;
|
|
187
|
+
$("#navWaitingCount").textContent = reviewCount;
|
|
188
|
+
const scheduledCount = (state.snapshot?.automations || [])
|
|
189
|
+
.filter((item) => isProviderVisible(item.provider || "codex")).length;
|
|
190
|
+
const loopCount = sessions.filter(isRuntimeLoopSession).length;
|
|
191
|
+
$("#navRuntimeCount").textContent = scheduledCount + loopCount;
|
|
192
|
+
const tmuxSessionCount = Number(state.snapshot?.tmux?.summary?.sessions || 0);
|
|
193
|
+
$("#navTmuxCount").textContent = tmuxSessionCount;
|
|
194
|
+
$("#advancedToolsCount").textContent = scheduledCount + loopCount + Number($("#navTerminalCount").textContent || 0) + tmuxSessionCount;
|
|
195
|
+
const navCounts = {
|
|
196
|
+
all: rootCount,
|
|
197
|
+
active: activeRootCount,
|
|
198
|
+
waiting: reviewCount,
|
|
199
|
+
runtime: scheduledCount + loopCount,
|
|
200
|
+
terminal: Number($("#navTerminalCount").textContent || 0),
|
|
201
|
+
tmux: tmuxSessionCount,
|
|
202
|
+
};
|
|
203
|
+
document.querySelectorAll(".nav-item[data-view]").forEach((button) => {
|
|
204
|
+
const key = {
|
|
205
|
+
all: "app.nav.home", active: "app.nav.active", waiting: "app.nav.needs_review", runtime: "app.nav.runtime",
|
|
206
|
+
terminal: "app.nav.session_terminal", tmux: "app.nav.tmux", settings: "app.nav.settings",
|
|
207
|
+
}[button.dataset.view];
|
|
208
|
+
const label = t(key);
|
|
209
|
+
const count = navCounts[button.dataset.view];
|
|
210
|
+
const unitKey = { all: "tasks", active: "tasks", waiting: "items", runtime: "runs", terminal: "sessions", tmux: "sessions" }[button.dataset.view];
|
|
211
|
+
const unit = unitKey ? t(`quality.unit.${unitKey}`) : "";
|
|
212
|
+
const accessibleLabel = Number.isFinite(count) ? t("quality.nav_count_detailed", { label, count, unit }) : label;
|
|
213
|
+
button.setAttribute("aria-label", accessibleLabel);
|
|
214
|
+
button.setAttribute("title", accessibleLabel);
|
|
215
|
+
});
|
|
216
|
+
const advancedCount = scheduledCount + loopCount + Number($("#navTerminalCount").textContent || 0) + tmuxSessionCount;
|
|
217
|
+
$("#advancedToolsNav")?.querySelector("summary")?.setAttribute("aria-label", t("quality.nav_count_detailed", {
|
|
218
|
+
label: t("management.advanced_tools"), count: advancedCount, unit: t("quality.unit.items"),
|
|
219
|
+
}));
|
|
220
|
+
const tmuxShortcut = $("#openTmuxFromAgentWork");
|
|
221
|
+
$("#agentWorkTmuxCount").textContent = tmuxSessionCount;
|
|
222
|
+
tmuxShortcut.dataset.i18nParams = JSON.stringify({ count: tmuxSessionCount });
|
|
223
|
+
tmuxShortcut.setAttribute("aria-label", t("graph.open_tmux_workspace_count", { count: tmuxSessionCount }));
|
|
110
224
|
}
|
|
111
225
|
|
|
112
226
|
function formatBytes(value) {
|
|
@@ -159,7 +273,7 @@ window.LoadToAgentAppFactories.createDashboard = function createDashboard(contex
|
|
|
159
273
|
],
|
|
160
274
|
downloaded: [
|
|
161
275
|
"✓", window.LoadToAgentI18n.t("ui.ready_to_install"), window.LoadToAgentI18n.t("ui.the_update_file_is_ready"),
|
|
162
|
-
window.LoadToAgentI18n.t("
|
|
276
|
+
window.LoadToAgentI18n.t("settings.update.auto_install_restart"),
|
|
163
277
|
],
|
|
164
278
|
error: [
|
|
165
279
|
"!", window.LoadToAgentI18n.t("ui.check_failed"), window.LoadToAgentI18n.t("ui.could_not_check_for_updates"),
|
|
@@ -202,7 +316,7 @@ window.LoadToAgentAppFactories.createDashboard = function createDashboard(contex
|
|
|
202
316
|
install.textContent = downloading
|
|
203
317
|
? window.LoadToAgentI18n.t("ui.downloading_2")
|
|
204
318
|
: downloaded
|
|
205
|
-
? window.LoadToAgentI18n.t("ui.
|
|
319
|
+
? `${window.LoadToAgentI18n.t("settings.update.download")} · ${window.LoadToAgentI18n.t("ui.restart")}`
|
|
206
320
|
: window.LoadToAgentI18n.t("settings.update.download");
|
|
207
321
|
const progress = $("#updateProgress");
|
|
208
322
|
progress.classList.toggle("hidden", !downloading && !downloaded);
|
|
@@ -210,11 +324,13 @@ window.LoadToAgentAppFactories.createDashboard = function createDashboard(contex
|
|
|
210
324
|
$("#updateProgressBar").style.width = `${Math.max(0, Math.min(100, Number(update.progress || 0)))}%`;
|
|
211
325
|
$(".update-progress-track").setAttribute("aria-valuenow", String(Math.max(0, Math.min(100, Number(update.progress || 0)))));
|
|
212
326
|
$("#updateProgressBytes").textContent = downloaded
|
|
213
|
-
? `${formatBytes(update.totalBytes || update.downloadedBytes)} ·
|
|
327
|
+
? `${formatBytes(update.totalBytes || update.downloadedBytes)} · ${window.LoadToAgentI18n.t("settings.update.file_verified")}`
|
|
214
328
|
: `${formatBytes(update.downloadedBytes)} / ${update.totalBytes ? formatBytes(update.totalBytes) : window.LoadToAgentI18n.t("ui.checking_size")}`;
|
|
215
329
|
const error = $("#updateError");
|
|
216
330
|
error.classList.toggle("hidden", !update.error);
|
|
217
|
-
error.textContent = update.error
|
|
331
|
+
error.textContent = update.error
|
|
332
|
+
? window.LoadToAgentI18n.errorText(update.error, "ui.could_not_check_for_updates")
|
|
333
|
+
: "";
|
|
218
334
|
const notes = $("#releaseNotes");
|
|
219
335
|
notes.classList.toggle("hidden", !update.latestVersion);
|
|
220
336
|
$("#releaseNotesText").textContent =
|
|
@@ -231,16 +347,20 @@ window.LoadToAgentAppFactories.createDashboard = function createDashboard(contex
|
|
|
231
347
|
function renderProviderOverview() {
|
|
232
348
|
pruneProviderFilters();
|
|
233
349
|
const summaries = (state.snapshot && state.snapshot.summary && state.snapshot.summary.providers) || state.providers;
|
|
234
|
-
const sessions = (
|
|
235
|
-
|
|
236
|
-
|
|
350
|
+
const sessions = displaySessions();
|
|
351
|
+
const visibleSummaries = summaries.filter((provider) => isProviderVisible(provider.id));
|
|
352
|
+
const overviewTabStopId = state.providerFilters.size ? [...state.providerFilters][0] : visibleSummaries[0]?.id;
|
|
353
|
+
$("#providerOverview").innerHTML = visibleSummaries
|
|
354
|
+
.map((provider, index) => {
|
|
237
355
|
const rootCount = sessions.filter((session) => session.provider === provider.id && !session.parentId).length;
|
|
238
356
|
const selected = state.providerFilters.has(provider.id);
|
|
357
|
+
const tabStop = provider.id === overviewTabStopId;
|
|
239
358
|
return `<button type="button" class="provider-overview-card ${selected ? "selected" : ""}"
|
|
240
359
|
data-provider-card="${esc(provider.id)}"
|
|
241
360
|
data-motion-key="provider:${esc(provider.id)}"
|
|
242
361
|
data-motion-value="${provider.active || 0}:${rootCount}:${(provider.usage && provider.usage.total) || 0}"
|
|
243
362
|
style="${providerStyle(provider.id)}"
|
|
363
|
+
tabindex="${tabStop ? "0" : "-1"}"
|
|
244
364
|
aria-pressed="${selected ? "true" : "false"}">
|
|
245
365
|
<div class="poc-head">
|
|
246
366
|
<span class="provider-mark">${esc(provider.mark)}</span>
|
|
@@ -263,7 +383,7 @@ window.LoadToAgentAppFactories.createDashboard = function createDashboard(contex
|
|
|
263
383
|
}
|
|
264
384
|
|
|
265
385
|
function pruneProviderFilters() {
|
|
266
|
-
const valid = new Set(
|
|
386
|
+
const valid = new Set(visibleProviders().map((provider) => provider.id));
|
|
267
387
|
for (const id of [...state.providerFilters]) if (!valid.has(id)) state.providerFilters.delete(id);
|
|
268
388
|
if (valid.size > 0 && state.providerFilters.size === valid.size) state.providerFilters.clear();
|
|
269
389
|
}
|
|
@@ -273,28 +393,29 @@ window.LoadToAgentAppFactories.createDashboard = function createDashboard(contex
|
|
|
273
393
|
if (providerId === "all") state.providerFilters.clear();
|
|
274
394
|
else if (state.providerFilters.has(providerId)) state.providerFilters.delete(providerId);
|
|
275
395
|
else state.providerFilters.add(providerId);
|
|
276
|
-
if (
|
|
396
|
+
if (visibleProviders().length > 0 && state.providerFilters.size === visibleProviders().length) state.providerFilters.clear();
|
|
277
397
|
}
|
|
278
398
|
|
|
279
399
|
function renderProviderFilter() {
|
|
280
400
|
pruneProviderFilters();
|
|
281
401
|
const allSelected = state.providerFilters.size === 0;
|
|
402
|
+
const tabStopId = allSelected ? "all" : [...state.providerFilters][0];
|
|
282
403
|
const button = (id, label, mark = "") => {
|
|
283
404
|
const selected = id === "all" ? allSelected : state.providerFilters.has(id);
|
|
284
405
|
return `<button type="button" class="provider-filter-chip ${selected ? "selected" : ""}"
|
|
285
|
-
data-provider-filter="${esc(id)}" aria-pressed="${selected ? "true" : "false"}">
|
|
406
|
+
data-provider-filter="${esc(id)}" tabindex="${id === tabStopId ? "0" : "-1"}" aria-pressed="${selected ? "true" : "false"}">
|
|
286
407
|
<i class="provider-filter-check" aria-hidden="true">✓</i>
|
|
287
408
|
${mark ? `<span class="provider-filter-mark" aria-hidden="true">${esc(mark)}</span>` : ""}<b>${esc(label)}</b>
|
|
288
409
|
</button>`;
|
|
289
410
|
};
|
|
290
411
|
$("#providerFilter").innerHTML =
|
|
291
412
|
button("all", window.LoadToAgentI18n.t("ui.all_ai")) +
|
|
292
|
-
|
|
413
|
+
visibleProviders().map((provider) => button(provider.id, provider.label, provider.mark)).join("");
|
|
293
414
|
}
|
|
294
415
|
|
|
295
416
|
function announceProviderFilter() {
|
|
296
417
|
const labels = state.providerFilters.size
|
|
297
|
-
?
|
|
418
|
+
? visibleProviders().filter((provider) => state.providerFilters.has(provider.id)).map((provider) => provider.label).join(", ")
|
|
298
419
|
: window.LoadToAgentI18n.t("ui.all_ai");
|
|
299
420
|
$("#providerFilterStatus").textContent = window.LoadToAgentI18n.t("filter.result_summary", {
|
|
300
421
|
providers: labels,
|
|
@@ -303,16 +424,16 @@ window.LoadToAgentAppFactories.createDashboard = function createDashboard(contex
|
|
|
303
424
|
}
|
|
304
425
|
|
|
305
426
|
function filteredSessions() {
|
|
306
|
-
const allSessions =
|
|
427
|
+
const allSessions = displaySessions();
|
|
307
428
|
let sessions = state.view === "waiting" ? allSessions : allSessions.filter((session) => !session.parentId);
|
|
308
429
|
if (state.view === "active") sessions = sessions.filter((session) => session.status === "running" || session.status === "starting");
|
|
309
|
-
if (state.view === "waiting") sessions = sessions.filter((session) => session
|
|
430
|
+
if (state.view === "waiting") sessions = sessions.filter((session) => context.needsManagementReview?.(session));
|
|
310
431
|
if (state.providerFilters.size) sessions = sessions.filter((session) => state.providerFilters.has(session.provider));
|
|
311
432
|
sessions = sessions.filter(matchesWorkspaceFilter);
|
|
312
|
-
const query = state.search.trim().toLowerCase();
|
|
433
|
+
const query = state.search.replace(/\s+/g, " ").trim().toLowerCase();
|
|
313
434
|
if (query) {
|
|
314
435
|
sessions = sessions.filter((session) =>
|
|
315
|
-
[session.title, session.model, session.cwd, session.agentName, ...(session.messages || []).slice(-12).map((item) => item.text)]
|
|
436
|
+
[session.title, session.model, session.originCwd, session.cwd, session.workspace, session.agentName, ...(session.messages || []).slice(-12).map((item) => item.text)]
|
|
316
437
|
.join(" ")
|
|
317
438
|
.toLowerCase()
|
|
318
439
|
.includes(query),
|
|
@@ -330,13 +451,13 @@ window.LoadToAgentAppFactories.createDashboard = function createDashboard(contex
|
|
|
330
451
|
}
|
|
331
452
|
|
|
332
453
|
function graphFilteredSessions() {
|
|
333
|
-
let sessions =
|
|
454
|
+
let sessions = displaySessions();
|
|
334
455
|
if (state.providerFilters.size) sessions = sessions.filter((session) => state.providerFilters.has(session.provider));
|
|
335
456
|
sessions = sessions.filter(matchesWorkspaceFilter);
|
|
336
|
-
const query = state.search.trim().toLowerCase();
|
|
457
|
+
const query = state.search.replace(/\s+/g, " ").trim().toLowerCase();
|
|
337
458
|
if (query)
|
|
338
459
|
sessions = sessions.filter((session) =>
|
|
339
|
-
[session.title, session.model, session.cwd, session.agentName, session.agentRole, ...(session.messages || []).map((item) => item.text)]
|
|
460
|
+
[session.title, session.model, session.originCwd, session.cwd, session.workspace, session.agentName, session.agentRole, ...(session.messages || []).map((item) => item.text)]
|
|
340
461
|
.join(" ")
|
|
341
462
|
.toLowerCase()
|
|
342
463
|
.includes(query),
|
|
@@ -344,9 +465,28 @@ window.LoadToAgentAppFactories.createDashboard = function createDashboard(contex
|
|
|
344
465
|
return sessions;
|
|
345
466
|
}
|
|
346
467
|
|
|
468
|
+
function renderProviderVisibilitySettings() {
|
|
469
|
+
const list = $("#providerVisibilityList");
|
|
470
|
+
if (!list) return;
|
|
471
|
+
list.innerHTML = state.providers.map((provider) => {
|
|
472
|
+
const visible = isProviderVisible(provider.id);
|
|
473
|
+
const status = window.LoadToAgentI18n.t(visible ? "settings.providers.visible" : "settings.providers.hidden");
|
|
474
|
+
return `<label class="provider-visibility-option ${visible ? "enabled" : "disabled"}" style="${providerStyle(provider.id)}">
|
|
475
|
+
<span class="provider-mark" aria-hidden="true">${esc(provider.mark)}</span>
|
|
476
|
+
<span class="provider-visibility-name"><b>${esc(provider.label)}</b><small>${esc(provider.company)}</small></span>
|
|
477
|
+
<span class="provider-visibility-status">${esc(status)}</span>
|
|
478
|
+
<input type="checkbox" data-provider-visibility="${esc(provider.id)}" ${visible ? "checked" : ""}
|
|
479
|
+
aria-label="${esc(`${provider.label} ${status}`)}">
|
|
480
|
+
<span class="provider-toggle" aria-hidden="true"><i></i></span>
|
|
481
|
+
</label>`;
|
|
482
|
+
}).join("");
|
|
483
|
+
}
|
|
484
|
+
|
|
347
485
|
return {
|
|
348
486
|
renderProviderRail,
|
|
349
487
|
isProjectlessSession,
|
|
488
|
+
sessionOriginPath,
|
|
489
|
+
observedProjects,
|
|
350
490
|
sessionWorkspaceLabel,
|
|
351
491
|
matchesWorkspaceFilter,
|
|
352
492
|
renderWorkspaces,
|
|
@@ -361,5 +501,6 @@ window.LoadToAgentAppFactories.createDashboard = function createDashboard(contex
|
|
|
361
501
|
announceProviderFilter,
|
|
362
502
|
filteredSessions,
|
|
363
503
|
graphFilteredSessions,
|
|
504
|
+
renderProviderVisibilitySettings,
|
|
364
505
|
};
|
|
365
506
|
};
|
|
@@ -4,35 +4,39 @@ window.LoadToAgentAppFactories = window.LoadToAgentAppFactories || {};
|
|
|
4
4
|
|
|
5
5
|
window.LoadToAgentAppFactories.createDrawerContent = function createDrawerContent(context = {}) {
|
|
6
6
|
const { esc, uiLocale, state, messageContentHtml, compact, fullNumber, timeOnly, providerInfo, statusIcon, agentPathTaskName, snapshotSession } = context;
|
|
7
|
+
const t = (key, params) => window.LoadToAgentI18n.t(key, params);
|
|
7
8
|
|
|
8
9
|
function chatHtml(session, options = {}) {
|
|
9
10
|
const messages = session.messages || [];
|
|
10
|
-
if (!messages.length) return
|
|
11
|
-
const userLabel = options.userLabel || "
|
|
11
|
+
if (!messages.length) return `<div class="empty-state"><h3>${esc(t("drawer.no_conversation"))}</h3></div>`;
|
|
12
|
+
const userLabel = options.userLabel || t("drawer.user");
|
|
12
13
|
const assistantLabel = options.assistantLabel || providerInfo(session.provider).label;
|
|
13
|
-
const conversationLabel = options.conversationLabel || "
|
|
14
|
+
const conversationLabel = options.conversationLabel || t("drawer.conversation");
|
|
14
15
|
const conversation = messages.filter((message) => message.role === "user" || message.role === "assistant");
|
|
15
16
|
const activities = messages.filter((message) => message.role !== "user" && message.role !== "assistant");
|
|
16
17
|
const omitted = Number(session.omittedMessages || 0);
|
|
17
18
|
const notice =
|
|
18
19
|
omitted || session.truncated
|
|
19
|
-
? `<div class="chat-truncated"
|
|
20
|
+
? `<div class="chat-truncated">${esc(t("drawer.recent_history"))}${omitted ? ` · ${esc(t("drawer.messages_omitted", { count: omitted.toLocaleString(uiLocale()) }))}` : ""}</div>`
|
|
20
21
|
: "";
|
|
21
22
|
const statusLabels = {
|
|
22
|
-
started: "
|
|
23
|
-
done:
|
|
23
|
+
started: t("ui.working"), running: t("ui.working"),
|
|
24
|
+
done: t("ui.completed"), completed: t("ui.completed"), failed: t("drawer.failed"),
|
|
24
25
|
};
|
|
25
26
|
const statusLabel = (value) => statusLabels[value] || value || "";
|
|
26
27
|
const rows = conversation
|
|
27
28
|
.map((message) => {
|
|
28
29
|
const role = message.role === "assistant" ? "assistant" : message.role === "tool" ? "tool" : message.role === "system" ? "system" : "user";
|
|
30
|
+
const renderedMessage = role === "tool" || role === "system"
|
|
31
|
+
? { ...message, text: window.LoadToAgentI18n.observedText(message.text) }
|
|
32
|
+
: message;
|
|
29
33
|
const label =
|
|
30
34
|
role === "assistant"
|
|
31
35
|
? assistantLabel
|
|
32
36
|
: role === "tool"
|
|
33
|
-
? message.title || "
|
|
37
|
+
? window.LoadToAgentI18n.observedText(message.title || t("session.tool"))
|
|
34
38
|
: message.role === "system"
|
|
35
|
-
? "
|
|
39
|
+
? t("drawer.system")
|
|
36
40
|
: userLabel;
|
|
37
41
|
const avatar = role === "assistant" ? providerInfo(session.provider).mark : role === "tool" ? "⌘" : role === "system" ? "i" : "ME";
|
|
38
42
|
const fullTime = new Date(message.timestamp).toLocaleString(uiLocale());
|
|
@@ -43,44 +47,44 @@ window.LoadToAgentAppFactories.createDrawerContent = function createDrawerConten
|
|
|
43
47
|
<b>${esc(label)}</b>
|
|
44
48
|
<span title="${esc(fullTime)}">${esc(timeOnly(message.timestamp))}</span>
|
|
45
49
|
${message.status ? `<span>${esc(statusLabel(message.status))}</span>` : ""}
|
|
46
|
-
</div>${messageContentHtml(
|
|
50
|
+
</div>${messageContentHtml(renderedMessage, session.id)}</div>
|
|
47
51
|
</div>`;
|
|
48
52
|
})
|
|
49
53
|
.join("");
|
|
50
54
|
const activityHtml = activities.length
|
|
51
|
-
? `<details class="chat-activities">
|
|
52
|
-
<summary
|
|
55
|
+
? `<details class="chat-activities" data-disclosure-key="${esc(`drawer:${session.id}:activities`)}">
|
|
56
|
+
<summary>${esc(t("drawer.activities_view", { count: activities.length }))}</summary>
|
|
53
57
|
<div>${activities
|
|
54
58
|
.map(
|
|
55
59
|
(message) => `<article>
|
|
56
60
|
<header>
|
|
57
|
-
<b>${esc(message.title || (message.role === "tool" ? "
|
|
61
|
+
<b>${esc(window.LoadToAgentI18n.observedText(message.title || (message.role === "tool" ? t("drawer.tool_execution") : t("drawer.system"))))}</b>
|
|
58
62
|
<span>${esc(statusLabel(message.status))} · ${esc(timeOnly(message.timestamp))}</span>
|
|
59
|
-
</header>${messageContentHtml(message)}</article>`,
|
|
63
|
+
</header>${messageContentHtml({ ...message, text: window.LoadToAgentI18n.observedText(message.text) }, session.id)}</article>`,
|
|
60
64
|
)
|
|
61
65
|
.join("")}</div>
|
|
62
66
|
</details>`
|
|
63
67
|
: "";
|
|
64
|
-
const emptyConversation = conversation.length ? "" :
|
|
68
|
+
const emptyConversation = conversation.length ? "" : `<div class="empty-state compact"><h3>${esc(t("drawer.no_user_ai_conversation"))}</h3></div>`;
|
|
65
69
|
return `${notice}<div class="chat-history-head">
|
|
66
|
-
<span>${esc(conversationLabel
|
|
67
|
-
<button type="button" data-scroll-latest
|
|
70
|
+
<span>${esc(t("drawer.conversation_summary", { label: conversationLabel, count: conversation.length, activities: activities.length ? ` · ${t("drawer.activities", { count: activities.length })}` : "" }))}</span>
|
|
71
|
+
<button type="button" data-scroll-latest>${esc(t("drawer.latest_conversation"))} ↓</button>
|
|
68
72
|
</div>
|
|
69
|
-
<div class="chat-list">${rows}${emptyConversation}${activityHtml}<div class="chat-latest-anchor" aria-label="
|
|
73
|
+
<div class="chat-list">${rows}${emptyConversation}${activityHtml}<div class="chat-latest-anchor" aria-label="${esc(t("drawer.latest_conversation"))}">
|
|
70
74
|
</div>
|
|
71
75
|
</div>`;
|
|
72
76
|
}
|
|
73
77
|
|
|
74
78
|
function lifecycleHtml(session) {
|
|
75
79
|
const events = session.lifecycle || [];
|
|
76
|
-
if (!events.length) return
|
|
80
|
+
if (!events.length) return `<div class="empty-state"><h3>${esc(t("drawer.no_lifecycle"))}</h3></div>`;
|
|
77
81
|
return `<div class="lifecycle-list">${events
|
|
78
82
|
.map(
|
|
79
83
|
(event) => `<div class="lifecycle-event ${esc(event.status)}">
|
|
80
84
|
<span class="life-node">${statusIcon(event.type)}</span>
|
|
81
85
|
<div class="life-copy">
|
|
82
|
-
<b>${esc(event.label)}</b>
|
|
83
|
-
<span>${esc(event.detail || event.type)}</span>
|
|
86
|
+
<b>${esc(window.LoadToAgentI18n.observedText(event.label))}</b>
|
|
87
|
+
<span>${esc(window.LoadToAgentI18n.observedText(event.detail || event.type))}</span>
|
|
84
88
|
</div>
|
|
85
89
|
<time>${esc(timeOnly(event.timestamp))}</time>
|
|
86
90
|
</div>`,
|
|
@@ -94,14 +98,14 @@ window.LoadToAgentAppFactories.createDrawerContent = function createDrawerConten
|
|
|
94
98
|
const context = session.context || {};
|
|
95
99
|
const sourceLabel =
|
|
96
100
|
context.source === "session"
|
|
97
|
-
? "
|
|
101
|
+
? t("drawer.context_source_session")
|
|
98
102
|
: context.source === "model-catalog"
|
|
99
|
-
? "
|
|
100
|
-
: "
|
|
103
|
+
? t("drawer.context_source_catalog")
|
|
104
|
+
: t("session.context_size_unknown");
|
|
101
105
|
return `<div class="token-hero" style="--drawer-provider:${providerInfo(session.provider).accent}">
|
|
102
106
|
<div class="token-hero-head">
|
|
103
|
-
<span
|
|
104
|
-
<b>${context.window ? `${fullNumber(context.used)} / ${fullNumber(context.window)}
|
|
107
|
+
<span>${esc(t("session.context_usage"))}</span>
|
|
108
|
+
<b>${esc(t("drawer.tokens", { count: context.window ? `${fullNumber(context.used)} / ${fullNumber(context.window)}` : fullNumber(context.used) }))}</b>
|
|
105
109
|
</div>
|
|
106
110
|
<div class="big-context"><span style="width:${Math.min(100, context.percent || 0)}%"></span></div>
|
|
107
111
|
<div class="context-scale">
|
|
@@ -110,15 +114,15 @@ window.LoadToAgentAppFactories.createDrawerContent = function createDrawerConten
|
|
|
110
114
|
</div>
|
|
111
115
|
</div>
|
|
112
116
|
<div class="token-grid">
|
|
113
|
-
<div class="token-tile"><span
|
|
114
|
-
<div class="token-tile"><span
|
|
115
|
-
<div class="token-tile"><span
|
|
116
|
-
<div class="token-tile"><span
|
|
117
|
-
<div class="token-tile"><span
|
|
118
|
-
<div class="token-tile"><span
|
|
119
|
-
<div class="token-tile"><span
|
|
120
|
-
<div class="token-tile"><span
|
|
121
|
-
</div><div class="token-note">${esc(
|
|
117
|
+
<div class="token-tile"><span>${esc(t("drawer.input"))}</span><strong>${fullNumber(usage.input)}</strong><small>${esc(t("drawer.input_help"))}</small></div>
|
|
118
|
+
<div class="token-tile"><span>${esc(t("drawer.output"))}</span><strong>${fullNumber(usage.output)}</strong><small>${esc(t("drawer.output_help"))}</small></div>
|
|
119
|
+
<div class="token-tile"><span>${esc(t("drawer.cached"))}</span><strong>${fullNumber(usage.cachedInput)}</strong><small>${esc(t("drawer.cached_help"))}</small></div>
|
|
120
|
+
<div class="token-tile"><span>${esc(t("drawer.cache_write"))}</span><strong>${fullNumber(usage.cacheWrite)}</strong><small>${esc(t("drawer.cache_write_help"))}</small></div>
|
|
121
|
+
<div class="token-tile"><span>${esc(t("drawer.reasoning"))}</span><strong>${fullNumber(usage.reasoning)}</strong><small>${esc(t("drawer.reasoning_help"))}</small></div>
|
|
122
|
+
<div class="token-tile"><span>${esc(t("drawer.total"))}</span><strong>${fullNumber(usage.total)}</strong><small>${esc(t("drawer.total_help"))}</small></div>
|
|
123
|
+
<div class="token-tile"><span>${esc(t("drawer.last_input"))}</span><strong>${fullNumber(turn.input)}</strong><small>${esc(t("drawer.latest_turn"))}</small></div>
|
|
124
|
+
<div class="token-tile"><span>${esc(t("drawer.last_total"))}</span><strong>${fullNumber(turn.total)}</strong><small>${esc(t("drawer.last_total_help"))}</small></div>
|
|
125
|
+
</div><div class="token-note">${esc(t("drawer.token_note", { source: sourceLabel }))}</div>`;
|
|
122
126
|
}
|
|
123
127
|
|
|
124
128
|
function subagentCommunicationEvents(session) {
|
|
@@ -183,16 +187,16 @@ window.LoadToAgentAppFactories.createDrawerContent = function createDrawerConten
|
|
|
183
187
|
.map((event) => {
|
|
184
188
|
const fromChild = event.kind === "result" || endpointIsChild(event.from);
|
|
185
189
|
const preview = subagentTextPreview(event.text);
|
|
186
|
-
const label = fromChild ?
|
|
190
|
+
const label = fromChild ? t("drawer.child_to_main", { child: session.agentName || taskName }) : t("drawer.main_to_child");
|
|
187
191
|
return `<article data-subagent-communication="${esc(event.kind)}">
|
|
188
|
-
<header><b>${esc(event.label || event.kind)}</b><span>${esc(label)} · ${esc(timeOnly(event.timestamp))}</span></header>
|
|
192
|
+
<header><b>${esc(window.LoadToAgentI18n.observedText(event.label || event.kind))}</b><span>${esc(label)} · ${esc(timeOnly(event.timestamp))}</span></header>
|
|
189
193
|
<div class="chat-content plain subagent-message-preview${preview.truncated ? " is-truncated" : ""}"
|
|
190
194
|
data-subagent-message-preview data-truncated="${preview.truncated ? "true" : "false"}"><p>${esc(preview.text)}</p></div>
|
|
191
195
|
</article>`;
|
|
192
196
|
})
|
|
193
197
|
.join("");
|
|
194
|
-
return `<details class="chat-activities subagent-coordination" data-subagent-coordination-count="${events.length}">
|
|
195
|
-
<summary
|
|
198
|
+
return `<details class="chat-activities subagent-coordination" data-subagent-coordination-count="${events.length}" data-disclosure-key="${esc(`drawer:${session.id}:coordination`)}">
|
|
199
|
+
<summary>${esc(t("drawer.coordination_events", { count: events.length }))}</summary><div>${rows}</div>
|
|
196
200
|
</details>`;
|
|
197
201
|
}
|
|
198
202
|
|
|
@@ -201,14 +205,14 @@ window.LoadToAgentAppFactories.createDrawerContent = function createDrawerConten
|
|
|
201
205
|
const conversationCount = messages.filter((message) => message.role === "user" || message.role === "assistant").length;
|
|
202
206
|
const workSession = { ...session, messages };
|
|
203
207
|
const sourceCopy = session.source === "collaboration-history"
|
|
204
|
-
? "
|
|
205
|
-
: "
|
|
208
|
+
? t("drawer.subagent_history_reconstructed")
|
|
209
|
+
: t("drawer.subagent_history_actual");
|
|
206
210
|
return `<section class="subagent-work-source" data-subagent-work-messages="${conversationCount}">
|
|
207
|
-
<b
|
|
211
|
+
<b>${esc(t("drawer.subagent_work_history"))}</b><span>${esc(sourceCopy)}</span>
|
|
208
212
|
</section>${chatHtml(workSession, {
|
|
209
|
-
userLabel: "
|
|
210
|
-
assistantLabel: session.agentName || "
|
|
211
|
-
conversationLabel: "
|
|
213
|
+
userLabel: t("drawer.assignment"),
|
|
214
|
+
assistantLabel: session.agentName || t("drawer.sub_ai"),
|
|
215
|
+
conversationLabel: t("drawer.work_history"),
|
|
212
216
|
})}${subagentCoordinationHtml(session)}`;
|
|
213
217
|
}
|
|
214
218
|
|