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,8 @@
|
|
|
3
3
|
window.LoadToAgentAppFactories = window.LoadToAgentAppFactories || {};
|
|
4
4
|
|
|
5
5
|
window.LoadToAgentAppFactories.createAgentActions = function createAgentActions(context = {}) {
|
|
6
|
+
const t = (key, params) => window.LoadToAgentI18n.t(key, params);
|
|
7
|
+
const errorText = (error, key, params) => window.LoadToAgentI18n.errorText(error, key, params);
|
|
6
8
|
const {
|
|
7
9
|
$,
|
|
8
10
|
esc,
|
|
@@ -27,18 +29,18 @@ window.LoadToAgentAppFactories.createAgentActions = function createAgentActions(
|
|
|
27
29
|
try {
|
|
28
30
|
return window.LoadToAgentTerminal && typeof window.LoadToAgentTerminal.resumeSupport === "function"
|
|
29
31
|
? window.LoadToAgentTerminal.resumeSupport(session)
|
|
30
|
-
: { supported: false, reason: "
|
|
32
|
+
: { supported: false, reason: t("agent.resume_preparing") };
|
|
31
33
|
} catch (error) {
|
|
32
|
-
return { supported: false, reason: (error
|
|
34
|
+
return { supported: false, reason: errorText(error, "agent.resume_check_failed") };
|
|
33
35
|
}
|
|
34
36
|
}
|
|
35
37
|
|
|
36
38
|
function originAppInfo(session) {
|
|
37
39
|
if (session && session.provider === "codex" && session.clientKind === "codex-desktop") {
|
|
38
|
-
return { provider: "Codex", label: "
|
|
40
|
+
return { provider: "Codex", label: t("agent.desktop_app", { provider: "Codex" }) };
|
|
39
41
|
}
|
|
40
42
|
if (session && session.provider === "claude" && session.clientKind === "claude-desktop") {
|
|
41
|
-
return { provider: "Claude", label: "
|
|
43
|
+
return { provider: "Claude", label: t("agent.desktop_app", { provider: "Claude" }) };
|
|
42
44
|
}
|
|
43
45
|
return null;
|
|
44
46
|
}
|
|
@@ -64,71 +66,71 @@ window.LoadToAgentAppFactories.createAgentActions = function createAgentActions(
|
|
|
64
66
|
const origin = originAppInfo(session);
|
|
65
67
|
const status =
|
|
66
68
|
mode === "direct"
|
|
67
|
-
?
|
|
69
|
+
? t("agent.direct_status", { target: targets.length === 1 ? target.label : t("agent.choose_terminal_count", { count: targets.length }) })
|
|
68
70
|
: mode === "handoff"
|
|
69
|
-
? "
|
|
71
|
+
? t("agent.handoff_status")
|
|
70
72
|
: mode === "resume"
|
|
71
|
-
? "
|
|
73
|
+
? t("agent.resume_status")
|
|
72
74
|
: mode === "origin-resume"
|
|
73
|
-
? "
|
|
75
|
+
? t("agent.origin_resume_status")
|
|
74
76
|
: mode === "connect"
|
|
75
|
-
? "
|
|
77
|
+
? t("agent.connect_status")
|
|
76
78
|
: mode === "origin"
|
|
77
|
-
? "
|
|
79
|
+
? t("agent.origin_status")
|
|
78
80
|
: window.LoadToAgentI18n.t("ui.ended_session");
|
|
79
81
|
const help =
|
|
80
82
|
mode === "direct"
|
|
81
|
-
? "
|
|
83
|
+
? t("agent.direct_help")
|
|
82
84
|
: mode === "handoff"
|
|
83
|
-
? "
|
|
85
|
+
? t("agent.handoff_help")
|
|
84
86
|
: mode === "resume"
|
|
85
|
-
? "
|
|
87
|
+
? t("agent.resume_help")
|
|
86
88
|
: mode === "origin-resume"
|
|
87
|
-
?
|
|
89
|
+
? t("agent.origin_resume_help", { provider: (origin && origin.provider) || t("agent.desktop") })
|
|
88
90
|
: mode === "connect"
|
|
89
|
-
?
|
|
91
|
+
? t("agent.connect_help", { provider: session.provider })
|
|
90
92
|
: mode === "origin"
|
|
91
|
-
?
|
|
92
|
-
: agentResumeSupport(session).reason || "
|
|
93
|
+
? t("agent.origin_help", { app: (origin && origin.label) || t("agent.desktop_app", { provider: "" }).trim() })
|
|
94
|
+
: agentResumeSupport(session).reason || t("agent.resume_method_unknown");
|
|
93
95
|
const picker =
|
|
94
96
|
targets.length > 1
|
|
95
97
|
? `<label class="agent-command-target">
|
|
96
|
-
<span
|
|
98
|
+
<span>${esc(t("agent.target_terminal"))}</span>
|
|
97
99
|
<select data-agent-command-target="${esc(session.id)}">
|
|
98
|
-
<option value=""
|
|
100
|
+
<option value="">${esc(t("agent.choose_terminal"))}</option>
|
|
99
101
|
${targets.map((item) => `<option value="${esc(item.id)}" ${item.id === targetId ? "selected" : ""}>${esc(item.label)}</option>`).join("")}
|
|
100
102
|
</select>
|
|
101
103
|
</label>`
|
|
102
104
|
: "";
|
|
103
|
-
const originAction = `<button type="button" data-agent-open-origin="${esc(session.id)}"
|
|
105
|
+
const originAction = `<button type="button" data-agent-open-origin="${esc(session.id)}">${esc(t("agent.continue_in_origin", { provider: (origin && origin.provider) || t("agent.desktop") }))}</button>`;
|
|
104
106
|
const actions =
|
|
105
107
|
mode === "direct"
|
|
106
|
-
? `<button type="button" data-agent-terminal-open="${esc(session.id)}" ${canSend ? "" : "disabled"}
|
|
107
|
-
<button type="submit" ${canSend ? "" : "disabled"}>${sending ? "
|
|
108
|
+
? `<button type="button" data-agent-terminal-open="${esc(session.id)}" ${canSend ? "" : "disabled"}>${esc(t("agent.open_terminal"))}</button>
|
|
109
|
+
<button type="submit" ${canSend ? "" : "disabled"}>${esc(t(sending ? "agent.sending" : "agent.send_now"))}</button>`
|
|
108
110
|
: mode === "resume"
|
|
109
|
-
? `<button type="submit" ${canSend ? "" : "disabled"}>${sending ? "
|
|
111
|
+
? `<button type="submit" ${canSend ? "" : "disabled"}>${esc(t(sending ? "agent.restoring" : "agent.restore_and_send"))}</button>`
|
|
110
112
|
: mode === "handoff"
|
|
111
|
-
? `<button type="submit" ${canSend ? "" : "disabled"}>${sending ? "
|
|
113
|
+
? `<button type="submit" ${canSend ? "" : "disabled"}>${esc(t(sending ? "agent.handing_off" : "agent.handoff_and_send"))}</button>`
|
|
112
114
|
: mode === "origin-resume"
|
|
113
|
-
? `${originAction}<button type="submit" ${canSend ? "" : "disabled"}>${sending ? "
|
|
115
|
+
? `${originAction}<button type="submit" ${canSend ? "" : "disabled"}>${esc(t(sending ? "agent.connecting" : "agent.background_and_send"))}</button>`
|
|
114
116
|
: mode === "connect"
|
|
115
|
-
? `<button type="button" data-agent-bridge-copy="${esc(session.provider)}"
|
|
117
|
+
? `<button type="button" data-agent-bridge-copy="${esc(session.provider)}">${esc(t("agent.copy_bridge"))}</button>`
|
|
116
118
|
: mode === "origin"
|
|
117
119
|
? originAction
|
|
118
120
|
: "";
|
|
119
121
|
const editable = ["direct", "resume", "handoff", "origin-resume"].includes(mode);
|
|
120
|
-
const placeholder = editable ? "
|
|
122
|
+
const placeholder = editable ? t("agent.command_example") : status;
|
|
121
123
|
const availabilityClass = mode === "direct" ? "connected" : ["resume", "handoff", "origin-resume"].includes(mode) ? "resume-ready" : "unavailable";
|
|
122
124
|
return `<form class="agent-command-panel ${availabilityClass} control-${mode}"
|
|
123
125
|
data-agent-command-form="${esc(session.id)}">
|
|
124
126
|
<header>
|
|
125
127
|
<span class="agent-command-icon" aria-hidden="true">›_</span>
|
|
126
|
-
<span><b
|
|
128
|
+
<span><b>${esc(t("agent.command_title"))}</b><small>${esc(status)}</small></span>
|
|
127
129
|
<i class="${mode === "direct" ? "connected" : ""}" aria-hidden="true"></i>
|
|
128
130
|
</header>
|
|
129
131
|
${picker}
|
|
130
132
|
<label class="agent-command-input">
|
|
131
|
-
<span class="sr-only"
|
|
133
|
+
<span class="sr-only">${esc(t("agent.command_sr"))}</span>
|
|
132
134
|
<textarea data-agent-command-draft="${esc(session.id)}" maxlength="8000" rows="3"
|
|
133
135
|
placeholder="${esc(placeholder)}" ${editable ? "" : "disabled"}>${editable ? esc(draft) : ""}</textarea>
|
|
134
136
|
</label>
|
|
@@ -156,9 +158,9 @@ window.LoadToAgentAppFactories.createAgentActions = function createAgentActions(
|
|
|
156
158
|
async function resumeAgentTerminal(sessionId, sendDraft = false) {
|
|
157
159
|
if (state.agentCommandSending.has(sessionId)) return;
|
|
158
160
|
const session = snapshotSession(sessionId) || state.details.get(sessionId);
|
|
159
|
-
if (!session || !window.LoadToAgentTerminal) return context.toast("
|
|
161
|
+
if (!session || !window.LoadToAgentTerminal) return context.toast(t("agent.session_not_found"));
|
|
160
162
|
const support = agentResumeSupport(session);
|
|
161
|
-
if (!support.supported) return context.toast(support.reason || "
|
|
163
|
+
if (!support.supported) return context.toast(support.reason || t("agent.cannot_reconnect"));
|
|
162
164
|
state.agentCommandSending.add(sessionId);
|
|
163
165
|
try {
|
|
164
166
|
if ($("#detailDrawer").classList.contains("open")) context.closeDrawer(false);
|
|
@@ -167,9 +169,9 @@ window.LoadToAgentAppFactories.createAgentActions = function createAgentActions(
|
|
|
167
169
|
await window.LoadToAgentTerminal.resumeForAgent(session, draft, sendDraft);
|
|
168
170
|
if (sendDraft && draft.trim()) state.agentCommandDrafts.delete(sessionId);
|
|
169
171
|
document.querySelector(".main-stage")?.scrollTo({ top: 0, behavior: "auto" });
|
|
170
|
-
context.toast(
|
|
172
|
+
context.toast(t("agent.reconnected", { provider: providerInfo(session.provider).label }));
|
|
171
173
|
} catch (error) {
|
|
172
|
-
context.toast((error
|
|
174
|
+
context.toast(errorText(error, "agent.reconnect_failed"));
|
|
173
175
|
} finally {
|
|
174
176
|
state.agentCommandSending.delete(sessionId);
|
|
175
177
|
}
|
|
@@ -178,31 +180,31 @@ window.LoadToAgentAppFactories.createAgentActions = function createAgentActions(
|
|
|
178
180
|
async function dispatchAgentCommand(sessionId, form) {
|
|
179
181
|
if (state.agentCommandSending.has(sessionId)) return;
|
|
180
182
|
const session = snapshotSession(sessionId);
|
|
181
|
-
if (!session || !window.LoadToAgentTerminal) return context.toast("
|
|
183
|
+
if (!session || !window.LoadToAgentTerminal) return context.toast(t("agent.latest_not_found"));
|
|
182
184
|
const mode = agentControlMode(session, agentCommandTargets(session));
|
|
183
185
|
if (mode === "resume" || mode === "handoff" || mode === "origin-resume") {
|
|
184
186
|
const input = form.querySelector("[data-agent-command-draft]");
|
|
185
187
|
if (input) state.agentCommandDrafts.set(sessionId, input.value);
|
|
186
|
-
if (!String((input && input.value) || "").trim()) return context.toast("
|
|
188
|
+
if (!String((input && input.value) || "").trim()) return context.toast(t("agent.enter_command"));
|
|
187
189
|
return resumeAgentTerminal(sessionId, true);
|
|
188
190
|
}
|
|
189
191
|
const target = chosenAgentCommandTarget(session);
|
|
190
192
|
const input = form.querySelector("[data-agent-command-draft]");
|
|
191
193
|
const command = String((input && input.value) || "").trim();
|
|
192
194
|
if (!target)
|
|
193
|
-
return context.toast(agentCommandTargets(session).length ? "
|
|
194
|
-
if (!command) return context.toast("
|
|
195
|
+
return context.toast(t(agentCommandTargets(session).length ? "agent.select_target_first" : "agent.no_writable_terminal"));
|
|
196
|
+
if (!command) return context.toast(t("agent.enter_command"));
|
|
195
197
|
state.agentCommandSending.add(sessionId);
|
|
196
198
|
const submit = form.querySelector('[type="submit"]');
|
|
197
199
|
if (submit) {
|
|
198
200
|
submit.disabled = true;
|
|
199
|
-
submit.textContent = "
|
|
201
|
+
submit.textContent = t("agent.sending");
|
|
200
202
|
}
|
|
201
203
|
try {
|
|
202
204
|
await window.LoadToAgentTerminal.dispatchAgentCommand(session, command, target.id);
|
|
203
205
|
state.agentCommandDrafts.delete(sessionId);
|
|
204
206
|
if (input) input.value = "";
|
|
205
|
-
context.toast(
|
|
207
|
+
context.toast(t("agent.command_sent", { target: target.label }));
|
|
206
208
|
} catch (error) {
|
|
207
209
|
const latest = snapshotSession(sessionId) || session;
|
|
208
210
|
const support = agentResumeSupport(latest);
|
|
@@ -212,61 +214,128 @@ window.LoadToAgentAppFactories.createAgentActions = function createAgentActions(
|
|
|
212
214
|
await window.LoadToAgentTerminal.resumeForAgent(latest, command, true);
|
|
213
215
|
state.agentCommandDrafts.delete(sessionId);
|
|
214
216
|
if (input) input.value = "";
|
|
215
|
-
context.toast("
|
|
217
|
+
context.toast(t("agent.recovered_and_sent"));
|
|
216
218
|
return;
|
|
217
219
|
} catch (resumeError) {
|
|
218
|
-
context.toast((resumeError
|
|
220
|
+
context.toast(errorText(resumeError, "agent.recovery_failed"));
|
|
219
221
|
}
|
|
220
|
-
} else context.toast((error
|
|
222
|
+
} else context.toast(errorText(error, "agent.send_failed"));
|
|
221
223
|
} finally {
|
|
222
224
|
state.agentCommandSending.delete(sessionId);
|
|
223
225
|
if (submit && submit.isConnected) {
|
|
224
226
|
submit.disabled = false;
|
|
225
|
-
submit.textContent = "
|
|
227
|
+
submit.textContent = t("agent.send_now");
|
|
226
228
|
}
|
|
227
229
|
}
|
|
228
230
|
}
|
|
229
231
|
|
|
230
232
|
async function openAgentTerminal(sessionId) {
|
|
231
233
|
const session = snapshotSession(sessionId);
|
|
232
|
-
if (!session || !window.LoadToAgentTerminal) return context.toast("
|
|
234
|
+
if (!session || !window.LoadToAgentTerminal) return context.toast(t("agent.terminal_info_not_found"));
|
|
233
235
|
const target = chosenAgentCommandTarget(session);
|
|
234
236
|
if (!target)
|
|
235
|
-
return context.toast(agentCommandTargets(session).length ? "
|
|
237
|
+
return context.toast(t(agentCommandTargets(session).length ? "agent.select_open_target" : "agent.no_writable_terminal"));
|
|
236
238
|
selectView(target.kind === "tmux" ? "tmux" : "terminal");
|
|
237
239
|
try {
|
|
238
240
|
await window.LoadToAgentTerminal.openForAgent(session, target.id, state.agentCommandDrafts.get(sessionId) || "");
|
|
239
241
|
document.querySelector(".main-stage")?.scrollTo({ top: 0, behavior: "auto" });
|
|
240
242
|
} catch (error) {
|
|
241
|
-
context.toast((error
|
|
243
|
+
context.toast(errorText(error, "agent.open_terminal_failed"));
|
|
242
244
|
}
|
|
243
245
|
}
|
|
244
246
|
|
|
245
247
|
async function copyBridgeCommand(provider) {
|
|
246
248
|
try {
|
|
247
249
|
const result = await window.loadtoagent.bridgeCommand(provider);
|
|
248
|
-
if (!result || !result.ok) throw new Error("
|
|
250
|
+
if (!result || !result.ok) throw new Error(t("agent.bridge_create_failed"));
|
|
249
251
|
const command = result.command;
|
|
250
252
|
await window.loadtoagent.writeClipboard(command);
|
|
251
|
-
context.toast(
|
|
253
|
+
context.toast(t("agent.command_copied", { command }));
|
|
252
254
|
} catch (error) {
|
|
253
|
-
context.toast((error
|
|
255
|
+
context.toast(errorText(error, "agent.bridge_copy_failed"));
|
|
254
256
|
}
|
|
255
257
|
}
|
|
256
258
|
|
|
257
259
|
async function openSessionOrigin(sessionId) {
|
|
258
260
|
const session = snapshotSession(sessionId);
|
|
259
261
|
const origin = originAppInfo(session);
|
|
260
|
-
if (!session || !origin) return context.toast("
|
|
262
|
+
if (!session || !origin) return context.toast(t("agent.origin_not_found"));
|
|
261
263
|
try {
|
|
262
264
|
const result = await window.loadtoagent.openSessionOrigin(session);
|
|
263
|
-
if (!result || !result.ok) return context.toast(
|
|
264
|
-
context.toast(
|
|
265
|
+
if (!result || !result.ok) return context.toast(t("agent.origin_cannot_open", { app: origin.label }));
|
|
266
|
+
context.toast(t("agent.origin_opened", { provider: origin.provider }));
|
|
265
267
|
} catch (error) {
|
|
266
|
-
context.toast((error
|
|
268
|
+
context.toast(errorText(error, "agent.origin_open_failed", { app: origin.label }));
|
|
267
269
|
}
|
|
268
270
|
}
|
|
269
271
|
|
|
272
|
+
async function controlManagedRun(sessionId, action) {
|
|
273
|
+
const session = snapshotSession(sessionId) || state.details.get(sessionId);
|
|
274
|
+
const runId = session && session.runId;
|
|
275
|
+
const key = `${runId || sessionId}:${action}`;
|
|
276
|
+
if (!session || !runId || state.runControlRequests.has(key)) return;
|
|
277
|
+
const methods = {
|
|
278
|
+
stop: "stopAgent",
|
|
279
|
+
pause: "pauseAgent",
|
|
280
|
+
resume: "resumeAgentRun",
|
|
281
|
+
retry: "retryAgent",
|
|
282
|
+
};
|
|
283
|
+
const method = methods[action];
|
|
284
|
+
if (!method || typeof window.loadtoagent?.[method] !== "function") return context.toast(t("management.control_unavailable"));
|
|
285
|
+
state.runControlRequests.add(key);
|
|
286
|
+
if (action === "stop") state.stopRequests.add(runId);
|
|
287
|
+
context.renderDrawer?.();
|
|
288
|
+
try {
|
|
289
|
+
const result = await window.loadtoagent[method](runId);
|
|
290
|
+
if (!result || result.ok === false) throw new Error(result && result.error || t("management.control_failed"));
|
|
291
|
+
context.toast(t(`management.control_${action}_sent`));
|
|
292
|
+
} catch (error) {
|
|
293
|
+
context.toast(errorText(error, "management.control_failed"));
|
|
294
|
+
} finally {
|
|
295
|
+
state.runControlRequests.delete(key);
|
|
296
|
+
state.stopRequests.delete(runId);
|
|
297
|
+
if (state.selectedId) context.renderDrawer?.();
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
function quickRespond(sessionId, value, root = document) {
|
|
302
|
+
const command = String(value || "").trim();
|
|
303
|
+
if (!command) return;
|
|
304
|
+
state.agentCommandDrafts.set(sessionId, command);
|
|
305
|
+
const form = root.querySelector?.(`[data-agent-command-form="${CSS.escape(sessionId)}"]`);
|
|
306
|
+
const input = form?.querySelector("[data-agent-command-draft]");
|
|
307
|
+
if (input) input.value = command;
|
|
308
|
+
if (form) form.requestSubmit();
|
|
309
|
+
else {
|
|
310
|
+
state.drawerTab = "summary";
|
|
311
|
+
context.openDrawer?.(sessionId);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
function prepareReassignment(sessionId) {
|
|
316
|
+
const session = snapshotSession(sessionId) || state.details.get(sessionId);
|
|
317
|
+
if (!session) return context.toast(t("agent.session_not_found"));
|
|
318
|
+
const provider = (context.visibleProviders?.() || state.providers)
|
|
319
|
+
.find(item => item.id !== session.provider && state.availability[item.id]);
|
|
320
|
+
if (!provider) return context.toast(t("management.reassign_unavailable"));
|
|
321
|
+
state.runProvider = provider.id;
|
|
322
|
+
context.closeDrawer?.(false);
|
|
323
|
+
context.openRunModal?.();
|
|
324
|
+
const request = session.sharedGoal
|
|
325
|
+
|| session.delegation?.assignment
|
|
326
|
+
|| [...(session.messages || [])].find(message => message.role === "user" && message.text)?.text
|
|
327
|
+
|| session.title;
|
|
328
|
+
const task = request && request !== session.title ? `${session.title}\n\n${request}` : (request || session.title);
|
|
329
|
+
const prompt = t("management.reassign_prompt", { task, provider: providerInfo(session.provider).label });
|
|
330
|
+
const promptInput = $("#runPrompt");
|
|
331
|
+
const cwdInput = $("#runCwd");
|
|
332
|
+
if (promptInput) promptInput.value = prompt;
|
|
333
|
+
if (cwdInput && session.cwd) cwdInput.value = session.cwd;
|
|
334
|
+
$("#runProviderPicker") && ($("#runProviderPicker").innerHTML = context.providerPickerHtml?.() || "");
|
|
335
|
+
context.syncRunComposer?.();
|
|
336
|
+
promptInput?.focus({ preventScroll: true });
|
|
337
|
+
}
|
|
338
|
+
|
|
270
339
|
return {
|
|
271
340
|
agentCommandTargets,
|
|
272
341
|
agentResumeSupport,
|
|
@@ -281,5 +350,8 @@ window.LoadToAgentAppFactories.createAgentActions = function createAgentActions(
|
|
|
281
350
|
openAgentTerminal,
|
|
282
351
|
copyBridgeCommand,
|
|
283
352
|
openSessionOrigin,
|
|
353
|
+
controlManagedRun,
|
|
354
|
+
quickRespond,
|
|
355
|
+
prepareReassignment,
|
|
284
356
|
};
|
|
285
357
|
};
|
|
@@ -2,26 +2,31 @@
|
|
|
2
2
|
|
|
3
3
|
(() => {
|
|
4
4
|
const factories = window.LoadToAgentAppFactories || {};
|
|
5
|
+
const t = (key, params) => window.LoadToAgentI18n.t(key, params);
|
|
5
6
|
const app = {};
|
|
6
7
|
const install = (name) => {
|
|
7
|
-
if (typeof factories[name] !== "function") throw new Error(
|
|
8
|
+
if (typeof factories[name] !== "function") throw new Error(t("bootstrap.module_missing", { name }));
|
|
8
9
|
Object.assign(app, factories[name](app));
|
|
9
10
|
};
|
|
10
11
|
|
|
11
12
|
[
|
|
12
13
|
"createCore",
|
|
14
|
+
"createProviderVisibility",
|
|
13
15
|
"createDashboard",
|
|
16
|
+
"createRuntimeOverview",
|
|
14
17
|
"createGraphModel",
|
|
15
18
|
"createGraphView",
|
|
16
19
|
"createGraphLayout",
|
|
17
20
|
"createGraphOrchestration",
|
|
18
21
|
"createTmuxRenderer",
|
|
19
|
-
"createSessionRenderer",
|
|
20
22
|
"createAgentActions",
|
|
23
|
+
"createManagement",
|
|
24
|
+
"createSessionRenderer",
|
|
21
25
|
"createDrawerData",
|
|
22
26
|
"createDrawerContent",
|
|
23
27
|
"createDrawer",
|
|
24
28
|
"createRunModal",
|
|
29
|
+
"createQualityEnhancements",
|
|
25
30
|
"createNavigationEventBindings",
|
|
26
31
|
"createSessionEventBindings",
|
|
27
32
|
"createFilterEventBindings",
|
|
@@ -30,32 +35,70 @@
|
|
|
30
35
|
].forEach(install);
|
|
31
36
|
window.LoadToAgentApp = app;
|
|
32
37
|
|
|
33
|
-
const { $, esc, state, loadGuideState, bindEvents, render, timeOnly, loadSessionDetail, renderUpdateSettings, syncViewChrome, selectView, openDrawer, openSubagentConversation, toast } = app;
|
|
38
|
+
const { $, esc, state, loadGuideState, loadQualityState = () => {}, loadProviderVisibility, projectVisibleSnapshot, visibleSnapshot, isProviderVisible, bindEvents, render, timeOnly, loadSessionDetail, renderUpdateSettings, syncViewChrome, selectView, openDrawer, openSubagentConversation, toast } = app;
|
|
39
|
+
|
|
40
|
+
let initializationError = "";
|
|
41
|
+
const showInitializationError = (message) => {
|
|
42
|
+
initializationError = String(message || t("ui.connection_failed"));
|
|
43
|
+
$("#lastSync").textContent = t("ui.connection_failed");
|
|
44
|
+
$("#appConnectionState")?.classList.add("connection-error");
|
|
45
|
+
$("#appErrorMessage").textContent = initializationError;
|
|
46
|
+
$("#appErrorBanner").classList.remove("hidden");
|
|
47
|
+
};
|
|
48
|
+
$("#appRetryBtn")?.addEventListener("click", () => window.location.reload());
|
|
49
|
+
$("#appErrorCopyBtn")?.addEventListener("click", async () => {
|
|
50
|
+
try {
|
|
51
|
+
await navigator.clipboard.writeText(initializationError);
|
|
52
|
+
toast(t("quality.copy_success"));
|
|
53
|
+
} catch (error) {
|
|
54
|
+
window.LoadToAgentRendererUtils.reportRecoverableError("initialization-error-copy", error);
|
|
55
|
+
toast(t("quality.copy_failed"));
|
|
56
|
+
}
|
|
57
|
+
});
|
|
34
58
|
|
|
35
59
|
async function init() {
|
|
60
|
+
loadQualityState();
|
|
36
61
|
loadGuideState();
|
|
37
62
|
if (!window.loadtoagent) {
|
|
38
63
|
$("#emptyState").classList.remove("hidden");
|
|
39
|
-
$("#emptyState p").textContent = "
|
|
64
|
+
$("#emptyState p").textContent = t("bootstrap.open_in_app");
|
|
65
|
+
showInitializationError(t("bootstrap.open_in_app"));
|
|
40
66
|
return;
|
|
41
67
|
}
|
|
42
68
|
const bootstrap = await window.loadtoagent.bootstrap();
|
|
43
69
|
if (window.loadtoagent.setLocale) await window.loadtoagent.setLocale(window.LoadToAgentI18n?.getLocale() || "ko");
|
|
44
70
|
state.providers = bootstrap.providers || [];
|
|
45
71
|
state.providerMap = new Map(state.providers.map((provider) => [provider.id, provider]));
|
|
72
|
+
loadProviderVisibility(bootstrap.providerVisibility);
|
|
46
73
|
state.availability = bootstrap.availability || {};
|
|
47
74
|
state.workspaces = bootstrap.workspaces || [];
|
|
48
|
-
state.
|
|
75
|
+
state.rawSnapshot = bootstrap.snapshot;
|
|
76
|
+
state.snapshot = projectVisibleSnapshot(bootstrap.snapshot);
|
|
49
77
|
state.activeRuns = bootstrap.activeRuns || [];
|
|
50
78
|
state.platform = bootstrap.platform || state.platform;
|
|
51
79
|
state.versions = bootstrap.versions || {};
|
|
52
80
|
state.update = bootstrap.update || { status: "idle", currentVersion: state.versions.app || "" };
|
|
81
|
+
const handleAttentionRequested = (payload) => {
|
|
82
|
+
const sessionId = String(payload && payload.sessionId || '');
|
|
83
|
+
const session = (state.snapshot && state.snapshot.sessions || []).find(item => item.id === sessionId);
|
|
84
|
+
if (session && !isProviderVisible(session.provider)) return;
|
|
85
|
+
selectView('waiting');
|
|
86
|
+
if (session) {
|
|
87
|
+
if (session.parentId) openSubagentConversation(session.id);
|
|
88
|
+
else openDrawer(session.id);
|
|
89
|
+
} else toast(t("bootstrap.opened_attention_list"));
|
|
90
|
+
};
|
|
91
|
+
if (window.loadtoagent.onAttentionRequested) window.loadtoagent.onAttentionRequested(handleAttentionRequested);
|
|
53
92
|
bindEvents();
|
|
54
93
|
render();
|
|
94
|
+
$("#appConnectionState")?.classList.remove("connection-error");
|
|
95
|
+
$("#appErrorBanner").classList.add("hidden");
|
|
96
|
+
app.initialized = true;
|
|
55
97
|
$("#lastSync").textContent = timeOnly(state.snapshot && state.snapshot.generatedAt);
|
|
56
98
|
window.loadtoagent.onSnapshot((snapshot) => {
|
|
57
|
-
state.
|
|
58
|
-
|
|
99
|
+
state.rawSnapshot = snapshot;
|
|
100
|
+
state.snapshot = projectVisibleSnapshot(snapshot);
|
|
101
|
+
if (window.LoadToAgentTerminal) window.LoadToAgentTerminal.updateSnapshot(visibleSnapshot(), state.workspaces);
|
|
59
102
|
$("#lastSync").textContent = timeOnly(snapshot.generatedAt);
|
|
60
103
|
render();
|
|
61
104
|
if (state.selectedId && $("#detailDrawer").classList.contains("open") && !state.detailLoadingIds.has(state.selectedId)) {
|
|
@@ -64,16 +107,7 @@
|
|
|
64
107
|
if (card && detail && card.updatedAt !== detail.updatedAt) loadSessionDetail(state.selectedId, true);
|
|
65
108
|
}
|
|
66
109
|
});
|
|
67
|
-
if (window.loadtoagent.
|
|
68
|
-
window.loadtoagent.onAttentionRequested((payload) => {
|
|
69
|
-
const sessionId = String(payload && payload.sessionId || '');
|
|
70
|
-
const session = (state.snapshot && state.snapshot.sessions || []).find(item => item.id === sessionId);
|
|
71
|
-
selectView('waiting');
|
|
72
|
-
if (session) {
|
|
73
|
-
if (session.parentId) openSubagentConversation(session.id);
|
|
74
|
-
else openDrawer(session.id);
|
|
75
|
-
} else toast('확인이 필요한 세션 목록을 열었습니다.');
|
|
76
|
-
});
|
|
110
|
+
if (window.loadtoagent.rendererReady) await window.loadtoagent.rendererReady();
|
|
77
111
|
if (window.loadtoagent.onUpdateState)
|
|
78
112
|
window.loadtoagent.onUpdateState((update) => {
|
|
79
113
|
state.update = update;
|
|
@@ -99,7 +133,8 @@
|
|
|
99
133
|
|
|
100
134
|
init().catch((error) => {
|
|
101
135
|
console.error(error);
|
|
102
|
-
|
|
103
|
-
|
|
136
|
+
const message = t("bootstrap.initialization_failed", { message: window.LoadToAgentI18n.errorText(error, "ui.connection_failed") });
|
|
137
|
+
showInitializationError(message);
|
|
138
|
+
toast(message);
|
|
104
139
|
});
|
|
105
140
|
})();
|