agentel 0.2.4 → 0.2.5

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/src/importers.js CHANGED
@@ -22,6 +22,8 @@ const { canonicalWebProvider, derivedAccountId, getWebAccount, upsertWebAccount
22
22
 
23
23
  const WEB_TOKEN_ESTIMATE_CHARS = 4;
24
24
  const WEB_CHAT_TOKEN_ESTIMATION_METHOD = "web-message-parts-chars-v1";
25
+ const OPENCODE_SOURCE_KINDS = new Set(["cli", "desktop", "web"]);
26
+ const OPENCODE_SESSION_ID_RE = /\bses_[A-Za-z0-9]+\b/g;
25
27
 
26
28
  function importCliHistory(options = {}, env = process.env) {
27
29
  const source = options.source || "all";
@@ -76,6 +78,7 @@ function importJsonlProvider(provider, roots, since, options = {}, env = process
76
78
  const archived = archivedSessionKeys(env);
77
79
  const files =
78
80
  provider === "claude_code" ? claudeFiles(env) : provider === "claude_sdk" ? claudeSdkFiles(env) : jsonlFiles(roots);
81
+ const claudeCodeMetadata = provider === "claude_code" ? claudeCodeSessionMetadataByCliSessionId(env) : new Map();
79
82
 
80
83
  const candidates = files
81
84
  .map((file) => ({ file, stat: safeStat(file) }))
@@ -95,7 +98,11 @@ function importJsonlProvider(provider, roots, since, options = {}, env = process
95
98
  for (let index = 0; index < candidates.length; index++) {
96
99
  const item = candidates[index];
97
100
  const sourceType = jsonlProviderSourceType(provider);
98
- const fingerprint = `${fingerprintPrefix(sourceType)}:${fileFingerprint(item.file, item.stat)}`;
101
+ const baseFingerprint = `${fingerprintPrefix(sourceType)}:${fileFingerprint(item.file, item.stat)}`;
102
+ const preliminaryMetadata = provider === "claude_code"
103
+ ? claudeCodeMetadata.get(claudeSessionIdFromFilename(item.file)) || null
104
+ : null;
105
+ let fingerprint = claudeCodeImportFingerprint(baseFingerprint, preliminaryMetadata);
99
106
  if (alreadyImportedFile(state, fingerprint, archived, provider)) {
100
107
  summary.skipped++;
101
108
  reportProgress(options, summary, index + 1, item.file);
@@ -115,15 +122,17 @@ function importJsonlProvider(provider, roots, since, options = {}, env = process
115
122
  reportProgress(options, summary, index + 1, item.file);
116
123
  continue;
117
124
  }
118
- const cwd = parsed.cwd || "";
125
+ const sessionId = parsed.sessionId || stableSessionId(provider, item.file, parsed.startedAt, parsed.messages);
126
+ const sessionMetadata = preliminaryMetadata || claudeCodeMetadata.get(sessionId) || null;
127
+ fingerprint = claudeCodeImportFingerprint(baseFingerprint, sessionMetadata);
128
+ const cwd = parsed.cwd || sessionMetadata?.cwd || "";
119
129
  const scopeCanonical = cwd ? "" : uncategorizedScope(provider);
120
- const repo = cwd ? canonicalRepo(cwd) : null;
130
+ const repo = repoInfoForImport(provider, cwd, sessionMetadata);
121
131
  if (options.repos && options.repos.length && (!repo || !options.repos.includes(repo.key))) {
122
132
  summary.skipped++;
123
133
  reportProgress(options, summary, index + 1, item.file);
124
134
  continue;
125
135
  }
126
- const sessionId = parsed.sessionId || stableSessionId(provider, item.file, parsed.startedAt, parsed.messages);
127
136
  if (alreadyImported(state, sessionId, fingerprint, archived, provider)) {
128
137
  state.files[fingerprint] = { sessionId, duplicate: true, at: new Date().toISOString() };
129
138
  summary.skipped++;
@@ -145,9 +154,10 @@ function importJsonlProvider(provider, roots, since, options = {}, env = process
145
154
  startedAt: parsed.startedAt,
146
155
  endedAt: parsed.endedAt,
147
156
  sourcePath: item.file,
148
- sourceFiles: [item.file, ...auxiliaryFiles],
157
+ sourceFiles: [item.file, sessionMetadata?.sourcePath || "", ...auxiliaryFiles].filter(Boolean),
149
158
  sourceType,
150
- title: parsed.title
159
+ title: jsonlSessionTitleForImport(parsed, sessionMetadata),
160
+ sessionSummary: claudeCodeSidecarSessionSummary(sessionMetadata)
151
161
  },
152
162
  env
153
163
  );
@@ -172,7 +182,7 @@ function jsonlProviderSourceType(provider) {
172
182
  function importClaudeDesktopProvider(provider, since, options = {}, env = process.env) {
173
183
  const state = loadImportState(env);
174
184
  const archived = archivedSessionKeys(env);
175
- const sessions = readClaudeDesktopSessions().filter((session) => {
185
+ const sessions = readClaudeDesktopSessions({}, env).filter((session) => {
176
186
  return !options.claudeDesktopKind || session.kind === options.claudeDesktopKind;
177
187
  });
178
188
  const candidates = sessions
@@ -199,7 +209,7 @@ function importClaudeDesktopProvider(provider, since, options = {}, env = proces
199
209
  continue;
200
210
  }
201
211
  const cwd = session.cwd || "";
202
- const repo = cwd ? canonicalRepo(cwd) : null;
212
+ const repo = repoInfoForImport(provider, cwd);
203
213
  if (options.repos && options.repos.length && !matchesImportedSessionRepo(session, repo, options.repos)) {
204
214
  summary.skipped++;
205
215
  reportProgress(options, summary, index + 1, session.sourcePath);
@@ -564,7 +574,7 @@ function structuredSessionReplaceSourcePathCopies(provider, sourceType) {
564
574
  }
565
575
 
566
576
  function structuredSessionUsesSharedRawFiles(provider, sourceType) {
567
- return provider === "opencode" && sourceType === "opencode-sqlite-history";
577
+ return provider === "opencode" && ["opencode-cli-sqlite-history", "opencode-web-sqlite-history", "opencode-sqlite-history", "opencode-desktop-sqlite-history"].includes(sourceType);
568
578
  }
569
579
 
570
580
  function parseAgentJsonl(file, provider) {
@@ -602,16 +612,114 @@ function parseAgentJsonl(file, provider) {
602
612
  }
603
613
  messages.sort((a, b) => String(a.timestamp || "").localeCompare(String(b.timestamp || "")));
604
614
  const deduped = dedupeAdjacentMessages(messages);
615
+ const inferredTitle = inferredJsonlSessionTitle(provider, deduped);
605
616
  return {
606
617
  cwd,
607
618
  sessionId,
608
- title,
619
+ title: title || inferredTitle,
620
+ titleSource: title ? "source" : inferredTitle ? "first-user-prompt" : "",
609
621
  messages: deduped,
610
622
  startedAt: deduped[0]?.timestamp || "",
611
623
  endedAt: deduped[deduped.length - 1]?.timestamp || ""
612
624
  };
613
625
  }
614
626
 
627
+ function repoInfoForImport(provider, cwd, metadata = null) {
628
+ const repoCwd = repoCwdForImport(provider, cwd, metadata);
629
+ if (!repoCwd) return null;
630
+ return canonicalRepo(repoCwd);
631
+ }
632
+
633
+ function repoCwdForImport(provider, cwd, metadata = null) {
634
+ const metadataCwd = firstExistingDirectory(metadata?.originCwd, metadata?.cwd);
635
+ if (metadataCwd) return metadataCwd;
636
+ return claudeWorktreeParentRepo(provider, cwd) || cwd;
637
+ }
638
+
639
+ function claudeWorktreeParentRepo(provider, cwd) {
640
+ if (!isClaudeJsonlProvider(provider)) return "";
641
+ const resolved = path.resolve(String(cwd || ""));
642
+ const marker = `${path.sep}.claude${path.sep}worktrees${path.sep}`;
643
+ const markerIndex = resolved.indexOf(marker);
644
+ if (markerIndex === -1) return "";
645
+ const parentRepo = resolved.slice(0, markerIndex);
646
+ const stat = safeStat(parentRepo);
647
+ return stat && stat.isDirectory() ? parentRepo : "";
648
+ }
649
+
650
+ function inferredJsonlSessionTitle(provider, messages) {
651
+ if (!isClaudeJsonlProvider(provider)) return "";
652
+ const firstUser = (messages || []).find((message) => message.role === "user" && !message.metadata?.providerGenerated);
653
+ return titleFromPrompt(firstUser?.content);
654
+ }
655
+
656
+ function titleFromPrompt(value) {
657
+ const cleaned = firstLine(value).replace(/\s+/g, " ").trim();
658
+ if (!cleaned) return "";
659
+ const max = 96;
660
+ return cleaned.length > max ? `${cleaned.slice(0, max - 1).trimEnd()}…` : cleaned;
661
+ }
662
+
663
+ function isClaudeJsonlProvider(provider) {
664
+ return provider === "claude_code" || provider === "claude_sdk";
665
+ }
666
+
667
+ function jsonlSessionTitleForImport(parsed, metadata = null) {
668
+ if (parsed?.titleSource === "source") return parsed.title || "";
669
+ return firstString(metadata?.title, parsed?.title);
670
+ }
671
+
672
+ function claudeCodeSidecarSessionSummary(metadata = null) {
673
+ if (!metadata) return null;
674
+ const sidecar = compactMetadata({
675
+ appSessionId: metadata.sessionId || undefined,
676
+ cliSessionId: metadata.cliSessionId || undefined,
677
+ title: metadata.title || undefined,
678
+ titleSource: metadata.titleSource || undefined,
679
+ cwd: metadata.cwd || undefined,
680
+ originCwd: metadata.originCwd || undefined,
681
+ worktreePath: metadata.worktreePath || undefined,
682
+ worktreeName: metadata.worktreeName || undefined,
683
+ sourceBranch: metadata.sourceBranch || undefined,
684
+ branch: metadata.branch || undefined,
685
+ createdAt: metadata.createdAt || undefined,
686
+ lastActivityAt: metadata.lastActivityAt || undefined,
687
+ model: metadata.model || undefined,
688
+ effort: metadata.effort || undefined,
689
+ permissionMode: metadata.permissionMode || undefined,
690
+ chromePermissionMode: metadata.chromePermissionMode || undefined,
691
+ completedTurns: metadata.completedTurns,
692
+ isArchived: typeof metadata.isArchived === "boolean" ? metadata.isArchived : undefined,
693
+ enabledMcpToolCount: metadata.enabledMcpToolCount,
694
+ mcpServerNames: metadata.mcpServerNames?.length ? metadata.mcpServerNames : undefined,
695
+ sourcePath: metadata.sourcePath || undefined
696
+ });
697
+ if (!sidecar) return null;
698
+ return compactMetadata({
699
+ claudeCodeSidecar: sidecar,
700
+ modelUsage: metadata.model ? [{ model: metadata.model, source: "claude-code-sidecar" }] : undefined
701
+ });
702
+ }
703
+
704
+ function firstExistingDirectory(...values) {
705
+ for (const value of values) {
706
+ if (typeof value !== "string" || !value.trim()) continue;
707
+ const stat = safeStat(value.trim());
708
+ if (stat && stat.isDirectory()) return value.trim();
709
+ }
710
+ return "";
711
+ }
712
+
713
+ function claudeCodeImportFingerprint(baseFingerprint, metadata = null) {
714
+ if (!metadata?.sourcePath) return baseFingerprint;
715
+ return `${baseFingerprint}:claude-code-session:${fileFingerprint(metadata.sourcePath, safeStat(metadata.sourcePath))}`;
716
+ }
717
+
718
+ function claudeSessionIdFromFilename(file) {
719
+ const base = path.basename(String(file || "")).replace(/\.jsonl(?:\.zst)?$/i, "");
720
+ return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(base) ? base : "";
721
+ }
722
+
615
723
  function extractMessages(event, provider, context = {}) {
616
724
  const claudeMessages = extractClaudeMessagesFromEvent(event, provider, context);
617
725
  if (claudeMessages.length) {
@@ -2388,7 +2496,7 @@ function discoverCliHistory(env = process.env, options = {}) {
2388
2496
 
2389
2497
  const claudeDesktopSessions = readClaudeDesktopSessions({
2390
2498
  onProgress: (event) => reportDiscoveryProgress(options, { ...event, provider: "Claude App" })
2391
- });
2499
+ }, env);
2392
2500
  publish("claudeCodeDesktop", "Claude Code Desktop", summarizeClaudeDesktopSessions(claudeDesktopSessions, "claude-code-desktop-metadata"));
2393
2501
  publish("claudeWorkspace", "Claude Workspace", summarizeClaudeDesktopSessions(claudeDesktopSessions, "claude-workspace-desktop"));
2394
2502
  publish("claudeSdk", "Claude SDK jobs", summarizeClaudeSdkScan(claudeScan));
@@ -2443,13 +2551,38 @@ function discoverCliHistory(env = process.env, options = {}) {
2443
2551
  );
2444
2552
 
2445
2553
  publish(
2446
- "opencode",
2447
- "OpenCode",
2554
+ "opencodeCli",
2555
+ "OpenCode CLI",
2556
+ summarizeStructuredSessions(
2557
+ readOpenCodeSessions(env, {
2558
+ openCodeKind: "cli",
2559
+ onProgress: (event) => reportDiscoveryProgress(options, { ...event, provider: "OpenCode CLI" })
2560
+ }),
2561
+ "OpenCode CLI/core SQLite database plus project JSON session/message/part storage"
2562
+ )
2563
+ );
2564
+
2565
+ publish(
2566
+ "opencodeDesktop",
2567
+ "OpenCode Desktop",
2568
+ summarizeStructuredSessions(
2569
+ readOpenCodeSessions(env, {
2570
+ openCodeKind: "desktop",
2571
+ onProgress: (event) => reportDiscoveryProgress(options, { ...event, provider: "OpenCode Desktop" })
2572
+ }),
2573
+ "OpenCode Desktop app-specific SQLite database and JSON session/message/part storage"
2574
+ )
2575
+ );
2576
+
2577
+ publish(
2578
+ "opencodeWeb",
2579
+ "OpenCode Web",
2448
2580
  summarizeStructuredSessions(
2449
2581
  readOpenCodeSessions(env, {
2450
- onProgress: (event) => reportDiscoveryProgress(options, { ...event, provider: "OpenCode" })
2582
+ openCodeKind: "web",
2583
+ onProgress: (event) => reportDiscoveryProgress(options, { ...event, provider: "OpenCode Web" })
2451
2584
  }),
2452
- "OpenCode SQLite database and JSON session/message/part storage"
2585
+ "OpenCode web sessions from the shared OpenCode SQLite store"
2453
2586
  )
2454
2587
  );
2455
2588
 
@@ -2524,7 +2657,7 @@ function summarizeClaudeSdkScan(scan) {
2524
2657
  }
2525
2658
 
2526
2659
  function summarizeClaudeDesktop(env = process.env, options = {}) {
2527
- const sessions = readClaudeDesktopSessions(options);
2660
+ const sessions = readClaudeDesktopSessions(options, env);
2528
2661
  return summarizeClaudeDesktopSessions(sessions);
2529
2662
  }
2530
2663
 
@@ -3045,10 +3178,10 @@ function summarizeCodexSources(threads) {
3045
3178
  return { cli, desktop, ...(archived ? { archived } : {}), ...(summaries ? { summaries } : {}) };
3046
3179
  }
3047
3180
 
3048
- function readClaudeDesktopSessions(options = {}) {
3181
+ function readClaudeDesktopSessions(options = {}, env = process.env) {
3049
3182
  const roots = [
3050
- { root: path.join(os.homedir(), "Library", "Application Support", "Claude", "claude-code-sessions"), kind: "claude-code-desktop-metadata" },
3051
- { root: path.join(os.homedir(), "Library", "Application Support", "Claude", "local-agent-mode-sessions"), kind: "claude-workspace-desktop" }
3183
+ { root: claudeCodeSessionsRoot(env), kind: "claude-code-desktop-metadata" },
3184
+ { root: claudeWorkspaceSessionsRoot(env), kind: "claude-workspace-desktop" }
3052
3185
  ];
3053
3186
  const candidates = [];
3054
3187
  for (const { root, kind } of roots) {
@@ -3071,6 +3204,90 @@ function readClaudeDesktopSessions(options = {}) {
3071
3204
  return sessions;
3072
3205
  }
3073
3206
 
3207
+ function claudeCodeSessionMetadataByCliSessionId(env = process.env) {
3208
+ const byCliSessionId = new Map();
3209
+ collectFiles(claudeCodeSessionsRoot(env), (file) => {
3210
+ if (!path.basename(file).startsWith("local_") || !file.endsWith(".json")) return;
3211
+ const metadata = parseClaudeCodeSessionMetadataFile(file);
3212
+ if (metadata?.cliSessionId) byCliSessionId.set(metadata.cliSessionId, metadata);
3213
+ });
3214
+ return byCliSessionId;
3215
+ }
3216
+
3217
+ function parseClaudeCodeSessionMetadataFile(file) {
3218
+ let data;
3219
+ try {
3220
+ data = JSON.parse(fs.readFileSync(file, "utf8"));
3221
+ } catch {
3222
+ return null;
3223
+ }
3224
+ const cliSessionId = firstString(data.cliSessionId);
3225
+ if (!cliSessionId) return null;
3226
+ return {
3227
+ cliSessionId,
3228
+ sessionId: firstString(data.sessionId),
3229
+ title: firstString(data.title),
3230
+ titleSource: firstString(data.titleSource),
3231
+ cwd: firstString(data.cwd),
3232
+ originCwd: firstString(data.originCwd),
3233
+ worktreePath: firstString(data.worktreePath),
3234
+ worktreeName: firstString(data.worktreeName),
3235
+ sourceBranch: firstString(data.sourceBranch),
3236
+ branch: firstString(data.branch),
3237
+ createdAt: toIso(data.createdAt),
3238
+ lastActivityAt: toIso(data.lastActivityAt),
3239
+ model: firstString(data.model),
3240
+ effort: firstString(data.effort),
3241
+ permissionMode: firstString(data.permissionMode),
3242
+ chromePermissionMode: firstString(data.chromePermissionMode),
3243
+ completedTurns: numberValue(data.completedTurns),
3244
+ isArchived: typeof data.isArchived === "boolean" ? data.isArchived : undefined,
3245
+ enabledMcpToolCount: enabledMcpToolCount(data.enabledMcpTools),
3246
+ mcpServerNames: claudeMcpServerNames(data.remoteMcpServersConfig),
3247
+ sourcePath: file
3248
+ };
3249
+ }
3250
+
3251
+ function enabledMcpToolCount(value) {
3252
+ if (!value || typeof value !== "object" || Array.isArray(value)) return undefined;
3253
+ return Object.values(value).filter(Boolean).length;
3254
+ }
3255
+
3256
+ function claudeMcpServerNames(value) {
3257
+ if (!Array.isArray(value)) return [];
3258
+ return [...new Set(value.map((server) => firstString(server?.name)).filter(Boolean))].sort((a, b) => a.localeCompare(b));
3259
+ }
3260
+
3261
+ function compactMetadata(value) {
3262
+ if (!value || typeof value !== "object" || Array.isArray(value)) return value || null;
3263
+ const result = {};
3264
+ for (const [key, item] of Object.entries(value)) {
3265
+ if (item === undefined || item === null || item === "") continue;
3266
+ if (Array.isArray(item) && !item.length) continue;
3267
+ if (item && typeof item === "object" && !Array.isArray(item) && !Object.keys(item).length) continue;
3268
+ result[key] = item;
3269
+ }
3270
+ return Object.keys(result).length ? result : null;
3271
+ }
3272
+
3273
+ function numberValue(value) {
3274
+ const n = Number(value);
3275
+ return Number.isFinite(n) ? n : undefined;
3276
+ }
3277
+
3278
+ function claudeAppSupportRoot(env = process.env) {
3279
+ const home = env && env.HOME ? env.HOME : os.homedir();
3280
+ return env.CLAUDE_APP_SUPPORT || path.join(home, "Library", "Application Support", "Claude");
3281
+ }
3282
+
3283
+ function claudeCodeSessionsRoot(env = process.env) {
3284
+ return path.join(claudeAppSupportRoot(env), "claude-code-sessions");
3285
+ }
3286
+
3287
+ function claudeWorkspaceSessionsRoot(env = process.env) {
3288
+ return path.join(claudeAppSupportRoot(env), "local-agent-mode-sessions");
3289
+ }
3290
+
3074
3291
  function parseClaudeDesktopSessionFile(file, kind = "claude-workspace-desktop") {
3075
3292
  let data;
3076
3293
  try {
@@ -3337,7 +3554,10 @@ function cursorBuildComposerInfoLookup(env = process.env) {
3337
3554
  "key,",
3338
3555
  "json_extract(value, '$.modelId') as modelId,",
3339
3556
  "json_extract(value, '$.modelName') as modelName,",
3340
- "json_extract(value, '$.model') as model",
3557
+ "json_extract(value, '$.model') as model,",
3558
+ "json_extract(value, '$.modelInfo.modelName') as modelInfoModelName,",
3559
+ "json_extract(value, '$.modelInfo.modelId') as modelInfoModelId,",
3560
+ "json_extract(value, '$.modelInfo.model') as modelInfoModel",
3341
3561
  "from cursorDiskKV where",
3342
3562
  "json_valid(value) and",
3343
3563
  cursorDiskKvPrefixRangeCondition("bubbleId:")
@@ -3348,7 +3568,14 @@ function cursorBuildComposerInfoLookup(env = process.env) {
3348
3568
  const keyMatch = String(row.key || "").match(/^bubbleId:([^:]+):/);
3349
3569
  if (!keyMatch) continue;
3350
3570
  const composerId = keyMatch[1].toLowerCase();
3351
- const model = firstString(row.modelId, row.modelName, row.model);
3571
+ const model = firstString(
3572
+ row.modelInfoModelName,
3573
+ row.modelInfoModelId,
3574
+ row.modelInfoModel,
3575
+ row.modelId,
3576
+ row.modelName,
3577
+ row.model
3578
+ );
3352
3579
  if (!model) continue;
3353
3580
  const entry = info.get(composerId) || { title: "", modelHist: new Map() };
3354
3581
  entry.modelHist.set(model, (entry.modelHist.get(model) || 0) + 1);
@@ -3960,6 +4187,7 @@ function cursorGlobalBubbleSelectColumns(valueExpression = "value", keyExpressio
3960
4187
  `json_extract(${valueExpression}, '$.modelName') as modelName`,
3961
4188
  `json_extract(${valueExpression}, '$.modelSlug') as modelSlug`,
3962
4189
  `json_extract(${valueExpression}, '$.modelConfig') as modelConfig`,
4190
+ `json_extract(${valueExpression}, '$.modelInfo') as modelInfo`,
3963
4191
  `json_extract(${valueExpression}, '$.providerOptions') as providerOptions`,
3964
4192
  `json_extract(${valueExpression}, '$.status') as status`,
3965
4193
  `json_extract(${valueExpression}, '$.state') as state`,
@@ -4014,6 +4242,7 @@ function cursorGlobalBubbleDataFromRow(row) {
4014
4242
  modelName: row.modelName,
4015
4243
  modelSlug: row.modelSlug,
4016
4244
  modelConfig: cursorParseSqliteJsonColumn(row.modelConfig),
4245
+ modelInfo: cursorParseSqliteJsonColumn(row.modelInfo),
4017
4246
  providerOptions: cursorParseSqliteJsonColumn(row.providerOptions),
4018
4247
  status: row.status,
4019
4248
  state: cursorParseSqliteJsonColumn(row.state) || row.state,
@@ -5151,6 +5380,11 @@ function cursorMessageMetadata(record, source) {
5151
5380
 
5152
5381
  function cursorModel(record) {
5153
5382
  return firstCursorModel(
5383
+ record?.modelInfo?.modelName,
5384
+ record?.modelInfo?.modelId,
5385
+ record?.modelInfo?.model,
5386
+ record?.message?.modelInfo?.modelName,
5387
+ record?.message?.modelInfo?.modelId,
5154
5388
  record?.model,
5155
5389
  record?.modelId,
5156
5390
  record?.modelID,
@@ -6539,15 +6773,15 @@ function clineTitle(messages) {
6539
6773
  }
6540
6774
 
6541
6775
  function readOpenCodeSessions(env = process.env, options = {}) {
6542
- const dbs = openCodeDatabaseFiles(env);
6543
- const roots = openCodeStorageRoots(env);
6776
+ const dbs = openCodeDatabaseFiles(env, options);
6777
+ const roots = openCodeStorageRoots(env, options);
6544
6778
  const files = roots.flatMap((root) => openCodeSessionFiles(root).map((file) => ({ root, file })));
6545
6779
  const sessions = [];
6546
6780
  reportDiscoveryProgress(options, { current: 0, total: dbs.length, message: "reading OpenCode SQLite stores" });
6547
6781
  for (let index = 0; index < dbs.length; index++) {
6548
6782
  let dbSessions = [];
6549
6783
  try {
6550
- dbSessions = readOpenCodeSqliteSessionsFromDb(dbs[index], options);
6784
+ dbSessions = readOpenCodeSqliteSessionsFromDb(dbs[index], options, env);
6551
6785
  } catch (error) {
6552
6786
  reportDiscoveryProgress(options, {
6553
6787
  current: index + 1,
@@ -6569,7 +6803,7 @@ function readOpenCodeSessions(env = process.env, options = {}) {
6569
6803
  reportDiscoveryProgress(options, { current: 0, total: files.length, message: "reading OpenCode storage" });
6570
6804
  for (let index = 0; index < files.length; index++) {
6571
6805
  const item = files[index];
6572
- const session = parseOpenCodeSessionFile(item.file, item.root);
6806
+ const session = parseOpenCodeSessionFile(item.file, item.root, env, options);
6573
6807
  if (session) {
6574
6808
  sessions.push(session);
6575
6809
  seenSessionIds.add(session.sessionId.replace(/^opencode-/, ""));
@@ -6584,38 +6818,70 @@ function readOpenCodeSessions(env = process.env, options = {}) {
6584
6818
  for (const root of roots) {
6585
6819
  for (const sessionId of openCodeMessageSessionIds(root)) {
6586
6820
  if (seenSessionIds.has(sessionId)) continue;
6587
- const session = parseOpenCodeMessageOnlySession(root, sessionId);
6821
+ const session = parseOpenCodeMessageOnlySession(root, sessionId, env, options);
6588
6822
  if (session) {
6589
6823
  sessions.push(session);
6590
6824
  seenSessionIds.add(sessionId);
6591
6825
  }
6592
6826
  }
6593
6827
  }
6594
- return dedupeStructuredSessions(sessions, "opencode");
6828
+ return filterOpenCodeSessionsForKind(dedupeStructuredSessions(sessions, "opencode"), options.openCodeKind);
6829
+ }
6830
+
6831
+ function filterOpenCodeSessionsForKind(sessions, kind) {
6832
+ if (!OPENCODE_SOURCE_KINDS.has(kind)) return sessions;
6833
+ return (sessions || []).filter((session) => openCodeSourceKindForType(session.sourceType) === kind);
6595
6834
  }
6596
6835
 
6597
- function openCodeDataRoots(env = process.env) {
6836
+ function openCodeDataRoots(env = process.env, options = {}) {
6598
6837
  const configured = env.AGENTLOG_OPENCODE_DATA_DIR || env.OPENCODE_DATA_DIR;
6599
6838
  if (configured) return existingUniquePaths([configured]);
6600
- const home = env.HOME || os.homedir();
6839
+ if (options.openCodeKind === "cli") return existingUniquePaths(openCodeCliDataRoots(env));
6840
+ if (options.openCodeKind === "desktop") return existingUniquePaths(openCodeDesktopDataRoots(env));
6841
+ if (options.openCodeKind === "web") return [];
6842
+ return existingUniquePaths([...openCodeCliDataRoots(env), ...openCodeDesktopDataRoots(env)]);
6843
+ }
6844
+
6845
+ function openCodeCliDataRoots(env = process.env) {
6846
+ const home = env.HOME || env.USERPROFILE || os.homedir();
6847
+ const roots = [
6848
+ path.join(home, ".local", "share", "opencode")
6849
+ ];
6850
+ return roots;
6851
+ }
6852
+
6853
+ function openCodeDesktopDataRoots(env = process.env) {
6854
+ const home = env.HOME || env.USERPROFILE || os.homedir();
6601
6855
  const roots = [
6602
- path.join(home, ".local", "share", "opencode"),
6856
+ path.join(home, "Library", "Application Support", "ai.opencode.desktop"),
6603
6857
  path.join(home, "Library", "Application Support", "opencode"),
6604
6858
  path.join(home, ".local", "share", "ai.opencode.app"),
6605
6859
  path.join(home, "Library", "Application Support", "ai.opencode.app")
6606
6860
  ];
6607
6861
  const appData = env.APPDATA || env.LOCALAPPDATA || env.LocalAppData;
6608
6862
  if (appData) {
6863
+ roots.push(path.join(appData, "ai.opencode.desktop"));
6609
6864
  roots.push(path.join(appData, "opencode"));
6610
6865
  roots.push(path.join(appData, "ai.opencode.app"));
6611
6866
  }
6612
6867
  return existingUniquePaths(roots);
6613
6868
  }
6614
6869
 
6615
- function openCodeStorageRoots(env = process.env) {
6870
+ function openCodeSqliteDataRoots(env = process.env) {
6871
+ return existingUniquePaths([...openCodeCliDataRoots(env), ...openCodeDesktopDataRoots(env)]);
6872
+ }
6873
+
6874
+ function openCodeDatabaseRoots(env = process.env, options = {}) {
6875
+ const configured = env.AGENTLOG_OPENCODE_DATA_DIR || env.OPENCODE_DATA_DIR;
6876
+ if (configured) return existingUniquePaths([configured]);
6877
+ if (OPENCODE_SOURCE_KINDS.has(options.openCodeKind)) return openCodeSqliteDataRoots(env);
6878
+ return openCodeSqliteDataRoots(env);
6879
+ }
6880
+
6881
+ function openCodeStorageRoots(env = process.env, options = {}) {
6616
6882
  const explicit = envPathList(env.AGENTLOG_OPENCODE_STORAGE_ROOTS || env.AGENTLOG_OPENCODE_STORAGE_DIR);
6617
6883
  if (explicit.length) return existingUniquePaths(explicit);
6618
- const dataRoots = openCodeDataRoots(env);
6884
+ const dataRoots = openCodeDataRoots(env, options);
6619
6885
  const roots = [];
6620
6886
  for (const dataRoot of dataRoots) {
6621
6887
  roots.push(path.join(dataRoot, "storage"));
@@ -6634,11 +6900,11 @@ function openCodeStorageRoots(env = process.env) {
6634
6900
  return existingUniquePaths(roots);
6635
6901
  }
6636
6902
 
6637
- function openCodeDatabaseFiles(env = process.env) {
6903
+ function openCodeDatabaseFiles(env = process.env, options = {}) {
6638
6904
  const explicit = envPathList(env.AGENTLOG_OPENCODE_DB || env.AGENTLOG_OPENCODE_DATABASE || env.OPENCODE_DB);
6639
6905
  if (explicit.length) return existingUniquePaths(explicit);
6640
6906
  if ((env.AGENTLOG_OPENCODE_STORAGE_ROOTS || env.AGENTLOG_OPENCODE_STORAGE_DIR) && !(env.AGENTLOG_OPENCODE_DATA_DIR || env.OPENCODE_DATA_DIR)) return [];
6641
- return existingUniquePaths(openCodeDataRoots(env).flatMap((root) => [
6907
+ return existingUniquePaths(openCodeDatabaseRoots(env, options).flatMap((root) => [
6642
6908
  path.join(root, "opencode.db"),
6643
6909
  path.join(root, "storage", "opencode.db")
6644
6910
  ]));
@@ -6668,11 +6934,40 @@ function openCodeMessageSessionIds(root) {
6668
6934
  .sort((a, b) => a.localeCompare(b));
6669
6935
  }
6670
6936
 
6671
- function readOpenCodeSqliteSessionsFromDb(dbPath, options = {}) {
6937
+ function openCodeDesktopSessionHints(env = process.env) {
6938
+ const hints = new Map();
6939
+ for (const root of openCodeDesktopDataRoots(env)) {
6940
+ collectFilesLimited(root, (file) => {
6941
+ if (!openCodeDesktopHintFile(file)) return;
6942
+ const stat = safeStat(file);
6943
+ if (!stat || stat.size > 2 * 1024 * 1024) return;
6944
+ let text = "";
6945
+ try {
6946
+ text = fs.readFileSync(file, "utf8");
6947
+ } catch {
6948
+ return;
6949
+ }
6950
+ for (const id of text.match(OPENCODE_SESSION_ID_RE) || []) {
6951
+ const files = hints.get(id) || new Set();
6952
+ files.add(file);
6953
+ hints.set(id, files);
6954
+ }
6955
+ }, 2, { skipDirs: new Set(["Cache", "Cache_Data", "Code Cache", "GPUCache", "blob_storage", "logs"]) });
6956
+ }
6957
+ return hints;
6958
+ }
6959
+
6960
+ function openCodeDesktopHintFile(file) {
6961
+ const base = path.basename(String(file || ""));
6962
+ return base.endsWith(".dat") || base === "opencode.settings" || base === "settings.json";
6963
+ }
6964
+
6965
+ function readOpenCodeSqliteSessionsFromDb(dbPath, options = {}, env = process.env) {
6672
6966
  if (!safeStat(dbPath)) return [];
6673
6967
  if (!sqliteTableExists(dbPath, "session") || !sqliteTableExists(dbPath, "message") || !sqliteTableExists(dbPath, "part")) return [];
6674
6968
  const sessionRows = readOpenCodeSqliteSessionRows(dbPath, options);
6675
6969
  if (!sessionRows.length) return [];
6970
+ const classifications = openCodeSqliteSessionClassifications(sessionRows, dbPath, env, options);
6676
6971
  const sessionIds = sessionRows.map((row) => row.id).filter(Boolean);
6677
6972
  const messageRows = sortOpenCodeSqliteRows(readOpenCodeSqliteMessageRows(dbPath, sessionIds), ["session_id", "time_created", "id"]);
6678
6973
  const partRows = sortOpenCodeSqliteRows(readOpenCodeSqlitePartRows(dbPath, sessionIds), ["session_id", "message_id", "time_created", "id"]);
@@ -6681,20 +6976,22 @@ function readOpenCodeSqliteSessionsFromDb(dbPath, options = {}) {
6681
6976
  const storageRoot = path.join(path.dirname(dbPath), "storage");
6682
6977
  const sessions = [];
6683
6978
  for (const row of sessionRows) {
6979
+ const sourceType = classifications.sourceTypes.get(String(row.id || "")) || openCodeSqliteSourceType(dbPath, env, options);
6684
6980
  const rows = messagesBySession.get(row.id) || [];
6685
6981
  const messages = stampMessages(
6686
6982
  dedupeAdjacentMessages(rows.flatMap((messageRow, index) => openCodeSqliteMessagesFromRow(messageRow, partsByMessage.get(messageRow.id) || [], index)))
6687
6983
  .sort((a, b) => String(a.timestamp).localeCompare(String(b.timestamp))),
6688
- "opencode-sqlite-history"
6984
+ sourceType
6689
6985
  );
6690
6986
  const diffFile = path.join(storageRoot, "session_diff", `${row.id}.json`);
6691
6987
  const diffMessage = openCodeDiffMessage(diffFile, messages[messages.length - 1]?.timestamp || toIso(row.time_updated || row.time_created));
6692
- const finalMessages = diffMessage ? messages.concat(stampMessages([diffMessage], "opencode-sqlite-history")) : messages;
6988
+ const finalMessages = diffMessage ? messages.concat(stampMessages([diffMessage], sourceType)) : messages;
6693
6989
  if (!finalMessages.length) continue;
6694
6990
  const sourceFiles = [dbPath, safeStat(diffFile) ? diffFile : ""].filter(Boolean);
6695
6991
  const startedAt = toIso(row.time_created) || finalMessages[0]?.timestamp || new Date(safeStat(dbPath)?.mtimeMs || Date.now()).toISOString();
6696
6992
  const endedAt = toIso(row.time_updated) || finalMessages[finalMessages.length - 1]?.timestamp || startedAt;
6697
6993
  const cwd = firstString(row.directory, row.path, row.project_worktree, openCodeCwdFromMessages(finalMessages));
6994
+ const hintFiles = classifications.hintFiles.get(String(row.id || "")) || [];
6698
6995
  sessions.push({
6699
6996
  sessionId: `opencode-${row.id}`,
6700
6997
  title: firstString(row.title, row.slug, clineTitle(finalMessages), row.id),
@@ -6703,11 +7000,12 @@ function readOpenCodeSqliteSessionsFromDb(dbPath, options = {}) {
6703
7000
  endedAt,
6704
7001
  messages: finalMessages,
6705
7002
  sourcePath: `${dbPath}#${row.id}`,
6706
- sourceFiles,
6707
- sourceType: "opencode-sqlite-history",
6708
- fingerprint: openCodeSqliteSessionFingerprint(dbPath, row, rows, sourceFiles),
7003
+ sourceFiles: existingUniquePaths([...sourceFiles, ...hintFiles]),
7004
+ sourceType,
7005
+ fingerprint: openCodeSqliteSessionFingerprint(dbPath, row, rows, existingUniquePaths([...sourceFiles, ...hintFiles]), sourceType),
6709
7006
  detailKey: "sqliteSessions",
6710
7007
  sessionSummary: {
7008
+ source: openCodeSourceKindForType(sourceType),
6711
7009
  projectId: row.project_id || undefined,
6712
7010
  parentId: row.parent_id || undefined,
6713
7011
  workspaceId: row.workspace_id || undefined,
@@ -6763,6 +7061,92 @@ function readOpenCodeSqliteSessionRows(dbPath, options = {}) {
6763
7061
  return readSqliteJson(dbPath, queryParts.join(" "), "OpenCode SQLite sessions");
6764
7062
  }
6765
7063
 
7064
+ function openCodeSqliteSessionClassifications(sessionRows, dbPath, env = process.env, options = {}) {
7065
+ const baseSourceType = openCodeSqliteSourceType(dbPath, env, options);
7066
+ const rowsById = new Map((sessionRows || []).map((row) => [String(row.id || ""), row]).filter(([id]) => id));
7067
+ const desktopHints = openCodeDesktopSessionHints(env);
7068
+ const sourceTypes = new Map();
7069
+ const hintFiles = new Map();
7070
+ const classify = (row, visiting = new Set()) => {
7071
+ const id = String(row?.id || "");
7072
+ if (!id) return baseSourceType;
7073
+ if (sourceTypes.has(id)) return sourceTypes.get(id);
7074
+ if (desktopHints.has(id)) {
7075
+ sourceTypes.set(id, "opencode-desktop-sqlite-history");
7076
+ hintFiles.set(id, [...desktopHints.get(id)]);
7077
+ return "opencode-desktop-sqlite-history";
7078
+ }
7079
+ if (row?.parent_id && !visiting.has(id)) {
7080
+ visiting.add(id);
7081
+ const parentId = String(row.parent_id);
7082
+ const parent = rowsById.get(parentId);
7083
+ const parentSourceType = parent ? classify(parent, visiting) : "";
7084
+ if (parentSourceType === "opencode-desktop-sqlite-history") {
7085
+ sourceTypes.set(id, parentSourceType);
7086
+ const parentFiles = hintFiles.get(parentId);
7087
+ if (parentFiles?.length) hintFiles.set(id, parentFiles);
7088
+ return parentSourceType;
7089
+ }
7090
+ }
7091
+ const sourceType = openCodeSqliteRowSourceType(row, dbPath, env, options, baseSourceType);
7092
+ sourceTypes.set(id, sourceType);
7093
+ return sourceType;
7094
+ };
7095
+ for (const row of sessionRows || []) classify(row);
7096
+ return { sourceTypes, hintFiles };
7097
+ }
7098
+
7099
+ function openCodeSqliteRowSourceType(row, dbPath, env = process.env, options = {}, baseSourceType = openCodeSqliteSourceType(dbPath, env, options)) {
7100
+ if (baseSourceType === "opencode-desktop-sqlite-history") return baseSourceType;
7101
+ if (!pathInsideAny(dbPath, openCodeCliDataRoots(env)) && !OPENCODE_SOURCE_KINDS.has(options.openCodeKind)) return baseSourceType;
7102
+ if (openCodeSqliteRowHasCliMetadata(row)) return "opencode-cli-sqlite-history";
7103
+ if (openCodeSqliteRowLooksWeb(row, dbPath, env)) return "opencode-web-sqlite-history";
7104
+ return baseSourceType;
7105
+ }
7106
+
7107
+ function openCodeSqliteRowHasCliMetadata(row) {
7108
+ return Boolean(firstString(row?.agent, row?.model));
7109
+ }
7110
+
7111
+ function openCodeSqliteRowLooksWeb(row, dbPath, env = process.env) {
7112
+ const version = firstString(row?.version);
7113
+ if (!version || version === "local") return false;
7114
+ return pathInsideAny(dbPath, openCodeCliDataRoots(env));
7115
+ }
7116
+
7117
+ function openCodeSqliteSourceType(dbPath, env = process.env, options = {}) {
7118
+ if (pathInsideAny(dbPath, openCodeDesktopDataRoots(env))) return "opencode-desktop-sqlite-history";
7119
+ if (pathInsideAny(dbPath, openCodeCliDataRoots(env))) return "opencode-sqlite-history";
7120
+ if (options.openCodeKind === "cli") return "opencode-cli-sqlite-history";
7121
+ if (options.openCodeKind === "desktop") return "opencode-desktop-sqlite-history";
7122
+ if (options.openCodeKind === "web") return "opencode-web-sqlite-history";
7123
+ return "opencode-sqlite-history";
7124
+ }
7125
+
7126
+ function openCodeStorageSourceType(storageRoot, env = process.env, options = {}) {
7127
+ if (options.openCodeKind === "cli") return "opencode-cli-history";
7128
+ if (options.openCodeKind === "desktop") return "opencode-desktop-history";
7129
+ if (pathInsideAny(storageRoot, openCodeCliDataRoots(env))) return "opencode-cli-history";
7130
+ if (pathInsideAny(storageRoot, openCodeDesktopDataRoots(env))) return "opencode-desktop-history";
7131
+ return "opencode-history";
7132
+ }
7133
+
7134
+ function openCodeSourceKindForType(sourceType) {
7135
+ if (String(sourceType || "").includes("cli")) return "cli";
7136
+ if (String(sourceType || "").includes("desktop")) return "desktop";
7137
+ if (String(sourceType || "").includes("web")) return "web";
7138
+ return "unknown";
7139
+ }
7140
+
7141
+ function pathInsideAny(candidate, roots) {
7142
+ const resolved = path.resolve(String(candidate || ""));
7143
+ return (roots || []).some((root) => {
7144
+ if (!root) return false;
7145
+ const resolvedRoot = path.resolve(String(root));
7146
+ return resolved === resolvedRoot || resolved.startsWith(`${resolvedRoot}${path.sep}`);
7147
+ });
7148
+ }
7149
+
6766
7150
  function openCodeSqliteSessionTimestampExpr(sessionColumns) {
6767
7151
  const candidates = ["time_updated", "time_created"].filter((column) => sessionColumns.has(column)).map((column) => `s.${column}`);
6768
7152
  if (!candidates.length) return "";
@@ -6915,19 +7299,20 @@ function openCodeUsageFromMessageData(data, parts = []) {
6915
7299
  return Object.values(usage).some((value) => value !== undefined) ? usage : null;
6916
7300
  }
6917
7301
 
6918
- function openCodeSqliteSessionFingerprint(dbPath, row, messageRows, sourceFiles) {
7302
+ function openCodeSqliteSessionFingerprint(dbPath, row, messageRows, sourceFiles, sourceType = "opencode-sqlite-history") {
6919
7303
  const sessionRevision = [
6920
7304
  row.id,
6921
7305
  row.time_updated || row.time_created || "",
6922
7306
  messageRows.length,
6923
7307
  messageRows.map((message) => `${message.id}:${message.time_updated || message.time_created || ""}`).join("|")
6924
7308
  ].join(":");
6925
- return `${fingerprintPrefix("opencode-sqlite-history")}:${structuredSessionFingerprint({ sourcePath: dbPath, sourceFiles })}:${hashId(sessionRevision)}`;
7309
+ return `${fingerprintPrefix(sourceType)}:${structuredSessionFingerprint({ sourcePath: dbPath, sourceFiles })}:${hashId(sessionRevision)}`;
6926
7310
  }
6927
7311
 
6928
- function parseOpenCodeSessionFile(file, storageRoot) {
7312
+ function parseOpenCodeSessionFile(file, storageRoot, env = process.env, options = {}) {
6929
7313
  const info = readJsonMaybe(file, null);
6930
7314
  if (!info || typeof info !== "object") return null;
7315
+ const sourceType = openCodeStorageSourceType(storageRoot, env, options);
6931
7316
  const sessionId = firstString(info.id, info.sessionID, info.sessionId, path.basename(file, ".json"));
6932
7317
  if (!sessionId) return null;
6933
7318
  const projectId = firstString(info.projectID, info.projectId, path.basename(path.dirname(file)));
@@ -6938,7 +7323,7 @@ function parseOpenCodeSessionFile(file, storageRoot) {
6938
7323
  const diffMessage = openCodeDiffMessage(diffFile, parsedMessages[parsedMessages.length - 1]?.timestamp || toIso(info.time?.updated || info.updatedAt || info.createdAt));
6939
7324
  const messages = stampMessages(
6940
7325
  dedupeAdjacentMessages(parsedMessages.concat(diffMessage ? [diffMessage] : [])).sort((a, b) => String(a.timestamp).localeCompare(String(b.timestamp))),
6941
- "opencode-history"
7326
+ sourceType
6942
7327
  );
6943
7328
  if (!messages.length) return null;
6944
7329
  const sourceFiles = [
@@ -6960,21 +7345,23 @@ function parseOpenCodeSessionFile(file, storageRoot) {
6960
7345
  messages,
6961
7346
  sourcePath: file,
6962
7347
  sourceFiles,
6963
- sourceType: "opencode-history",
6964
- fingerprint: `${fingerprintPrefix("opencode-history")}:${structuredSessionFingerprint({ sourcePath: file, sourceFiles })}`,
6965
- detailKey: "sessions"
7348
+ sourceType,
7349
+ fingerprint: `${fingerprintPrefix(sourceType)}:${structuredSessionFingerprint({ sourcePath: file, sourceFiles })}`,
7350
+ detailKey: "sessions",
7351
+ sessionSummary: { source: openCodeSourceKindForType(sourceType) }
6966
7352
  };
6967
7353
  }
6968
7354
 
6969
- function parseOpenCodeMessageOnlySession(storageRoot, sessionId) {
7355
+ function parseOpenCodeMessageOnlySession(storageRoot, sessionId, env = process.env, options = {}) {
6970
7356
  if (!sessionId) return null;
7357
+ const sourceType = openCodeStorageSourceType(storageRoot, env, options);
6971
7358
  const messageFiles = openCodeMessageFiles(storageRoot, sessionId);
6972
7359
  const parsedMessages = messageFiles.flatMap((messageFile, index) => openCodeMessagesFromFile(messageFile, storageRoot, index));
6973
7360
  const diffFile = path.join(storageRoot, "session_diff", `${sessionId}.json`);
6974
7361
  const diffMessage = openCodeDiffMessage(diffFile, parsedMessages[parsedMessages.length - 1]?.timestamp);
6975
7362
  const messages = stampMessages(
6976
7363
  dedupeAdjacentMessages(parsedMessages.concat(diffMessage ? [diffMessage] : [])).sort((a, b) => String(a.timestamp).localeCompare(String(b.timestamp))),
6977
- "opencode-history"
7364
+ sourceType
6978
7365
  );
6979
7366
  if (!messages.length) return null;
6980
7367
  const sourceFiles = [
@@ -6994,9 +7381,10 @@ function parseOpenCodeMessageOnlySession(storageRoot, sessionId) {
6994
7381
  messages,
6995
7382
  sourcePath: path.join(storageRoot, "message", sessionId),
6996
7383
  sourceFiles,
6997
- sourceType: "opencode-history",
6998
- fingerprint: `${fingerprintPrefix("opencode-history")}:${structuredSessionFingerprint({ sourcePath: path.join(storageRoot, "message", sessionId), sourceFiles })}`,
6999
- detailKey: "sessions"
7384
+ sourceType,
7385
+ fingerprint: `${fingerprintPrefix(sourceType)}:${structuredSessionFingerprint({ sourcePath: path.join(storageRoot, "message", sessionId), sourceFiles })}`,
7386
+ detailKey: "sessions",
7387
+ sessionSummary: { source: openCodeSourceKindForType(sourceType), recoveredFromMessages: true }
7000
7388
  };
7001
7389
  }
7002
7390
 
@@ -7365,7 +7753,12 @@ function dedupeOpenCodeSessions(sessions) {
7365
7753
  }
7366
7754
 
7367
7755
  function openCodeSourceRank(sourceType) {
7756
+ if (sourceType === "opencode-cli-sqlite-history") return 3;
7757
+ if (sourceType === "opencode-web-sqlite-history") return 3;
7758
+ if (sourceType === "opencode-desktop-sqlite-history") return 3;
7368
7759
  if (sourceType === "opencode-sqlite-history") return 3;
7760
+ if (sourceType === "opencode-cli-history") return 2;
7761
+ if (sourceType === "opencode-desktop-history") return 2;
7369
7762
  if (sourceType === "opencode-history") return 2;
7370
7763
  return 1;
7371
7764
  }