loadtoagent 1.3.7 → 1.3.9
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 +13 -1
- package/README.md +13 -1
- package/README.zh-CN.md +13 -1
- package/main.js +3 -2
- package/package.json +2 -1
- package/renderer/app-agent-actions.js +73 -22
- package/renderer/app-dashboard.js +56 -13
- package/renderer/app-drawer-content.js +152 -38
- package/renderer/app-drawer.js +13 -2
- package/renderer/app-events-filters.js +19 -23
- package/renderer/app-events-sessions.js +98 -0
- package/renderer/app-graph-model.js +6 -2
- package/renderer/app-graph-orchestration.js +12 -18
- package/renderer/app-graph-view.js +49 -16
- package/renderer/app-quality.js +5 -0
- package/renderer/app.js +69 -5
- package/renderer/conversation-delivery.js +74 -0
- package/renderer/i18n-messages.js +58 -1
- package/renderer/index.html +5 -6
- package/renderer/styles-components.css +240 -62
- package/renderer/styles-control-room.css +64 -43
- package/renderer/styles-responsive-shell.css +6 -2
- package/renderer/terminal-agent.js +14 -0
- package/src/agentMonitor/claudeParser.js +85 -10
- package/src/agentMonitor/hierarchy.js +7 -2
- package/src/agentMonitor.js +70 -1
- package/src/processMonitor.js +5 -0
- package/src/terminalHost.js +1 -1
- package/src/terminalManager.js +15 -3
|
@@ -25,6 +25,14 @@ window.LoadToAgentAppFactories.createGraphOrchestration = function createGraphOr
|
|
|
25
25
|
} = context;
|
|
26
26
|
const t = (key, params) => window.LoadToAgentI18n.t(key, params);
|
|
27
27
|
|
|
28
|
+
function syncControlRoomDisclosureButtons() {
|
|
29
|
+
const groups = Array.from(document.querySelectorAll("#liveSessionGrid .control-room-project-group"));
|
|
30
|
+
const canExpand = groups.some(group => !group.open);
|
|
31
|
+
const canCollapse = groups.some(group => group.open);
|
|
32
|
+
if ($("#controlRoomExpandAll")) $("#controlRoomExpandAll").disabled = !canExpand;
|
|
33
|
+
if ($("#controlRoomCollapseAll")) $("#controlRoomCollapseAll").disabled = !canCollapse;
|
|
34
|
+
}
|
|
35
|
+
|
|
28
36
|
function renderAgentMap(sessions, motionKind = "refresh") {
|
|
29
37
|
const liveSessionGrid = $("#liveSessionGrid");
|
|
30
38
|
rememberDisclosureStates(liveSessionGrid);
|
|
@@ -45,9 +53,7 @@ window.LoadToAgentAppFactories.createGraphOrchestration = function createGraphOr
|
|
|
45
53
|
$("#agentMapToolbar")?.classList.add("hidden");
|
|
46
54
|
$("#controlRoomProjectToolbar")?.classList.remove("hidden");
|
|
47
55
|
$("#controlRoomListToolbar")?.classList.remove("hidden");
|
|
48
|
-
|
|
49
|
-
if ($("#controlRoomPagePrev")) $("#controlRoomPagePrev").disabled = true;
|
|
50
|
-
if ($("#controlRoomPageNext")) $("#controlRoomPageNext").disabled = true;
|
|
56
|
+
syncControlRoomDisclosureButtons();
|
|
51
57
|
return 0;
|
|
52
58
|
}
|
|
53
59
|
|
|
@@ -71,30 +77,18 @@ window.LoadToAgentAppFactories.createGraphOrchestration = function createGraphOr
|
|
|
71
77
|
scheduleAgentWorkflowConnections();
|
|
72
78
|
} else {
|
|
73
79
|
const runtime = runtimeAgentSummary(model, liveTmuxEntries(state.snapshot && state.snapshot.tmux));
|
|
74
|
-
|
|
75
|
-
const maxPage = Math.max(0, Math.ceil(roots.length / pageSize) - 1);
|
|
76
|
-
state.controlRoomPage = Math.min(maxPage, Math.max(0, Number(state.controlRoomPage || 0)));
|
|
77
|
-
const startIndex = state.controlRoomPage * pageSize;
|
|
78
|
-
const endIndex = Math.min(roots.length, startIndex + pageSize);
|
|
79
|
-
const visibleRoots = roots.slice(startIndex, endIndex);
|
|
80
|
-
liveSessionGrid.innerHTML = runtimeSeparatedOverview(visibleRoots, model, roots);
|
|
80
|
+
liveSessionGrid.innerHTML = runtimeSeparatedOverview(roots, model, roots);
|
|
81
81
|
restoreDisclosureStates(liveSessionGrid);
|
|
82
82
|
$("#graphBreadcrumbs").innerHTML = "";
|
|
83
83
|
$("#agentMapToolbar")?.classList.add("hidden");
|
|
84
84
|
$("#controlRoomProjectToolbar")?.classList.remove("hidden");
|
|
85
85
|
$("#controlRoomListToolbar")?.classList.remove("hidden");
|
|
86
|
-
|
|
87
|
-
start: roots.length ? startIndex + 1 : 0,
|
|
88
|
-
end: endIndex,
|
|
89
|
-
total: roots.length,
|
|
90
|
-
});
|
|
91
|
-
if ($("#controlRoomPagePrev")) $("#controlRoomPagePrev").disabled = state.controlRoomPage === 0;
|
|
92
|
-
if ($("#controlRoomPageNext")) $("#controlRoomPageNext").disabled = state.controlRoomPage >= maxPage;
|
|
86
|
+
syncControlRoomDisclosureButtons();
|
|
93
87
|
$("#graphResetBtn").classList.add("hidden");
|
|
94
88
|
return runtime.activeCount;
|
|
95
89
|
}
|
|
96
90
|
return model.nodes.filter(isControlRoomSession).length;
|
|
97
91
|
}
|
|
98
92
|
|
|
99
|
-
return { renderAgentMap };
|
|
93
|
+
return { renderAgentMap, syncControlRoomDisclosureButtons };
|
|
100
94
|
};
|
|
@@ -22,6 +22,7 @@ window.LoadToAgentAppFactories.createGraphView = function createGraphView(contex
|
|
|
22
22
|
isLiveSession,
|
|
23
23
|
isControlRoomSession = isLiveSession,
|
|
24
24
|
controlRoomStatus = session => session?.status,
|
|
25
|
+
pendingConversationDelivery = () => null,
|
|
25
26
|
sessionRetentionMinutes = () => 0,
|
|
26
27
|
subagentWorkState,
|
|
27
28
|
subagentWorkLabel,
|
|
@@ -34,12 +35,29 @@ window.LoadToAgentAppFactories.createGraphView = function createGraphView(contex
|
|
|
34
35
|
graphDescendantCount,
|
|
35
36
|
sessionWorkspaceLabel,
|
|
36
37
|
controlRoomProject = session => ({ key: String(session?.workspace || session?.id || "unknown"), label: sessionWorkspaceLabel(session) }),
|
|
38
|
+
ensureProjectOrder = projectKeys => projectKeys,
|
|
37
39
|
} = context;
|
|
38
40
|
const t = (key, params) => window.LoadToAgentI18n.t(key, params);
|
|
39
41
|
const statusLabel = (status) => ({
|
|
40
42
|
starting: t("ui.preparing"), running: t("ui.working"), waiting: t("ui.waiting_for_review"), idle: t("ui.idle"),
|
|
41
43
|
completed: t("ui.completed"), failed: t("ui.problem"), cancelled: t("ui.stopped"),
|
|
42
44
|
})[status] || STATUS[status] || status;
|
|
45
|
+
const deliveryLabelKey = (phase) => ({
|
|
46
|
+
sending: "control.delivery_sending",
|
|
47
|
+
confirming: "control.delivery_confirming",
|
|
48
|
+
delayed: "control.delivery_delayed",
|
|
49
|
+
received: "control.delivery_received",
|
|
50
|
+
responding: "control.delivery_responding",
|
|
51
|
+
failed: "control.delivery_failed",
|
|
52
|
+
})[phase] || "control.delivery_confirming";
|
|
53
|
+
const deliverySummaryKey = (phase) => ({
|
|
54
|
+
sending: "drawer.delivery_sending_title",
|
|
55
|
+
confirming: "drawer.delivery_confirming_title",
|
|
56
|
+
delayed: "drawer.delivery_delayed_title",
|
|
57
|
+
received: "drawer.delivery_received_title",
|
|
58
|
+
responding: "drawer.delivery_responding_title",
|
|
59
|
+
failed: "drawer.delivery_failed_title",
|
|
60
|
+
})[phase] || "drawer.delivery_confirming_title";
|
|
43
61
|
|
|
44
62
|
function graphNode(session, options = {}) {
|
|
45
63
|
const provider = providerInfo(session.provider);
|
|
@@ -49,7 +67,8 @@ window.LoadToAgentAppFactories.createGraphView = function createGraphView(contex
|
|
|
49
67
|
const percent = Math.max(0, Math.min(100, Number(context.percent || 0)));
|
|
50
68
|
const running = isLiveSession(session);
|
|
51
69
|
const presentationStatus = controlRoomStatus(session);
|
|
52
|
-
const
|
|
70
|
+
const delivery = pendingConversationDelivery(session);
|
|
71
|
+
const retained = isControlRoomSession(session) && !running && !delivery;
|
|
53
72
|
const childCount = (session.childIds || []).length;
|
|
54
73
|
const childMetrics = session.collaboration && session.collaboration.metrics;
|
|
55
74
|
const cumulativeChildren = childMetrics ? childMetrics.cumulativeCreated : childCount;
|
|
@@ -61,7 +80,7 @@ window.LoadToAgentAppFactories.createGraphView = function createGraphView(contex
|
|
|
61
80
|
? delegation.taskName || session.taskName
|
|
62
81
|
: session.title;
|
|
63
82
|
const goalPreview = readablePreview(displayedTask, options.focus ? 118 : 96);
|
|
64
|
-
const currentWork = latestWorkCopy(session);
|
|
83
|
+
const currentWork = delivery ? t(deliverySummaryKey(delivery.phase)) : latestWorkCopy(session);
|
|
65
84
|
const currentPreview = readablePreview(currentWork, options.focus ? 132 : 108);
|
|
66
85
|
const role = session.parentId
|
|
67
86
|
? t("graph.helper_ai_identity", {
|
|
@@ -78,7 +97,7 @@ window.LoadToAgentAppFactories.createGraphView = function createGraphView(contex
|
|
|
78
97
|
<span class="provider-mark">${esc(provider.mark)}</span>
|
|
79
98
|
<span class="agent-identity"><b>${esc(role)}</b><small>${esc(provider.label)} · ${esc(session.model || t("graph.model_unknown"))}</small></span>
|
|
80
99
|
${executionModeBadge(session, true)}
|
|
81
|
-
<span class="status-pill ${statusClass(presentationStatus)}">${esc(statusLabel(presentationStatus))}</span>
|
|
100
|
+
<span class="status-pill ${statusClass(presentationStatus)}">${esc(delivery ? t(deliveryLabelKey(delivery.phase)) : statusLabel(presentationStatus))}</span>
|
|
82
101
|
</span>
|
|
83
102
|
<span class="agent-task-label">
|
|
84
103
|
${session.parentId ? t("graph.assigned_task", { source: delegation.assignmentSource === "protected"
|
|
@@ -123,6 +142,7 @@ window.LoadToAgentAppFactories.createGraphView = function createGraphView(contex
|
|
|
123
142
|
const usage = session.usage || {};
|
|
124
143
|
const directChildren = graphChildren(session, model).length;
|
|
125
144
|
const presentationStatus = controlRoomStatus(session);
|
|
145
|
+
const delivery = pendingConversationDelivery(session);
|
|
126
146
|
const identity = session.parentId
|
|
127
147
|
? t("graph.helper_ai_named", { name: session.agentName || agentRoleLabel(session.agentRole) })
|
|
128
148
|
: t("project.origin_named", { name: sessionWorkspaceLabel(session) });
|
|
@@ -131,7 +151,7 @@ window.LoadToAgentAppFactories.createGraphView = function createGraphView(contex
|
|
|
131
151
|
const assignedWork = delegation.assignmentObserved && delegation.assignment ? delegation.assignment : taskName || session.title;
|
|
132
152
|
const sharedGoal = delegation.sharedGoal || session.sharedGoal || "";
|
|
133
153
|
const outcome = delegation.result || session.result || "";
|
|
134
|
-
const outcomeText = outcome || latestWorkCopy(session);
|
|
154
|
+
const outcomeText = delivery ? t(deliverySummaryKey(delivery.phase)) : outcome || latestWorkCopy(session);
|
|
135
155
|
const assignedWorkPreview = readablePreview(assignedWork, session.parentId ? 110 : 104);
|
|
136
156
|
const taskLabel = session.parentId ? `${label || agentRoleLabel(session.agentRole)}${taskName ? t("graph.assigned_name_suffix", { name: taskName }) : ""}` : label;
|
|
137
157
|
const assignmentSourceNote = session.parentId && delegation.assignmentSource === "protected"
|
|
@@ -196,7 +216,7 @@ window.LoadToAgentAppFactories.createGraphView = function createGraphView(contex
|
|
|
196
216
|
<em>${esc(identity)} · ${directChildren ? `${t("graph.helper_ai_count", { count: directChildren })} · ` : ""}${esc(timeAgo(session.updatedAt))}</em>
|
|
197
217
|
${assignmentSourceNote}${sharedGoalCopy}${outcomeCopy}
|
|
198
218
|
</span>
|
|
199
|
-
<span class="agent-flow-provider"><i>${esc(provider.mark)}</i><small>${esc(statusLabel(presentationStatus))}</small></span>
|
|
219
|
+
<span class="agent-flow-provider"><i>${esc(provider.mark)}</i><small>${esc(delivery ? t(deliveryLabelKey(delivery.phase)) : statusLabel(presentationStatus))}</small></span>
|
|
200
220
|
</button>`;
|
|
201
221
|
}
|
|
202
222
|
|
|
@@ -446,8 +466,9 @@ window.LoadToAgentAppFactories.createGraphView = function createGraphView(contex
|
|
|
446
466
|
function controlRoomSession(root, model) {
|
|
447
467
|
const provider = providerInfo(root.provider);
|
|
448
468
|
const presentationStatus = controlRoomStatus(root);
|
|
469
|
+
const delivery = pendingConversationDelivery(root);
|
|
449
470
|
const waiting = presentationStatus === "waiting";
|
|
450
|
-
const retained = isControlRoomSession(root) && !isLiveSession(root);
|
|
471
|
+
const retained = isControlRoomSession(root) && !isLiveSession(root) && !delivery;
|
|
451
472
|
const descendants = controlRoomDescendants(root, model);
|
|
452
473
|
const actors = [root, ...descendants];
|
|
453
474
|
const executionItems = actors.flatMap(owner => (owner.executions || []).map(activity => ({ activity, owner })));
|
|
@@ -462,14 +483,14 @@ window.LoadToAgentAppFactories.createGraphView = function createGraphView(contex
|
|
|
462
483
|
...completedChildren.map(child => ({ kind: "child", child, timestamp: child.completedAt || child.updatedAt })),
|
|
463
484
|
...completedExecutions.map(item => ({ kind: "execution", item, timestamp: item.activity.updatedAt || item.activity.startedAt })),
|
|
464
485
|
].sort((a, b) => Date.parse(b.timestamp || 0) - Date.parse(a.timestamp || 0)).slice(0, 3);
|
|
465
|
-
const current = controlRoomSummary(latestWorkCopy(root) || root.statusDetail || root.title, 74);
|
|
486
|
+
const current = controlRoomSummary(delivery ? t(deliverySummaryKey(delivery.phase)) : latestWorkCopy(root) || root.statusDetail || root.title, 74);
|
|
466
487
|
const title = controlRoomAgentGoal(root, 64);
|
|
467
488
|
const unitCount = activeUnits.length;
|
|
468
489
|
const main = `<button type="button" class="control-room-main" data-open-session="${esc(root.id)}"
|
|
469
490
|
data-control-summary="${esc(title.text)}"
|
|
470
491
|
data-motion-key="control-main:${esc(root.id)}" data-motion-value="${esc(root.updatedAt || "")}:${esc(root.status || "")}"
|
|
471
492
|
style="${providerStyle(root.provider)}">
|
|
472
|
-
<span class="control-main-top"><span class="provider-mark">${esc(provider.mark)}</span><span><small>${esc(t("control.main_agent"))}</small><b>${esc(provider.label)} · ${esc(root.model || t("session.model_unknown"))}</b></span><em><i aria-hidden="true"></i>${esc(statusLabel(presentationStatus))}</em></span>
|
|
493
|
+
<span class="control-main-top"><span class="provider-mark">${esc(provider.mark)}</span><span><small>${esc(t("control.main_agent"))}</small><b>${esc(provider.label)} · ${esc(root.model || t("session.model_unknown"))}</b></span><em><i aria-hidden="true"></i>${esc(delivery ? t(deliveryLabelKey(delivery.phase)) : statusLabel(presentationStatus))}</em></span>
|
|
473
494
|
<strong title="${esc(title.full)}">${esc(title.text)}</strong>
|
|
474
495
|
<span class="control-main-now"><small>${esc(t("graph.current_work"))}</small><b title="${esc(current.full)}">${esc(current.text)}</b></span>
|
|
475
496
|
<span class="control-main-meta"><small>${esc(t("control.unit_counts", { helpers: activeChildren.length, executions: activeExecutions.length }))}</small><b>${esc(t("graph.view_conversation"))} →</b></span>
|
|
@@ -483,9 +504,11 @@ window.LoadToAgentAppFactories.createGraphView = function createGraphView(contex
|
|
|
483
504
|
? completedUnits.map(unit => unit.kind === "child" ? controlRoomChildNode(unit.child) : controlRoomExecutionNode(unit.item)).join("")
|
|
484
505
|
: `<div class="control-room-complete-empty"><span>✓</span><small>${esc(t("control.completed_empty"))}</small></div>`;
|
|
485
506
|
const waitingWithBackground = waiting && activeExecutions.some(item => item.activity.mode === "background" || item.activity.kind === "background");
|
|
486
|
-
const sessionStateKey =
|
|
487
|
-
?
|
|
488
|
-
:
|
|
507
|
+
const sessionStateKey = delivery
|
|
508
|
+
? deliveryLabelKey(delivery.phase)
|
|
509
|
+
: waitingWithBackground
|
|
510
|
+
? "control.waiting_background_session"
|
|
511
|
+
: (waiting ? "control.waiting_session" : (retained ? "control.recently_completed" : "control.live_session"));
|
|
489
512
|
const retention = retained ? `<small class="control-session-retention">${esc(t("control.auto_history_in_minutes", { minutes: sessionRetentionMinutes(root) }))}</small>` : "";
|
|
490
513
|
const archive = retained ? `<button type="button" class="control-session-archive" data-session-archive="${esc(root.id)}">${esc(t("control.move_to_history"))}</button>` : "";
|
|
491
514
|
return `<article class="control-room-session ${waiting ? "is-waiting" : ""} ${waitingWithBackground ? "has-background-work" : ""}" data-control-session="${esc(root.id)}" data-session-sortable="${esc(root.id)}"
|
|
@@ -519,20 +542,30 @@ window.LoadToAgentAppFactories.createGraphView = function createGraphView(contex
|
|
|
519
542
|
if (!groups.has(key)) groups.set(key, { name, roots: [] });
|
|
520
543
|
groups.get(key).roots.push(root);
|
|
521
544
|
});
|
|
522
|
-
const
|
|
545
|
+
const defaultOrderedGroups = [...groups.entries()].sort(([leftKey], [rightKey]) =>
|
|
546
|
+
Number(allGroups.get(rightKey)?.roots.length || 0) - Number(allGroups.get(leftKey)?.roots.length || 0));
|
|
547
|
+
const projectOrder = ensureProjectOrder(defaultOrderedGroups.map(([key]) => key));
|
|
548
|
+
const projectRank = new Map(projectOrder.map((key, index) => [key, index]));
|
|
549
|
+
const orderedGroups = defaultOrderedGroups.sort(([leftKey], [rightKey]) =>
|
|
550
|
+
Number(projectRank.get(leftKey) ?? Number.MAX_SAFE_INTEGER) - Number(projectRank.get(rightKey) ?? Number.MAX_SAFE_INTEGER));
|
|
551
|
+
const projectGroups = orderedGroups.map(([key, { name, roots: projectRoots }], index) => {
|
|
523
552
|
const projectTotals = allGroups.get(key)?.roots || projectRoots;
|
|
524
553
|
const activeCount = projectTotals.filter((root) => isLiveSession(root)).length;
|
|
525
|
-
const attentionCount = projectTotals.filter((root) =>
|
|
554
|
+
const attentionCount = projectTotals.filter((root) =>
|
|
555
|
+
!isLiveSession(root)
|
|
556
|
+
&& isControlRoomSession(root)
|
|
557
|
+
&& ["waiting", "failed", "paused"].includes(root.status)).length;
|
|
526
558
|
const summary = attentionCount
|
|
527
559
|
? t("control.project_live_attention_summary", { active: activeCount, attention: attentionCount })
|
|
528
560
|
: t("control.project_live_summary", { active: activeCount });
|
|
529
561
|
const disclosureKey = `control-project:${key}`;
|
|
530
562
|
const presentation = index === 0 ? "is-primary" : "is-secondary";
|
|
531
563
|
const projectFocusId = projectRoots[0]?.id || "";
|
|
532
|
-
return `<details class="control-room-project-group ${presentation}" data-control-project="${esc(name)}" data-
|
|
533
|
-
<summary class="control-project-header" data-project-toggle="${esc(name)}"
|
|
564
|
+
return `<details class="control-room-project-group ${presentation}" data-control-project="${esc(name)}" data-project-sortable="${esc(key)}" data-disclosure-key="${esc(disclosureKey)}">
|
|
565
|
+
<summary class="control-project-header" data-project-toggle="${esc(name)}" draggable="true" aria-grabbed="false"
|
|
566
|
+
aria-keyshortcuts="Alt+ArrowUp Alt+ArrowDown" aria-label="${esc(t("project.drag_label", { name }))}" aria-describedby="projectReorderHelp">
|
|
534
567
|
<span class="control-project-heading"><i aria-hidden="true">□</i><b>${esc(name)}</b><small>${esc(summary)}</small><em>${projectTotals.length}</em></span>
|
|
535
|
-
<span class="control-project-handle"
|
|
568
|
+
<span class="control-project-handle" aria-hidden="true" title="${esc(t("project.reorder_hint"))}"></span>
|
|
536
569
|
</summary>
|
|
537
570
|
<button type="button" class="control-project-flow-link" data-graph-focus="${esc(projectFocusId)}"><span>${esc(t("control.open_full_flow"))} ↗</span></button>
|
|
538
571
|
<div class="control-project-body">${projectRoots.map(root => controlRoomSession(root, model)).join("")}</div>
|
package/renderer/app-quality.js
CHANGED
|
@@ -98,12 +98,16 @@ window.LoadToAgentAppFactories.createQualityEnhancements = function createQualit
|
|
|
98
98
|
state.sessionOrder = Array.isArray(dashboard.sessionOrder)
|
|
99
99
|
? dashboard.sessionOrder.filter(id => typeof id === "string" && id.length <= 500).slice(0, 1_000)
|
|
100
100
|
: [];
|
|
101
|
+
state.projectOrder = Array.isArray(dashboard.projectOrder)
|
|
102
|
+
? dashboard.projectOrder.filter(id => typeof id === "string" && id.length <= 2_000).slice(0, 1_000)
|
|
103
|
+
: [];
|
|
101
104
|
} else {
|
|
102
105
|
state.search = "";
|
|
103
106
|
state.providerFilters.clear();
|
|
104
107
|
state.workspace = "all";
|
|
105
108
|
state.sort = "recent";
|
|
106
109
|
state.sessionOrder = [];
|
|
110
|
+
state.projectOrder = [];
|
|
107
111
|
}
|
|
108
112
|
const search = $("#searchInput");
|
|
109
113
|
if (search) search.value = state.search;
|
|
@@ -133,6 +137,7 @@ window.LoadToAgentAppFactories.createQualityEnhancements = function createQualit
|
|
|
133
137
|
sort: ["recent", "tokens", "context"].includes(state.sort) ? state.sort : "recent",
|
|
134
138
|
controlRoomSort: ["recent", "tokens", "context"].includes(state.controlRoomSort) ? state.controlRoomSort : "recent",
|
|
135
139
|
sessionOrder: (state.sessionOrder || []).filter(id => typeof id === "string" && id.length <= 500).slice(0, 1_000),
|
|
140
|
+
projectOrder: (state.projectOrder || []).filter(id => typeof id === "string" && id.length <= 2_000).slice(0, 1_000),
|
|
136
141
|
};
|
|
137
142
|
try {
|
|
138
143
|
localStorage.setItem(DASHBOARD_STORAGE_KEY, JSON.stringify(value));
|
package/renderer/app.js
CHANGED
|
@@ -24,6 +24,7 @@ window.LoadToAgentAppFactories.createCore = function createCore(context = {}) {
|
|
|
24
24
|
search: "",
|
|
25
25
|
sort: "recent",
|
|
26
26
|
sessionOrder: [],
|
|
27
|
+
projectOrder: [],
|
|
27
28
|
sessionArchives: new Map(),
|
|
28
29
|
controlRoomObservedIds: new Set(),
|
|
29
30
|
selectedId: null,
|
|
@@ -36,8 +37,6 @@ window.LoadToAgentAppFactories.createCore = function createCore(context = {}) {
|
|
|
36
37
|
drawerForceLatest: false,
|
|
37
38
|
visibleLimit: 30,
|
|
38
39
|
graphFocusId: null,
|
|
39
|
-
controlRoomPage: 0,
|
|
40
|
-
controlRoomPageSize: 4,
|
|
41
40
|
controlRoomSort: "recent",
|
|
42
41
|
supervisionFocusId: null,
|
|
43
42
|
graphExpandedProviders: new Set(),
|
|
@@ -50,6 +49,7 @@ window.LoadToAgentAppFactories.createCore = function createCore(context = {}) {
|
|
|
50
49
|
agentCommandTargets: new Map(),
|
|
51
50
|
agentCommandRoutes: new Map(),
|
|
52
51
|
agentCommandSending: new Set(),
|
|
52
|
+
pendingConversationMessages: new Map(),
|
|
53
53
|
stopRequests: new Set(),
|
|
54
54
|
runControlRequests: new Set(),
|
|
55
55
|
managementFilter: "all",
|
|
@@ -585,6 +585,54 @@ window.LoadToAgentAppFactories.createCore = function createCore(context = {}) {
|
|
|
585
585
|
const completedAt = Date.parse(session?.completedAt || session?.endedAt || 0);
|
|
586
586
|
return Number.isFinite(completedAt) ? completedAt : 0;
|
|
587
587
|
}
|
|
588
|
+
function conversationMessageKey(message) {
|
|
589
|
+
const delivery = window.LoadToAgentConversationDelivery;
|
|
590
|
+
if (delivery?.messageKey) return delivery.messageKey(message);
|
|
591
|
+
const id = String(message?.id || "").trim();
|
|
592
|
+
if (id) return `id:${id}`;
|
|
593
|
+
return `${message?.role || ""}:${String(message?.text || "").replace(/\s+/g, " ").trim()}:${message?.timestamp || ""}`;
|
|
594
|
+
}
|
|
595
|
+
function conversationDeliveryState(session, entry, now = Date.now()) {
|
|
596
|
+
return window.LoadToAgentConversationDelivery?.deliveryState?.(session, entry, now) || null;
|
|
597
|
+
}
|
|
598
|
+
function pendingConversationDelivery(session, now = Date.now()) {
|
|
599
|
+
const pending = state.pendingConversationMessages.get(String(session?.id || "")) || [];
|
|
600
|
+
const retained = [];
|
|
601
|
+
let latest = null;
|
|
602
|
+
for (const entry of pending) {
|
|
603
|
+
const delivery = conversationDeliveryState(session, entry, now);
|
|
604
|
+
if (!delivery) continue;
|
|
605
|
+
observeConversationDelivery(session, entry, delivery);
|
|
606
|
+
if (delivery.phase === "responded") {
|
|
607
|
+
clearTimeout(entry.confirmationTimer);
|
|
608
|
+
continue;
|
|
609
|
+
}
|
|
610
|
+
retained.push(entry);
|
|
611
|
+
latest = { ...delivery, entry };
|
|
612
|
+
}
|
|
613
|
+
if (retained.length !== pending.length) {
|
|
614
|
+
if (retained.length) state.pendingConversationMessages.set(String(session?.id || ""), retained);
|
|
615
|
+
else state.pendingConversationMessages.delete(String(session?.id || ""));
|
|
616
|
+
}
|
|
617
|
+
return latest;
|
|
618
|
+
}
|
|
619
|
+
function observeConversationDelivery(session, entry, delivery) {
|
|
620
|
+
if (!entry || !delivery || entry.observedPhase === delivery.phase) return;
|
|
621
|
+
const previousPhase = entry.observedPhase || "";
|
|
622
|
+
entry.observedPhase = delivery.phase;
|
|
623
|
+
entry.phase = delivery.phase;
|
|
624
|
+
entry.phaseChangedAt = new Date().toISOString();
|
|
625
|
+
console.info("[LoadToAgent:conversation-delivery]", {
|
|
626
|
+
event: "conversation-delivery-phase-changed",
|
|
627
|
+
sessionId: String(session?.id || ""),
|
|
628
|
+
previousPhase,
|
|
629
|
+
phase: delivery.phase,
|
|
630
|
+
elapsedMs: Math.round(Number(delivery.elapsedMs || 0)),
|
|
631
|
+
userMessageObserved: Boolean(delivery.userMessage),
|
|
632
|
+
responseStartObserved: Boolean(delivery.responseStartEvent),
|
|
633
|
+
assistantMessageObserved: Boolean(delivery.assistantMessage),
|
|
634
|
+
});
|
|
635
|
+
}
|
|
588
636
|
function loadSessionArchives() {
|
|
589
637
|
try {
|
|
590
638
|
const saved = JSON.parse(localStorage.getItem(SESSION_ARCHIVE_STORAGE_KEY) || "{}");
|
|
@@ -614,14 +662,22 @@ window.LoadToAgentAppFactories.createCore = function createCore(context = {}) {
|
|
|
614
662
|
}
|
|
615
663
|
function isControlRoomSession(session, now = Date.now()) {
|
|
616
664
|
if (!session) return false;
|
|
617
|
-
if (
|
|
665
|
+
if (pendingConversationDelivery(session, now)) {
|
|
666
|
+
state.controlRoomObservedIds.add(String(session.id || ""));
|
|
667
|
+
return true;
|
|
668
|
+
}
|
|
669
|
+
if (isLiveSession(session)) {
|
|
670
|
+
state.controlRoomObservedIds.add(String(session.id || ""));
|
|
671
|
+
return true;
|
|
672
|
+
}
|
|
673
|
+
if (isSessionManuallyArchived(session)) return false;
|
|
674
|
+
if (hasRunningExecution(session)) {
|
|
618
675
|
state.controlRoomObservedIds.add(String(session.id || ""));
|
|
619
676
|
return true;
|
|
620
677
|
}
|
|
621
678
|
if (!state.controlRoomObservedIds.has(String(session.id || ""))) return false;
|
|
622
679
|
const responseAt = sessionResponseTimestamp(session);
|
|
623
680
|
const retained = Boolean(responseAt
|
|
624
|
-
&& !isSessionManuallyArchived(session)
|
|
625
681
|
&& Math.max(0, Number(now) - responseAt) < SESSION_RETENTION_MS);
|
|
626
682
|
if (!retained && responseAt && Math.max(0, Number(now) - responseAt) >= SESSION_RETENTION_MS) {
|
|
627
683
|
state.controlRoomObservedIds.delete(String(session.id || ""));
|
|
@@ -629,7 +685,11 @@ window.LoadToAgentAppFactories.createCore = function createCore(context = {}) {
|
|
|
629
685
|
return retained;
|
|
630
686
|
}
|
|
631
687
|
function controlRoomStatus(session, now = Date.now()) {
|
|
632
|
-
|
|
688
|
+
// Retention controls where a recently active session is shown, not what
|
|
689
|
+
// state it is in. Preserve the observed provider status so an idle or
|
|
690
|
+
// completed session is never presented as waiting for user input.
|
|
691
|
+
isControlRoomSession(session, now);
|
|
692
|
+
return session?.status;
|
|
633
693
|
}
|
|
634
694
|
function sessionRetentionMinutes(session, now = Date.now()) {
|
|
635
695
|
const remaining = SESSION_RETENTION_MS - Math.max(0, Number(now) - sessionResponseTimestamp(session));
|
|
@@ -778,6 +838,10 @@ window.LoadToAgentAppFactories.createCore = function createCore(context = {}) {
|
|
|
778
838
|
isLiveSession,
|
|
779
839
|
hasRunningExecution,
|
|
780
840
|
sessionResponseTimestamp,
|
|
841
|
+
conversationMessageKey,
|
|
842
|
+
conversationDeliveryState,
|
|
843
|
+
pendingConversationDelivery,
|
|
844
|
+
observeConversationDelivery,
|
|
781
845
|
loadSessionArchives,
|
|
782
846
|
saveSessionArchives,
|
|
783
847
|
isSessionManuallyArchived,
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
(function exposeConversationDelivery(root, factory) {
|
|
4
|
+
const api = factory();
|
|
5
|
+
if (typeof module === "object" && module.exports) module.exports = api;
|
|
6
|
+
if (root) root.LoadToAgentConversationDelivery = api;
|
|
7
|
+
})(typeof window === "object" ? window : null, function createConversationDelivery() {
|
|
8
|
+
const CONFIRMATION_DELAY_MS = 12_000;
|
|
9
|
+
|
|
10
|
+
function normalizedText(value) {
|
|
11
|
+
return String(value || "").replace(/\s+/g, " ").trim();
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function messageKey(message) {
|
|
15
|
+
const id = String(message?.id || "").trim();
|
|
16
|
+
if (id) return `id:${id}`;
|
|
17
|
+
return `${message?.role || ""}:${normalizedText(message?.text)}:${message?.timestamp || ""}`;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function newMessagesForEntry(session, entry) {
|
|
21
|
+
const baseline = entry?.baselineMessageKeys instanceof Set
|
|
22
|
+
? entry.baselineMessageKeys
|
|
23
|
+
: new Set(entry?.baselineMessageKeys || []);
|
|
24
|
+
return (session?.messages || []).filter(message => !baseline.has(messageKey(message)));
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function deliveryState(session, entry, now = Date.now()) {
|
|
28
|
+
if (!entry) return null;
|
|
29
|
+
const newMessages = newMessagesForEntry(session, entry);
|
|
30
|
+
const expectedText = normalizedText(entry.text);
|
|
31
|
+
const userMessage = newMessages.find(message =>
|
|
32
|
+
message?.role === "user" && normalizedText(message.text) === expectedText) || null;
|
|
33
|
+
const assistantMessage = newMessages.find(message =>
|
|
34
|
+
message?.role === "assistant" && normalizedText(message.text)) || null;
|
|
35
|
+
const dispatchedAt = Date.parse(entry.dispatchedAt || entry.timestamp || 0);
|
|
36
|
+
const elapsedMs = Number.isFinite(dispatchedAt) ? Math.max(0, Number(now) - dispatchedAt) : 0;
|
|
37
|
+
const userObservedAt = Date.parse(userMessage?.timestamp || 0);
|
|
38
|
+
const responseStartEvent = userMessage
|
|
39
|
+
? (session?.lifecycle || []).find(event => {
|
|
40
|
+
const eventAt = Date.parse(event?.timestamp || 0);
|
|
41
|
+
return Number.isFinite(eventAt)
|
|
42
|
+
&& (!Number.isFinite(userObservedAt) || eventAt >= userObservedAt)
|
|
43
|
+
&& event?.status === "running"
|
|
44
|
+
&& /start|turn|run/i.test(String(event?.type || event?.label || ""));
|
|
45
|
+
}) || null
|
|
46
|
+
: null;
|
|
47
|
+
|
|
48
|
+
let phase = "confirming";
|
|
49
|
+
if (entry.status === "failed") phase = "failed";
|
|
50
|
+
else if (assistantMessage) phase = "responded";
|
|
51
|
+
else if (userMessage && responseStartEvent) phase = "responding";
|
|
52
|
+
else if (userMessage) phase = "received";
|
|
53
|
+
else if (entry.status === "sending") phase = "sending";
|
|
54
|
+
else if (elapsedMs >= CONFIRMATION_DELAY_MS) phase = "delayed";
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
phase,
|
|
58
|
+
elapsedMs,
|
|
59
|
+
userMessage,
|
|
60
|
+
assistantMessage,
|
|
61
|
+
responseStartEvent,
|
|
62
|
+
receivedAt: userMessage?.timestamp || null,
|
|
63
|
+
responseObservedAt: assistantMessage?.timestamp || responseStartEvent?.timestamp || null,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return {
|
|
68
|
+
CONFIRMATION_DELAY_MS,
|
|
69
|
+
normalizedText,
|
|
70
|
+
messageKey,
|
|
71
|
+
newMessagesForEntry,
|
|
72
|
+
deliveryState,
|
|
73
|
+
};
|
|
74
|
+
});
|
|
@@ -213,6 +213,7 @@
|
|
|
213
213
|
"project.all": {"ko":"모든 프로젝트 폴더","en":"All project folders","zh-CN":"所有项目文件夹"},
|
|
214
214
|
"project.empty": {"ko":"AI 세션이 감지되면 작업 시작 폴더별로 자동 분류됩니다.","en":"Detected AI sessions are grouped automatically by their work-origin folder.","zh-CN":"检测到 AI 会话后,将按任务起始文件夹自动分组。"},
|
|
215
215
|
"project.filter_named": {"ko":"{name} 폴더에서 시작한 AI 세션 {count}개 보기","en":"Show {count} AI sessions started from the {name} folder","zh-CN":"查看从 {name} 文件夹启动的 {count} 个 AI 会话"},
|
|
216
|
+
"project.in_progress": {"ko":"진행 중","en":"In progress","zh-CN":"进行中"},
|
|
216
217
|
"project.origin": {"ko":"작업 시작 폴더","en":"Work origin folder","zh-CN":"任务起始文件夹"},
|
|
217
218
|
"project.origin_named": {"ko":"작업 시작 폴더 · {name}","en":"Work origin folder · {name}","zh-CN":"任务起始文件夹 · {name}"},
|
|
218
219
|
"bootstrap.module_missing": {"ko":"앱 모듈을 찾지 못했습니다: {name}","en":"App module not found: {name}","zh-CN":"找不到应用模块:{name}"},
|
|
@@ -256,7 +257,7 @@
|
|
|
256
257
|
"agent.handing_off": {"ko":"이어받는 중…","en":"Handing off…","zh-CN":"正在接管…"},
|
|
257
258
|
"agent.handoff_and_send": {"ko":"관리 터미널로 전환하고 보내기 ↵","en":"Switch to a managed terminal and send ↵","zh-CN":"切换到托管终端并发送 ↵"},
|
|
258
259
|
"agent.connecting": {"ko":"연결하는 중…","en":"Connecting…","zh-CN":"正在连接…"},
|
|
259
|
-
"agent.background_and_send": {"ko":"
|
|
260
|
+
"agent.background_and_send": {"ko":"내용을 이어서 보내기","en":"Continue the conversation","zh-CN":"继续对话"},
|
|
260
261
|
"agent.copy_bridge": {"ko":"연결용 시작 명령 복사","en":"Copy connection start command","zh-CN":"复制连接启动命令"},
|
|
261
262
|
"agent.command_example": {"ko":"예: 이전 작업에 이어서 테스트를 실행하고 결과를 알려줘","en":"Example: Continue the previous work, run the tests, and report the results","zh-CN":"示例:继续之前的工作,运行测试并告知结果"},
|
|
262
263
|
"agent.command_title": {"ko":"연결된 입력 채널에 지시 보내기","en":"Send an instruction through the linked input channel","zh-CN":"通过已连接的输入通道发送指令"},
|
|
@@ -285,6 +286,7 @@
|
|
|
285
286
|
"agent.select_target_first": {"ko":"지시를 보낼 터미널을 먼저 선택하세요.","en":"Choose a terminal before sending an instruction.","zh-CN":"请先选择要发送指令的终端。"},
|
|
286
287
|
"agent.no_writable_terminal": {"ko":"이 AI에 연결된 입력 가능한 터미널이 없습니다.","en":"There is no writable terminal connected to this AI.","zh-CN":"没有连接到此 AI 的可输入终端。"},
|
|
287
288
|
"agent.command_sent": {"ko":"{target}에 지시를 보냈습니다.","en":"Sent the instruction to {target}.","zh-CN":"已将指令发送到 {target}。"},
|
|
289
|
+
"agent.command_sent_background": {"ko":"전송 요청을 보냈습니다. 실제 수신과 응답 시작은 대화 상태에서 확인합니다.","en":"The send request was submitted. Check the conversation status for confirmed receipt and response start.","zh-CN":"发送请求已提交,请在对话状态中确认实际接收和回复开始。"},
|
|
288
290
|
"agent.recovered_and_sent": {"ko":"원래 터미널 연결이 종료되어 같은 AI 세션으로 복구한 뒤 지시를 보냈습니다.","en":"The original terminal connection ended, so the AI session was restored before sending the instruction.","zh-CN":"原终端连接已结束,因此恢复同一 AI 会话后发送了指令。"},
|
|
289
291
|
"agent.recovery_failed": {"ko":"터미널 연결이 끊어졌고 세션 복구에도 실패했습니다.","en":"The terminal disconnected and session recovery also failed.","zh-CN":"终端连接已断开,会话恢复也失败。"},
|
|
290
292
|
"agent.send_failed": {"ko":"터미널에 지시를 보내지 못했습니다.","en":"Could not send the instruction to the terminal.","zh-CN":"无法向终端发送指令。"},
|
|
@@ -348,6 +350,45 @@
|
|
|
348
350
|
"drawer.current_progress": {"ko":"현재 진행 상황","en":"Current progress","zh-CN":"当前进度"},
|
|
349
351
|
"drawer.last_progress": {"ko":"마지막 진행 상황","en":"Latest progress","zh-CN":"最近进度"},
|
|
350
352
|
"drawer.preparing_response": {"ko":"AI가 현재 응답을 준비하고 있습니다","en":"The AI is preparing a response","zh-CN":"AI 正在准备回复"},
|
|
353
|
+
"drawer.message_sending": {"ko":"보내는 중","en":"Sending","zh-CN":"发送中"},
|
|
354
|
+
"drawer.message_sent": {"ko":"전송됨","en":"Sent","zh-CN":"已发送"},
|
|
355
|
+
"drawer.message_confirming": {"ko":"수신 확인 중","en":"Confirming receipt","zh-CN":"正在确认接收"},
|
|
356
|
+
"drawer.message_unconfirmed": {"ko":"수신 미확인","en":"Receipt unconfirmed","zh-CN":"尚未确认接收"},
|
|
357
|
+
"drawer.message_received": {"ko":"AI 수신 확인","en":"Received by AI session","zh-CN":"AI 会话已接收"},
|
|
358
|
+
"drawer.message_responding": {"ko":"응답 중","en":"Responding","zh-CN":"正在回复"},
|
|
359
|
+
"drawer.message_failed": {"ko":"전송 실패","en":"Send failed","zh-CN":"发送失败"},
|
|
360
|
+
"drawer.delivery_sending_title": {"ko":"메시지를 보내고 있습니다","en":"Sending your message","zh-CN":"正在发送消息"},
|
|
361
|
+
"drawer.delivery_sending_detail": {"ko":"관리 터미널에 전송 요청을 전달하는 중입니다.","en":"The send request is being passed to the managed terminal.","zh-CN":"正在将发送请求传递给托管终端。"},
|
|
362
|
+
"drawer.delivery_confirming_title": {"ko":"AI 세션의 수신을 확인하고 있습니다","en":"Confirming receipt in the AI session","zh-CN":"正在确认 AI 会话是否接收"},
|
|
363
|
+
"drawer.delivery_confirming_detail": {"ko":"전송 요청은 성공했습니다. AI 대화 기록에 메시지가 나타나는지 확인 중입니다.","en":"The send request succeeded. Waiting for the message to appear in the AI conversation log.","zh-CN":"发送请求已成功,正在等待消息出现在 AI 对话记录中。"},
|
|
364
|
+
"drawer.delivery_delayed_title": {"ko":"아직 AI 수신을 확인하지 못했습니다","en":"AI receipt is not confirmed yet","zh-CN":"尚未确认 AI 已接收"},
|
|
365
|
+
"drawer.delivery_delayed_detail": {"ko":"전송 요청은 완료됐지만 AI 대화 기록에는 아직 메시지가 없습니다. 응답 중이라고 단정하지 않습니다.","en":"The send request completed, but the message is not yet in the AI conversation log. The app is not claiming the AI is responding.","zh-CN":"发送请求已完成,但 AI 对话记录中尚无该消息,因此应用不会声称 AI 正在回复。"},
|
|
366
|
+
"drawer.delivery_received_title": {"ko":"AI 세션에서 메시지를 확인했습니다","en":"Message confirmed in the AI session","zh-CN":"已在 AI 会话中确认消息"},
|
|
367
|
+
"drawer.delivery_received_detail": {"ko":"대화 기록에 메시지가 들어왔습니다. AI 실행 시작 신호를 기다리고 있습니다.","en":"The message is in the conversation log. Waiting for an AI execution-start signal.","zh-CN":"消息已进入对话记录,正在等待 AI 执行开始信号。"},
|
|
368
|
+
"drawer.delivery_responding_title": {"ko":"AI가 이 메시지에 응답하고 있습니다","en":"The AI is responding to this message","zh-CN":"AI 正在回复此消息"},
|
|
369
|
+
"drawer.delivery_responding_detail": {"ko":"메시지 수신과 새 실행 시작을 모두 확인했습니다. 첫 응답을 기다리는 중입니다.","en":"Both message receipt and a new execution start were observed. Waiting for the first response.","zh-CN":"已确认消息接收和新执行开始,正在等待首条回复。"},
|
|
370
|
+
"drawer.delivery_failed_title": {"ko":"메시지를 보내지 못했습니다","en":"The message could not be sent","zh-CN":"消息发送失败"},
|
|
371
|
+
"drawer.delivery_failed_detail": {"ko":"전송 요청이 실패했습니다. AI는 이 메시지를 받지 못했을 수 있습니다.","en":"The send request failed. The AI may not have received this message.","zh-CN":"发送请求失败,AI 可能没有收到此消息。"},
|
|
372
|
+
"drawer.delivery_failed_with_reason": {"ko":"전송 요청이 실패했습니다: {reason}","en":"The send request failed: {reason}","zh-CN":"发送请求失败:{reason}"},
|
|
373
|
+
"drawer.delivery_observed_title": {"ko":"AI 세션이 실행 중입니다","en":"The AI session is running","zh-CN":"AI 会话正在运行"},
|
|
374
|
+
"drawer.delivery_observed_detail": {"ko":"대화 기록과 세션 실행 상태를 기준으로 확인했습니다. 이 앱에서 시작하지 않은 전송 단계는 추적하지 않습니다.","en":"Confirmed from the conversation log and session runtime state. Send steps started outside this app are not tracked.","zh-CN":"已根据对话记录和会话运行状态确认;不会跟踪在本应用之外发起的发送步骤。"},
|
|
375
|
+
"drawer.delivery_observed_message": {"ko":"대화 기록 확인","en":"Conversation log confirmed","zh-CN":"已确认对话记录"},
|
|
376
|
+
"drawer.delivery_observed_message_help": {"ko":"사용자 메시지가 실제 기록에 있습니다","en":"The user message exists in the actual log","zh-CN":"用户消息已存在于实际记录中"},
|
|
377
|
+
"drawer.delivery_observed_session": {"ko":"AI 실행 상태 확인","en":"AI runtime state confirmed","zh-CN":"已确认 AI 运行状态"},
|
|
378
|
+
"drawer.delivery_observed_session_help": {"ko":"현재 세션이 실행 중으로 관측됩니다","en":"The current session is observed as running","zh-CN":"当前会话被观察为正在运行"},
|
|
379
|
+
"drawer.delivery_evidence_sending": {"ko":"확인 근거 · 터미널 전송 요청 진행 중","en":"Evidence · terminal send request in progress","zh-CN":"确认依据 · 终端发送请求进行中"},
|
|
380
|
+
"drawer.delivery_evidence_confirming": {"ko":"확인 근거 · 전송 요청 성공, 대화 기록 미확인","en":"Evidence · send request succeeded, conversation log not yet confirmed","zh-CN":"确认依据 · 发送请求成功,对话记录尚未确认"},
|
|
381
|
+
"drawer.delivery_evidence_delayed": {"ko":"확인 근거 · 12초 이상 대화 기록에서 메시지 미발견","en":"Evidence · message absent from the conversation log for over 12 seconds","zh-CN":"确认依据 · 超过 12 秒未在对话记录中发现消息"},
|
|
382
|
+
"drawer.delivery_evidence_received": {"ko":"확인 근거 · 대화 기록에서 사용자 메시지 발견","en":"Evidence · user message found in the conversation log","zh-CN":"确认依据 · 已在对话记录中发现用户消息"},
|
|
383
|
+
"drawer.delivery_evidence_responding": {"ko":"확인 근거 · 사용자 메시지와 새 실행 시작 이벤트 발견","en":"Evidence · user message and new execution-start event observed","zh-CN":"确认依据 · 已发现用户消息和新的执行开始事件"},
|
|
384
|
+
"drawer.delivery_evidence_failed": {"ko":"확인 근거 · 터미널 전송 오류","en":"Evidence · terminal send error","zh-CN":"确认依据 · 终端发送错误"},
|
|
385
|
+
"drawer.delivery_evidence_observed": {"ko":"확인 근거 · 사용자 메시지 기록 + 세션 실행 상태","en":"Evidence · user-message log + session runtime state","zh-CN":"确认依据 · 用户消息记录 + 会话运行状态"},
|
|
386
|
+
"drawer.delivery_step_dispatch": {"ko":"앱에서 전송 요청","en":"Send request from app","zh-CN":"应用发起发送请求"},
|
|
387
|
+
"drawer.delivery_step_dispatch_help": {"ko":"관리 터미널에 메시지를 전달합니다","en":"Pass the message to the managed terminal","zh-CN":"将消息传递给托管终端"},
|
|
388
|
+
"drawer.delivery_step_received": {"ko":"AI 세션에서 수신 확인","en":"Confirm receipt in AI session","zh-CN":"确认 AI 会话接收"},
|
|
389
|
+
"drawer.delivery_step_received_help": {"ko":"실제 대화 기록에서 같은 메시지를 찾습니다","en":"Find the same message in the actual conversation log","zh-CN":"在实际对话记录中查找相同消息"},
|
|
390
|
+
"drawer.delivery_step_response": {"ko":"AI 응답 시작 확인","en":"Confirm AI response start","zh-CN":"确认 AI 开始回复"},
|
|
391
|
+
"drawer.delivery_step_response_help": {"ko":"새 실행 이벤트나 응답 메시지를 확인합니다","en":"Observe a new execution event or response message","zh-CN":"观察新的执行事件或回复消息"},
|
|
351
392
|
"drawer.progress_updates": {"ko":"진행 업데이트 {count}개","en":"{count} progress updates","zh-CN":"{count} 条进度更新"},
|
|
352
393
|
"drawer.progress_updates_help": {"ko":"중간 작업 설명과 원문 전체 보기","en":"View intermediate notes and full text","zh-CN":"查看中间说明和完整原文"},
|
|
353
394
|
"drawer.progress_update_item": {"ko":"진행 {count}","en":"Update {count}","zh-CN":"进度 {count}"},
|
|
@@ -438,6 +479,10 @@
|
|
|
438
479
|
"session.reorder_hint": {"ko":"끌어서 위치 변경 · Alt+↑/↓ 키도 사용 가능","en":"Drag to reorder · Alt+↑/↓ also works","zh-CN":"拖动调整位置 · 也可使用 Alt+↑/↓"},
|
|
439
480
|
"session.reorder_help": {"ko":"세션 카드를 끌어 놓아 위치를 바꿀 수 있습니다. 키보드에서는 카드에 초점을 둔 뒤 Alt와 위아래 화살표 키를 사용하세요.","en":"Drag and drop a session card to change its position. With a keyboard, focus the card and use Alt with the up or down arrow key.","zh-CN":"拖放会话卡片可调整位置。使用键盘时,请聚焦卡片并按 Alt 加上或下方向键。"},
|
|
440
481
|
"session.position_changed": {"ko":"세션 위치를 변경했습니다.","en":"Session position updated.","zh-CN":"会话位置已更新。"},
|
|
482
|
+
"project.drag_label": {"ko":"{name} 프로젝트 위치 변경","en":"Reorder {name} project","zh-CN":"调整 {name} 项目的位置"},
|
|
483
|
+
"project.reorder_hint": {"ko":"끌어서 프로젝트 위치 변경 · 닫힌 상태와 Alt+↑/↓ 키도 지원","en":"Drag to reorder · works while collapsed and with Alt+↑/↓","zh-CN":"拖动调整项目位置 · 折叠状态及 Alt+↑/↓ 均可用"},
|
|
484
|
+
"project.reorder_help": {"ko":"프로젝트 헤더를 끌어 놓아 위치를 바꿀 수 있습니다. 프로젝트가 닫혀 있어도 이동할 수 있으며, 키보드에서는 헤더에 초점을 둔 뒤 Alt와 위아래 화살표 키를 사용하세요.","en":"Drag and drop a project header to change its position, even while collapsed. With a keyboard, focus the header and use Alt with the up or down arrow key.","zh-CN":"拖放项目标题可调整位置,折叠时也可以操作。使用键盘时,请聚焦标题并按 Alt 加上或下方向键。"},
|
|
485
|
+
"project.position_changed": {"ko":"프로젝트 위치를 변경했습니다.","en":"Project position updated.","zh-CN":"项目位置已更新。"},
|
|
441
486
|
"bootstrap.open_in_app": {"ko":"LoadToAgent 프로그램에서 열면 이 컴퓨터의 AI 작업 기록을 불러옵니다.","en":"Open this page in LoadToAgent to load AI work history from this computer.","zh-CN":"请在 LoadToAgent 应用中打开此页面,以加载此电脑上的 AI 工作记录。"},
|
|
442
487
|
"bootstrap.opened_attention_list": {"ko":"확인이 필요한 세션 목록을 열었습니다.","en":"Opened the list of sessions that need your review.","zh-CN":"已打开需要您确认的会话列表。"},
|
|
443
488
|
"bootstrap.initialization_failed": {"ko":"초기화 실패: {message}","en":"Initialization failed: {message}","zh-CN":"初始化失败:{message}"},
|
|
@@ -698,6 +743,7 @@
|
|
|
698
743
|
"terminal.agent.no_input_target": {"ko":"이 AI가 실행 중인 입력 가능한 터미널을 찾지 못했습니다. 외부 터미널에서 시작한 AI는 LoadToAgent가 직접 입력할 수 없습니다.","en":"No active terminal that accepts input was found for this AI. LoadToAgent cannot type into an AI started in an external terminal.","zh-CN":"找不到此 AI 正在运行且可输入的终端。LoadToAgent 无法直接向在外部终端中启动的 AI 输入内容。"},
|
|
699
744
|
"terminal.agent.reconnected": {"ko":"{provider} 세션 {sessionId}을 다시 연결했습니다. 이어서 지시할 수 있습니다.","en":"Reconnected {provider} session {sessionId}. You can continue giving instructions.","zh-CN":"已重新连接 {provider} 会话 {sessionId},您可以继续发出指令。"},
|
|
700
745
|
"terminal.agent.resume_terminal_failed": {"ko":"AI 세션을 재개할 터미널을 만들지 못했습니다.","en":"Could not create a terminal to resume the AI session.","zh-CN":"无法创建用于恢复 AI 会话的终端。"},
|
|
746
|
+
"terminal.agent.wsl_distro_missing": {"ko":"이 세션은 WSL에서 시작됐지만 사용할 Linux 배포판을 확인할 수 없습니다. tmux 화면을 새로고침한 뒤 다시 시도해 주세요.","en":"This session started in WSL, but its Linux distribution could not be identified. Refresh the tmux view and try again.","zh-CN":"此会话在 WSL 中启动,但无法识别其 Linux 发行版。请刷新 tmux 视图后重试。"},
|
|
701
747
|
"terminal.agent.resume_title": {"ko":"{provider} · {session} 이어서 작업","en":"{provider} · Continue {session}","zh-CN":"{provider} · 继续 {session}"},
|
|
702
748
|
"terminal.agent.resumed_and_sent": {"ko":"{provider} 세션 {sessionId}을 이어받아 지시를 보냈습니다.","en":"Resumed {provider} session {sessionId} and sent the instructions.","zh-CN":"已恢复 {provider} 会话 {sessionId} 并发送指令。"},
|
|
703
749
|
"terminal.agent.select_target": {"ko":"지시를 보낼 터미널을 먼저 선택하세요.","en":"Select the terminal to send instructions to first.","zh-CN":"请先选择要发送指令的终端。"},
|
|
@@ -1361,8 +1407,19 @@
|
|
|
1361
1407
|
"control.live_session": {"ko":"실행 중인 세션","en":"Live session","zh-CN":"运行中的会话"},
|
|
1362
1408
|
"control.waiting_session": {"ko":"대기 중인 세션","en":"Waiting session","zh-CN":"等待中的会话"},
|
|
1363
1409
|
"control.waiting_background_session": {"ko":"응답 대기 · 백그라운드 실행 중","en":"Waiting for response · background work running","zh-CN":"等待回复 · 后台任务运行中"},
|
|
1410
|
+
"control.delivery_sending": {"ko":"메시지 전송 중","en":"Sending message","zh-CN":"正在发送消息"},
|
|
1411
|
+
"control.delivery_confirming": {"ko":"AI 수신 확인 중","en":"Confirming AI receipt","zh-CN":"正在确认 AI 接收"},
|
|
1412
|
+
"control.delivery_delayed": {"ko":"전달 확인 지연","en":"Delivery confirmation delayed","zh-CN":"发送确认延迟"},
|
|
1413
|
+
"control.delivery_received": {"ko":"AI 메시지 수신 확인","en":"AI message receipt confirmed","zh-CN":"已确认 AI 接收消息"},
|
|
1414
|
+
"control.delivery_responding": {"ko":"AI 응답 생성 중","en":"AI response in progress","zh-CN":"AI 正在生成回复"},
|
|
1415
|
+
"control.delivery_failed": {"ko":"메시지 전송 실패","en":"Message send failed","zh-CN":"消息发送失败"},
|
|
1364
1416
|
"control.auto_history_in_minutes": {"ko":"{minutes}분 뒤 지난 기록으로 이동","en":"Moves to history in {minutes} min","zh-CN":"{minutes} 分钟后移至历史记录"},
|
|
1365
1417
|
"control.move_to_history": {"ko":"지난 기록으로 이동","en":"Move to history","zh-CN":"移至历史记录"},
|
|
1418
|
+
"control.project_group_actions": {"ko":"프로젝트 그룹 열기와 닫기","en":"Project group expand and collapse controls","zh-CN":"项目组展开和折叠控件"},
|
|
1419
|
+
"control.expand_all_projects": {"ko":"모두 열기","en":"Expand all","zh-CN":"全部展开"},
|
|
1420
|
+
"control.collapse_all_projects": {"ko":"모두 닫기","en":"Collapse all","zh-CN":"全部折叠"},
|
|
1421
|
+
"control.all_projects_expanded": {"ko":"모든 프로젝트 그룹을 열었습니다.","en":"Expanded all project groups.","zh-CN":"已展开所有项目组。"},
|
|
1422
|
+
"control.all_projects_collapsed": {"ko":"모든 프로젝트 그룹을 닫았습니다.","en":"Collapsed all project groups.","zh-CN":"已折叠所有项目组。"},
|
|
1366
1423
|
"control.moved_to_history": {"ko":"세션을 지난 기록으로 옮겼습니다.","en":"Session moved to history.","zh-CN":"会话已移至历史记录。"},
|
|
1367
1424
|
"control.live_summary": {"ko":"메인 {sessions} · 서브 {helpers} · 실행 {executions}","en":"{sessions} lead · {helpers} helper · {executions} commands","zh-CN":"主 {sessions} · 协助 {helpers} · 执行 {executions}"},
|
|
1368
1425
|
"control.main_assignment": {"ko":"메인 에이전트가 시킨 일","en":"Assigned by the lead agent","zh-CN":"主智能体分配的任务"},
|