@xcanwin/manyoyo 5.6.9 → 5.7.0

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.
@@ -16,7 +16,10 @@ const AGENT_PROMPT_TEMPLATE_MAP = {
16
16
  opencode: 'opencode run {prompt}'
17
17
  };
18
18
 
19
+ const CLAUDE_DANGEROUS_FLAG = '--dangerously-skip-permissions';
20
+ const GEMINI_YOLO_FLAG = '--yolo';
19
21
  const CODEX_DANGEROUS_FLAG = '--dangerously-bypass-approvals-and-sandbox';
22
+ const OPENCODE_PERMISSION_KEY = 'OPENCODE_PERMISSION=';
20
23
 
21
24
  function stripLeadingAssignments(commandText) {
22
25
  let rest = String(commandText || '').trim();
@@ -80,11 +83,20 @@ function resolveAgentPromptCommandTemplate(commandText) {
80
83
  const normalizedCommand = String(commandText || '').trim();
81
84
  const program = resolveAgentProgram(commandText);
82
85
  const template = AGENT_PROMPT_TEMPLATE_MAP[program] || '';
86
+ if (program === 'claude' && normalizedCommand.includes(CLAUDE_DANGEROUS_FLAG)) {
87
+ return `${normalizedCommand} -p {prompt}`;
88
+ }
89
+ if (program === 'gemini' && normalizedCommand.includes(GEMINI_YOLO_FLAG)) {
90
+ return `${normalizedCommand} -p {prompt}`;
91
+ }
83
92
  if (program === 'codex' && template) {
84
93
  if (normalizedCommand.includes(CODEX_DANGEROUS_FLAG)) {
85
94
  return `codex exec ${CODEX_DANGEROUS_FLAG} --skip-git-repo-check {prompt}`;
86
95
  }
87
96
  }
97
+ if (program === 'opencode' && normalizedCommand.includes(OPENCODE_PERMISSION_KEY)) {
98
+ return `${normalizedCommand} run {prompt}`;
99
+ }
88
100
  return template;
89
101
  }
90
102
 
@@ -1047,6 +1047,154 @@ body.command-mode .msg.origin-agent .bubble {
1047
1047
  line-height: 1.55;
1048
1048
  }
1049
1049
 
1050
+ .trace-bubble {
1051
+ padding: 12px;
1052
+ }
1053
+
1054
+ .trace-structured {
1055
+ display: flex;
1056
+ flex-direction: column;
1057
+ gap: 10px;
1058
+ }
1059
+
1060
+ .trace-summary {
1061
+ display: flex;
1062
+ flex-direction: column;
1063
+ gap: 6px;
1064
+ }
1065
+
1066
+ .trace-summary-line {
1067
+ padding: 7px 10px;
1068
+ border-radius: 8px;
1069
+ border: 1px dashed rgba(181, 146, 99, 0.45);
1070
+ background: rgba(255, 250, 242, 0.82);
1071
+ color: var(--muted);
1072
+ font-size: 12px;
1073
+ line-height: 1.45;
1074
+ }
1075
+
1076
+ .trace-flow {
1077
+ display: flex;
1078
+ flex-direction: column;
1079
+ gap: 8px;
1080
+ }
1081
+
1082
+ .trace-card {
1083
+ border: 1px solid rgba(181, 146, 99, 0.32);
1084
+ border-left-width: 3px;
1085
+ border-radius: 10px;
1086
+ background: rgba(255, 255, 255, 0.88);
1087
+ overflow: hidden;
1088
+ }
1089
+
1090
+ .trace-card.trace-tone-command {
1091
+ border-left-color: var(--subaccent);
1092
+ }
1093
+
1094
+ .trace-card.trace-tone-mcp {
1095
+ border-left-color: var(--accent);
1096
+ }
1097
+
1098
+ .trace-card.trace-tone-note {
1099
+ border-left-color: #6f62b5;
1100
+ }
1101
+
1102
+ .trace-card.trace-tone-status {
1103
+ border-left-color: #7d8aa1;
1104
+ }
1105
+
1106
+ .trace-card.trace-tone-error {
1107
+ border-left-color: var(--danger);
1108
+ background: rgba(255, 239, 236, 0.92);
1109
+ }
1110
+
1111
+ .trace-card.trace-tone-neutral {
1112
+ border-left-color: var(--line-strong);
1113
+ }
1114
+
1115
+ .trace-card-summary {
1116
+ list-style: none;
1117
+ display: flex;
1118
+ align-items: center;
1119
+ gap: 8px;
1120
+ padding: 9px 10px;
1121
+ cursor: default;
1122
+ }
1123
+
1124
+ details.trace-card > .trace-card-summary {
1125
+ cursor: pointer;
1126
+ }
1127
+
1128
+ .trace-card-summary::-webkit-details-marker {
1129
+ display: none;
1130
+ }
1131
+
1132
+ .trace-card-badge {
1133
+ flex: 0 0 auto;
1134
+ display: inline-flex;
1135
+ align-items: center;
1136
+ justify-content: center;
1137
+ min-width: 42px;
1138
+ padding: 2px 7px;
1139
+ border-radius: 999px;
1140
+ background: rgba(31, 26, 20, 0.08);
1141
+ color: var(--text);
1142
+ font-size: 11px;
1143
+ font-weight: 700;
1144
+ letter-spacing: 0.2px;
1145
+ }
1146
+
1147
+ .trace-card-title {
1148
+ min-width: 0;
1149
+ flex: 1;
1150
+ color: var(--text);
1151
+ font-size: 12px;
1152
+ font-weight: 600;
1153
+ line-height: 1.45;
1154
+ word-break: break-word;
1155
+ }
1156
+
1157
+ .trace-card-phase {
1158
+ flex: 0 0 auto;
1159
+ color: var(--muted);
1160
+ font-size: 11px;
1161
+ font-weight: 700;
1162
+ }
1163
+
1164
+ .trace-card-body {
1165
+ padding: 0 10px 10px;
1166
+ display: flex;
1167
+ flex-direction: column;
1168
+ gap: 8px;
1169
+ }
1170
+
1171
+ .trace-card-body-section {
1172
+ display: flex;
1173
+ flex-direction: column;
1174
+ gap: 4px;
1175
+ }
1176
+
1177
+ .trace-card-body-label {
1178
+ color: var(--muted);
1179
+ font-size: 11px;
1180
+ font-weight: 700;
1181
+ letter-spacing: 0.2px;
1182
+ text-transform: uppercase;
1183
+ }
1184
+
1185
+ .trace-card-body-pre {
1186
+ margin: 0;
1187
+ padding: 8px 9px;
1188
+ border-radius: 8px;
1189
+ background: rgba(31, 26, 20, 0.05);
1190
+ color: var(--text);
1191
+ white-space: pre-wrap;
1192
+ word-break: break-word;
1193
+ font-family: var(--font-mono);
1194
+ font-size: 12px;
1195
+ line-height: 1.5;
1196
+ }
1197
+
1050
1198
  .composer {
1051
1199
  border-top: 1px solid rgba(181, 146, 99, 0.45);
1052
1200
  margin-top: 12px;
@@ -178,6 +178,10 @@
178
178
  codex: 'codex exec --skip-git-repo-check {prompt}',
179
179
  opencode: 'opencode run {prompt}'
180
180
  };
181
+ const CLAUDE_DANGEROUS_FLAG = '--dangerously-skip-permissions';
182
+ const GEMINI_YOLO_FLAG = '--yolo';
183
+ const CODEX_DANGEROUS_FLAG = '--dangerously-bypass-approvals-and-sandbox';
184
+ const OPENCODE_PERMISSION_KEY = 'OPENCODE_PERMISSION=';
181
185
  const markdownRenderer = window.ManyoyoMarkdown
182
186
  && typeof window.ManyoyoMarkdown.shouldRenderMessage === 'function'
183
187
  && typeof window.ManyoyoMarkdown.render === 'function'
@@ -190,6 +194,198 @@
190
194
  bubble.appendChild(pre);
191
195
  }
