@xcanwin/manyoyo 5.7.0 → 5.7.2

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.
@@ -34,14 +34,14 @@ function buildVersionSuggestions(version) {
34
34
  }
35
35
  return [
36
36
  {
37
- key: 'patch',
38
- label: '第3段 +1 (patch)',
37
+ key: 'major',
38
+ label: '第1段 +1 (major)',
39
39
  version: formatReleaseVersion({
40
- major: parsed.major,
41
- minor: parsed.minor,
42
- patch: parsed.patch + 1
40
+ major: parsed.major + 1,
41
+ minor: 0,
42
+ patch: 0
43
43
  }),
44
- recommended: true
44
+ recommended: false
45
45
  },
46
46
  {
47
47
  key: 'minor',
@@ -54,18 +54,24 @@ function buildVersionSuggestions(version) {
54
54
  recommended: false
55
55
  },
56
56
  {
57
- key: 'major',
58
- label: '第1段 +1 (major)',
57
+ key: 'patch',
58
+ label: '第3段 +1 (patch)',
59
59
  version: formatReleaseVersion({
60
- major: parsed.major + 1,
61
- minor: 0,
62
- patch: 0
60
+ major: parsed.major,
61
+ minor: parsed.minor,
62
+ patch: parsed.patch + 1
63
63
  }),
64
- recommended: false
64
+ recommended: true
65
65
  }
66
66
  ];
67
67
  }
68
68
 
