ultracontext 1.3.0 → 1.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/entry.mjs +3 -3
- package/dist/cli/sdk-daemon.mjs +11 -314
- package/dist/cli/sdk-daemon.mjs.map +1 -1
- package/dist/{ctl-9dwvaRrC.mjs → ctl-BVPu-D57.mjs} +3 -3
- package/dist/{ctl-9dwvaRrC.mjs.map → ctl-BVPu-D57.mjs.map} +1 -1
- package/dist/{launcher-DXUM0K-z.mjs → launcher-BFPi7_wD.mjs} +3 -3
- package/dist/{launcher-DXUM0K-z.mjs.map → launcher-BFPi7_wD.mjs.map} +1 -1
- package/dist/{lock-Q6z0l6Mr.mjs → lock-CQ3xrIlj.mjs} +3 -26
- package/dist/lock-CQ3xrIlj.mjs.map +1 -0
- package/dist/src-BSCJv6SU.mjs +151 -0
- package/dist/src-BSCJv6SU.mjs.map +1 -0
- package/dist/src-DzUz8GPJ.mjs +921 -0
- package/dist/src-DzUz8GPJ.mjs.map +1 -0
- package/dist/{tui-DNqvslCq.mjs → tui-C3H6iRjz.mjs} +31 -292
- package/dist/tui-C3H6iRjz.mjs.map +1 -0
- package/package.json +1 -1
- package/dist/lock-Q6z0l6Mr.mjs.map +0 -1
- package/dist/src-Xh68VkBy.mjs +0 -83
- package/dist/src-Xh68VkBy.mjs.map +0 -1
- package/dist/tui-DNqvslCq.mjs.map +0 -1
|
@@ -0,0 +1,921 @@
|
|
|
1
|
+
import { _ as preserveText, b as truncateString, d as coerceMessageText, f as expandHome, g as normalizeWhitespace, h as normalizeRole, m as firstMessageTimestamp, p as extractSessionIdFromPath, u as asIso, v as safeJsonParse, y as toMessage } from "./src-BSCJv6SU.mjs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { randomUUID } from "node:crypto";
|
|
4
|
+
import fs from "node:fs/promises";
|
|
5
|
+
|
|
6
|
+
//#region ../../packages/parsers/src/agents/claude.mjs
|
|
7
|
+
function formatToolUse(item) {
|
|
8
|
+
const name = (item.name ?? "unknown").toLowerCase();
|
|
9
|
+
const input = item.input ?? {};
|
|
10
|
+
const filePath = input.file_path ?? input.path ?? "";
|
|
11
|
+
if (name === "write") return `[Write] ${filePath}\n${preserveText(input.content ?? input.file_text ?? "")}`;
|
|
12
|
+
if (name === "edit") {
|
|
13
|
+
const parts = [`[Edit] ${filePath}`];
|
|
14
|
+
if (input.old_string) parts.push(`- ${preserveText(input.old_string)}`);
|
|
15
|
+
if (input.new_string) parts.push(`+ ${preserveText(input.new_string)}`);
|
|
16
|
+
return parts.join("\n");
|
|
17
|
+
}
|
|
18
|
+
if (name === "read") return `[Read] ${filePath}`;
|
|
19
|
+
if (name === "bash") return `[Bash] ${preserveText(input.command ?? "")}`;
|
|
20
|
+
if (name === "grep" || name === "glob") {
|
|
21
|
+
const loc = filePath ? ` in ${filePath}` : "";
|
|
22
|
+
return `[${item.name}] ${input.pattern ?? ""}${loc}`;
|
|
23
|
+
}
|
|
24
|
+
const compact = JSON.stringify(input);
|
|
25
|
+
return `[${item.name ?? name}] ${compact.length > 500 ? compact.slice(0, 500) + "..." : compact}`;
|
|
26
|
+
}
|
|
27
|
+
function formatToolResult(item) {
|
|
28
|
+
const content = item.content ?? "";
|
|
29
|
+
if (typeof content === "string") {
|
|
30
|
+
const text = preserveText(content);
|
|
31
|
+
return text ? `[result] ${truncateString(text, 1e3)}` : "[result] ok";
|
|
32
|
+
}
|
|
33
|
+
if (Array.isArray(content)) {
|
|
34
|
+
const text = content.filter((c) => c?.type === "text" && typeof c.text === "string").map((c) => preserveText(c.text)).filter(Boolean).join("\n");
|
|
35
|
+
return text ? `[result] ${truncateString(text, 1e3)}` : "[result] ok";
|
|
36
|
+
}
|
|
37
|
+
return "[result] ok";
|
|
38
|
+
}
|
|
39
|
+
function extractClaudeTextContent(content) {
|
|
40
|
+
if (!content) return "";
|
|
41
|
+
if (typeof content === "string") return preserveText(content);
|
|
42
|
+
if (Array.isArray(content)) {
|
|
43
|
+
const parts = [];
|
|
44
|
+
for (const item of content) {
|
|
45
|
+
if (!item || typeof item !== "object") continue;
|
|
46
|
+
if (item.type === "text" && typeof item.text === "string") {
|
|
47
|
+
const chunk = preserveText(item.text);
|
|
48
|
+
if (chunk) parts.push(chunk);
|
|
49
|
+
}
|
|
50
|
+
if (item.type === "thinking" && typeof item.thinking === "string") {
|
|
51
|
+
const chunk = preserveText(item.thinking);
|
|
52
|
+
if (chunk) parts.push(`[thinking] ${chunk}`);
|
|
53
|
+
}
|
|
54
|
+
if (item.type === "tool_use") parts.push(formatToolUse(item));
|
|
55
|
+
if (item.type === "tool_result") parts.push(formatToolResult(item));
|
|
56
|
+
}
|
|
57
|
+
return parts.join("\n\n");
|
|
58
|
+
}
|
|
59
|
+
if (typeof content === "object") {
|
|
60
|
+
if (typeof content.text === "string") return preserveText(content.text);
|
|
61
|
+
if (typeof content.content === "string") return preserveText(content.content);
|
|
62
|
+
}
|
|
63
|
+
return "";
|
|
64
|
+
}
|
|
65
|
+
function parseClaudeCodeLine({ line, filePath }) {
|
|
66
|
+
const parsed = safeJsonParse(line);
|
|
67
|
+
if (!parsed || typeof parsed !== "object") return null;
|
|
68
|
+
const type = String(parsed.type ?? "").toLowerCase();
|
|
69
|
+
const sessionId = parsed.sessionId ?? parsed.session_id ?? parsed.payload?.sessionId ?? parsed.payload?.session_id ?? extractSessionIdFromPath(filePath);
|
|
70
|
+
const timestamp = parsed.timestamp ?? parsed.ts ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
71
|
+
if (type === "summary") {
|
|
72
|
+
const summary = normalizeWhitespace(parsed.summary);
|
|
73
|
+
if (!summary) return null;
|
|
74
|
+
return {
|
|
75
|
+
sessionId,
|
|
76
|
+
eventType: "claude.summary",
|
|
77
|
+
kind: "system",
|
|
78
|
+
timestamp,
|
|
79
|
+
message: toMessage(summary),
|
|
80
|
+
raw: parsed
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
if (type === "file-history-snapshot") {
|
|
84
|
+
const trackedFiles = Object.keys(parsed.snapshot?.trackedFileBackups ?? {}).length;
|
|
85
|
+
return {
|
|
86
|
+
sessionId,
|
|
87
|
+
eventType: "claude.file_snapshot",
|
|
88
|
+
kind: "system",
|
|
89
|
+
timestamp,
|
|
90
|
+
message: toMessage(`${parsed.isSnapshotUpdate ? "File snapshot update" : "File snapshot"}: ${trackedFiles} tracked files`),
|
|
91
|
+
raw: parsed
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
if (type === "system") {
|
|
95
|
+
const subtype = parsed.subtype ?? "unknown";
|
|
96
|
+
let message;
|
|
97
|
+
if (subtype === "local_command") message = parsed.content ? preserveText(parsed.content) : "Local command executed";
|
|
98
|
+
else if (subtype === "stop_hook_summary") message = `Hook summary: ${parsed.hookCount ?? 0} hooks`;
|
|
99
|
+
else if (subtype === "turn_duration") message = `Turn completed in ${parsed.durationMs ?? 0}ms (${parsed.messageCount ?? 0} messages)`;
|
|
100
|
+
else message = `System event: ${subtype}`;
|
|
101
|
+
if (!message) return null;
|
|
102
|
+
return {
|
|
103
|
+
sessionId,
|
|
104
|
+
eventType: `claude.system.${subtype}`,
|
|
105
|
+
kind: "system",
|
|
106
|
+
timestamp,
|
|
107
|
+
message: toMessage(message),
|
|
108
|
+
raw: parsed
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
if (type === "attachment") {
|
|
112
|
+
const attachmentType = parsed.attachment?.type ?? "unknown";
|
|
113
|
+
const parts = [];
|
|
114
|
+
const added = parsed.attachment?.addedNames;
|
|
115
|
+
const removed = parsed.attachment?.removedNames;
|
|
116
|
+
if (Array.isArray(added) && added.length) parts.push(`added: ${added.join(", ")}`);
|
|
117
|
+
if (Array.isArray(removed) && removed.length) parts.push(`removed: ${removed.join(", ")}`);
|
|
118
|
+
const detail = parts.length ? ` (${parts.join("; ")})` : "";
|
|
119
|
+
return {
|
|
120
|
+
sessionId,
|
|
121
|
+
eventType: `claude.attachment.${attachmentType}`,
|
|
122
|
+
kind: "system",
|
|
123
|
+
timestamp,
|
|
124
|
+
message: toMessage(`Attachment: ${attachmentType}${detail}`),
|
|
125
|
+
raw: parsed
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
if (type === "progress") {
|
|
129
|
+
const data = parsed.data ?? {};
|
|
130
|
+
const subtype = data.type ?? "unknown";
|
|
131
|
+
const label = data.hookName ?? subtype;
|
|
132
|
+
return {
|
|
133
|
+
sessionId,
|
|
134
|
+
eventType: `claude.progress.${subtype}`,
|
|
135
|
+
kind: "system",
|
|
136
|
+
timestamp,
|
|
137
|
+
message: toMessage(`Progress: ${subtype} (${label})`),
|
|
138
|
+
raw: parsed
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
if (type === "queue-operation") return {
|
|
142
|
+
sessionId,
|
|
143
|
+
eventType: "claude.queue_operation",
|
|
144
|
+
kind: "system",
|
|
145
|
+
timestamp,
|
|
146
|
+
message: toMessage(`Queue ${parsed.operation ?? "unknown"}${parsed.content ? `: ${parsed.content}` : ""}`),
|
|
147
|
+
raw: parsed
|
|
148
|
+
};
|
|
149
|
+
if (type === "custom-title") return {
|
|
150
|
+
sessionId,
|
|
151
|
+
eventType: "claude.custom_title",
|
|
152
|
+
kind: "system",
|
|
153
|
+
timestamp,
|
|
154
|
+
message: toMessage(`Custom title: ${parsed.customTitle ?? "unknown"}`),
|
|
155
|
+
raw: parsed
|
|
156
|
+
};
|
|
157
|
+
if (type === "agent-name") return {
|
|
158
|
+
sessionId,
|
|
159
|
+
eventType: "claude.agent_name",
|
|
160
|
+
kind: "system",
|
|
161
|
+
timestamp,
|
|
162
|
+
message: toMessage(`Agent name: ${parsed.agentName ?? "unknown"}`),
|
|
163
|
+
raw: parsed
|
|
164
|
+
};
|
|
165
|
+
if (type === "pr-link") return {
|
|
166
|
+
sessionId,
|
|
167
|
+
eventType: "claude.pr_link",
|
|
168
|
+
kind: "system",
|
|
169
|
+
timestamp,
|
|
170
|
+
message: toMessage(`PR #${parsed.prNumber ?? 0}: ${parsed.prUrl ?? ""}`),
|
|
171
|
+
raw: parsed
|
|
172
|
+
};
|
|
173
|
+
if (type === "permission-mode") return {
|
|
174
|
+
sessionId,
|
|
175
|
+
eventType: "claude.permission_mode",
|
|
176
|
+
kind: "system",
|
|
177
|
+
timestamp,
|
|
178
|
+
message: toMessage(`Permission mode: ${parsed.permissionMode ?? "unknown"}`),
|
|
179
|
+
raw: parsed
|
|
180
|
+
};
|
|
181
|
+
if (type === "last-prompt") {
|
|
182
|
+
const prompt = parsed.lastPrompt ?? "";
|
|
183
|
+
if (!prompt) return null;
|
|
184
|
+
return {
|
|
185
|
+
sessionId,
|
|
186
|
+
eventType: "claude.last_prompt",
|
|
187
|
+
kind: "system",
|
|
188
|
+
timestamp,
|
|
189
|
+
message: toMessage(prompt),
|
|
190
|
+
raw: parsed
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
if (type === "ai-title") return {
|
|
194
|
+
sessionId,
|
|
195
|
+
eventType: "claude.ai_title",
|
|
196
|
+
kind: "system",
|
|
197
|
+
timestamp,
|
|
198
|
+
message: toMessage(`AI title: ${parsed.aiTitle ?? "unknown"}`),
|
|
199
|
+
raw: parsed
|
|
200
|
+
};
|
|
201
|
+
if (type === "task-summary") return {
|
|
202
|
+
sessionId,
|
|
203
|
+
eventType: "claude.task_summary",
|
|
204
|
+
kind: "system",
|
|
205
|
+
timestamp,
|
|
206
|
+
message: toMessage(parsed.summary ?? "[task summary]"),
|
|
207
|
+
raw: parsed
|
|
208
|
+
};
|
|
209
|
+
if (type === "tag") return {
|
|
210
|
+
sessionId,
|
|
211
|
+
eventType: "claude.tag",
|
|
212
|
+
kind: "system",
|
|
213
|
+
timestamp,
|
|
214
|
+
message: toMessage(`Tag: ${parsed.tag ?? ""}`),
|
|
215
|
+
raw: parsed
|
|
216
|
+
};
|
|
217
|
+
if (type === "agent-color") return {
|
|
218
|
+
sessionId,
|
|
219
|
+
eventType: "claude.agent_color",
|
|
220
|
+
kind: "system",
|
|
221
|
+
timestamp,
|
|
222
|
+
message: toMessage(`Agent color: ${parsed.agentColor ?? "unknown"}`),
|
|
223
|
+
raw: parsed
|
|
224
|
+
};
|
|
225
|
+
if (type === "agent-setting") return {
|
|
226
|
+
sessionId,
|
|
227
|
+
eventType: "claude.agent_setting",
|
|
228
|
+
kind: "system",
|
|
229
|
+
timestamp,
|
|
230
|
+
message: toMessage(`Agent setting: ${parsed.agentSetting ?? "unknown"}`),
|
|
231
|
+
raw: parsed
|
|
232
|
+
};
|
|
233
|
+
if (type === "mode") return {
|
|
234
|
+
sessionId,
|
|
235
|
+
eventType: "claude.mode",
|
|
236
|
+
kind: "system",
|
|
237
|
+
timestamp,
|
|
238
|
+
message: toMessage(`Mode: ${parsed.mode ?? "unknown"}`),
|
|
239
|
+
raw: parsed
|
|
240
|
+
};
|
|
241
|
+
if (type === "worktree-state") {
|
|
242
|
+
const ws = parsed.worktreeSession;
|
|
243
|
+
return {
|
|
244
|
+
sessionId,
|
|
245
|
+
eventType: "claude.worktree_state",
|
|
246
|
+
kind: "system",
|
|
247
|
+
timestamp,
|
|
248
|
+
message: toMessage(`Worktree: ${ws ? `entered ${ws.worktreePath ?? ""}` : "exited"}`),
|
|
249
|
+
raw: parsed
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
if (type === "attribution-snapshot") {
|
|
253
|
+
const fileCount = Object.keys(parsed.fileStates ?? {}).length;
|
|
254
|
+
return {
|
|
255
|
+
sessionId,
|
|
256
|
+
eventType: "claude.attribution_snapshot",
|
|
257
|
+
kind: "system",
|
|
258
|
+
timestamp,
|
|
259
|
+
message: toMessage(`Attribution snapshot: ${fileCount} files`),
|
|
260
|
+
raw: parsed
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
if (type === "content-replacement") return {
|
|
264
|
+
sessionId,
|
|
265
|
+
eventType: "claude.content_replacement",
|
|
266
|
+
kind: "system",
|
|
267
|
+
timestamp,
|
|
268
|
+
message: toMessage(`Content replacement: ${parsed.replacements?.length ?? 0} blocks`),
|
|
269
|
+
raw: parsed
|
|
270
|
+
};
|
|
271
|
+
if (type === "speculation-accept") return {
|
|
272
|
+
sessionId,
|
|
273
|
+
eventType: "claude.speculation_accept",
|
|
274
|
+
kind: "system",
|
|
275
|
+
timestamp,
|
|
276
|
+
message: toMessage(`Speculation accepted: saved ${parsed.timeSavedMs ?? 0}ms`),
|
|
277
|
+
raw: parsed
|
|
278
|
+
};
|
|
279
|
+
if (type === "marble-origami-commit") return {
|
|
280
|
+
sessionId,
|
|
281
|
+
eventType: "claude.context_collapse_commit",
|
|
282
|
+
kind: "system",
|
|
283
|
+
timestamp,
|
|
284
|
+
message: toMessage(parsed.summary ?? `Context collapse: ${parsed.collapseId ?? ""}`),
|
|
285
|
+
raw: parsed
|
|
286
|
+
};
|
|
287
|
+
if (type === "marble-origami-snapshot") return {
|
|
288
|
+
sessionId,
|
|
289
|
+
eventType: "claude.context_collapse_snapshot",
|
|
290
|
+
kind: "system",
|
|
291
|
+
timestamp,
|
|
292
|
+
message: toMessage(`Context collapse snapshot: ${parsed.staged?.length ?? 0} staged`),
|
|
293
|
+
raw: parsed
|
|
294
|
+
};
|
|
295
|
+
if (type !== "user" && type !== "assistant") return null;
|
|
296
|
+
const rawContent = parsed.message?.content ?? parsed.message ?? parsed.content ?? parsed.payload?.content ?? "";
|
|
297
|
+
let message = extractClaudeTextContent(rawContent);
|
|
298
|
+
if (!message) {
|
|
299
|
+
if (!(Array.isArray(rawContent) && rawContent.some((c) => c?.type === "thinking"))) return null;
|
|
300
|
+
message = "[thinking]";
|
|
301
|
+
}
|
|
302
|
+
const roleHint = parsed.message?.role ?? type;
|
|
303
|
+
return {
|
|
304
|
+
sessionId,
|
|
305
|
+
eventType: `claude.${type}`,
|
|
306
|
+
kind: normalizeRole(roleHint, type === "user" ? "user" : "assistant"),
|
|
307
|
+
timestamp,
|
|
308
|
+
message: toMessage(message),
|
|
309
|
+
raw: parsed
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
//#endregion
|
|
314
|
+
//#region ../../packages/parsers/src/agents/codex.mjs
|
|
315
|
+
function parseCodexLine({ line, filePath }) {
|
|
316
|
+
const parsed = safeJsonParse(line);
|
|
317
|
+
if (!parsed || typeof parsed !== "object") return null;
|
|
318
|
+
const payload = parsed.payload ?? {};
|
|
319
|
+
const sessionId = payload.session_id ?? payload.id ?? parsed.session_id ?? extractSessionIdFromPath(filePath);
|
|
320
|
+
if (parsed.type === "event_msg") {
|
|
321
|
+
const eventType = payload.type ?? "unknown";
|
|
322
|
+
if (![
|
|
323
|
+
"user_message",
|
|
324
|
+
"agent_message",
|
|
325
|
+
"task_started",
|
|
326
|
+
"task_complete",
|
|
327
|
+
"token_count",
|
|
328
|
+
"agent_reasoning",
|
|
329
|
+
"turn_aborted",
|
|
330
|
+
"context_compacted"
|
|
331
|
+
].includes(eventType)) return null;
|
|
332
|
+
const kind = eventType === "user_message" ? "user" : eventType === "agent_message" ? "assistant" : "system";
|
|
333
|
+
const message = eventType === "token_count" ? "Token count update" : eventType === "agent_reasoning" ? payload.text : eventType === "turn_aborted" ? payload.reason ? `Turn aborted: ${payload.reason}` : "Turn aborted" : eventType === "context_compacted" ? "Context compacted" : payload.message ?? payload.last_agent_message ?? `${eventType}${payload.turn_id ? ` (${payload.turn_id})` : ""}`;
|
|
334
|
+
return {
|
|
335
|
+
sessionId,
|
|
336
|
+
eventType: `event_msg.${eventType}`,
|
|
337
|
+
kind,
|
|
338
|
+
timestamp: parsed.timestamp ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
339
|
+
message: toMessage(message),
|
|
340
|
+
raw: parsed
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
if (parsed.type === "session_meta") return {
|
|
344
|
+
sessionId,
|
|
345
|
+
eventType: "session_meta",
|
|
346
|
+
kind: "system",
|
|
347
|
+
timestamp: parsed.timestamp ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
348
|
+
message: `Session started in ${payload.cwd ?? "unknown cwd"}`,
|
|
349
|
+
raw: parsed
|
|
350
|
+
};
|
|
351
|
+
if (parsed.type === "response_item") {
|
|
352
|
+
const subtype = payload.type;
|
|
353
|
+
const ts = parsed.timestamp ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
354
|
+
if (subtype === "message") {
|
|
355
|
+
const text = (payload.content ?? []).map((c) => c.text ?? "").filter(Boolean).join("\n");
|
|
356
|
+
return {
|
|
357
|
+
sessionId,
|
|
358
|
+
eventType: "response_item.message",
|
|
359
|
+
kind: {
|
|
360
|
+
developer: "system",
|
|
361
|
+
assistant: "assistant",
|
|
362
|
+
user: "user"
|
|
363
|
+
}[payload.role] ?? normalizeRole(payload.role),
|
|
364
|
+
timestamp: ts,
|
|
365
|
+
message: toMessage(text || `[${payload.role ?? "unknown"} message]`),
|
|
366
|
+
raw: parsed
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
if (subtype === "reasoning") return {
|
|
370
|
+
sessionId,
|
|
371
|
+
eventType: "response_item.reasoning",
|
|
372
|
+
kind: "system",
|
|
373
|
+
timestamp: ts,
|
|
374
|
+
message: toMessage((payload.summary ?? []).map((s) => s.text ?? "").filter(Boolean).join("\n") || "[reasoning]"),
|
|
375
|
+
raw: parsed
|
|
376
|
+
};
|
|
377
|
+
if (subtype === "function_call") return {
|
|
378
|
+
sessionId,
|
|
379
|
+
eventType: "response_item.function_call",
|
|
380
|
+
kind: "system",
|
|
381
|
+
timestamp: ts,
|
|
382
|
+
message: toMessage(`[${payload.name ?? "unknown"}] ${payload.arguments ?? ""}`),
|
|
383
|
+
raw: parsed
|
|
384
|
+
};
|
|
385
|
+
if (subtype === "function_call_output") return {
|
|
386
|
+
sessionId,
|
|
387
|
+
eventType: "response_item.function_call_output",
|
|
388
|
+
kind: "system",
|
|
389
|
+
timestamp: ts,
|
|
390
|
+
message: toMessage(payload.output ?? `[output ${payload.call_id ?? ""}]`),
|
|
391
|
+
raw: parsed
|
|
392
|
+
};
|
|
393
|
+
if (subtype === "web_search_call") {
|
|
394
|
+
const query = payload.action?.query;
|
|
395
|
+
return {
|
|
396
|
+
sessionId,
|
|
397
|
+
eventType: "response_item.web_search_call",
|
|
398
|
+
kind: "system",
|
|
399
|
+
timestamp: ts,
|
|
400
|
+
message: query ? `[web_search] ${query}` : "[web_search]",
|
|
401
|
+
raw: parsed
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
if (subtype === "custom_tool_call") return {
|
|
405
|
+
sessionId,
|
|
406
|
+
eventType: "response_item.custom_tool_call",
|
|
407
|
+
kind: "system",
|
|
408
|
+
timestamp: ts,
|
|
409
|
+
message: toMessage(`[${payload.name ?? "unknown"}] ${payload.input ?? ""}`),
|
|
410
|
+
raw: parsed
|
|
411
|
+
};
|
|
412
|
+
if (subtype === "custom_tool_call_output") return {
|
|
413
|
+
sessionId,
|
|
414
|
+
eventType: "response_item.custom_tool_call_output",
|
|
415
|
+
kind: "system",
|
|
416
|
+
timestamp: ts,
|
|
417
|
+
message: toMessage(payload.output ?? `[output ${payload.call_id ?? ""}]`),
|
|
418
|
+
raw: parsed
|
|
419
|
+
};
|
|
420
|
+
return null;
|
|
421
|
+
}
|
|
422
|
+
if (parsed.type === "compacted") return {
|
|
423
|
+
sessionId,
|
|
424
|
+
eventType: "compacted",
|
|
425
|
+
kind: "system",
|
|
426
|
+
timestamp: parsed.timestamp ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
427
|
+
message: "Session compacted",
|
|
428
|
+
raw: parsed
|
|
429
|
+
};
|
|
430
|
+
if (parsed.type === "turn_context") return {
|
|
431
|
+
sessionId,
|
|
432
|
+
eventType: "turn_context",
|
|
433
|
+
kind: "system",
|
|
434
|
+
timestamp: parsed.timestamp ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
435
|
+
message: `Turn context: model=${payload.model}, policy=${payload.approval_policy}`,
|
|
436
|
+
raw: parsed
|
|
437
|
+
};
|
|
438
|
+
return null;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
//#endregion
|
|
442
|
+
//#region ../../packages/parsers/src/agents/openclaw.mjs
|
|
443
|
+
function extractOpenClawTextContent(content) {
|
|
444
|
+
if (!content) return "";
|
|
445
|
+
if (typeof content === "string") return normalizeWhitespace(content);
|
|
446
|
+
if (Array.isArray(content)) {
|
|
447
|
+
const textParts = [];
|
|
448
|
+
for (const item of content) {
|
|
449
|
+
if (!item || typeof item !== "object") continue;
|
|
450
|
+
if (item.type === "text" && typeof item.text === "string") {
|
|
451
|
+
const chunk = normalizeWhitespace(item.text);
|
|
452
|
+
if (chunk) textParts.push(chunk);
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
return textParts.join("\n");
|
|
456
|
+
}
|
|
457
|
+
if (typeof content === "object" && typeof content.text === "string") return normalizeWhitespace(content.text);
|
|
458
|
+
return "";
|
|
459
|
+
}
|
|
460
|
+
function extractOpenClawToolCalls(content) {
|
|
461
|
+
if (!Array.isArray(content)) return [];
|
|
462
|
+
const names = [];
|
|
463
|
+
for (const item of content) {
|
|
464
|
+
if (!item || typeof item !== "object" || item.type !== "toolCall") continue;
|
|
465
|
+
const name = normalizeWhitespace(item.name);
|
|
466
|
+
if (name) names.push(name);
|
|
467
|
+
}
|
|
468
|
+
return names;
|
|
469
|
+
}
|
|
470
|
+
function buildOpenClawRaw(parsed) {
|
|
471
|
+
const raw = {
|
|
472
|
+
type: parsed.type,
|
|
473
|
+
id: parsed.id,
|
|
474
|
+
parentId: parsed.parentId,
|
|
475
|
+
timestamp: parsed.timestamp
|
|
476
|
+
};
|
|
477
|
+
if (parsed.type === "session") {
|
|
478
|
+
raw.session = {
|
|
479
|
+
id: parsed.id,
|
|
480
|
+
version: parsed.version,
|
|
481
|
+
cwd: parsed.cwd,
|
|
482
|
+
parentSession: parsed.parentSession
|
|
483
|
+
};
|
|
484
|
+
return raw;
|
|
485
|
+
}
|
|
486
|
+
if (parsed.type === "custom") {
|
|
487
|
+
raw.customType = parsed.customType;
|
|
488
|
+
if (parsed.customType === "model-snapshot" && parsed.data && typeof parsed.data === "object") raw.data = {
|
|
489
|
+
provider: parsed.data.provider,
|
|
490
|
+
modelApi: parsed.data.modelApi,
|
|
491
|
+
modelId: parsed.data.modelId,
|
|
492
|
+
timestamp: parsed.data.timestamp
|
|
493
|
+
};
|
|
494
|
+
return raw;
|
|
495
|
+
}
|
|
496
|
+
if (parsed.message && typeof parsed.message === "object") {
|
|
497
|
+
const contentTypes = Array.isArray(parsed.message.content) ? parsed.message.content.filter((item) => item && typeof item === "object").map((item) => String(item.type ?? "unknown")).slice(0, 12) : [];
|
|
498
|
+
raw.message = {
|
|
499
|
+
role: parsed.message.role,
|
|
500
|
+
stopReason: parsed.message.stopReason,
|
|
501
|
+
toolName: parsed.message.toolName,
|
|
502
|
+
toolCallId: parsed.message.toolCallId,
|
|
503
|
+
isError: parsed.message.isError,
|
|
504
|
+
contentTypes
|
|
505
|
+
};
|
|
506
|
+
}
|
|
507
|
+
if (parsed.type === "compaction") raw.compaction = {
|
|
508
|
+
firstKeptEntryId: parsed.firstKeptEntryId,
|
|
509
|
+
tokensBefore: parsed.tokensBefore
|
|
510
|
+
};
|
|
511
|
+
else if (parsed.type === "branch_summary") raw.branchSummary = {
|
|
512
|
+
firstKeptEntryId: parsed.firstKeptEntryId,
|
|
513
|
+
summary: typeof parsed.summary === "string" ? truncateString(parsed.summary, 350) : ""
|
|
514
|
+
};
|
|
515
|
+
return raw;
|
|
516
|
+
}
|
|
517
|
+
function parseOpenClawLine({ line, filePath }) {
|
|
518
|
+
const parsed = safeJsonParse(line);
|
|
519
|
+
if (!parsed || typeof parsed !== "object") return null;
|
|
520
|
+
const type = String(parsed.type ?? "").toLowerCase();
|
|
521
|
+
const sessionId = parsed.session_id ?? parsed.sessionId ?? parsed.message?.session_id ?? parsed.message?.sessionId ?? extractSessionIdFromPath(filePath);
|
|
522
|
+
const timestamp = parsed.timestamp ?? parsed.message?.timestamp ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
523
|
+
if (type === "session") return {
|
|
524
|
+
sessionId,
|
|
525
|
+
eventType: "openclaw.session",
|
|
526
|
+
kind: "system",
|
|
527
|
+
timestamp,
|
|
528
|
+
message: toMessage(`Session started in ${parsed.cwd ?? "unknown cwd"}`),
|
|
529
|
+
raw: buildOpenClawRaw(parsed)
|
|
530
|
+
};
|
|
531
|
+
if (type === "custom") {
|
|
532
|
+
const customType = normalizeWhitespace(parsed.customType || "custom");
|
|
533
|
+
if (customType === "openclaw.cache-ttl") return null;
|
|
534
|
+
let message = `Custom event: ${customType}`;
|
|
535
|
+
if (customType === "model-snapshot" && parsed.data && typeof parsed.data === "object") {
|
|
536
|
+
const provider = normalizeWhitespace(parsed.data.provider || "");
|
|
537
|
+
const modelId = normalizeWhitespace(parsed.data.modelId || "");
|
|
538
|
+
const modelApi = normalizeWhitespace(parsed.data.modelApi || "");
|
|
539
|
+
const details = [provider, modelId].filter(Boolean).join("/");
|
|
540
|
+
message = `Model snapshot${details ? `: ${details}` : ""}${modelApi ? ` (${modelApi})` : ""}`;
|
|
541
|
+
}
|
|
542
|
+
return {
|
|
543
|
+
sessionId,
|
|
544
|
+
eventType: `openclaw.custom.${customType || "custom"}`,
|
|
545
|
+
kind: "system",
|
|
546
|
+
timestamp,
|
|
547
|
+
message: toMessage(message),
|
|
548
|
+
raw: buildOpenClawRaw(parsed)
|
|
549
|
+
};
|
|
550
|
+
}
|
|
551
|
+
if (type === "compaction") return {
|
|
552
|
+
sessionId,
|
|
553
|
+
eventType: "openclaw.compaction",
|
|
554
|
+
kind: "system",
|
|
555
|
+
timestamp,
|
|
556
|
+
message: toMessage("Session compaction summary updated"),
|
|
557
|
+
raw: buildOpenClawRaw(parsed)
|
|
558
|
+
};
|
|
559
|
+
if (type === "branch_summary") return {
|
|
560
|
+
sessionId,
|
|
561
|
+
eventType: "openclaw.branch_summary",
|
|
562
|
+
kind: "system",
|
|
563
|
+
timestamp,
|
|
564
|
+
message: toMessage(normalizeWhitespace(parsed.summary || "") || "Branch summary updated"),
|
|
565
|
+
raw: buildOpenClawRaw(parsed)
|
|
566
|
+
};
|
|
567
|
+
if (type !== "message" && type !== "custom_message") return null;
|
|
568
|
+
const eventMessage = parsed.message ?? {};
|
|
569
|
+
const role = String(eventMessage.role ?? "").toLowerCase();
|
|
570
|
+
if (role === "user" || role === "assistant") {
|
|
571
|
+
const text = extractOpenClawTextContent(eventMessage.content);
|
|
572
|
+
if (text) return {
|
|
573
|
+
sessionId,
|
|
574
|
+
eventType: `openclaw.${role}`,
|
|
575
|
+
kind: role === "user" ? "user" : "assistant",
|
|
576
|
+
timestamp,
|
|
577
|
+
message: toMessage(text),
|
|
578
|
+
raw: buildOpenClawRaw(parsed)
|
|
579
|
+
};
|
|
580
|
+
if (role === "assistant") {
|
|
581
|
+
const toolCalls = extractOpenClawToolCalls(eventMessage.content);
|
|
582
|
+
if (toolCalls.length > 0) return {
|
|
583
|
+
sessionId,
|
|
584
|
+
eventType: "openclaw.assistant.tool_use",
|
|
585
|
+
kind: "system",
|
|
586
|
+
timestamp,
|
|
587
|
+
message: toMessage(`Assistant requested tools: ${toolCalls.slice(0, 5).join(", ")}${toolCalls.length > 5 ? ` (+${toolCalls.length - 5})` : ""}`),
|
|
588
|
+
raw: buildOpenClawRaw(parsed)
|
|
589
|
+
};
|
|
590
|
+
}
|
|
591
|
+
return null;
|
|
592
|
+
}
|
|
593
|
+
if (role === "toolresult") {
|
|
594
|
+
const toolName = normalizeWhitespace(eventMessage.toolName || "");
|
|
595
|
+
const isError = Boolean(eventMessage.isError);
|
|
596
|
+
let message = `Tool result${toolName ? `: ${toolName}` : ""} (${isError ? "error" : "ok"})`;
|
|
597
|
+
const text = extractOpenClawTextContent(eventMessage.content);
|
|
598
|
+
if (text) message = `${message} ${truncateString(text, 320)}`;
|
|
599
|
+
return {
|
|
600
|
+
sessionId,
|
|
601
|
+
eventType: "openclaw.tool_result",
|
|
602
|
+
kind: "system",
|
|
603
|
+
timestamp,
|
|
604
|
+
message: toMessage(message),
|
|
605
|
+
raw: buildOpenClawRaw(parsed)
|
|
606
|
+
};
|
|
607
|
+
}
|
|
608
|
+
return null;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
//#endregion
|
|
612
|
+
//#region ../../packages/parsers/src/gstack.mjs
|
|
613
|
+
function extractGstackProjectSlug(filePath) {
|
|
614
|
+
return filePath.match(/\.gstack\/projects\/([^/]+)\//)?.[1] ?? "unknown-project";
|
|
615
|
+
}
|
|
616
|
+
function gstackFileType(filePath) {
|
|
617
|
+
const base = filePath.split("/").pop() ?? "";
|
|
618
|
+
if (base === "learnings.jsonl") return "learning";
|
|
619
|
+
if (base === "timeline.jsonl") return "timeline";
|
|
620
|
+
if (base === "resources-shown.jsonl") return "resource";
|
|
621
|
+
if (base.endsWith("-reviews.jsonl")) return "review";
|
|
622
|
+
return "unknown";
|
|
623
|
+
}
|
|
624
|
+
function parseGstackLine({ line, filePath }) {
|
|
625
|
+
const parsed = safeJsonParse(line);
|
|
626
|
+
if (!parsed || typeof parsed !== "object") return null;
|
|
627
|
+
const projectSlug = extractGstackProjectSlug(filePath);
|
|
628
|
+
const fileType = gstackFileType(filePath);
|
|
629
|
+
const timestamp = parsed.ts ?? parsed.timestamp ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
630
|
+
const sessionId = projectSlug;
|
|
631
|
+
if (fileType === "learning") {
|
|
632
|
+
const insight = normalizeWhitespace(parsed.insight);
|
|
633
|
+
if (!insight) return null;
|
|
634
|
+
const skill = parsed.skill ?? "unknown";
|
|
635
|
+
const conf = typeof parsed.confidence === "number" ? ` [${parsed.confidence}/10]` : "";
|
|
636
|
+
return {
|
|
637
|
+
sessionId,
|
|
638
|
+
eventType: `gstack.learning.${parsed.type ?? "insight"}`,
|
|
639
|
+
kind: "system",
|
|
640
|
+
timestamp,
|
|
641
|
+
message: toMessage(`[${skill}]${conf} ${insight}`),
|
|
642
|
+
raw: parsed
|
|
643
|
+
};
|
|
644
|
+
}
|
|
645
|
+
if (fileType === "timeline") {
|
|
646
|
+
const skill = parsed.skill ?? "unknown";
|
|
647
|
+
const event = parsed.event ?? "unknown";
|
|
648
|
+
const branch = parsed.branch ? ` (${parsed.branch})` : "";
|
|
649
|
+
const outcome = parsed.outcome ? ` → ${parsed.outcome}` : "";
|
|
650
|
+
const duration = parsed.duration_s ? ` ${parsed.duration_s}s` : "";
|
|
651
|
+
return {
|
|
652
|
+
sessionId,
|
|
653
|
+
eventType: `gstack.timeline.${event}`,
|
|
654
|
+
kind: "system",
|
|
655
|
+
timestamp,
|
|
656
|
+
message: toMessage(`${skill} ${event}${branch}${outcome}${duration}`),
|
|
657
|
+
raw: parsed
|
|
658
|
+
};
|
|
659
|
+
}
|
|
660
|
+
if (fileType === "review") {
|
|
661
|
+
const skill = parsed.skill ?? "review";
|
|
662
|
+
const score = parsed.overall_score != null ? ` score=${parsed.overall_score}` : "";
|
|
663
|
+
return {
|
|
664
|
+
sessionId,
|
|
665
|
+
eventType: "gstack.review",
|
|
666
|
+
kind: "system",
|
|
667
|
+
timestamp,
|
|
668
|
+
message: toMessage(`${skill}${parsed.status ? ` [${parsed.status}]` : ""}${score}`),
|
|
669
|
+
raw: parsed
|
|
670
|
+
};
|
|
671
|
+
}
|
|
672
|
+
if (fileType === "resource") {
|
|
673
|
+
const title = normalizeWhitespace(parsed.title);
|
|
674
|
+
const url = parsed.url ?? "";
|
|
675
|
+
if (!title && !url) return null;
|
|
676
|
+
return {
|
|
677
|
+
sessionId,
|
|
678
|
+
eventType: "gstack.resource",
|
|
679
|
+
kind: "system",
|
|
680
|
+
timestamp,
|
|
681
|
+
message: toMessage(title ? `${title} — ${url}` : url),
|
|
682
|
+
raw: parsed
|
|
683
|
+
};
|
|
684
|
+
}
|
|
685
|
+
return null;
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
//#endregion
|
|
689
|
+
//#region ../../packages/parsers/src/writers/claude.mjs
|
|
690
|
+
function isUuid(value) {
|
|
691
|
+
return /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(String(value ?? "").trim());
|
|
692
|
+
}
|
|
693
|
+
function normalizeSessionUuid(raw) {
|
|
694
|
+
const value = String(raw ?? "").trim();
|
|
695
|
+
if (isUuid(value)) return value;
|
|
696
|
+
return randomUUID();
|
|
697
|
+
}
|
|
698
|
+
function claudeProjectDirName(cwd) {
|
|
699
|
+
return path.resolve(String(cwd || process.cwd())).replace(/[\\/]/g, "-").replace(/[^A-Za-z0-9._-]/g, "-");
|
|
700
|
+
}
|
|
701
|
+
function claudeSessionFilePath(sessionId, cwd, baseDir) {
|
|
702
|
+
const root = baseDir || expandHome("~/.claude/projects");
|
|
703
|
+
return path.join(root, claudeProjectDirName(cwd), `${sessionId}.jsonl`);
|
|
704
|
+
}
|
|
705
|
+
async function hasLocalClaudeSession(sessionId, cwd = "", baseDir) {
|
|
706
|
+
const id = String(sessionId ?? "").trim();
|
|
707
|
+
if (!id) return false;
|
|
708
|
+
const preferredPath = claudeSessionFilePath(id, cwd || process.cwd(), baseDir);
|
|
709
|
+
try {
|
|
710
|
+
if ((await fs.stat(preferredPath)).isFile()) return true;
|
|
711
|
+
} catch {}
|
|
712
|
+
const fg = (await import("fast-glob")).default;
|
|
713
|
+
const root = baseDir || expandHome("~/.claude/projects");
|
|
714
|
+
return (await fg([path.join(root, `**/*${id}.jsonl`)], {
|
|
715
|
+
onlyFiles: true,
|
|
716
|
+
absolute: true,
|
|
717
|
+
unique: true,
|
|
718
|
+
suppressErrors: true,
|
|
719
|
+
followSymbolicLinks: false
|
|
720
|
+
})).some((filePath) => path.basename(filePath, ".jsonl") === id);
|
|
721
|
+
}
|
|
722
|
+
async function writeClaudeSession({ sessionId, cwd, messages, baseDir }) {
|
|
723
|
+
const runCwd = String(cwd || process.cwd());
|
|
724
|
+
const resolvedSessionId = normalizeSessionUuid(sessionId);
|
|
725
|
+
const firstTs = asIso(firstMessageTimestamp(messages));
|
|
726
|
+
const filePath = claudeSessionFilePath(resolvedSessionId, runCwd, baseDir);
|
|
727
|
+
if (await hasLocalClaudeSession(resolvedSessionId, runCwd, baseDir)) return {
|
|
728
|
+
written: false,
|
|
729
|
+
reason: "already_exists",
|
|
730
|
+
filePath,
|
|
731
|
+
sessionId: resolvedSessionId
|
|
732
|
+
};
|
|
733
|
+
try {
|
|
734
|
+
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
|
735
|
+
const lines = [];
|
|
736
|
+
let parentUuid = null;
|
|
737
|
+
for (let i = 0; i < (messages?.length ?? 0); i += 1) {
|
|
738
|
+
const message = messages[i];
|
|
739
|
+
const normalizedRole = normalizeRole(message?.role);
|
|
740
|
+
const role = normalizedRole === "assistant" ? "assistant" : normalizedRole === "user" ? "user" : "assistant";
|
|
741
|
+
const rawText = coerceMessageText(message).trim();
|
|
742
|
+
if (!rawText) continue;
|
|
743
|
+
const text = normalizedRole === "system" ? `[system] ${rawText}` : rawText;
|
|
744
|
+
const ts = asIso(message?.content?.timestamp ?? message?.metadata?.timestamp ?? new Date(new Date(firstTs).getTime() + i * 1e3).toISOString());
|
|
745
|
+
const entryUuid = randomUUID();
|
|
746
|
+
lines.push(JSON.stringify({
|
|
747
|
+
parentUuid,
|
|
748
|
+
isSidechain: false,
|
|
749
|
+
userType: "external",
|
|
750
|
+
cwd: runCwd,
|
|
751
|
+
sessionId: resolvedSessionId,
|
|
752
|
+
version: "adapter",
|
|
753
|
+
gitBranch: "",
|
|
754
|
+
type: role,
|
|
755
|
+
message: {
|
|
756
|
+
role,
|
|
757
|
+
content: text
|
|
758
|
+
},
|
|
759
|
+
timestamp: ts,
|
|
760
|
+
uuid: entryUuid
|
|
761
|
+
}));
|
|
762
|
+
parentUuid = entryUuid;
|
|
763
|
+
}
|
|
764
|
+
if (lines.length === 0) {
|
|
765
|
+
const entryUuid = randomUUID();
|
|
766
|
+
lines.push(JSON.stringify({
|
|
767
|
+
parentUuid: null,
|
|
768
|
+
isSidechain: false,
|
|
769
|
+
userType: "external",
|
|
770
|
+
cwd: runCwd,
|
|
771
|
+
sessionId: resolvedSessionId,
|
|
772
|
+
version: "adapter",
|
|
773
|
+
gitBranch: "",
|
|
774
|
+
type: "assistant",
|
|
775
|
+
message: {
|
|
776
|
+
role: "assistant",
|
|
777
|
+
content: "[system] Session restored from UltraContext with no user/assistant messages."
|
|
778
|
+
},
|
|
779
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
780
|
+
uuid: entryUuid
|
|
781
|
+
}));
|
|
782
|
+
}
|
|
783
|
+
await fs.writeFile(filePath, `${lines.join("\n")}\n`, "utf8");
|
|
784
|
+
return {
|
|
785
|
+
written: true,
|
|
786
|
+
reason: "created",
|
|
787
|
+
filePath,
|
|
788
|
+
sessionId: resolvedSessionId
|
|
789
|
+
};
|
|
790
|
+
} catch (error) {
|
|
791
|
+
return {
|
|
792
|
+
written: false,
|
|
793
|
+
reason: "write_failed",
|
|
794
|
+
filePath,
|
|
795
|
+
sessionId: resolvedSessionId,
|
|
796
|
+
error: error instanceof Error ? error.message : String(error)
|
|
797
|
+
};
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
//#endregion
|
|
802
|
+
//#region ../../packages/parsers/src/writers/codex.mjs
|
|
803
|
+
function buildEventMsgLine(message, fallbackTs) {
|
|
804
|
+
const ts = asIso(message?.content?.timestamp ?? message?.metadata?.timestamp ?? fallbackTs);
|
|
805
|
+
const role = normalizeRole(message?.role);
|
|
806
|
+
const text = coerceMessageText(message).trim();
|
|
807
|
+
if (!text) return null;
|
|
808
|
+
if (role === "user") return {
|
|
809
|
+
timestamp: ts,
|
|
810
|
+
type: "event_msg",
|
|
811
|
+
payload: {
|
|
812
|
+
type: "user_message",
|
|
813
|
+
message: text,
|
|
814
|
+
images: [],
|
|
815
|
+
local_images: [],
|
|
816
|
+
text_elements: []
|
|
817
|
+
}
|
|
818
|
+
};
|
|
819
|
+
if (role === "assistant") return {
|
|
820
|
+
timestamp: ts,
|
|
821
|
+
type: "event_msg",
|
|
822
|
+
payload: {
|
|
823
|
+
type: "agent_message",
|
|
824
|
+
message: text
|
|
825
|
+
}
|
|
826
|
+
};
|
|
827
|
+
return {
|
|
828
|
+
timestamp: ts,
|
|
829
|
+
type: "event_msg",
|
|
830
|
+
payload: {
|
|
831
|
+
type: "agent_message",
|
|
832
|
+
message: `[system] ${text}`
|
|
833
|
+
}
|
|
834
|
+
};
|
|
835
|
+
}
|
|
836
|
+
function sessionFilePath(sessionId, firstTimestamp, baseDir) {
|
|
837
|
+
const iso = asIso(firstTimestamp);
|
|
838
|
+
const [year, month, day] = iso.slice(0, 10).split("-");
|
|
839
|
+
const stamp = iso.replace(/\.\d{3}Z$/, "").replace(/:/g, "-").replace("Z", "");
|
|
840
|
+
const root = baseDir || expandHome("~/.codex/sessions");
|
|
841
|
+
const fileName = `rollout-${stamp}-${sessionId}.jsonl`;
|
|
842
|
+
return path.join(root, year, month, day, fileName);
|
|
843
|
+
}
|
|
844
|
+
async function hasLocalCodexSession(sessionId, baseDir) {
|
|
845
|
+
const id = String(sessionId ?? "").trim();
|
|
846
|
+
if (!id) return false;
|
|
847
|
+
const fg = (await import("fast-glob")).default;
|
|
848
|
+
const root = baseDir || expandHome("~/.codex/sessions");
|
|
849
|
+
return (await fg([path.join(root, `**/*${id}*.jsonl`)], {
|
|
850
|
+
onlyFiles: true,
|
|
851
|
+
absolute: true,
|
|
852
|
+
unique: true,
|
|
853
|
+
suppressErrors: true,
|
|
854
|
+
followSymbolicLinks: false
|
|
855
|
+
})).some((filePath) => filePath.includes(id));
|
|
856
|
+
}
|
|
857
|
+
async function writeCodexSession({ sessionId, cwd, messages, baseDir }) {
|
|
858
|
+
const id = String(sessionId ?? "").trim();
|
|
859
|
+
if (!id) return {
|
|
860
|
+
written: false,
|
|
861
|
+
reason: "missing_session_id",
|
|
862
|
+
filePath: ""
|
|
863
|
+
};
|
|
864
|
+
if (await hasLocalCodexSession(id, baseDir)) return {
|
|
865
|
+
written: false,
|
|
866
|
+
reason: "already_exists",
|
|
867
|
+
filePath: ""
|
|
868
|
+
};
|
|
869
|
+
const firstTs = asIso(firstMessageTimestamp(messages));
|
|
870
|
+
const filePath = sessionFilePath(id, firstTs, baseDir);
|
|
871
|
+
try {
|
|
872
|
+
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
|
873
|
+
const lines = [];
|
|
874
|
+
lines.push(JSON.stringify({
|
|
875
|
+
timestamp: firstTs,
|
|
876
|
+
type: "session_meta",
|
|
877
|
+
payload: {
|
|
878
|
+
id,
|
|
879
|
+
timestamp: firstTs,
|
|
880
|
+
cwd: cwd || process.cwd(),
|
|
881
|
+
originator: "ultracontext_daemon",
|
|
882
|
+
cli_version: "restored",
|
|
883
|
+
source: "cli",
|
|
884
|
+
model_provider: "openai"
|
|
885
|
+
}
|
|
886
|
+
}));
|
|
887
|
+
let emitted = 0;
|
|
888
|
+
for (let i = 0; i < (messages?.length ?? 0); i += 1) {
|
|
889
|
+
const fallbackTs = new Date(new Date(firstTs).getTime() + i * 1e3).toISOString();
|
|
890
|
+
const line = buildEventMsgLine(messages[i], fallbackTs);
|
|
891
|
+
if (!line) continue;
|
|
892
|
+
lines.push(JSON.stringify(line));
|
|
893
|
+
emitted += 1;
|
|
894
|
+
}
|
|
895
|
+
if (emitted === 0) lines.push(JSON.stringify({
|
|
896
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
897
|
+
type: "event_msg",
|
|
898
|
+
payload: {
|
|
899
|
+
type: "agent_message",
|
|
900
|
+
message: "[system] Session restored from UltraContext with no user/assistant messages."
|
|
901
|
+
}
|
|
902
|
+
}));
|
|
903
|
+
await fs.writeFile(filePath, `${lines.join("\n")}\n`, "utf8");
|
|
904
|
+
return {
|
|
905
|
+
written: true,
|
|
906
|
+
reason: "created",
|
|
907
|
+
filePath
|
|
908
|
+
};
|
|
909
|
+
} catch (error) {
|
|
910
|
+
return {
|
|
911
|
+
written: false,
|
|
912
|
+
reason: "write_failed",
|
|
913
|
+
filePath,
|
|
914
|
+
error: error instanceof Error ? error.message : String(error)
|
|
915
|
+
};
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
//#endregion
|
|
920
|
+
export { parseGstackLine as a, parseClaudeCodeLine as c, writeClaudeSession as i, writeCodexSession as n, parseOpenClawLine as o, hasLocalClaudeSession as r, parseCodexLine as s, hasLocalCodexSession as t };
|
|
921
|
+
//# sourceMappingURL=src-DzUz8GPJ.mjs.map
|