ultracontext 1.4.13 → 1.6.0

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.
Files changed (41) hide show
  1. package/dist/cli/entry.mjs +9 -3
  2. package/dist/cli/entry.mjs.map +1 -1
  3. package/dist/cli/onboarding.mjs +268 -111
  4. package/dist/cli/onboarding.mjs.map +1 -1
  5. package/dist/cli/sdk-sync.mjs +199 -939
  6. package/dist/cli/sdk-sync.mjs.map +1 -1
  7. package/dist/cli/switch.mjs +168 -0
  8. package/dist/cli/switch.mjs.map +1 -0
  9. package/dist/{ctl-CXfNEPN8.mjs → ctl-DTQZxn3N.mjs} +2 -2
  10. package/dist/{ctl-CXfNEPN8.mjs.map → ctl-DTQZxn3N.mjs.map} +1 -1
  11. package/dist/hero-art-C03HmDXN.mjs +46 -0
  12. package/dist/hero-art-C03HmDXN.mjs.map +1 -0
  13. package/dist/index.d.mts +21 -1
  14. package/dist/index.d.mts.map +1 -1
  15. package/dist/index.mjs +25 -3
  16. package/dist/index.mjs.map +1 -1
  17. package/dist/{launcher-BMMjzr5k.mjs → launcher-ZylswrpR.mjs} +3 -3
  18. package/dist/{launcher-BMMjzr5k.mjs.map → launcher-ZylswrpR.mjs.map} +1 -1
  19. package/dist/{lock-5aJnda81.mjs → lock-BhZX2aF3.mjs} +2 -2
  20. package/dist/{lock-5aJnda81.mjs.map → lock-BhZX2aF3.mjs.map} +1 -1
  21. package/dist/onboarding-preferences-Alhblobi.mjs +76 -0
  22. package/dist/onboarding-preferences-Alhblobi.mjs.map +1 -0
  23. package/dist/src-Bovo1ukU.mjs +1200 -0
  24. package/dist/src-Bovo1ukU.mjs.map +1 -0
  25. package/dist/{tui-DZ1SDOH2.mjs → tui-DLEjew3K.mjs} +334 -115
  26. package/dist/tui-DLEjew3K.mjs.map +1 -0
  27. package/dist/utils-BTfShW0g.mjs +36 -0
  28. package/dist/utils-BTfShW0g.mjs.map +1 -0
  29. package/dist/{utils-CmuIYHtm.mjs → utils-D9CKnbke.mjs} +26 -34
  30. package/dist/utils-D9CKnbke.mjs.map +1 -0
  31. package/lib/register-skills.mjs +96 -0
  32. package/package.json +8 -3
  33. package/plugin/.claude-plugin/plugin.json +6 -0
  34. package/plugin/README.md +112 -0
  35. package/plugin/marketplace.json +17 -0
  36. package/plugin/skills/switch/SKILL.md +27 -0
  37. package/postinstall.mjs +35 -2
  38. package/dist/Spinner-CwBjkXHv.mjs +0 -153
  39. package/dist/Spinner-CwBjkXHv.mjs.map +0 -1
  40. package/dist/tui-DZ1SDOH2.mjs.map +0 -1
  41. package/dist/utils-CmuIYHtm.mjs.map +0 -1
