myagent-ai 1.10.9 → 1.11.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/web/ui/chat/chat.css +5 -0
- package/web/ui/chat/chat_main.js +117 -66
- package/web/ui/chat/flow_engine.js +17 -13
package/package.json
CHANGED
package/web/ui/chat/chat.css
CHANGED
|
@@ -482,6 +482,11 @@ input,textarea,select{font:inherit}
|
|
|
482
482
|
@keyframes badgePulse{0%,100%{opacity:1}50%{opacity:.6}}
|
|
483
483
|
.thought-block:not(.streaming) summary .thought-badge{background:var(--bg4);color:var(--text3);animation:none}
|
|
484
484
|
.thought-content{width:100%;padding:10px 14px 14px;font-size:13px;line-height:1.7;color:var(--text2);border-top:1px solid var(--border-light);max-height:300px;overflow-y:auto;overflow-x:hidden;word-break:break-word;overflow-wrap:break-word}
|
|
485
|
+
/* Reasoning (模型推理过程): limit to ~5 lines, scrollable */
|
|
486
|
+
.thought-block .thought-content.reasoning-content{max-height:calc(1.7em * 5 + 28px);min-height:0}
|
|
487
|
+
.thought-block .thought-content.reasoning-content::-webkit-scrollbar{width:4px}
|
|
488
|
+
.thought-block .thought-content.reasoning-content::-webkit-scrollbar-thumb{background:var(--bg4);border-radius:2px}
|
|
489
|
+
.thought-block .thought-content.reasoning-content::-webkit-scrollbar-track{background:transparent}
|
|
485
490
|
.thought-content p{margin:4px 0}
|
|
486
491
|
.thought-content p:first-child{margin-top:0}
|
|
487
492
|
.thought-content p:last-child{margin-bottom:0}
|
package/web/ui/chat/chat_main.js
CHANGED
|
@@ -1600,6 +1600,7 @@ async function clearSessionFromMenu(id) {
|
|
|
1600
1600
|
}
|
|
1601
1601
|
|
|
1602
1602
|
function formatSessionName(id) {
|
|
1603
|
+
if (!id) return '新会话';
|
|
1603
1604
|
if (id.startsWith('web_')) return id.replace('web_', '').replace(/_/g, ' ');
|
|
1604
1605
|
if (id.startsWith('cli_')) return 'CLI: ' + id.replace('cli_', '');
|
|
1605
1606
|
// Strip agent prefix if present (e.g., "default_web_2024..." -> "web_2024...")
|
|
@@ -2001,108 +2002,112 @@ async function clearCurrentChat() {
|
|
|
2001
2002
|
}
|
|
2002
2003
|
|
|
2003
2004
|
// ── Group History Messages ──
|
|
2004
|
-
// Groups consecutive non-user messages (assistant +
|
|
2005
|
-
// with parts[] for timeline rendering, matching the streaming display
|
|
2006
|
-
//
|
|
2005
|
+
// Groups consecutive non-user messages (assistant + tool_call + tool_result) into single
|
|
2006
|
+
// assistant messages with parts[] for timeline rendering, matching the streaming display.
|
|
2007
|
+
//
|
|
2008
|
+
// DB storage pattern:
|
|
2009
|
+
// user_input → role="user", key="user_input"
|
|
2010
|
+
// text → role="assistant", key=""
|
|
2011
|
+
// tool_call → role="assistant", key="tool_call" (NOT role="tool"!)
|
|
2012
|
+
// tool_result→ role="tool", key="tool_result"
|
|
2013
|
+
//
|
|
2014
|
+
// Result: user → [assistant { text → tool_call → tool_result → text → ... }] → user
|
|
2007
2015
|
function groupHistoryMessages(messages) {
|
|
2008
2016
|
if (!Array.isArray(messages) || messages.length === 0) return messages;
|
|
2009
2017
|
|
|
2010
2018
|
const grouped = [];
|
|
2011
2019
|
let i = 0;
|
|
2020
|
+
let _evtId = 0; // Event ID counter
|
|
2012
2021
|
|
|
2013
2022
|
while (i < messages.length) {
|
|
2014
2023
|
const msg = messages[i];
|
|
2015
2024
|
|
|
2016
2025
|
if (msg.role === 'user') {
|
|
2017
|
-
// User message: pass through as-is
|
|
2018
2026
|
grouped.push({ role: 'user', content: msg.content, time: msg.time || '' });
|
|
2019
2027
|
i++;
|
|
2020
2028
|
} else if (msg.role === 'assistant') {
|
|
2021
|
-
// Start of a new agent group: collect
|
|
2029
|
+
// Start of a new agent group: collect ALL consecutive non-user messages
|
|
2022
2030
|
const parts = [];
|
|
2023
2031
|
let lastAssistantTime = msg.time || '';
|
|
2024
2032
|
|
|
2025
|
-
//
|
|
2026
|
-
if (msg.
|
|
2033
|
+
// Process the first assistant message
|
|
2034
|
+
if (msg.key === 'tool_call') {
|
|
2035
|
+
const toolName = (msg.content.match(/^调用工具:\s*(\S+)/) || [])[1] || '';
|
|
2036
|
+
parts.push({
|
|
2037
|
+
type: 'exec',
|
|
2038
|
+
data: {
|
|
2039
|
+
id: _evtId++,
|
|
2040
|
+
type: 'tool_call',
|
|
2041
|
+
title: toolName ? ('调用工具: ' + toolName) : msg.content.substring(0, 100),
|
|
2042
|
+
tool_name: toolName,
|
|
2043
|
+
status: 'done',
|
|
2044
|
+
}
|
|
2045
|
+
});
|
|
2046
|
+
} else if (msg.content && msg.content.trim() && msg.content !== '(无回复)') {
|
|
2027
2047
|
parts.push({ type: 'text', content: msg.content });
|
|
2028
2048
|
}
|
|
2029
2049
|
|
|
2030
|
-
i++;
|
|
2050
|
+
i++;
|
|
2031
2051
|
|
|
2032
|
-
// Collect following tool
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
const
|
|
2052
|
+
// Collect all following tool_call (role=assistant), tool_result (role=tool),
|
|
2053
|
+
// and assistant text messages into the same group.
|
|
2054
|
+
// This handles: text → tool_call → tool_result → text → tool_call → tool_result → text
|
|
2055
|
+
while (i < messages.length) {
|
|
2056
|
+
const next = messages[i];
|
|
2037
2057
|
|
|
2038
|
-
if (
|
|
2039
|
-
|
|
2040
|
-
const toolName = (toolMsg.content.match(/^调用工具:\s*(\S+)/) || [])[1] || '';
|
|
2058
|
+
if (next.role === 'tool') {
|
|
2059
|
+
const isOk = !next.content.includes('失败');
|
|
2041
2060
|
parts.push({
|
|
2042
2061
|
type: 'exec',
|
|
2043
2062
|
data: {
|
|
2044
|
-
id:
|
|
2045
|
-
type: 'tool_call',
|
|
2046
|
-
title: toolMsg.content.substring(0, 100) || ('调用工具: ' + toolName),
|
|
2047
|
-
tool_name: toolName,
|
|
2048
|
-
status: 'done',
|
|
2049
|
-
}
|
|
2050
|
-
});
|
|
2051
|
-
} else if (isResult) {
|
|
2052
|
-
// Determine success/failure from content
|
|
2053
|
-
const isOk = !toolMsg.content.includes('失败');
|
|
2054
|
-
parts.push({
|
|
2055
|
-
type: 'exec',
|
|
2056
|
-
data: {
|
|
2057
|
-
id: 'hist_tool_' + i,
|
|
2063
|
+
id: _evtId++,
|
|
2058
2064
|
type: 'tool_result',
|
|
2059
|
-
title:
|
|
2065
|
+
title: next.content.substring(0, 80) || '工具执行结果',
|
|
2060
2066
|
success: isOk,
|
|
2061
|
-
summary:
|
|
2067
|
+
summary: next.content.substring(0, 500),
|
|
2068
|
+
result: { output: next.content.substring(0, 2000) },
|
|
2062
2069
|
}
|
|
2063
2070
|
});
|
|
2064
|
-
|
|
2065
|
-
|
|
2071
|
+
i++;
|
|
2072
|
+
} else if (next.role === 'assistant' && next.key === 'tool_call') {
|
|
2073
|
+
const toolName = (next.content.match(/^调用工具:\s*(\S+)/) || [])[1] || '';
|
|
2066
2074
|
parts.push({
|
|
2067
2075
|
type: 'exec',
|
|
2068
2076
|
data: {
|
|
2069
|
-
id:
|
|
2077
|
+
id: _evtId++,
|
|
2070
2078
|
type: 'tool_call',
|
|
2071
|
-
title:
|
|
2079
|
+
title: toolName ? ('调用工具: ' + toolName) : next.content.substring(0, 100),
|
|
2080
|
+
tool_name: toolName,
|
|
2072
2081
|
status: 'done',
|
|
2073
2082
|
}
|
|
2074
2083
|
});
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
// This handles the pattern: text → tool → text → tool
|
|
2081
|
-
if (i < messages.length && messages[i].role === 'assistant') {
|
|
2082
|
-
const nextAssistant = messages[i];
|
|
2083
|
-
if (nextAssistant.content && nextAssistant.content.trim() && nextAssistant.content !== '(无回复)') {
|
|
2084
|
-
parts.push({ type: 'text', content: nextAssistant.content });
|
|
2085
|
-
lastAssistantTime = nextAssistant.time || lastAssistantTime;
|
|
2084
|
+
lastAssistantTime = next.time || lastAssistantTime;
|
|
2085
|
+
i++;
|
|
2086
|
+
} else if (next.role === 'assistant') {
|
|
2087
|
+
if (next.content && next.content.trim() && next.content !== '(无回复)') {
|
|
2088
|
+
parts.push({ type: 'text', content: next.content });
|
|
2086
2089
|
}
|
|
2090
|
+
lastAssistantTime = next.time || lastAssistantTime;
|
|
2087
2091
|
i++;
|
|
2092
|
+
} else {
|
|
2093
|
+
break;
|
|
2088
2094
|
}
|
|
2089
2095
|
}
|
|
2090
2096
|
|
|
2091
|
-
// Create grouped assistant message with parts
|
|
2092
|
-
// Assemble content from text parts for backward compat
|
|
2093
2097
|
const textParts = parts.filter(p => p.type === 'text');
|
|
2094
|
-
const assembledContent = textParts.
|
|
2098
|
+
const assembledContent = textParts.length > 0
|
|
2099
|
+
? textParts[textParts.length - 1].content
|
|
2100
|
+
: '';
|
|
2095
2101
|
|
|
2096
2102
|
grouped.push({
|
|
2097
2103
|
role: 'assistant',
|
|
2098
|
-
content: assembledContent
|
|
2104
|
+
content: assembledContent,
|
|
2099
2105
|
time: lastAssistantTime,
|
|
2100
2106
|
parts: parts.length > 0 ? parts : undefined,
|
|
2101
|
-
// Also collect exec_events for backward compat display
|
|
2102
2107
|
exec_events: parts.filter(p => p.type === 'exec').map(p => p.data),
|
|
2103
2108
|
});
|
|
2104
2109
|
} else if (msg.role === 'tool') {
|
|
2105
|
-
// Orphan tool message
|
|
2110
|
+
// Orphan tool message — wrap in an assistant group
|
|
2106
2111
|
const parts = [];
|
|
2107
2112
|
const isResult = msg.key === 'tool_result';
|
|
2108
2113
|
const isCall = msg.key === 'tool_call';
|
|
@@ -2112,9 +2117,9 @@ function groupHistoryMessages(messages) {
|
|
|
2112
2117
|
parts.push({
|
|
2113
2118
|
type: 'exec',
|
|
2114
2119
|
data: {
|
|
2115
|
-
id:
|
|
2120
|
+
id: _evtId++,
|
|
2116
2121
|
type: 'tool_call',
|
|
2117
|
-
title:
|
|
2122
|
+
title: toolName ? ('调用工具: ' + toolName) : msg.content.substring(0, 100),
|
|
2118
2123
|
tool_name: toolName,
|
|
2119
2124
|
status: 'done',
|
|
2120
2125
|
}
|
|
@@ -2124,34 +2129,65 @@ function groupHistoryMessages(messages) {
|
|
|
2124
2129
|
parts.push({
|
|
2125
2130
|
type: 'exec',
|
|
2126
2131
|
data: {
|
|
2127
|
-
id:
|
|
2132
|
+
id: _evtId++,
|
|
2128
2133
|
type: 'tool_result',
|
|
2129
2134
|
title: msg.content.substring(0, 80) || '工具执行结果',
|
|
2130
2135
|
success: isOk,
|
|
2131
2136
|
summary: msg.content.substring(0, 500),
|
|
2137
|
+
result: { output: msg.content.substring(0, 2000) },
|
|
2132
2138
|
}
|
|
2133
2139
|
});
|
|
2134
2140
|
}
|
|
2135
2141
|
|
|
2136
2142
|
i++;
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
parts.push({
|
|
2143
|
+
while (i < messages.length) {
|
|
2144
|
+
const next = messages[i];
|
|
2145
|
+
if (next.role === 'assistant' && next.key === 'tool_call') {
|
|
2146
|
+
const toolName = (next.content.match(/^调用工具:\s*(\S+)/) || [])[1] || '';
|
|
2147
|
+
parts.push({
|
|
2148
|
+
type: 'exec',
|
|
2149
|
+
data: {
|
|
2150
|
+
id: _evtId++,
|
|
2151
|
+
type: 'tool_call',
|
|
2152
|
+
title: toolName ? ('调用工具: ' + toolName) : next.content.substring(0, 100),
|
|
2153
|
+
tool_name: toolName,
|
|
2154
|
+
status: 'done',
|
|
2155
|
+
}
|
|
2156
|
+
});
|
|
2157
|
+
i++;
|
|
2158
|
+
} else if (next.role === 'assistant') {
|
|
2159
|
+
if (next.content && next.content.trim() && next.content !== '(无回复)') {
|
|
2160
|
+
parts.push({ type: 'text', content: next.content });
|
|
2161
|
+
}
|
|
2162
|
+
i++;
|
|
2163
|
+
} else if (next.role === 'tool') {
|
|
2164
|
+
const isOk = !next.content.includes('失败');
|
|
2165
|
+
parts.push({
|
|
2166
|
+
type: 'exec',
|
|
2167
|
+
data: {
|
|
2168
|
+
id: _evtId++,
|
|
2169
|
+
type: 'tool_result',
|
|
2170
|
+
title: next.content.substring(0, 80) || '工具执行结果',
|
|
2171
|
+
success: isOk,
|
|
2172
|
+
summary: next.content.substring(0, 500),
|
|
2173
|
+
result: { output: next.content.substring(0, 2000) },
|
|
2174
|
+
}
|
|
2175
|
+
});
|
|
2176
|
+
i++;
|
|
2177
|
+
} else {
|
|
2178
|
+
break;
|
|
2142
2179
|
}
|
|
2143
|
-
i++;
|
|
2144
2180
|
}
|
|
2145
2181
|
|
|
2182
|
+
const textParts = parts.filter(p => p.type === 'text');
|
|
2146
2183
|
grouped.push({
|
|
2147
2184
|
role: 'assistant',
|
|
2148
|
-
content:
|
|
2185
|
+
content: textParts.length > 0 ? textParts[textParts.length - 1].content : '',
|
|
2149
2186
|
time: msg.time || '',
|
|
2150
2187
|
parts: parts.length > 0 ? parts : undefined,
|
|
2151
2188
|
exec_events: parts.filter(p => p.type === 'exec').map(p => p.data),
|
|
2152
2189
|
});
|
|
2153
2190
|
} else {
|
|
2154
|
-
// Skip unknown roles
|
|
2155
2191
|
i++;
|
|
2156
2192
|
}
|
|
2157
2193
|
}
|
|
@@ -2261,7 +2297,7 @@ function _renderMessagesInner() {
|
|
|
2261
2297
|
<span class="thought-label">模型推理过程</span>
|
|
2262
2298
|
${isStreaming ? '<span class="thought-badge">推理中...</span>' : '<span class="thought-badge">已完成</span>'}
|
|
2263
2299
|
</summary>
|
|
2264
|
-
<div class="thought-content">${renderMarkdown(msg.reasoning)}</div>
|
|
2300
|
+
<div class="thought-content reasoning-content">${renderMarkdown(msg.reasoning)}</div>
|
|
2265
2301
|
</details>`;
|
|
2266
2302
|
})() : '';
|
|
2267
2303
|
const actionBtns = (!isUser && msg.content) ? `
|
|
@@ -2323,7 +2359,7 @@ function _renderMessagesInner() {
|
|
|
2323
2359
|
const execEventsHtml = (!isUser && !hasParts && msg.exec_events && msg.exec_events.length > 0)
|
|
2324
2360
|
? renderExecEvents(msg.exec_events, i) : '';
|
|
2325
2361
|
html += `
|
|
2326
|
-
<div class="message-row ${msg.role}">
|
|
2362
|
+
<div class="message-row ${msg.role}${msg.streaming ? ' streaming' : ''}">
|
|
2327
2363
|
<div class="message-avatar">${avatar}</div>
|
|
2328
2364
|
<div class="message-content" style="flex:1;min-width:0">
|
|
2329
2365
|
${reasoningHtml}
|
|
@@ -2441,6 +2477,21 @@ function formatTime(timeStr) {
|
|
|
2441
2477
|
function scrollToBottom(force) {
|
|
2442
2478
|
const c = document.getElementById('messagesContainer');
|
|
2443
2479
|
if (!c) return;
|
|
2480
|
+
// During streaming: pin the active assistant message to the top of the chat window
|
|
2481
|
+
// so the user can see the full response content below
|
|
2482
|
+
const isStreaming = state.isGenerating;
|
|
2483
|
+
if (isStreaming && !force) {
|
|
2484
|
+
const activeRow = c.querySelector('.message-row.assistant.streaming, .message-row.assistant:last-of-type');
|
|
2485
|
+
if (activeRow) {
|
|
2486
|
+
requestAnimationFrame(() => {
|
|
2487
|
+
const rowTop = activeRow.offsetTop;
|
|
2488
|
+
// Scroll so the assistant row sits at the very top of the visible area
|
|
2489
|
+
c.scrollTop = rowTop;
|
|
2490
|
+
updateScrollToBottomBtn(c.scrollHeight - c.scrollTop - c.clientHeight);
|
|
2491
|
+
});
|
|
2492
|
+
return;
|
|
2493
|
+
}
|
|
2494
|
+
}
|
|
2444
2495
|
requestAnimationFrame(() => {
|
|
2445
2496
|
// Smart scroll: only auto-scroll if user is near bottom (within 120px)
|
|
2446
2497
|
// or if force is true
|
|
@@ -377,7 +377,7 @@ function updateStreamingMessage(msgIdx) {
|
|
|
377
377
|
<span class="thought-label">模型推理过程${reasoningWordCount}</span>
|
|
378
378
|
${msg.streaming ? '<span class="thought-badge">推理中...</span>' : '<span class="thought-badge">已完成</span>'}
|
|
379
379
|
</summary>
|
|
380
|
-
<div class="thought-content">${renderMarkdown(msg.reasoning)}</div>
|
|
380
|
+
<div class="thought-content reasoning-content">${renderMarkdown(msg.reasoning)}</div>
|
|
381
381
|
</details>`;
|
|
382
382
|
contentArea.insertAdjacentHTML('afterbegin', reasoningHtml);
|
|
383
383
|
// Set initial length tracking
|
|
@@ -1438,25 +1438,29 @@ async function sendMessage() {
|
|
|
1438
1438
|
// evt.result contains: {success, output, error, ...}
|
|
1439
1439
|
// 记录到调试控制台
|
|
1440
1440
|
if (window.addDebugLog) {
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1441
|
+
var _toolResult = evt.result || {};
|
|
1442
|
+
var _toolInfo = evt.tool || {};
|
|
1443
|
+
window.addDebugLog(_toolResult.success ? 'tool' : 'error',
|
|
1444
|
+
(_toolInfo.toolname || '工具') + ' 执行' + (_toolResult.success ? '成功' : '失败'), {
|
|
1445
|
+
tool: _toolInfo.toolname,
|
|
1446
|
+
success: _toolResult.success,
|
|
1447
|
+
error: _toolResult.error,
|
|
1448
|
+
result: _toolResult.output || _toolResult.error
|
|
1447
1449
|
});
|
|
1448
1450
|
}
|
|
1451
|
+
var _r = evt.result || {};
|
|
1452
|
+
var _t = evt.tool || {};
|
|
1449
1453
|
var resultEvent = {
|
|
1450
1454
|
type: 'v2_tool',
|
|
1451
1455
|
data: {
|
|
1452
1456
|
id: 'v2tool_' + Date.now() + '_' + allExecEvents.length,
|
|
1453
1457
|
type: 'tool_result',
|
|
1454
|
-
title: (
|
|
1455
|
-
tool_name:
|
|
1456
|
-
success:
|
|
1457
|
-
summary: (
|
|
1458
|
-
result:
|
|
1459
|
-
callback:
|
|
1458
|
+
title: (_t.toolname || '工具') + ' 执行完成',
|
|
1459
|
+
tool_name: _t.toolname,
|
|
1460
|
+
success: !!_r.success,
|
|
1461
|
+
summary: (_r.output || _r.error || '').substring(0, 500),
|
|
1462
|
+
result: _r,
|
|
1463
|
+
callback: _t.callback
|
|
1460
1464
|
}
|
|
1461
1465
|
};
|
|
1462
1466
|
msgParts.push(resultEvent);
|