loadtoagent 1.0.0 → 1.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.ko.md +7 -0
- package/README.md +7 -0
- package/README.zh-CN.md +7 -0
- package/docs/PROVIDER-CONTRACTS.md +23 -1
- package/docs/UI-AUDIT-100.md +118 -0
- package/docs/UI-AUDIT-101-200.md +120 -0
- package/docs/UI-AUDIT-201-300.md +120 -0
- package/main.js +183 -37
- package/package.json +15 -4
- package/preload.js +11 -0
- package/renderer/app-agent-actions.js +125 -53
- package/renderer/app-bootstrap.js +54 -9
- package/renderer/app-dashboard.js +197 -58
- package/renderer/app-drawer-content.js +103 -86
- package/renderer/app-drawer-data.js +26 -13
- package/renderer/app-drawer.js +79 -55
- package/renderer/app-events-dialogs.js +146 -37
- package/renderer/app-events-filters.js +172 -9
- package/renderer/app-events-navigation.js +78 -31
- package/renderer/app-events-sessions.js +171 -4
- package/renderer/app-events.js +2 -1
- package/renderer/app-graph-model.js +32 -4
- package/renderer/app-graph-orchestration.js +16 -13
- package/renderer/app-graph-view.js +228 -112
- package/renderer/app-management.js +211 -0
- package/renderer/app-provider-visibility.js +124 -0
- package/renderer/app-quality.js +568 -0
- package/renderer/app-run-modal.js +103 -33
- package/renderer/app-runtime-overview.js +312 -0
- package/renderer/app-session-render.js +94 -37
- package/renderer/app-tmux-render.js +119 -38
- package/renderer/app.js +155 -76
- package/renderer/i18n-messages.js +833 -15
- package/renderer/i18n.js +104 -0
- package/renderer/index.html +162 -75
- package/renderer/shared.js +17 -0
- package/renderer/styles-agent-map.css +50 -1
- package/renderer/styles-cards.css +26 -0
- package/renderer/styles-collaboration.css +18 -88
- package/renderer/styles-components.css +126 -14
- package/renderer/styles-management.css +355 -0
- package/renderer/styles-overlays.css +13 -2
- package/renderer/styles-product.css +24 -16
- package/renderer/styles-quality.css +312 -0
- package/renderer/styles-readability.css +862 -0
- package/renderer/styles-responsive-product.css +84 -12
- package/renderer/styles-responsive-runtime.css +31 -14
- package/renderer/styles-responsive-shell.css +44 -48
- package/renderer/styles-responsive-workflows.css +37 -17
- package/renderer/styles-run-composer.css +5 -0
- package/renderer/styles-runtime-overview.css +285 -0
- package/renderer/styles-settings.css +160 -0
- package/renderer/styles-terminal.css +339 -2
- package/renderer/styles-tmux.css +154 -0
- package/renderer/styles-workflow-map.css +362 -15
- package/renderer/styles-workflows.css +69 -2
- package/renderer/styles.css +27 -12
- package/renderer/terminal-agent.js +17 -13
- package/renderer/terminal-events.js +233 -40
- package/renderer/terminal-workbench.js +176 -78
- package/renderer/terminal.js +350 -37
- package/src/agentMonitor/claudeParser.js +96 -7
- package/src/agentMonitor/codexParser.js +59 -4
- package/src/agentMonitor/executionActivity.js +282 -0
- package/src/agentMonitor/genericParser.js +56 -13
- package/src/agentMonitor/hierarchy.js +17 -0
- package/src/agentMonitor/responseIntent.js +51 -0
- package/src/agentMonitor.js +21 -3
- package/src/agentRunner.js +66 -1
- package/src/attentionNotifier.js +71 -0
- package/src/automationMonitor.js +258 -0
- package/src/contracts.js +10 -1
- package/src/ipc/registerAgentIpc.js +9 -3
- package/src/ipc/registerAppIpc.js +4 -1
- package/src/ipc/registerTerminalIpc.js +12 -6
- package/src/macUpdateHelper.js +210 -0
- package/src/monitorWorker.js +65 -6
- package/src/platformPath.js +80 -0
- package/src/processMonitor.js +81 -23
- package/src/providerVisibilityStore.js +45 -0
- package/src/sessionIntelligence.js +247 -0
- package/src/terminalHost.js +381 -0
- package/src/terminalHostDaemon.js +60 -0
- package/src/terminalManager.js +158 -3
- package/src/tmuxMonitor.js +2 -2
- package/src/updateInstaller.js +175 -0
package/renderer/app.js
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
window.LoadToAgentAppFactories = window.LoadToAgentAppFactories || {};
|
|
3
3
|
window.LoadToAgentAppFactories.createCore = function createCore(context = {}) {
|
|
4
4
|
const { $, $$, esc, uiLocale, providerLabel, reportRecoverableError } = window.LoadToAgentRendererUtils;
|
|
5
|
+
const t = (key, params) => window.LoadToAgentI18n.t(key, params);
|
|
6
|
+
const observedText = (value) => window.LoadToAgentI18n.observedText(value);
|
|
5
7
|
const PROJECTLESS_WORKSPACE = "__projectless__";
|
|
6
8
|
const state = {
|
|
7
9
|
providers: [],
|
|
@@ -9,16 +11,18 @@ window.LoadToAgentAppFactories.createCore = function createCore(context = {}) {
|
|
|
9
11
|
availability: {},
|
|
10
12
|
workspaces: [],
|
|
11
13
|
snapshot: null,
|
|
14
|
+
rawSnapshot: null,
|
|
12
15
|
activeRuns: [],
|
|
13
16
|
versions: {},
|
|
14
17
|
update: null,
|
|
15
18
|
view: "all",
|
|
16
19
|
providerFilters: new Set(),
|
|
20
|
+
hiddenProviders: new Set(),
|
|
17
21
|
workspace: "all",
|
|
18
22
|
search: "",
|
|
19
23
|
sort: "recent",
|
|
20
24
|
selectedId: null,
|
|
21
|
-
drawerTab: "
|
|
25
|
+
drawerTab: "summary",
|
|
22
26
|
drawerMode: "session",
|
|
23
27
|
runProvider: "claude",
|
|
24
28
|
details: new Map(),
|
|
@@ -27,16 +31,22 @@ window.LoadToAgentAppFactories.createCore = function createCore(context = {}) {
|
|
|
27
31
|
visibleLimit: 30,
|
|
28
32
|
graphFocusId: null,
|
|
29
33
|
graphExpandedProviders: new Set(),
|
|
34
|
+
expandedExecutionSessions: new Set(),
|
|
30
35
|
expandedCompletedSubagents: new Set(),
|
|
36
|
+
expandedTmuxSubagents: new Set(),
|
|
37
|
+
selectedRuntimeLoopId: null,
|
|
31
38
|
tmuxFocus: null,
|
|
32
39
|
agentCommandDrafts: new Map(),
|
|
33
40
|
agentCommandTargets: new Map(),
|
|
34
41
|
agentCommandSending: new Set(),
|
|
35
42
|
stopRequests: new Set(),
|
|
43
|
+
runControlRequests: new Set(),
|
|
44
|
+
managementFilter: "all",
|
|
36
45
|
detailErrors: new Map(),
|
|
46
|
+
disclosureStates: new Map(),
|
|
37
47
|
guideCompleted: new Set(),
|
|
38
|
-
guideExpanded:
|
|
39
|
-
platform: { id: "win32", label: "Windows", localShell: "powershell", localShellLabel: "
|
|
48
|
+
guideExpanded: false,
|
|
49
|
+
platform: { id: "win32", label: "Windows", localShell: "powershell", localShellLabel: t("terminal.windows_shell"), nativeTmux: false },
|
|
40
50
|
};
|
|
41
51
|
Object.defineProperty(state, "provider", {
|
|
42
52
|
enumerable: true,
|
|
@@ -51,9 +61,24 @@ window.LoadToAgentAppFactories.createCore = function createCore(context = {}) {
|
|
|
51
61
|
});
|
|
52
62
|
const motionPreference = window.matchMedia("(prefers-reduced-motion: reduce)");
|
|
53
63
|
const motionState = {
|
|
54
|
-
ready: false, modalTimer: 0, toastTimer: 0, drawerTimer: 0, drawerContentTimer: 0,
|
|
55
|
-
drawerRenderKey: "", drawerTab: "", activeDialogTrigger: null,
|
|
64
|
+
ready: false, modalTimer: 0, modalFocusTimer: 0, toastTimer: 0, drawerTimer: 0, drawerContentTimer: 0,
|
|
65
|
+
drawerRenderKey: "", drawerTab: "", activeDialogTrigger: null, dialogGeneration: 0,
|
|
56
66
|
};
|
|
67
|
+
function disclosureElements(root = document) {
|
|
68
|
+
const elements = [];
|
|
69
|
+
if (root instanceof Element && root.matches("details[data-disclosure-key]")) elements.push(root);
|
|
70
|
+
root.querySelectorAll?.("details[data-disclosure-key]").forEach((element) => elements.push(element));
|
|
71
|
+
return elements;
|
|
72
|
+
}
|
|
73
|
+
function rememberDisclosureStates(root = document) {
|
|
74
|
+
disclosureElements(root).forEach((element) => state.disclosureStates.set(element.dataset.disclosureKey, element.open));
|
|
75
|
+
}
|
|
76
|
+
function restoreDisclosureStates(root = document) {
|
|
77
|
+
disclosureElements(root).forEach((element) => {
|
|
78
|
+
const key = element.dataset.disclosureKey;
|
|
79
|
+
if (state.disclosureStates.has(key)) element.open = state.disclosureStates.get(key);
|
|
80
|
+
});
|
|
81
|
+
}
|
|
57
82
|
document.documentElement.dataset.motion = motionPreference.matches ? "reduced" : "full";
|
|
58
83
|
motionPreference.addEventListener("change", (event) => {
|
|
59
84
|
document.documentElement.dataset.motion = event.matches ? "reduced" : "full";
|
|
@@ -127,54 +152,43 @@ window.LoadToAgentAppFactories.createCore = function createCore(context = {}) {
|
|
|
127
152
|
requestAnimationFrame(() => section.classList.add("motion-section-in"));
|
|
128
153
|
});
|
|
129
154
|
}
|
|
130
|
-
const
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
155
|
+
const localizedLookup = (keys) => new Proxy(Object.create(null), {
|
|
156
|
+
get: (_target, property) => keys[property] ? t(keys[property]) : undefined,
|
|
157
|
+
});
|
|
158
|
+
const STATUS = localizedLookup({
|
|
159
|
+
starting: "ui.preparing", running: "ui.working", paused: "management.status.paused", waiting: "app.nav.needs_review", idle: "ui.idle",
|
|
160
|
+
completed: "ui.completed", failed: "ui.problem", cancelled: "ui.stopped",
|
|
161
|
+
});
|
|
162
|
+
const VIEW_TITLES = localizedLookup({
|
|
163
|
+
all: "ui.recent_conversations_and_tasks", active: "ui.active_tasks", waiting: "ui.tasks_needing_review",
|
|
164
|
+
runtime: "runtime.title", terminal: "app.nav.session_terminal", tmux: "app.nav.tmux", settings: "settings.title",
|
|
165
|
+
});
|
|
166
|
+
const VIEW_META_KEYS = {
|
|
167
|
+
all: ["ui.ai_work_overview", "ui.see_all_ai_work_at_a_glance", "ui.active_work_and_items_needing_your_review_appear_first_find"],
|
|
168
|
+
active: ["ui.active_now", "ui.see_which_ai_is_working_now", "ui.see_what_is_being_handled_then_open_a_task_for"],
|
|
169
|
+
waiting: ["ui.your_turn", "ui.handle_items_that_need_your_review_first", "ui.only_tasks_waiting_for_your_response_or_choice_are_shown"],
|
|
170
|
+
runtime: ["runtime.eyebrow", "runtime.title", "runtime.description"],
|
|
171
|
+
terminal: ["ui.continue_an_existing_conversation", "ui.continue_ai_sessions_in_the_terminal", "ui.continue_the_same_task_with_its_previous_conversation_beside_the"],
|
|
172
|
+
tmux: ["ui.advanced_work_tools", "ui.manage_multi_terminal_work_in_one_place", "ui.this_view_is_only_for_existing_tmux_workflows_home_and"],
|
|
173
|
+
settings: ["ui.application_management", "ui.check_versions_and_updates", "ui.compare_the_installed_and_latest_stable_versions_then_download_a"],
|
|
140
174
|
};
|
|
141
|
-
const VIEW_META = {
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
subtitle:
|
|
175
|
+
const VIEW_META = new Proxy(Object.create(null), {
|
|
176
|
+
get: (_target, property) => {
|
|
177
|
+
const keys = VIEW_META_KEYS[property];
|
|
178
|
+
return keys ? { eyebrow: t(keys[0]), title: t(keys[1]), subtitle: t(keys[2]) } : undefined;
|
|
145
179
|
},
|
|
146
|
-
|
|
147
|
-
eyebrow: window.LoadToAgentI18n.t("ui.active_now"), title: window.LoadToAgentI18n.t("ui.see_which_ai_is_working_now"),
|
|
148
|
-
subtitle: window.LoadToAgentI18n.t("ui.see_what_is_being_handled_then_open_a_task_for"),
|
|
149
|
-
},
|
|
150
|
-
waiting: {
|
|
151
|
-
eyebrow: window.LoadToAgentI18n.t("ui.your_turn"), title: window.LoadToAgentI18n.t("ui.handle_items_that_need_your_review_first"),
|
|
152
|
-
subtitle: window.LoadToAgentI18n.t("ui.only_tasks_waiting_for_your_response_or_choice_are_shown"),
|
|
153
|
-
},
|
|
154
|
-
terminal: {
|
|
155
|
-
eyebrow: window.LoadToAgentI18n.t("ui.continue_an_existing_conversation"), title: window.LoadToAgentI18n.t("ui.continue_ai_sessions_in_the_terminal"),
|
|
156
|
-
subtitle: window.LoadToAgentI18n.t("ui.continue_the_same_task_with_its_previous_conversation_beside_the"),
|
|
157
|
-
},
|
|
158
|
-
tmux: {
|
|
159
|
-
eyebrow: window.LoadToAgentI18n.t("ui.advanced_work_tools"), title: window.LoadToAgentI18n.t("ui.manage_multi_terminal_work_in_one_place"),
|
|
160
|
-
subtitle: window.LoadToAgentI18n.t("ui.this_view_is_only_for_existing_tmux_workflows_home_and"),
|
|
161
|
-
},
|
|
162
|
-
settings: {
|
|
163
|
-
eyebrow: window.LoadToAgentI18n.t("ui.application_management"), title: window.LoadToAgentI18n.t("ui.check_versions_and_updates"),
|
|
164
|
-
subtitle: window.LoadToAgentI18n.t("ui.compare_the_installed_and_latest_stable_versions_then_download_a"),
|
|
165
|
-
},
|
|
166
|
-
};
|
|
180
|
+
});
|
|
167
181
|
const GUIDE_STORAGE_KEY = "loadtoagent:start-guide:v1";
|
|
168
182
|
const GUIDE_STEPS = ["create", "active", "waiting", "detail"];
|
|
169
183
|
function loadGuideState() {
|
|
170
184
|
try {
|
|
171
185
|
const saved = JSON.parse(localStorage.getItem(GUIDE_STORAGE_KEY) || "{}");
|
|
172
186
|
state.guideCompleted = new Set((saved.completed || []).filter((step) => GUIDE_STEPS.includes(step)));
|
|
173
|
-
state.guideExpanded = saved.expanded
|
|
187
|
+
state.guideExpanded = saved.expanded === true;
|
|
174
188
|
} catch (error) {
|
|
175
189
|
reportRecoverableError("guide-state-load", error);
|
|
176
190
|
state.guideCompleted = new Set();
|
|
177
|
-
state.guideExpanded =
|
|
191
|
+
state.guideExpanded = false;
|
|
178
192
|
}
|
|
179
193
|
}
|
|
180
194
|
function saveGuideState() {
|
|
@@ -225,22 +239,39 @@ window.LoadToAgentAppFactories.createCore = function createCore(context = {}) {
|
|
|
225
239
|
else item.removeAttribute("aria-current");
|
|
226
240
|
});
|
|
227
241
|
const advancedView = ["terminal", "tmux", "settings"].includes(state.view);
|
|
242
|
+
const advancedToolsView = ["runtime", "terminal", "tmux"].includes(state.view);
|
|
243
|
+
if (advancedToolsView) $("#advancedToolsNav")?.setAttribute("open", "");
|
|
244
|
+
$("#advancedToolsNav")?.classList.toggle("active", advancedToolsView);
|
|
228
245
|
$("#mobileMoreBtn")?.classList.toggle("active", advancedView);
|
|
229
246
|
if (advancedView) $("#mobileMoreBtn")?.setAttribute("aria-current", "page");
|
|
230
247
|
else $("#mobileMoreBtn")?.removeAttribute("aria-current");
|
|
231
248
|
}
|
|
232
249
|
function selectView(view, options = {}) {
|
|
233
250
|
state.view = view;
|
|
251
|
+
state.managementFilter = view === "waiting" ? (options.managementFilter || "all") : "all";
|
|
234
252
|
state.visibleLimit = 30;
|
|
235
253
|
if (view === "active" || view === "waiting") markGuideStep(view);
|
|
236
254
|
syncViewChrome();
|
|
237
255
|
context.renderSessions(options.motionKind || "view");
|
|
238
|
-
$("#mobileToolsMenu")
|
|
256
|
+
const mobileToolsMenu = $("#mobileToolsMenu");
|
|
257
|
+
if (mobileToolsMenu && !mobileToolsMenu.classList.contains("hidden")) {
|
|
258
|
+
setDialogOpenState(mobileToolsMenu, false);
|
|
259
|
+
mobileToolsMenu.classList.add("hidden");
|
|
260
|
+
}
|
|
239
261
|
$("#mobileMoreBtn")?.setAttribute("aria-expanded", "false");
|
|
240
262
|
document.querySelector(".main-stage")?.scrollTo({ top: 0, behavior: "auto" });
|
|
241
263
|
if (options.focusMain) $("#mainContent")?.focus({ preventScroll: true });
|
|
264
|
+
const resultCount = ["all", "active", "waiting"].includes(view) && typeof context.filteredSessions === "function"
|
|
265
|
+
? context.filteredSessions().length
|
|
266
|
+
: null;
|
|
267
|
+
announce(resultCount == null
|
|
268
|
+
? t("navigation.view_changed", { view: VIEW_TITLES[view] || view })
|
|
269
|
+
: t("navigation.view_results", { view: VIEW_TITLES[view] || view, count: resultCount }));
|
|
242
270
|
}
|
|
243
271
|
function currentDialog() {
|
|
272
|
+
if (!$("#mobileToolsMenu")?.classList.contains("hidden")) return $("#mobileToolsMenu");
|
|
273
|
+
if (!$("#quickPaletteModal")?.classList.contains("hidden")) return $("#quickPaletteModal");
|
|
274
|
+
if (!$("#shortcutHelpModal")?.classList.contains("hidden")) return $("#shortcutHelpModal");
|
|
244
275
|
if (!$("#runModal").classList.contains("hidden")) return $("#runModal");
|
|
245
276
|
if (!$("#tmuxCreateModal").classList.contains("hidden")) return $("#tmuxCreateModal");
|
|
246
277
|
if ($("#detailDrawer").classList.contains("open")) return $("#detailDrawer");
|
|
@@ -272,14 +303,44 @@ window.LoadToAgentAppFactories.createCore = function createCore(context = {}) {
|
|
|
272
303
|
}
|
|
273
304
|
}
|
|
274
305
|
function rememberDialogTrigger() {
|
|
306
|
+
motionState.dialogGeneration += 1;
|
|
275
307
|
motionState.activeDialogTrigger = document.activeElement instanceof HTMLElement ? document.activeElement : null;
|
|
276
308
|
}
|
|
277
|
-
function restoreDialogTrigger() {
|
|
309
|
+
function restoreDialogTrigger(expectedGeneration = null) {
|
|
310
|
+
if (expectedGeneration != null && expectedGeneration !== motionState.dialogGeneration) return false;
|
|
278
311
|
const trigger = motionState.activeDialogTrigger;
|
|
279
312
|
motionState.activeDialogTrigger = null;
|
|
280
|
-
if (trigger && trigger.isConnected) trigger.focus();
|
|
313
|
+
if (trigger && trigger.isConnected) trigger.focus({ preventScroll: true });
|
|
314
|
+
return true;
|
|
315
|
+
}
|
|
316
|
+
function setDialogOpenState(dialog, open) {
|
|
317
|
+
if (!dialog) return;
|
|
318
|
+
const shell = $("#appShell");
|
|
319
|
+
if (open) {
|
|
320
|
+
dialog.removeAttribute("inert");
|
|
321
|
+
dialog.setAttribute("aria-hidden", "false");
|
|
322
|
+
shell?.setAttribute("inert", "");
|
|
323
|
+
document.body.classList.add("dialog-open");
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
dialog.setAttribute("inert", "");
|
|
327
|
+
dialog.setAttribute("aria-hidden", "true");
|
|
328
|
+
const anotherDialog = [$("#mobileToolsMenu"), $("#runModal"), $("#tmuxCreateModal"), $("#detailDrawer"), $("#quickPaletteModal"), $("#shortcutHelpModal")]
|
|
329
|
+
.some((item) => item && item !== dialog && !item.classList.contains("hidden") && (item.classList.contains("open") || item.matches(".modal-backdrop") || item.id === "mobileToolsMenu"));
|
|
330
|
+
if (!anotherDialog) {
|
|
331
|
+
shell?.removeAttribute("inert");
|
|
332
|
+
document.body.classList.remove("dialog-open");
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
function announce(message) {
|
|
336
|
+
const region = $("#globalStatus");
|
|
337
|
+
if (!region) return;
|
|
338
|
+
region.textContent = "";
|
|
339
|
+
requestAnimationFrame(() => {
|
|
340
|
+
region.textContent = String(message || "");
|
|
341
|
+
});
|
|
281
342
|
}
|
|
282
|
-
window.LoadToAgentA11y = { rememberDialogTrigger, restoreDialogTrigger };
|
|
343
|
+
window.LoadToAgentA11y = { rememberDialogTrigger, restoreDialogTrigger, setDialogOpenState, announce };
|
|
283
344
|
function readablePreview(value, maxCharacters = 120) {
|
|
284
345
|
const full = String(value == null ? "" : value)
|
|
285
346
|
.replace(/\s+/g, " ")
|
|
@@ -291,19 +352,22 @@ window.LoadToAgentAppFactories.createCore = function createCore(context = {}) {
|
|
|
291
352
|
return { full, text: `${sample.slice(0, cut).trimEnd()}…`, truncated: true };
|
|
292
353
|
}
|
|
293
354
|
function memoryCategoryLabel(value) {
|
|
294
|
-
const
|
|
295
|
-
|
|
355
|
+
const keys = {
|
|
356
|
+
insight: "content.memory.insight", convention: "content.memory.convention", failure: "content.memory.failure",
|
|
357
|
+
decision: "content.memory.decision", pattern: "content.memory.pattern",
|
|
358
|
+
};
|
|
359
|
+
return keys[String(value || "").toLowerCase()] ? t(keys[String(value || "").toLowerCase()]) : String(value || t("content.memory.record"));
|
|
296
360
|
}
|
|
297
361
|
function jsonValueHtml(value, depth = 0) {
|
|
298
|
-
if (value == null) return
|
|
299
|
-
if (typeof value === "boolean") return `<span class="json-primitive">${value ? "
|
|
362
|
+
if (value == null) return `<span class="json-empty">${esc(t("content.none"))}</span>`;
|
|
363
|
+
if (typeof value === "boolean") return `<span class="json-primitive">${esc(t(value ? "content.yes" : "content.no"))}</span>`;
|
|
300
364
|
if (typeof value === "number") return `<span class="json-primitive">${esc(value.toLocaleString(uiLocale()))}</span>`;
|
|
301
365
|
if (typeof value === "string") return `<span class="json-string">${esc(value)}</span>`;
|
|
302
366
|
if (depth >= 4) return `<span class="json-string">${esc(JSON.stringify(value))}</span>`;
|
|
303
367
|
if (Array.isArray(value)) {
|
|
304
368
|
const shown = value.slice(0, 40);
|
|
305
369
|
const visibleItems = shown.map((item) => `<li>${jsonValueHtml(item, depth + 1)}</li>`).join("");
|
|
306
|
-
const moreItems = value.length > shown.length ? `<li class="json-more"
|
|
370
|
+
const moreItems = value.length > shown.length ? `<li class="json-more">${esc(t("content.more_items", { count: value.length - shown.length }))}</li>` : "";
|
|
307
371
|
return `<ol class="json-array">${visibleItems}${moreItems}</ol>`;
|
|
308
372
|
}
|
|
309
373
|
const entries = Object.entries(value).slice(0, 40);
|
|
@@ -312,8 +376,8 @@ window.LoadToAgentAppFactories.createCore = function createCore(context = {}) {
|
|
|
312
376
|
function memoryCandidatesHtml(items) {
|
|
313
377
|
return `<div class="memory-candidates">
|
|
314
378
|
<div class="structured-heading">
|
|
315
|
-
<b
|
|
316
|
-
<span>${items.length}
|
|
379
|
+
<b>${esc(t("content.memory.to_save"))}</b>
|
|
380
|
+
<span>${esc(t("common.items", { count: items.length }))}</span>
|
|
317
381
|
</div>${items
|
|
318
382
|
.map(
|
|
319
383
|
(item, index) => `<article class="memory-candidate">
|
|
@@ -322,7 +386,7 @@ window.LoadToAgentAppFactories.createCore = function createCore(context = {}) {
|
|
|
322
386
|
<span class="memory-target">${esc(item.target || "MEMORY")}</span>
|
|
323
387
|
<span class="memory-category">${esc(memoryCategoryLabel(item.category))}</span>
|
|
324
388
|
</header>
|
|
325
|
-
<p>${esc(item.content || "
|
|
389
|
+
<p>${esc(item.content || t("content.empty"))}</p>
|
|
326
390
|
</article>`,
|
|
327
391
|
)
|
|
328
392
|
.join("")}</div>`;
|
|
@@ -384,7 +448,7 @@ window.LoadToAgentAppFactories.createCore = function createCore(context = {}) {
|
|
|
384
448
|
if (fence) output.push(`<pre><code>${esc(code.join("\n"))}</code></pre>`);
|
|
385
449
|
return `<div class="chat-content markdown">${output.join("")}</div>`;
|
|
386
450
|
}
|
|
387
|
-
function roadmapHtml(value) {
|
|
451
|
+
function roadmapHtml(value, disclosureId = "") {
|
|
388
452
|
const text = String(value || "").trim();
|
|
389
453
|
const lines = text
|
|
390
454
|
.replace(/\r\n/g, "\n")
|
|
@@ -395,17 +459,18 @@ window.LoadToAgentAppFactories.createCore = function createCore(context = {}) {
|
|
|
395
459
|
const steps = lines.filter((line) => /^(?:[-*]\s+|\d+[.)]\s+|#{2,3}\s+(?!로드맵|roadmap))/i.test(line));
|
|
396
460
|
if (!hasRoadmapSignal || (text.length < 420 && steps.length < 6)) return "";
|
|
397
461
|
const heading = lines.find((line) => /^#{1,3}\s+/.test(line));
|
|
398
|
-
const title = readablePreview((heading || "
|
|
462
|
+
const title = readablePreview((heading || t("content.roadmap.title")).replace(/^#{1,3}\s+/, ""), 72).text;
|
|
399
463
|
const previewSteps = (steps.length ? steps : lines.filter((line) => !/^#{1,3}\s+/.test(line)))
|
|
400
464
|
.slice(0, 3)
|
|
401
465
|
.map((line) => readablePreview(line.replace(/^(?:[-*]\s+|\d+[.)]\s+|#{2,3}\s+)/, "").replace(/\*\*/g, ""), 92).text);
|
|
402
|
-
const countLabel = steps.length ?
|
|
403
|
-
|
|
466
|
+
const countLabel = steps.length ? t("content.roadmap.steps", { count: steps.length }) : t("content.roadmap.long_plan");
|
|
467
|
+
const disclosureKey = `roadmap:${disclosureId || `${text.length}:${title}`}`;
|
|
468
|
+
return `<details class="chat-roadmap" data-roadmap-collapsed="true" data-disclosure-key="${esc(disclosureKey)}">
|
|
404
469
|
<summary>
|
|
405
470
|
<span class="chat-roadmap-mark" aria-hidden="true">MAP</span>
|
|
406
471
|
<span>
|
|
407
472
|
<b>${esc(title)}</b>
|
|
408
|
-
<small>${esc(countLabel)}
|
|
473
|
+
<small>${esc(t("content.roadmap.collapsed", { count: countLabel }))}</small>
|
|
409
474
|
</span>
|
|
410
475
|
<i aria-hidden="true">↓</i>
|
|
411
476
|
</summary>
|
|
@@ -413,9 +478,9 @@ window.LoadToAgentAppFactories.createCore = function createCore(context = {}) {
|
|
|
413
478
|
<div class="chat-roadmap-full">${markdownHtml(text)}</div>
|
|
414
479
|
</details>`;
|
|
415
480
|
}
|
|
416
|
-
function messageContentHtml(message) {
|
|
481
|
+
function messageContentHtml(message, ownerId = "") {
|
|
417
482
|
const text = String((message && message.text) || "").trim();
|
|
418
|
-
if (!text) return
|
|
483
|
+
if (!text) return `<div class="chat-content empty">${esc(t("content.no_displayable_content"))}</div>`;
|
|
419
484
|
if (/^[\[{]/.test(text) && /[\]}]$/.test(text)) {
|
|
420
485
|
try {
|
|
421
486
|
const parsed = JSON.parse(text);
|
|
@@ -426,14 +491,15 @@ window.LoadToAgentAppFactories.createCore = function createCore(context = {}) {
|
|
|
426
491
|
}
|
|
427
492
|
return `<div class="structured-json">
|
|
428
493
|
<div class="structured-heading">
|
|
429
|
-
<b
|
|
430
|
-
<span>${Array.isArray(parsed) ?
|
|
494
|
+
<b>${esc(t("content.structured_data"))}</b>
|
|
495
|
+
<span>${Array.isArray(parsed) ? esc(t("common.items", { count: parsed.length })) : "JSON"}</span>
|
|
431
496
|
</div>${jsonValueHtml(parsed)}</div>`;
|
|
432
497
|
} catch (_plainChatMessage) {
|
|
433
498
|
// A normal chat message may begin with JSON punctuation without being JSON.
|
|
434
499
|
}
|
|
435
500
|
}
|
|
436
|
-
const
|
|
501
|
+
const messageId = message && (message.id || message.timestamp) || text.length;
|
|
502
|
+
const roadmap = message && message.role === "assistant" ? roadmapHtml(text, `${ownerId}:${messageId}`) : "";
|
|
437
503
|
if (roadmap) return roadmap;
|
|
438
504
|
return markdownHtml(text);
|
|
439
505
|
}
|
|
@@ -479,7 +545,7 @@ window.LoadToAgentAppFactories.createCore = function createCore(context = {}) {
|
|
|
479
545
|
return labels[String(value || "").toLowerCase()] || String(value || window.LoadToAgentI18n.t("ui.assistance"));
|
|
480
546
|
}
|
|
481
547
|
function statusClass(status) {
|
|
482
|
-
return ["running", "waiting", "completed", "failed", "cancelled"].includes(status) ? status : "";
|
|
548
|
+
return ["running", "paused", "waiting", "completed", "failed", "cancelled"].includes(status) ? status : "";
|
|
483
549
|
}
|
|
484
550
|
function currentActivity(session) {
|
|
485
551
|
const items = session.lifecycle || [];
|
|
@@ -487,15 +553,22 @@ window.LoadToAgentAppFactories.createCore = function createCore(context = {}) {
|
|
|
487
553
|
const last = running || items[items.length - 1];
|
|
488
554
|
if (last) {
|
|
489
555
|
return {
|
|
490
|
-
title: last.label ||
|
|
556
|
+
title: observedText(last.label || t("ui.activity")), detail: observedText(last.detail || session.statusDetail || ""), type: last.type || "activity",
|
|
491
557
|
};
|
|
492
558
|
}
|
|
493
559
|
const message = (session.messages || [])[session.messages.length - 1];
|
|
494
|
-
return { title: session.statusDetail ||
|
|
560
|
+
return { title: observedText(session.statusDetail || t("ui.temporarily_idle")), detail: observedText((message && message.text) || ""), type: "activity" };
|
|
495
561
|
}
|
|
496
562
|
function isLiveSession(session) {
|
|
497
563
|
return session && (session.status === "running" || session.status === "starting");
|
|
498
564
|
}
|
|
565
|
+
function isRuntimeLoopSession(session) {
|
|
566
|
+
if (!session || session.parentId || !isLiveSession(session)) return false;
|
|
567
|
+
if (session.loop === true || (session.loop && typeof session.loop === "object")) return true;
|
|
568
|
+
const ids = new Set([String(session.id || ""), String(session.externalId || "")].filter(Boolean));
|
|
569
|
+
return (state.snapshot?.automations || []).some((automation) =>
|
|
570
|
+
automation.enabled && automation.targetThreadId && ids.has(String(automation.targetThreadId)));
|
|
571
|
+
}
|
|
499
572
|
function subagentWorkState(session) {
|
|
500
573
|
if (isLiveSession(session)) return "working";
|
|
501
574
|
if (session && session.status === "failed") return "attention";
|
|
@@ -514,13 +587,13 @@ window.LoadToAgentAppFactories.createCore = function createCore(context = {}) {
|
|
|
514
587
|
try {
|
|
515
588
|
const parsed = JSON.parse(text);
|
|
516
589
|
if (!parsed || Array.isArray(parsed) || typeof parsed !== "object") return text;
|
|
517
|
-
if (parsed.cell_id) return
|
|
518
|
-
if (parsed.command) return
|
|
519
|
-
if (parsed.path || parsed.file_path) return
|
|
520
|
-
if (parsed.prompt) return
|
|
590
|
+
if (parsed.cell_id) return t("activity.waiting_for_result", { wait: parsed.yield_time_ms ? t("activity.max_seconds", { count: Math.round(Number(parsed.yield_time_ms) / 1000) }) : "" });
|
|
591
|
+
if (parsed.command) return t("activity.command", { value: String(parsed.command).replace(/\s+/g, " ").slice(0, 180) });
|
|
592
|
+
if (parsed.path || parsed.file_path) return t("activity.file", { value: parsed.path || parsed.file_path });
|
|
593
|
+
if (parsed.prompt) return t("activity.delegated", { value: String(parsed.prompt).replace(/\s+/g, " ").slice(0, 180) });
|
|
521
594
|
const summary = Object.entries(parsed)
|
|
522
595
|
.slice(0, 3)
|
|
523
|
-
.map(([key, item]) => `${key}: ${typeof item === "object" ? "
|
|
596
|
+
.map(([key, item]) => `${key}: ${typeof item === "object" ? t("activity.structured_data") : item}`)
|
|
524
597
|
.join(" · ");
|
|
525
598
|
return summary || text;
|
|
526
599
|
} catch (_malformedCommandPreview) {
|
|
@@ -531,16 +604,16 @@ window.LoadToAgentAppFactories.createCore = function createCore(context = {}) {
|
|
|
531
604
|
function latestWorkCopy(session) {
|
|
532
605
|
const delegation = session.delegation || {};
|
|
533
606
|
const completedResult = delegation.result || session.result;
|
|
534
|
-
if (session.status === "completed" && completedResult) return
|
|
535
|
-
if (session.status === "completed") return "
|
|
607
|
+
if (session.status === "completed" && completedResult) return t("activity.completed_result", { value: completedResult });
|
|
608
|
+
if (session.status === "completed") return t("activity.completed_returned");
|
|
536
609
|
const activity = currentActivity(session);
|
|
537
610
|
if (activity.detail) return readableActivityDetail(activity.detail);
|
|
538
611
|
const messages = session.messages || [];
|
|
539
612
|
const assistant = [...messages].reverse().find((item) => item.role === "assistant" && item.text);
|
|
540
613
|
if (assistant) return assistant.text;
|
|
541
614
|
const tool = [...messages].reverse().find((item) => item.role === "tool");
|
|
542
|
-
if (tool) return
|
|
543
|
-
return activity.title || session.statusDetail || "
|
|
615
|
+
if (tool) return t("activity.tool_execution", { tool: tool.title || t("session.tool"), value: tool.text || t("activity.waiting_for_result_short") });
|
|
616
|
+
return activity.title || session.statusDetail || t("activity.waiting_for_next");
|
|
544
617
|
}
|
|
545
618
|
function statusIcon(type) {
|
|
546
619
|
if (/tool/.test(type)) return "⌘";
|
|
@@ -557,10 +630,13 @@ window.LoadToAgentAppFactories.createCore = function createCore(context = {}) {
|
|
|
557
630
|
uiLocale,
|
|
558
631
|
providerLabel,
|
|
559
632
|
reportRecoverableError,
|
|
633
|
+
observedText,
|
|
560
634
|
PROJECTLESS_WORKSPACE,
|
|
561
635
|
state,
|
|
562
636
|
motionPreference,
|
|
563
637
|
motionState,
|
|
638
|
+
rememberDisclosureStates,
|
|
639
|
+
restoreDisclosureStates,
|
|
564
640
|
STATUS,
|
|
565
641
|
VIEW_TITLES,
|
|
566
642
|
VIEW_META,
|
|
@@ -581,6 +657,8 @@ window.LoadToAgentAppFactories.createCore = function createCore(context = {}) {
|
|
|
581
657
|
trapDialogFocus,
|
|
582
658
|
rememberDialogTrigger,
|
|
583
659
|
restoreDialogTrigger,
|
|
660
|
+
setDialogOpenState,
|
|
661
|
+
announce,
|
|
584
662
|
readablePreview,
|
|
585
663
|
memoryCategoryLabel,
|
|
586
664
|
jsonValueHtml,
|
|
@@ -599,6 +677,7 @@ window.LoadToAgentAppFactories.createCore = function createCore(context = {}) {
|
|
|
599
677
|
statusClass,
|
|
600
678
|
currentActivity,
|
|
601
679
|
isLiveSession,
|
|
680
|
+
isRuntimeLoopSession,
|
|
602
681
|
subagentWorkState,
|
|
603
682
|
subagentWorkLabel,
|
|
604
683
|
readableActivityDetail,
|