@obtoai/agent-bridge 0.1.0-beta.13 → 0.1.0-beta.15

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.13",
3
+ "version": "0.1.0-beta.15",
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.",
@@ -28,7 +28,7 @@ const CODEX_DIR = path.join(os.homedir(), '.codex', 'sessions');
28
28
 
29
29
  const PREVIEW_MAX_CHARS = 200;
30
30
  const TITLE_MAX_CHARS = 80;
31
- const TAIL_READ_BYTES = 8192; // read the last 8KB of each JSONL for the last message
31
+ const TAIL_READ_BYTES = 16384; // last 16KB covers the last message AND the late-appended ai-title record
32
32
  const TITLE_MAX_LINES = 40; // scan up to this many lines for a real first message
33
33
  const TITLE_MAX_BYTES = 65536; // hard ceiling per file even if MAX_LINES never reached
34
34
 
@@ -53,7 +53,30 @@ const readTail = (filePath, maxBytes = TAIL_READ_BYTES) => {
53
53
  }
54
54
  };
55
55
 
56
- // Claude Code (and to a lesser extent Codex) prepend a parade of injected
56
+ // Claude Code writes an LLM-generated title as a `type: "ai-title"` JSONL
57
+ // record near the end of each session file (this is the same title VSCode's
58
+ // session list shows — "Analyze MongoDB MCP server architecture" style).
59
+ // If we find one, it beats anything we could extract from the user's first
60
+ // raw prompt. Scan the tail backwards to hit it fast.
61
+ const extractAiTitleFromTail = (jsonlTail) => {
62
+ if (!jsonlTail) return '';
63
+ const lines = jsonlTail.split(/\r?\n/);
64
+ for (let i = lines.length - 1; i >= 0; i--) {
65
+ const line = lines[i];
66
+ // Cheap pre-filter so we only JSON.parse candidate lines.
67
+ if (line.indexOf('ai-title') === -1 && line.indexOf('aiTitle') === -1) continue;
68
+ try {
69
+ const obj = JSON.parse(line);
70
+ if (obj && obj.type === 'ai-title' && typeof obj.aiTitle === 'string') {
71
+ const t = obj.aiTitle.trim();
72
+ if (t) return t.length > TITLE_MAX_CHARS ? t.slice(0, TITLE_MAX_CHARS) : t;
73
+ }
74
+ } catch (_) { /* not a JSON line — keep walking */ }
75
+ }
76
+ return '';
77
+ };
78
+
79
+
57
80
  // "user" messages BEFORE the human's first real prompt — system reminders,
58
81
  // IDE state, untrusted-metadata wrappers, command blocks, etc. These are
59
82
  // noise from a label perspective. Filter them so the first PLAIN user
@@ -223,7 +246,11 @@ const scanClaude = () => {
223
246
  try { stat = fs.statSync(filePath); } catch (_) { continue; }
224
247
  const tail = readTail(filePath);
225
248
  const lastMsg = extractLastMessage(tail);
226
- const title = extractTitleFromFile(filePath);
249
+ // Prefer Claude Code's own LLM-summarized title (matches VSCode's list);
250
+ // fall back to first-user-message scanning if a session is too new to
251
+ // have an ai-title record yet.
252
+ let title = extractAiTitleFromTail(tail);
253
+ if (!title) title = extractTitleFromFile(filePath);
227
254
  out.push({
228
255
  source: 'claude',
229
256
  sessionId,
@@ -296,9 +323,11 @@ const scanCodex = () => {
296
323
  }
297
324
 
298
325
  const tail = readTail(filePath);
299
- const head = readHead(filePath);
300
326
  const lastMsg = extractLastMessage(tail);
301
- const title = extractTitle(head);
327
+ // Codex rollouts don't carry an ai-title record (as of this writing),
328
+ // so we just use the first-user-message fallback. If they add one
329
+ // later, swap in extractAiTitleFromTail like the claude path does.
330
+ const title = extractTitleFromFile(filePath);
302
331
  out.push({
303
332
  source: 'codex',
304
333
  sessionId,