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.
@@ -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