@@ -0,0 +1,1200 @@
1
+ import { a as extractSessionIdFromPath, c as normalizeRole, d as safeJsonParse, f as stripIDEContextTags, i as expandHome, l as normalizeWhitespace, m as truncateString, n as claudeProjectDirName, o as firstMessageTimestamp, p as toMessage, r as coerceMessageText, s as isSafeCwd, t as asIso, u as preserveText } from "./utils-D9CKnbke.mjs";
2
+ import path from "node:path";
3
+ import fs from "node:fs";
4
+ import { randomUUID } from "node:crypto";
5
+ import fs$1 from "node:fs/promises";
6
+
7
+ //#region \0rolldown/runtime.js
8
+ var __defProp = Object.defineProperty;
9
+ var __exportAll = (all, no_symbols) => {
10
+ let target = {};
11
+ for (var name in all) {
12
+ __defProp(target, name, {
13
+ get: all[name],
14
+ enumerable: true
15
+ });
16
+ }
17
+ if (!no_symbols) {
18
+ __defProp(target, Symbol.toStringTag, { value: "Module" });
19
+ }
20
+ return target;
21
+ };
22
+
23
+ //#endregion
24
+ //#region ../../packages/parsers/src/agents/claude.mjs
25
+ function formatToolUse(item) {
26
+ const name = (item.name ?? "unknown").toLowerCase();
27
+ const input = item.input ?? {};
28
+ const filePath = input.file_path ?? input.path ?? "";
29
+ if (name === "write") return `[Write] ${filePath}\n${preserveText(input.content ?? input.file_text ?? "")}`;
30
+ if (name === "edit") {
31
+ const parts = [`[Edit] ${filePath}`];
32
+ if (input.old_string) parts.push(`- ${preserveText(input.old_string)}`);
33
+ if (input.new_string) parts.push(`+ ${preserveText(input.new_string)}`);
34
+ return parts.join("\n");
35
+ }
36
+ if (name === "read") return `[Read] ${filePath}`;
37
+ if (name === "bash") return `[Bash] ${preserveText(input.command ?? "")}`;
38
+ if (name === "grep" || name === "glob") {
39
+ const loc = filePath ? ` in ${filePath}` : "";
40
+ return `[${item.name}] ${input.pattern ?? ""}${loc}`;
41
+ }
42
+ const compact = JSON.stringify(input);
43
+ return `[${item.name ?? name}] ${compact.length > 500 ? compact.slice(0, 500) + "..." : compact}`;
44
+ }
45
+ function formatToolResult(item) {
46
+ const content = item.content ?? "";
47
+ if (typeof content === "string") {
48
+ const text = preserveText(content);
49
+ return text ? `[result] ${truncateString(text, 1e3)}` : "[result] ok";
50
+ }
51
+ if (Array.isArray(content)) {
52
+ const text = content.filter((c) => c?.type === "text" && typeof c.text === "string").map((c) => preserveText(c.text)).filter(Boolean).join("\n");
53
+ return text ? `[result] ${truncateString(text, 1e3)}` : "[result] ok";
54
+ }
55
+ return "[result] ok";
56
+ }
57
+ function extractClaudeTextContent(content) {
58
+ if (!content) return "";
59
+ if (typeof content === "string") return preserveText(content);
60
+ if (Array.isArray(content)) {
61
+ const parts = [];
62
+ for (const item of content) {
63
+ if (!item || typeof item !== "object") continue;
64
+ if (item.type === "text" && typeof item.text === "string") {
65
+ const chunk = preserveText(item.text);
66
+ if (chunk) parts.push(chunk);
67
+ }
68
+ if (item.type === "thinking" && typeof item.thinking === "string") {
69
+ const chunk = preserveText(item.thinking);
70
+ if (chunk) parts.push(`[thinking] ${chunk}`);
71
+ }
72
+ if (item.type === "tool_use") parts.push(formatToolUse(item));
73
+ if (item.type === "tool_result") parts.push(formatToolResult(item));
74
+ }
75
+ return parts.join("\n\n");
76
+ }
77
+ if (typeof content === "object") {
78
+ if (typeof content.text === "string") return preserveText(content.text);
79
+ if (typeof content.content === "string") return preserveText(content.content);
80
+ }
81
+ return "";
82
+ }
83
+ function parseClaudeCodeLine({ line, filePath }) {
84
+ const parsed = safeJsonParse(line);
85
+ if (!parsed || typeof parsed !== "object") return null;
86
+ const type = String(parsed.type ?? "").toLowerCase();
87
+ const sessionId = parsed.sessionId ?? parsed.session_id ?? parsed.payload?.sessionId ?? parsed.payload?.session_id ?? extractSessionIdFromPath(filePath);
88
+ const timestamp = parsed.timestamp ?? parsed.ts ?? (/* @__PURE__ */ new Date()).toISOString();
89
+ if (type === "summary") {
90
+ const summary = normalizeWhitespace(parsed.summary);
91
+ if (!summary) return null;
92
+ return {
93
+ sessionId,
94
+ eventType: "claude.summary",
95
+ kind: "system",
96
+ timestamp,
97
+ message: toMessage(summary),
98
+ raw: parsed
99
+ };
100
+ }
101
+ if (type === "file-history-snapshot") {
102
+ const trackedFiles = Object.keys(parsed.snapshot?.trackedFileBackups ?? {}).length;
103
+ return {
104
+ sessionId,
105
+ eventType: "claude.file_snapshot",
106
+ kind: "system",
107
+ timestamp,
108
+ message: toMessage(`${parsed.isSnapshotUpdate ? "File snapshot update" : "File snapshot"}: ${trackedFiles} tracked files`),
109
+ raw: parsed
110
+ };
111
+ }
112
+ if (type === "system") {
113
+ const subtype = parsed.subtype ?? "unknown";
114
+ let message;
115
+ if (subtype === "local_command") message = parsed.content ? preserveText(parsed.content) : "Local command executed";
116
+ else if (subtype === "stop_hook_summary") message = `Hook summary: ${parsed.hookCount ?? 0} hooks`;
117
+ else if (subtype === "turn_duration") message = `Turn completed in ${parsed.durationMs ?? 0}ms (${parsed.messageCount ?? 0} messages)`;
118
+ else message = `System event: ${subtype}`;
119
+ if (!message) return null;
120
+ return {
121
+ sessionId,
122
+ eventType: `claude.system.${subtype}`,
123
+ kind: "system",
124
+ timestamp,
125
+ message: toMessage(message),
126
+ raw: parsed
127
+ };
128
+ }
129
+ if (type === "attachment") {
130
+ const attachmentType = parsed.attachment?.type ?? "unknown";
131
+ const parts = [];
132
+ const added = parsed.attachment?.addedNames;
133
+ const removed = parsed.attachment?.removedNames;
134
+ if (Array.isArray(added) && added.length) parts.push(`added: ${added.join(", ")}`);
135
+ if (Array.isArray(removed) && removed.length) parts.push(`removed: ${removed.join(", ")}`);
136
+ const detail = parts.length ? ` (${parts.join("; ")})` : "";
137
+ return {
138
+ sessionId,
139
+ eventType: `claude.attachment.${attachmentType}`,
140
+ kind: "system",
141
+ timestamp,
142
+ message: toMessage(`Attachment: ${attachmentType}${detail}`),
143
+ raw: parsed
144
+ };
145
+ }
146
+ if (type === "progress") {
147
+ const data = parsed.data ?? {};
148
+ const subtype = data.type ?? "unknown";
149
+ const label = data.hookName ?? subtype;
150
+ return {
151
+ sessionId,
152
+ eventType: `claude.progress.${subtype}`,
153
+ kind: "system",
154
+ timestamp,
155
+ message: toMessage(`Progress: ${subtype} (${label})`),
156
+ raw: parsed
157
+ };
158
+ }
159
+ if (type === "queue-operation") return {
160
+ sessionId,
161
+ eventType: "claude.queue_operation",
162
+ kind: "system",
163
+ timestamp,
164
+ message: toMessage(`Queue ${parsed.operation ?? "unknown"}${parsed.content ? `: ${parsed.content}` : ""}`),
165
+ raw: parsed
166
+ };
167
+ if (type === "custom-title") return {
168
+ sessionId,
169
+ eventType: "claude.custom_title",
170
+ kind: "system",
171
+ timestamp,
172
+ message: toMessage(`Custom title: ${parsed.customTitle ?? "unknown"}`),
173
+ raw: parsed
174
+ };
175
+ if (type === "agent-name") return {
176
+ sessionId,
177
+ eventType: "claude.agent_name",
178
+ kind: "system",
179
+ timestamp,
180
+ message: toMessage(`Agent name: ${parsed.agentName ?? "unknown"}`),
181
+ raw: parsed
182
+ };
183
+ if (type === "pr-link") return {
184
+ sessionId,
185
+ eventType: "claude.pr_link",
186
+ kind: "system",
187
+ timestamp,
188
+ message: toMessage(`PR #${parsed.prNumber ?? 0}: ${parsed.prUrl ?? ""}`),
189
+ raw: parsed
190
+ };
191
+ if (type === "permission-mode") return {
192
+ sessionId,
193
+ eventType: "claude.permission_mode",
194
+ kind: "system",
195
+ timestamp,
196
+ message: toMessage(`Permission mode: ${parsed.permissionMode ?? "unknown"}`),
197
+ raw: parsed
198
+ };
199
+ if (type === "last-prompt") {
200
+ const prompt = parsed.lastPrompt ?? "";
201
+ if (!prompt) return null;
202
+ return {
203
+ sessionId,
204
+ eventType: "claude.last_prompt",
205
+ kind: "system",
206
+ timestamp,
207
+ message: toMessage(prompt),
208
+ raw: parsed
209
+ };
210
+ }
211
+ if (type === "ai-title") return {
212
+ sessionId,
213
+ eventType: "claude.ai_title",
214
+ kind: "system",
215
+ timestamp,
216
+ message: toMessage(`AI title: ${parsed.aiTitle ?? "unknown"}`),
217
+ raw: parsed
218
+ };
219
+ if (type === "task-summary") return {
220
+ sessionId,
221
+ eventType: "claude.task_summary",
222
+ kind: "system",
223
+ timestamp,
224
+ message: toMessage(parsed.summary ?? "[task summary]"),
225
+ raw: parsed
226
+ };
227
+ if (type === "tag") return {
228
+ sessionId,
229
+ eventType: "claude.tag",
230
+ kind: "system",
231
+ timestamp,
232
+ message: toMessage(`Tag: ${parsed.tag ?? ""}`),
233
+ raw: parsed
234
+ };
235
+ if (type === "agent-color") return {
236
+ sessionId,
237
+ eventType: "claude.agent_color",
238
+ kind: "system",
239
+ timestamp,
240
+ message: toMessage(`Agent color: ${parsed.agentColor ?? "unknown"}`),
241
+ raw: parsed
242
+ };
243
+ if (type === "agent-setting") return {
244
+ sessionId,
245
+ eventType: "claude.agent_setting",
246
+ kind: "system",
247
+ timestamp,
248
+ message: toMessage(`Agent setting: ${parsed.agentSetting ?? "unknown"}`),
249
+ raw: parsed
250
+ };
251
+ if (type === "mode") return {
252
+ sessionId,
253
+ eventType: "claude.mode",
254
+ kind: "system",
255
+ timestamp,
256
+ message: toMessage(`Mode: ${parsed.mode ?? "unknown"}`),
257
+ raw: parsed
258
+ };
259
+ if (type === "worktree-state") {
260
+ const ws = parsed.worktreeSession;
261
+ return {
262
+ sessionId,
263
+ eventType: "claude.worktree_state",
264
+ kind: "system",
265
+ timestamp,
266
+ message: toMessage(`Worktree: ${ws ? `entered ${ws.worktreePath ?? ""}` : "exited"}`),
267
+ raw: parsed
268
+ };
269
+ }
270
+ if (type === "attribution-snapshot") {
271
+ const fileCount = Object.keys(parsed.fileStates ?? {}).length;
272
+ return {
273
+ sessionId,
274
+ eventType: "claude.attribution_snapshot",
275
+ kind: "system",
276
+ timestamp,
277
+ message: toMessage(`Attribution snapshot: ${fileCount} files`),
278
+ raw: parsed
279
+ };
280
+ }
281
+ if (type === "content-replacement") return {
282
+ sessionId,
283
+ eventType: "claude.content_replacement",
284
+ kind: "system",
285
+ timestamp,
286
+ message: toMessage(`Content replacement: ${parsed.replacements?.length ?? 0} blocks`),
287
+ raw: parsed
288
+ };
289
+ if (type === "speculation-accept") return {
290
+ sessionId,
291
+ eventType: "claude.speculation_accept",
292
+ kind: "system",
293
+ timestamp,
294
+ message: toMessage(`Speculation accepted: saved ${parsed.timeSavedMs ?? 0}ms`),
295
+ raw: parsed
296
+ };
297
+ if (type === "marble-origami-commit") return {
298
+ sessionId,
299
+ eventType: "claude.context_collapse_commit",
300
+ kind: "system",
301
+ timestamp,
302
+ message: toMessage(parsed.summary ?? `Context collapse: ${parsed.collapseId ?? ""}`),
303
+ raw: parsed
304
+ };
305
+ if (type === "marble-origami-snapshot") return {
306
+ sessionId,
307
+ eventType: "claude.context_collapse_snapshot",
308
+ kind: "system",
309
+ timestamp,
310
+ message: toMessage(`Context collapse snapshot: ${parsed.staged?.length ?? 0} staged`),
311
+ raw: parsed
312
+ };
313
+ if (type !== "user" && type !== "assistant") return null;
314
+ const rawContent = parsed.message?.content ?? parsed.message ?? parsed.content ?? parsed.payload?.content ?? "";
315
+ let message = extractClaudeTextContent(rawContent);
316
+ if (!message) {
317
+ if (!(Array.isArray(rawContent) && rawContent.some((c) => c?.type === "thinking"))) return null;
318
+ message = "[thinking]";
319
+ }
320
+ const roleHint = parsed.message?.role ?? type;
321
+ return {
322
+ sessionId,
323
+ eventType: `claude.${type}`,
324
+ kind: normalizeRole(roleHint, type === "user" ? "user" : "assistant"),
325
+ timestamp,
326
+ message: toMessage(message),
327
+ raw: parsed
328
+ };
329
+ }
330
+
331
+ //#endregion
332
+ //#region ../../packages/parsers/src/agents/codex.mjs
333
+ function parseCodexLine({ line, filePath }) {
334
+ const parsed = safeJsonParse(line);
335
+ if (!parsed || typeof parsed !== "object") return null;
336
+ const payload = parsed.payload ?? {};
337
+ const sessionId = payload.session_id ?? payload.id ?? parsed.session_id ?? extractSessionIdFromPath(filePath);
338
+ if (parsed.type === "event_msg") {
339
+ const eventType = payload.type ?? "unknown";
340
+ if (![
341
+ "user_message",
342
+ "agent_message",
343
+ "task_started",
344
+ "task_complete",
345
+ "token_count",
346
+ "agent_reasoning",
347
+ "turn_aborted",
348
+ "context_compacted"
349
+ ].includes(eventType)) return null;
350
+ const kind = eventType === "user_message" ? "user" : eventType === "agent_message" ? "assistant" : "system";
351
+ 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})` : ""}`;
352
+ return {
353
+ sessionId,
354
+ eventType: `event_msg.${eventType}`,
355
+ kind,
356
+ timestamp: parsed.timestamp ?? (/* @__PURE__ */ new Date()).toISOString(),
357
+ message: toMessage(message),
358
+ raw: parsed
359
+ };
360
+ }
361
+ if (parsed.type === "session_meta") return {
362
+ sessionId,
363
+ eventType: "session_meta",
364
+ kind: "system",
365
+ timestamp: parsed.timestamp ?? (/* @__PURE__ */ new Date()).toISOString(),
366
+ message: `Session started in ${payload.cwd ?? "unknown cwd"}`,
367
+ raw: parsed
368
+ };
369
+ if (parsed.type === "response_item") {
370
+ const subtype = payload.type;
371
+ const ts = parsed.timestamp ?? (/* @__PURE__ */ new Date()).toISOString();
372
+ if (subtype === "message") {
373
+ const text = (payload.content ?? []).map((c) => c.text ?? "").filter(Boolean).join("\n");
374
+ return {
375
+ sessionId,
376
+ eventType: "response_item.message",
377
+ kind: {
378
+ developer: "system",
379
+ assistant: "assistant",
380
+ user: "user"
381
+ }[payload.role] ?? normalizeRole(payload.role),
382
+ timestamp: ts,
383
+ message: toMessage(text || `[${payload.role ?? "unknown"} message]`),
384
+ raw: parsed
385
+ };
386
+ }
387
+ if (subtype === "reasoning") return {
388
+ sessionId,
389
+ eventType: "response_item.reasoning",
390
+ kind: "system",
391
+ timestamp: ts,
392
+ message: toMessage((payload.summary ?? []).map((s) => s.text ?? "").filter(Boolean).join("\n") || "[reasoning]"),
393
+ raw: parsed
394
+ };
395
+ if (subtype === "function_call") return {
396
+ sessionId,
397
+ eventType: "response_item.function_call",
398
+ kind: "system",
399
+ timestamp: ts,
400
+ message: toMessage(`[${payload.name ?? "unknown"}] ${payload.arguments ?? ""}`),
401
+ raw: parsed
402
+ };
403
+ if (subtype === "function_call_output") return {
404
+ sessionId,
405
+ eventType: "response_item.function_call_output",
406
+ kind: "system",
407
+ timestamp: ts,
408
+ message: toMessage(payload.output ?? `[output ${payload.call_id ?? ""}]`),
409
+ raw: parsed
410
+ };
411
+ if (subtype === "web_search_call") {
412
+ const query = payload.action?.query;
413
+ return {
414
+ sessionId,
415
+ eventType: "response_item.web_search_call",
416
+ kind: "system",
417
+ timestamp: ts,
418
+ message: query ? `[web_search] ${query}` : "[web_search]",
419
+ raw: parsed
420
+ };
421
+ }
422
+ if (subtype === "custom_tool_call") return {
423
+ sessionId,
424
+ eventType: "response_item.custom_tool_call",
425
+ kind: "system",
426
+ timestamp: ts,
427
+ message: toMessage(`[${payload.name ?? "unknown"}] ${payload.input ?? ""}`),
428
+ raw: parsed
429
+ };
430
+ if (subtype === "custom_tool_call_output") return {
431
+ sessionId,
432
+ eventType: "response_item.custom_tool_call_output",
433
+ kind: "system",
434
+ timestamp: ts,
435
+ message: toMessage(payload.output ?? `[output ${payload.call_id ?? ""}]`),
436
+ raw: parsed
437
+ };
438
+ return null;
439
+ }
440
+ if (parsed.type === "compacted") return {
441
+ sessionId,
442
+ eventType: "compacted",
443
+ kind: "system",
444
+ timestamp: parsed.timestamp ?? (/* @__PURE__ */ new Date()).toISOString(),
445
+ message: "Session compacted",
446
+ raw: parsed
447
+ };
448
+ if (parsed.type === "turn_context") return {
449
+ sessionId,
450
+ eventType: "turn_context",
451
+ kind: "system",
452
+ timestamp: parsed.timestamp ?? (/* @__PURE__ */ new Date()).toISOString(),
453
+ message: `Turn context: model=${payload.model}, policy=${payload.approval_policy}`,
454
+ raw: parsed
455
+ };
456
+ return null;
457
+ }
458
+
459
+ //#endregion
460
+ //#region ../../packages/parsers/src/agents/openclaw.mjs
461
+ function extractOpenClawTextContent(content) {
462
+ if (!content) return "";
463
+ if (typeof content === "string") return normalizeWhitespace(content);
464
+ if (Array.isArray(content)) {
465
+ const textParts = [];
466
+ for (const item of content) {
467
+ if (!item || typeof item !== "object") continue;
468
+ if (item.type === "text" && typeof item.text === "string") {
469
+ const chunk = normalizeWhitespace(item.text);
470
+ if (chunk) textParts.push(chunk);
471
+ }
472
+ }
473
+ return textParts.join("\n");
474
+ }
475
+ if (typeof content === "object" && typeof content.text === "string") return normalizeWhitespace(content.text);
476
+ return "";
477
+ }
478
+ function extractOpenClawToolCalls(content) {
479
+ if (!Array.isArray(content)) return [];
480
+ const names = [];
481
+ for (const item of content) {
482
+ if (!item || typeof item !== "object" || item.type !== "toolCall") continue;
483
+ const name = normalizeWhitespace(item.name);
484
+ if (name) names.push(name);
485
+ }
486
+ return names;
487
+ }
488
+ function buildOpenClawRaw(parsed) {
489
+ const raw = {
490
+ type: parsed.type,
491
+ id: parsed.id,
492
+ parentId: parsed.parentId,
493
+ timestamp: parsed.timestamp
494
+ };
495
+ if (parsed.type === "session") {
496
+ raw.session = {
497
+ id: parsed.id,
498
+ version: parsed.version,
499
+ cwd: parsed.cwd,
500
+ parentSession: parsed.parentSession
501
+ };
502
+ return raw;
503
+ }
504
+ if (parsed.type === "custom") {
505
+ raw.customType = parsed.customType;
506
+ if (parsed.customType === "model-snapshot" && parsed.data && typeof parsed.data === "object") raw.data = {
507
+ provider: parsed.data.provider,
508
+ modelApi: parsed.data.modelApi,
509
+ modelId: parsed.data.modelId,
510
+ timestamp: parsed.data.timestamp
511
+ };
512
+ return raw;
513
+ }
514
+ if (parsed.message && typeof parsed.message === "object") {
515
+ 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) : [];
516
+ raw.message = {
517
+ role: parsed.message.role,
518
+ stopReason: parsed.message.stopReason,
519
+ toolName: parsed.message.toolName,
520
+ toolCallId: parsed.message.toolCallId,
521
+ isError: parsed.message.isError,
522
+ contentTypes
523
+ };
524
+ }
525
+ if (parsed.type === "compaction") raw.compaction = {
526
+ firstKeptEntryId: parsed.firstKeptEntryId,
527
+ tokensBefore: parsed.tokensBefore
528
+ };
529
+ else if (parsed.type === "branch_summary") raw.branchSummary = {
530
+ firstKeptEntryId: parsed.firstKeptEntryId,
531
+ summary: typeof parsed.summary === "string" ? truncateString(parsed.summary, 350) : ""
532
+ };
533
+ return raw;
534
+ }
535
+ function parseOpenClawLine({ line, filePath }) {
536
+ const parsed = safeJsonParse(line);
537
+ if (!parsed || typeof parsed !== "object") return null;
538
+ const type = String(parsed.type ?? "").toLowerCase();
539
+ const sessionId = parsed.session_id ?? parsed.sessionId ?? parsed.message?.session_id ?? parsed.message?.sessionId ?? extractSessionIdFromPath(filePath);
540
+ const timestamp = parsed.timestamp ?? parsed.message?.timestamp ?? (/* @__PURE__ */ new Date()).toISOString();
541
+ if (type === "session") return {
542
+ sessionId,
543
+ eventType: "openclaw.session",
544
+ kind: "system",
545
+ timestamp,
546
+ message: toMessage(`Session started in ${parsed.cwd ?? "unknown cwd"}`),
547
+ raw: buildOpenClawRaw(parsed)
548
+ };
549
+ if (type === "custom") {
550
+ const customType = normalizeWhitespace(parsed.customType || "custom");
551
+ if (customType === "openclaw.cache-ttl") return null;
552
+ let message = `Custom event: ${customType}`;
553
+ if (customType === "model-snapshot" && parsed.data && typeof parsed.data === "object") {
554
+ const provider = normalizeWhitespace(parsed.data.provider || "");
555
+ const modelId = normalizeWhitespace(parsed.data.modelId || "");
556
+ const modelApi = normalizeWhitespace(parsed.data.modelApi || "");
557
+ const details = [provider, modelId].filter(Boolean).join("/");
558
+ message = `Model snapshot${details ? `: ${details}` : ""}${modelApi ? ` (${modelApi})` : ""}`;
559
+ }
560
+ return {
561
+ sessionId,
562
+ eventType: `openclaw.custom.${customType || "custom"}`,
563
+ kind: "system",
564
+ timestamp,
565
+ message: toMessage(message),
566
+ raw: buildOpenClawRaw(parsed)
567
+ };
568
+ }
569
+ if (type === "compaction") return {
570
+ sessionId,
571
+ eventType: "openclaw.compaction",
572
+ kind: "system",
573
+ timestamp,
574
+ message: toMessage("Session compaction summary updated"),
575
+ raw: buildOpenClawRaw(parsed)
576
+ };
577
+ if (type === "branch_summary") return {
578
+ sessionId,
579
+ eventType: "openclaw.branch_summary",
580
+ kind: "system",
581
+ timestamp,
582
+ message: toMessage(normalizeWhitespace(parsed.summary || "") || "Branch summary updated"),
583
+ raw: buildOpenClawRaw(parsed)
584
+ };
585
+ if (type !== "message" && type !== "custom_message") return null;
586
+ const eventMessage = parsed.message ?? {};
587
+ const role = String(eventMessage.role ?? "").toLowerCase();
588
+ if (role === "user" || role === "assistant") {
589
+ const text = extractOpenClawTextContent(eventMessage.content);
590
+ if (text) return {
591
+ sessionId,
592
+ eventType: `openclaw.${role}`,
593
+ kind: role === "user" ? "user" : "assistant",
594
+ timestamp,
595
+ message: toMessage(text),
596
+ raw: buildOpenClawRaw(parsed)
597
+ };
598
+ if (role === "assistant") {
599
+ const toolCalls = extractOpenClawToolCalls(eventMessage.content);
600
+ if (toolCalls.length > 0) return {
601
+ sessionId,
602
+ eventType: "openclaw.assistant.tool_use",
603
+ kind: "system",
604
+ timestamp,
605
+ message: toMessage(`Assistant requested tools: ${toolCalls.slice(0, 5).join(", ")}${toolCalls.length > 5 ? ` (+${toolCalls.length - 5})` : ""}`),
606
+ raw: buildOpenClawRaw(parsed)
607
+ };
608
+ }
609
+ return null;
610
+ }
611
+ if (role === "toolresult") {
612
+ const toolName = normalizeWhitespace(eventMessage.toolName || "");
613
+ const isError = Boolean(eventMessage.isError);
614
+ let message = `Tool result${toolName ? `: ${toolName}` : ""} (${isError ? "error" : "ok"})`;
615
+ const text = extractOpenClawTextContent(eventMessage.content);
616
+ if (text) message = `${message} ${truncateString(text, 320)}`;
617
+ return {
618
+ sessionId,
619
+ eventType: "openclaw.tool_result",
620
+ kind: "system",
621
+ timestamp,
622
+ message: toMessage(message),
623
+ raw: buildOpenClawRaw(parsed)
624
+ };
625
+ }
626
+ return null;
627
+ }
628
+
629
+ //#endregion
630
+ //#region ../../packages/parsers/src/agents/cursor.mjs
631
+ function parseCursorLine({ line, filePath }) {
632
+ const parsed = safeJsonParse(line);
633
+ if (!parsed || typeof parsed !== "object") return null;
634
+ if (!parsed.type && parsed.role) parsed.type = parsed.role;
635
+ const result = parseClaudeCodeLine({
636
+ line: JSON.stringify(parsed),
637
+ filePath
638
+ });
639
+ if (!result) return null;
640
+ result.eventType = result.eventType.replace(/^claude\./, "cursor.");
641
+ if (result.kind === "user" && result.message) result.message = stripIDEContextTags(result.message);
642
+ return result;
643
+ }
644
+
645
+ //#endregion
646
+ //#region ../../packages/parsers/src/agents/gemini.mjs
647
+ const FILE_MOD_TOOLS = [
648
+ "write_file",
649
+ "edit_file",
650
+ "save_file",
651
+ "replace"
652
+ ];
653
+ const FILE_PATH_KEYS = [
654
+ "file_path",
655
+ "path",
656
+ "filePath",
657
+ "filename"
658
+ ];
659
+ function extractGeminiTextContent(content) {
660
+ if (!content) return "";
661
+ if (typeof content === "string") return preserveText(content);
662
+ if (Array.isArray(content)) {
663
+ const parts = [];
664
+ for (const item of content) {
665
+ if (!item || typeof item !== "object") continue;
666
+ if (typeof item.text === "string") {
667
+ const chunk = preserveText(item.text);
668
+ if (chunk) parts.push(chunk);
669
+ }
670
+ }
671
+ return parts.join("\n");
672
+ }
673
+ if (typeof content === "object" && typeof content.text === "string") return preserveText(content.text);
674
+ return "";
675
+ }
676
+ function formatToolCall(tc) {
677
+ const name = tc.name ?? "unknown";
678
+ const args = tc.args ?? {};
679
+ let filePath = "";
680
+ for (const key of FILE_PATH_KEYS) if (typeof args[key] === "string" && args[key]) {
681
+ filePath = args[key];
682
+ break;
683
+ }
684
+ if (FILE_MOD_TOOLS.includes(name)) {
685
+ const content = preserveText(args.content ?? args.file_text ?? args.new_content ?? "");
686
+ if (content) return `[${name}] ${filePath}\n${truncateString(content, 500)}`;
687
+ return `[${name}] ${filePath}`;
688
+ }
689
+ const compact = JSON.stringify(args);
690
+ const detail = compact.length > 500 ? compact.slice(0, 500) + "..." : compact;
691
+ return `[${name}]${filePath ? ` ${filePath}` : ""} ${detail}`;
692
+ }
693
+ function extractGeminiSessionId(filePath) {
694
+ const base = path.basename(filePath, ".json");
695
+ const match = base.match(/session-[\d-]+-(.+)$/);
696
+ if (match) return match[1];
697
+ return base || "unknown-session";
698
+ }
699
+ function extractGeminiTimestamp(filePath) {
700
+ const match = path.basename(filePath, ".json").match(/session-(\d{4})(\d{2})(\d{2})/);
701
+ if (match) return `${match[1]}-${match[2]}-${match[3]}T00:00:00.000Z`;
702
+ return (/* @__PURE__ */ new Date()).toISOString();
703
+ }
704
+ function parseGeminiFile({ fileContents, filePath }) {
705
+ const parsed = safeJsonParse(fileContents);
706
+ if (!parsed || typeof parsed !== "object") return [];
707
+ const messages = parsed.messages;
708
+ if (!Array.isArray(messages) || messages.length === 0) return [];
709
+ const sessionId = extractGeminiSessionId(filePath);
710
+ const fileTimestamp = extractGeminiTimestamp(filePath);
711
+ const events = [];
712
+ for (let i = 0; i < messages.length; i++) {
713
+ const msg = messages[i];
714
+ if (!msg || typeof msg !== "object") continue;
715
+ const type = String(msg.type ?? "").toLowerCase();
716
+ const isUser = type === "user";
717
+ if (!isUser && !(type === "gemini")) continue;
718
+ const text = extractGeminiTextContent(msg.content);
719
+ const toolCallTexts = [];
720
+ if (Array.isArray(msg.toolCalls)) for (const tc of msg.toolCalls) {
721
+ if (!tc || typeof tc !== "object") continue;
722
+ toolCallTexts.push(formatToolCall(tc));
723
+ }
724
+ const parts = [];
725
+ if (text) parts.push(text);
726
+ if (toolCallTexts.length) parts.push(toolCallTexts.join("\n\n"));
727
+ const message = parts.join("\n\n");
728
+ if (!message) continue;
729
+ events.push({
730
+ sessionId,
731
+ eventType: isUser ? "gemini.user" : "gemini.assistant",
732
+ kind: isUser ? "user" : "assistant",
733
+ timestamp: msg.timestamp ?? fileTimestamp,
734
+ message: toMessage(message),
735
+ raw: {
736
+ type: msg.type,
737
+ id: msg.id,
738
+ index: i,
739
+ hasToolCalls: toolCallTexts.length > 0,
740
+ toolCallCount: toolCallTexts.length
741
+ }
742
+ });
743
+ }
744
+ return events;
745
+ }
746
+
747
+ //#endregion
748
+ //#region ../../packages/parsers/src/gstack.mjs
749
+ function extractGstackProjectSlug(filePath) {
750
+ return filePath.match(/\.gstack\/projects\/([^/]+)\//)?.[1] ?? "unknown-project";
751
+ }
752
+ function gstackFileType(filePath) {
753
+ const base = filePath.split("/").pop() ?? "";
754
+ if (base === "learnings.jsonl") return "learning";
755
+ if (base === "timeline.jsonl") return "timeline";
756
+ if (base === "resources-shown.jsonl") return "resource";
757
+ if (base.endsWith("-reviews.jsonl")) return "review";
758
+ return "unknown";
759
+ }
760
+ function parseGstackLine({ line, filePath }) {
761
+ const parsed = safeJsonParse(line);
762
+ if (!parsed || typeof parsed !== "object") return null;
763
+ const projectSlug = extractGstackProjectSlug(filePath);
764
+ const fileType = gstackFileType(filePath);
765
+ const timestamp = parsed.ts ?? parsed.timestamp ?? (/* @__PURE__ */ new Date()).toISOString();
766
+ const sessionId = projectSlug;
767
+ if (fileType === "learning") {
768
+ const insight = normalizeWhitespace(parsed.insight);
769
+ if (!insight) return null;
770
+ const skill = parsed.skill ?? "unknown";
771
+ const conf = typeof parsed.confidence === "number" ? ` [${parsed.confidence}/10]` : "";
772
+ return {
773
+ sessionId,
774
+ eventType: `gstack.learning.${parsed.type ?? "insight"}`,
775
+ kind: "system",
776
+ timestamp,
777
+ message: toMessage(`[${skill}]${conf} ${insight}`),
778
+ raw: parsed
779
+ };
780
+ }
781
+ if (fileType === "timeline") {
782
+ const skill = parsed.skill ?? "unknown";
783
+ const event = parsed.event ?? "unknown";
784
+ const branch = parsed.branch ? ` (${parsed.branch})` : "";
785
+ const outcome = parsed.outcome ? ` → ${parsed.outcome}` : "";
786
+ const duration = parsed.duration_s ? ` ${parsed.duration_s}s` : "";
787
+ return {
788
+ sessionId,
789
+ eventType: `gstack.timeline.${event}`,
790
+ kind: "system",
791
+ timestamp,
792
+ message: toMessage(`${skill} ${event}${branch}${outcome}${duration}`),
793
+ raw: parsed
794
+ };
795
+ }
796
+ if (fileType === "review") {
797
+ const skill = parsed.skill ?? "review";
798
+ const score = parsed.overall_score != null ? ` score=${parsed.overall_score}` : "";
799
+ return {
800
+ sessionId,
801
+ eventType: "gstack.review",
802
+ kind: "system",
803
+ timestamp,
804
+ message: toMessage(`${skill}${parsed.status ? ` [${parsed.status}]` : ""}${score}`),
805
+ raw: parsed
806
+ };
807
+ }
808
+ if (fileType === "resource") {
809
+ const title = normalizeWhitespace(parsed.title);
810
+ const url = parsed.url ?? "";
811
+ if (!title && !url) return null;
812
+ return {
813
+ sessionId,
814
+ eventType: "gstack.resource",
815
+ kind: "system",
816
+ timestamp,
817
+ message: toMessage(title ? `${title} — ${url}` : url),
818
+ raw: parsed
819
+ };
820
+ }
821
+ return null;
822
+ }
823
+
824
+ //#endregion
825
+ //#region ../../packages/parsers/src/writers/claude.mjs
826
+ function isUuid(value) {
827
+ 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());
828
+ }
829
+ function normalizeSessionUuid(raw) {
830
+ const value = String(raw ?? "").trim();
831
+ if (isUuid(value)) return value;
832
+ return randomUUID();
833
+ }
834
+ function claudeSessionFilePath(sessionId, cwd, baseDir) {
835
+ const root = baseDir || expandHome("~/.claude/projects");
836
+ return path.join(root, claudeProjectDirName(cwd), `${sessionId}.jsonl`);
837
+ }
838
+ async function hasLocalClaudeSession(sessionId, cwd = "", baseDir) {
839
+ const id = String(sessionId ?? "").trim();
840
+ if (!id) return false;
841
+ const preferredPath = claudeSessionFilePath(id, cwd || process.cwd(), baseDir);
842
+ try {
843
+ if ((await fs$1.stat(preferredPath)).isFile()) return true;
844
+ } catch {}
845
+ const fg = (await import("fast-glob")).default;
846
+ const root = baseDir || expandHome("~/.claude/projects");
847
+ return (await fg([path.join(root, `**/*${id}.jsonl`)], {
848
+ onlyFiles: true,
849
+ absolute: true,
850
+ unique: true,
851
+ suppressErrors: true,
852
+ followSymbolicLinks: false
853
+ })).some((filePath) => path.basename(filePath, ".jsonl") === id);
854
+ }
855
+ function isNativeClaudeEntry(raw) {
856
+ return raw && typeof raw === "object" && "type" in raw && ("sessionId" in raw || "uuid" in raw);
857
+ }
858
+ async function writeClaudeSession({ sessionId, cwd, messages, baseDir }) {
859
+ const runCwd = String(cwd || process.cwd());
860
+ const resolvedSessionId = normalizeSessionUuid(sessionId);
861
+ const firstTs = asIso(firstMessageTimestamp(messages));
862
+ const filePath = claudeSessionFilePath(resolvedSessionId, runCwd, baseDir);
863
+ if (await hasLocalClaudeSession(resolvedSessionId, runCwd, baseDir)) return {
864
+ written: false,
865
+ reason: "already_exists",
866
+ filePath,
867
+ sessionId: resolvedSessionId
868
+ };
869
+ try {
870
+ await fs$1.mkdir(path.dirname(filePath), { recursive: true });
871
+ const lines = [];
872
+ let parentUuid = null;
873
+ for (let i = 0; i < (messages?.length ?? 0); i += 1) {
874
+ const message = messages[i];
875
+ const raw = message?.content?.raw;
876
+ if (isNativeClaudeEntry(raw)) {
877
+ const entryUuid = raw.uuid || randomUUID();
878
+ lines.push(JSON.stringify({
879
+ ...raw,
880
+ parentUuid,
881
+ sessionId: resolvedSessionId
882
+ }));
883
+ parentUuid = entryUuid;
884
+ continue;
885
+ }
886
+ const normalizedRole = normalizeRole(message?.role);
887
+ const role = normalizedRole === "assistant" ? "assistant" : normalizedRole === "user" ? "user" : "assistant";
888
+ const text = extractMessageText(message, normalizedRole);
889
+ if (!text) continue;
890
+ const ts = asIso(message?.content?.timestamp ?? message?.metadata?.timestamp ?? new Date(new Date(firstTs).getTime() + i * 1e3).toISOString());
891
+ const entryUuid = randomUUID();
892
+ lines.push(JSON.stringify({
893
+ parentUuid,
894
+ isSidechain: false,
895
+ userType: "external",
896
+ cwd: runCwd,
897
+ sessionId: resolvedSessionId,
898
+ version: "adapter",
899
+ gitBranch: "",
900
+ type: role,
901
+ message: {
902
+ role,
903
+ content: [{
904
+ type: "text",
905
+ text
906
+ }]
907
+ },
908
+ timestamp: ts,
909
+ uuid: entryUuid
910
+ }));
911
+ parentUuid = entryUuid;
912
+ }
913
+ if (lines.length === 0) lines.push(JSON.stringify({
914
+ parentUuid: null,
915
+ isSidechain: false,
916
+ userType: "external",
917
+ cwd: runCwd,
918
+ sessionId: resolvedSessionId,
919
+ version: "adapter",
920
+ gitBranch: "",
921
+ type: "assistant",
922
+ message: {
923
+ role: "assistant",
924
+ content: [{
925
+ type: "text",
926
+ text: "[system] Session restored from UltraContext with no user/assistant messages."
927
+ }]
928
+ },
929
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
930
+ uuid: randomUUID()
931
+ }));
932
+ await fs$1.writeFile(filePath, `${lines.join("\n")}\n`, "utf8");
933
+ return {
934
+ written: true,
935
+ reason: "created",
936
+ filePath,
937
+ sessionId: resolvedSessionId
938
+ };
939
+ } catch (error) {
940
+ return {
941
+ written: false,
942
+ reason: "write_failed",
943
+ filePath,
944
+ sessionId: resolvedSessionId,
945
+ error: error instanceof Error ? error.message : String(error)
946
+ };
947
+ }
948
+ }
949
+ function extractMessageText(message, normalizedRole) {
950
+ const raw = message?.content?.raw;
951
+ if (raw?.type === "response_item" && raw?.payload?.type === "message") {
952
+ const text = (raw.payload.content ?? []).map((c) => c.text ?? "").filter(Boolean).join("\n");
953
+ if (text) return normalizedRole === "system" ? `[system] ${text}` : text;
954
+ }
955
+ if (raw?.type === "event_msg" && raw?.payload?.type === "user_message") return raw.payload.message ?? "";
956
+ const msg = message?.content?.message ?? "";
957
+ if (!msg) return "";
958
+ return normalizedRole === "system" ? `[system] ${msg}` : msg;
959
+ }
960
+
961
+ //#endregion
962
+ //#region ../../packages/parsers/src/writers/codex.mjs
963
+ function buildEventMsgLine(message, fallbackTs) {
964
+ const ts = asIso(message?.content?.timestamp ?? message?.metadata?.timestamp ?? fallbackTs);
965
+ const role = normalizeRole(message?.role);
966
+ const text = coerceMessageText(message).trim();
967
+ if (!text) return null;
968
+ if (role === "user") return {
969
+ timestamp: ts,
970
+ type: "event_msg",
971
+ payload: {
972
+ type: "user_message",
973
+ message: text,
974
+ images: [],
975
+ local_images: [],
976
+ text_elements: []
977
+ }
978
+ };
979
+ if (role === "assistant") return {
980
+ timestamp: ts,
981
+ type: "event_msg",
982
+ payload: {
983
+ type: "agent_message",
984
+ message: text
985
+ }
986
+ };
987
+ return {
988
+ timestamp: ts,
989
+ type: "event_msg",
990
+ payload: {
991
+ type: "agent_message",
992
+ message: `[system] ${text}`
993
+ }
994
+ };
995
+ }
996
+ function sessionFilePath(sessionId, firstTimestamp, baseDir) {
997
+ const iso = asIso(firstTimestamp);
998
+ const [year, month, day] = iso.slice(0, 10).split("-");
999
+ const stamp = iso.replace(/\.\d{3}Z$/, "").replace(/:/g, "-").replace("Z", "");
1000
+ const root = baseDir || expandHome("~/.codex/sessions");
1001
+ const fileName = `rollout-${stamp}-${sessionId}.jsonl`;
1002
+ return path.join(root, year, month, day, fileName);
1003
+ }
1004
+ async function hasLocalCodexSession(sessionId, baseDir) {
1005
+ const id = String(sessionId ?? "").trim();
1006
+ if (!id) return false;
1007
+ const fg = (await import("fast-glob")).default;
1008
+ const root = baseDir || expandHome("~/.codex/sessions");
1009
+ return (await fg([path.join(root, `**/*${id}*.jsonl`)], {
1010
+ onlyFiles: true,
1011
+ absolute: true,
1012
+ unique: true,
1013
+ suppressErrors: true,
1014
+ followSymbolicLinks: false
1015
+ })).some((filePath) => filePath.includes(id));
1016
+ }
1017
+ async function writeCodexSession({ sessionId, cwd, messages, baseDir }) {
1018
+ const id = String(sessionId ?? "").trim();
1019
+ if (!id) return {
1020
+ written: false,
1021
+ reason: "missing_session_id",
1022
+ filePath: "",
1023
+ sessionId: ""
1024
+ };
1025
+ if (await hasLocalCodexSession(id, baseDir)) return {
1026
+ written: false,
1027
+ reason: "already_exists",
1028
+ filePath: "",
1029
+ sessionId: id
1030
+ };
1031
+ const firstTs = asIso(firstMessageTimestamp(messages));
1032
+ const filePath = sessionFilePath(id, firstTs, baseDir);
1033
+ try {
1034
+ await fs$1.mkdir(path.dirname(filePath), { recursive: true });
1035
+ const lines = [];
1036
+ lines.push(JSON.stringify({
1037
+ timestamp: firstTs,
1038
+ type: "session_meta",
1039
+ payload: {
1040
+ id,
1041
+ timestamp: firstTs,
1042
+ cwd: cwd || process.cwd(),
1043
+ originator: "ultracontext_daemon",
1044
+ cli_version: "restored",
1045
+ source: "cli",
1046
+ model_provider: "openai"
1047
+ }
1048
+ }));
1049
+ let emitted = 0;
1050
+ for (let i = 0; i < (messages?.length ?? 0); i += 1) {
1051
+ const fallbackTs = new Date(new Date(firstTs).getTime() + i * 1e3).toISOString();
1052
+ const line = buildEventMsgLine(messages[i], fallbackTs);
1053
+ if (!line) continue;
1054
+ lines.push(JSON.stringify(line));
1055
+ emitted += 1;
1056
+ }
1057
+ if (emitted === 0) lines.push(JSON.stringify({
1058
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1059
+ type: "event_msg",
1060
+ payload: {
1061
+ type: "agent_message",
1062
+ message: "[system] Session restored from UltraContext with no user/assistant messages."
1063
+ }
1064
+ }));
1065
+ await fs$1.writeFile(filePath, `${lines.join("\n")}\n`, "utf8");
1066
+ return {
1067
+ written: true,
1068
+ reason: "created",
1069
+ filePath,
1070
+ sessionId: id
1071
+ };
1072
+ } catch (error) {
1073
+ return {
1074
+ written: false,
1075
+ reason: "write_failed",
1076
+ filePath,
1077
+ sessionId: id,
1078
+ error: error instanceof Error ? error.message : String(error)
1079
+ };
1080
+ }
1081
+ }
1082
+
1083
+ //#endregion
1084
+ //#region ../../packages/parsers/src/switch.mjs
1085
+ const MAX_SESSION_BYTES = 256 * 1024 * 1024;
1086
+ async function findLatestSession(pattern, excludePattern) {
1087
+ const fg = (await import("fast-glob")).default;
1088
+ const files = await fg([pattern], {
1089
+ onlyFiles: true,
1090
+ absolute: true,
1091
+ unique: true,
1092
+ suppressErrors: true,
1093
+ followSymbolicLinks: false,
1094
+ ignore: excludePattern ? [excludePattern] : [],
1095
+ stats: true
1096
+ });
1097
+ if (!files.length) return null;
1098
+ files.sort((a, b) => (b.stats?.mtimeMs ?? 0) - (a.stats?.mtimeMs ?? 0));
1099
+ return files[0].path;
1100
+ }
1101
+ async function resolveSessionPath(source, cwd, baseDirs = {}) {
1102
+ if (source === "claude") {
1103
+ const projectDir = claudeProjectDirName(cwd);
1104
+ const root = baseDirs.claude || expandHome("~/.claude/projects");
1105
+ return findLatestSession(path.join(root, projectDir, "*.jsonl"), "**/subagents/**");
1106
+ }
1107
+ if (source === "codex") {
1108
+ const root = baseDirs.codex || expandHome("~/.codex/sessions");
1109
+ return findLatestSession(path.join(root, "**/*.jsonl"));
1110
+ }
1111
+ return null;
1112
+ }
1113
+ async function readLocalSession({ source, sessionPath, cwd, sourceRoots }) {
1114
+ const resolvedCwd = cwd || process.cwd();
1115
+ const filePath = sessionPath || await resolveSessionPath(source, resolvedCwd, sourceRoots);
1116
+ if (!filePath) throw new Error(`No ${source} session found. Specify --session <path>.`);
1117
+ if (!fs.existsSync(filePath)) throw new Error(`Session file not found: ${filePath}`);
1118
+ const stat = fs.lstatSync(filePath);
1119
+ if (!stat.isFile()) throw new Error(`Session path is not a regular file: ${filePath}`);
1120
+ if (stat.size > MAX_SESSION_BYTES) throw new Error(`Session file too large: ${stat.size} bytes (max ${MAX_SESSION_BYTES}).`);
1121
+ const lines = fs.readFileSync(filePath, "utf8").split("\n").filter((l) => l.trim());
1122
+ const parser = source === "claude" ? parseClaudeCodeLine : parseCodexLine;
1123
+ const messages = [];
1124
+ let extractedCwd = null;
1125
+ let sessionId = null;
1126
+ for (const line of lines) {
1127
+ const parsed = parser({
1128
+ line,
1129
+ filePath
1130
+ });
1131
+ if (!parsed) continue;
1132
+ if (!sessionId && parsed.sessionId) sessionId = parsed.sessionId;
1133
+ if (source === "claude" && parsed.raw?.cwd && !extractedCwd && isSafeCwd(parsed.raw.cwd)) extractedCwd = parsed.raw.cwd;
1134
+ if (source === "codex" && parsed.raw?.type === "session_meta" && !extractedCwd && isSafeCwd(parsed.raw.payload?.cwd)) extractedCwd = parsed.raw.payload.cwd;
1135
+ if (parsed.kind === "user" || parsed.kind === "assistant") messages.push(parsed);
1136
+ }
1137
+ return {
1138
+ messages,
1139
+ sessionId: sessionId || extractSessionIdFromPath(filePath),
1140
+ cwd: extractedCwd || resolvedCwd,
1141
+ filePath
1142
+ };
1143
+ }
1144
+ function toWriterMessages(messages, source) {
1145
+ return messages.map((m) => ({
1146
+ role: m.kind,
1147
+ content: {
1148
+ message: typeof m.message === "string" ? m.message : m.message ?? "",
1149
+ timestamp: m.timestamp,
1150
+ raw: m.raw
1151
+ },
1152
+ metadata: { source }
1153
+ }));
1154
+ }
1155
+ async function switchSession({ source, target, sessionPath, cwd, last, baseDir, sourceRoots }) {
1156
+ const session = await readLocalSession({
1157
+ source,
1158
+ sessionPath,
1159
+ cwd,
1160
+ sourceRoots
1161
+ });
1162
+ let msgs = session.messages;
1163
+ if (typeof last === "number" && last > 0) msgs = msgs.slice(-last);
1164
+ const writerMessages = toWriterMessages(msgs, source);
1165
+ const firstId = randomUUID();
1166
+ const writer = target === "codex" ? writeCodexSession : writeClaudeSession;
1167
+ let result = await writer({
1168
+ sessionId: firstId,
1169
+ cwd: session.cwd,
1170
+ messages: writerMessages,
1171
+ baseDir
1172
+ });
1173
+ let usedId = firstId;
1174
+ if (!result.written && result.reason === "already_exists") {
1175
+ const retryId = randomUUID();
1176
+ result = await writer({
1177
+ sessionId: retryId,
1178
+ cwd: session.cwd,
1179
+ messages: writerMessages,
1180
+ baseDir
1181
+ });
1182
+ usedId = retryId;
1183
+ }
1184
+ return {
1185
+ written: result.written,
1186
+ filePath: result.filePath,
1187
+ sessionId: result.written ? result.sessionId || usedId : null,
1188
+ messageCount: writerMessages.length,
1189
+ reason: result.reason,
1190
+ cwd: session.cwd
1191
+ };
1192
+ }
1193
+
1194
+ //#endregion
1195
+ //#region ../../packages/parsers/src/index.mjs
1196
+ var src_exports = /* @__PURE__ */ __exportAll({ switchSession: () => switchSession });
1197
+
1198
+ //#endregion
1199
+ export { writeClaudeSession as a, parseCursorLine as c, parseClaudeCodeLine as d, hasLocalClaudeSession as i, parseOpenClawLine as l, hasLocalCodexSession as n, parseGstackLine as o, writeCodexSession as r, parseGeminiFile as s, src_exports as t, parseCodexLine as u };
1200
+ //# sourceMappingURL=src-Bovo1ukU.mjs.map