loadtoagent 1.3.4 → 1.3.6
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/docs/PROVIDER-CONTRACTS.md +3 -1
- package/package.json +4 -2
- package/renderer/app-dashboard.js +75 -13
- package/renderer/app-drawer-content.js +189 -8
- package/renderer/app-drawer.js +4 -3
- package/renderer/app-events-dialogs.js +6 -1
- package/renderer/app-events-filters.js +80 -2
- package/renderer/app-events-sessions.js +110 -17
- package/renderer/app-graph-model.js +8 -5
- package/renderer/app-graph-orchestration.js +43 -11
- package/renderer/app-graph-view.js +75 -14
- package/renderer/app-quality.js +2 -0
- package/renderer/app-session-render.js +7 -7
- package/renderer/app.js +104 -0
- package/renderer/i18n-messages.js +46 -9
- package/renderer/index.html +33 -7
- package/renderer/styles-components.css +218 -1
- package/renderer/styles-control-room.css +820 -29
- package/renderer/styles-readability.css +1 -9
- package/renderer/styles-responsive-shell.css +22 -1
- package/renderer/styles-terminal.css +21 -31
- package/renderer/styles-workflow-map.css +8 -0
- package/renderer/terminal-events.js +0 -13
- package/renderer/terminal-workbench.js +1 -4
- package/src/agentMonitor/claudeParser.js +189 -4
- package/src/agentMonitor/codexCollaboration.js +1 -0
- package/src/agentMonitor/codexParser.js +6 -5
- package/src/agentMonitor/executionActivity.js +26 -1
- package/src/agentMonitor/hierarchy.js +6 -3
- package/src/agentMonitor.js +10 -3
- package/src/contracts.js +1 -1
- package/src/monitorWorker.js +2 -0
- package/src/nodePtyRuntime.js +58 -0
- package/src/terminalHost.js +104 -5
- package/src/terminalManager.js +5 -1
|
@@ -20,6 +20,9 @@ window.LoadToAgentAppFactories.createGraphView = function createGraphView(contex
|
|
|
20
20
|
statusClass,
|
|
21
21
|
currentActivity,
|
|
22
22
|
isLiveSession,
|
|
23
|
+
isControlRoomSession = isLiveSession,
|
|
24
|
+
controlRoomStatus = session => session?.status,
|
|
25
|
+
sessionRetentionMinutes = () => 0,
|
|
23
26
|
subagentWorkState,
|
|
24
27
|
subagentWorkLabel,
|
|
25
28
|
latestWorkCopy,
|
|
@@ -30,6 +33,7 @@ window.LoadToAgentAppFactories.createGraphView = function createGraphView(contex
|
|
|
30
33
|
executionModeBadge,
|
|
31
34
|
graphDescendantCount,
|
|
32
35
|
sessionWorkspaceLabel,
|
|
36
|
+
controlRoomProject = session => ({ key: String(session?.workspace || session?.id || "unknown"), label: sessionWorkspaceLabel(session) }),
|
|
33
37
|
} = context;
|
|
34
38
|
const t = (key, params) => window.LoadToAgentI18n.t(key, params);
|
|
35
39
|
const statusLabel = (status) => ({
|
|
@@ -44,6 +48,8 @@ window.LoadToAgentAppFactories.createGraphView = function createGraphView(contex
|
|
|
44
48
|
const usage = session.usage || {};
|
|
45
49
|
const percent = Math.max(0, Math.min(100, Number(context.percent || 0)));
|
|
46
50
|
const running = isLiveSession(session);
|
|
51
|
+
const presentationStatus = controlRoomStatus(session);
|
|
52
|
+
const retained = isControlRoomSession(session) && !running;
|
|
47
53
|
const childCount = (session.childIds || []).length;
|
|
48
54
|
const childMetrics = session.collaboration && session.collaboration.metrics;
|
|
49
55
|
const cumulativeChildren = childMetrics ? childMetrics.cumulativeCreated : childCount;
|
|
@@ -72,10 +78,12 @@ window.LoadToAgentAppFactories.createGraphView = function createGraphView(contex
|
|
|
72
78
|
<span class="provider-mark">${esc(provider.mark)}</span>
|
|
73
79
|
<span class="agent-identity"><b>${esc(role)}</b><small>${esc(provider.label)} · ${esc(session.model || t("graph.model_unknown"))}</small></span>
|
|
74
80
|
${executionModeBadge(session, true)}
|
|
75
|
-
<span class="status-pill ${statusClass(
|
|
81
|
+
<span class="status-pill ${statusClass(presentationStatus)}">${esc(statusLabel(presentationStatus))}</span>
|
|
76
82
|
</span>
|
|
77
83
|
<span class="agent-task-label">
|
|
78
|
-
${session.parentId ? t("graph.assigned_task", { source: delegation.assignmentSource === "
|
|
84
|
+
${session.parentId ? t("graph.assigned_task", { source: delegation.assignmentSource === "protected"
|
|
85
|
+
? t("graph.assignment_protected_suffix")
|
|
86
|
+
: (delegation.assignmentSource === "parent-narration" ? t("graph.main_ai_explanation_suffix") : "") }) : t("graph.current_goal")}
|
|
79
87
|
</span>
|
|
80
88
|
<strong class="agent-task" title="${esc(goalPreview.full)}">${esc(goalPreview.text)}</strong>
|
|
81
89
|
${goalPreview.truncated ? `<span class="agent-goal-note">${esc(t("graph.summary_shown"))}</span>` : ""}
|
|
@@ -103,6 +111,7 @@ window.LoadToAgentAppFactories.createGraphView = function createGraphView(contex
|
|
|
103
111
|
<span>${session.parentId
|
|
104
112
|
? (cumulativeChildren ? t("graph.subagents_created", { count: cumulativeChildren }) : t("graph.helper_ai"))
|
|
105
113
|
: t("project.origin_named", { name: sessionWorkspaceLabel(session) })}</span>
|
|
114
|
+
${retained ? `<button type="button" data-session-archive="${esc(session.id)}">${esc(t("control.move_to_history"))}</button>` : ""}
|
|
106
115
|
<button type="button" data-open-session="${esc(session.id)}">${esc(t("graph.view_conversation"))} <b>↗</b>
|
|
107
116
|
</button>
|
|
108
117
|
</footer>
|
|
@@ -113,6 +122,7 @@ window.LoadToAgentAppFactories.createGraphView = function createGraphView(contex
|
|
|
113
122
|
const provider = providerInfo(session.provider);
|
|
114
123
|
const usage = session.usage || {};
|
|
115
124
|
const directChildren = graphChildren(session, model).length;
|
|
125
|
+
const presentationStatus = controlRoomStatus(session);
|
|
116
126
|
const identity = session.parentId
|
|
117
127
|
? t("graph.helper_ai_named", { name: session.agentName || agentRoleLabel(session.agentRole) })
|
|
118
128
|
: t("project.origin_named", { name: sessionWorkspaceLabel(session) });
|
|
@@ -124,8 +134,9 @@ window.LoadToAgentAppFactories.createGraphView = function createGraphView(contex
|
|
|
124
134
|
const outcomeText = outcome || latestWorkCopy(session);
|
|
125
135
|
const assignedWorkPreview = readablePreview(assignedWork, session.parentId ? 110 : 104);
|
|
126
136
|
const taskLabel = session.parentId ? `${label || agentRoleLabel(session.agentRole)}${taskName ? t("graph.assigned_name_suffix", { name: taskName }) : ""}` : label;
|
|
127
|
-
const assignmentSourceNote =
|
|
128
|
-
|
|
137
|
+
const assignmentSourceNote = session.parentId && delegation.assignmentSource === "protected"
|
|
138
|
+
? `<span class="agent-flow-assignment-source">${esc(t("graph.assignment_source_protected"))}</span>`
|
|
139
|
+
: session.parentId && delegation.assignmentSource === "parent-narration"
|
|
129
140
|
? `<span class="agent-flow-assignment-source">${esc(t("graph.main_ai_prestart_explanation"))}</span>`
|
|
130
141
|
: "";
|
|
131
142
|
const sharedGoalCopy =
|
|
@@ -173,7 +184,7 @@ window.LoadToAgentAppFactories.createGraphView = function createGraphView(contex
|
|
|
173
184
|
</span>
|
|
174
185
|
</button>`;
|
|
175
186
|
}
|
|
176
|
-
return `<button type="button" class="agent-flow-row ${isLiveSession(session) ? "running" : ""} ${statusClass(
|
|
187
|
+
return `<button type="button" class="agent-flow-row ${isLiveSession(session) ? "running" : ""} ${statusClass(presentationStatus)}"
|
|
177
188
|
data-graph-focus="${esc(session.id)}"
|
|
178
189
|
data-motion-key="agent:${esc(session.id)}"
|
|
179
190
|
data-motion-value="${esc(session.updatedAt || "")}:${usage.total || 0}:${esc(session.status || "")}"
|
|
@@ -185,7 +196,7 @@ window.LoadToAgentAppFactories.createGraphView = function createGraphView(contex
|
|
|
185
196
|
<em>${esc(identity)} · ${directChildren ? `${t("graph.helper_ai_count", { count: directChildren })} · ` : ""}${esc(timeAgo(session.updatedAt))}</em>
|
|
186
197
|
${assignmentSourceNote}${sharedGoalCopy}${outcomeCopy}
|
|
187
198
|
</span>
|
|
188
|
-
<span class="agent-flow-provider"><i>${esc(provider.mark)}</i><small>${esc(statusLabel(
|
|
199
|
+
<span class="agent-flow-provider"><i>${esc(provider.mark)}</i><small>${esc(statusLabel(presentationStatus))}</small></span>
|
|
189
200
|
</button>`;
|
|
190
201
|
}
|
|
191
202
|
|
|
@@ -418,7 +429,8 @@ window.LoadToAgentAppFactories.createGraphView = function createGraphView(contex
|
|
|
418
429
|
const executionLabel = t("control.runtime_work", { runtime, kind: executionKind });
|
|
419
430
|
const ownerGoal = controlRoomAgentGoal(owner, 52);
|
|
420
431
|
const running = activity.status === "running";
|
|
421
|
-
|
|
432
|
+
const stateClass = running ? "is-running" : (activity.status === "unverified" ? "is-unverified" : "is-complete");
|
|
433
|
+
return `<button type="button" class="control-room-node execution-node ${stateClass}"
|
|
422
434
|
data-open-execution-owner="${esc(owner.id)}"
|
|
423
435
|
data-open-execution-id="${esc(activity.id)}"
|
|
424
436
|
data-control-summary="${esc(summary.text)}"
|
|
@@ -433,10 +445,13 @@ window.LoadToAgentAppFactories.createGraphView = function createGraphView(contex
|
|
|
433
445
|
|
|
434
446
|
function controlRoomSession(root, model) {
|
|
435
447
|
const provider = providerInfo(root.provider);
|
|
448
|
+
const presentationStatus = controlRoomStatus(root);
|
|
449
|
+
const waiting = presentationStatus === "waiting";
|
|
450
|
+
const retained = isControlRoomSession(root) && !isLiveSession(root);
|
|
436
451
|
const descendants = controlRoomDescendants(root, model);
|
|
437
452
|
const actors = [root, ...descendants];
|
|
438
453
|
const executionItems = actors.flatMap(owner => (owner.executions || []).map(activity => ({ activity, owner })));
|
|
439
|
-
const activeChildren = descendants.filter(child =>
|
|
454
|
+
const activeChildren = descendants.filter(child => ["starting", "running", "paused", "waiting"].includes(child.status) && !child.completionObserved);
|
|
440
455
|
const completedChildren = descendants.filter(child => ["completed", "cancelled", "failed"].includes(child.status) || child.completionObserved);
|
|
441
456
|
const activeExecutions = executionItems.filter(item => item.activity.status === "running");
|
|
442
457
|
const completedExecutions = executionItems
|
|
@@ -454,7 +469,7 @@ window.LoadToAgentAppFactories.createGraphView = function createGraphView(contex
|
|
|
454
469
|
data-control-summary="${esc(title.text)}"
|
|
455
470
|
data-motion-key="control-main:${esc(root.id)}" data-motion-value="${esc(root.updatedAt || "")}:${esc(root.status || "")}"
|
|
456
471
|
style="${providerStyle(root.provider)}">
|
|
457
|
-
<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(
|
|
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>
|
|
458
473
|
<strong title="${esc(title.full)}">${esc(title.text)}</strong>
|
|
459
474
|
<span class="control-main-now"><small>${esc(t("graph.current_work"))}</small><b title="${esc(current.full)}">${esc(current.text)}</b></span>
|
|
460
475
|
<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>
|
|
@@ -467,8 +482,16 @@ window.LoadToAgentAppFactories.createGraphView = function createGraphView(contex
|
|
|
467
482
|
const completed = completedUnits.length
|
|
468
483
|
? completedUnits.map(unit => unit.kind === "child" ? controlRoomChildNode(unit.child) : controlRoomExecutionNode(unit.item)).join("")
|
|
469
484
|
: `<div class="control-room-complete-empty"><span>✓</span><small>${esc(t("control.completed_empty"))}</small></div>`;
|
|
470
|
-
|
|
471
|
-
|
|
485
|
+
const waitingWithBackground = waiting && activeExecutions.some(item => item.activity.mode === "background" || item.activity.kind === "background");
|
|
486
|
+
const sessionStateKey = waitingWithBackground
|
|
487
|
+
? "control.waiting_background_session"
|
|
488
|
+
: (waiting ? "control.waiting_session" : "control.live_session");
|
|
489
|
+
const retention = retained ? `<small class="control-session-retention">${esc(t("control.auto_history_in_minutes", { minutes: sessionRetentionMinutes(root) }))}</small>` : "";
|
|
490
|
+
const archive = retained ? `<button type="button" class="control-session-archive" data-session-archive="${esc(root.id)}">${esc(t("control.move_to_history"))}</button>` : "";
|
|
491
|
+
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)}"
|
|
492
|
+
style="${providerStyle(root.provider)}" role="group" tabindex="0" draggable="true" aria-grabbed="false"
|
|
493
|
+
aria-keyshortcuts="Alt+ArrowUp Alt+ArrowDown" aria-label="${esc(t("session.drag_label", { title: title.text }))}" aria-describedby="sessionReorderHelp">
|
|
494
|
+
<header><div><span class="control-session-live"><i></i>${esc(t(sessionStateKey))}</span><b>${esc(title.text)}</b>${retention}</div><span class="session-drag-handle" aria-hidden="true" title="${esc(t("session.reorder_hint"))}"></span>${archive}<button type="button" class="control-session-flow" data-graph-focus="${esc(root.id)}">${esc(t("control.open_full_flow"))} ↗</button></header>
|
|
472
495
|
<div class="control-room-flow">
|
|
473
496
|
<section class="control-room-column main-column"><span class="control-column-label">${esc(t("control.main_work_column"))}</span>${main}</section>
|
|
474
497
|
<span class="control-flow-link live" aria-hidden="true"><i></i></span>
|
|
@@ -479,9 +502,44 @@ window.LoadToAgentAppFactories.createGraphView = function createGraphView(contex
|
|
|
479
502
|
</article>`;
|
|
480
503
|
}
|
|
481
504
|
|
|
482
|
-
function runtimeSeparatedOverview(roots, model) {
|
|
505
|
+
function runtimeSeparatedOverview(roots, model, allRoots = roots) {
|
|
506
|
+
const projectDescriptor = (root) => {
|
|
507
|
+
const project = controlRoomProject(root);
|
|
508
|
+
return { key: project.key, name: project.label };
|
|
509
|
+
};
|
|
510
|
+
const allGroups = new Map();
|
|
511
|
+
allRoots.forEach((root) => {
|
|
512
|
+
const { key, name } = projectDescriptor(root);
|
|
513
|
+
if (!allGroups.has(key)) allGroups.set(key, { name, roots: [] });
|
|
514
|
+
allGroups.get(key).roots.push(root);
|
|
515
|
+
});
|
|
516
|
+
const groups = new Map();
|
|
517
|
+
roots.forEach((root) => {
|
|
518
|
+
const { key, name } = projectDescriptor(root);
|
|
519
|
+
if (!groups.has(key)) groups.set(key, { name, roots: [] });
|
|
520
|
+
groups.get(key).roots.push(root);
|
|
521
|
+
});
|
|
522
|
+
const projectGroups = [...groups.entries()].map(([key, { name, roots: projectRoots }], index) => {
|
|
523
|
+
const projectTotals = allGroups.get(key)?.roots || projectRoots;
|
|
524
|
+
const activeCount = projectTotals.filter((root) => isLiveSession(root)).length;
|
|
525
|
+
const attentionCount = projectTotals.filter((root) => !isLiveSession(root) && isControlRoomSession(root)).length;
|
|
526
|
+
const summary = attentionCount
|
|
527
|
+
? t("control.project_live_attention_summary", { active: activeCount, attention: attentionCount })
|
|
528
|
+
: t("control.project_live_summary", { active: activeCount });
|
|
529
|
+
const disclosureKey = `control-project:${key}`;
|
|
530
|
+
const presentation = index === 0 ? "is-primary" : index === 1 ? "is-preview" : "is-collapsed";
|
|
531
|
+
const projectFocusId = projectRoots[0]?.id || "";
|
|
532
|
+
return `<details class="control-room-project-group ${presentation}" data-control-project="${esc(name)}" data-disclosure-key="${esc(disclosureKey)}" ${index < 2 ? "open" : ""}>
|
|
533
|
+
<summary class="control-project-header" data-project-toggle="${esc(name)}">
|
|
534
|
+
<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" role="img" aria-label="프로젝트 그룹" title="프로젝트 그룹 · 세션은 최근 활동순으로 표시됩니다"></span>
|
|
536
|
+
</summary>
|
|
537
|
+
<button type="button" class="control-project-flow-link" data-graph-focus="${esc(projectFocusId)}"><span>${esc(t("control.open_full_flow"))} ↗</span></button>
|
|
538
|
+
<div class="control-project-body" ${index === 1 ? 'inert aria-hidden="true"' : ""}>${projectRoots.map(root => controlRoomSession(root, model)).join("")}</div>
|
|
539
|
+
</details>`;
|
|
540
|
+
}).join("");
|
|
483
541
|
return `<div class="control-room-overview" data-control-room-overview="true">
|
|
484
|
-
${
|
|
542
|
+
${projectGroups}
|
|
485
543
|
</div>`;
|
|
486
544
|
}
|
|
487
545
|
|
|
@@ -609,7 +667,9 @@ window.LoadToAgentAppFactories.createGraphView = function createGraphView(contex
|
|
|
609
667
|
: event.kind === "started"
|
|
610
668
|
? t("graph.runtime_start_confirmed")
|
|
611
669
|
: t("graph.status_only_recorded"));
|
|
612
|
-
const sourceLabel = event.assignmentSource === "
|
|
670
|
+
const sourceLabel = event.assignmentSource === "protected"
|
|
671
|
+
? ` · ${t("graph.assignment_source_protected_short")}`
|
|
672
|
+
: (event.assignmentSource === "parent-narration" ? ` · ${t("graph.main_ai_prestart_short")}` : "");
|
|
613
673
|
return `<article class="agent-communication-event ${esc(event.kind)}" data-communication-kind="${esc(event.kind)}">
|
|
614
674
|
<span class="communication-route">
|
|
615
675
|
<b>${esc(communicationEndpoint(event.from, owner, model))}</b><i>→</i>
|
|
@@ -650,6 +710,7 @@ window.LoadToAgentAppFactories.createGraphView = function createGraphView(contex
|
|
|
650
710
|
running: t("graph.execution_running"),
|
|
651
711
|
completed: t("graph.execution_completed"),
|
|
652
712
|
failed: t("graph.execution_failed"),
|
|
713
|
+
unverified: t("graph.execution_unverified"),
|
|
653
714
|
cancelled: t("ui.stopped"),
|
|
654
715
|
})[activity.status] || activity.status;
|
|
655
716
|
}
|
package/renderer/app-quality.js
CHANGED
|
@@ -94,6 +94,7 @@ window.LoadToAgentAppFactories.createQualityEnhancements = function createQualit
|
|
|
94
94
|
? dashboard.workspace
|
|
95
95
|
: "all";
|
|
96
96
|
state.sort = ["recent", "tokens", "context"].includes(dashboard.sort) ? dashboard.sort : "recent";
|
|
97
|
+
state.controlRoomSort = ["recent", "tokens", "context"].includes(dashboard.controlRoomSort) ? dashboard.controlRoomSort : "recent";
|
|
97
98
|
state.sessionOrder = Array.isArray(dashboard.sessionOrder)
|
|
98
99
|
? dashboard.sessionOrder.filter(id => typeof id === "string" && id.length <= 500).slice(0, 1_000)
|
|
99
100
|
: [];
|
|
@@ -130,6 +131,7 @@ window.LoadToAgentAppFactories.createQualityEnhancements = function createQualit
|
|
|
130
131
|
providers: [...state.providerFilters],
|
|
131
132
|
workspace: String(state.workspace || "all").slice(0, 2_000),
|
|
132
133
|
sort: ["recent", "tokens", "context"].includes(state.sort) ? state.sort : "recent",
|
|
134
|
+
controlRoomSort: ["recent", "tokens", "context"].includes(state.controlRoomSort) ? state.controlRoomSort : "recent",
|
|
133
135
|
sessionOrder: (state.sessionOrder || []).filter(id => typeof id === "string" && id.length <= 500).slice(0, 1_000),
|
|
134
136
|
};
|
|
135
137
|
try {
|
|
@@ -24,6 +24,7 @@ window.LoadToAgentAppFactories.createSessionRenderer = function createSessionRen
|
|
|
24
24
|
statusClass,
|
|
25
25
|
currentActivity,
|
|
26
26
|
isLiveSession,
|
|
27
|
+
isControlRoomSession = isLiveSession,
|
|
27
28
|
latestWorkCopy,
|
|
28
29
|
statusIcon,
|
|
29
30
|
renderProviderRail,
|
|
@@ -75,11 +76,13 @@ window.LoadToAgentAppFactories.createSessionRenderer = function createSessionRen
|
|
|
75
76
|
const originLabel = sessionWorkspaceLabel(session);
|
|
76
77
|
return `<article class="session-card session-record ${opts.live ? "live-card" : ""} ${statusClass(session.status)} ${session.parentId ? "subagent" : ""}"
|
|
77
78
|
data-session-id="${esc(session.id)}"
|
|
79
|
+
data-session-sortable="${esc(session.id)}"
|
|
78
80
|
data-motion-key="session:${esc(session.id)}"
|
|
79
81
|
data-motion-value="${esc(session.updatedAt || "")}:${esc(session.status || "")}"
|
|
80
82
|
style="${providerStyle(session.provider)}"
|
|
81
|
-
role="button" tabindex="0"
|
|
82
|
-
aria-
|
|
83
|
+
role="button" tabindex="0" draggable="true" aria-grabbed="false"
|
|
84
|
+
aria-keyshortcuts="Alt+ArrowUp Alt+ArrowDown"
|
|
85
|
+
aria-labelledby="${accessibleId}-title" aria-describedby="${accessibleId}-summary sessionReorderHelp">
|
|
83
86
|
<div class="card-head">
|
|
84
87
|
<span class="provider-mark">${esc(provider.mark)}</span>
|
|
85
88
|
<div class="card-head-main"><div class="card-provider-line"><b>${esc(provider.label)}</b><span>${esc(session.model || t("session.model_unknown"))}</span></div></div>
|
|
@@ -95,10 +98,7 @@ window.LoadToAgentAppFactories.createSessionRenderer = function createSessionRen
|
|
|
95
98
|
</div>
|
|
96
99
|
<footer class="card-footer">
|
|
97
100
|
<span>${esc(timeAgo(session.updatedAt))}</span>
|
|
98
|
-
<span class="session-
|
|
99
|
-
<button type="button" data-session-order-move="${esc(session.id)}" data-session-order-offset="-1" title="${esc(t("session.move_up"))}" aria-label="${esc(t("session.move_up"))}">↑</button>
|
|
100
|
-
<button type="button" data-session-order-move="${esc(session.id)}" data-session-order-offset="1" title="${esc(t("session.move_down"))}" aria-label="${esc(t("session.move_down"))}">↓</button>
|
|
101
|
-
</span>
|
|
101
|
+
<span class="session-drag-handle" aria-hidden="true" title="${esc(t("session.reorder_hint"))}"></span>
|
|
102
102
|
<strong>${esc(t("graph.view_conversation"))}<i aria-hidden="true">→</i></strong>
|
|
103
103
|
</footer>
|
|
104
104
|
</article>`;
|
|
@@ -166,7 +166,7 @@ window.LoadToAgentAppFactories.createSessionRenderer = function createSessionRen
|
|
|
166
166
|
const attentionCount = attentionView ? renderAttentionInbox() : 0;
|
|
167
167
|
const showMap = ["all", "active"].includes(state.view);
|
|
168
168
|
const graphLiveCount = showMap ? renderAgentMap(graphFilteredSessions(), motionKind) : 0;
|
|
169
|
-
const regular = state.view === "all" ? sessions.filter((session) => !
|
|
169
|
+
const regular = state.view === "all" ? sessions.filter((session) => !isControlRoomSession(session)) : state.view === "active" ? [] : sessions;
|
|
170
170
|
const visible = regular.slice(0, state.visibleLimit);
|
|
171
171
|
const resultCount = attentionView ? attentionCount : graphLiveCount + regular.length;
|
|
172
172
|
$("#sessionResultSummary").textContent = window.LoadToAgentI18n.t("quality.results_summary", { count: resultCount });
|
package/renderer/app.js
CHANGED
|
@@ -5,6 +5,8 @@ window.LoadToAgentAppFactories.createCore = function createCore(context = {}) {
|
|
|
5
5
|
const t = (key, params) => window.LoadToAgentI18n.t(key, params);
|
|
6
6
|
const observedText = (value) => window.LoadToAgentI18n.observedText(value);
|
|
7
7
|
const PROJECTLESS_WORKSPACE = "__projectless__";
|
|
8
|
+
const SESSION_RETENTION_MS = 30 * 60 * 1000;
|
|
9
|
+
const SESSION_ARCHIVE_STORAGE_KEY = "loadtoagent:session-archives:v1";
|
|
8
10
|
const state = {
|
|
9
11
|
providers: [],
|
|
10
12
|
providerMap: new Map(),
|
|
@@ -22,6 +24,8 @@ window.LoadToAgentAppFactories.createCore = function createCore(context = {}) {
|
|
|
22
24
|
search: "",
|
|
23
25
|
sort: "recent",
|
|
24
26
|
sessionOrder: [],
|
|
27
|
+
sessionArchives: new Map(),
|
|
28
|
+
controlRoomObservedIds: new Set(),
|
|
25
29
|
selectedId: null,
|
|
26
30
|
drawerTab: "chat",
|
|
27
31
|
drawerMode: "session",
|
|
@@ -32,6 +36,9 @@ window.LoadToAgentAppFactories.createCore = function createCore(context = {}) {
|
|
|
32
36
|
drawerForceLatest: false,
|
|
33
37
|
visibleLimit: 30,
|
|
34
38
|
graphFocusId: null,
|
|
39
|
+
controlRoomPage: 0,
|
|
40
|
+
controlRoomPageSize: 4,
|
|
41
|
+
controlRoomSort: "recent",
|
|
35
42
|
supervisionFocusId: null,
|
|
36
43
|
graphExpandedProviders: new Set(),
|
|
37
44
|
expandedExecutionSessions: new Set(),
|
|
@@ -566,6 +573,91 @@ window.LoadToAgentAppFactories.createCore = function createCore(context = {}) {
|
|
|
566
573
|
function isLiveSession(session) {
|
|
567
574
|
return session && (session.status === "running" || session.status === "starting");
|
|
568
575
|
}
|
|
576
|
+
function hasRunningExecution(session) {
|
|
577
|
+
return Boolean((session?.executions || []).some((execution) => execution?.status === "running"));
|
|
578
|
+
}
|
|
579
|
+
function sessionResponseTimestamp(session) {
|
|
580
|
+
const assistantAt = Math.max(0, ...(session?.messages || [])
|
|
581
|
+
.filter((message) => message?.role === "assistant")
|
|
582
|
+
.map((message) => Date.parse(message.timestamp || 0))
|
|
583
|
+
.filter(Number.isFinite));
|
|
584
|
+
if (assistantAt) return assistantAt;
|
|
585
|
+
const completedAt = Date.parse(session?.completedAt || session?.endedAt || 0);
|
|
586
|
+
return Number.isFinite(completedAt) ? completedAt : 0;
|
|
587
|
+
}
|
|
588
|
+
function loadSessionArchives() {
|
|
589
|
+
try {
|
|
590
|
+
const saved = JSON.parse(localStorage.getItem(SESSION_ARCHIVE_STORAGE_KEY) || "{}");
|
|
591
|
+
state.sessionArchives = new Map(Object.entries(saved)
|
|
592
|
+
.filter(([id, value]) => id && Number.isFinite(Number(value?.responseAt)))
|
|
593
|
+
.map(([id, value]) => [id, { responseAt: Number(value.responseAt), archivedAt: Number(value.archivedAt || 0) }]));
|
|
594
|
+
} catch (error) {
|
|
595
|
+
reportRecoverableError("session-archives-load", error);
|
|
596
|
+
state.sessionArchives = new Map();
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
function saveSessionArchives() {
|
|
600
|
+
try {
|
|
601
|
+
const recent = [...state.sessionArchives.entries()]
|
|
602
|
+
.sort((left, right) => Number(right[1]?.archivedAt || 0) - Number(left[1]?.archivedAt || 0))
|
|
603
|
+
.slice(0, 500);
|
|
604
|
+
state.sessionArchives = new Map(recent);
|
|
605
|
+
localStorage.setItem(SESSION_ARCHIVE_STORAGE_KEY, JSON.stringify(Object.fromEntries(recent)));
|
|
606
|
+
} catch (error) {
|
|
607
|
+
reportRecoverableError("session-archives-save", error);
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
function isSessionManuallyArchived(session) {
|
|
611
|
+
const archived = session && state.sessionArchives.get(String(session.id || ""));
|
|
612
|
+
if (!archived) return false;
|
|
613
|
+
return sessionResponseTimestamp(session) <= Number(archived.responseAt || 0);
|
|
614
|
+
}
|
|
615
|
+
function isControlRoomSession(session, now = Date.now()) {
|
|
616
|
+
if (!session) return false;
|
|
617
|
+
if (isLiveSession(session) || hasRunningExecution(session)) {
|
|
618
|
+
state.controlRoomObservedIds.add(String(session.id || ""));
|
|
619
|
+
return true;
|
|
620
|
+
}
|
|
621
|
+
if (!state.controlRoomObservedIds.has(String(session.id || ""))) return false;
|
|
622
|
+
const responseAt = sessionResponseTimestamp(session);
|
|
623
|
+
const retained = Boolean(responseAt
|
|
624
|
+
&& !isSessionManuallyArchived(session)
|
|
625
|
+
&& Math.max(0, Number(now) - responseAt) < SESSION_RETENTION_MS);
|
|
626
|
+
if (!retained && responseAt && Math.max(0, Number(now) - responseAt) >= SESSION_RETENTION_MS) {
|
|
627
|
+
state.controlRoomObservedIds.delete(String(session.id || ""));
|
|
628
|
+
}
|
|
629
|
+
return retained;
|
|
630
|
+
}
|
|
631
|
+
function controlRoomStatus(session, now = Date.now()) {
|
|
632
|
+
return isLiveSession(session) ? session.status : (isControlRoomSession(session, now) ? "waiting" : session?.status);
|
|
633
|
+
}
|
|
634
|
+
function sessionRetentionMinutes(session, now = Date.now()) {
|
|
635
|
+
const remaining = SESSION_RETENTION_MS - Math.max(0, Number(now) - sessionResponseTimestamp(session));
|
|
636
|
+
return Math.max(0, Math.ceil(remaining / 60_000));
|
|
637
|
+
}
|
|
638
|
+
function archiveSession(sessionOrId) {
|
|
639
|
+
const session = typeof sessionOrId === "object"
|
|
640
|
+
? sessionOrId
|
|
641
|
+
: (state.snapshot?.sessions || []).find((item) => item.id === String(sessionOrId || ""));
|
|
642
|
+
if (!session || isLiveSession(session)) return false;
|
|
643
|
+
const responseAt = sessionResponseTimestamp(session);
|
|
644
|
+
if (!responseAt) return false;
|
|
645
|
+
const archivedAt = Date.now();
|
|
646
|
+
const sessions = state.snapshot?.sessions || [];
|
|
647
|
+
const byId = new Map(sessions.map((item) => [item.id, item]));
|
|
648
|
+
const queue = [session, ...(session.childIds || []).map((id) => byId.get(id)).filter(Boolean)];
|
|
649
|
+
const seen = new Set();
|
|
650
|
+
while (queue.length) {
|
|
651
|
+
const item = queue.shift();
|
|
652
|
+
if (!item?.id || seen.has(item.id)) continue;
|
|
653
|
+
seen.add(item.id);
|
|
654
|
+
const itemResponseAt = sessionResponseTimestamp(item);
|
|
655
|
+
if (itemResponseAt) state.sessionArchives.set(String(item.id), { responseAt: itemResponseAt, archivedAt });
|
|
656
|
+
queue.push(...(item.childIds || []).map((id) => byId.get(id)).filter(Boolean));
|
|
657
|
+
}
|
|
658
|
+
saveSessionArchives();
|
|
659
|
+
return true;
|
|
660
|
+
}
|
|
569
661
|
function isRuntimeLoopSession(session) {
|
|
570
662
|
if (!session || session.parentId || !isLiveSession(session)) return false;
|
|
571
663
|
if (session.loop === true || (session.loop && typeof session.loop === "object")) return true;
|
|
@@ -627,6 +719,7 @@ window.LoadToAgentAppFactories.createCore = function createCore(context = {}) {
|
|
|
627
719
|
if (/end|complete/.test(type)) return "✓";
|
|
628
720
|
return "·";
|
|
629
721
|
}
|
|
722
|
+
loadSessionArchives();
|
|
630
723
|
return {
|
|
631
724
|
$,
|
|
632
725
|
$$,
|
|
@@ -636,6 +729,8 @@ window.LoadToAgentAppFactories.createCore = function createCore(context = {}) {
|
|
|
636
729
|
reportRecoverableError,
|
|
637
730
|
observedText,
|
|
638
731
|
PROJECTLESS_WORKSPACE,
|
|
732
|
+
SESSION_RETENTION_MS,
|
|
733
|
+
SESSION_ARCHIVE_STORAGE_KEY,
|
|
639
734
|
state,
|
|
640
735
|
motionPreference,
|
|
641
736
|
motionState,
|
|
@@ -681,6 +776,15 @@ window.LoadToAgentAppFactories.createCore = function createCore(context = {}) {
|
|
|
681
776
|
statusClass,
|
|
682
777
|
currentActivity,
|
|
683
778
|
isLiveSession,
|
|
779
|
+
hasRunningExecution,
|
|
780
|
+
sessionResponseTimestamp,
|
|
781
|
+
loadSessionArchives,
|
|
782
|
+
saveSessionArchives,
|
|
783
|
+
isSessionManuallyArchived,
|
|
784
|
+
isControlRoomSession,
|
|
785
|
+
controlRoomStatus,
|
|
786
|
+
sessionRetentionMinutes,
|
|
787
|
+
archiveSession,
|
|
684
788
|
isRuntimeLoopSession,
|
|
685
789
|
subagentWorkState,
|
|
686
790
|
subagentWorkLabel,
|
|
@@ -351,6 +351,18 @@
|
|
|
351
351
|
"drawer.progress_updates": {"ko":"진행 업데이트 {count}개","en":"{count} progress updates","zh-CN":"{count} 条进度更新"},
|
|
352
352
|
"drawer.progress_updates_help": {"ko":"중간 작업 설명과 원문 전체 보기","en":"View intermediate notes and full text","zh-CN":"查看中间说明和完整原文"},
|
|
353
353
|
"drawer.progress_update_item": {"ko":"진행 {count}","en":"Update {count}","zh-CN":"进度 {count}"},
|
|
354
|
+
"drawer.subagent_called": {"ko":"서브에이전트 호출","en":"Helper agent called","zh-CN":"已调用协助智能体"},
|
|
355
|
+
"drawer.subagent_call_point": {"ko":"서브에이전트 호출 #{count}","en":"Helper-agent call #{count}","zh-CN":"协助智能体调用 #{count}"},
|
|
356
|
+
"drawer.called_after_user_request": {"ko":"사용자 요청 {elapsed} 후 호출","en":"Called {elapsed} after the user request","zh-CN":"用户请求 {elapsed} 后调用"},
|
|
357
|
+
"drawer.duration_less_than_second": {"ko":"1초 미만","en":"less than 1s","zh-CN":"不到 1 秒"},
|
|
358
|
+
"drawer.duration_seconds": {"ko":"{seconds}초","en":"{seconds}s","zh-CN":"{seconds} 秒"},
|
|
359
|
+
"drawer.duration_minutes_seconds": {"ko":"{minutes}분 {seconds}초","en":"{minutes}m {seconds}s","zh-CN":"{minutes} 分 {seconds} 秒"},
|
|
360
|
+
"drawer.duration_hours_minutes_seconds": {"ko":"{hours}시간 {minutes}분 {seconds}초","en":"{hours}h {minutes}m {seconds}s","zh-CN":"{hours} 小时 {minutes} 分 {seconds} 秒"},
|
|
361
|
+
"drawer.called_after_main_message": {"ko":"메인 에이전트가 이 말 다음에 호출","en":"The lead agent called a helper after this message","zh-CN":"主智能体在这段话之后调用了协助智能体"},
|
|
362
|
+
"drawer.main_called_here": {"ko":"메인 에이전트가 이 지점에서 서브에이전트를 호출","en":"The lead agent called a helper at this point","zh-CN":"主智能体在此处调用了协助智能体"},
|
|
363
|
+
"drawer.main_progress_before_call": {"ko":"메인 에이전트 중간 응답","en":"Lead-agent progress update","zh-CN":"主智能体中间回复"},
|
|
364
|
+
"drawer.subagent_name": {"ko":"호출된 서브에이전트 · {name}","en":"Called helper · {name}","zh-CN":"调用的协助智能体 · {name}"},
|
|
365
|
+
"drawer.open_subagent_work": {"ko":"작업 내용 보기","en":"View work","zh-CN":"查看工作内容"},
|
|
354
366
|
"drawer.activities": {"ko":"활동 {count}건","en":"{count} activities","zh-CN":"{count} 条活动"},
|
|
355
367
|
"drawer.latest_conversation": {"ko":"가장 최근 대화","en":"Latest conversation","zh-CN":"最新对话"},
|
|
356
368
|
"drawer.no_lifecycle": {"ko":"아직 기록된 진행 과정이 없습니다","en":"No progress has been recorded yet","zh-CN":"尚未记录进度"},
|
|
@@ -381,6 +393,12 @@
|
|
|
381
393
|
"drawer.subagent_history_actual": {"ko":"이 도움 AI 세션에 실제로 저장된 작업 지시와 답변을 시간순으로 표시합니다.","en":"Shows the instructions and responses actually saved in this helper-AI session, in chronological order.","zh-CN":"按时间顺序显示此协助 AI 会话中实际保存的指令和回答。"},
|
|
382
394
|
"drawer.subagent_work_history": {"ko":"도움 AI 실제 작업 기록","en":"Actual helper-AI work history","zh-CN":"协助 AI 实际工作记录"},
|
|
383
395
|
"drawer.assignment": {"ko":"작업 지시","en":"Assignment","zh-CN":"任务指令"},
|
|
396
|
+
"drawer.assignment_context": {"ko":"호출 직전 메인 AI 설명 (실제 전달 원문 아님)","en":"Lead-AI explanation before the call (not the transmitted prompt)","zh-CN":"调用前主 AI 的说明(不是实际传递的原文)"},
|
|
397
|
+
"drawer.assignment_protected": {"ko":"실제 작업 지시 원문은 제공사 로그에서 암호화되어 있어 확인할 수 없습니다.","en":"The exact assignment is encrypted in the provider log and cannot be verified.","zh-CN":"实际任务指令原文在提供商日志中已加密,无法核实。"},
|
|
398
|
+
"drawer.assignment_protected_short": {"ko":"실제 지시 원문 보호됨","en":"Exact prompt protected","zh-CN":"实际指令原文受保护"},
|
|
399
|
+
"drawer.assignment_source_claude": {"ko":"Claude Agent/Task 호출 prompt 원문","en":"Exact Claude Agent/Task call prompt","zh-CN":"Claude Agent/Task 调用的 prompt 原文"},
|
|
400
|
+
"drawer.assignment_source_codex": {"ko":"Codex spawn_agent 호출 message 원문","en":"Exact Codex spawn_agent call message","zh-CN":"Codex spawn_agent 调用的 message 原文"},
|
|
401
|
+
"drawer.assignment_source_protected": {"ko":"제공사 로그에서 원문 보호됨","en":"Exact text protected in provider log","zh-CN":"原文在提供商日志中受保护"},
|
|
384
402
|
"drawer.sub_ai": {"ko":"도움 AI","en":"Helper AI","zh-CN":"协助 AI"},
|
|
385
403
|
"drawer.work_history": {"ko":"작업 기록","en":"Work history","zh-CN":"工作记录"},
|
|
386
404
|
"drawer.execution_unit": {"ko":"실행 작업","en":"Execution unit","zh-CN":"执行任务"},
|
|
@@ -416,9 +434,9 @@
|
|
|
416
434
|
"session.total_tokens": {"ko":"전체 사용","en":"Total usage","zh-CN":"总用量"},
|
|
417
435
|
"session.subagents_created": {"ko":"도움 AI {count}개 누적 생성","en":"{count} helper AI created","zh-CN":"累计创建 {count} 个协助 AI"},
|
|
418
436
|
"session.local_history": {"ko":"내 PC의 작업 기록","en":"Work history on this PC","zh-CN":"此电脑上的工作记录"},
|
|
419
|
-
"session.
|
|
420
|
-
"session.
|
|
421
|
-
"session.
|
|
437
|
+
"session.drag_label": {"ko":"{title} 위치 변경","en":"Reorder {title}","zh-CN":"调整 {title} 的位置"},
|
|
438
|
+
"session.reorder_hint": {"ko":"끌어서 위치 변경 · Alt+↑/↓ 키도 사용 가능","en":"Drag to reorder · Alt+↑/↓ also works","zh-CN":"拖动调整位置 · 也可使用 Alt+↑/↓"},
|
|
439
|
+
"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 加上或下方向键。"},
|
|
422
440
|
"session.position_changed": {"ko":"세션 위치를 변경했습니다.","en":"Session position updated.","zh-CN":"会话位置已更新。"},
|
|
423
441
|
"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 工作记录。"},
|
|
424
442
|
"bootstrap.opened_attention_list": {"ko":"확인이 필요한 세션 목록을 열었습니다.","en":"Opened the list of sessions that need your review.","zh-CN":"已打开需要您确认的会话列表。"},
|
|
@@ -541,6 +559,7 @@
|
|
|
541
559
|
"graph.execution_recent_hidden": {"ko":"이전 실행 기록 {count}개는 상세 기록에 보존됩니다.","en":"{count} older execution records remain available in session history.","zh-CN":"会话历史中仍保留 {count} 条较早的执行记录。"},
|
|
542
560
|
"graph.execution_show_older": {"ko":"이전 실행 {count}개 더 보기","en":"Show {count} older executions","zh-CN":"再显示 {count} 条较早运行"},
|
|
543
561
|
"graph.execution_running": {"ko":"실행 중","en":"Running","zh-CN":"运行中"},
|
|
562
|
+
"graph.execution_unverified": {"ko":"상태 미확인","en":"Status unverified","zh-CN":"状态未确认"},
|
|
544
563
|
"graph.execution_runtime": {"ko":"실행 환경","en":"Runtime","zh-CN":"运行环境"},
|
|
545
564
|
"graph.execution_started": {"ko":"시작","en":"Started","zh-CN":"开始"},
|
|
546
565
|
"graph.execution_status_label": {"ko":"상태","en":"Status","zh-CN":"状态"},
|
|
@@ -563,6 +582,9 @@
|
|
|
563
582
|
"graph.main_ai_in_charge": {"ko":"전체 일을 맡은 메인 AI","en":"Main AI in charge of the whole task","zh-CN":"负责整体工作的主 AI"},
|
|
564
583
|
"graph.main_ai_prestart_explanation": {"ko":"메인 AI가 작업 시작 직전에 설명한 내용","en":"Description provided by the main AI just before the task started","zh-CN":"主 AI 在任务开始前说明的内容"},
|
|
565
584
|
"graph.main_ai_prestart_short": {"ko":"작업 시작 직전 메인 AI 설명","en":"Main AI description before start","zh-CN":"任务开始前的主 AI 说明"},
|
|
585
|
+
"graph.assignment_protected_suffix": {"ko":" · 실제 지시 원문 보호됨","en":" · Exact prompt protected","zh-CN":" · 实际指令原文受保护"},
|
|
586
|
+
"graph.assignment_source_protected": {"ko":"실제 지시 원문은 암호화되어 확인할 수 없음","en":"The exact prompt is encrypted and unavailable","zh-CN":"实际指令原文已加密,无法查看"},
|
|
587
|
+
"graph.assignment_source_protected_short": {"ko":"실제 지시 원문 보호됨","en":"Exact prompt protected","zh-CN":"实际指令原文受保护"},
|
|
566
588
|
"graph.major_tasks_and_agents": {"ko":"큰 일 {tasks}개 · 작업 중 도움 AI {active}명 · 도움 AI 기록 {records}명","en":"{tasks} major tasks · {active} active helper AI · {records} helper records","zh-CN":"{tasks} 个主要任务 · {active} 个工作中的协助 AI · {records} 条协助 AI 记录"},
|
|
567
589
|
"graph.memory_usage": {"ko":"기억 공간 사용","en":"Memory usage","zh-CN":"记忆空间使用"},
|
|
568
590
|
"graph.model_unknown": {"ko":"모델 정보 없음","en":"Model information unavailable","zh-CN":"无模型信息"},
|
|
@@ -666,11 +688,8 @@
|
|
|
666
688
|
"time.minutes_ago": {"ko":"{count}분 전","en":"{count} min ago","zh-CN":"{count} 分钟前"},
|
|
667
689
|
"time.seconds_ago": {"ko":"{count}초 전","en":"{count} sec ago","zh-CN":"{count} 秒前"},
|
|
668
690
|
"terminal.activity_details": {"ko":"활동 {count}건은 상세에서 확인","en":"See {count} activities in details","zh-CN":"在详情中查看 {count} 条活动"},
|
|
669
|
-
"terminal.move_down": {"ko":"{title} 아래로 이동","en":"Move {title} down","zh-CN":"下移 {title}"},
|
|
670
|
-
"terminal.move_up": {"ko":"{title} 위로 이동","en":"Move {title} up","zh-CN":"上移 {title}"},
|
|
671
|
-
"terminal.reorder_group": {"ko":"{title} 순서 변경","en":"Reorder {title}","zh-CN":"调整 {title} 的顺序"},
|
|
672
691
|
"terminal.reorder_hint": {"ko":"끌어서 순서 변경 · Alt+↑/↓ 키도 사용 가능","en":"Drag to reorder · Alt+↑/↓ also works","zh-CN":"拖动排序 · 也可使用 Alt+↑/↓"},
|
|
673
|
-
"terminal.reorder_help": {"ko":"
|
|
692
|
+
"terminal.reorder_help": {"ko":"세션을 끌어 놓아 순서를 바꿀 수 있습니다. 키보드에서는 세션에 초점을 둔 뒤 Alt와 위아래 화살표 키를 사용하세요.","en":"Drag and drop a session to reorder it. With a keyboard, focus the session and use Alt with the up or down arrow key.","zh-CN":"拖放会话可调整顺序。使用键盘时,请聚焦会话并按 Alt 加上或下方向键。"},
|
|
674
693
|
"terminal.reordered": {"ko":"세션 순서를 변경했습니다.","en":"Session order updated.","zh-CN":"会话顺序已更新。"},
|
|
675
694
|
"terminal.agent.ai_terminal": {"ko":"AI 명령창","en":"AI terminal","zh-CN":"AI 终端"},
|
|
676
695
|
"terminal.agent.command_required": {"ko":"AI에게 보낼 지시를 입력하세요.","en":"Enter instructions to send to the AI.","zh-CN":"请输入要发送给 AI 的指令。"},
|
|
@@ -1324,8 +1343,10 @@
|
|
|
1324
1343
|
"control.attention_eyebrow": {"ko":"확인 필요","en":"Action needed","zh-CN":"需要处理"},
|
|
1325
1344
|
"control.attention_more": {"ko":"나머지 {count}개 모두 보기","en":"View {count} more","zh-CN":"查看其余 {count} 项"},
|
|
1326
1345
|
"control.attention_title": {"ko":"지금 개입할 작업 {count}","en":"{count} tasks need action","zh-CN":"{count} 个任务需要处理"},
|
|
1327
|
-
"control.
|
|
1328
|
-
"control.
|
|
1346
|
+
"control.add_project": {"ko":"프로젝트 추가","en":"Add project","zh-CN":"添加项目"},
|
|
1347
|
+
"control.all_projects": {"ko":"모든 프로젝트","en":"All projects","zh-CN":"所有项目"},
|
|
1348
|
+
"control.completed_empty": {"ko":"아직 최근 실행 기록이 없습니다","en":"No recent execution record yet","zh-CN":"尚无最近的执行记录"},
|
|
1349
|
+
"control.completed_work_column": {"ko":"최근 끝난 작업","en":"Recent work","zh-CN":"最近的工作"},
|
|
1329
1350
|
"control.background_execution": {"ko":"{owner} 백그라운드 작업","en":"{owner} background work","zh-CN":"{owner}后台工作"},
|
|
1330
1351
|
"control.background_work_kind": {"ko":"백그라운드 작업","en":"Background work","zh-CN":"后台任务"},
|
|
1331
1352
|
"control.command_execution": {"ko":"{owner} 실행 작업","en":"{owner} command work","zh-CN":"{owner}命令工作"},
|
|
@@ -1338,6 +1359,11 @@
|
|
|
1338
1359
|
"control.legend_spawn": {"ko":"생성 중","en":"Spawning","zh-CN":"创建中"},
|
|
1339
1360
|
"control.live_activity": {"ko":"실시간 활동","en":"Live activity","zh-CN":"实时活动"},
|
|
1340
1361
|
"control.live_session": {"ko":"실행 중인 세션","en":"Live session","zh-CN":"运行中的会话"},
|
|
1362
|
+
"control.waiting_session": {"ko":"대기 중인 세션","en":"Waiting session","zh-CN":"等待中的会话"},
|
|
1363
|
+
"control.waiting_background_session": {"ko":"응답 대기 · 백그라운드 실행 중","en":"Waiting for response · background work running","zh-CN":"等待回复 · 后台任务运行中"},
|
|
1364
|
+
"control.auto_history_in_minutes": {"ko":"{minutes}분 뒤 지난 기록으로 이동","en":"Moves to history in {minutes} min","zh-CN":"{minutes} 分钟后移至历史记录"},
|
|
1365
|
+
"control.move_to_history": {"ko":"지난 기록으로 이동","en":"Move to history","zh-CN":"移至历史记录"},
|
|
1366
|
+
"control.moved_to_history": {"ko":"세션을 지난 기록으로 옮겼습니다.","en":"Session moved to history.","zh-CN":"会话已移至历史记录。"},
|
|
1341
1367
|
"control.live_summary": {"ko":"메인 {sessions} · 서브 {helpers} · 실행 {executions}","en":"{sessions} lead · {helpers} helper · {executions} commands","zh-CN":"主 {sessions} · 协助 {helpers} · 执行 {executions}"},
|
|
1342
1368
|
"control.main_assignment": {"ko":"메인 에이전트가 시킨 일","en":"Assigned by the lead agent","zh-CN":"主智能体分配的任务"},
|
|
1343
1369
|
"control.main_agent": {"ko":"메인 에이전트","en":"Lead agent","zh-CN":"主智能体"},
|
|
@@ -1351,7 +1377,18 @@
|
|
|
1351
1377
|
"control.open_full_flow": {"ko":"전체 구조 보기","en":"Open full structure","zh-CN":"查看完整结构"},
|
|
1352
1378
|
"control.open_remaining_units": {"ko":"나머지 {count}개 보기","en":"View {count} more","zh-CN":"查看其余 {count} 个"},
|
|
1353
1379
|
"control.open_subagent": {"ko":"서브에이전트 작업 ‘{task}’ 대화 열기","en":"Open conversation for helper task ‘{task}’","zh-CN":"打开协助任务“{task}”的对话"},
|
|
1380
|
+
"control.open_search": {"ko":"실행 세션 검색 열기","en":"Open live-session search","zh-CN":"打开运行会话搜索"},
|
|
1381
|
+
"control.other_projects": {"ko":"기타","en":"Other","zh-CN":"其他"},
|
|
1354
1382
|
"control.other_sessions": {"ko":"다른 세션","en":"Other sessions","zh-CN":"其他会话"},
|
|
1383
|
+
"control.page_summary": {"ko":"{start}–{end} / {total}","en":"{start}–{end} / {total}","zh-CN":"{start}–{end} / {total}"},
|
|
1384
|
+
"control.previous_page": {"ko":"이전 실행 세션","en":"Previous live sessions","zh-CN":"上一页运行会话"},
|
|
1385
|
+
"control.next_page": {"ko":"다음 실행 세션","en":"Next live sessions","zh-CN":"下一页运行会话"},
|
|
1386
|
+
"control.project_filter": {"ko":"프로젝트별 실행 세션 필터","en":"Filter live sessions by project","zh-CN":"按项目筛选运行会话"},
|
|
1387
|
+
"control.project_live_attention_summary": {"ko":"실행 중 {active} · 확인 필요 {attention}","en":"{active} running · {attention} need attention","zh-CN":"运行中 {active} · 需确认 {attention}"},
|
|
1388
|
+
"control.project_live_summary": {"ko":"실행 중 {active}","en":"{active} running","zh-CN":"运行中 {active}"},
|
|
1389
|
+
"control.recent_activity": {"ko":"최근 활동순","en":"Recent activity","zh-CN":"最近活动"},
|
|
1390
|
+
"control.search_sessions": {"ko":"실행 세션 검색","en":"Search live sessions","zh-CN":"搜索运行会话"},
|
|
1391
|
+
"control.sort_sessions": {"ko":"실행 세션 정렬","en":"Sort live sessions","zh-CN":"排序运行会话"},
|
|
1355
1392
|
"control.recently_completed": {"ko":"방금 완료","en":"Just completed","zh-CN":"刚刚完成"},
|
|
1356
1393
|
"control.subagent": {"ko":"서브에이전트","en":"Helper agent","zh-CN":"协助智能体"},
|
|
1357
1394
|
"control.subagent_short": {"ko":"서브","en":"Helper","zh-CN":"协助"},
|