@xcanwin/manyoyo 5.9.1 → 5.9.3
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/lib/web/frontend/app.css +5 -46
- package/lib/web/frontend/app.html +4 -3
- package/lib/web/frontend/app.js +159 -61
- package/lib/web/server.js +51 -5
- package/package.json +1 -1
package/lib/web/frontend/app.css
CHANGED
|
@@ -1667,22 +1667,6 @@ body.command-mode .msg.origin-agent .bubble {
|
|
|
1667
1667
|
gap: 10px;
|
|
1668
1668
|
}
|
|
1669
1669
|
|
|
1670
|
-
.trace-summary {
|
|
1671
|
-
display: flex;
|
|
1672
|
-
flex-direction: column;
|
|
1673
|
-
gap: 6px;
|
|
1674
|
-
}
|
|
1675
|
-
|
|
1676
|
-
.trace-summary-line {
|
|
1677
|
-
padding: 7px 10px;
|
|
1678
|
-
border-radius: 8px;
|
|
1679
|
-
border: 1px dashed rgba(181, 146, 99, 0.45);
|
|
1680
|
-
background: rgba(255, 250, 242, 0.82);
|
|
1681
|
-
color: var(--muted);
|
|
1682
|
-
font-size: 12px;
|
|
1683
|
-
line-height: 1.45;
|
|
1684
|
-
}
|
|
1685
|
-
|
|
1686
1670
|
.trace-flow {
|
|
1687
1671
|
display: flex;
|
|
1688
1672
|
flex-direction: column;
|
|
@@ -1722,6 +1706,11 @@ body.command-mode .msg.origin-agent .bubble {
|
|
|
1722
1706
|
border-left-color: var(--line-strong);
|
|
1723
1707
|
}
|
|
1724
1708
|
|
|
1709
|
+
.trace-card.trace-card-residual {
|
|
1710
|
+
background: rgba(255, 250, 242, 0.86);
|
|
1711
|
+
box-shadow: none;
|
|
1712
|
+
}
|
|
1713
|
+
|
|
1725
1714
|
.trace-card-summary {
|
|
1726
1715
|
list-style: none;
|
|
1727
1716
|
display: flex;
|
|
@@ -1735,36 +1724,6 @@ details.trace-card > .trace-card-summary {
|
|
|
1735
1724
|
cursor: pointer;
|
|
1736
1725
|
}
|
|
1737
1726
|
|
|
1738
|
-
.trace-card.trace-card-compact {
|
|
1739
|
-
border-left-width: 2px;
|
|
1740
|
-
border-radius: 999px;
|
|
1741
|
-
background: rgba(255, 251, 245, 0.78);
|
|
1742
|
-
box-shadow: none;
|
|
1743
|
-
}
|
|
1744
|
-
|
|
1745
|
-
.trace-card.trace-card-compact .trace-card-summary {
|
|
1746
|
-
padding: 5px 10px;
|
|
1747
|
-
gap: 7px;
|
|
1748
|
-
}
|
|
1749
|
-
|
|
1750
|
-
.trace-card.trace-card-compact .trace-card-badge {
|
|
1751
|
-
min-width: 0;
|
|
1752
|
-
padding: 2px 6px;
|
|
1753
|
-
background: rgba(31, 26, 20, 0.06);
|
|
1754
|
-
font-size: 10px;
|
|
1755
|
-
}
|
|
1756
|
-
|
|
1757
|
-
.trace-card.trace-card-compact .trace-card-title {
|
|
1758
|
-
font-size: 11px;
|
|
1759
|
-
font-weight: 500;
|
|
1760
|
-
color: #6e6256;
|
|
1761
|
-
}
|
|
1762
|
-
|
|
1763
|
-
.trace-card.trace-card-compact .trace-card-phase {
|
|
1764
|
-
font-size: 10px;
|
|
1765
|
-
color: #8a7d70;
|
|
1766
|
-
}
|
|
1767
|
-
|
|
1768
1727
|
.trace-card-summary::-webkit-details-marker {
|
|
1769
1728
|
display: none;
|
|
1770
1729
|
}
|
|
@@ -58,9 +58,10 @@
|
|
|
58
58
|
>更多</button>
|
|
59
59
|
<div class="header-actions" id="headerActions">
|
|
60
60
|
<button type="button" id="refreshBtn" class="secondary">刷新</button>
|
|
61
|
-
<button type="button" id="
|
|
62
|
-
<button type="button" id="removeBtn" class="danger
|
|
63
|
-
<button type="button" id="
|
|
61
|
+
<button type="button" id="openCreateMenuBtn" class="secondary">新建容器</button>
|
|
62
|
+
<button type="button" id="removeBtn" class="danger">删除容器</button>
|
|
63
|
+
<button type="button" id="addAgentBtn" class="secondary">新建 AGENT</button>
|
|
64
|
+
<button type="button" id="removeAllBtn" class="danger">删除 AGENT</button>
|
|
64
65
|
</div>
|
|
65
66
|
</div>
|
|
66
67
|
</div>
|
package/lib/web/frontend/app.js
CHANGED
|
@@ -57,6 +57,7 @@
|
|
|
57
57
|
configSaveMessage: '',
|
|
58
58
|
createLoading: false,
|
|
59
59
|
createSubmitting: false,
|
|
60
|
+
creatingAgent: false,
|
|
60
61
|
agentTemplateSaving: false,
|
|
61
62
|
configSnapshot: null,
|
|
62
63
|
sessionDetail: null,
|
|
@@ -131,6 +132,7 @@
|
|
|
131
132
|
const sidebarBackdrop = document.getElementById('sidebarBackdrop');
|
|
132
133
|
const openConfigBtn = document.getElementById('openConfigBtn');
|
|
133
134
|
const openCreateBtn = document.getElementById('openCreateBtn');
|
|
135
|
+
const openCreateMenuBtn = document.getElementById('openCreateMenuBtn');
|
|
134
136
|
const configModal = document.getElementById('configModal');
|
|
135
137
|
const configModalTitle = document.getElementById('configModalTitle');
|
|
136
138
|
const configPath = document.getElementById('configPath');
|
|
@@ -385,34 +387,6 @@
|
|
|
385
387
|
return 'neutral';
|
|
386
388
|
}
|
|
387
389
|
|
|
388
|
-
function shouldCompactTraceEvent(traceEvent) {
|
|
389
|
-
const event = traceEvent && typeof traceEvent === 'object' ? traceEvent : {};
|
|
390
|
-
const phase = event.phase ? String(event.phase) : '';
|
|
391
|
-
const kind = event.kind ? String(event.kind) : '';
|
|
392
|
-
if (phase !== 'completed') {
|
|
393
|
-
return false;
|
|
394
|
-
}
|
|
395
|
-
return kind === 'command' || kind === 'mcp' || kind === 'tool';
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
function buildCompactTraceText(traceEvent) {
|
|
399
|
-
const event = traceEvent && typeof traceEvent === 'object' ? traceEvent : {};
|
|
400
|
-
if (event.kind === 'command') {
|
|
401
|
-
const suffix = typeof event.exitCode === 'number'
|
|
402
|
-
? `exit ${event.exitCode}`
|
|
403
|
-
: (event.status ? String(event.status) : 'completed');
|
|
404
|
-
return `${event.command || event.text || '命令'} · ${suffix}`;
|
|
405
|
-
}
|
|
406
|
-
if (event.kind === 'mcp') {
|
|
407
|
-
const toolLabel = [event.server, event.tool].filter(Boolean).join('.') || event.text || 'MCP';
|
|
408
|
-
return event.argumentSummary ? `${toolLabel} · ${event.argumentSummary}` : toolLabel;
|
|
409
|
-
}
|
|
410
|
-
if (event.kind === 'tool') {
|
|
411
|
-
return event.toolName || event.text || '工具';
|
|
412
|
-
}
|
|
413
|
-
return event.text || '事件';
|
|
414
|
-
}
|
|
415
|
-
|
|
416
390
|
function appendTraceCardBody(cardBody, label, value) {
|
|
417
391
|
const text = stringifyPrettyJson(value).trim();
|
|
418
392
|
if (!text) {
|
|
@@ -436,12 +410,19 @@
|
|
|
436
410
|
|
|
437
411
|
function createTraceEventCard(traceEvent) {
|
|
438
412
|
const event = traceEvent && typeof traceEvent === 'object' ? traceEvent : {};
|
|
439
|
-
const compact = shouldCompactTraceEvent(event);
|
|
440
413
|
const bodyParts = [];
|
|
441
|
-
if (
|
|
414
|
+
if (event.kind === 'command' && event.command) {
|
|
442
415
|
bodyParts.push({ label: '命令', value: event.command });
|
|
416
|
+
if (typeof event.exitCode === 'number') {
|
|
417
|
+
bodyParts.push({ label: '退出码', value: String(event.exitCode) });
|
|
418
|
+
} else if (event.status) {
|
|
419
|
+
bodyParts.push({ label: '状态', value: event.status });
|
|
420
|
+
}
|
|
443
421
|
}
|
|
444
|
-
if (
|
|
422
|
+
if (event.kind === 'mcp') {
|
|
423
|
+
if (event.server || event.tool) {
|
|
424
|
+
bodyParts.push({ label: '工具', value: [event.server, event.tool].filter(Boolean).join('.') });
|
|
425
|
+
}
|
|
445
426
|
if (event.argumentSummary) {
|
|
446
427
|
bodyParts.push({ label: '参数摘要', value: event.argumentSummary });
|
|
447
428
|
}
|
|
@@ -455,7 +436,7 @@
|
|
|
455
436
|
bodyParts.push({ label: '错误', value: event.error });
|
|
456
437
|
}
|
|
457
438
|
}
|
|
458
|
-
if (
|
|
439
|
+
if (event.kind === 'tool' && event.toolName) {
|
|
459
440
|
bodyParts.push({ label: '工具', value: event.toolName });
|
|
460
441
|
}
|
|
461
442
|
if ((event.kind === 'agent_message' || event.kind === 'status' || event.kind === 'error') && event.detail) {
|
|
@@ -464,7 +445,7 @@
|
|
|
464
445
|
|
|
465
446
|
const hasBody = bodyParts.length > 0;
|
|
466
447
|
const card = document.createElement(hasBody ? 'details' : 'div');
|
|
467
|
-
card.className = 'trace-card trace-tone-' + resolveTraceTone(event)
|
|
448
|
+
card.className = 'trace-card trace-tone-' + resolveTraceTone(event);
|
|
468
449
|
if (hasBody && event.kind === 'error') {
|
|
469
450
|
card.open = true;
|
|
470
451
|
}
|
|
@@ -479,7 +460,7 @@
|
|
|
479
460
|
|
|
480
461
|
const title = document.createElement('span');
|
|
481
462
|
title.className = 'trace-card-title';
|
|
482
|
-
title.textContent =
|
|
463
|
+
title.textContent = event && event.text ? String(event.text) : '事件';
|
|
483
464
|
header.appendChild(title);
|
|
484
465
|
|
|
485
466
|
const phaseText = humanizeTracePhase(event);
|
|
@@ -498,9 +479,44 @@
|
|
|
498
479
|
bodyParts.forEach(function (part) {
|
|
499
480
|
appendTraceCardBody(body, part.label, part.value);
|
|
500
481
|
});
|
|
501
|
-
|
|
482
|
+
card.appendChild(body);
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
return card;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
function resolveResidualTraceTone(line) {
|
|
489
|
+
const text = String(line || '').trim();
|
|
490
|
+
if (!text) {
|
|
491
|
+
return 'neutral';
|
|
502
492
|
}
|
|
493
|
+
if (text.startsWith('[stderr]') || text.startsWith('[错误]')) {
|
|
494
|
+
return 'error';
|
|
495
|
+
}
|
|
496
|
+
if (text.startsWith('[任务]') || text.includes('上下文模式') || text.includes('会话恢复') || text.includes('等待 Agent 启动')) {
|
|
497
|
+
return 'status';
|
|
498
|
+
}
|
|
499
|
+
return 'neutral';
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
function createResidualTraceCard(line) {
|
|
503
|
+
const card = document.createElement('div');
|
|
504
|
+
card.className = 'trace-card trace-tone-' + resolveResidualTraceTone(line) + ' trace-card-residual';
|
|
503
505
|
|
|
506
|
+
const header = document.createElement('div');
|
|
507
|
+
header.className = 'trace-card-summary';
|
|
508
|
+
|
|
509
|
+
const badge = document.createElement('span');
|
|
510
|
+
badge.className = 'trace-card-badge';
|
|
511
|
+
badge.textContent = '状态';
|
|
512
|
+
header.appendChild(badge);
|
|
513
|
+
|
|
514
|
+
const title = document.createElement('span');
|
|
515
|
+
title.className = 'trace-card-title';
|
|
516
|
+
title.textContent = String(line || '');
|
|
517
|
+
header.appendChild(title);
|
|
518
|
+
|
|
519
|
+
card.appendChild(header);
|
|
504
520
|
return card;
|
|
505
521
|
}
|
|
506
522
|
|
|
@@ -509,21 +525,11 @@
|
|
|
509
525
|
const container = document.createElement('div');
|
|
510
526
|
container.className = 'trace-structured';
|
|
511
527
|
|
|
512
|
-
const residualLines = buildStructuredTraceResidualLines(message);
|
|
513
|
-
if (residualLines.length) {
|
|
514
|
-
const summary = document.createElement('div');
|
|
515
|
-
summary.className = 'trace-summary';
|
|
516
|
-
residualLines.forEach(function (line) {
|
|
517
|
-
const item = document.createElement('div');
|
|
518
|
-
item.className = 'trace-summary-line';
|
|
519
|
-
item.textContent = line;
|
|
520
|
-
summary.appendChild(item);
|
|
521
|
-
});
|
|
522
|
-
container.appendChild(summary);
|
|
523
|
-
}
|
|
524
|
-
|
|
525
528
|
const flow = document.createElement('div');
|
|
526
529
|
flow.className = 'trace-flow';
|
|
530
|
+
buildStructuredTraceResidualLines(message).forEach(function (line) {
|
|
531
|
+
flow.appendChild(createResidualTraceCard(line));
|
|
532
|
+
});
|
|
527
533
|
(Array.isArray(message && message.traceEvents) ? message.traceEvents : []).forEach(function (traceEvent) {
|
|
528
534
|
flow.appendChild(createTraceEventCard(traceEvent));
|
|
529
535
|
});
|
|
@@ -2189,10 +2195,11 @@
|
|
|
2189
2195
|
}
|
|
2190
2196
|
|
|
2191
2197
|
const activeAgentRunning = isAgentRunActiveForSession(state.active) || hasPendingAgentMessagesForSession(state.active);
|
|
2192
|
-
const busy = state.loadingSessions || state.loadingMessages || state.sending;
|
|
2198
|
+
const busy = state.loadingSessions || state.loadingMessages || state.sending || state.creatingAgent;
|
|
2193
2199
|
refreshBtn.disabled = busy;
|
|
2194
2200
|
if (addAgentBtn) {
|
|
2195
2201
|
addAgentBtn.disabled = !state.active || busy;
|
|
2202
|
+
addAgentBtn.textContent = state.creatingAgent ? '新建中...' : '新建 AGENT';
|
|
2196
2203
|
}
|
|
2197
2204
|
removeBtn.disabled = !state.active || busy;
|
|
2198
2205
|
removeAllBtn.disabled = !state.active || busy;
|
|
@@ -2217,6 +2224,9 @@
|
|
|
2217
2224
|
if (openCreateBtn) {
|
|
2218
2225
|
openCreateBtn.disabled = state.createLoading || state.createSubmitting;
|
|
2219
2226
|
}
|
|
2227
|
+
if (openCreateMenuBtn) {
|
|
2228
|
+
openCreateMenuBtn.disabled = state.createLoading || state.createSubmitting;
|
|
2229
|
+
}
|
|
2220
2230
|
if (openConfigBtn) {
|
|
2221
2231
|
openConfigBtn.disabled = state.configLoading || state.configSaving;
|
|
2222
2232
|
}
|
|
@@ -2247,6 +2257,9 @@
|
|
|
2247
2257
|
if (!state.active) {
|
|
2248
2258
|
sendState.textContent = '未选择会话';
|
|
2249
2259
|
sendState.classList.remove('is-active');
|
|
2260
|
+
} else if (state.creatingAgent) {
|
|
2261
|
+
sendState.textContent = '正在新建 AGENT…';
|
|
2262
|
+
sendState.classList.add('is-active');
|
|
2250
2263
|
} else if (activeAgentRunning && agentMode) {
|
|
2251
2264
|
sendState.textContent = state.agentRun.stopping ? '正在停止 Agent…' : 'Agent 执行中';
|
|
2252
2265
|
sendState.classList.add('is-active');
|
|
@@ -2300,6 +2313,8 @@
|
|
|
2300
2313
|
);
|
|
2301
2314
|
if (!state.active) {
|
|
2302
2315
|
sendState.textContent = '未选择会话';
|
|
2316
|
+
} else if (state.creatingAgent) {
|
|
2317
|
+
sendState.textContent = '正在新建 AGENT…';
|
|
2303
2318
|
} else if (agentMode && !agentEnabled) {
|
|
2304
2319
|
sendState.textContent = '当前会话未配置 AGENT 模板';
|
|
2305
2320
|
} else if (state.sending) {
|
|
@@ -2309,7 +2324,7 @@
|
|
|
2309
2324
|
} else {
|
|
2310
2325
|
sendState.textContent = '就绪';
|
|
2311
2326
|
}
|
|
2312
|
-
sendState.classList.toggle('is-active', state.sending);
|
|
2327
|
+
sendState.classList.toggle('is-active', state.sending || state.creatingAgent);
|
|
2313
2328
|
if (composer) {
|
|
2314
2329
|
composer.hidden = !activityTab;
|
|
2315
2330
|
}
|
|
@@ -2695,6 +2710,11 @@
|
|
|
2695
2710
|
if (!targetContainer) {
|
|
2696
2711
|
return;
|
|
2697
2712
|
}
|
|
2713
|
+
if (state.creatingAgent) {
|
|
2714
|
+
return;
|
|
2715
|
+
}
|
|
2716
|
+
state.creatingAgent = true;
|
|
2717
|
+
syncUi();
|
|
2698
2718
|
try {
|
|
2699
2719
|
const data = await api('/api/sessions/' + encodeURIComponent(targetContainer) + '/agents', {
|
|
2700
2720
|
method: 'POST',
|
|
@@ -2708,6 +2728,9 @@
|
|
|
2708
2728
|
}
|
|
2709
2729
|
} catch (e) {
|
|
2710
2730
|
alert(e.message);
|
|
2731
|
+
} finally {
|
|
2732
|
+
state.creatingAgent = false;
|
|
2733
|
+
syncUi();
|
|
2711
2734
|
}
|
|
2712
2735
|
}
|
|
2713
2736
|
|
|
@@ -2751,9 +2774,7 @@
|
|
|
2751
2774
|
});
|
|
2752
2775
|
group.containers.forEach(function (containerGroup) {
|
|
2753
2776
|
containerGroup.sessions.sort(function (a, b) {
|
|
2754
|
-
|
|
2755
|
-
const timeB = b && b.updatedAt ? new Date(b.updatedAt).getTime() : 0;
|
|
2756
|
-
return timeB - timeA;
|
|
2777
|
+
return compareSessionByCreatedDesc(a, b);
|
|
2757
2778
|
});
|
|
2758
2779
|
});
|
|
2759
2780
|
return group;
|
|
@@ -2768,6 +2789,72 @@
|
|
|
2768
2789
|
return (parts || []).filter(Boolean).join(' · ');
|
|
2769
2790
|
}
|
|
2770
2791
|
|
|
2792
|
+
function getSessionCreatedTime(session) {
|
|
2793
|
+
if (session && session.createdAt) {
|
|
2794
|
+
const time = new Date(session.createdAt).getTime();
|
|
2795
|
+
if (Number.isFinite(time)) {
|
|
2796
|
+
return time;
|
|
2797
|
+
}
|
|
2798
|
+
}
|
|
2799
|
+
return 0;
|
|
2800
|
+
}
|
|
2801
|
+
|
|
2802
|
+
function getSessionUpdatedTime(session) {
|
|
2803
|
+
if (session && session.updatedAt) {
|
|
2804
|
+
const time = new Date(session.updatedAt).getTime();
|
|
2805
|
+
if (Number.isFinite(time)) {
|
|
2806
|
+
return time;
|
|
2807
|
+
}
|
|
2808
|
+
}
|
|
2809
|
+
return 0;
|
|
2810
|
+
}
|
|
2811
|
+
|
|
2812
|
+
function getSessionAgentCreationRank(session) {
|
|
2813
|
+
const agentId = session && session.agentId ? String(session.agentId) : '';
|
|
2814
|
+
if (!agentId || agentId === 'default') {
|
|
2815
|
+
return 1;
|
|
2816
|
+
}
|
|
2817
|
+
const matched = agentId.match(/^agent-(\d+)$/);
|
|
2818
|
+
return matched ? (Number(matched[1]) || 0) : 0;
|
|
2819
|
+
}
|
|
2820
|
+
|
|
2821
|
+
function compareSessionByCreatedDesc(a, b) {
|
|
2822
|
+
const createdA = getSessionCreatedTime(a);
|
|
2823
|
+
const createdB = getSessionCreatedTime(b);
|
|
2824
|
+
if (createdA !== createdB) {
|
|
2825
|
+
return createdB - createdA;
|
|
2826
|
+
}
|
|
2827
|
+
if (a && b && a.containerName && a.containerName === b.containerName) {
|
|
2828
|
+
const rankA = getSessionAgentCreationRank(a);
|
|
2829
|
+
const rankB = getSessionAgentCreationRank(b);
|
|
2830
|
+
if (rankA !== rankB) {
|
|
2831
|
+
return rankB - rankA;
|
|
2832
|
+
}
|
|
2833
|
+
}
|
|
2834
|
+
const updatedA = getSessionUpdatedTime(a);
|
|
2835
|
+
const updatedB = getSessionUpdatedTime(b);
|
|
2836
|
+
if (updatedA !== updatedB) {
|
|
2837
|
+
return updatedB - updatedA;
|
|
2838
|
+
}
|
|
2839
|
+
return String((a && a.name) || '').localeCompare(String((b && b.name) || ''), 'zh-CN');
|
|
2840
|
+
}
|
|
2841
|
+
|
|
2842
|
+
function findLatestCreatedSessionName(sessions, preferredContainerName) {
|
|
2843
|
+
const list = Array.isArray(sessions) ? sessions.filter(Boolean) : [];
|
|
2844
|
+
if (!list.length) {
|
|
2845
|
+
return '';
|
|
2846
|
+
}
|
|
2847
|
+
const targetContainer = String(preferredContainerName || '').trim();
|
|
2848
|
+
const scoped = targetContainer
|
|
2849
|
+
? list.filter(function (session) {
|
|
2850
|
+
return session && session.containerName === targetContainer;
|
|
2851
|
+
})
|
|
2852
|
+
: list;
|
|
2853
|
+
const candidates = scoped.length ? scoped : list;
|
|
2854
|
+
const sorted = candidates.slice().sort(compareSessionByCreatedDesc);
|
|
2855
|
+
return sorted.length && sorted[0] && sorted[0].name ? sorted[0].name : '';
|
|
2856
|
+
}
|
|
2857
|
+
|
|
2771
2858
|
function createDisclosureButton(expanded, label) {
|
|
2772
2859
|
const button = document.createElement('button');
|
|
2773
2860
|
button.type = 'button';
|
|
@@ -2912,7 +2999,7 @@
|
|
|
2912
2999
|
const addAgentBtn = document.createElement('button');
|
|
2913
3000
|
addAgentBtn.type = 'button';
|
|
2914
3001
|
addAgentBtn.className = 'secondary tree-node-menu-item';
|
|
2915
|
-
addAgentBtn.textContent = '新建AGENT';
|
|
3002
|
+
addAgentBtn.textContent = '新建 AGENT';
|
|
2916
3003
|
addAgentBtn.addEventListener('click', function (event) {
|
|
2917
3004
|
event.stopPropagation();
|
|
2918
3005
|
createAgentSession(containerName);
|
|
@@ -3298,7 +3385,7 @@
|
|
|
3298
3385
|
}
|
|
3299
3386
|
}
|
|
3300
3387
|
|
|
3301
|
-
function applySessionsSnapshot(rawSessions, preferredName) {
|
|
3388
|
+
function applySessionsSnapshot(rawSessions, preferredName, preferredContainerName) {
|
|
3302
3389
|
const previousActive = state.active;
|
|
3303
3390
|
state.sessions = Array.isArray(rawSessions) ? rawSessions : [];
|
|
3304
3391
|
pruneSidebarTreeState();
|
|
@@ -3313,7 +3400,7 @@
|
|
|
3313
3400
|
state.sessionDetailError = '';
|
|
3314
3401
|
}
|
|
3315
3402
|
if (!state.active && state.sessions.length) {
|
|
3316
|
-
state.active = state.sessions[0].name;
|
|
3403
|
+
state.active = findLatestCreatedSessionName(state.sessions, preferredContainerName) || state.sessions[0].name;
|
|
3317
3404
|
}
|
|
3318
3405
|
if (state.active && state.active !== previousActive) {
|
|
3319
3406
|
ensureSessionPathExpanded(state.active);
|
|
@@ -3337,7 +3424,7 @@
|
|
|
3337
3424
|
let requestError = null;
|
|
3338
3425
|
try {
|
|
3339
3426
|
const data = await api('/api/sessions');
|
|
3340
|
-
applySessionsSnapshot(data.sessions, opts.preferredName);
|
|
3427
|
+
applySessionsSnapshot(data.sessions, opts.preferredName, opts.preferredContainerName);
|
|
3341
3428
|
} catch (e) {
|
|
3342
3429
|
requestError = e;
|
|
3343
3430
|
} finally {
|
|
@@ -3763,6 +3850,13 @@
|
|
|
3763
3850
|
});
|
|
3764
3851
|
}
|
|
3765
3852
|
|
|
3853
|
+
if (openCreateMenuBtn) {
|
|
3854
|
+
openCreateMenuBtn.addEventListener('click', function () {
|
|
3855
|
+
closeMobileActionsMenu();
|
|
3856
|
+
openCreateModal();
|
|
3857
|
+
});
|
|
3858
|
+
}
|
|
3859
|
+
|
|
3766
3860
|
if (agentTemplateBtn) {
|
|
3767
3861
|
agentTemplateBtn.addEventListener('click', function () {
|
|
3768
3862
|
openAgentTemplateModal().catch(function (e) {
|
|
@@ -4343,14 +4437,18 @@
|
|
|
4343
4437
|
closeMobileActionsMenu();
|
|
4344
4438
|
const activeSession = getActiveSession();
|
|
4345
4439
|
const targetAgent = activeSession && activeSession.agentName ? activeSession.agentName : state.active;
|
|
4346
|
-
const yes = confirm('
|
|
4440
|
+
const yes = confirm('确认删除 AGENT ' + targetAgent + ' ?');
|
|
4347
4441
|
if (!yes) return;
|
|
4348
4442
|
try {
|
|
4349
|
-
const
|
|
4350
|
-
await api('/api/sessions/' + encodeURIComponent(
|
|
4443
|
+
const targetContainerName = activeSession && activeSession.containerName ? activeSession.containerName : '';
|
|
4444
|
+
await api('/api/sessions/' + encodeURIComponent(state.active) + '/remove-with-history', {
|
|
4351
4445
|
method: 'POST'
|
|
4352
4446
|
});
|
|
4353
|
-
await
|
|
4447
|
+
await refreshSessions({
|
|
4448
|
+
preferredContainerName: targetContainerName,
|
|
4449
|
+
withLoading: true,
|
|
4450
|
+
reloadMessages: true
|
|
4451
|
+
});
|
|
4354
4452
|
} catch (e) {
|
|
4355
4453
|
alert(e.message);
|
|
4356
4454
|
}
|
package/lib/web/server.js
CHANGED
|
@@ -165,6 +165,7 @@ function createEmptyWebAgentSession(agentId, agentName) {
|
|
|
165
165
|
agentId,
|
|
166
166
|
agentName: normalizeWebAgentName(agentId, agentName),
|
|
167
167
|
agentPromptCommand: '',
|
|
168
|
+
createdAt: null,
|
|
168
169
|
updatedAt: null,
|
|
169
170
|
messages: [],
|
|
170
171
|
lastResumeAt: null,
|
|
@@ -181,6 +182,7 @@ function normalizeWebAgentSessionRecord(agentId, rawAgent) {
|
|
|
181
182
|
agentPromptCommand: typeof source.agentPromptCommand === 'string'
|
|
182
183
|
? normalizeAgentPromptCommandTemplate(source.agentPromptCommand, `agents.${agentId}.agentPromptCommand`)
|
|
183
184
|
: '',
|
|
185
|
+
createdAt: typeof source.createdAt === 'string' ? source.createdAt : null,
|
|
184
186
|
updatedAt: typeof source.updatedAt === 'string' ? source.updatedAt : null,
|
|
185
187
|
messages: Array.isArray(source.messages) ? source.messages : [],
|
|
186
188
|
lastResumeAt: typeof source.lastResumeAt === 'string' ? source.lastResumeAt : null,
|
|
@@ -384,6 +386,45 @@ function listWebAgentSessions(history, options = {}) {
|
|
|
384
386
|
});
|
|
385
387
|
}
|
|
386
388
|
|
|
389
|
+
function getWebAgentCreationRank(agentId) {
|
|
390
|
+
if (agentId === WEB_DEFAULT_AGENT_ID) {
|
|
391
|
+
return 1;
|
|
392
|
+
}
|
|
393
|
+
const matched = String(agentId || '').match(/^agent-(\d+)$/);
|
|
394
|
+
return matched ? (Number(matched[1]) || 0) : 0;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
function getWebSessionCreatedTime(sessionSummary) {
|
|
398
|
+
if (sessionSummary && sessionSummary.createdAt) {
|
|
399
|
+
const time = new Date(sessionSummary.createdAt).getTime();
|
|
400
|
+
if (Number.isFinite(time)) {
|
|
401
|
+
return time;
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
return 0;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
function compareWebSessionCreatedDesc(a, b) {
|
|
408
|
+
const timeA = getWebSessionCreatedTime(a);
|
|
409
|
+
const timeB = getWebSessionCreatedTime(b);
|
|
410
|
+
if (timeA !== timeB) {
|
|
411
|
+
return timeB - timeA;
|
|
412
|
+
}
|
|
413
|
+
if (a && b && a.containerName === b.containerName) {
|
|
414
|
+
const rankA = getWebAgentCreationRank(a.agentId);
|
|
415
|
+
const rankB = getWebAgentCreationRank(b.agentId);
|
|
416
|
+
if (rankA !== rankB) {
|
|
417
|
+
return rankB - rankA;
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
const updatedA = a && a.updatedAt ? new Date(a.updatedAt).getTime() : 0;
|
|
421
|
+
const updatedB = b && b.updatedAt ? new Date(b.updatedAt).getTime() : 0;
|
|
422
|
+
if (updatedA !== updatedB) {
|
|
423
|
+
return updatedB - updatedA;
|
|
424
|
+
}
|
|
425
|
+
return String((a && a.name) || '').localeCompare(String((b && b.name) || ''), 'zh-CN');
|
|
426
|
+
}
|
|
427
|
+
|
|
387
428
|
function createWebSessionMessageId() {
|
|
388
429
|
return `${Date.now()}-${Math.random().toString(16).slice(2, 8)}`;
|
|
389
430
|
}
|
|
@@ -402,6 +443,9 @@ function appendWebSessionMessage(webHistoryDir, sessionRefOrContainerName, role,
|
|
|
402
443
|
timestamp,
|
|
403
444
|
...extra
|
|
404
445
|
};
|
|
446
|
+
if (!agentSession.createdAt) {
|
|
447
|
+
agentSession.createdAt = timestamp;
|
|
448
|
+
}
|
|
405
449
|
agentSession.messages.push(message);
|
|
406
450
|
|
|
407
451
|
if (agentSession.messages.length > WEB_HISTORY_MAX_MESSAGES) {
|
|
@@ -584,7 +628,11 @@ function createWebAgentSession(history) {
|
|
|
584
628
|
}
|
|
585
629
|
const agentId = `agent-${agentIndex}`;
|
|
586
630
|
const agentSession = createEmptyWebAgentSession(agentId, `AGENT ${agentIndex}`);
|
|
631
|
+
const timestamp = new Date().toISOString();
|
|
632
|
+
agentSession.createdAt = timestamp;
|
|
633
|
+
agentSession.updatedAt = timestamp;
|
|
587
634
|
sessionHistory.agents[agentId] = agentSession;
|
|
635
|
+
sessionHistory.updatedAt = timestamp;
|
|
588
636
|
return agentSession;
|
|
589
637
|
}
|
|
590
638
|
|
|
@@ -3041,6 +3089,7 @@ function buildSessionSummary(ctx, state, containerMap, sessionRef) {
|
|
|
3041
3089
|
status: containerInfo.status || 'history',
|
|
3042
3090
|
defaultCommand: containerInfo.defaultCommand || ''
|
|
3043
3091
|
});
|
|
3092
|
+
const createdAt = agentSession.createdAt || containerInfo.createdAt || null;
|
|
3044
3093
|
const updatedAt = agentSession.updatedAt || history.updatedAt || (latestMessage && latestMessage.timestamp) || containerInfo.createdAt || null;
|
|
3045
3094
|
return {
|
|
3046
3095
|
name: buildWebSessionKey(containerName, agentId),
|
|
@@ -3049,6 +3098,7 @@ function buildSessionSummary(ctx, state, containerMap, sessionRef) {
|
|
|
3049
3098
|
agentName: agentSession.agentName,
|
|
3050
3099
|
status: containerInfo.status || 'history',
|
|
3051
3100
|
image: containerInfo.image || '',
|
|
3101
|
+
createdAt,
|
|
3052
3102
|
updatedAt,
|
|
3053
3103
|
messageCount: agentSession.messages.length,
|
|
3054
3104
|
agentEnabled: isAgentPromptCommandEnabled(effectiveAgentPromptCommand),
|
|
@@ -3594,11 +3644,7 @@ async function handleWebApi(req, res, pathname, ctx, state) {
|
|
|
3594
3644
|
}))
|
|
3595
3645
|
.filter(Boolean);
|
|
3596
3646
|
})
|
|
3597
|
-
.sort(
|
|
3598
|
-
const timeA = a.updatedAt ? new Date(a.updatedAt).getTime() : 0;
|
|
3599
|
-
const timeB = b.updatedAt ? new Date(b.updatedAt).getTime() : 0;
|
|
3600
|
-
return timeB - timeA;
|
|
3601
|
-
});
|
|
3647
|
+
.sort(compareWebSessionCreatedDesc);
|
|
3602
3648
|
|
|
3603
3649
|
sendJson(res, 200, { sessions });
|
|
3604
3650
|
}
|