@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.
- package/lib/agent-resume.js +12 -0
- package/lib/web/frontend/app.css +148 -0
- package/lib/web/frontend/app.js +228 -7
- package/lib/web/server.js +145 -27
- package/package.json +1 -1
package/lib/agent-resume.js
CHANGED
|
@@ -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
|
|
package/lib/web/frontend/app.css
CHANGED
|
@@ -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;
|
package/lib/web/frontend/app.js
CHANGED
|
@@ -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 === '
|
|
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 (
|
|
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
|
|
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
|
|
456
|
-
|
|
457
|
-
|
|
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
|
|
478
|
-
|
|
479
|
-
|
|
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
|
|
1436
|
-
if (
|
|
1437
|
-
onEvent({
|
|
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,
|