loadtoagent 1.1.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 +135 -38
- package/package.json +13 -4
- package/preload.js +6 -0
- package/renderer/app-agent-actions.js +125 -53
- package/renderer/app-bootstrap.js +54 -19
- package/renderer/app-dashboard.js +190 -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 +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 +52 -44
- package/renderer/app.js +153 -75
- package/renderer/i18n-messages.js +833 -16
- package/renderer/i18n.js +104 -0
- package/renderer/index.html +145 -59
- package/renderer/shared.js +17 -0
- package/renderer/styles-agent-map.css +6 -0
- package/renderer/styles-cards.css +26 -0
- package/renderer/styles-components.css +86 -9
- package/renderer/styles-management.css +355 -0
- package/renderer/styles-overlays.css +8 -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 +22 -14
- package/renderer/styles-responsive-shell.css +44 -48
- package/renderer/styles-responsive-workflows.css +37 -3
- 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-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 +165 -77
- package/renderer/terminal.js +341 -34
- 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 -6
- 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 +247 -0
- package/src/terminalHost.js +381 -0
- package/src/terminalHostDaemon.js +60 -0
- package/src/terminalManager.js +158 -3
- 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,14 @@ 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
|
|
|
16
21
|
function renderProviderRail() {
|
|
17
|
-
$("#providerRail").innerHTML =
|
|
22
|
+
$("#providerRail").innerHTML = visibleProviders()
|
|
18
23
|
.map((provider) => {
|
|
19
24
|
const available = !!state.availability[provider.id];
|
|
20
25
|
return `<div class="provider-rail-item ${available ? "connected" : ""}" style="${providerStyle(provider.id)}">
|
|
@@ -27,34 +32,95 @@ window.LoadToAgentAppFactories.createDashboard = function createDashboard(contex
|
|
|
27
32
|
}
|
|
28
33
|
|
|
29
34
|
function isProjectlessSession(session) {
|
|
30
|
-
|
|
35
|
+
const cwd = session && (session.originCwd || session.cwd);
|
|
36
|
+
if (!cwd) return true;
|
|
31
37
|
if (typeof session.projectless === "boolean") return session.projectless;
|
|
32
|
-
const normalized = String(
|
|
38
|
+
const normalized = String(cwd).replace(/\\/g, "/").replace(/\/+$/, "");
|
|
33
39
|
return session.provider === "codex" && session.clientKind === "codex-desktop" && /(?:^|\/)Documents\/Codex\/\d{4}-\d{2}-\d{2}\/new-chat$/i.test(normalized);
|
|
34
40
|
}
|
|
35
41
|
|
|
42
|
+
function sessionOriginPath(session) {
|
|
43
|
+
return String(session && (session.originCwd || session.cwd) || "").trim();
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function normalizedProjectPath(value) {
|
|
47
|
+
return String(value || "").trim().replace(/\\/g, "/").replace(/\/+$/, "").toLocaleLowerCase();
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function projectContainsPath(projectPath, candidatePath) {
|
|
51
|
+
const project = normalizedProjectPath(projectPath);
|
|
52
|
+
const candidate = normalizedProjectPath(candidatePath);
|
|
53
|
+
return Boolean(project && candidate && (candidate === project || candidate.startsWith(`${project}/`)));
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function projectName(projectPath) {
|
|
57
|
+
const normalized = String(projectPath || "").replace(/\\/g, "/").replace(/\/+$/, "");
|
|
58
|
+
return normalized.split("/").filter(Boolean).pop() || t("workspace.unknown");
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function observedProjects() {
|
|
62
|
+
const projects = new Map();
|
|
63
|
+
const saved = state.workspaces.map((item) => ({ ...item, key: normalizedProjectPath(item.path) }));
|
|
64
|
+
saved.forEach((item) => projects.set(item.key, { path: item.path, name: item.name || projectName(item.path), saved: true, count: 0 }));
|
|
65
|
+
visibleSessions().filter((session) => !session.parentId && !isProjectlessSession(session)).forEach((session) => {
|
|
66
|
+
const originPath = sessionOriginPath(session);
|
|
67
|
+
if (!originPath) return;
|
|
68
|
+
const owner = saved
|
|
69
|
+
.filter((item) => projectContainsPath(item.path, originPath))
|
|
70
|
+
.sort((a, b) => b.key.length - a.key.length)[0];
|
|
71
|
+
const path = owner ? owner.path : originPath;
|
|
72
|
+
const key = normalizedProjectPath(path);
|
|
73
|
+
const project = projects.get(key) || { path, name: projectName(path), saved: false, count: 0 };
|
|
74
|
+
project.count += 1;
|
|
75
|
+
project.lastActivityAt = !project.lastActivityAt || Date.parse(session.updatedAt || 0) > Date.parse(project.lastActivityAt || 0)
|
|
76
|
+
? session.updatedAt
|
|
77
|
+
: project.lastActivityAt;
|
|
78
|
+
projects.set(key, project);
|
|
79
|
+
});
|
|
80
|
+
const items = [...projects.values()];
|
|
81
|
+
const duplicateNames = new Map();
|
|
82
|
+
items.forEach((item) => duplicateNames.set(item.name.toLocaleLowerCase(), (duplicateNames.get(item.name.toLocaleLowerCase()) || 0) + 1));
|
|
83
|
+
items.forEach((item) => {
|
|
84
|
+
if (duplicateNames.get(item.name.toLocaleLowerCase()) < 2) return;
|
|
85
|
+
const parts = String(item.path).replace(/\\/g, "/").replace(/\/+$/, "").split("/").filter(Boolean);
|
|
86
|
+
item.name = parts.slice(-2).join("/") || item.name;
|
|
87
|
+
});
|
|
88
|
+
return items.sort((a, b) => Number(b.count || 0) - Number(a.count || 0)
|
|
89
|
+
|| Number(b.saved) - Number(a.saved)
|
|
90
|
+
|| String(a.name).localeCompare(String(b.name), uiLocale()));
|
|
91
|
+
}
|
|
92
|
+
|
|
36
93
|
function sessionWorkspaceLabel(session) {
|
|
37
|
-
return isProjectlessSession(session)
|
|
94
|
+
return isProjectlessSession(session)
|
|
95
|
+
? t("ui.no_project")
|
|
96
|
+
: (session && session.workspace) || projectName(sessionOriginPath(session));
|
|
38
97
|
}
|
|
39
98
|
|
|
40
99
|
function matchesWorkspaceFilter(session) {
|
|
41
100
|
if (state.workspace === "all") return true;
|
|
42
101
|
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
|
-
);
|
|
102
|
+
return !isProjectlessSession(session) && projectContainsPath(state.workspace, sessionOriginPath(session));
|
|
49
103
|
}
|
|
50
104
|
|
|
51
105
|
function renderWorkspaces() {
|
|
52
|
-
const
|
|
53
|
-
const
|
|
54
|
-
|
|
106
|
+
const lists = [$("#workspaceList"), $("#mobileWorkspaceList")].filter(Boolean);
|
|
107
|
+
const rootSessions = visibleSessions().filter((session) => !session.parentId);
|
|
108
|
+
const projects = observedProjects();
|
|
109
|
+
const projectlessCount = rootSessions.filter(isProjectlessSession).length;
|
|
110
|
+
const savedWorkspaceExists = state.workspace === "all"
|
|
111
|
+
|| (state.workspace === PROJECTLESS_WORKSPACE && projectlessCount > 0)
|
|
112
|
+
|| projects.some((project) => normalizedProjectPath(project.path) === normalizedProjectPath(state.workspace));
|
|
113
|
+
if (!savedWorkspaceExists) state.workspace = "all";
|
|
114
|
+
const projectButton = (item) => `<button type="button" class="workspace-item observed-project ${state.workspace === item.path ? "selected" : ""}"
|
|
115
|
+
data-workspace="${esc(item.path)}" title="${esc(item.path)}"
|
|
116
|
+
aria-label="${esc(t("project.filter_named", { name: item.name, count: item.count }))}"
|
|
117
|
+
aria-pressed="${state.workspace === item.path ? "true" : "false"}">
|
|
118
|
+
<strong>${esc(item.name)}</strong><small>${Number(item.count || 0)}</small>
|
|
119
|
+
</button>`;
|
|
120
|
+
const html =
|
|
55
121
|
`<button type="button" class="workspace-item ${state.workspace === "all" ? "selected" : ""}"
|
|
56
122
|
data-workspace="all" aria-pressed="${state.workspace === "all" ? "true" : "false"}">
|
|
57
|
-
<strong>${window.LoadToAgentI18n.t("
|
|
123
|
+
<strong>${window.LoadToAgentI18n.t("project.all")}</strong><small>${rootSessions.length}</small>
|
|
58
124
|
</button>` +
|
|
59
125
|
(projectlessCount
|
|
60
126
|
? `<button type="button" class="workspace-item projectless ${state.workspace === PROJECTLESS_WORKSPACE ? "selected" : ""}"
|
|
@@ -65,34 +131,39 @@ window.LoadToAgentAppFactories.createDashboard = function createDashboard(contex
|
|
|
65
131
|
<small>${projectlessCount}</small>
|
|
66
132
|
</button>`
|
|
67
133
|
: "") +
|
|
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>
|
|
134
|
+
projects.map((item) => item.saved ? `<div class="workspace-row">
|
|
135
|
+
${projectButton(item)}
|
|
76
136
|
<button type="button" class="workspace-remove" data-remove-workspace="${esc(item.path)}"
|
|
77
|
-
aria-label="${esc(item.name)}
|
|
137
|
+
aria-label="${esc(t("workspace.remove_named", { name: item.name }))}"
|
|
78
138
|
title="${esc(window.LoadToAgentI18n.t("ui.remove_from_list"))}">×</button>
|
|
79
|
-
</div
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
139
|
+
</div>` : projectButton(item)).join("") +
|
|
140
|
+
(!projects.length && !projectlessCount ? `<div class="workspace-empty">${window.LoadToAgentI18n.t("project.empty")}</div>` : "");
|
|
141
|
+
lists.forEach((list) => { list.innerHTML = html; });
|
|
142
|
+
const selectedProject = projects.find((project) => normalizedProjectPath(project.path) === normalizedProjectPath(state.workspace));
|
|
143
|
+
const mobileSummary = $("#mobileWorkspaceSummary");
|
|
144
|
+
if (mobileSummary) mobileSummary.textContent = state.workspace === "all"
|
|
145
|
+
? t("project.all")
|
|
146
|
+
: state.workspace === PROJECTLESS_WORKSPACE
|
|
147
|
+
? t("ui.no_project")
|
|
148
|
+
: selectedProject?.name || projectName(state.workspace);
|
|
83
149
|
}
|
|
84
150
|
|
|
85
151
|
function renderGlobalStats() {
|
|
86
|
-
const
|
|
87
|
-
const
|
|
152
|
+
const sessions = visibleSessions();
|
|
153
|
+
const totals = {
|
|
154
|
+
active: sessions.filter((session) => session.status === "running" || session.status === "starting").length,
|
|
155
|
+
waiting: sessions.filter((session) => session.attention?.required).length,
|
|
156
|
+
usage: { total: sessions.reduce((sum, session) => sum + Number(session.usage && session.usage.total || 0), 0) },
|
|
157
|
+
};
|
|
88
158
|
const rootCount = sessions.filter((session) => !session.parentId).length;
|
|
89
|
-
const
|
|
159
|
+
const criticalCount = sessions.filter((session) => session.health?.level === "critical").length;
|
|
160
|
+
const riskCount = sessions.filter((session) => session.health?.level === "warning" || Number(session.context?.percent || 0) >= 75).length;
|
|
90
161
|
const items = [
|
|
91
162
|
[window.LoadToAgentI18n.t("ui.all_tasks"), rootCount, window.LoadToAgentI18n.t("ui.items"), ""],
|
|
92
163
|
[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("
|
|
164
|
+
[window.LoadToAgentI18n.t("management.action_required"), totals.waiting || 0, window.LoadToAgentI18n.t("ui.items"), "alert"],
|
|
165
|
+
[window.LoadToAgentI18n.t("management.health.critical"), criticalCount, window.LoadToAgentI18n.t("ui.items"), "critical"],
|
|
166
|
+
[window.LoadToAgentI18n.t("management.risk_total"), riskCount, window.LoadToAgentI18n.t("ui.items"), "warning"],
|
|
96
167
|
];
|
|
97
168
|
$("#globalStats").innerHTML = items
|
|
98
169
|
.map(
|
|
@@ -104,9 +175,46 @@ window.LoadToAgentAppFactories.createDashboard = function createDashboard(contex
|
|
|
104
175
|
)
|
|
105
176
|
.join("");
|
|
106
177
|
$("#navAllCount").textContent = rootCount;
|
|
107
|
-
|
|
108
|
-
$("#
|
|
109
|
-
|
|
178
|
+
const activeRootCount = sessions.filter((session) => !session.parentId && ["running", "starting"].includes(session.status)).length;
|
|
179
|
+
$("#navActiveCount").textContent = activeRootCount;
|
|
180
|
+
const reviewCount = sessions.filter((session) => context.managementBucket?.(session) !== "healthy").length;
|
|
181
|
+
$("#navWaitingCount").textContent = reviewCount;
|
|
182
|
+
const scheduledCount = (state.snapshot?.automations || [])
|
|
183
|
+
.filter((item) => isProviderVisible(item.provider || "codex")).length;
|
|
184
|
+
const loopCount = sessions.filter(isRuntimeLoopSession).length;
|
|
185
|
+
$("#navRuntimeCount").textContent = scheduledCount + loopCount;
|
|
186
|
+
const tmuxSessionCount = Number(state.snapshot?.tmux?.summary?.sessions || 0);
|
|
187
|
+
$("#navTmuxCount").textContent = tmuxSessionCount;
|
|
188
|
+
$("#advancedToolsCount").textContent = scheduledCount + loopCount + Number($("#navTerminalCount").textContent || 0) + tmuxSessionCount;
|
|
189
|
+
const navCounts = {
|
|
190
|
+
all: rootCount,
|
|
191
|
+
active: activeRootCount,
|
|
192
|
+
waiting: reviewCount,
|
|
193
|
+
runtime: scheduledCount + loopCount,
|
|
194
|
+
terminal: Number($("#navTerminalCount").textContent || 0),
|
|
195
|
+
tmux: tmuxSessionCount,
|
|
196
|
+
};
|
|
197
|
+
document.querySelectorAll(".nav-item[data-view]").forEach((button) => {
|
|
198
|
+
const key = {
|
|
199
|
+
all: "app.nav.home", active: "app.nav.active", waiting: "app.nav.needs_review", runtime: "app.nav.runtime",
|
|
200
|
+
terminal: "app.nav.session_terminal", tmux: "app.nav.tmux", settings: "app.nav.settings",
|
|
201
|
+
}[button.dataset.view];
|
|
202
|
+
const label = t(key);
|
|
203
|
+
const count = navCounts[button.dataset.view];
|
|
204
|
+
const unitKey = { all: "tasks", active: "tasks", waiting: "tasks", runtime: "runs", terminal: "sessions", tmux: "groups" }[button.dataset.view];
|
|
205
|
+
const unit = unitKey ? t(`quality.unit.${unitKey}`) : "";
|
|
206
|
+
const accessibleLabel = Number.isFinite(count) ? t("quality.nav_count_detailed", { label, count, unit }) : label;
|
|
207
|
+
button.setAttribute("aria-label", accessibleLabel);
|
|
208
|
+
button.setAttribute("title", accessibleLabel);
|
|
209
|
+
});
|
|
210
|
+
const advancedCount = scheduledCount + loopCount + Number($("#navTerminalCount").textContent || 0) + tmuxSessionCount;
|
|
211
|
+
$("#advancedToolsNav")?.querySelector("summary")?.setAttribute("aria-label", t("quality.nav_count_detailed", {
|
|
212
|
+
label: t("management.advanced_tools"), count: advancedCount, unit: t("quality.unit.items"),
|
|
213
|
+
}));
|
|
214
|
+
const tmuxShortcut = $("#openTmuxFromAgentWork");
|
|
215
|
+
$("#agentWorkTmuxCount").textContent = tmuxSessionCount;
|
|
216
|
+
tmuxShortcut.dataset.i18nParams = JSON.stringify({ count: tmuxSessionCount });
|
|
217
|
+
tmuxShortcut.setAttribute("aria-label", t("graph.open_tmux_workspace_count", { count: tmuxSessionCount }));
|
|
110
218
|
}
|
|
111
219
|
|
|
112
220
|
function formatBytes(value) {
|
|
@@ -159,7 +267,7 @@ window.LoadToAgentAppFactories.createDashboard = function createDashboard(contex
|
|
|
159
267
|
],
|
|
160
268
|
downloaded: [
|
|
161
269
|
"✓", window.LoadToAgentI18n.t("ui.ready_to_install"), window.LoadToAgentI18n.t("ui.the_update_file_is_ready"),
|
|
162
|
-
window.LoadToAgentI18n.t("
|
|
270
|
+
window.LoadToAgentI18n.t("settings.update.auto_install_restart"),
|
|
163
271
|
],
|
|
164
272
|
error: [
|
|
165
273
|
"!", window.LoadToAgentI18n.t("ui.check_failed"), window.LoadToAgentI18n.t("ui.could_not_check_for_updates"),
|
|
@@ -202,7 +310,7 @@ window.LoadToAgentAppFactories.createDashboard = function createDashboard(contex
|
|
|
202
310
|
install.textContent = downloading
|
|
203
311
|
? window.LoadToAgentI18n.t("ui.downloading_2")
|
|
204
312
|
: downloaded
|
|
205
|
-
? window.LoadToAgentI18n.t("ui.
|
|
313
|
+
? `${window.LoadToAgentI18n.t("settings.update.download")} · ${window.LoadToAgentI18n.t("ui.restart")}`
|
|
206
314
|
: window.LoadToAgentI18n.t("settings.update.download");
|
|
207
315
|
const progress = $("#updateProgress");
|
|
208
316
|
progress.classList.toggle("hidden", !downloading && !downloaded);
|
|
@@ -210,11 +318,13 @@ window.LoadToAgentAppFactories.createDashboard = function createDashboard(contex
|
|
|
210
318
|
$("#updateProgressBar").style.width = `${Math.max(0, Math.min(100, Number(update.progress || 0)))}%`;
|
|
211
319
|
$(".update-progress-track").setAttribute("aria-valuenow", String(Math.max(0, Math.min(100, Number(update.progress || 0)))));
|
|
212
320
|
$("#updateProgressBytes").textContent = downloaded
|
|
213
|
-
? `${formatBytes(update.totalBytes || update.downloadedBytes)} ·
|
|
321
|
+
? `${formatBytes(update.totalBytes || update.downloadedBytes)} · ${window.LoadToAgentI18n.t("settings.update.file_verified")}`
|
|
214
322
|
: `${formatBytes(update.downloadedBytes)} / ${update.totalBytes ? formatBytes(update.totalBytes) : window.LoadToAgentI18n.t("ui.checking_size")}`;
|
|
215
323
|
const error = $("#updateError");
|
|
216
324
|
error.classList.toggle("hidden", !update.error);
|
|
217
|
-
error.textContent = update.error
|
|
325
|
+
error.textContent = update.error
|
|
326
|
+
? window.LoadToAgentI18n.errorText(update.error, "ui.could_not_check_for_updates")
|
|
327
|
+
: "";
|
|
218
328
|
const notes = $("#releaseNotes");
|
|
219
329
|
notes.classList.toggle("hidden", !update.latestVersion);
|
|
220
330
|
$("#releaseNotesText").textContent =
|
|
@@ -231,16 +341,20 @@ window.LoadToAgentAppFactories.createDashboard = function createDashboard(contex
|
|
|
231
341
|
function renderProviderOverview() {
|
|
232
342
|
pruneProviderFilters();
|
|
233
343
|
const summaries = (state.snapshot && state.snapshot.summary && state.snapshot.summary.providers) || state.providers;
|
|
234
|
-
const sessions = (
|
|
235
|
-
|
|
236
|
-
|
|
344
|
+
const sessions = visibleSessions();
|
|
345
|
+
const visibleSummaries = summaries.filter((provider) => isProviderVisible(provider.id));
|
|
346
|
+
const overviewTabStopId = state.providerFilters.size ? [...state.providerFilters][0] : visibleSummaries[0]?.id;
|
|
347
|
+
$("#providerOverview").innerHTML = visibleSummaries
|
|
348
|
+
.map((provider, index) => {
|
|
237
349
|
const rootCount = sessions.filter((session) => session.provider === provider.id && !session.parentId).length;
|
|
238
350
|
const selected = state.providerFilters.has(provider.id);
|
|
351
|
+
const tabStop = provider.id === overviewTabStopId;
|
|
239
352
|
return `<button type="button" class="provider-overview-card ${selected ? "selected" : ""}"
|
|
240
353
|
data-provider-card="${esc(provider.id)}"
|
|
241
354
|
data-motion-key="provider:${esc(provider.id)}"
|
|
242
355
|
data-motion-value="${provider.active || 0}:${rootCount}:${(provider.usage && provider.usage.total) || 0}"
|
|
243
356
|
style="${providerStyle(provider.id)}"
|
|
357
|
+
tabindex="${tabStop ? "0" : "-1"}"
|
|
244
358
|
aria-pressed="${selected ? "true" : "false"}">
|
|
245
359
|
<div class="poc-head">
|
|
246
360
|
<span class="provider-mark">${esc(provider.mark)}</span>
|
|
@@ -263,7 +377,7 @@ window.LoadToAgentAppFactories.createDashboard = function createDashboard(contex
|
|
|
263
377
|
}
|
|
264
378
|
|
|
265
379
|
function pruneProviderFilters() {
|
|
266
|
-
const valid = new Set(
|
|
380
|
+
const valid = new Set(visibleProviders().map((provider) => provider.id));
|
|
267
381
|
for (const id of [...state.providerFilters]) if (!valid.has(id)) state.providerFilters.delete(id);
|
|
268
382
|
if (valid.size > 0 && state.providerFilters.size === valid.size) state.providerFilters.clear();
|
|
269
383
|
}
|
|
@@ -273,28 +387,29 @@ window.LoadToAgentAppFactories.createDashboard = function createDashboard(contex
|
|
|
273
387
|
if (providerId === "all") state.providerFilters.clear();
|
|
274
388
|
else if (state.providerFilters.has(providerId)) state.providerFilters.delete(providerId);
|
|
275
389
|
else state.providerFilters.add(providerId);
|
|
276
|
-
if (
|
|
390
|
+
if (visibleProviders().length > 0 && state.providerFilters.size === visibleProviders().length) state.providerFilters.clear();
|
|
277
391
|
}
|
|
278
392
|
|
|
279
393
|
function renderProviderFilter() {
|
|
280
394
|
pruneProviderFilters();
|
|
281
395
|
const allSelected = state.providerFilters.size === 0;
|
|
396
|
+
const tabStopId = allSelected ? "all" : [...state.providerFilters][0];
|
|
282
397
|
const button = (id, label, mark = "") => {
|
|
283
398
|
const selected = id === "all" ? allSelected : state.providerFilters.has(id);
|
|
284
399
|
return `<button type="button" class="provider-filter-chip ${selected ? "selected" : ""}"
|
|
285
|
-
data-provider-filter="${esc(id)}" aria-pressed="${selected ? "true" : "false"}">
|
|
400
|
+
data-provider-filter="${esc(id)}" tabindex="${id === tabStopId ? "0" : "-1"}" aria-pressed="${selected ? "true" : "false"}">
|
|
286
401
|
<i class="provider-filter-check" aria-hidden="true">✓</i>
|
|
287
402
|
${mark ? `<span class="provider-filter-mark" aria-hidden="true">${esc(mark)}</span>` : ""}<b>${esc(label)}</b>
|
|
288
403
|
</button>`;
|
|
289
404
|
};
|
|
290
405
|
$("#providerFilter").innerHTML =
|
|
291
406
|
button("all", window.LoadToAgentI18n.t("ui.all_ai")) +
|
|
292
|
-
|
|
407
|
+
visibleProviders().map((provider) => button(provider.id, provider.label, provider.mark)).join("");
|
|
293
408
|
}
|
|
294
409
|
|
|
295
410
|
function announceProviderFilter() {
|
|
296
411
|
const labels = state.providerFilters.size
|
|
297
|
-
?
|
|
412
|
+
? visibleProviders().filter((provider) => state.providerFilters.has(provider.id)).map((provider) => provider.label).join(", ")
|
|
298
413
|
: window.LoadToAgentI18n.t("ui.all_ai");
|
|
299
414
|
$("#providerFilterStatus").textContent = window.LoadToAgentI18n.t("filter.result_summary", {
|
|
300
415
|
providers: labels,
|
|
@@ -303,16 +418,16 @@ window.LoadToAgentAppFactories.createDashboard = function createDashboard(contex
|
|
|
303
418
|
}
|
|
304
419
|
|
|
305
420
|
function filteredSessions() {
|
|
306
|
-
const allSessions = [...(
|
|
421
|
+
const allSessions = [...visibleSessions()];
|
|
307
422
|
let sessions = state.view === "waiting" ? allSessions : allSessions.filter((session) => !session.parentId);
|
|
308
423
|
if (state.view === "active") sessions = sessions.filter((session) => session.status === "running" || session.status === "starting");
|
|
309
|
-
if (state.view === "waiting") sessions = sessions.filter((session) => session.
|
|
424
|
+
if (state.view === "waiting") sessions = sessions.filter((session) => session.attention?.required || ["critical", "warning", "unknown"].includes(session.health?.level) || !session.health);
|
|
310
425
|
if (state.providerFilters.size) sessions = sessions.filter((session) => state.providerFilters.has(session.provider));
|
|
311
426
|
sessions = sessions.filter(matchesWorkspaceFilter);
|
|
312
|
-
const query = state.search.trim().toLowerCase();
|
|
427
|
+
const query = state.search.replace(/\s+/g, " ").trim().toLowerCase();
|
|
313
428
|
if (query) {
|
|
314
429
|
sessions = sessions.filter((session) =>
|
|
315
|
-
[session.title, session.model, session.cwd, session.agentName, ...(session.messages || []).slice(-12).map((item) => item.text)]
|
|
430
|
+
[session.title, session.model, session.originCwd, session.cwd, session.workspace, session.agentName, ...(session.messages || []).slice(-12).map((item) => item.text)]
|
|
316
431
|
.join(" ")
|
|
317
432
|
.toLowerCase()
|
|
318
433
|
.includes(query),
|
|
@@ -330,13 +445,13 @@ window.LoadToAgentAppFactories.createDashboard = function createDashboard(contex
|
|
|
330
445
|
}
|
|
331
446
|
|
|
332
447
|
function graphFilteredSessions() {
|
|
333
|
-
let sessions = [...(
|
|
448
|
+
let sessions = [...visibleSessions()];
|
|
334
449
|
if (state.providerFilters.size) sessions = sessions.filter((session) => state.providerFilters.has(session.provider));
|
|
335
450
|
sessions = sessions.filter(matchesWorkspaceFilter);
|
|
336
|
-
const query = state.search.trim().toLowerCase();
|
|
451
|
+
const query = state.search.replace(/\s+/g, " ").trim().toLowerCase();
|
|
337
452
|
if (query)
|
|
338
453
|
sessions = sessions.filter((session) =>
|
|
339
|
-
[session.title, session.model, session.cwd, session.agentName, session.agentRole, ...(session.messages || []).map((item) => item.text)]
|
|
454
|
+
[session.title, session.model, session.originCwd, session.cwd, session.workspace, session.agentName, session.agentRole, ...(session.messages || []).map((item) => item.text)]
|
|
340
455
|
.join(" ")
|
|
341
456
|
.toLowerCase()
|
|
342
457
|
.includes(query),
|
|
@@ -344,9 +459,28 @@ window.LoadToAgentAppFactories.createDashboard = function createDashboard(contex
|
|
|
344
459
|
return sessions;
|
|
345
460
|
}
|
|
346
461
|
|
|
462
|
+
function renderProviderVisibilitySettings() {
|
|
463
|
+
const list = $("#providerVisibilityList");
|
|
464
|
+
if (!list) return;
|
|
465
|
+
list.innerHTML = state.providers.map((provider) => {
|
|
466
|
+
const visible = isProviderVisible(provider.id);
|
|
467
|
+
const status = window.LoadToAgentI18n.t(visible ? "settings.providers.visible" : "settings.providers.hidden");
|
|
468
|
+
return `<label class="provider-visibility-option ${visible ? "enabled" : "disabled"}" style="${providerStyle(provider.id)}">
|
|
469
|
+
<span class="provider-mark" aria-hidden="true">${esc(provider.mark)}</span>
|
|
470
|
+
<span class="provider-visibility-name"><b>${esc(provider.label)}</b><small>${esc(provider.company)}</small></span>
|
|
471
|
+
<span class="provider-visibility-status">${esc(status)}</span>
|
|
472
|
+
<input type="checkbox" data-provider-visibility="${esc(provider.id)}" ${visible ? "checked" : ""}
|
|
473
|
+
aria-label="${esc(`${provider.label} ${status}`)}">
|
|
474
|
+
<span class="provider-toggle" aria-hidden="true"><i></i></span>
|
|
475
|
+
</label>`;
|
|
476
|
+
}).join("");
|
|
477
|
+
}
|
|
478
|
+
|
|
347
479
|
return {
|
|
348
480
|
renderProviderRail,
|
|
349
481
|
isProjectlessSession,
|
|
482
|
+
sessionOriginPath,
|
|
483
|
+
observedProjects,
|
|
350
484
|
sessionWorkspaceLabel,
|
|
351
485
|
matchesWorkspaceFilter,
|
|
352
486
|
renderWorkspaces,
|
|
@@ -361,5 +495,6 @@ window.LoadToAgentAppFactories.createDashboard = function createDashboard(contex
|
|
|
361
495
|
announceProviderFilter,
|
|
362
496
|
filteredSessions,
|
|
363
497
|
graphFilteredSessions,
|
|
498
|
+
renderProviderVisibilitySettings,
|
|
364
499
|
};
|
|
365
500
|
};
|
|
@@ -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
|
|