69
+ function findRecommendedChoiceIndex(options, fallbackIndex = 0) {
70
+ const list = Array.isArray(options) ? options : [];
71
+ const matchedIndex = list.findIndex(option => option && option.recommended === true);
72
+ return matchedIndex >= 0 ? matchedIndex : fallbackIndex;
73
+ }
74
+
69
75
  function pickLatestVersionTag(tags) {
70
76
  let latest = null;
71
77
  for (const rawTag of (tags || [])) {
@@ -129,6 +135,7 @@ module.exports = {
129
135
  parseReleaseVersion,
130
136
  compareReleaseVersions,
131
137
  buildVersionSuggestions,
138
+ findRecommendedChoiceIndex,
132
139
  pickLatestVersionTag,
133
140
  normalizeCommitMessage,
134
141
  extractAgentMessageFromCodexJsonl
package/lib/web/server.js CHANGED
@@ -297,13 +297,210 @@ function buildCodexAgentExecCommand(template, prompt) {
297
297
  : renderAgentPromptCommand(codexTemplate, prompt);
298
298
  }
299
299
 
300
+ function prependAgentFlags(commandText, matchPattern, flagSpecs) {
301
+ const matched = String(commandText || '').match(matchPattern);
302
+ if (!matched) {
303
+ return String(commandText || '');
304
+ }
305
+ const prefix = matched[1] || '';
306
+ let suffix = matched[matched.length - 1] || '';
307
+ for (let i = flagSpecs.length - 1; i >= 0; i -= 1) {
308
+ const spec = flagSpecs[i];
309
+ if (!spec || !spec.flag || !(spec.pattern instanceof RegExp) || spec.pattern.test(suffix)) {
310
+ continue;
311
+ }
312
+ suffix = ` ${spec.flag}${suffix}`;
313
+ }
314
+ return `${prefix}${suffix}`;
315
+ }
316
+
317
+ function buildClaudeAgentExecCommand(template, prompt) {
318
+ const templateText = normalizeAgentPromptCommandTemplate(template, 'agentPromptCommand');
319
+ const claudeTemplate = prependAgentFlags(
320
+ templateText,
321
+ /^(((?:(?:[A-Za-z_][A-Za-z0-9_]*=)(?:"(?:\\.|[^"])*"|'(?:\\.|[^'])*'|[^\s]+)\s+)*)claude\b)(.*)$/,
322
+ [
323
+ { flag: '--verbose', pattern: /(?:^|\s)--verbose(?:\s|$)/ },
324
+ { flag: '--output-format stream-json', pattern: /(?:^|\s)--output-format(?:\s|$)/ }
325
+ ]
326
+ );
327
+ return claudeTemplate === templateText
328
+ ? renderAgentPromptCommand(templateText, prompt)
329
+ : renderAgentPromptCommand(claudeTemplate, prompt);
330
+ }
331
+
332
+ function buildGeminiAgentExecCommand(template, prompt) {
333
+ const templateText = normalizeAgentPromptCommandTemplate(template, 'agentPromptCommand');
334
+ const geminiTemplate = prependAgentFlags(
335
+ templateText,
336
+ /^(((?:(?:[A-Za-z_][A-Za-z0-9_]*=)(?:"(?:\\.|[^"])*"|'(?:\\.|[^'])*'|[^\s]+)\s+)*)gemini\b)(.*)$/,
337
+ [
338
+ { flag: '--output-format stream-json', pattern: /(?:^|\s)--output-format(?:\s|$)/ }
339
+ ]
340
+ );
341
+ return geminiTemplate === templateText
342
+ ? renderAgentPromptCommand(templateText, prompt)
343
+ : renderAgentPromptCommand(geminiTemplate, prompt);
344
+ }
345
+
346
+ function buildOpenCodeAgentExecCommand(template, prompt) {
347
+ const templateText = normalizeAgentPromptCommandTemplate(template, 'agentPromptCommand');
348
+ const opencodeTemplate = prependAgentFlags(
349
+ templateText,
350
+ /^(((?:(?:[A-Za-z_][A-Za-z0-9_]*=)(?:"(?:\\.|[^"])*"|'(?:\\.|[^'])*'|[^\s]+)\s+)*)opencode\s+run\b)(.*)$/,
351
+ [
352
+ { flag: '--format json', pattern: /(?:^|\s)--format(?:\s|$)/ }
353
+ ]
354
+ );
355
+ return opencodeTemplate === templateText
356
+ ? renderAgentPromptCommand(templateText, prompt)
357
+ : renderAgentPromptCommand(opencodeTemplate, prompt);
358
+ }
359
+
300
360
  function buildWebAgentExecCommand(template, prompt, agentProgram) {
301
- if (agentProgram === 'codex') {
361
+ switch (agentProgram) {
362
+ case 'claude':
363
+ return buildClaudeAgentExecCommand(template, prompt);
364
+ case 'gemini':
365
+ return buildGeminiAgentExecCommand(template, prompt);
366
+ case 'codex':
302
367
  return buildCodexAgentExecCommand(template, prompt);
368
+ case 'opencode':
369
+ return buildOpenCodeAgentExecCommand(template, prompt);
370
+ default:
371
+ break;
303
372
  }
304
373
  return renderAgentPromptCommand(template, prompt);
305
374
  }
306
375
 
376
+ function parseJsonObjectLine(line) {
377
+ const text = String(line || '').trim();
378
+ if (!text) {
379
+ return null;
380
+ }
381
+ try {
382
+ const payload = JSON.parse(text);
383
+ return payload && typeof payload === 'object' ? payload : null;
384
+ } catch (e) {
385
+ return null;
386
+ }
387
+ }
388
+
389
+ function collectStructuredText(value) {
390
+ if (typeof value === 'string') {
391
+ return value.trim();
392
+ }
393
+ if (Array.isArray(value)) {
394
+ return value.map(item => collectStructuredText(item)).filter(Boolean).join('\n').trim();
395
+ }
396
+ if (!value || typeof value !== 'object') {
397
+ return '';
398
+ }
399
+ if (typeof value.text === 'string' && value.text.trim()) {
400
+ return value.text.trim();
401
+ }
402
+ if (typeof value.content === 'string' && value.content.trim()) {
403
+ return value.content.trim();
404
+ }
405
+ if (Array.isArray(value.content)) {
406
+ return value.content.map(item => collectStructuredText(item)).filter(Boolean).join('\n').trim();
407
+ }
408
+ return '';
409
+ }
410
+
411
+ function extractClaudeAgentMessage(text) {
412
+ let lastMessage = '';
413
+ for (const rawLine of String(text || '').split('\n')) {
414
+ const payload = parseJsonObjectLine(rawLine);
415
+ if (!payload || payload.type !== 'assistant') {
416
+ continue;
417
+ }
418
+ const message = toPlainObject(payload.message);
419
+ const content = Array.isArray(message.content) ? message.content : [];
420
+ const nextMessage = content
421
+ .filter(item => item && typeof item === 'object' && item.type === 'text')
422
+ .map(item => collectStructuredText(item))
423
+ .filter(Boolean)
424
+ .join('\n')
425
+ .trim();
426
+ if (nextMessage) {
427
+ lastMessage = nextMessage;
428
+ }
429
+ }
430
+ return lastMessage.trim();
431
+ }
432
+
433
+ function extractGeminiAgentMessage(text) {
434
+ let lastMessage = '';
435
+ let deltaMessage = '';
436
+ for (const rawLine of String(text || '').split('\n')) {
437
+ const payload = parseJsonObjectLine(rawLine);
438
+ if (!payload || payload.type !== 'message' || payload.role !== 'assistant') {
439
+ continue;
440
+ }
441
+ const content = collectStructuredText(payload.content);
442
+ if (!content) {
443
+ continue;
444
+ }
445
+ if (payload.delta === true) {
446
+ deltaMessage += content;
447
+ lastMessage = deltaMessage.trim();
448
+ continue;
449
+ }
450
+ deltaMessage = '';
451
+ lastMessage = content;
452
+ }
453
+ return lastMessage.trim();
454
+ }
455
+
456
+ function extractOpenCodeAgentMessage(text) {
457
+ let lastMessage = '';
458
+ let deltaMessage = '';
459
+ for (const rawLine of String(text || '').split('\n')) {
460
+ const payload = parseJsonObjectLine(rawLine);
461
+ if (!payload) {
462
+ continue;
463
+ }
464
+ const eventType = pickFirstString(payload.type);
465
+ const message = toPlainObject(payload.message);
466
+ const role = pickFirstString(payload.role, message.role);
467
+ if (eventType !== 'message' && eventType !== 'assistant' && eventType !== 'assistant_message' && eventType !== 'text') {
468
+ continue;
469
+ }
470
+ if (role && role !== 'assistant') {
471
+ continue;
472
+ }
473
+ const content = collectStructuredText(message.content || payload.content || payload.text || payload);
474
+ if (!content) {
475
+ continue;
476
+ }
477
+ if (payload.delta === true) {
478
+ deltaMessage += content;
479
+ lastMessage = deltaMessage.trim();
480
+ continue;
481
+ }
482
+ deltaMessage = '';
483
+ lastMessage = content;
484
+ }
485
+ return lastMessage.trim();
486
+ }
487
+
488
+ function extractAgentMessageFromStructuredOutput(agentProgram, text) {
489
+ if (agentProgram === 'codex') {
490
+ return extractAgentMessageFromCodexJsonl(text);
491
+ }
492
+ if (agentProgram === 'claude') {
493
+ return extractClaudeAgentMessage(text);
494
+ }
495
+ if (agentProgram === 'gemini') {
496
+ return extractGeminiAgentMessage(text);
497
+ }
498
+ if (agentProgram === 'opencode') {
499
+ return extractOpenCodeAgentMessage(text);
500
+ }
501
+ return '';
502
+ }
503
+
307
504
  function getAgentRuntimeMeta(history) {
308
505
  const sessionHistory = history && typeof history === 'object' ? history : {};
309
506
  const template = normalizeAgentPromptCommandTemplate(sessionHistory.agentPromptCommand, 'agentPromptCommand');
@@ -376,6 +573,333 @@ function buildAgentPromptWithHistory(history, prompt) {
376
573
  ].join('\n');
377
574
  }
378
575
 
576
+ function shortenTraceText(value, maxChars = 140) {
577
+ const raw = clipText(stripAnsi(String(value || '')).replace(/\s+/g, ' ').trim(), maxChars);
578
+ return raw.trim();
579
+ }
580
+
581
+ function summarizeTraceArguments(args) {
582
+ if (!args || typeof args !== 'object' || Array.isArray(args)) {
583
+ return '';
584
+ }
585
+ const parts = [];
586
+ for (const [key, value] of Object.entries(args)) {
587
+ if (value === undefined || value === null) continue;
588
+ if (typeof value === 'string') {
589
+ const textValue = value.trim();
590
+ if (!textValue) continue;
591
+ parts.push(`${key}=${shortenTraceText(textValue, 80)}`);
592
+ continue;
593
+ }
594
+ if (typeof value === 'number' || typeof value === 'boolean') {
595
+ parts.push(`${key}=${String(value)}`);
596
+ }
597
+ }
598
+ return parts.slice(0, 3).join(', ');
599
+ }
600
+
601
+ function createStructuredTraceEvent(provider, kind, eventType, textValue, extra = {}) {
602
+ const normalizedText = String(textValue || '').trim();
603
+ if (!normalizedText) {
604
+ return null;
605
+ }
606
+ return {
607
+ provider,
608
+ kind,
609
+ eventType,
610
+ text: normalizedText,
611
+ ...extra
612
+ };
613
+ }
614
+
615
+ function prepareClaudeTraceEvents(payload, state) {
616
+ const eventType = pickFirstString(payload.type);
617
+ const subtype = pickFirstString(payload.subtype);
618
+ const message = toPlainObject(payload.message);
619
+ const content = Array.isArray(message.content) ? message.content : [];
620
+ const toolNamesById = state.toolNamesById;
621
+ const events = [];
622
+
623
+ if (eventType === 'system' && subtype === 'init') {
624
+ events.push(createStructuredTraceEvent('claude', 'thread', eventType, '[会话] Claude 已开始处理', {
625
+ phase: 'started',
626
+ status: 'started',
627
+ subtype
628
+ }));
629
+ return events.filter(Boolean);
630
+ }
631
+ if (eventType === 'assistant') {
632
+ content.forEach(item => {
633
+ if (!item || typeof item !== 'object') {
634
+ return;
635
+ }
636
+ if (item.type === 'text') {
637
+ const detail = collectStructuredText(item);
638
+ if (detail) {
639
+ events.push(createStructuredTraceEvent('claude', 'agent_message', eventType, `[说明] ${detail}`, {
640
+ phase: 'completed',
641
+ status: 'completed',
642
+ detail
643
+ }));
644
+ }
645
+ return;
646
+ }
647
+ if (item.type === 'tool_use') {
648
+ const toolName = pickFirstString(item.name, item.id, 'tool');
649
+ const toolId = pickFirstString(item.id);
650
+ if (toolId) {
651
+ toolNamesById.set(toolId, toolName);
652
+ }
653
+ const summary = summarizeTraceArguments(toPlainObject(item.input));
654
+ events.push(createStructuredTraceEvent(
655
+ 'claude',
656
+ 'tool',
657
+ eventType,
658
+ summary ? `[工具开始] ${toolName} (${summary})` : `[工具开始] ${toolName}`,
659
+ {
660
+ phase: 'started',
661
+ status: 'in_progress',
662
+ toolName,
663
+ toolId,
664
+ arguments: toPlainObject(item.input),
665
+ argumentSummary: summary
666
+ }
667
+ ));
668
+ }
669
+ });
670
+ return events.filter(Boolean);
671
+ }
672
+ if (eventType === 'user') {
673
+ content.forEach(item => {
674
+ if (!item || typeof item !== 'object' || item.type !== 'tool_result') {
675
+ return;
676
+ }
677
+ const toolId = pickFirstString(item.tool_use_id);
678
+ const toolName = pickFirstString(toolNamesById.get(toolId), toolId, 'tool');
679
+ const status = item.is_error === true ? 'error' : 'success';
680
+ events.push(createStructuredTraceEvent('claude', 'tool', eventType, `[工具完成] ${toolName} (${status})`, {
681
+ phase: 'completed',
682
+ status,
683
+ toolName,
684
+ toolId,
685
+ result: collectStructuredText(item.content),
686
+ error: item.is_error === true ? collectStructuredText(item.content) : ''
687
+ }));
688
+ });
689
+ return events.filter(Boolean);
690
+ }
691
+ if (eventType === 'result') {
692
+ events.push(createStructuredTraceEvent('claude', 'turn', eventType, '[回合] 响应完成', {
693
+ phase: 'completed',
694
+ status: pickFirstString(subtype, 'completed'),
695
+ subtype
696
+ }));
697
+ return events.filter(Boolean);
698
+ }
699
+ if (eventType === 'error') {
700
+ const detail = pickFirstString(payload.message, payload.error);
701
+ events.push(createStructuredTraceEvent('claude', 'error', eventType, detail ? `[错误] ${detail}` : '[错误] Claude 返回了错误事件', {
702
+ status: 'error',
703
+ detail
704
+ }));
705
+ return events.filter(Boolean);
706
+ }
707
+ return [];
708
+ }
709
+
710
+ function prepareGeminiTraceEvents(payload, state) {
711
+ const eventType = pickFirstString(payload.type);
712
+ const toolNamesById = state.toolNamesById;
713
+ const events = [];
714
+
715
+ if (eventType === 'init') {
716
+ events.push(createStructuredTraceEvent('gemini', 'thread', eventType, '[会话] Gemini 已开始处理', {
717
+ phase: 'started',
718
+ status: 'started',
719
+ sessionId: pickFirstString(payload.session_id),
720
+ model: pickFirstString(payload.model)
721
+ }));
722
+ return events.filter(Boolean);
723
+ }
724
+ if (eventType === 'message' && payload.role === 'assistant') {
725
+ if (payload.delta === true) {
726
+ return [];
727
+ }
728
+ const detail = collectStructuredText(payload.content);
729
+ if (!detail) {
730
+ return [];
731
+ }
732
+ events.push(createStructuredTraceEvent('gemini', 'agent_message', eventType, `[说明] ${detail}`, {
733
+ phase: 'completed',
734
+ status: 'completed',
735
+ detail
736
+ }));
737
+ return events.filter(Boolean);
738
+ }
739
+ if (eventType === 'tool_use') {
740
+ const toolName = pickFirstString(payload.tool_name, payload.tool_id, 'tool');
741
+ const toolId = pickFirstString(payload.tool_id);
742
+ if (toolId) {
743
+ toolNamesById.set(toolId, toolName);
744
+ }
745
+ const summary = summarizeTraceArguments(toPlainObject(payload.parameters));
746
+ events.push(createStructuredTraceEvent(
747
+ 'gemini',
748
+ 'tool',
749
+ eventType,
750
+ summary ? `[工具开始] ${toolName} (${summary})` : `[工具开始] ${toolName}`,
751
+ {
752
+ phase: 'started',
753
+ status: 'in_progress',
754
+ toolName,
755
+ toolId,
756
+ arguments: toPlainObject(payload.parameters),
757
+ argumentSummary: summary
758
+ }
759
+ ));
760
+ return events.filter(Boolean);
761
+ }
762
+ if (eventType === 'tool_result') {
763
+ const toolId = pickFirstString(payload.tool_id);
764
+ const toolName = pickFirstString(toolNamesById.get(toolId), toolId, 'tool');
765
+ const status = pickFirstString(payload.status, 'completed');
766
+ events.push(createStructuredTraceEvent('gemini', 'tool', eventType, `[工具完成] ${toolName} (${status})`, {
767
+ phase: 'completed',
768
+ status,
769
+ toolName,
770
+ toolId,
771
+ result: collectStructuredText(payload.output),
772
+ error: toPlainObject(payload.error)
773
+ }));
774
+ return events.filter(Boolean);
775
+ }
776
+ if (eventType === 'result') {
777
+ events.push(createStructuredTraceEvent('gemini', 'turn', eventType, '[回合] 响应完成', {
778
+ phase: 'completed',
779
+ status: pickFirstString(payload.status, 'completed')
780
+ }));
781
+ return events.filter(Boolean);
782
+ }
783
+ if (eventType === 'error') {
784
+ const detail = pickFirstString(payload.message);
785
+ events.push(createStructuredTraceEvent('gemini', 'error', eventType, detail ? `[错误] ${detail}` : '[错误] Gemini 返回了错误事件', {
786
+ status: pickFirstString(payload.severity, 'error'),
787
+ detail
788
+ }));
789
+ return events.filter(Boolean);
790
+ }
791
+ return [];
792
+ }
793
+
794
+ function prepareOpenCodeTraceEvents(payload, state) {
795
+ const eventType = pickFirstString(payload.type);
796
+ const message = toPlainObject(payload.message);
797
+ const role = pickFirstString(payload.role, message.role);
798
+ const toolNamesById = state.toolNamesById;
799
+ const events = [];
800
+
801
+ if (eventType === 'session.start' || eventType === 'init') {
802
+ events.push(createStructuredTraceEvent('opencode', 'thread', eventType, '[会话] OpenCode 已开始处理', {
803
+ phase: 'started',
804
+ status: 'started',
805
+ sessionId: pickFirstString(payload.session_id, payload.sessionID)
806
+ }));
807
+ return events.filter(Boolean);
808
+ }
809
+ if (eventType === 'message' || eventType === 'assistant' || eventType === 'assistant_message' || eventType === 'text') {
810
+ if (role && role !== 'assistant') {
811
+ return [];
812
+ }
813
+ if (payload.delta === true) {
814
+ return [];
815
+ }
816
+ const detail = collectStructuredText(message.content || payload.content || payload.text || payload);
817
+ if (!detail) {
818
+ return [];
819
+ }
820
+ events.push(createStructuredTraceEvent('opencode', 'agent_message', eventType, `[说明] ${detail}`, {
821
+ phase: 'completed',
822
+ status: 'completed',
823
+ detail
824
+ }));
825
+ return events.filter(Boolean);
826
+ }
827
+ if (eventType === 'tool_use' || eventType === 'step_start') {
828
+ const toolName = pickFirstString(payload.tool_name, payload.name, payload.tool, payload.step, payload.tool_id, 'tool');
829
+ const toolId = pickFirstString(payload.tool_id, payload.id);
830
+ if (toolId) {
831
+ toolNamesById.set(toolId, toolName);
832
+ }
833
+ const argumentsValue = toPlainObject(payload.parameters || payload.input || payload.arguments);
834
+ const summary = summarizeTraceArguments(argumentsValue);
835
+ events.push(createStructuredTraceEvent(
836
+ 'opencode',
837
+ 'tool',
838
+ eventType,
839
+ summary ? `[工具开始] ${toolName} (${summary})` : `[工具开始] ${toolName}`,
840
+ {
841
+ phase: 'started',
842
+ status: pickFirstString(payload.status, 'in_progress'),
843
+ toolName,
844
+ toolId,
845
+ arguments: argumentsValue,
846
+ argumentSummary: summary
847
+ }
848
+ ));
849
+ return events.filter(Boolean);
850
+ }
851
+ if (eventType === 'tool_result' || eventType === 'step_finish') {
852
+ const toolId = pickFirstString(payload.tool_id, payload.id);
853
+ const toolName = pickFirstString(toolNamesById.get(toolId), payload.tool_name, payload.name, payload.tool, toolId, 'tool');
854
+ const status = pickFirstString(payload.status, payload.state, 'completed');
855
+ events.push(createStructuredTraceEvent('opencode', 'tool', eventType, `[工具完成] ${toolName} (${status})`, {
856
+ phase: 'completed',
857
+ status,
858
+ toolName,
859
+ toolId,
860
+ result: collectStructuredText(payload.output || payload.result),
861
+ error: toPlainObject(payload.error)
862
+ }));
863
+ return events.filter(Boolean);
864
+ }
865
+ if (eventType === 'result') {
866
+ events.push(createStructuredTraceEvent('opencode', 'turn', eventType, '[回合] 响应完成', {
867
+ phase: 'completed',
868
+ status: pickFirstString(payload.status, 'completed')
869
+ }));
870
+ return events.filter(Boolean);
871
+ }
872
+ if (eventType === 'error') {
873
+ const detail = pickFirstString(payload.message, payload.error && payload.error.message);
874
+ events.push(createStructuredTraceEvent('opencode', 'error', eventType, detail ? `[错误] ${detail}` : '[错误] OpenCode 返回了错误事件', {
875
+ status: 'error',
876
+ detail
877
+ }));
878
+ return events.filter(Boolean);
879
+ }
880
+ return [];
881
+ }
882
+
883
+ function prepareStructuredTraceEvents(agentProgram, payload, state) {
884
+ if (!payload || typeof payload !== 'object') {
885
+ return [];
886
+ }
887
+ if (agentProgram === 'codex') {
888
+ const traceEvent = prepareCodexTraceEvent(payload);
889
+ return traceEvent ? [traceEvent] : [];
890
+ }
891
+ if (agentProgram === 'claude') {
892
+ return prepareClaudeTraceEvents(payload, state);
893
+ }
894
+ if (agentProgram === 'gemini') {
895
+ return prepareGeminiTraceEvents(payload, state);
896
+ }
897
+ if (agentProgram === 'opencode') {
898
+ return prepareOpenCodeTraceEvents(payload, state);
899
+ }
900
+ return [];
901
+ }
902
+
379
903
  function prepareCodexTraceEvent(payload) {
380
904
  if (!payload || typeof payload !== 'object') {
381
905
  return null;
@@ -1424,7 +1948,9 @@ async function ensureWebContainer(ctx, state, containerInput) {
1424
1948
  }
1425
1949
  }
1426
1950
 
1427
- async function execCommandInWebContainer(ctx, containerName, command) {
1951
+ async function execCommandInWebContainer(ctx, containerName, command, options = {}) {
1952
+ const opts = options && typeof options === 'object' ? options : {};
1953
+ const agentProgram = typeof opts.agentProgram === 'string' ? opts.agentProgram : '';
1428
1954
  return await new Promise((resolve, reject) => {
1429
1955
  const process = spawn(
1430
1956
  ctx.dockerCmd,
@@ -1474,8 +2000,8 @@ async function execCommandInWebContainer(ctx, containerName, command) {
1474
2000
  const clippedStdout = stdoutTruncated ? `${stdoutOutput}\n...[stdout-truncated]` : stdoutOutput;
1475
2001
  const clippedStderr = stderrTruncated ? `${stderrOutput}\n...[stderr-truncated]` : stderrOutput;
1476
2002
  const clippedRaw = `${clippedStdout}${clippedStdout && clippedStderr ? '\n' : ''}${clippedStderr}`;
1477
- const extractedJsonAgentMessage = extractAgentMessageFromCodexJsonl(clippedStdout);
1478
- const cleanOutputSource = extractedJsonAgentMessage || clippedRaw;
2003
+ const extractedAgentMessage = extractAgentMessageFromStructuredOutput(agentProgram, clippedStdout);
2004
+ const cleanOutputSource = extractedAgentMessage || clippedRaw;
1479
2005
  const output = clipText(stripAnsi(cleanOutputSource).trim() || '(无输出)');
1480
2006
  resolve({ exitCode, output });
1481
2007
  });
@@ -1509,6 +2035,9 @@ async function execAgentInWebContainerStream(ctx, state, containerName, command,
1509
2035
  let stderrTruncated = false;
1510
2036
  let stdoutPending = '';
1511
2037
  let stderrPending = '';
2038
+ const structuredTraceState = {
2039
+ toolNamesById: new Map()
2040
+ };
1512
2041
  function appendChunk(chunk, target) {
1513
2042
  if (!chunk) return;
1514
2043
  const text = chunk.toString('utf-8');
@@ -1531,26 +2060,24 @@ async function execAgentInWebContainerStream(ctx, state, containerName, command,
1531
2060
  if (!rawLine) {
1532
2061
  return;
1533
2062
  }
1534
- if (agentProgram === 'codex') {
1535
- let payload = null;
1536
- try {
1537
- payload = JSON.parse(rawLine);
1538
- } catch (e) {
1539
- payload = null;
1540
- }
2063
+ if (agentProgram === 'claude' || agentProgram === 'gemini' || agentProgram === 'codex' || agentProgram === 'opencode') {
2064
+ const payload = parseJsonObjectLine(rawLine);
1541
2065
  if (payload) {
1542
- const traceEvent = prepareCodexTraceEvent(payload);
1543
- if (traceEvent && traceEvent.text) {
2066
+ const traceEvents = prepareStructuredTraceEvents(agentProgram, payload, structuredTraceState);
2067
+ traceEvents.forEach(traceEvent => {
2068
+ if (!traceEvent || !traceEvent.text) {
2069
+ return;
2070
+ }
1544
2071
  onEvent({
1545
2072
  type: 'trace',
1546
2073
  stream: 'stdout',
1547
2074
  text: traceEvent.text,
1548
2075
  traceEvent
1549
2076
  });
1550
- }
2077
+ });
1551
2078
  return;
1552
2079
  }
1553
- if (/^OpenAI Codex\b/.test(rawLine) || /^tokens used\b/i.test(rawLine)) {
2080
+ if (agentProgram === 'codex' && (/^OpenAI Codex\b/.test(rawLine) || /^tokens used\b/i.test(rawLine))) {
1554
2081
  return;
1555
2082
  }
1556
2083
  }
@@ -1614,8 +2141,8 @@ async function execAgentInWebContainerStream(ctx, state, containerName, command,
1614
2141
  const clippedStdout = stdoutTruncated ? `${stdoutOutput}\n...[stdout-truncated]` : stdoutOutput;
1615
2142
  const clippedStderr = stderrTruncated ? `${stderrOutput}\n...[stderr-truncated]` : stderrOutput;
1616
2143
  const clippedRaw = `${clippedStdout}${clippedStdout && clippedStderr ? '\n' : ''}${clippedStderr}`;
1617
- const extractedJsonAgentMessage = extractAgentMessageFromCodexJsonl(clippedStdout);
1618
- const cleanOutputSource = extractedJsonAgentMessage || clippedRaw;
2144
+ const extractedAgentMessage = extractAgentMessageFromStructuredOutput(agentProgram, clippedStdout);
2145
+ const cleanOutputSource = extractedAgentMessage || clippedRaw;
1619
2146
  const output = clipText(stripAnsi(cleanOutputSource).trim() || '(无输出)');
1620
2147
  resolve({
1621
2148
  exitCode,
@@ -2287,7 +2814,9 @@ async function handleWebApi(req, res, pathname, ctx, state) {
2287
2814
  mode: 'agent',
2288
2815
  contextMode
2289
2816
  });
2290
- const result = await execCommandInWebContainer(ctx, containerName, command);
2817
+ const result = await execCommandInWebContainer(ctx, containerName, command, {
2818
+ agentProgram: agentMeta.agentProgram
2819
+ });
2291
2820
  finalizeWebAgentExecution(state, containerName, history, agentMeta, {
2292
2821
  contextMode,
2293
2822
  resumeAttempted,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xcanwin/manyoyo",
3
- "version": "5.7.0",
3
+ "version": "5.7.2",
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",