shennian 0.2.88 → 0.2.90
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/dist/assets/wechat-channel/macos/manifest.json +22 -0
- package/dist/assets/wechat-channel/macos/shennian-wechat-channel-helper +0 -0
- package/dist/bin/shennian.js +1 -1
- package/dist/publish-build-manifest.json +548 -0
- package/dist/scripts/wechat-rpa-confirmation.mjs +5 -97
- package/dist/src/agent-env.js +4 -105
- package/dist/src/agents/adapter.d.ts +6 -0
- package/dist/src/agents/adapter.js +1 -19
- package/dist/src/agents/claude.js +8 -305
- package/dist/src/agents/codex-control.d.ts +35 -0
- package/dist/src/agents/codex-control.js +2 -0
- package/dist/src/agents/codex-utils.js +7 -200
- package/dist/src/agents/codex.d.ts +8 -0
- package/dist/src/agents/codex.js +15 -863
- package/dist/src/agents/command-spec.js +2 -413
- package/dist/src/agents/config-status.js +1 -226
- package/dist/src/agents/cursor.js +1 -249
- package/dist/src/agents/custom.js +4 -271
- package/dist/src/agents/detect.js +1 -56
- package/dist/src/agents/external-channel-instructions.js +10 -94
- package/dist/src/agents/gemini.js +1 -173
- package/dist/src/agents/manager.js +13 -157
- package/dist/src/agents/model-registry/cache.js +1 -37
- package/dist/src/agents/model-registry/discovery.js +2 -187
- package/dist/src/agents/model-registry/parsers.js +4 -447
- package/dist/src/agents/model-registry/runner.js +1 -30
- package/dist/src/agents/model-registry/service.js +1 -78
- package/dist/src/agents/model-registry/types.js +1 -8
- package/dist/src/agents/model-registry.js +1 -18
- package/dist/src/agents/openclaw.js +2 -275
- package/dist/src/agents/opencode.js +1 -231
- package/dist/src/agents/pi-context.js +12 -217
- package/dist/src/agents/pi.js +14 -723
- package/dist/src/agents/platform-instructions.js +9 -54
- package/dist/src/channels/base.d.ts +4 -1
- package/dist/src/channels/base.js +1 -3
- package/dist/src/channels/registry.js +1 -30
- package/dist/src/channels/reply-split.js +10 -89
- package/dist/src/channels/runtime.d.ts +1 -0
- package/dist/src/channels/runtime.js +5 -533
- package/dist/src/channels/secret-registry.d.ts +1 -0
- package/dist/src/channels/secret-registry.js +1 -46
- package/dist/src/channels/websocket.js +8 -378
- package/dist/src/channels/wechat-channel/anchor.d.ts +10 -0
- package/dist/src/channels/wechat-channel/anchor.js +1 -0
- package/dist/src/channels/wechat-channel/client.d.ts +74 -0
- package/dist/src/channels/wechat-channel/client.js +1 -0
- package/dist/src/channels/wechat-channel/cooldown.d.ts +15 -0
- package/dist/src/channels/wechat-channel/cooldown.js +1 -0
- package/dist/src/channels/wechat-channel/fingerprint.d.ts +28 -0
- package/dist/src/channels/wechat-channel/fingerprint.js +1 -0
- package/dist/src/channels/wechat-channel/helper-assets.d.ts +37 -0
- package/dist/src/channels/wechat-channel/helper-assets.js +1 -0
- package/dist/src/channels/wechat-channel/helper-client.d.ts +25 -0
- package/dist/src/channels/wechat-channel/helper-client.js +3 -0
- package/dist/src/channels/wechat-channel/helper-protocol.d.ts +84 -0
- package/dist/src/channels/wechat-channel/helper-protocol.js +1 -0
- package/dist/src/channels/wechat-channel/index.d.ts +17 -0
- package/dist/src/channels/wechat-channel/index.js +1 -0
- package/dist/src/channels/wechat-channel/ledger.d.ts +33 -0
- package/dist/src/channels/wechat-channel/ledger.js +1 -0
- package/dist/src/channels/wechat-channel/media-resolver.d.ts +32 -0
- package/dist/src/channels/wechat-channel/media-resolver.js +1 -0
- package/dist/src/channels/wechat-channel/message-key.d.ts +19 -0
- package/dist/src/channels/wechat-channel/message-key.js +1 -0
- package/dist/src/channels/wechat-channel/observer.d.ts +64 -0
- package/dist/src/channels/wechat-channel/observer.js +1 -0
- package/dist/src/channels/wechat-channel/outbound-ledger.d.ts +69 -0
- package/dist/src/channels/wechat-channel/outbound-ledger.js +2 -0
- package/dist/src/channels/wechat-channel/outbound-sender.d.ts +26 -0
- package/dist/src/channels/wechat-channel/outbound-sender.js +1 -0
- package/dist/src/channels/wechat-channel/preflight.d.ts +37 -0
- package/dist/src/channels/wechat-channel/preflight.js +1 -0
- package/dist/src/channels/wechat-channel/runner.d.ts +34 -0
- package/dist/src/channels/wechat-channel/runner.js +1 -0
- package/dist/src/channels/wechat-channel/runtime.d.ts +45 -0
- package/dist/src/channels/wechat-channel/runtime.js +1 -0
- package/dist/src/channels/wechat-channel/scheduler.d.ts +35 -0
- package/dist/src/channels/wechat-channel/scheduler.js +1 -0
- package/dist/src/channels/wechat-rpa/macos-flow.js +1 -96
- package/dist/src/channels/wechat-rpa/macos.js +6 -48
- package/dist/src/channels/wechat-rpa/normalizer.js +7 -127
- package/dist/src/channels/wechat-rpa.d.ts +21 -0
- package/dist/src/channels/wechat-rpa.js +6 -1022
- package/dist/src/channels/wecom.js +4 -357
- package/dist/src/commands/agent.js +6 -131
- package/dist/src/commands/daemon-windows.js +8 -48
- package/dist/src/commands/daemon.js +19 -1013
- package/dist/src/commands/external-attachments.js +1 -51
- package/dist/src/commands/external.js +1 -137
- package/dist/src/commands/manager.js +2 -389
- package/dist/src/commands/pair-qr.js +1 -6
- package/dist/src/commands/pair.js +9 -287
- package/dist/src/commands/tools.js +1 -34
- package/dist/src/commands/upgrade.js +1 -198
- package/dist/src/config/index.js +1 -35
- package/dist/src/daemon-log.js +6 -58
- package/dist/src/env-path.js +1 -64
- package/dist/src/fs/boundary.js +1 -126
- package/dist/src/fs/handler.js +1 -130
- package/dist/src/fs/security.js +1 -32
- package/dist/src/fs/text-decoder.d.ts +10 -0
- package/dist/src/fs/text-decoder.js +1 -0
- package/dist/src/index.js +2 -404
- package/dist/src/log-reporter.js +1 -16
- package/dist/src/manager/prompt.js +29 -34
- package/dist/src/manager/registry.js +2 -269
- package/dist/src/manager/runtime.js +19 -1003
- package/dist/src/native-fusion/config.js +1 -5
- package/dist/src/native-fusion/opencode-parser.js +3 -123
- package/dist/src/native-fusion/parser-common.js +8 -264
- package/dist/src/native-fusion/parsers.js +8 -729
- package/dist/src/native-fusion/service.d.ts +10 -0
- package/dist/src/native-fusion/service.js +2 -198
- package/dist/src/native-fusion/state.js +1 -22
- package/dist/src/native-fusion/types.js +1 -1
- package/dist/src/region.js +1 -88
- package/dist/src/relay/client.js +1 -343
- package/dist/src/session/archive-zip.js +1 -220
- package/dist/src/session/handlers/agent-config.js +1 -150
- package/dist/src/session/handlers/agents.js +1 -55
- package/dist/src/session/handlers/chat.js +2 -733
- package/dist/src/session/handlers/control.js +1 -55
- package/dist/src/session/handlers/fs.js +1 -747
- package/dist/src/session/handlers/session-refresh.js +1 -35
- package/dist/src/session/handlers/skills.js +1 -121
- package/dist/src/session/handlers/title.js +1 -60
- package/dist/src/session/handlers/tool-detail.d.ts +3 -0
- package/dist/src/session/handlers/tool-detail.js +1 -0
- package/dist/src/session/manager.d.ts +3 -0
- package/dist/src/session/manager.js +1 -261
- package/dist/src/session/projection.js +1 -54
- package/dist/src/session/queue.js +4 -317
- package/dist/src/session/remote-attachments.js +1 -72
- package/dist/src/session/store.js +3 -109
- package/dist/src/session/types.d.ts +4 -0
- package/dist/src/session/types.js +1 -4
- package/dist/src/skills/registry.js +15 -148
- package/dist/src/skills/setup.js +1 -101
- package/dist/src/tools/markdown-to-pdf.js +10 -346
- package/dist/src/upgrade/engine.js +3 -347
- package/package.json +3 -2
- package/dist/scripts/wechat-rpa-download-candidates.mjs +0 -105
|
@@ -1,729 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
function parseCodexLocalImageAttachment(value) {
|
|
10
|
-
if (typeof value === 'string') {
|
|
11
|
-
const imagePath = value.trim();
|
|
12
|
-
if (!imagePath)
|
|
13
|
-
return null;
|
|
14
|
-
return {
|
|
15
|
-
path: imagePath,
|
|
16
|
-
name: path.basename(imagePath) || 'image.png',
|
|
17
|
-
mimeType: inferMimeType(imagePath),
|
|
18
|
-
kind: 'image',
|
|
19
|
-
};
|
|
20
|
-
}
|
|
21
|
-
if (typeof value !== 'object' || value === null)
|
|
22
|
-
return null;
|
|
23
|
-
const entry = value;
|
|
24
|
-
const imagePath = typeof entry.path === 'string' ? entry.path
|
|
25
|
-
: typeof entry.file_path === 'string' ? entry.file_path
|
|
26
|
-
: typeof entry.saved_path === 'string' ? entry.saved_path
|
|
27
|
-
: '';
|
|
28
|
-
if (!imagePath.trim())
|
|
29
|
-
return null;
|
|
30
|
-
const name = typeof entry.name === 'string' && entry.name.trim()
|
|
31
|
-
? entry.name
|
|
32
|
-
: path.basename(imagePath) || 'image.png';
|
|
33
|
-
const mimeType = typeof entry.mimeType === 'string' && entry.mimeType.trim()
|
|
34
|
-
? entry.mimeType
|
|
35
|
-
: inferMimeType(imagePath);
|
|
36
|
-
return {
|
|
37
|
-
path: imagePath,
|
|
38
|
-
name,
|
|
39
|
-
mimeType,
|
|
40
|
-
kind: 'image',
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
function parseCodexTextElements(value) {
|
|
44
|
-
if (!Array.isArray(value))
|
|
45
|
-
return '';
|
|
46
|
-
const parts = value
|
|
47
|
-
.map((item) => {
|
|
48
|
-
if (typeof item === 'string')
|
|
49
|
-
return item;
|
|
50
|
-
if (typeof item === 'object' && item !== null) {
|
|
51
|
-
const record = item;
|
|
52
|
-
return typeof record.text === 'string' ? record.text : '';
|
|
53
|
-
}
|
|
54
|
-
return '';
|
|
55
|
-
})
|
|
56
|
-
.map((text) => normalizeText(text))
|
|
57
|
-
.filter(Boolean);
|
|
58
|
-
return parts.join('\n\n');
|
|
59
|
-
}
|
|
60
|
-
function stripCodexUserMessageWrapper(text) {
|
|
61
|
-
const normalized = normalizeText(text);
|
|
62
|
-
if (!normalized)
|
|
63
|
-
return { text: '', strippedWrapper: false };
|
|
64
|
-
const requestMatch = normalized.match(/My request for Codex:\s*([\s\S]*)$/i);
|
|
65
|
-
if (requestMatch && requestMatch[1]) {
|
|
66
|
-
return { text: stripCodexImagePlaceholders(requestMatch[1]), strippedWrapper: true };
|
|
67
|
-
}
|
|
68
|
-
return { text: normalized, strippedWrapper: false };
|
|
69
|
-
}
|
|
70
|
-
function stripCodexImagePlaceholders(text) {
|
|
71
|
-
return normalizeText(text.replace(/<image>\s*<\/image>/gi, ''));
|
|
72
|
-
}
|
|
73
|
-
function isCodexInjectedContextText(text) {
|
|
74
|
-
const normalized = normalizeText(text);
|
|
75
|
-
if (!normalized)
|
|
76
|
-
return false;
|
|
77
|
-
const hasProjectInstructions = normalized.startsWith('# AGENTS.md instructions for ') &&
|
|
78
|
-
normalized.includes('<INSTRUCTIONS>') &&
|
|
79
|
-
normalized.includes('</INSTRUCTIONS>');
|
|
80
|
-
const hasEnvironmentContext = normalized.includes('<environment_context>') &&
|
|
81
|
-
normalized.includes('</environment_context>');
|
|
82
|
-
return hasProjectInstructions && hasEnvironmentContext;
|
|
83
|
-
}
|
|
84
|
-
function parseCodexUserMessage(payload) {
|
|
85
|
-
const textFromElements = parseCodexTextElements(payload.text_elements);
|
|
86
|
-
const textFromInput = Array.isArray(payload.input)
|
|
87
|
-
? parseCodexTextElements(payload.input)
|
|
88
|
-
: '';
|
|
89
|
-
const strippedMessage = typeof payload.message === 'string'
|
|
90
|
-
? stripCodexUserMessageWrapper(payload.message)
|
|
91
|
-
: { text: '', strippedWrapper: false };
|
|
92
|
-
const textFromMessage = strippedMessage.text;
|
|
93
|
-
const text = textFromElements || textFromInput || textFromMessage;
|
|
94
|
-
const repairHint = strippedMessage.strippedWrapper ? 'codex_app_context_wrapper' : undefined;
|
|
95
|
-
const attachments = Array.isArray(payload.local_images)
|
|
96
|
-
? payload.local_images
|
|
97
|
-
.map(parseCodexLocalImageAttachment)
|
|
98
|
-
.filter((item) => item != null)
|
|
99
|
-
: [];
|
|
100
|
-
if (!text && attachments.length === 0)
|
|
101
|
-
return null;
|
|
102
|
-
if (isSystemControlPayload(text) && attachments.length === 0)
|
|
103
|
-
return null;
|
|
104
|
-
if (isCodexInjectedContextText(text) && attachments.length === 0)
|
|
105
|
-
return null;
|
|
106
|
-
if (attachments.length > 0) {
|
|
107
|
-
return {
|
|
108
|
-
payload: buildUserMessagePayload(text, attachments),
|
|
109
|
-
titleText: text,
|
|
110
|
-
repairHint,
|
|
111
|
-
};
|
|
112
|
-
}
|
|
113
|
-
return { payload: text, titleText: text, repairHint };
|
|
114
|
-
}
|
|
115
|
-
function isCodexTerminalEventType(eventType) {
|
|
116
|
-
return eventType === 'turn_completed' || eventType === 'turn_complete' || eventType === 'task_complete';
|
|
117
|
-
}
|
|
118
|
-
function isCodexApprovalEventType(eventType) {
|
|
119
|
-
return /approval|permission|confirm|user[_-]?input|input[_-]?required/i.test(eventType);
|
|
120
|
-
}
|
|
121
|
-
function buildCodexApprovalPayload(payload) {
|
|
122
|
-
const command = typeof payload.command === 'string'
|
|
123
|
-
? payload.command
|
|
124
|
-
: Array.isArray(payload.command)
|
|
125
|
-
? payload.command.join(' ')
|
|
126
|
-
: '';
|
|
127
|
-
const content = typeof payload.prompt === 'string' ? payload.prompt
|
|
128
|
-
: typeof payload.reason === 'string' ? payload.reason
|
|
129
|
-
: typeof payload.message === 'string' ? payload.message
|
|
130
|
-
: command ? `Codex 请求执行命令:${command}`
|
|
131
|
-
: 'Codex 正在等待用户确认后继续。';
|
|
132
|
-
return buildApprovalPendingPayload({
|
|
133
|
-
title: '需要用户确认',
|
|
134
|
-
content,
|
|
135
|
-
source: 'codex',
|
|
136
|
-
actionHint: '当前神念暂不支持远程确认,请回到运行 Codex 的电脑/终端完成确认。',
|
|
137
|
-
});
|
|
138
|
-
}
|
|
139
|
-
function codexMessageContentText(content, textType) {
|
|
140
|
-
if (!Array.isArray(content))
|
|
141
|
-
return '';
|
|
142
|
-
return normalizeText(content
|
|
143
|
-
.map((part) => {
|
|
144
|
-
if (typeof part === 'string')
|
|
145
|
-
return part;
|
|
146
|
-
if (typeof part !== 'object' || part === null)
|
|
147
|
-
return '';
|
|
148
|
-
const record = part;
|
|
149
|
-
if (record.type !== textType)
|
|
150
|
-
return '';
|
|
151
|
-
return typeof record.text === 'string' ? record.text : '';
|
|
152
|
-
})
|
|
153
|
-
.filter(Boolean)
|
|
154
|
-
.join('\n\n'));
|
|
155
|
-
}
|
|
156
|
-
function codexMessageInputImageAttachments(content) {
|
|
157
|
-
if (!Array.isArray(content))
|
|
158
|
-
return [];
|
|
159
|
-
const attachments = [];
|
|
160
|
-
for (const part of content) {
|
|
161
|
-
if (typeof part !== 'object' || part === null)
|
|
162
|
-
continue;
|
|
163
|
-
const record = part;
|
|
164
|
-
if (record.type !== 'input_image')
|
|
165
|
-
continue;
|
|
166
|
-
const imagePath = typeof record.path === 'string' ? record.path
|
|
167
|
-
: typeof record.file_path === 'string' ? record.file_path
|
|
168
|
-
: typeof record.saved_path === 'string' ? record.saved_path
|
|
169
|
-
: typeof record.image_path === 'string' ? record.image_path
|
|
170
|
-
: '';
|
|
171
|
-
if (!imagePath.trim())
|
|
172
|
-
continue;
|
|
173
|
-
attachments.push({
|
|
174
|
-
path: imagePath,
|
|
175
|
-
name: path.basename(imagePath) || 'image.png',
|
|
176
|
-
mimeType: inferMimeType(imagePath),
|
|
177
|
-
kind: 'image',
|
|
178
|
-
});
|
|
179
|
-
}
|
|
180
|
-
return attachments;
|
|
181
|
-
}
|
|
182
|
-
function parseCodexResponseMessage(payload) {
|
|
183
|
-
if (payload.type !== 'message')
|
|
184
|
-
return null;
|
|
185
|
-
const role = payload.role === 'assistant' ? 'agent' : payload.role === 'user' ? 'user' : null;
|
|
186
|
-
if (!role)
|
|
187
|
-
return null;
|
|
188
|
-
if (role === 'agent') {
|
|
189
|
-
const text = codexMessageContentText(payload.content, 'output_text');
|
|
190
|
-
if (isSystemControlPayload(text))
|
|
191
|
-
return null;
|
|
192
|
-
return text ? { role, payload: text, titleText: text } : null;
|
|
193
|
-
}
|
|
194
|
-
const stripped = stripCodexUserMessageWrapper(codexMessageContentText(payload.content, 'input_text'));
|
|
195
|
-
const text = stripped.text;
|
|
196
|
-
const attachments = codexMessageInputImageAttachments(payload.content);
|
|
197
|
-
if (!text && attachments.length === 0)
|
|
198
|
-
return null;
|
|
199
|
-
if (isSystemControlPayload(text) && attachments.length === 0)
|
|
200
|
-
return null;
|
|
201
|
-
if (isCodexInjectedContextText(text) && attachments.length === 0)
|
|
202
|
-
return null;
|
|
203
|
-
return {
|
|
204
|
-
role,
|
|
205
|
-
payload: attachments.length > 0 ? buildUserMessagePayload(text, attachments) : text,
|
|
206
|
-
titleText: text,
|
|
207
|
-
repairHint: stripped.strippedWrapper ? 'codex_app_context_wrapper' : undefined,
|
|
208
|
-
};
|
|
209
|
-
}
|
|
210
|
-
function codexDedupeText(payload) {
|
|
211
|
-
if (!payload)
|
|
212
|
-
return '';
|
|
213
|
-
try {
|
|
214
|
-
const parsed = JSON.parse(payload);
|
|
215
|
-
if (typeof parsed === 'object' && parsed !== null) {
|
|
216
|
-
const record = parsed;
|
|
217
|
-
if (record.type === 'user' && typeof record.content === 'string')
|
|
218
|
-
return normalizeText(record.content);
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
catch {
|
|
222
|
-
/* plain text payload */
|
|
223
|
-
}
|
|
224
|
-
return normalizeText(payload);
|
|
225
|
-
}
|
|
226
|
-
function isDuplicateCodexChatEvent(event, role, payload, ts) {
|
|
227
|
-
if (event.agentType !== 'codex')
|
|
228
|
-
return false;
|
|
229
|
-
if (event.role !== role)
|
|
230
|
-
return false;
|
|
231
|
-
if (isToolPayload(event.payload))
|
|
232
|
-
return false;
|
|
233
|
-
if (Math.abs(event.ts - ts) > 5 * 60 * 1000)
|
|
234
|
-
return false;
|
|
235
|
-
return codexDedupeText(event.payload) === codexDedupeText(payload);
|
|
236
|
-
}
|
|
237
|
-
function findDuplicateCodexChatEventIndex(events, role, payload, ts) {
|
|
238
|
-
for (let i = events.length - 1; i >= 0; i -= 1) {
|
|
239
|
-
const event = events[i];
|
|
240
|
-
if (!event)
|
|
241
|
-
continue;
|
|
242
|
-
if (isDuplicateCodexChatEvent(event, role, payload, ts))
|
|
243
|
-
return i;
|
|
244
|
-
if (event.agentType === 'codex' && Math.abs(event.ts - ts) > 5 * 60 * 1000)
|
|
245
|
-
break;
|
|
246
|
-
}
|
|
247
|
-
return -1;
|
|
248
|
-
}
|
|
249
|
-
function pushCodexEvent(events, filePath, lineOffset, kind, sourceSessionKey, ts, payload, title, modelId, workDir, role = 'agent', terminal = false, repairHint) {
|
|
250
|
-
if (!payload)
|
|
251
|
-
return;
|
|
252
|
-
events.push({
|
|
253
|
-
agentType: 'codex',
|
|
254
|
-
sourceMode: 'codex_rollout_import',
|
|
255
|
-
sourceSessionKey,
|
|
256
|
-
sourceEventKey: makeEventKey(filePath, lineOffset, kind),
|
|
257
|
-
cursor: makeCursor(filePath, lineOffset),
|
|
258
|
-
role,
|
|
259
|
-
ts,
|
|
260
|
-
payload,
|
|
261
|
-
title,
|
|
262
|
-
modelId,
|
|
263
|
-
workDir,
|
|
264
|
-
terminal,
|
|
265
|
-
repairHint,
|
|
266
|
-
});
|
|
267
|
-
}
|
|
268
|
-
function pushCodexToolEvent(events, filePath, lineOffset, kind, sourceSessionKey, ts, toolName, title, modelId, workDir, args, result) {
|
|
269
|
-
pushCodexEvent(events, filePath, lineOffset, kind, sourceSessionKey, ts, buildToolPayload(toolName, args, result), title, modelId, workDir);
|
|
270
|
-
}
|
|
271
|
-
function parseCodexResponseItem(events, filePath, lineOffset, payload, sourceSessionKey, ts, title, modelId, workDir) {
|
|
272
|
-
const itemType = typeof payload.type === 'string' ? payload.type : '';
|
|
273
|
-
if (!itemType)
|
|
274
|
-
return;
|
|
275
|
-
if (itemType === 'message') {
|
|
276
|
-
const parsedMessage = parseCodexResponseMessage(payload);
|
|
277
|
-
if (!parsedMessage)
|
|
278
|
-
return;
|
|
279
|
-
const duplicateIndex = findDuplicateCodexChatEventIndex(events, parsedMessage.role, parsedMessage.payload, ts);
|
|
280
|
-
if (duplicateIndex >= 0)
|
|
281
|
-
return;
|
|
282
|
-
pushCodexEvent(events, filePath, lineOffset, `${itemType}:${parsedMessage.role}`, sourceSessionKey, ts, parsedMessage.payload, title, modelId, workDir, parsedMessage.role, false, parsedMessage.repairHint);
|
|
283
|
-
return;
|
|
284
|
-
}
|
|
285
|
-
if (itemType === 'function_call') {
|
|
286
|
-
const name = typeof payload.name === 'string' ? payload.name : 'function_call';
|
|
287
|
-
pushCodexToolEvent(events, filePath, lineOffset, itemType, sourceSessionKey, ts, name, title, modelId, workDir, parseStructuredString(payload.arguments));
|
|
288
|
-
return;
|
|
289
|
-
}
|
|
290
|
-
if (itemType === 'function_call_output') {
|
|
291
|
-
pushCodexToolEvent(events, filePath, lineOffset, itemType, sourceSessionKey, ts, 'function_call_output', title, modelId, workDir, payload.call_id, payload.output);
|
|
292
|
-
return;
|
|
293
|
-
}
|
|
294
|
-
if (itemType === 'custom_tool_call') {
|
|
295
|
-
const name = typeof payload.name === 'string' ? payload.name : 'custom_tool_call';
|
|
296
|
-
const args = payload.input !== undefined ? { input: payload.input } : undefined;
|
|
297
|
-
pushCodexToolEvent(events, filePath, lineOffset, itemType, sourceSessionKey, ts, name, title, modelId, workDir, args);
|
|
298
|
-
return;
|
|
299
|
-
}
|
|
300
|
-
if (itemType === 'custom_tool_call_output') {
|
|
301
|
-
const name = typeof payload.name === 'string' ? payload.name : 'custom_tool_call_output';
|
|
302
|
-
const result = payload.output ?? payload.result ?? payload.content;
|
|
303
|
-
pushCodexToolEvent(events, filePath, lineOffset, itemType, sourceSessionKey, ts, name, title, modelId, workDir, payload.call_id, result);
|
|
304
|
-
return;
|
|
305
|
-
}
|
|
306
|
-
if (itemType === 'web_search_call') {
|
|
307
|
-
const action = typeof payload.action === 'object' && payload.action !== null
|
|
308
|
-
? payload.action
|
|
309
|
-
: null;
|
|
310
|
-
pushCodexToolEvent(events, filePath, lineOffset, itemType, sourceSessionKey, ts, 'web_search', title, modelId, workDir, action ?? payload.query);
|
|
311
|
-
return;
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
function parseCodexEventMessage(events, filePath, lineOffset, payload, sourceSessionKey, ts, title, modelId, workDir) {
|
|
315
|
-
const eventType = typeof payload.type === 'string' ? payload.type : '';
|
|
316
|
-
if (!eventType)
|
|
317
|
-
return;
|
|
318
|
-
if (eventType === 'user_message') {
|
|
319
|
-
const parsedUser = parseCodexUserMessage(payload);
|
|
320
|
-
if (!parsedUser)
|
|
321
|
-
return;
|
|
322
|
-
pushCodexEvent(events, filePath, lineOffset, eventType, sourceSessionKey, ts, parsedUser.payload, title, modelId, workDir, 'user', false, parsedUser.repairHint);
|
|
323
|
-
return;
|
|
324
|
-
}
|
|
325
|
-
if (eventType === 'agent_message') {
|
|
326
|
-
const text = typeof payload.message === 'string' ? normalizeText(payload.message) : '';
|
|
327
|
-
if (!text || isSystemControlPayload(text))
|
|
328
|
-
return;
|
|
329
|
-
pushCodexEvent(events, filePath, lineOffset, eventType, sourceSessionKey, ts, text, title, modelId, workDir);
|
|
330
|
-
return;
|
|
331
|
-
}
|
|
332
|
-
if (eventType === 'turn_aborted' || eventType === 'error') {
|
|
333
|
-
const text = typeof payload.message === 'string'
|
|
334
|
-
? normalizeText(payload.message)
|
|
335
|
-
: typeof payload.error === 'string'
|
|
336
|
-
? normalizeText(payload.error)
|
|
337
|
-
: '';
|
|
338
|
-
if (!text)
|
|
339
|
-
return;
|
|
340
|
-
pushCodexEvent(events, filePath, lineOffset, eventType, sourceSessionKey, ts, text, title, modelId, workDir, 'agent', true);
|
|
341
|
-
return;
|
|
342
|
-
}
|
|
343
|
-
if (isCodexApprovalEventType(eventType)) {
|
|
344
|
-
pushCodexEvent(events, filePath, lineOffset, eventType, sourceSessionKey, ts, buildCodexApprovalPayload(payload), title, modelId, workDir, 'agent');
|
|
345
|
-
return;
|
|
346
|
-
}
|
|
347
|
-
if (eventType === 'exec_command_end') {
|
|
348
|
-
const command = Array.isArray(payload.command) ? payload.command : payload.parsed_cmd ?? payload.command;
|
|
349
|
-
const exitCode = payload.exit_code ?? payload.exitCode;
|
|
350
|
-
const output = [
|
|
351
|
-
typeof payload.aggregated_output === 'string' ? payload.aggregated_output : '',
|
|
352
|
-
exitCode != null ? `\n(exit ${String(exitCode)})` : '',
|
|
353
|
-
].join('');
|
|
354
|
-
pushCodexToolEvent(events, filePath, lineOffset, eventType, sourceSessionKey, ts, 'command_execution', title, modelId, workDir, { command }, output);
|
|
355
|
-
return;
|
|
356
|
-
}
|
|
357
|
-
if (eventType === 'patch_apply_end') {
|
|
358
|
-
pushCodexToolEvent(events, filePath, lineOffset, eventType, sourceSessionKey, ts, 'apply_patch', title, modelId, workDir, payload.changes, {
|
|
359
|
-
success: payload.success,
|
|
360
|
-
stdout: payload.stdout,
|
|
361
|
-
stderr: payload.stderr,
|
|
362
|
-
});
|
|
363
|
-
return;
|
|
364
|
-
}
|
|
365
|
-
if (eventType === 'mcp_tool_call_end') {
|
|
366
|
-
const invocation = typeof payload.invocation === 'object' && payload.invocation !== null
|
|
367
|
-
? payload.invocation
|
|
368
|
-
: null;
|
|
369
|
-
const tool = typeof invocation?.tool === 'string' ? invocation.tool : 'mcp_tool_call';
|
|
370
|
-
const args = {
|
|
371
|
-
server: invocation?.server,
|
|
372
|
-
...(typeof invocation?.arguments === 'object' && invocation.arguments !== null
|
|
373
|
-
? invocation.arguments
|
|
374
|
-
: {}),
|
|
375
|
-
};
|
|
376
|
-
pushCodexToolEvent(events, filePath, lineOffset, eventType, sourceSessionKey, ts, tool, title, modelId, workDir, args, payload.result);
|
|
377
|
-
return;
|
|
378
|
-
}
|
|
379
|
-
if (eventType === 'dynamic_tool_call_request') {
|
|
380
|
-
const tool = typeof payload.tool === 'string' ? payload.tool : 'dynamic_tool_call';
|
|
381
|
-
pushCodexToolEvent(events, filePath, lineOffset, eventType, sourceSessionKey, ts, tool, title, modelId, workDir, payload.arguments);
|
|
382
|
-
return;
|
|
383
|
-
}
|
|
384
|
-
if (eventType === 'dynamic_tool_call_response') {
|
|
385
|
-
const tool = typeof payload.tool === 'string' ? payload.tool : 'dynamic_tool_call';
|
|
386
|
-
pushCodexToolEvent(events, filePath, lineOffset, eventType, sourceSessionKey, ts, tool, title, modelId, workDir, payload.callId ?? payload.call_id, payload.result ?? payload.output);
|
|
387
|
-
return;
|
|
388
|
-
}
|
|
389
|
-
if (eventType === 'web_search_end') {
|
|
390
|
-
const action = typeof payload.action === 'object' && payload.action !== null
|
|
391
|
-
? payload.action
|
|
392
|
-
: null;
|
|
393
|
-
pushCodexToolEvent(events, filePath, lineOffset, eventType, sourceSessionKey, ts, 'web_search', title, modelId, workDir, action ?? payload.query, payload.query);
|
|
394
|
-
return;
|
|
395
|
-
}
|
|
396
|
-
if (eventType === 'image_generation_end') {
|
|
397
|
-
const savedPath = typeof payload.saved_path === 'string' ? payload.saved_path.trim() : '';
|
|
398
|
-
if (!savedPath)
|
|
399
|
-
return;
|
|
400
|
-
pushCodexEvent(events, filePath, lineOffset, eventType, sourceSessionKey, ts, buildImagePayload(savedPath), title, modelId, workDir);
|
|
401
|
-
return;
|
|
402
|
-
}
|
|
403
|
-
if (eventType === 'view_image_tool_call') {
|
|
404
|
-
pushCodexToolEvent(events, filePath, lineOffset, eventType, sourceSessionKey, ts, 'view_image', title, modelId, workDir, payload.path ?? payload.image_path ?? payload);
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
function relativeProjectDir(cwd) {
|
|
408
|
-
const home = os.homedir();
|
|
409
|
-
if (cwd.startsWith(home))
|
|
410
|
-
return cwd.slice(home.length).replace(/^\//, '') || '~';
|
|
411
|
-
return cwd.replace(/\//g, '-');
|
|
412
|
-
}
|
|
413
|
-
function readCodexSessionMeta(filePath) {
|
|
414
|
-
const fd = fs.openSync(filePath, 'r');
|
|
415
|
-
try {
|
|
416
|
-
const chunks = [];
|
|
417
|
-
let position = 0;
|
|
418
|
-
let firstLine = '';
|
|
419
|
-
while (position < 1024 * 1024) {
|
|
420
|
-
const buffer = Buffer.alloc(16 * 1024);
|
|
421
|
-
const bytesRead = fs.readSync(fd, buffer, 0, buffer.length, position);
|
|
422
|
-
if (bytesRead <= 0)
|
|
423
|
-
break;
|
|
424
|
-
const chunk = buffer.subarray(0, bytesRead);
|
|
425
|
-
chunks.push(chunk);
|
|
426
|
-
position += bytesRead;
|
|
427
|
-
const combined = Buffer.concat(chunks);
|
|
428
|
-
const newlineIndex = combined.indexOf(0x0a);
|
|
429
|
-
if (newlineIndex >= 0) {
|
|
430
|
-
firstLine = combined.subarray(0, newlineIndex).toString('utf8');
|
|
431
|
-
break;
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
if (!firstLine && chunks.length > 0) {
|
|
435
|
-
firstLine = Buffer.concat(chunks).toString('utf8');
|
|
436
|
-
}
|
|
437
|
-
const parsed = safeParse(firstLine);
|
|
438
|
-
if (!parsed || parsed.type !== 'session_meta') {
|
|
439
|
-
return {
|
|
440
|
-
sourceSessionKey: '',
|
|
441
|
-
workDir: '',
|
|
442
|
-
modelId: null,
|
|
443
|
-
title: '',
|
|
444
|
-
};
|
|
445
|
-
}
|
|
446
|
-
const payload = typeof parsed.payload === 'object' && parsed.payload !== null
|
|
447
|
-
? parsed.payload
|
|
448
|
-
: null;
|
|
449
|
-
if (!payload) {
|
|
450
|
-
return {
|
|
451
|
-
sourceSessionKey: '',
|
|
452
|
-
workDir: '',
|
|
453
|
-
modelId: null,
|
|
454
|
-
title: '',
|
|
455
|
-
};
|
|
456
|
-
}
|
|
457
|
-
const sourceSessionKey = typeof payload.id === 'string' ? payload.id : '';
|
|
458
|
-
return {
|
|
459
|
-
sourceSessionKey,
|
|
460
|
-
workDir: typeof payload.cwd === 'string' ? payload.cwd : '',
|
|
461
|
-
modelId: readCodexModelId(payload),
|
|
462
|
-
title: lookupCodexThreadName(sourceSessionKey) ?? '',
|
|
463
|
-
};
|
|
464
|
-
}
|
|
465
|
-
finally {
|
|
466
|
-
fs.closeSync(fd);
|
|
467
|
-
}
|
|
468
|
-
return {
|
|
469
|
-
sourceSessionKey: '',
|
|
470
|
-
workDir: '',
|
|
471
|
-
modelId: null,
|
|
472
|
-
title: '',
|
|
473
|
-
};
|
|
474
|
-
}
|
|
475
|
-
function readCodexModelId(payload) {
|
|
476
|
-
const model = typeof payload.model === 'string' ? payload.model.trim() : '';
|
|
477
|
-
return model || null;
|
|
478
|
-
}
|
|
479
|
-
function readCodexIndexTitle(parsed, sourceSessionKey) {
|
|
480
|
-
const id = typeof parsed.id === 'string'
|
|
481
|
-
? parsed.id
|
|
482
|
-
: typeof parsed.thread_id === 'string'
|
|
483
|
-
? parsed.thread_id
|
|
484
|
-
: typeof parsed.session_id === 'string'
|
|
485
|
-
? parsed.session_id
|
|
486
|
-
: '';
|
|
487
|
-
if (id !== sourceSessionKey)
|
|
488
|
-
return null;
|
|
489
|
-
const title = typeof parsed.thread_name === 'string'
|
|
490
|
-
? parsed.thread_name
|
|
491
|
-
: typeof parsed.title === 'string'
|
|
492
|
-
? parsed.title
|
|
493
|
-
: '';
|
|
494
|
-
const trimmed = title.trim();
|
|
495
|
-
return trimmed ? trimmed.slice(0, 120) : null;
|
|
496
|
-
}
|
|
497
|
-
function lookupCodexThreadName(sourceSessionKey) {
|
|
498
|
-
if (!sourceSessionKey)
|
|
499
|
-
return null;
|
|
500
|
-
const indexPath = path.join(os.homedir(), '.codex', 'session_index.jsonl');
|
|
501
|
-
if (!fs.existsSync(indexPath))
|
|
502
|
-
return null;
|
|
503
|
-
let latestTitle = null;
|
|
504
|
-
readJsonlLines(indexPath, 0, (line) => {
|
|
505
|
-
if (!line.trim())
|
|
506
|
-
return;
|
|
507
|
-
const parsed = safeParse(line);
|
|
508
|
-
if (!parsed)
|
|
509
|
-
return;
|
|
510
|
-
const title = readCodexIndexTitle(parsed, sourceSessionKey);
|
|
511
|
-
if (title)
|
|
512
|
-
latestTitle = title;
|
|
513
|
-
});
|
|
514
|
-
return latestTitle;
|
|
515
|
-
}
|
|
516
|
-
export function listCodexRolloutFiles() {
|
|
517
|
-
const roots = [
|
|
518
|
-
path.join(os.homedir(), '.codex', 'sessions'),
|
|
519
|
-
path.join(os.homedir(), '.codex', 'archived_sessions'),
|
|
520
|
-
];
|
|
521
|
-
const files = [];
|
|
522
|
-
const walk = (dir) => {
|
|
523
|
-
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
524
|
-
const full = path.join(dir, entry.name);
|
|
525
|
-
if (entry.isDirectory())
|
|
526
|
-
walk(full);
|
|
527
|
-
else if (entry.isFile() && entry.name.endsWith('.jsonl'))
|
|
528
|
-
files.push(full);
|
|
529
|
-
}
|
|
530
|
-
};
|
|
531
|
-
for (const root of roots) {
|
|
532
|
-
if (fs.existsSync(root))
|
|
533
|
-
walk(root);
|
|
534
|
-
}
|
|
535
|
-
return [...new Set(files)].sort();
|
|
536
|
-
}
|
|
537
|
-
export function listClaudeTranscriptFiles() {
|
|
538
|
-
const root = path.join(os.homedir(), '.claude', 'projects');
|
|
539
|
-
if (!fs.existsSync(root))
|
|
540
|
-
return [];
|
|
541
|
-
const files = [];
|
|
542
|
-
const walk = (dir) => {
|
|
543
|
-
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
544
|
-
const full = path.join(dir, entry.name);
|
|
545
|
-
if (entry.isDirectory()) {
|
|
546
|
-
if (dir === root && shouldIgnoreClaudeProjectDir(entry.name))
|
|
547
|
-
continue;
|
|
548
|
-
walk(full);
|
|
549
|
-
}
|
|
550
|
-
else if (entry.isFile() && entry.name.endsWith('.jsonl'))
|
|
551
|
-
files.push(full);
|
|
552
|
-
}
|
|
553
|
-
};
|
|
554
|
-
walk(root);
|
|
555
|
-
return files.sort();
|
|
556
|
-
}
|
|
557
|
-
export function lookupClaudeTranscriptCwd(sourceSessionKey) {
|
|
558
|
-
if (!sourceSessionKey)
|
|
559
|
-
return null;
|
|
560
|
-
const match = listClaudeTranscriptFiles().find((filePath) => path.basename(filePath, '.jsonl') === sourceSessionKey);
|
|
561
|
-
if (!match)
|
|
562
|
-
return null;
|
|
563
|
-
let found = null;
|
|
564
|
-
readJsonlLines(match, 0, (line) => {
|
|
565
|
-
if (found || !line.trim())
|
|
566
|
-
return;
|
|
567
|
-
const parsed = safeParse(line);
|
|
568
|
-
if (!parsed)
|
|
569
|
-
return;
|
|
570
|
-
const cwd = readClaudeEventCwd(parsed);
|
|
571
|
-
if (cwd)
|
|
572
|
-
found = cwd;
|
|
573
|
-
});
|
|
574
|
-
return found;
|
|
575
|
-
}
|
|
576
|
-
export function parseCodexRolloutChunk(filePath, startOffset) {
|
|
577
|
-
const events = [];
|
|
578
|
-
let { sourceSessionKey, workDir, modelId, title } = readCodexSessionMeta(filePath);
|
|
579
|
-
let pendingTerminal = false;
|
|
580
|
-
const nextOffset = readJsonlLines(filePath, startOffset, (line, lineOffset) => {
|
|
581
|
-
const parsed = safeParse(line);
|
|
582
|
-
if (!parsed)
|
|
583
|
-
return;
|
|
584
|
-
const type = typeof parsed.type === 'string' ? parsed.type : '';
|
|
585
|
-
const payload = typeof parsed.payload === 'object' && parsed.payload !== null
|
|
586
|
-
? parsed.payload
|
|
587
|
-
: null;
|
|
588
|
-
const ts = readTimestamp(parsed.timestamp);
|
|
589
|
-
if (!payload || !ts)
|
|
590
|
-
return;
|
|
591
|
-
if (type === 'session_meta') {
|
|
592
|
-
sourceSessionKey = typeof payload.id === 'string' ? payload.id : sourceSessionKey;
|
|
593
|
-
workDir = typeof payload.cwd === 'string' ? payload.cwd : workDir;
|
|
594
|
-
modelId = readCodexModelId(payload) ?? modelId;
|
|
595
|
-
if (!title)
|
|
596
|
-
title = lookupCodexThreadName(sourceSessionKey) ?? '';
|
|
597
|
-
return;
|
|
598
|
-
}
|
|
599
|
-
if (!sourceSessionKey)
|
|
600
|
-
return;
|
|
601
|
-
if (type === 'response_item') {
|
|
602
|
-
const parsedMessage = parseCodexResponseMessage(payload);
|
|
603
|
-
if (parsedMessage?.role === 'user' && parsedMessage.titleText && !title) {
|
|
604
|
-
title = parsedMessage.titleText.slice(0, 80);
|
|
605
|
-
}
|
|
606
|
-
parseCodexResponseItem(events, filePath, lineOffset, payload, sourceSessionKey, ts, title, modelId, workDir);
|
|
607
|
-
return;
|
|
608
|
-
}
|
|
609
|
-
if (type === 'event_msg') {
|
|
610
|
-
const eventType = typeof payload.type === 'string' ? payload.type : '';
|
|
611
|
-
if (isCodexTerminalEventType(eventType)) {
|
|
612
|
-
for (let i = events.length - 1; i >= 0; i -= 1) {
|
|
613
|
-
const event = events[i];
|
|
614
|
-
if (event?.agentType !== 'codex')
|
|
615
|
-
continue;
|
|
616
|
-
if (event.role !== 'agent')
|
|
617
|
-
continue;
|
|
618
|
-
if (isToolPayload(event.payload))
|
|
619
|
-
continue;
|
|
620
|
-
event.terminal = true;
|
|
621
|
-
pendingTerminal = false;
|
|
622
|
-
break;
|
|
623
|
-
}
|
|
624
|
-
return;
|
|
625
|
-
}
|
|
626
|
-
if (eventType === 'user_message') {
|
|
627
|
-
const parsedUser = parseCodexUserMessage(payload);
|
|
628
|
-
if (parsedUser?.titleText && !title)
|
|
629
|
-
title = parsedUser.titleText.slice(0, 80);
|
|
630
|
-
if (parsedUser) {
|
|
631
|
-
const duplicateIndex = findDuplicateCodexChatEventIndex(events, 'user', parsedUser.payload, ts);
|
|
632
|
-
if (duplicateIndex >= 0)
|
|
633
|
-
events.splice(duplicateIndex, 1);
|
|
634
|
-
}
|
|
635
|
-
}
|
|
636
|
-
else if (eventType === 'agent_message') {
|
|
637
|
-
const text = typeof payload.message === 'string' ? normalizeText(payload.message) : '';
|
|
638
|
-
if (text && !isSystemControlPayload(text)) {
|
|
639
|
-
const duplicateIndex = findDuplicateCodexChatEventIndex(events, 'agent', text, ts);
|
|
640
|
-
if (duplicateIndex >= 0)
|
|
641
|
-
events.splice(duplicateIndex, 1);
|
|
642
|
-
}
|
|
643
|
-
}
|
|
644
|
-
const beforeCount = events.length;
|
|
645
|
-
parseCodexEventMessage(events, filePath, lineOffset, payload, sourceSessionKey, ts, title, modelId, workDir);
|
|
646
|
-
if (pendingTerminal && events.length > beforeCount) {
|
|
647
|
-
events[events.length - 1].terminal = true;
|
|
648
|
-
pendingTerminal = false;
|
|
649
|
-
}
|
|
650
|
-
}
|
|
651
|
-
});
|
|
652
|
-
return { nextOffset, events };
|
|
653
|
-
}
|
|
654
|
-
export function parseClaudeTranscriptChunk(filePath, startOffset) {
|
|
655
|
-
const fileSize = fs.statSync(filePath).size;
|
|
656
|
-
if (isClaudeSubagentTranscript(filePath) || shouldIgnoreClaudeTranscriptPath(filePath)) {
|
|
657
|
-
return { nextOffset: fileSize, events: [] };
|
|
658
|
-
}
|
|
659
|
-
const events = [];
|
|
660
|
-
const sourceSessionKey = path.basename(filePath, '.jsonl');
|
|
661
|
-
const fallbackWorkDir = relativeProjectDir(path.dirname(filePath).replace(path.join(os.homedir(), '.claude', 'projects') + path.sep, ''));
|
|
662
|
-
if (shouldIgnoreNativeScanPath(fallbackWorkDir)) {
|
|
663
|
-
return { nextOffset: fileSize, events: [] };
|
|
664
|
-
}
|
|
665
|
-
let title = '';
|
|
666
|
-
const nextOffset = readJsonlLines(filePath, startOffset, (line, lineOffset) => {
|
|
667
|
-
const parsed = safeParse(line);
|
|
668
|
-
if (!parsed)
|
|
669
|
-
return;
|
|
670
|
-
const ts = readTimestamp(parsed.timestamp);
|
|
671
|
-
const type = typeof parsed.type === 'string' ? parsed.type : '';
|
|
672
|
-
if (!ts || !type)
|
|
673
|
-
return;
|
|
674
|
-
const eventCwd = readClaudeEventCwd(parsed);
|
|
675
|
-
if (eventCwd && shouldIgnoreNativeScanPath(eventCwd))
|
|
676
|
-
return;
|
|
677
|
-
if (type === 'user') {
|
|
678
|
-
const message = typeof parsed.message === 'object' && parsed.message !== null
|
|
679
|
-
? parsed.message
|
|
680
|
-
: null;
|
|
681
|
-
const text = typeof message?.content === 'string' ? normalizeText(message.content) : '';
|
|
682
|
-
if (!text)
|
|
683
|
-
return;
|
|
684
|
-
if (!title)
|
|
685
|
-
title = text.slice(0, 80);
|
|
686
|
-
events.push({
|
|
687
|
-
agentType: 'claude',
|
|
688
|
-
sourceMode: 'claude_transcript_import',
|
|
689
|
-
sourceSessionKey,
|
|
690
|
-
sourceEventKey: makeEventKey(filePath, lineOffset, 'user'),
|
|
691
|
-
cursor: makeCursor(filePath, lineOffset),
|
|
692
|
-
role: 'user',
|
|
693
|
-
ts,
|
|
694
|
-
payload: text,
|
|
695
|
-
title,
|
|
696
|
-
workDir: eventCwd ?? fallbackWorkDir,
|
|
697
|
-
});
|
|
698
|
-
}
|
|
699
|
-
else if (type === 'assistant') {
|
|
700
|
-
const message = typeof parsed.message === 'object' && parsed.message !== null
|
|
701
|
-
? parsed.message
|
|
702
|
-
: null;
|
|
703
|
-
const content = Array.isArray(message?.content) ? message.content : [];
|
|
704
|
-
const text = normalizeText(content
|
|
705
|
-
.map((part) => typeof part === 'object' && part !== null && typeof part.text === 'string'
|
|
706
|
-
? part.text
|
|
707
|
-
: '')
|
|
708
|
-
.filter(Boolean)
|
|
709
|
-
.join('\n\n'));
|
|
710
|
-
if (!text)
|
|
711
|
-
return;
|
|
712
|
-
const modelId = typeof message?.model === 'string' ? message.model : null;
|
|
713
|
-
events.push({
|
|
714
|
-
agentType: 'claude',
|
|
715
|
-
sourceMode: 'claude_transcript_import',
|
|
716
|
-
sourceSessionKey,
|
|
717
|
-
sourceEventKey: makeEventKey(filePath, lineOffset, 'assistant'),
|
|
718
|
-
cursor: makeCursor(filePath, lineOffset),
|
|
719
|
-
role: 'agent',
|
|
720
|
-
ts,
|
|
721
|
-
payload: text,
|
|
722
|
-
title,
|
|
723
|
-
modelId,
|
|
724
|
-
workDir: eventCwd ?? fallbackWorkDir,
|
|
725
|
-
});
|
|
726
|
-
}
|
|
727
|
-
});
|
|
728
|
-
return { nextOffset, events };
|
|
729
|
-
}
|
|
1
|
+
import T from"node:fs";import j from"node:os";import x from"node:path";import{buildApprovalPendingPayload as V,buildUserMessagePayload as k,isSystemControlPayload as A,isToolPayload as F}from"@shennian/wire";import{listOpenCodeSessionFiles as jt,parseOpenCodeSessionFile as At,parseOpenCodeSessionSnapshot as Mt}from"./opencode-parser.js";import{buildImagePayload as X,buildToolPayload as Y,inferMimeType as E,makeCursor as I,makeEventKey as w,normalizeText as h,parseStructuredString as Z,readClaudeEventCwd as N,readJsonlLines as v,readTimestamp as q,safeParse as M,isClaudeSubagentTranscript as D,shouldIgnoreClaudeProjectDir as K,shouldIgnoreClaudeTranscriptPath as O,shouldIgnoreNativeScanPath as R}from"./parser-common.js";function P(e){if(typeof e=="string"){const s=e.trim();return s?{path:s,name:x.basename(s)||"image.png",mimeType:E(s),kind:"image"}:null}if(typeof e!="object"||e===null)return null;const r=e,n=typeof r.path=="string"?r.path:typeof r.file_path=="string"?r.file_path:typeof r.saved_path=="string"?r.saved_path:"";if(!n.trim())return null;const t=typeof r.name=="string"&&r.name.trim()?r.name:x.basename(n)||"image.png",o=typeof r.mimeType=="string"&&r.mimeType.trim()?r.mimeType:E(n);return{path:n,name:t,mimeType:o,kind:"image"}}function U(e){return Array.isArray(e)?e.map(n=>{if(typeof n=="string")return n;if(typeof n=="object"&&n!==null){const t=n;return typeof t.text=="string"?t.text:""}return""}).map(n=>h(n)).filter(Boolean).join(`
|
|
2
|
+
|
|
3
|
+
`):""}function B(e){const r=h(e);if(!r)return{text:"",strippedWrapper:!1};const n=r.match(/My request for Codex:\s*([\s\S]*)$/i);return n&&n[1]?{text:tt(n[1]),strippedWrapper:!0}:{text:r,strippedWrapper:!1}}function tt(e){return h(e.replace(/<image>\s*<\/image>/gi,""))}function z(e){const r=h(e);if(!r)return!1;const n=r.startsWith("# AGENTS.md instructions for ")&&r.includes("<INSTRUCTIONS>")&&r.includes("</INSTRUCTIONS>"),t=r.includes("<environment_context>")&&r.includes("</environment_context>");return n&&t}function H(e){const r=U(e.text_elements),n=Array.isArray(e.input)?U(e.input):"",t=typeof e.message=="string"?B(e.message):{text:"",strippedWrapper:!1},o=t.text,s=r||n||o,u=t.strippedWrapper?"codex_app_context_wrapper":void 0,a=Array.isArray(e.local_images)?e.local_images.map(P).filter(l=>l!=null):[];return!s&&a.length===0||A(s)&&a.length===0||z(s)&&a.length===0?null:a.length>0?{payload:k(s,a),titleText:s,repairHint:u}:{payload:s,titleText:s,repairHint:u}}function et(e){return e==="turn_completed"||e==="turn_complete"||e==="task_complete"}function nt(e){return/approval|permission|confirm|user[_-]?input|input[_-]?required/i.test(e)}function rt(e){const r=typeof e.command=="string"?e.command:Array.isArray(e.command)?e.command.join(" "):"",n=typeof e.prompt=="string"?e.prompt:typeof e.reason=="string"?e.reason:typeof e.message=="string"?e.message:r?`Codex \u8BF7\u6C42\u6267\u884C\u547D\u4EE4\uFF1A${r}`:"Codex \u6B63\u5728\u7B49\u5F85\u7528\u6237\u786E\u8BA4\u540E\u7EE7\u7EED\u3002";return V({title:"\u9700\u8981\u7528\u6237\u786E\u8BA4",content:n,source:"codex",actionHint:"\u5F53\u524D\u795E\u5FF5\u6682\u4E0D\u652F\u6301\u8FDC\u7A0B\u786E\u8BA4\uFF0C\u8BF7\u56DE\u5230\u8FD0\u884C Codex \u7684\u7535\u8111/\u7EC8\u7AEF\u5B8C\u6210\u786E\u8BA4\u3002"})}function $(e,r){return Array.isArray(e)?h(e.map(n=>{if(typeof n=="string")return n;if(typeof n!="object"||n===null)return"";const t=n;return t.type!==r?"":typeof t.text=="string"?t.text:""}).filter(Boolean).join(`
|
|
4
|
+
|
|
5
|
+
`)):""}function ot(e){if(!Array.isArray(e))return[];const r=[];for(const n of e){if(typeof n!="object"||n===null)continue;const t=n;if(t.type!=="input_image")continue;const o=typeof t.path=="string"?t.path:typeof t.file_path=="string"?t.file_path:typeof t.saved_path=="string"?t.saved_path:typeof t.image_path=="string"?t.image_path:"";o.trim()&&r.push({path:o,name:x.basename(o)||"image.png",mimeType:E(o),kind:"image"})}return r}function L(e){if(e.type!=="message")return null;const r=e.role==="assistant"?"agent":e.role==="user"?"user":null;if(!r)return null;if(r==="agent"){const s=$(e.content,"output_text");return A(s)?null:s?{role:r,payload:s,titleText:s}:null}const n=B($(e.content,"input_text")),t=n.text,o=ot(e.content);return!t&&o.length===0||A(t)&&o.length===0||z(t)&&o.length===0?null:{role:r,payload:o.length>0?k(t,o):t,titleText:t,repairHint:n.strippedWrapper?"codex_app_context_wrapper":void 0}}function J(e){if(!e)return"";try{const r=JSON.parse(e);if(typeof r=="object"&&r!==null){const n=r;if(n.type==="user"&&typeof n.content=="string")return h(n.content)}}catch{}return h(e)}function st(e,r,n,t){return e.agentType!=="codex"||e.role!==r||F(e.payload)||Math.abs(e.ts-t)>300*1e3?!1:J(e.payload)===J(n)}function W(e,r,n,t){for(let o=e.length-1;o>=0;o-=1){const s=e[o];if(s){if(st(s,r,n,t))return o;if(s.agentType==="codex"&&Math.abs(s.ts-t)>300*1e3)break}}return-1}function b(e,r,n,t,o,s,u,a,l,c,i="agent",f=!1,m){u&&e.push({agentType:"codex",sourceMode:"codex_rollout_import",sourceSessionKey:o,sourceEventKey:w(r,n,t),cursor:I(r,n),role:i,ts:s,payload:u,title:a,modelId:l,workDir:c,terminal:f,repairHint:m})}function C(e,r,n,t,o,s,u,a,l,c,i,f){b(e,r,n,t,o,s,Y(u,i,f),a,l,c)}function it(e,r,n,t,o,s,u,a,l){const c=typeof t.type=="string"?t.type:"";if(c){if(c==="message"){const i=L(t);if(!i||W(e,i.role,i.payload,s)>=0)return;b(e,r,n,`${c}:${i.role}`,o,s,i.payload,u,a,l,i.role,!1,i.repairHint);return}if(c==="function_call"){const i=typeof t.name=="string"?t.name:"function_call";C(e,r,n,c,o,s,i,u,a,l,Z(t.arguments));return}if(c==="function_call_output"){C(e,r,n,c,o,s,"function_call_output",u,a,l,t.call_id,t.output);return}if(c==="custom_tool_call"){const i=typeof t.name=="string"?t.name:"custom_tool_call",f=t.input!==void 0?{input:t.input}:void 0;C(e,r,n,c,o,s,i,u,a,l,f);return}if(c==="custom_tool_call_output"){const i=typeof t.name=="string"?t.name:"custom_tool_call_output",f=t.output??t.result??t.content;C(e,r,n,c,o,s,i,u,a,l,t.call_id,f);return}if(c==="web_search_call"){const i=typeof t.action=="object"&&t.action!==null?t.action:null;C(e,r,n,c,o,s,"web_search",u,a,l,i??t.query);return}}}function ct(e,r,n,t,o,s,u,a,l){const c=typeof t.type=="string"?t.type:"";if(c){if(c==="user_message"){const i=H(t);if(!i)return;b(e,r,n,c,o,s,i.payload,u,a,l,"user",!1,i.repairHint);return}if(c==="agent_message"){const i=typeof t.message=="string"?h(t.message):"";if(!i||A(i))return;b(e,r,n,c,o,s,i,u,a,l);return}if(c==="turn_aborted"||c==="error"){const i=typeof t.message=="string"?h(t.message):typeof t.error=="string"?h(t.error):"";if(!i)return;b(e,r,n,c,o,s,i,u,a,l,"agent",!0);return}if(nt(c)){b(e,r,n,c,o,s,rt(t),u,a,l,"agent");return}if(c==="exec_command_end"){const i=Array.isArray(t.command)?t.command:t.parsed_cmd??t.command,f=t.exit_code??t.exitCode,m=[typeof t.aggregated_output=="string"?t.aggregated_output:"",f!=null?`
|
|
6
|
+
(exit ${String(f)})`:""].join("");C(e,r,n,c,o,s,"command_execution",u,a,l,{command:i},m);return}if(c==="patch_apply_end"){C(e,r,n,c,o,s,"apply_patch",u,a,l,t.changes,{success:t.success,stdout:t.stdout,stderr:t.stderr});return}if(c==="mcp_tool_call_end"){const i=typeof t.invocation=="object"&&t.invocation!==null?t.invocation:null,f=typeof i?.tool=="string"?i.tool:"mcp_tool_call",m={server:i?.server,...typeof i?.arguments=="object"&&i.arguments!==null?i.arguments:{}};C(e,r,n,c,o,s,f,u,a,l,m,t.result);return}if(c==="dynamic_tool_call_request"){const i=typeof t.tool=="string"?t.tool:"dynamic_tool_call";C(e,r,n,c,o,s,i,u,a,l,t.arguments);return}if(c==="dynamic_tool_call_response"){const i=typeof t.tool=="string"?t.tool:"dynamic_tool_call";C(e,r,n,c,o,s,i,u,a,l,t.callId??t.call_id,t.result??t.output);return}if(c==="web_search_end"){const i=typeof t.action=="object"&&t.action!==null?t.action:null;C(e,r,n,c,o,s,"web_search",u,a,l,i??t.query,t.query);return}if(c==="image_generation_end"){const i=typeof t.saved_path=="string"?t.saved_path.trim():"";if(!i)return;b(e,r,n,c,o,s,X(i),u,a,l);return}c==="view_image_tool_call"&&C(e,r,n,c,o,s,"view_image",u,a,l,t.path??t.image_path??t)}}function ut(e){const r=j.homedir();return e.startsWith(r)?e.slice(r.length).replace(/^\//,"")||"~":e.replace(/\//g,"-")}function at(e){const r=T.openSync(e,"r");try{const n=[];let t=0,o="";for(;t<1024*1024;){const l=Buffer.alloc(16384),c=T.readSync(r,l,0,l.length,t);if(c<=0)break;const i=l.subarray(0,c);n.push(i),t+=c;const f=Buffer.concat(n),m=f.indexOf(10);if(m>=0){o=f.subarray(0,m).toString("utf8");break}}!o&&n.length>0&&(o=Buffer.concat(n).toString("utf8"));const s=M(o);if(!s||s.type!=="session_meta")return{sourceSessionKey:"",workDir:"",modelId:null,title:""};const u=typeof s.payload=="object"&&s.payload!==null?s.payload:null;if(!u)return{sourceSessionKey:"",workDir:"",modelId:null,title:""};const a=typeof u.id=="string"?u.id:"";return{sourceSessionKey:a,workDir:typeof u.cwd=="string"?u.cwd:"",modelId:G(u),title:Q(a)??""}}finally{T.closeSync(r)}return{sourceSessionKey:"",workDir:"",modelId:null,title:""}}function G(e){return(typeof e.model=="string"?e.model.trim():"")||null}function lt(e,r){if((typeof e.id=="string"?e.id:typeof e.thread_id=="string"?e.thread_id:typeof e.session_id=="string"?e.session_id:"")!==r)return null;const o=(typeof e.thread_name=="string"?e.thread_name:typeof e.title=="string"?e.title:"").trim();return o?o.slice(0,120):null}function Q(e){if(!e)return null;const r=x.join(j.homedir(),".codex","session_index.jsonl");if(!T.existsSync(r))return null;let n=null;return v(r,0,t=>{if(!t.trim())return;const o=M(t);if(!o)return;const s=lt(o,e);s&&(n=s)}),n}function _t(){const e=[x.join(j.homedir(),".codex","sessions"),x.join(j.homedir(),".codex","archived_sessions")],r=[],n=t=>{for(const o of T.readdirSync(t,{withFileTypes:!0})){const s=x.join(t,o.name);o.isDirectory()?n(s):o.isFile()&&o.name.endsWith(".jsonl")&&r.push(s)}};for(const t of e)T.existsSync(t)&&n(t);return[...new Set(r)].sort()}function ft(){const e=x.join(j.homedir(),".claude","projects");if(!T.existsSync(e))return[];const r=[],n=t=>{for(const o of T.readdirSync(t,{withFileTypes:!0})){const s=x.join(t,o.name);if(o.isDirectory()){if(t===e&&K(o.name))continue;n(s)}else o.isFile()&&o.name.endsWith(".jsonl")&&r.push(s)}};return n(e),r.sort()}function yt(e){if(!e)return null;const r=ft().find(t=>x.basename(t,".jsonl")===e);if(!r)return null;let n=null;return v(r,0,t=>{if(n||!t.trim())return;const o=M(t);if(!o)return;const s=N(o);s&&(n=s)}),n}function ht(e,r){const n=[];let{sourceSessionKey:t,workDir:o,modelId:s,title:u}=at(e),a=!1;return{nextOffset:v(e,r,(c,i)=>{const f=M(c);if(!f)return;const m=typeof f.type=="string"?f.type:"",p=typeof f.payload=="object"&&f.payload!==null?f.payload:null,_=q(f.timestamp);if(!(!p||!_)){if(m==="session_meta"){t=typeof p.id=="string"?p.id:t,o=typeof p.cwd=="string"?p.cwd:o,s=G(p)??s,u||(u=Q(t)??"");return}if(t){if(m==="response_item"){const y=L(p);y?.role==="user"&&y.titleText&&!u&&(u=y.titleText.slice(0,80)),it(n,e,i,p,t,_,u,s,o);return}if(m==="event_msg"){const y=typeof p.type=="string"?p.type:"";if(et(y)){for(let d=n.length-1;d>=0;d-=1){const g=n[d];if(g?.agentType==="codex"&&g.role==="agent"&&!F(g.payload)){g.terminal=!0,a=!1;break}}return}if(y==="user_message"){const d=H(p);if(d?.titleText&&!u&&(u=d.titleText.slice(0,80)),d){const g=W(n,"user",d.payload,_);g>=0&&n.splice(g,1)}}else if(y==="agent_message"){const d=typeof p.message=="string"?h(p.message):"";if(d&&!A(d)){const g=W(n,"agent",d,_);g>=0&&n.splice(g,1)}}const S=n.length;ct(n,e,i,p,t,_,u,s,o),a&&n.length>S&&(n[n.length-1].terminal=!0,a=!1)}}}}),events:n}}function Ct(e,r){const n=T.statSync(e).size;if(D(e)||O(e))return{nextOffset:n,events:[]};const t=[],o=x.basename(e,".jsonl"),s=ut(x.dirname(e).replace(x.join(j.homedir(),".claude","projects")+x.sep,""));if(R(s))return{nextOffset:n,events:[]};let u="";return{nextOffset:v(e,r,(l,c)=>{const i=M(l);if(!i)return;const f=q(i.timestamp),m=typeof i.type=="string"?i.type:"";if(!f||!m)return;const p=N(i);if(!(p&&R(p))){if(m==="user"){const _=typeof i.message=="object"&&i.message!==null?i.message:null,y=typeof _?.content=="string"?h(_.content):"";if(!y)return;u||(u=y.slice(0,80)),t.push({agentType:"claude",sourceMode:"claude_transcript_import",sourceSessionKey:o,sourceEventKey:w(e,c,"user"),cursor:I(e,c),role:"user",ts:f,payload:y,title:u,workDir:p??s})}else if(m==="assistant"){const _=typeof i.message=="object"&&i.message!==null?i.message:null,y=Array.isArray(_?.content)?_.content:[],S=h(y.map(g=>typeof g=="object"&&g!==null&&typeof g.text=="string"?g.text:"").filter(Boolean).join(`
|
|
7
|
+
|
|
8
|
+
`));if(!S)return;const d=typeof _?.model=="string"?_.model:null;t.push({agentType:"claude",sourceMode:"claude_transcript_import",sourceSessionKey:o,sourceEventKey:w(e,c,"assistant"),cursor:I(e,c),role:"agent",ts:f,payload:S,title:u,modelId:d,workDir:p??s})}}}),events:t}}export{ft as listClaudeTranscriptFiles,_t as listCodexRolloutFiles,jt as listOpenCodeSessionFiles,yt as lookupClaudeTranscriptCwd,Ct as parseClaudeTranscriptChunk,ht as parseCodexRolloutChunk,At as parseOpenCodeSessionFile,Mt as parseOpenCodeSessionSnapshot};
|