shennian 0.2.60 → 0.2.62
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.
|
@@ -4,7 +4,7 @@ import { createHash } from 'node:crypto';
|
|
|
4
4
|
import fs from 'node:fs';
|
|
5
5
|
import os from 'node:os';
|
|
6
6
|
import path from 'node:path';
|
|
7
|
-
import { buildUserMessagePayload, isToolPayload } from '@shennian/wire';
|
|
7
|
+
import { buildUserMessagePayload, isSystemEnvironmentContextPayload, isToolPayload } from '@shennian/wire';
|
|
8
8
|
import { resolveBuiltinCommand, spawnResolvedCommandSync } from '../agents/command-spec.js';
|
|
9
9
|
const MAX_JSONL_LINE_BYTES = 64 * 1024 * 1024;
|
|
10
10
|
const DEFAULT_NATIVE_SCAN_IGNORED_PATHS = [
|
|
@@ -342,6 +342,8 @@ function parseCodexUserMessage(payload) {
|
|
|
342
342
|
: [];
|
|
343
343
|
if (!text && attachments.length === 0)
|
|
344
344
|
return null;
|
|
345
|
+
if (isSystemEnvironmentContextPayload(text) && attachments.length === 0)
|
|
346
|
+
return null;
|
|
345
347
|
if (attachments.length > 0) {
|
|
346
348
|
return {
|
|
347
349
|
payload: buildUserMessagePayload(text, attachments),
|
|
@@ -350,6 +352,115 @@ function parseCodexUserMessage(payload) {
|
|
|
350
352
|
}
|
|
351
353
|
return { payload: text, titleText: text };
|
|
352
354
|
}
|
|
355
|
+
function isCodexTerminalEventType(eventType) {
|
|
356
|
+
return eventType === 'turn_completed' || eventType === 'turn_complete' || eventType === 'task_complete';
|
|
357
|
+
}
|
|
358
|
+
function codexMessageContentText(content, textType) {
|
|
359
|
+
if (!Array.isArray(content))
|
|
360
|
+
return '';
|
|
361
|
+
return normalizeText(content
|
|
362
|
+
.map((part) => {
|
|
363
|
+
if (typeof part === 'string')
|
|
364
|
+
return part;
|
|
365
|
+
if (typeof part !== 'object' || part === null)
|
|
366
|
+
return '';
|
|
367
|
+
const record = part;
|
|
368
|
+
if (record.type !== textType)
|
|
369
|
+
return '';
|
|
370
|
+
return typeof record.text === 'string' ? record.text : '';
|
|
371
|
+
})
|
|
372
|
+
.filter(Boolean)
|
|
373
|
+
.join('\n\n'));
|
|
374
|
+
}
|
|
375
|
+
function codexMessageInputImageAttachments(content) {
|
|
376
|
+
if (!Array.isArray(content))
|
|
377
|
+
return [];
|
|
378
|
+
const attachments = [];
|
|
379
|
+
for (const part of content) {
|
|
380
|
+
if (typeof part !== 'object' || part === null)
|
|
381
|
+
continue;
|
|
382
|
+
const record = part;
|
|
383
|
+
if (record.type !== 'input_image')
|
|
384
|
+
continue;
|
|
385
|
+
const imagePath = typeof record.path === 'string' ? record.path
|
|
386
|
+
: typeof record.file_path === 'string' ? record.file_path
|
|
387
|
+
: typeof record.saved_path === 'string' ? record.saved_path
|
|
388
|
+
: typeof record.image_path === 'string' ? record.image_path
|
|
389
|
+
: '';
|
|
390
|
+
if (!imagePath.trim())
|
|
391
|
+
continue;
|
|
392
|
+
attachments.push({
|
|
393
|
+
path: imagePath,
|
|
394
|
+
name: path.basename(imagePath) || 'image.png',
|
|
395
|
+
mimeType: inferMimeType(imagePath),
|
|
396
|
+
kind: 'image',
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
return attachments;
|
|
400
|
+
}
|
|
401
|
+
function parseCodexResponseMessage(payload) {
|
|
402
|
+
if (payload.type !== 'message')
|
|
403
|
+
return null;
|
|
404
|
+
const role = payload.role === 'assistant' ? 'agent' : payload.role === 'user' ? 'user' : null;
|
|
405
|
+
if (!role)
|
|
406
|
+
return null;
|
|
407
|
+
if (role === 'agent') {
|
|
408
|
+
const text = codexMessageContentText(payload.content, 'output_text');
|
|
409
|
+
if (isSystemEnvironmentContextPayload(text))
|
|
410
|
+
return null;
|
|
411
|
+
return text ? { role, payload: text, titleText: text } : null;
|
|
412
|
+
}
|
|
413
|
+
const text = codexMessageContentText(payload.content, 'input_text');
|
|
414
|
+
const attachments = codexMessageInputImageAttachments(payload.content);
|
|
415
|
+
if (!text && attachments.length === 0)
|
|
416
|
+
return null;
|
|
417
|
+
if (isSystemEnvironmentContextPayload(text) && attachments.length === 0)
|
|
418
|
+
return null;
|
|
419
|
+
return {
|
|
420
|
+
role,
|
|
421
|
+
payload: attachments.length > 0 ? buildUserMessagePayload(text, attachments) : text,
|
|
422
|
+
titleText: text,
|
|
423
|
+
};
|
|
424
|
+
}
|
|
425
|
+
function codexDedupeText(payload) {
|
|
426
|
+
if (!payload)
|
|
427
|
+
return '';
|
|
428
|
+
try {
|
|
429
|
+
const parsed = JSON.parse(payload);
|
|
430
|
+
if (typeof parsed === 'object' && parsed !== null) {
|
|
431
|
+
const record = parsed;
|
|
432
|
+
if (record.type === 'user' && typeof record.content === 'string')
|
|
433
|
+
return normalizeText(record.content);
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
catch {
|
|
437
|
+
/* plain text payload */
|
|
438
|
+
}
|
|
439
|
+
return normalizeText(payload);
|
|
440
|
+
}
|
|
441
|
+
function isDuplicateCodexChatEvent(event, role, payload, ts) {
|
|
442
|
+
if (event.agentType !== 'codex')
|
|
443
|
+
return false;
|
|
444
|
+
if (event.role !== role)
|
|
445
|
+
return false;
|
|
446
|
+
if (isToolPayload(event.payload))
|
|
447
|
+
return false;
|
|
448
|
+
if (Math.abs(event.ts - ts) > 5 * 60 * 1000)
|
|
449
|
+
return false;
|
|
450
|
+
return codexDedupeText(event.payload) === codexDedupeText(payload);
|
|
451
|
+
}
|
|
452
|
+
function findDuplicateCodexChatEventIndex(events, role, payload, ts) {
|
|
453
|
+
for (let i = events.length - 1; i >= 0; i -= 1) {
|
|
454
|
+
const event = events[i];
|
|
455
|
+
if (!event)
|
|
456
|
+
continue;
|
|
457
|
+
if (isDuplicateCodexChatEvent(event, role, payload, ts))
|
|
458
|
+
return i;
|
|
459
|
+
if (event.agentType === 'codex' && Math.abs(event.ts - ts) > 5 * 60 * 1000)
|
|
460
|
+
break;
|
|
461
|
+
}
|
|
462
|
+
return -1;
|
|
463
|
+
}
|
|
353
464
|
function pushCodexEvent(events, filePath, lineOffset, kind, sourceSessionKey, ts, payload, title, modelId, workDir, role = 'agent', terminal = false) {
|
|
354
465
|
if (!payload)
|
|
355
466
|
return;
|
|
@@ -490,6 +601,16 @@ function parseCodexResponseItem(events, filePath, lineOffset, payload, sourceSes
|
|
|
490
601
|
const itemType = typeof payload.type === 'string' ? payload.type : '';
|
|
491
602
|
if (!itemType)
|
|
492
603
|
return;
|
|
604
|
+
if (itemType === 'message') {
|
|
605
|
+
const parsedMessage = parseCodexResponseMessage(payload);
|
|
606
|
+
if (!parsedMessage)
|
|
607
|
+
return;
|
|
608
|
+
const duplicateIndex = findDuplicateCodexChatEventIndex(events, parsedMessage.role, parsedMessage.payload, ts);
|
|
609
|
+
if (duplicateIndex >= 0)
|
|
610
|
+
return;
|
|
611
|
+
pushCodexEvent(events, filePath, lineOffset, `${itemType}:${parsedMessage.role}`, sourceSessionKey, ts, parsedMessage.payload, title, modelId, workDir, parsedMessage.role);
|
|
612
|
+
return;
|
|
613
|
+
}
|
|
493
614
|
if (itemType === 'function_call') {
|
|
494
615
|
const name = typeof payload.name === 'string' ? payload.name : 'function_call';
|
|
495
616
|
pushCodexToolEvent(events, filePath, lineOffset, itemType, sourceSessionKey, ts, name, title, modelId, workDir, parseStructuredString(payload.arguments));
|
|
@@ -532,7 +653,7 @@ function parseCodexEventMessage(events, filePath, lineOffset, payload, sourceSes
|
|
|
532
653
|
}
|
|
533
654
|
if (eventType === 'agent_message') {
|
|
534
655
|
const text = typeof payload.message === 'string' ? normalizeText(payload.message) : '';
|
|
535
|
-
if (!text)
|
|
656
|
+
if (!text || isSystemEnvironmentContextPayload(text))
|
|
536
657
|
return;
|
|
537
658
|
pushCodexEvent(events, filePath, lineOffset, eventType, sourceSessionKey, ts, text, title, modelId, workDir);
|
|
538
659
|
return;
|
|
@@ -803,12 +924,16 @@ export function parseCodexRolloutChunk(filePath, startOffset) {
|
|
|
803
924
|
if (!sourceSessionKey)
|
|
804
925
|
return;
|
|
805
926
|
if (type === 'response_item') {
|
|
927
|
+
const parsedMessage = parseCodexResponseMessage(payload);
|
|
928
|
+
if (parsedMessage?.role === 'user' && parsedMessage.titleText && !title) {
|
|
929
|
+
title = parsedMessage.titleText.slice(0, 80);
|
|
930
|
+
}
|
|
806
931
|
parseCodexResponseItem(events, filePath, lineOffset, payload, sourceSessionKey, ts, title, modelId, workDir);
|
|
807
932
|
return;
|
|
808
933
|
}
|
|
809
934
|
if (type === 'event_msg') {
|
|
810
935
|
const eventType = typeof payload.type === 'string' ? payload.type : '';
|
|
811
|
-
if (eventType
|
|
936
|
+
if (isCodexTerminalEventType(eventType)) {
|
|
812
937
|
for (let i = events.length - 1; i >= 0; i -= 1) {
|
|
813
938
|
const event = events[i];
|
|
814
939
|
if (event?.agentType !== 'codex')
|
|
@@ -827,6 +952,19 @@ export function parseCodexRolloutChunk(filePath, startOffset) {
|
|
|
827
952
|
const parsedUser = parseCodexUserMessage(payload);
|
|
828
953
|
if (parsedUser?.titleText && !title)
|
|
829
954
|
title = parsedUser.titleText.slice(0, 80);
|
|
955
|
+
if (parsedUser) {
|
|
956
|
+
const duplicateIndex = findDuplicateCodexChatEventIndex(events, 'user', parsedUser.payload, ts);
|
|
957
|
+
if (duplicateIndex >= 0)
|
|
958
|
+
events.splice(duplicateIndex, 1);
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
else if (eventType === 'agent_message') {
|
|
962
|
+
const text = typeof payload.message === 'string' ? normalizeText(payload.message) : '';
|
|
963
|
+
if (text && !isSystemEnvironmentContextPayload(text)) {
|
|
964
|
+
const duplicateIndex = findDuplicateCodexChatEventIndex(events, 'agent', text, ts);
|
|
965
|
+
if (duplicateIndex >= 0)
|
|
966
|
+
events.splice(duplicateIndex, 1);
|
|
967
|
+
}
|
|
830
968
|
}
|
|
831
969
|
const beforeCount = events.length;
|
|
832
970
|
parseCodexEventMessage(events, filePath, lineOffset, payload, sourceSessionKey, ts, title, modelId, workDir);
|