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

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.15",
3
+ "version": "0.1.0-beta.16",
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.",
@@ -53,6 +53,50 @@ const readTail = (filePath, maxBytes = TAIL_READ_BYTES) => {
53
53
  }
54
54
  };
55
55
 
56
+ // Pull the last N user/assistant turns from the tail, oldest-first. Used at
57
+ // adopt time so the bridge thread shows actual prior context (not just a
58
+ // one-line preview) — Phase 6.2.2. Skips injection-pattern user messages so
59
+ // the history feels like real conversation, not platform noise.
60
+ const RECENT_MESSAGE_BODY_MAX = 1500;
61
+ const extractRecentMessages = (jsonlTail, n = 5) => {
62
+ if (!jsonlTail) return [];
63
+ const lines = jsonlTail.split(/\r?\n/);
64
+ const out = [];
65
+ for (let i = lines.length - 1; i >= 0 && out.length < n; i--) {
66
+ const line = lines[i].trim();
67
+ if (!line) continue;
68
+ let obj;
69
+ try { obj = JSON.parse(line); } catch (_) { continue; }
70
+ let role = null, raw = null;
71
+ if (obj && obj.message && (obj.message.role || obj.type)) {
72
+ role = obj.message.role || (obj.type === 'user' ? 'user' : 'assistant');
73
+ raw = obj.message.content;
74
+ } else if (obj && obj.role && (obj.content || obj.text)) {
75
+ role = obj.role;
76
+ raw = obj.content != null ? obj.content : obj.text;
77
+ }
78
+ if (role !== 'user' && role !== 'assistant') continue;
79
+ if (raw == null) continue;
80
+ let text = '';
81
+ if (typeof raw === 'string') text = raw;
82
+ else if (Array.isArray(raw)) {
83
+ text = raw
84
+ .filter((p) => p && (p.type === 'text' || typeof p.text === 'string'))
85
+ .map((p) => String(p.text || ''))
86
+ .join(' ');
87
+ }
88
+ text = text.replace(/\s+/g, ' ').trim();
89
+ if (!text) continue;
90
+ if (role === 'user' && isInjectionMessage(text)) continue;
91
+ out.unshift({
92
+ role: role,
93
+ body: text.length > RECENT_MESSAGE_BODY_MAX ? text.slice(0, RECENT_MESSAGE_BODY_MAX) : text,
94
+ ts: obj.timestamp || null,
95
+ });
96
+ }
97
+ return out;
98
+ };
99
+
56
100
  // Claude Code writes an LLM-generated title as a `type: "ai-title"` JSONL
57
101
  // record near the end of each session file (this is the same title VSCode's
58
102
  // session list shows — "Analyze MongoDB MCP server architecture" style).
@@ -251,12 +295,14 @@ const scanClaude = () => {
251
295
  // have an ai-title record yet.
252
296
  let title = extractAiTitleFromTail(tail);
253
297
  if (!title) title = extractTitleFromFile(filePath);
298
+ const recentMessages = extractRecentMessages(tail);
254
299
  out.push({
255
300
  source: 'claude',
256
301
  sessionId,
257
302
  projectDir: entry, // raw encoded form
258
303
  projectName: decodeClaudeProjectDir(entry), // best-effort decoded
259
- title: title, // first-user-message slice
304
+ title: title, // ai-title (Claude) or first-user-message fallback
305
+ recentMessages: recentMessages, // last ≤5 user/assistant turns for adopt-time history
260
306
  lastActivityAt: stat.mtimeMs,
261
307
  lastMessagePreview: lastMsg ? lastMsg.preview : '',
262
308
  lastMessageAuthor: lastMsg ? lastMsg.author : null,
@@ -324,16 +370,15 @@ const scanCodex = () => {
324
370
 
325
371
  const tail = readTail(filePath);
326
372
  const lastMsg = extractLastMessage(tail);
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
373
  const title = extractTitleFromFile(filePath);
374
+ const recentMessages = extractRecentMessages(tail);
331
375
  out.push({
332
376
  source: 'codex',
333
377
  sessionId,
334
378
  projectDir: projectDir || `${y}/${m}/${d}`,
335
379
  projectName: projectDir || null,
336
380
  title: title,
381
+ recentMessages: recentMessages,
337
382
  lastActivityAt: stat.mtimeMs,
338
383
  lastMessagePreview: lastMsg ? lastMsg.preview : '',
339
384
  lastMessageAuthor: lastMsg ? lastMsg.author : null,