@obtoai/agent-bridge 0.1.0-beta.18 → 0.1.0-beta.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@obtoai/agent-bridge",
3
- "version": "0.1.0-beta.18",
3
+ "version": "0.1.0-beta.19",
4
4
  "description": "Local consumer for the OBTO Agent Bridge. Receives bridge events over SSE and drives a coding agent (Claude Code or OpenAI Codex) on your machine.",
5
5
  "license": "Apache-2.0",
6
6
  "author": "OBTO Inc.",
@@ -25,6 +25,16 @@ const os = require('os');
25
25
 
26
26
  const CLAUDE_DIR = path.join(os.homedir(), '.claude', 'projects');
27
27
  const CODEX_DIR = path.join(os.homedir(), '.codex', 'sessions');
28
+ // Claude Desktop app's local-agent mode stores session JSONLs under
29
+ // ~/Library/Application Support/Claude/local-agent-mode-sessions/<uuid>/<uuid>/local_<uuid>/.claude/projects/<encoded>/<sid>.jsonl
30
+ // The file format is identical to Claude Code CLI's — same queue-operation
31
+ // preamble, same user/assistant message shape. Just a different root.
32
+ // Phase 6.2.5 — beta.19: surface these so the bridge sees every Claude
33
+ // session on disk, not just the CLI/extension subset.
34
+ const CLAUDE_DESKTOP_BASE = path.join(
35
+ os.homedir(),
36
+ 'Library', 'Application Support', 'Claude', 'local-agent-mode-sessions',
37
+ );
28
38
 
29
39
  const PREVIEW_MAX_CHARS = 200;
30
40
  const TITLE_MAX_CHARS = 80;
@@ -351,14 +361,45 @@ const decodeClaudeProjectDir = (encoded) => {
351
361
  return encoded.replace(/-/g, '/');
352
362
  };
353
363
 
354
- // Scan ~/.claude/projects/<encoded-projectdir>/<sessionId>.jsonl
355
- const scanClaude = () => {
364
+ // Discover all Claude Desktop "local-agent-mode" project roots.
365
+ // Structure is 3 levels deep before we hit `.claude/projects/`:
366
+ // <BASE>/<accountUuid>/<workspaceUuid>/local_<sessionUuid>/.claude/projects/
367
+ // We collect every leaf `.claude/projects` dir, then walk each like we walk
368
+ // the CLI's ~/.claude/projects.
369
+ const findClaudeDesktopProjectRoots = () => {
370
+ const out = [];
371
+ let l1;
372
+ try { l1 = fs.readdirSync(CLAUDE_DESKTOP_BASE); } catch (_) { return out; }
373
+ for (const a of l1) {
374
+ const aPath = path.join(CLAUDE_DESKTOP_BASE, a);
375
+ let aStat; try { aStat = fs.statSync(aPath); } catch (_) { continue; }
376
+ if (!aStat.isDirectory()) continue;
377
+ let l2; try { l2 = fs.readdirSync(aPath); } catch (_) { continue; }
378
+ for (const b of l2) {
379
+ const bPath = path.join(aPath, b);
380
+ let bStat; try { bStat = fs.statSync(bPath); } catch (_) { continue; }
381
+ if (!bStat.isDirectory()) continue;
382
+ let l3; try { l3 = fs.readdirSync(bPath); } catch (_) { continue; }
383
+ for (const c of l3) {
384
+ if (!c.startsWith('local_')) continue;
385
+ const projects = path.join(bPath, c, '.claude', 'projects');
386
+ try { if (fs.statSync(projects).isDirectory()) out.push(projects); } catch (_) {}
387
+ }
388
+ }
389
+ }
390
+ return out;
391
+ };
392
+
393
+ // Walk a single Claude projects root (works for both ~/.claude/projects and
394
+ // each of the Desktop app's local-agent-mode project roots — file format is
395
+ // identical, only the path differs).
396
+ const walkClaudeProjectsRoot = (root) => {
356
397
  const out = [];
357
398
  let topEntries;
358
- try { topEntries = fs.readdirSync(CLAUDE_DIR); } catch (_) { return out; }
399
+ try { topEntries = fs.readdirSync(root); } catch (_) { return out; }
359
400
 
360
401
  for (const entry of topEntries) {
361
- const projectPath = path.join(CLAUDE_DIR, entry);
402
+ const projectPath = path.join(root, entry);
362
403
  let projectStat;
363
404
  try { projectStat = fs.statSync(projectPath); } catch (_) { continue; }
364
405
  if (!projectStat.isDirectory()) continue;
@@ -374,21 +415,16 @@ const scanClaude = () => {
374
415
  try { stat = fs.statSync(filePath); } catch (_) { continue; }
375
416
  const tail = readTail(filePath);
376
417
  const lastMsg = extractLastMessage(tail);
377
- // Prefer Claude Code's own LLM-summarized title (matches VSCode's list);
378
- // fall back to first-user-message scanning if a session is too new to
379
- // have an ai-title record yet.
380
418
  let title = extractAiTitleFromTail(tail);
381
419
  if (!title) title = extractTitleFromFile(filePath);
382
- // Recent messages get the streaming tail (handles huge per-message
383
- // assistant turns that overflow the 16KB fixed budget).
384
420
  const recentMessages = extractRecentMessages(readTailUntilMessages(filePath));
385
421
  out.push({
386
422
  source: 'claude',
387
423
  sessionId,
388
- projectDir: entry, // raw encoded form
389
- projectName: decodeClaudeProjectDir(entry), // best-effort decoded
390
- title: title, // ai-title (Claude) or first-user-message fallback
391
- recentMessages: recentMessages, // last ≤5 user/assistant turns for adopt-time history
424
+ projectDir: entry,
425
+ projectName: decodeClaudeProjectDir(entry),
426
+ title: title,
427
+ recentMessages: recentMessages,
392
428
  lastActivityAt: stat.mtimeMs,
393
429
  lastMessagePreview: lastMsg ? lastMsg.preview : '',
394
430
  lastMessageAuthor: lastMsg ? lastMsg.author : null,
@@ -398,6 +434,18 @@ const scanClaude = () => {
398
434
  return out;
399
435
  };
400
436
 
437
+ // Scan all Claude session storage on this machine — CLI + VSCode extension
438
+ // (~/.claude/projects) AND every Claude Desktop local-agent-mode subdir
439
+ // (~/Library/Application Support/Claude/local-agent-mode-sessions/.../).
440
+ const scanClaude = () => {
441
+ const roots = [CLAUDE_DIR].concat(findClaudeDesktopProjectRoots());
442
+ const out = [];
443
+ for (const root of roots) {
444
+ out.push(...walkClaudeProjectsRoot(root));
445
+ }
446
+ return out;
447
+ };
448
+
401
449
  // Scan ~/.codex/sessions/YYYY/MM/DD/rollout-<ts>-<sessionId>.jsonl
402
450
  // The first JSONL line for a Codex rollout is a session-meta record that
403
451
  // contains the working directory; we read it once for projectDir.