shennian 0.2.5 → 0.2.6
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/package.json +1 -1
- package/dist/bin/shennian.d.ts +0 -2
- package/dist/bin/shennian.js +0 -2
- package/dist/src/agents/adapter.d.ts +0 -35
- package/dist/src/agents/adapter.js +0 -19
- package/dist/src/agents/claude.d.ts +0 -25
- package/dist/src/agents/claude.js +0 -234
- package/dist/src/agents/codex.d.ts +0 -49
- package/dist/src/agents/codex.js +0 -794
- package/dist/src/agents/command-spec.d.ts +0 -36
- package/dist/src/agents/command-spec.js +0 -303
- package/dist/src/agents/cursor.d.ts +0 -22
- package/dist/src/agents/cursor.js +0 -248
- package/dist/src/agents/custom.d.ts +0 -30
- package/dist/src/agents/custom.js +0 -210
- package/dist/src/agents/detect.d.ts +0 -9
- package/dist/src/agents/detect.js +0 -46
- package/dist/src/agents/gemini.d.ts +0 -17
- package/dist/src/agents/gemini.js +0 -172
- package/dist/src/agents/model-registry/cache.d.ts +0 -4
- package/dist/src/agents/model-registry/cache.js +0 -37
- package/dist/src/agents/model-registry/discovery.d.ts +0 -4
- package/dist/src/agents/model-registry/discovery.js +0 -161
- package/dist/src/agents/model-registry/parsers.d.ts +0 -11
- package/dist/src/agents/model-registry/parsers.js +0 -349
- package/dist/src/agents/model-registry/runner.d.ts +0 -6
- package/dist/src/agents/model-registry/runner.js +0 -29
- package/dist/src/agents/model-registry/service.d.ts +0 -6
- package/dist/src/agents/model-registry/service.js +0 -71
- package/dist/src/agents/model-registry/types.d.ts +0 -30
- package/dist/src/agents/model-registry/types.js +0 -8
- package/dist/src/agents/model-registry.d.ts +0 -13
- package/dist/src/agents/model-registry.js +0 -15
- package/dist/src/agents/openclaw.d.ts +0 -20
- package/dist/src/agents/openclaw.js +0 -273
- package/dist/src/agents/pi.d.ts +0 -51
- package/dist/src/agents/pi.js +0 -793
- package/dist/src/commands/agent.d.ts +0 -2
- package/dist/src/commands/agent.js +0 -131
- package/dist/src/commands/daemon.d.ts +0 -35
- package/dist/src/commands/daemon.js +0 -612
- package/dist/src/commands/pair-qr.d.ts +0 -4
- package/dist/src/commands/pair-qr.js +0 -6
- package/dist/src/commands/pair.d.ts +0 -8
- package/dist/src/commands/pair.js +0 -162
- package/dist/src/commands/upgrade.d.ts +0 -5
- package/dist/src/commands/upgrade.js +0 -145
- package/dist/src/config/index.d.ts +0 -32
- package/dist/src/config/index.js +0 -31
- package/dist/src/fs/handler.d.ts +0 -26
- package/dist/src/fs/handler.js +0 -130
- package/dist/src/fs/security.d.ts +0 -2
- package/dist/src/fs/security.js +0 -32
- package/dist/src/index.d.ts +0 -1
- package/dist/src/index.js +0 -277
- package/dist/src/log-reporter.d.ts +0 -18
- package/dist/src/log-reporter.js +0 -16
- package/dist/src/native-fusion/parsers.d.ts +0 -12
- package/dist/src/native-fusion/parsers.js +0 -687
- package/dist/src/native-fusion/service.d.ts +0 -25
- package/dist/src/native-fusion/service.js +0 -176
- package/dist/src/native-fusion/state.d.ts +0 -3
- package/dist/src/native-fusion/state.js +0 -22
- package/dist/src/native-fusion/types.d.ts +0 -24
- package/dist/src/native-fusion/types.js +0 -1
- package/dist/src/region.d.ts +0 -15
- package/dist/src/region.js +0 -99
- package/dist/src/relay/client.d.ts +0 -62
- package/dist/src/relay/client.js +0 -311
- package/dist/src/session/handlers/agents.d.ts +0 -4
- package/dist/src/session/handlers/agents.js +0 -55
- package/dist/src/session/handlers/chat.d.ts +0 -4
- package/dist/src/session/handlers/chat.js +0 -326
- package/dist/src/session/handlers/control.d.ts +0 -5
- package/dist/src/session/handlers/control.js +0 -57
- package/dist/src/session/handlers/fs.d.ts +0 -10
- package/dist/src/session/handlers/fs.js +0 -256
- package/dist/src/session/manager.d.ts +0 -28
- package/dist/src/session/manager.js +0 -174
- package/dist/src/session/store.d.ts +0 -7
- package/dist/src/session/store.js +0 -46
- package/dist/src/session/types.d.ts +0 -35
- package/dist/src/session/types.js +0 -4
- package/dist/src/upgrade/engine.d.ts +0 -64
- package/dist/src/upgrade/engine.js +0 -238
|
@@ -1,687 +0,0 @@
|
|
|
1
|
-
// @arch docs/architecture/cli/native-session-fusion.md
|
|
2
|
-
// @test src/__tests__/native-fusion-parsers.test.ts
|
|
3
|
-
import { createHash } from 'node:crypto';
|
|
4
|
-
import fs from 'node:fs';
|
|
5
|
-
import os from 'node:os';
|
|
6
|
-
import path from 'node:path';
|
|
7
|
-
import { buildUserMessagePayload } from '@shennian/wire';
|
|
8
|
-
function normalizeText(text) {
|
|
9
|
-
return stripGitDirectiveArtifacts(text.replace(/\r\n/g, '\n').trim());
|
|
10
|
-
}
|
|
11
|
-
const GIT_DIRECTIVE_LINE_RE = /^\s*::git-[a-z-]+\{[^\n]*\}\s*$/gm;
|
|
12
|
-
function stripGitDirectiveArtifacts(text) {
|
|
13
|
-
if (!text)
|
|
14
|
-
return '';
|
|
15
|
-
const hadDirective = /(^|\n)\s*::git-[a-z-]+\{[^\n]*\}\s*(?=\n|$)/.test(text);
|
|
16
|
-
const stripped = text
|
|
17
|
-
.replace(GIT_DIRECTIVE_LINE_RE, '')
|
|
18
|
-
.replace(/\n{3,}/g, '\n\n');
|
|
19
|
-
if (!stripped.trim())
|
|
20
|
-
return '';
|
|
21
|
-
return hadDirective
|
|
22
|
-
? stripped.replace(/[ \t]+\n/g, '\n').replace(/\n+$/g, '')
|
|
23
|
-
: stripped;
|
|
24
|
-
}
|
|
25
|
-
function readTimestamp(value) {
|
|
26
|
-
if (typeof value !== 'string')
|
|
27
|
-
return null;
|
|
28
|
-
const ts = Date.parse(value);
|
|
29
|
-
return Number.isFinite(ts) ? ts : null;
|
|
30
|
-
}
|
|
31
|
-
function safeParse(line) {
|
|
32
|
-
try {
|
|
33
|
-
const parsed = JSON.parse(line);
|
|
34
|
-
return typeof parsed === 'object' && parsed !== null ? parsed : null;
|
|
35
|
-
}
|
|
36
|
-
catch {
|
|
37
|
-
return null;
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
function readClaudeEventCwd(parsed) {
|
|
41
|
-
return typeof parsed.cwd === 'string' && parsed.cwd.trim() ? parsed.cwd : null;
|
|
42
|
-
}
|
|
43
|
-
function makeCursor(filePath, offset) {
|
|
44
|
-
return `${filePath}:${offset}`;
|
|
45
|
-
}
|
|
46
|
-
function makeEventKey(filePath, offset, kind) {
|
|
47
|
-
const fingerprint = createHash('sha1').update(filePath).digest('hex').slice(0, 16);
|
|
48
|
-
return `${fingerprint}:${offset}:${kind}`;
|
|
49
|
-
}
|
|
50
|
-
function safeStringify(value) {
|
|
51
|
-
if (value == null)
|
|
52
|
-
return '';
|
|
53
|
-
if (typeof value === 'string')
|
|
54
|
-
return value;
|
|
55
|
-
try {
|
|
56
|
-
return JSON.stringify(value);
|
|
57
|
-
}
|
|
58
|
-
catch {
|
|
59
|
-
return String(value);
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
function normalizeToolText(text) {
|
|
63
|
-
return text.replace(/\r\n/g, '\n').trim();
|
|
64
|
-
}
|
|
65
|
-
function parseStructuredString(value) {
|
|
66
|
-
if (typeof value !== 'string')
|
|
67
|
-
return value;
|
|
68
|
-
const trimmed = value.trim();
|
|
69
|
-
if (!trimmed)
|
|
70
|
-
return '';
|
|
71
|
-
try {
|
|
72
|
-
return JSON.parse(trimmed);
|
|
73
|
-
}
|
|
74
|
-
catch {
|
|
75
|
-
return value;
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
function summarizeToolResult(value) {
|
|
79
|
-
if (value == null)
|
|
80
|
-
return '';
|
|
81
|
-
if (typeof value === 'string')
|
|
82
|
-
return normalizeToolText(value);
|
|
83
|
-
if (Array.isArray(value)) {
|
|
84
|
-
const textParts = value
|
|
85
|
-
.map((item) => extractTextContent(item))
|
|
86
|
-
.filter(Boolean);
|
|
87
|
-
if (textParts.length > 0)
|
|
88
|
-
return normalizeToolText(textParts.join('\n\n'));
|
|
89
|
-
}
|
|
90
|
-
if (typeof value === 'object') {
|
|
91
|
-
const text = extractTextContent(value);
|
|
92
|
-
if (text)
|
|
93
|
-
return normalizeToolText(text);
|
|
94
|
-
}
|
|
95
|
-
return normalizeToolText(safeStringify(value));
|
|
96
|
-
}
|
|
97
|
-
function extractTextContent(value) {
|
|
98
|
-
if (value == null)
|
|
99
|
-
return '';
|
|
100
|
-
if (typeof value === 'string')
|
|
101
|
-
return value;
|
|
102
|
-
if (Array.isArray(value)) {
|
|
103
|
-
return value
|
|
104
|
-
.map((item) => extractTextContent(item))
|
|
105
|
-
.filter(Boolean)
|
|
106
|
-
.join('\n\n');
|
|
107
|
-
}
|
|
108
|
-
if (typeof value !== 'object')
|
|
109
|
-
return '';
|
|
110
|
-
const record = value;
|
|
111
|
-
if (typeof record.text === 'string')
|
|
112
|
-
return record.text;
|
|
113
|
-
if (typeof record.output === 'string')
|
|
114
|
-
return record.output;
|
|
115
|
-
if (typeof record.message === 'string')
|
|
116
|
-
return record.message;
|
|
117
|
-
if ('Ok' in record)
|
|
118
|
-
return extractTextContent(record.Ok);
|
|
119
|
-
if ('Err' in record)
|
|
120
|
-
return extractTextContent(record.Err);
|
|
121
|
-
if ('content' in record)
|
|
122
|
-
return extractTextContent(record.content);
|
|
123
|
-
if ('contentItems' in record)
|
|
124
|
-
return extractTextContent(record.contentItems);
|
|
125
|
-
if ('result' in record)
|
|
126
|
-
return extractTextContent(record.result);
|
|
127
|
-
return '';
|
|
128
|
-
}
|
|
129
|
-
function buildToolPayload(name, args, result) {
|
|
130
|
-
const payload = {
|
|
131
|
-
v: 1,
|
|
132
|
-
type: 'tool',
|
|
133
|
-
name,
|
|
134
|
-
};
|
|
135
|
-
if (args !== undefined)
|
|
136
|
-
payload.args = args;
|
|
137
|
-
if (result !== undefined)
|
|
138
|
-
payload.result = summarizeToolResult(result);
|
|
139
|
-
return JSON.stringify(payload);
|
|
140
|
-
}
|
|
141
|
-
function escapeMarkdownLabel(label) {
|
|
142
|
-
return label.replace(/[[\]\\]/g, '\\$&');
|
|
143
|
-
}
|
|
144
|
-
function buildMarkdownImageRef(filePath, label = 'Generated image') {
|
|
145
|
-
return ``;
|
|
146
|
-
}
|
|
147
|
-
function buildImagePayload(filePath) {
|
|
148
|
-
const name = path.basename(filePath) || 'generated-image.png';
|
|
149
|
-
return JSON.stringify({
|
|
150
|
-
v: 1,
|
|
151
|
-
type: 'image',
|
|
152
|
-
content: buildMarkdownImageRef(filePath, name),
|
|
153
|
-
attachments: [
|
|
154
|
-
{
|
|
155
|
-
path: filePath,
|
|
156
|
-
name,
|
|
157
|
-
mimeType: 'image/png',
|
|
158
|
-
kind: 'image',
|
|
159
|
-
},
|
|
160
|
-
],
|
|
161
|
-
});
|
|
162
|
-
}
|
|
163
|
-
function inferMimeType(filePath) {
|
|
164
|
-
const ext = path.extname(filePath).toLowerCase();
|
|
165
|
-
if (ext === '.png')
|
|
166
|
-
return 'image/png';
|
|
167
|
-
if (ext === '.jpg' || ext === '.jpeg')
|
|
168
|
-
return 'image/jpeg';
|
|
169
|
-
if (ext === '.webp')
|
|
170
|
-
return 'image/webp';
|
|
171
|
-
if (ext === '.gif')
|
|
172
|
-
return 'image/gif';
|
|
173
|
-
return 'application/octet-stream';
|
|
174
|
-
}
|
|
175
|
-
function parseCodexLocalImageAttachment(value) {
|
|
176
|
-
if (typeof value === 'string') {
|
|
177
|
-
const imagePath = value.trim();
|
|
178
|
-
if (!imagePath)
|
|
179
|
-
return null;
|
|
180
|
-
return {
|
|
181
|
-
path: imagePath,
|
|
182
|
-
name: path.basename(imagePath) || 'image.png',
|
|
183
|
-
mimeType: inferMimeType(imagePath),
|
|
184
|
-
kind: 'image',
|
|
185
|
-
};
|
|
186
|
-
}
|
|
187
|
-
if (typeof value !== 'object' || value === null)
|
|
188
|
-
return null;
|
|
189
|
-
const entry = value;
|
|
190
|
-
const imagePath = typeof entry.path === 'string' ? entry.path
|
|
191
|
-
: typeof entry.file_path === 'string' ? entry.file_path
|
|
192
|
-
: typeof entry.saved_path === 'string' ? entry.saved_path
|
|
193
|
-
: '';
|
|
194
|
-
if (!imagePath.trim())
|
|
195
|
-
return null;
|
|
196
|
-
const name = typeof entry.name === 'string' && entry.name.trim()
|
|
197
|
-
? entry.name
|
|
198
|
-
: path.basename(imagePath) || 'image.png';
|
|
199
|
-
const mimeType = typeof entry.mimeType === 'string' && entry.mimeType.trim()
|
|
200
|
-
? entry.mimeType
|
|
201
|
-
: inferMimeType(imagePath);
|
|
202
|
-
return {
|
|
203
|
-
path: imagePath,
|
|
204
|
-
name,
|
|
205
|
-
mimeType,
|
|
206
|
-
kind: 'image',
|
|
207
|
-
};
|
|
208
|
-
}
|
|
209
|
-
function parseCodexTextElements(value) {
|
|
210
|
-
if (!Array.isArray(value))
|
|
211
|
-
return '';
|
|
212
|
-
const parts = value
|
|
213
|
-
.map((item) => {
|
|
214
|
-
if (typeof item === 'string')
|
|
215
|
-
return item;
|
|
216
|
-
if (typeof item === 'object' && item !== null) {
|
|
217
|
-
const record = item;
|
|
218
|
-
return typeof record.text === 'string' ? record.text : '';
|
|
219
|
-
}
|
|
220
|
-
return '';
|
|
221
|
-
})
|
|
222
|
-
.map((text) => normalizeText(text))
|
|
223
|
-
.filter(Boolean);
|
|
224
|
-
return parts.join('\n\n');
|
|
225
|
-
}
|
|
226
|
-
function stripCodexUserMessageWrapper(text) {
|
|
227
|
-
const normalized = normalizeText(text);
|
|
228
|
-
if (!normalized)
|
|
229
|
-
return '';
|
|
230
|
-
const requestMatch = normalized.match(/My request for Codex:\s*([\s\S]*)$/i);
|
|
231
|
-
if (requestMatch && requestMatch[1])
|
|
232
|
-
return normalizeText(requestMatch[1]);
|
|
233
|
-
return normalized;
|
|
234
|
-
}
|
|
235
|
-
function parseCodexUserMessage(payload) {
|
|
236
|
-
const textFromElements = parseCodexTextElements(payload.text_elements);
|
|
237
|
-
const textFromInput = Array.isArray(payload.input)
|
|
238
|
-
? parseCodexTextElements(payload.input)
|
|
239
|
-
: '';
|
|
240
|
-
const textFromMessage = typeof payload.message === 'string'
|
|
241
|
-
? stripCodexUserMessageWrapper(payload.message)
|
|
242
|
-
: '';
|
|
243
|
-
const text = textFromElements || textFromInput || textFromMessage;
|
|
244
|
-
const attachments = Array.isArray(payload.local_images)
|
|
245
|
-
? payload.local_images
|
|
246
|
-
.map(parseCodexLocalImageAttachment)
|
|
247
|
-
.filter((item) => item != null)
|
|
248
|
-
: [];
|
|
249
|
-
if (!text && attachments.length === 0)
|
|
250
|
-
return null;
|
|
251
|
-
if (attachments.length > 0) {
|
|
252
|
-
return {
|
|
253
|
-
payload: buildUserMessagePayload(text, attachments),
|
|
254
|
-
titleText: text,
|
|
255
|
-
};
|
|
256
|
-
}
|
|
257
|
-
return { payload: text, titleText: text };
|
|
258
|
-
}
|
|
259
|
-
function pushCodexEvent(events, filePath, lineOffset, kind, sourceSessionKey, ts, payload, title, modelId, workDir, role = 'agent') {
|
|
260
|
-
if (!payload)
|
|
261
|
-
return;
|
|
262
|
-
events.push({
|
|
263
|
-
agentType: 'codex',
|
|
264
|
-
sourceMode: 'codex_rollout_import',
|
|
265
|
-
sourceSessionKey,
|
|
266
|
-
sourceEventKey: makeEventKey(filePath, lineOffset, kind),
|
|
267
|
-
cursor: makeCursor(filePath, lineOffset),
|
|
268
|
-
role,
|
|
269
|
-
ts,
|
|
270
|
-
payload,
|
|
271
|
-
title,
|
|
272
|
-
modelId,
|
|
273
|
-
workDir,
|
|
274
|
-
});
|
|
275
|
-
}
|
|
276
|
-
function pushCodexToolEvent(events, filePath, lineOffset, kind, sourceSessionKey, ts, toolName, title, modelId, workDir, args, result) {
|
|
277
|
-
pushCodexEvent(events, filePath, lineOffset, kind, sourceSessionKey, ts, buildToolPayload(toolName, args, result), title, modelId, workDir);
|
|
278
|
-
}
|
|
279
|
-
function parseCodexResponseItem(events, filePath, lineOffset, payload, sourceSessionKey, ts, title, modelId, workDir) {
|
|
280
|
-
const itemType = typeof payload.type === 'string' ? payload.type : '';
|
|
281
|
-
if (!itemType)
|
|
282
|
-
return;
|
|
283
|
-
if (itemType === 'function_call') {
|
|
284
|
-
const name = typeof payload.name === 'string' ? payload.name : 'function_call';
|
|
285
|
-
pushCodexToolEvent(events, filePath, lineOffset, itemType, sourceSessionKey, ts, name, title, modelId, workDir, parseStructuredString(payload.arguments));
|
|
286
|
-
return;
|
|
287
|
-
}
|
|
288
|
-
if (itemType === 'function_call_output') {
|
|
289
|
-
pushCodexToolEvent(events, filePath, lineOffset, itemType, sourceSessionKey, ts, 'function_call_output', title, modelId, workDir, payload.call_id, payload.output);
|
|
290
|
-
return;
|
|
291
|
-
}
|
|
292
|
-
if (itemType === 'custom_tool_call') {
|
|
293
|
-
const name = typeof payload.name === 'string' ? payload.name : 'custom_tool_call';
|
|
294
|
-
const args = payload.input !== undefined ? { input: payload.input } : undefined;
|
|
295
|
-
pushCodexToolEvent(events, filePath, lineOffset, itemType, sourceSessionKey, ts, name, title, modelId, workDir, args);
|
|
296
|
-
return;
|
|
297
|
-
}
|
|
298
|
-
if (itemType === 'custom_tool_call_output') {
|
|
299
|
-
const name = typeof payload.name === 'string' ? payload.name : 'custom_tool_call_output';
|
|
300
|
-
const result = payload.output ?? payload.result ?? payload.content;
|
|
301
|
-
pushCodexToolEvent(events, filePath, lineOffset, itemType, sourceSessionKey, ts, name, title, modelId, workDir, payload.call_id, result);
|
|
302
|
-
return;
|
|
303
|
-
}
|
|
304
|
-
if (itemType === 'web_search_call') {
|
|
305
|
-
const action = typeof payload.action === 'object' && payload.action !== null
|
|
306
|
-
? payload.action
|
|
307
|
-
: null;
|
|
308
|
-
pushCodexToolEvent(events, filePath, lineOffset, itemType, sourceSessionKey, ts, 'web_search', title, modelId, workDir, action ?? payload.query);
|
|
309
|
-
return;
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
function parseCodexEventMessage(events, filePath, lineOffset, payload, sourceSessionKey, ts, title, modelId, workDir) {
|
|
313
|
-
const eventType = typeof payload.type === 'string' ? payload.type : '';
|
|
314
|
-
if (!eventType)
|
|
315
|
-
return;
|
|
316
|
-
if (eventType === 'user_message') {
|
|
317
|
-
const parsedUser = parseCodexUserMessage(payload);
|
|
318
|
-
if (!parsedUser)
|
|
319
|
-
return;
|
|
320
|
-
pushCodexEvent(events, filePath, lineOffset, eventType, sourceSessionKey, ts, parsedUser.payload, title, modelId, workDir, 'user');
|
|
321
|
-
return;
|
|
322
|
-
}
|
|
323
|
-
if (eventType === 'agent_message') {
|
|
324
|
-
const text = typeof payload.message === 'string' ? normalizeText(payload.message) : '';
|
|
325
|
-
if (!text)
|
|
326
|
-
return;
|
|
327
|
-
pushCodexEvent(events, filePath, lineOffset, eventType, sourceSessionKey, ts, text, title, modelId, workDir);
|
|
328
|
-
return;
|
|
329
|
-
}
|
|
330
|
-
if (eventType === 'exec_command_end') {
|
|
331
|
-
const command = Array.isArray(payload.command) ? payload.command : payload.parsed_cmd ?? payload.command;
|
|
332
|
-
const exitCode = payload.exit_code ?? payload.exitCode;
|
|
333
|
-
const output = [
|
|
334
|
-
typeof payload.aggregated_output === 'string' ? payload.aggregated_output : '',
|
|
335
|
-
exitCode != null ? `\n(exit ${String(exitCode)})` : '',
|
|
336
|
-
].join('');
|
|
337
|
-
pushCodexToolEvent(events, filePath, lineOffset, eventType, sourceSessionKey, ts, 'command_execution', title, modelId, workDir, { command }, output);
|
|
338
|
-
return;
|
|
339
|
-
}
|
|
340
|
-
if (eventType === 'patch_apply_end') {
|
|
341
|
-
pushCodexToolEvent(events, filePath, lineOffset, eventType, sourceSessionKey, ts, 'apply_patch', title, modelId, workDir, payload.changes, {
|
|
342
|
-
success: payload.success,
|
|
343
|
-
stdout: payload.stdout,
|
|
344
|
-
stderr: payload.stderr,
|
|
345
|
-
});
|
|
346
|
-
return;
|
|
347
|
-
}
|
|
348
|
-
if (eventType === 'mcp_tool_call_end') {
|
|
349
|
-
const invocation = typeof payload.invocation === 'object' && payload.invocation !== null
|
|
350
|
-
? payload.invocation
|
|
351
|
-
: null;
|
|
352
|
-
const tool = typeof invocation?.tool === 'string' ? invocation.tool : 'mcp_tool_call';
|
|
353
|
-
const args = {
|
|
354
|
-
server: invocation?.server,
|
|
355
|
-
...(typeof invocation?.arguments === 'object' && invocation.arguments !== null
|
|
356
|
-
? invocation.arguments
|
|
357
|
-
: {}),
|
|
358
|
-
};
|
|
359
|
-
pushCodexToolEvent(events, filePath, lineOffset, eventType, sourceSessionKey, ts, tool, title, modelId, workDir, args, payload.result);
|
|
360
|
-
return;
|
|
361
|
-
}
|
|
362
|
-
if (eventType === 'dynamic_tool_call_request') {
|
|
363
|
-
const tool = typeof payload.tool === 'string' ? payload.tool : 'dynamic_tool_call';
|
|
364
|
-
pushCodexToolEvent(events, filePath, lineOffset, eventType, sourceSessionKey, ts, tool, title, modelId, workDir, payload.arguments);
|
|
365
|
-
return;
|
|
366
|
-
}
|
|
367
|
-
if (eventType === 'dynamic_tool_call_response') {
|
|
368
|
-
const tool = typeof payload.tool === 'string' ? payload.tool : 'dynamic_tool_call';
|
|
369
|
-
pushCodexToolEvent(events, filePath, lineOffset, eventType, sourceSessionKey, ts, tool, title, modelId, workDir, payload.callId ?? payload.call_id, payload.result ?? payload.output);
|
|
370
|
-
return;
|
|
371
|
-
}
|
|
372
|
-
if (eventType === 'web_search_end') {
|
|
373
|
-
const action = typeof payload.action === 'object' && payload.action !== null
|
|
374
|
-
? payload.action
|
|
375
|
-
: null;
|
|
376
|
-
pushCodexToolEvent(events, filePath, lineOffset, eventType, sourceSessionKey, ts, 'web_search', title, modelId, workDir, action ?? payload.query, payload.query);
|
|
377
|
-
return;
|
|
378
|
-
}
|
|
379
|
-
if (eventType === 'image_generation_end') {
|
|
380
|
-
const savedPath = typeof payload.saved_path === 'string' ? payload.saved_path.trim() : '';
|
|
381
|
-
if (!savedPath)
|
|
382
|
-
return;
|
|
383
|
-
pushCodexEvent(events, filePath, lineOffset, eventType, sourceSessionKey, ts, buildImagePayload(savedPath), title, modelId, workDir);
|
|
384
|
-
return;
|
|
385
|
-
}
|
|
386
|
-
if (eventType === 'view_image_tool_call') {
|
|
387
|
-
pushCodexToolEvent(events, filePath, lineOffset, eventType, sourceSessionKey, ts, 'view_image', title, modelId, workDir, payload.path ?? payload.image_path ?? payload);
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
function relativeProjectDir(cwd) {
|
|
391
|
-
const home = os.homedir();
|
|
392
|
-
if (cwd.startsWith(home))
|
|
393
|
-
return cwd.slice(home.length).replace(/^\//, '') || '~';
|
|
394
|
-
return cwd.replace(/\//g, '-');
|
|
395
|
-
}
|
|
396
|
-
function readCodexSessionMeta(filePath) {
|
|
397
|
-
const fd = fs.openSync(filePath, 'r');
|
|
398
|
-
try {
|
|
399
|
-
const chunks = [];
|
|
400
|
-
let position = 0;
|
|
401
|
-
let firstLine = '';
|
|
402
|
-
while (position < 1024 * 1024) {
|
|
403
|
-
const buffer = Buffer.alloc(16 * 1024);
|
|
404
|
-
const bytesRead = fs.readSync(fd, buffer, 0, buffer.length, position);
|
|
405
|
-
if (bytesRead <= 0)
|
|
406
|
-
break;
|
|
407
|
-
const chunk = buffer.subarray(0, bytesRead);
|
|
408
|
-
chunks.push(chunk);
|
|
409
|
-
position += bytesRead;
|
|
410
|
-
const combined = Buffer.concat(chunks);
|
|
411
|
-
const newlineIndex = combined.indexOf(0x0a);
|
|
412
|
-
if (newlineIndex >= 0) {
|
|
413
|
-
firstLine = combined.subarray(0, newlineIndex).toString('utf8');
|
|
414
|
-
break;
|
|
415
|
-
}
|
|
416
|
-
}
|
|
417
|
-
if (!firstLine && chunks.length > 0) {
|
|
418
|
-
firstLine = Buffer.concat(chunks).toString('utf8');
|
|
419
|
-
}
|
|
420
|
-
const parsed = safeParse(firstLine);
|
|
421
|
-
if (!parsed || parsed.type !== 'session_meta') {
|
|
422
|
-
return {
|
|
423
|
-
sourceSessionKey: '',
|
|
424
|
-
workDir: '',
|
|
425
|
-
modelId: null,
|
|
426
|
-
title: '',
|
|
427
|
-
};
|
|
428
|
-
}
|
|
429
|
-
const payload = typeof parsed.payload === 'object' && parsed.payload !== null
|
|
430
|
-
? parsed.payload
|
|
431
|
-
: null;
|
|
432
|
-
if (!payload) {
|
|
433
|
-
return {
|
|
434
|
-
sourceSessionKey: '',
|
|
435
|
-
workDir: '',
|
|
436
|
-
modelId: null,
|
|
437
|
-
title: '',
|
|
438
|
-
};
|
|
439
|
-
}
|
|
440
|
-
const sourceSessionKey = typeof payload.id === 'string' ? payload.id : '';
|
|
441
|
-
return {
|
|
442
|
-
sourceSessionKey,
|
|
443
|
-
workDir: typeof payload.cwd === 'string' ? payload.cwd : '',
|
|
444
|
-
modelId: readCodexModelId(payload),
|
|
445
|
-
title: lookupCodexThreadName(sourceSessionKey) ?? '',
|
|
446
|
-
};
|
|
447
|
-
}
|
|
448
|
-
finally {
|
|
449
|
-
fs.closeSync(fd);
|
|
450
|
-
}
|
|
451
|
-
return {
|
|
452
|
-
sourceSessionKey: '',
|
|
453
|
-
workDir: '',
|
|
454
|
-
modelId: null,
|
|
455
|
-
title: '',
|
|
456
|
-
};
|
|
457
|
-
}
|
|
458
|
-
function readCodexModelId(payload) {
|
|
459
|
-
const model = typeof payload.model === 'string' ? payload.model.trim() : '';
|
|
460
|
-
return model || null;
|
|
461
|
-
}
|
|
462
|
-
function readCodexIndexTitle(parsed, sourceSessionKey) {
|
|
463
|
-
const id = typeof parsed.id === 'string'
|
|
464
|
-
? parsed.id
|
|
465
|
-
: typeof parsed.thread_id === 'string'
|
|
466
|
-
? parsed.thread_id
|
|
467
|
-
: typeof parsed.session_id === 'string'
|
|
468
|
-
? parsed.session_id
|
|
469
|
-
: '';
|
|
470
|
-
if (id !== sourceSessionKey)
|
|
471
|
-
return null;
|
|
472
|
-
const title = typeof parsed.thread_name === 'string'
|
|
473
|
-
? parsed.thread_name
|
|
474
|
-
: typeof parsed.title === 'string'
|
|
475
|
-
? parsed.title
|
|
476
|
-
: '';
|
|
477
|
-
const trimmed = title.trim();
|
|
478
|
-
return trimmed ? trimmed.slice(0, 120) : null;
|
|
479
|
-
}
|
|
480
|
-
function lookupCodexThreadName(sourceSessionKey) {
|
|
481
|
-
if (!sourceSessionKey)
|
|
482
|
-
return null;
|
|
483
|
-
const indexPath = path.join(os.homedir(), '.codex', 'session_index.jsonl');
|
|
484
|
-
if (!fs.existsSync(indexPath))
|
|
485
|
-
return null;
|
|
486
|
-
const content = fs.readFileSync(indexPath, 'utf8');
|
|
487
|
-
const lines = content.split('\n');
|
|
488
|
-
for (let index = lines.length - 1; index >= 0; index -= 1) {
|
|
489
|
-
const line = lines[index];
|
|
490
|
-
if (!line.trim())
|
|
491
|
-
continue;
|
|
492
|
-
const parsed = safeParse(line);
|
|
493
|
-
if (!parsed)
|
|
494
|
-
continue;
|
|
495
|
-
const title = readCodexIndexTitle(parsed, sourceSessionKey);
|
|
496
|
-
if (title)
|
|
497
|
-
return title;
|
|
498
|
-
}
|
|
499
|
-
return null;
|
|
500
|
-
}
|
|
501
|
-
export function listCodexRolloutFiles() {
|
|
502
|
-
const root = path.join(os.homedir(), '.codex', 'sessions');
|
|
503
|
-
if (!fs.existsSync(root))
|
|
504
|
-
return [];
|
|
505
|
-
const files = [];
|
|
506
|
-
const walk = (dir) => {
|
|
507
|
-
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
508
|
-
const full = path.join(dir, entry.name);
|
|
509
|
-
if (entry.isDirectory())
|
|
510
|
-
walk(full);
|
|
511
|
-
else if (entry.isFile() && entry.name.endsWith('.jsonl'))
|
|
512
|
-
files.push(full);
|
|
513
|
-
}
|
|
514
|
-
};
|
|
515
|
-
walk(root);
|
|
516
|
-
return files.sort();
|
|
517
|
-
}
|
|
518
|
-
export function listClaudeTranscriptFiles() {
|
|
519
|
-
const root = path.join(os.homedir(), '.claude', 'projects');
|
|
520
|
-
if (!fs.existsSync(root))
|
|
521
|
-
return [];
|
|
522
|
-
const files = [];
|
|
523
|
-
const walk = (dir) => {
|
|
524
|
-
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
525
|
-
const full = path.join(dir, entry.name);
|
|
526
|
-
if (entry.isDirectory())
|
|
527
|
-
walk(full);
|
|
528
|
-
else if (entry.isFile() && entry.name.endsWith('.jsonl'))
|
|
529
|
-
files.push(full);
|
|
530
|
-
}
|
|
531
|
-
};
|
|
532
|
-
walk(root);
|
|
533
|
-
return files.sort();
|
|
534
|
-
}
|
|
535
|
-
export function lookupClaudeTranscriptCwd(sourceSessionKey) {
|
|
536
|
-
if (!sourceSessionKey)
|
|
537
|
-
return null;
|
|
538
|
-
const match = listClaudeTranscriptFiles().find((filePath) => path.basename(filePath, '.jsonl') === sourceSessionKey);
|
|
539
|
-
if (!match)
|
|
540
|
-
return null;
|
|
541
|
-
const content = fs.readFileSync(match, 'utf8');
|
|
542
|
-
for (const line of content.split('\n')) {
|
|
543
|
-
if (!line.trim())
|
|
544
|
-
continue;
|
|
545
|
-
const parsed = safeParse(line);
|
|
546
|
-
if (!parsed)
|
|
547
|
-
continue;
|
|
548
|
-
const cwd = readClaudeEventCwd(parsed);
|
|
549
|
-
if (cwd)
|
|
550
|
-
return cwd;
|
|
551
|
-
}
|
|
552
|
-
return null;
|
|
553
|
-
}
|
|
554
|
-
export function parseCodexRolloutChunk(filePath, startOffset) {
|
|
555
|
-
const stat = fs.statSync(filePath);
|
|
556
|
-
const fileSize = stat.size;
|
|
557
|
-
if (startOffset >= fileSize)
|
|
558
|
-
return { nextOffset: fileSize, events: [] };
|
|
559
|
-
const chunkBuffer = fs.readFileSync(filePath).subarray(startOffset);
|
|
560
|
-
const lastNewline = chunkBuffer.lastIndexOf(0x0a);
|
|
561
|
-
if (lastNewline < 0)
|
|
562
|
-
return { nextOffset: startOffset, events: [] };
|
|
563
|
-
const nextOffset = startOffset + lastNewline + 1;
|
|
564
|
-
const chunk = chunkBuffer.subarray(0, lastNewline + 1).toString('utf8');
|
|
565
|
-
const lines = chunk.split('\n').filter(Boolean);
|
|
566
|
-
const events = [];
|
|
567
|
-
let { sourceSessionKey, workDir, modelId, title } = readCodexSessionMeta(filePath);
|
|
568
|
-
for (let index = 0, offset = startOffset; index < lines.length; index++) {
|
|
569
|
-
const line = lines[index];
|
|
570
|
-
const parsed = safeParse(line);
|
|
571
|
-
const lineOffset = offset;
|
|
572
|
-
offset += Buffer.byteLength(line, 'utf8') + 1;
|
|
573
|
-
if (!parsed)
|
|
574
|
-
continue;
|
|
575
|
-
const type = typeof parsed.type === 'string' ? parsed.type : '';
|
|
576
|
-
const payload = typeof parsed.payload === 'object' && parsed.payload !== null
|
|
577
|
-
? parsed.payload
|
|
578
|
-
: null;
|
|
579
|
-
const ts = readTimestamp(parsed.timestamp);
|
|
580
|
-
if (!payload || !ts)
|
|
581
|
-
continue;
|
|
582
|
-
if (type === 'session_meta') {
|
|
583
|
-
sourceSessionKey = typeof payload.id === 'string' ? payload.id : sourceSessionKey;
|
|
584
|
-
workDir = typeof payload.cwd === 'string' ? payload.cwd : workDir;
|
|
585
|
-
modelId = readCodexModelId(payload) ?? modelId;
|
|
586
|
-
if (!title)
|
|
587
|
-
title = lookupCodexThreadName(sourceSessionKey) ?? '';
|
|
588
|
-
continue;
|
|
589
|
-
}
|
|
590
|
-
if (!sourceSessionKey)
|
|
591
|
-
continue;
|
|
592
|
-
if (type === 'response_item') {
|
|
593
|
-
parseCodexResponseItem(events, filePath, lineOffset, payload, sourceSessionKey, ts, title, modelId, workDir);
|
|
594
|
-
continue;
|
|
595
|
-
}
|
|
596
|
-
if (type === 'event_msg') {
|
|
597
|
-
const eventType = typeof payload.type === 'string' ? payload.type : '';
|
|
598
|
-
if (eventType === 'user_message') {
|
|
599
|
-
const parsedUser = parseCodexUserMessage(payload);
|
|
600
|
-
if (parsedUser?.titleText && !title)
|
|
601
|
-
title = parsedUser.titleText.slice(0, 80);
|
|
602
|
-
}
|
|
603
|
-
parseCodexEventMessage(events, filePath, lineOffset, payload, sourceSessionKey, ts, title, modelId, workDir);
|
|
604
|
-
}
|
|
605
|
-
}
|
|
606
|
-
return { nextOffset, events };
|
|
607
|
-
}
|
|
608
|
-
export function parseClaudeTranscriptChunk(filePath, startOffset) {
|
|
609
|
-
const stat = fs.statSync(filePath);
|
|
610
|
-
const fileSize = stat.size;
|
|
611
|
-
if (startOffset >= fileSize)
|
|
612
|
-
return { nextOffset: fileSize, events: [] };
|
|
613
|
-
const chunkBuffer = fs.readFileSync(filePath).subarray(startOffset);
|
|
614
|
-
const lastNewline = chunkBuffer.lastIndexOf(0x0a);
|
|
615
|
-
if (lastNewline < 0)
|
|
616
|
-
return { nextOffset: startOffset, events: [] };
|
|
617
|
-
const nextOffset = startOffset + lastNewline + 1;
|
|
618
|
-
const chunk = chunkBuffer.subarray(0, lastNewline + 1).toString('utf8');
|
|
619
|
-
const lines = chunk.split('\n').filter(Boolean);
|
|
620
|
-
const events = [];
|
|
621
|
-
const sourceSessionKey = path.basename(filePath, '.jsonl');
|
|
622
|
-
const fallbackWorkDir = relativeProjectDir(path.dirname(filePath).replace(path.join(os.homedir(), '.claude', 'projects') + path.sep, ''));
|
|
623
|
-
let title = '';
|
|
624
|
-
for (let index = 0, offset = startOffset; index < lines.length; index++) {
|
|
625
|
-
const line = lines[index];
|
|
626
|
-
const parsed = safeParse(line);
|
|
627
|
-
const lineOffset = offset;
|
|
628
|
-
offset += Buffer.byteLength(line, 'utf8') + 1;
|
|
629
|
-
if (!parsed)
|
|
630
|
-
continue;
|
|
631
|
-
const ts = readTimestamp(parsed.timestamp);
|
|
632
|
-
const type = typeof parsed.type === 'string' ? parsed.type : '';
|
|
633
|
-
if (!ts || !type)
|
|
634
|
-
continue;
|
|
635
|
-
if (type === 'user') {
|
|
636
|
-
const message = typeof parsed.message === 'object' && parsed.message !== null
|
|
637
|
-
? parsed.message
|
|
638
|
-
: null;
|
|
639
|
-
const text = typeof message?.content === 'string' ? normalizeText(message.content) : '';
|
|
640
|
-
if (!text)
|
|
641
|
-
continue;
|
|
642
|
-
if (!title)
|
|
643
|
-
title = text.slice(0, 80);
|
|
644
|
-
events.push({
|
|
645
|
-
agentType: 'claude',
|
|
646
|
-
sourceMode: 'claude_transcript_import',
|
|
647
|
-
sourceSessionKey,
|
|
648
|
-
sourceEventKey: makeEventKey(filePath, lineOffset, 'user'),
|
|
649
|
-
cursor: makeCursor(filePath, lineOffset),
|
|
650
|
-
role: 'user',
|
|
651
|
-
ts,
|
|
652
|
-
payload: text,
|
|
653
|
-
title,
|
|
654
|
-
workDir: readClaudeEventCwd(parsed) ?? fallbackWorkDir,
|
|
655
|
-
});
|
|
656
|
-
}
|
|
657
|
-
else if (type === 'assistant') {
|
|
658
|
-
const message = typeof parsed.message === 'object' && parsed.message !== null
|
|
659
|
-
? parsed.message
|
|
660
|
-
: null;
|
|
661
|
-
const content = Array.isArray(message?.content) ? message.content : [];
|
|
662
|
-
const text = normalizeText(content
|
|
663
|
-
.map((part) => typeof part === 'object' && part !== null && typeof part.text === 'string'
|
|
664
|
-
? part.text
|
|
665
|
-
: '')
|
|
666
|
-
.filter(Boolean)
|
|
667
|
-
.join('\n\n'));
|
|
668
|
-
if (!text)
|
|
669
|
-
continue;
|
|
670
|
-
const modelId = typeof message?.model === 'string' ? message.model : null;
|
|
671
|
-
events.push({
|
|
672
|
-
agentType: 'claude',
|
|
673
|
-
sourceMode: 'claude_transcript_import',
|
|
674
|
-
sourceSessionKey,
|
|
675
|
-
sourceEventKey: makeEventKey(filePath, lineOffset, 'assistant'),
|
|
676
|
-
cursor: makeCursor(filePath, lineOffset),
|
|
677
|
-
role: 'agent',
|
|
678
|
-
ts,
|
|
679
|
-
payload: text,
|
|
680
|
-
title,
|
|
681
|
-
modelId,
|
|
682
|
-
workDir: readClaudeEventCwd(parsed) ?? fallbackWorkDir,
|
|
683
|
-
});
|
|
684
|
-
}
|
|
685
|
-
}
|
|
686
|
-
return { nextOffset, events };
|
|
687
|
-
}
|