@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 +1 -1
- package/src/external-scanner.js +49 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@obtoai/agent-bridge",
|
|
3
|
-
"version": "0.1.0-beta.
|
|
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.",
|
package/src/external-scanner.js
CHANGED
|
@@ -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
|
|
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,
|