myagent-ai 1.9.2 → 1.9.4
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.js +1 -1
- package/web/ui/chat/flow_engine.js +91 -23
- package/web/ui/chat/middle_chat.html +13 -0
package/package.json
CHANGED
package/web/ui/chat/chat.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
(function() {
|
|
5
5
|
var base = new URL('.', window.location.href).href;
|
|
6
|
-
var v = '?v=
|
|
6
|
+
var v = '?v=6';
|
|
7
7
|
var files = [
|
|
8
8
|
{ url: base + 'left_sessions.html' + v, container: 'leftSessionsContainer' },
|
|
9
9
|
{ url: base + 'middle_chat.html' + v, container: 'middleChatContainer' },
|
|
@@ -620,17 +620,17 @@ function toggleExecEventsPanel(header) {
|
|
|
620
620
|
function renderInlineExecEvent(data, msgIdx) {
|
|
621
621
|
// V2 Tool Event handling (called with full part: {type:'v2_tool', data:{...}})
|
|
622
622
|
if (data.type === 'v2_tool') {
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
623
|
+
let inner = data.data;
|
|
624
|
+
let isStart = inner.status === 'running' || inner.type === 'tool_start';
|
|
625
|
+
let iconEmoji = isStart ? '⚡' : (inner.success ? '✅' : '❌');
|
|
626
|
+
let title = inner.title || (inner.tool_name || '工具调用');
|
|
627
|
+
let metaParts = [];
|
|
628
628
|
if (inner.tool_name) metaParts.push(escapeHtml(inner.tool_name));
|
|
629
629
|
if (inner.timeout) metaParts.push('超时 ' + inner.timeout + 's');
|
|
630
630
|
if (inner.callback !== undefined) metaParts.push(inner.callback ? '回调LLM' : '无回调');
|
|
631
|
-
|
|
631
|
+
let metaText = metaParts.join(' · ');
|
|
632
632
|
|
|
633
|
-
|
|
633
|
+
let bodyHtml = '';
|
|
634
634
|
if (inner.type === 'tool_result' && inner.summary) {
|
|
635
635
|
bodyHtml += '<div class="inline-exec-summary">' + escapeHtml(inner.summary) + '</div>';
|
|
636
636
|
}
|
|
@@ -639,7 +639,7 @@ function renderInlineExecEvent(data, msgIdx) {
|
|
|
639
639
|
}
|
|
640
640
|
// Show params for tool_start
|
|
641
641
|
if (inner.type === 'tool_start' && inner.params) {
|
|
642
|
-
|
|
642
|
+
let paramPreview = typeof inner.params === 'string' ? inner.params : JSON.stringify(inner.params);
|
|
643
643
|
if (paramPreview.length > 200) paramPreview = paramPreview.substring(0, 200) + '...';
|
|
644
644
|
bodyHtml += '<div class="inline-exec-code">' + escapeHtml(paramPreview) + '</div>';
|
|
645
645
|
}
|
|
@@ -814,6 +814,31 @@ function showToolResultModal(msgIndex, eventId) {
|
|
|
814
814
|
document.body.appendChild(overlay);
|
|
815
815
|
}
|
|
816
816
|
|
|
817
|
+
// ══════════════════════════════════════════════════════
|
|
818
|
+
// ── V2 Content Assembler (V2 内容组装) ──
|
|
819
|
+
// ══════════════════════════════════════════════════════
|
|
820
|
+
|
|
821
|
+
function _assembleV2Content(msg, msgParts) {
|
|
822
|
+
// Priority 1: V2 reasoning text (user-facing response from v2_reasoning events)
|
|
823
|
+
if (msg._v2Reasoning && msg._v2Reasoning.trim()) {
|
|
824
|
+
return msg._v2Reasoning.trim();
|
|
825
|
+
}
|
|
826
|
+
// Priority 2: V2 ask user text
|
|
827
|
+
if (msg._askUser && msg._askUser.trim()) {
|
|
828
|
+
return msg._askUser.trim();
|
|
829
|
+
}
|
|
830
|
+
// Priority 3: V1 text parts (backward compat — non-V2 mode)
|
|
831
|
+
var textParts = msgParts.filter(function(p) { return p.type === 'text'; });
|
|
832
|
+
if (textParts.length > 0) {
|
|
833
|
+
return textParts.map(function(p) { return p.content; }).join('\n\n');
|
|
834
|
+
}
|
|
835
|
+
// Priority 4: raw content from message (server-stored response)
|
|
836
|
+
if (msg.content && msg.content.trim() && msg.content !== '(无回复)') {
|
|
837
|
+
return msg.content.trim();
|
|
838
|
+
}
|
|
839
|
+
return '(无回复)';
|
|
840
|
+
}
|
|
841
|
+
|
|
817
842
|
// ══════════════════════════════════════════════════════
|
|
818
843
|
// ── Send Message (核心 SSE 流式消息发送) ──
|
|
819
844
|
// ══════════════════════════════════════════════════════
|
|
@@ -827,7 +852,8 @@ async function sendMessage() {
|
|
|
827
852
|
// ── 如果正在生成,弹出处理选择框 ──
|
|
828
853
|
if (state.isGenerating) {
|
|
829
854
|
state.tempInputText = text;
|
|
830
|
-
document.getElementById('injectModal')
|
|
855
|
+
var modal = document.getElementById('injectModal');
|
|
856
|
+
if (modal) { modal.style.display = 'flex'; }
|
|
831
857
|
return;
|
|
832
858
|
}
|
|
833
859
|
|
|
@@ -903,10 +929,17 @@ async function sendMessage() {
|
|
|
903
929
|
let msgIdx = state.messages.length;
|
|
904
930
|
let sessionIdReceived = sessionId;
|
|
905
931
|
let fullThought = '';
|
|
932
|
+
let _isV2Mode = false; // Track whether V2 structured output events are received
|
|
933
|
+
let _v2RawXml = ''; // In V2 mode, accumulate raw XML from text_delta separately
|
|
934
|
+
let _v2ReasoningText = ''; // Accumulated V2 reasoning text for content
|
|
906
935
|
|
|
907
936
|
function flushCurrentText() {
|
|
908
937
|
if (currentText.trim()) {
|
|
909
|
-
|
|
938
|
+
// In V2 mode, text_delta contains raw XML — do NOT add to msgParts as user-facing text
|
|
939
|
+
if (!_isV2Mode) {
|
|
940
|
+
msgParts.push({type: 'text', content: currentText});
|
|
941
|
+
}
|
|
942
|
+
// Always accumulate for backward compat content field
|
|
910
943
|
}
|
|
911
944
|
currentText = '';
|
|
912
945
|
}
|
|
@@ -949,12 +982,16 @@ async function sendMessage() {
|
|
|
949
982
|
} else if (evt.type === 'text_delta') {
|
|
950
983
|
// Incremental streaming token
|
|
951
984
|
currentText += evt.content;
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
985
|
+
if (_isV2Mode) {
|
|
986
|
+
// In V2 mode, text_delta contains raw XML — store separately, not as user-facing content
|
|
987
|
+
_v2RawXml = currentText;
|
|
988
|
+
// Show V2 reasoning text as streaming content (if available)
|
|
989
|
+
state.messages[msgIdx]._streamingText = _v2ReasoningText || '';
|
|
990
|
+
} else {
|
|
991
|
+
// V1 mode: text_delta IS the user-facing content
|
|
992
|
+
state.messages[msgIdx]._streamingText = currentText;
|
|
993
|
+
}
|
|
955
994
|
state.messages[msgIdx].parts = [...msgParts];
|
|
956
|
-
state.messages[msgIdx]._streamingText = currentText;
|
|
957
|
-
state.messages[msgIdx].content = allText;
|
|
958
995
|
throttledStreamUpdate(msgIdx);
|
|
959
996
|
// ── 分段流式 TTS:推送增量文本 ──
|
|
960
997
|
if (ttsManager.enabled && !ttsManager._streamActive) {
|
|
@@ -981,7 +1018,7 @@ async function sendMessage() {
|
|
|
981
1018
|
if (state.messages[msgIdx]) {
|
|
982
1019
|
state.messages[msgIdx].streaming = false;
|
|
983
1020
|
state.messages[msgIdx].parts = [...msgParts];
|
|
984
|
-
state.messages[msgIdx].content =
|
|
1021
|
+
state.messages[msgIdx].content = _assembleV2Content(state.messages[msgIdx], msgParts);
|
|
985
1022
|
state.messages[msgIdx]._streamingText = '';
|
|
986
1023
|
if (allExecEvents.length > 0) state.messages[msgIdx].exec_events = [...allExecEvents];
|
|
987
1024
|
}
|
|
@@ -999,7 +1036,7 @@ async function sendMessage() {
|
|
|
999
1036
|
flushCurrentText();
|
|
1000
1037
|
state.messages[msgIdx].parts = [...msgParts];
|
|
1001
1038
|
state.messages[msgIdx]._streamingText = '';
|
|
1002
|
-
state.messages[msgIdx].content =
|
|
1039
|
+
state.messages[msgIdx].content = _assembleV2Content(state.messages[msgIdx], msgParts);
|
|
1003
1040
|
throttledStreamUpdate(msgIdx);
|
|
1004
1041
|
// 停止当前轮次的 TTS 播放,防止旧迭代语音与新迭代语音互相打断
|
|
1005
1042
|
if (ttsManager.enabled && ttsManager._streamActive) {
|
|
@@ -1040,7 +1077,10 @@ async function sendMessage() {
|
|
|
1040
1077
|
}
|
|
1041
1078
|
}
|
|
1042
1079
|
}
|
|
1043
|
-
// V2 Structured Output Events
|
|
1080
|
+
// V2 Structured Output Events — mark V2 mode
|
|
1081
|
+
} else if (evt.type === 'v2_context') {
|
|
1082
|
+
// V2 context event — confirms we're in V2 mode
|
|
1083
|
+
_isV2Mode = true;
|
|
1044
1084
|
} else if (evt.type === 'v2_output_parsed') {
|
|
1045
1085
|
// LLM output was parsed into structured format
|
|
1046
1086
|
// evt.data contains: {usersays_correct, task_plan, tools_to_call, remember, recall, ask_user, finish}
|
|
@@ -1119,11 +1159,22 @@ async function sendMessage() {
|
|
|
1119
1159
|
state.messages[msgIdx]._memorySaved = (state.messages[msgIdx]._memorySaved || '') +
|
|
1120
1160
|
(evt.content ? evt.content.substring(0, 100) : '');
|
|
1121
1161
|
} else if (evt.type === 'v2_reasoning') {
|
|
1122
|
-
// V2 reasoning text from model
|
|
1162
|
+
// V2 reasoning text from model — this IS the user-facing content in V2 mode
|
|
1123
1163
|
if (!state.messages[msgIdx]._v2Reasoning) {
|
|
1124
1164
|
state.messages[msgIdx]._v2Reasoning = '';
|
|
1125
1165
|
}
|
|
1166
|
+
// Add separator between multiple reasoning events (e.g., beforecalltext + final text)
|
|
1167
|
+
if (state.messages[msgIdx]._v2Reasoning.length > 0 && evt.content) {
|
|
1168
|
+
state.messages[msgIdx]._v2Reasoning += '\n\n';
|
|
1169
|
+
}
|
|
1126
1170
|
state.messages[msgIdx]._v2Reasoning += evt.content;
|
|
1171
|
+
_v2ReasoningText = state.messages[msgIdx]._v2Reasoning;
|
|
1172
|
+
// In V2 mode, update streaming text to show reasoning content
|
|
1173
|
+
if (_isV2Mode) {
|
|
1174
|
+
state.messages[msgIdx]._streamingText = _v2ReasoningText;
|
|
1175
|
+
// Also update content for real-time display
|
|
1176
|
+
state.messages[msgIdx].content = _v2ReasoningText;
|
|
1177
|
+
}
|
|
1127
1178
|
throttledStreamUpdate(msgIdx);
|
|
1128
1179
|
} else if (evt.type === 'done') {
|
|
1129
1180
|
flushCurrentText();
|
|
@@ -1134,7 +1185,8 @@ async function sendMessage() {
|
|
|
1134
1185
|
state.messages[msgIdx].parts = [...msgParts];
|
|
1135
1186
|
state.messages[msgIdx]._streamingText = '';
|
|
1136
1187
|
state.messages[msgIdx].exec_events = [...allExecEvents];
|
|
1137
|
-
|
|
1188
|
+
// Assemble final content: prefer V2 reasoning/ask text over raw XML
|
|
1189
|
+
state.messages[msgIdx].content = _assembleV2Content(state.messages[msgIdx], msgParts);
|
|
1138
1190
|
} else if (evt.type === 'reasoning_delta') {
|
|
1139
1191
|
// 模型推理过程增量文本(OpenAI o1/o3/DeepSeek-R1 等推理模型)
|
|
1140
1192
|
if (!state.messages[msgIdx].reasoning) state.messages[msgIdx].reasoning = '';
|
|
@@ -1163,7 +1215,8 @@ async function sendMessage() {
|
|
|
1163
1215
|
state.messages[msgIdx].parts = [...msgParts];
|
|
1164
1216
|
state.messages[msgIdx]._streamingText = '';
|
|
1165
1217
|
state.messages[msgIdx].exec_events = allExecEvents;
|
|
1166
|
-
|
|
1218
|
+
// Assemble final content: prefer V2 reasoning/ask text over raw XML
|
|
1219
|
+
state.messages[msgIdx].content = _assembleV2Content(state.messages[msgIdx], msgParts);
|
|
1167
1220
|
}
|
|
1168
1221
|
|
|
1169
1222
|
// Task list 已通过 SSE task_list_update 事件实时推送,无需再轮询
|
|
@@ -1230,6 +1283,19 @@ function stopGenerating() {
|
|
|
1230
1283
|
if (state.abortController) {
|
|
1231
1284
|
state.abortController.abort();
|
|
1232
1285
|
}
|
|
1286
|
+
// 立即重置 UI 状态(防止 abort 后 finally 未执行导致 UI 卡死)
|
|
1287
|
+
state.isGenerating = false;
|
|
1288
|
+
state.abortController = null;
|
|
1289
|
+
hideTypingIndicator();
|
|
1290
|
+
stopExecTimerPolling();
|
|
1291
|
+
var sendBtn = document.getElementById('sendBtn');
|
|
1292
|
+
var stopBtn = document.getElementById('stopBtn');
|
|
1293
|
+
var input = document.getElementById('userInput');
|
|
1294
|
+
if (sendBtn) { sendBtn.style.display = ''; sendBtn.disabled = !(input && input.value.trim()); }
|
|
1295
|
+
if (stopBtn) stopBtn.style.display = 'none';
|
|
1296
|
+
// 关闭可能打开的 injectModal
|
|
1297
|
+
var injectModal = document.getElementById('injectModal');
|
|
1298
|
+
if (injectModal) injectModal.style.display = 'none';
|
|
1233
1299
|
}
|
|
1234
1300
|
|
|
1235
1301
|
// ══════════════════════════════════════════════════════
|
|
@@ -1237,7 +1303,8 @@ function stopGenerating() {
|
|
|
1237
1303
|
// ══════════════════════════════════════════════════════
|
|
1238
1304
|
|
|
1239
1305
|
function closeInjectModal() {
|
|
1240
|
-
document.getElementById('injectModal')
|
|
1306
|
+
var modal = document.getElementById('injectModal');
|
|
1307
|
+
if (modal) modal.style.display = 'none';
|
|
1241
1308
|
state.tempInputText = '';
|
|
1242
1309
|
}
|
|
1243
1310
|
|
|
@@ -1438,7 +1505,8 @@ function handleKeyDown(e) {
|
|
|
1438
1505
|
if (state.isGenerating) {
|
|
1439
1506
|
// 任务执行中,使用统一的 injectModal 弹出选择对话框
|
|
1440
1507
|
state.tempInputText = text;
|
|
1441
|
-
document.getElementById('injectModal')
|
|
1508
|
+
var modal = document.getElementById('injectModal');
|
|
1509
|
+
if (modal) { modal.style.display = 'flex'; }
|
|
1442
1510
|
} else {
|
|
1443
1511
|
sendMessage();
|
|
1444
1512
|
}
|
|
@@ -142,4 +142,17 @@
|
|
|
142
142
|
<div class="input-hint">MyAgent 可能会出错,请核实重要信息</div>
|
|
143
143
|
</div>
|
|
144
144
|
</div>
|
|
145
|
+
|
|
146
|
+
<!-- Inject Modal (生成中消息注入选择) -->
|
|
147
|
+
<div id="injectModal" class="modal-overlay" style="display:none">
|
|
148
|
+
<div class="modal">
|
|
149
|
+
<h3>任务执行中</h3>
|
|
150
|
+
<p>Agent 正在执行任务,请选择如何处理你的消息:</p>
|
|
151
|
+
<div style="display:flex;flex-direction:column;gap:8px;margin-bottom:0">
|
|
152
|
+
<button class="modal-btn" style="width:100%;justify-content:center;background:var(--accent);color:#fff" onclick="handleInjectChoice('queue')">排队等待</button>
|
|
153
|
+
<button class="modal-btn" style="width:100%;justify-content:center" onclick="handleInjectChoice('inject')">注入当前任务</button>
|
|
154
|
+
<button class="modal-btn" style="width:100%;justify-content:center;color:var(--text2)" onclick="closeInjectModal()">取消</button>
|
|
155
|
+
</div>
|
|
156
|
+
</div>
|
|
157
|
+
</div>
|
|
145
158
|
</div>
|