192
196
 
197
+ function stringifyPrettyJson(value) {
198
+ if (value === undefined || value === null) {
199
+ return '';
200
+ }
201
+ if (typeof value === 'string') {
202
+ return value;
203
+ }
204
+ try {
205
+ return JSON.stringify(value, null, 2);
206
+ } catch (e) {
207
+ return String(value);
208
+ }
209
+ }
210
+
211
+ function humanizeTraceKind(traceEvent) {
212
+ const kind = traceEvent && traceEvent.kind ? String(traceEvent.kind) : '';
213
+ if (kind === 'thread') return '会话';
214
+ if (kind === 'turn') return '回合';
215
+ if (kind === 'status') return '状态';
216
+ if (kind === 'agent_message') return '说明';
217
+ if (kind === 'command') return '命令';
218
+ if (kind === 'mcp') return 'MCP';
219
+ if (kind === 'tool') return '工具';
220
+ if (kind === 'error') return '错误';
221
+ return '事件';
222
+ }
223
+
224
+ function humanizeTracePhase(traceEvent) {
225
+ const phase = traceEvent && traceEvent.phase ? String(traceEvent.phase) : '';
226
+ if (phase === 'started') return '开始';
227
+ if (phase === 'completed') return '完成';
228
+ const status = traceEvent && traceEvent.status ? String(traceEvent.status).trim() : '';
229
+ return status;
230
+ }
231
+
232
+ function buildStructuredTraceResidualLines(message) {
233
+ const lines = String(message && message.content ? message.content : '')
234
+ .split('\n')
235
+ .map(function (line) {
236
+ return String(line || '').trim();
237
+ })
238
+ .filter(Boolean);
239
+ const traceEvents = Array.isArray(message && message.traceEvents) ? message.traceEvents : [];
240
+ const consumed = new Map();
241
+ traceEvents.forEach(function (traceEvent) {
242
+ const key = traceEvent && traceEvent.text ? String(traceEvent.text).trim() : '';
243
+ if (!key) {
244
+ return;
245
+ }
246
+ consumed.set(key, (consumed.get(key) || 0) + 1);
247
+ });
248
+ return lines.filter(function (line) {
249
+ if (!line || line === '[执行过程]') {
250
+ return false;
251
+ }
252
+ const remaining = consumed.get(line) || 0;
253
+ if (remaining > 0) {
254
+ consumed.set(line, remaining - 1);
255
+ return false;
256
+ }
257
+ return true;
258
+ });
259
+ }
260
+
261
+ function resolveTraceTone(traceEvent) {
262
+ const kind = traceEvent && traceEvent.kind ? String(traceEvent.kind) : '';
263
+ if (kind === 'command') return 'command';
264
+ if (kind === 'mcp') return 'mcp';
265
+ if (kind === 'error') return 'error';
266
+ if (kind === 'agent_message') return 'note';
267
+ if (kind === 'status') return 'status';
268
+ return 'neutral';
269
+ }
270
+
271
+ function appendTraceCardBody(cardBody, label, value) {
272
+ const text = stringifyPrettyJson(value).trim();
273
+ if (!text) {
274
+ return;
275
+ }
276
+ const section = document.createElement('div');
277
+ section.className = 'trace-card-body-section';
278
+
279
+ const title = document.createElement('div');
280
+ title.className = 'trace-card-body-label';
281
+ title.textContent = label;
282
+ section.appendChild(title);
283
+
284
+ const pre = document.createElement('pre');
285
+ pre.className = 'trace-card-body-pre';
286
+ pre.textContent = text;
287
+ section.appendChild(pre);
288
+
289
+ cardBody.appendChild(section);
290
+ }
291
+
292
+ function createTraceEventCard(traceEvent) {
293
+ const event = traceEvent && typeof traceEvent === 'object' ? traceEvent : {};
294
+ const bodyParts = [];
295
+ if (event.kind === 'command' && event.command) {
296
+ bodyParts.push({ label: '命令', value: event.command });
297
+ }
298
+ if (event.kind === 'mcp') {
299
+ if (event.argumentSummary) {
300
+ bodyParts.push({ label: '参数摘要', value: event.argumentSummary });
301
+ }
302
+ if (event.arguments) {
303
+ bodyParts.push({ label: '参数', value: event.arguments });
304
+ }
305
+ if (event.result) {
306
+ bodyParts.push({ label: '结果', value: event.result });
307
+ }
308
+ if (event.error) {
309
+ bodyParts.push({ label: '错误', value: event.error });
310
+ }
311
+ }
312
+ if (event.kind === 'tool' && event.toolName) {
313
+ bodyParts.push({ label: '工具', value: event.toolName });
314
+ }
315
+ if ((event.kind === 'agent_message' || event.kind === 'status' || event.kind === 'error') && event.detail) {
316
+ bodyParts.push({ label: '详情', value: event.detail });
317
+ }
318
+
319
+ const hasBody = bodyParts.length > 0;
320
+ const card = document.createElement(hasBody ? 'details' : 'div');
321
+ card.className = 'trace-card trace-tone-' + resolveTraceTone(event);
322
+ if (hasBody && event.kind === 'error') {
323
+ card.open = true;
324
+ }
325
+
326
+ const header = document.createElement(hasBody ? 'summary' : 'div');
327
+ header.className = 'trace-card-summary';
328
+
329
+ const badge = document.createElement('span');
330
+ badge.className = 'trace-card-badge';
331
+ badge.textContent = humanizeTraceKind(event);
332
+ header.appendChild(badge);
333
+
334
+ const title = document.createElement('span');
335
+ title.className = 'trace-card-title';
336
+ title.textContent = event && event.text ? String(event.text) : '事件';
337
+ header.appendChild(title);
338
+
339
+ const phaseText = humanizeTracePhase(event);
340
+ if (phaseText) {
341
+ const phase = document.createElement('span');
342
+ phase.className = 'trace-card-phase';
343
+ phase.textContent = phaseText;
344
+ header.appendChild(phase);
345
+ }
346
+
347
+ card.appendChild(header);
348
+
349
+ if (hasBody) {
350
+ const body = document.createElement('div');
351
+ body.className = 'trace-card-body';
352
+ bodyParts.forEach(function (part) {
353
+ appendTraceCardBody(body, part.label, part.value);
354
+ });
355
+ card.appendChild(body);
356
+ }
357
+
358
+ return card;
359
+ }
360
+
361
+ function appendStructuredTraceContent(bubble, message) {
362
+ bubble.classList.add('trace-bubble');
363
+ const container = document.createElement('div');
364
+ container.className = 'trace-structured';
365
+
366
+ const residualLines = buildStructuredTraceResidualLines(message);
367
+ if (residualLines.length) {
368
+ const summary = document.createElement('div');
369
+ summary.className = 'trace-summary';
370
+ residualLines.forEach(function (line) {
371
+ const item = document.createElement('div');
372
+ item.className = 'trace-summary-line';
373
+ item.textContent = line;
374
+ summary.appendChild(item);
375
+ });
376
+ container.appendChild(summary);
377
+ }
378
+
379
+ const flow = document.createElement('div');
380
+ flow.className = 'trace-flow';
381
+ (Array.isArray(message && message.traceEvents) ? message.traceEvents : []).forEach(function (traceEvent) {
382
+ flow.appendChild(createTraceEventCard(traceEvent));
383
+ });
384
+ container.appendChild(flow);
385
+
386
+ bubble.appendChild(container);
387
+ }
388
+
193
389
  function roleName(role, message) {
194
390
  if (role === 'user') return '我';
195
391
  if (role === 'assistant') {
@@ -367,11 +563,21 @@
367
563
  }
368
564
 
369
565
  function resolveAgentPromptTemplate(commandText) {
566
+ const normalizedCommand = String(commandText || '').trim();
370
567
  const program = resolveAgentProgram(commandText);
371
568
  const template = AGENT_PROMPT_TEMPLATE_MAP[program] || '';
372
- if (program === 'codex' && String(commandText || '').includes('--dangerously-bypass-approvals-and-sandbox')) {
569
+ if (program === 'claude' && normalizedCommand.includes(CLAUDE_DANGEROUS_FLAG)) {
570
+ return `${normalizedCommand} -p {prompt}`;
571
+ }
572
+ if (program === 'gemini' && normalizedCommand.includes(GEMINI_YOLO_FLAG)) {
573
+ return `${normalizedCommand} -p {prompt}`;
574
+ }
575
+ if (program === 'codex' && normalizedCommand.includes(CODEX_DANGEROUS_FLAG)) {
373
576
  return 'codex exec --dangerously-bypass-approvals-and-sandbox --skip-git-repo-check {prompt}';
374
577
  }
578
+ if (program === 'opencode' && normalizedCommand.includes(OPENCODE_PERMISSION_KEY)) {
579
+ return `${normalizedCommand} run {prompt}`;
580
+ }
375
581
  return template;
376
582
  }
377
583
 
@@ -1763,8 +1969,16 @@
1763
1969
  const bubble = document.createElement('div');
1764
1970
  bubble.className = 'bubble';
1765
1971
 
1972
+ const shouldRenderStructuredTrace = Boolean(
1973
+ msg
1974
+ && msg.streamTrace
1975
+ && Array.isArray(msg.traceEvents)
1976
+ && msg.traceEvents.length
1977
+ );
1766
1978
  const shouldRenderMarkdown = Boolean(!msg.streamTrace && markdownRenderer && markdownRenderer.shouldRenderMessage(msg));
1767
- if (shouldRenderMarkdown) {
1979
+ if (shouldRenderStructuredTrace) {
1980
+ appendStructuredTraceContent(bubble, msg);
1981
+ } else if (shouldRenderMarkdown) {
1768
1982
  const markdownNode = document.createElement('div');
1769
1983
  markdownNode.className = 'md-content';
1770
1984
  let renderedMarkdown = '';
@@ -2070,7 +2284,8 @@
2070
2284
  content: '[执行过程]\n等待 Agent 启动…',
2071
2285
  timestamp: new Date().toISOString(),
2072
2286
  mode: 'agent',
2073
- streamTrace: true
2287
+ streamTrace: true,
2288
+ traceEvents: []
2074
2289
  };
2075
2290
  if (state.active === sessionName) {
2076
2291
  state.messages.push(traceMessage);
@@ -2078,7 +2293,7 @@
2078
2293
  return traceMessage.id;
2079
2294
  }
2080
2295
 
2081
- function updateAgentTraceMessageLocal(sessionName, traceMessageId, content) {
2296
+ function updateAgentTraceMessageLocal(sessionName, traceMessageId, content, traceEvent) {
2082
2297
  if (state.active !== sessionName) {
2083
2298
  return;
2084
2299
  }
@@ -2089,6 +2304,12 @@
2089
2304
  }
2090
2305
  message.content = String(content || '');
2091
2306
  message.timestamp = new Date().toISOString();
2307
+ if (traceEvent && typeof traceEvent === 'object') {
2308
+ if (!Array.isArray(message.traceEvents)) {
2309
+ message.traceEvents = [];
2310
+ }
2311
+ message.traceEvents.push(traceEvent);
2312
+ }
2092
2313
  return;
2093
2314
  }
2094
2315
  }
@@ -2133,7 +2354,7 @@
2133
2354
  renderMessages(state.messages, { stickToBottom: true });
2134
2355
  syncUi();
2135
2356
 
2136
- function pushTraceLine(text) {
2357
+ function pushTraceLine(text, traceEvent) {
2137
2358
  const line = String(text || '').trim();
2138
2359
  if (!line) {
2139
2360
  return;
@@ -2142,7 +2363,7 @@
2142
2363
  return;
2143
2364
  }
2144
2365
  traceLines.push(line);
2145
- updateAgentTraceMessageLocal(sessionName, traceMessageId, traceLines.join('\n'));
2366
+ updateAgentTraceMessageLocal(sessionName, traceMessageId, traceLines.join('\n'), traceEvent);
2146
2367
  if (state.active === sessionName) {
2147
2368
  renderMessages(state.messages, { stickToBottom: true });
2148
2369
  }
@@ -2170,7 +2391,7 @@
2170
2391
  return;
2171
2392
  }
2172
2393
  if (event.type === 'trace') {
2173
- pushTraceLine(event.text || '');
2394
+ pushTraceLine(event.text || '', event.traceEvent || null);
2174
2395
  return;
2175
2396
  }
2176
2397
  if (event.type === 'result') {
package/lib/web/server.js CHANGED
@@ -376,9 +376,9 @@ function buildAgentPromptWithHistory(history, prompt) {
376
376
  ].join('\n');
377
377
  }
378
378
 
379
- function prepareCodexTraceDisplayLine(payload) {
379
+ function prepareCodexTraceEvent(payload) {
380
380
  if (!payload || typeof payload !== 'object') {
381
- return '';
381
+ return null;
382
382
  }
383
383
 
384
384
  const eventType = typeof payload.type === 'string' ? payload.type : '';
@@ -431,66 +431,173 @@ function prepareCodexTraceDisplayLine(payload) {
431
431
  return parts.slice(0, 3).join(', ');
432
432
  }
433
433
 
434
+ function pickDisplayStatus(defaultStatus) {
435
+ const status = String(itemStatus || defaultStatus || '').trim();
436
+ return status || '';
437
+ }
438
+
439
+ function createTraceEvent(kind, textValue, extra = {}) {
440
+ const normalizedText = String(textValue || '').trim();
441
+ if (!normalizedText) {
442
+ return null;
443
+ }
444
+ return {
445
+ provider: 'codex',
446
+ kind,
447
+ eventType,
448
+ itemType: itemType || '',
449
+ text: normalizedText,
450
+ ...extra
451
+ };
452
+ }
453
+
434
454
  if (eventType === 'thread.started') {
435
- return '[会话] Codex 已开始处理';
455
+ return createTraceEvent('thread', '[会话] Codex 已开始处理', {
456
+ phase: 'started',
457
+ status: 'started'
458
+ });
436
459
  }
437
460
  if (eventType === 'thread.completed') {
438
- return '[会话] Codex 已完成当前任务';
461
+ return createTraceEvent('thread', '[会话] Codex 已完成当前任务', {
462
+ phase: 'completed',
463
+ status: 'completed'
464
+ });
439
465
  }
440
466
  if (eventType === 'turn.started') {
441
- return '[回合] 开始生成响应';
467
+ return createTraceEvent('turn', '[回合] 开始生成响应', {
468
+ phase: 'started',
469
+ status: 'started'
470
+ });
442
471
  }
443
472
  if (eventType === 'turn.completed') {
444
- return '[回合] 响应完成';
473
+ return createTraceEvent('turn', '[回合] 响应完成', {
474
+ phase: 'completed',
475
+ status: 'completed'
476
+ });
445
477
  }
446
478
  if (eventType === 'item.started') {
447
479
  if (itemType === 'tool_call') {
448
- return `[工具开始] ${toolName || 'tool_call'}`;
480
+ return createTraceEvent('tool', `[工具开始] ${toolName || 'tool_call'}`, {
481
+ phase: 'started',
482
+ status: pickDisplayStatus('in_progress'),
483
+ toolName: toolName || 'tool_call'
484
+ });
449
485
  }
450
486
  if (itemType === 'command_execution') {
451
- return `[命令开始] ${commandText || 'command_execution'}`;
487
+ return createTraceEvent('command', `[命令开始] ${commandText || 'command_execution'}`, {
488
+ phase: 'started',
489
+ status: pickDisplayStatus('in_progress'),
490
+ command: commandText || 'command_execution'
491
+ });
452
492
  }
453
493
  if (itemType === 'mcp_tool_call') {
454
494
  const summary = summarizeArguments(item.arguments);
455
- return summary
456
- ? `[MCP开始] ${mcpServer || 'mcp'}.${mcpTool || 'tool'} (${summary})`
457
- : `[MCP开始] ${mcpServer || 'mcp'}.${mcpTool || 'tool'}`;
495
+ return createTraceEvent(
496
+ 'mcp',
497
+ summary
498
+ ? `[MCP开始] ${mcpServer || 'mcp'}.${mcpTool || 'tool'} (${summary})`
499
+ : `[MCP开始] ${mcpServer || 'mcp'}.${mcpTool || 'tool'}`,
500
+ {
501
+ phase: 'started',
502
+ status: pickDisplayStatus('in_progress'),
503
+ server: mcpServer || 'mcp',
504
+ tool: mcpTool || 'tool',
505
+ arguments: item.arguments && typeof item.arguments === 'object' && !Array.isArray(item.arguments)
506
+ ? item.arguments
507
+ : null,
508
+ argumentSummary: summary
509
+ }
510
+ );
458
511
  }
459
512
  if (itemType === 'reasoning') {
460
- return text ? `[状态] ${text}` : '[状态] Codex 正在分析';
513
+ return createTraceEvent('status', text ? `[状态] ${text}` : '[状态] Codex 正在分析', {
514
+ phase: 'started',
515
+ status: pickDisplayStatus('in_progress'),
516
+ detail: text || 'Codex 正在分析'
517
+ });
461
518
  }
462
519
  if (itemType === 'agent_message') {
463
- return text ? `[说明] ${text}` : '[回复] 正在生成最终答复';
520
+ return createTraceEvent('agent_message', text ? `[说明] ${text}` : '[回复] 正在生成最终答复', {
521
+ phase: 'started',
522
+ status: pickDisplayStatus('in_progress'),
523
+ detail: text || '正在生成最终答复'
524
+ });
464
525
  }
465
- return text ? `[事件开始] ${text}` : `[事件开始] ${itemType || eventType}`;
526
+ return createTraceEvent('event', text ? `[事件开始] ${text}` : `[事件开始] ${itemType || eventType}`, {
527
+ phase: 'started',
528
+ status: pickDisplayStatus('in_progress'),
529
+ detail: text || itemType || eventType
530
+ });
466
531
  }
467
532
  if (eventType === 'item.completed') {
468
533
  if (itemType === 'tool_call') {
469
- return `[工具完成] ${toolName || 'tool_call'}`;
534
+ return createTraceEvent('tool', `[工具完成] ${toolName || 'tool_call'}`, {
535
+ phase: 'completed',
536
+ status: pickDisplayStatus('completed'),
537
+ toolName: toolName || 'tool_call'
538
+ });
470
539
  }
471
540
  if (itemType === 'command_execution') {
472
541
  const suffix = itemStatus || (typeof item.exit_code === 'number' ? `exit=${item.exit_code}` : 'completed');
473
- return `[命令完成] ${commandText || 'command_execution'} (${suffix})`;
542
+ return createTraceEvent('command', `[命令完成] ${commandText || 'command_execution'} (${suffix})`, {
543
+ phase: 'completed',
544
+ status: pickDisplayStatus(suffix),
545
+ command: commandText || 'command_execution',
546
+ exitCode: typeof item.exit_code === 'number' ? item.exit_code : null
547
+ });
474
548
  }
475
549
  if (itemType === 'mcp_tool_call') {
476
550
  const summary = summarizeArguments(item.arguments);
477
- return summary
478
- ? `[MCP完成] ${mcpServer || 'mcp'}.${mcpTool || 'tool'} (${summary})`
479
- : `[MCP完成] ${mcpServer || 'mcp'}.${mcpTool || 'tool'}`;
551
+ return createTraceEvent(
552
+ 'mcp',
553
+ summary
554
+ ? `[MCP完成] ${mcpServer || 'mcp'}.${mcpTool || 'tool'} (${summary})`
555
+ : `[MCP完成] ${mcpServer || 'mcp'}.${mcpTool || 'tool'}`,
556
+ {
557
+ phase: 'completed',
558
+ status: pickDisplayStatus('completed'),
559
+ server: mcpServer || 'mcp',
560
+ tool: mcpTool || 'tool',
561
+ arguments: item.arguments && typeof item.arguments === 'object' && !Array.isArray(item.arguments)
562
+ ? item.arguments
563
+ : null,
564
+ argumentSummary: summary,
565
+ result: item.result !== undefined ? item.result : null,
566
+ error: item.error !== undefined ? item.error : null
567
+ }
568
+ );
480
569
  }
481
570
  if (itemType === 'reasoning') {
482
- return text ? `[状态] ${text}` : '';
571
+ return createTraceEvent('status', text ? `[状态] ${text}` : '', {
572
+ phase: 'completed',
573
+ status: pickDisplayStatus('completed'),
574
+ detail: text || ''
575
+ });
483
576
  }
484
577
  if (itemType === 'agent_message') {
485
- return text ? `[说明] ${text}` : '[回复] 已生成';
578
+ return createTraceEvent('agent_message', text ? `[说明] ${text}` : '[回复] 已生成', {
579
+ phase: 'completed',
580
+ status: pickDisplayStatus('completed'),
581
+ detail: text || '已生成'
582
+ });
486
583
  }
487
- return text ? `[事件完成] ${text}` : `[事件完成] ${itemType || eventType}`;
584
+ return createTraceEvent('event', text ? `[事件完成] ${text}` : `[事件完成] ${itemType || eventType}`, {
585
+ phase: 'completed',
586
+ status: pickDisplayStatus('completed'),
587
+ detail: text || itemType || eventType
588
+ });
488
589
  }
489
590
  if (eventType === 'error') {
490
- return text ? `[错误] ${text}` : '[错误] Codex 返回了错误事件';
591
+ return createTraceEvent('error', text ? `[错误] ${text}` : '[错误] Codex 返回了错误事件', {
592
+ status: 'error',
593
+ detail: text || 'Codex 返回了错误事件'
594
+ });
491
595
  }
492
596
 
493
- return `[事件] ${eventType}`;
597
+ return createTraceEvent('event', `[事件] ${eventType}`, {
598
+ status: itemStatus || '',
599
+ detail: eventType
600
+ });
494
601
  }
495
602
 
496
603
  async function prepareWebAgentExecution(ctx, state, containerName, prompt) {
@@ -1432,9 +1539,14 @@ async function execAgentInWebContainerStream(ctx, state, containerName, command,
1432
1539
  payload = null;
1433
1540
  }
1434
1541
  if (payload) {
1435
- const display = prepareCodexTraceDisplayLine(payload);
1436
- if (display) {
1437
- onEvent({ type: 'trace', stream: 'stdout', text: display });
1542
+ const traceEvent = prepareCodexTraceEvent(payload);
1543
+ if (traceEvent && traceEvent.text) {
1544
+ onEvent({
1545
+ type: 'trace',
1546
+ stream: 'stdout',
1547
+ text: traceEvent.text,
1548
+ traceEvent
1549
+ });
1438
1550
  }
1439
1551
  return;
1440
1552
  }
@@ -2222,6 +2334,7 @@ async function handleWebApi(req, res, pathname, ctx, state) {
2222
2334
 
2223
2335
  const { history, agentMeta, command, contextMode, resumeAttempted, resumeSucceeded, resumeError } = prepared;
2224
2336
  const traceLines = ['[执行过程]'];
2337
+ const traceEvents = [];
2225
2338
  appendWebSessionMessage(state.webHistoryDir, containerName, 'user', prompt, {
2226
2339
  mode: 'agent',
2227
2340
  contextMode
@@ -2253,12 +2366,16 @@ async function handleWebApi(req, res, pathname, ctx, state) {
2253
2366
  onEvent: event => {
2254
2367
  if (event && event.type === 'trace' && event.text) {
2255
2368
  traceLines.push(String(event.text));
2369
+ if (event.traceEvent && typeof event.traceEvent === 'object') {
2370
+ traceEvents.push(event.traceEvent);
2371
+ }
2256
2372
  }
2257
2373
  sendNdjson(res, event);
2258
2374
  }
2259
2375
  });
2260
2376
  traceLines.push(result.interrupted === true ? '[任务] 已停止' : '[任务] 已完成');
2261
2377
  appendWebAgentTraceMessage(state.webHistoryDir, containerName, traceLines.join('\n'), {
2378
+ traceEvents,
2262
2379
  contextMode,
2263
2380
  resumeAttempted,
2264
2381
  resumeSucceeded,
@@ -2282,6 +2399,7 @@ async function handleWebApi(req, res, pathname, ctx, state) {
2282
2399
  } catch (e) {
2283
2400
  traceLines.push(`[错误] ${e && e.message ? e.message : 'Agent 执行失败'}`);
2284
2401
  appendWebAgentTraceMessage(state.webHistoryDir, containerName, traceLines.join('\n'), {
2402
+ traceEvents,
2285
2403
  contextMode,
2286
2404
  resumeAttempted,
2287
2405
  resumeSucceeded,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xcanwin/manyoyo",
3
- "version": "5.6.9",
3
+ "version": "5.7.0",
4
4
  "imageVersion": "1.9.0-common",
5
5
  "playwrightCliVersion": "0.1.1",
6
6
  "description": "AI Agent CLI Security Sandbox for Docker and Podman",