botmux 2.11.1 → 2.12.1

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.
Files changed (99) hide show
  1. package/dist/adapters/backend/pty-backend.d.ts +6 -0
  2. package/dist/adapters/backend/pty-backend.d.ts.map +1 -1
  3. package/dist/adapters/backend/pty-backend.js +6 -0
  4. package/dist/adapters/backend/pty-backend.js.map +1 -1
  5. package/dist/adapters/backend/tmux-backend.d.ts +16 -2
  6. package/dist/adapters/backend/tmux-backend.d.ts.map +1 -1
  7. package/dist/adapters/backend/tmux-backend.js +40 -10
  8. package/dist/adapters/backend/tmux-backend.js.map +1 -1
  9. package/dist/adapters/backend/tmux-pipe-backend.d.ts +59 -0
  10. package/dist/adapters/backend/tmux-pipe-backend.d.ts.map +1 -0
  11. package/dist/adapters/backend/tmux-pipe-backend.js +288 -0
  12. package/dist/adapters/backend/tmux-pipe-backend.js.map +1 -0
  13. package/dist/adapters/cli/claude-code.d.ts +15 -0
  14. package/dist/adapters/cli/claude-code.d.ts.map +1 -1
  15. package/dist/adapters/cli/claude-code.js +205 -24
  16. package/dist/adapters/cli/claude-code.js.map +1 -1
  17. package/dist/adapters/cli/codex.d.ts.map +1 -1
  18. package/dist/adapters/cli/codex.js +78 -16
  19. package/dist/adapters/cli/codex.js.map +1 -1
  20. package/dist/adapters/cli/types.d.ts +10 -0
  21. package/dist/adapters/cli/types.d.ts.map +1 -1
  22. package/dist/cli.js +63 -8
  23. package/dist/cli.js.map +1 -1
  24. package/dist/core/command-handler.d.ts +10 -0
  25. package/dist/core/command-handler.d.ts.map +1 -1
  26. package/dist/core/command-handler.js +29 -1
  27. package/dist/core/command-handler.js.map +1 -1
  28. package/dist/core/scheduler.d.ts +3 -0
  29. package/dist/core/scheduler.d.ts.map +1 -1
  30. package/dist/core/scheduler.js +3 -0
  31. package/dist/core/scheduler.js.map +1 -1
  32. package/dist/core/session-manager.d.ts +17 -0
  33. package/dist/core/session-manager.d.ts.map +1 -1
  34. package/dist/core/session-manager.js +51 -3
  35. package/dist/core/session-manager.js.map +1 -1
  36. package/dist/core/types.d.ts +4 -0
  37. package/dist/core/types.d.ts.map +1 -1
  38. package/dist/core/types.js.map +1 -1
  39. package/dist/core/worker-pool.d.ts +13 -1
  40. package/dist/core/worker-pool.d.ts.map +1 -1
  41. package/dist/core/worker-pool.js +115 -4
  42. package/dist/core/worker-pool.js.map +1 -1
  43. package/dist/daemon.d.ts.map +1 -1
  44. package/dist/daemon.js +59 -120
  45. package/dist/daemon.js.map +1 -1
  46. package/dist/im/lark/card-builder.d.ts +2 -1
  47. package/dist/im/lark/card-builder.d.ts.map +1 -1
  48. package/dist/im/lark/card-builder.js +23 -9
  49. package/dist/im/lark/card-builder.js.map +1 -1
  50. package/dist/im/lark/card-handler.d.ts.map +1 -1
  51. package/dist/im/lark/card-handler.js +26 -93
  52. package/dist/im/lark/card-handler.js.map +1 -1
  53. package/dist/im/lark/merge-forward.d.ts +32 -0
  54. package/dist/im/lark/merge-forward.d.ts.map +1 -0
  55. package/dist/im/lark/merge-forward.js +99 -0
  56. package/dist/im/lark/merge-forward.js.map +1 -0
  57. package/dist/services/bridge-fallback-gate.d.ts +42 -0
  58. package/dist/services/bridge-fallback-gate.d.ts.map +1 -0
  59. package/dist/services/bridge-fallback-gate.js +12 -0
  60. package/dist/services/bridge-fallback-gate.js.map +1 -0
  61. package/dist/services/bridge-turn-queue.d.ts +111 -0
  62. package/dist/services/bridge-turn-queue.d.ts.map +1 -0
  63. package/dist/services/bridge-turn-queue.js +213 -0
  64. package/dist/services/bridge-turn-queue.js.map +1 -0
  65. package/dist/services/claude-transcript.d.ts +168 -0
  66. package/dist/services/claude-transcript.d.ts.map +1 -0
  67. package/dist/services/claude-transcript.js +524 -0
  68. package/dist/services/claude-transcript.js.map +1 -0
  69. package/dist/services/codex-bridge-queue.d.ts +39 -0
  70. package/dist/services/codex-bridge-queue.d.ts.map +1 -0
  71. package/dist/services/codex-bridge-queue.js +116 -0
  72. package/dist/services/codex-bridge-queue.js.map +1 -0
  73. package/dist/services/codex-transcript.d.ts +35 -0
  74. package/dist/services/codex-transcript.d.ts.map +1 -0
  75. package/dist/services/codex-transcript.js +163 -0
  76. package/dist/services/codex-transcript.js.map +1 -0
  77. package/dist/services/schedule-store.d.ts +3 -0
  78. package/dist/services/schedule-store.d.ts.map +1 -1
  79. package/dist/services/schedule-store.js +6 -0
  80. package/dist/services/schedule-store.js.map +1 -1
  81. package/dist/services/session-store.d.ts +10 -0
  82. package/dist/services/session-store.d.ts.map +1 -1
  83. package/dist/services/session-store.js +40 -0
  84. package/dist/services/session-store.js.map +1 -1
  85. package/dist/skills/definitions.d.ts.map +1 -1
  86. package/dist/skills/definitions.js +2 -1
  87. package/dist/skills/definitions.js.map +1 -1
  88. package/dist/types.d.ts +22 -0
  89. package/dist/types.d.ts.map +1 -1
  90. package/dist/utils/render-dimensions.d.ts +48 -0
  91. package/dist/utils/render-dimensions.d.ts.map +1 -0
  92. package/dist/utils/render-dimensions.js +55 -0
  93. package/dist/utils/render-dimensions.js.map +1 -0
  94. package/dist/utils/terminal-renderer.d.ts.map +1 -1
  95. package/dist/utils/terminal-renderer.js +5 -2
  96. package/dist/utils/terminal-renderer.js.map +1 -1
  97. package/dist/worker.js +1317 -37
  98. package/dist/worker.js.map +1 -1
  99. package/package.json +1 -1
@@ -0,0 +1,524 @@
1
+ /**
2
+ * Incremental reader for Claude Code transcript JSONL files.
3
+ *
4
+ * Used by the adopt-bridge pipeline (worker.ts) to:
5
+ * 1. baseline the transcript at attach time so historical messages aren't
6
+ * replayed to Lark.
7
+ * 2. drain newly-appended assistant messages between user turns.
8
+ * 3. tolerate truncation, rotation, half-written JSON lines, and races with
9
+ * Claude Code's writer.
10
+ *
11
+ * The functions are pure (no fs.watch — that's the worker's wakeup concern)
12
+ * to keep them unit-testable.
13
+ */
14
+ import { existsSync, openSync, readSync, closeSync, statSync, readdirSync } from 'node:fs';
15
+ import { join } from 'node:path';
16
+ /**
17
+ * Read everything from `path` starting at `fromOffset` and return parsed
18
+ * JSONL events plus the new file offset.
19
+ *
20
+ * - Returns `{ events: [], newOffset: 0, pendingTail: '' }` if the file
21
+ * doesn't exist (caller treats this as "nothing yet").
22
+ * - Detects truncation (size < fromOffset): resets to 0 and re-drains so a
23
+ * rotated/cleared transcript doesn't silently swallow new lines.
24
+ * - Skips malformed JSON lines (logs nothing — robustness over noise).
25
+ * - The trailing partial line (no `\n` yet) is *not* parsed and *not*
26
+ * counted toward `newOffset`, so the next drain re-reads it.
27
+ */
28
+ export function drainTranscript(path, fromOffset) {
29
+ if (!existsSync(path)) {
30
+ return { events: [], newOffset: 0, pendingTail: '' };
31
+ }
32
+ let size;
33
+ try {
34
+ size = statSync(path).size;
35
+ }
36
+ catch {
37
+ return { events: [], newOffset: fromOffset, pendingTail: '' };
38
+ }
39
+ let start = fromOffset;
40
+ if (size < start) {
41
+ // Truncated/rotated — re-read from the top.
42
+ start = 0;
43
+ }
44
+ if (size === start) {
45
+ return { events: [], newOffset: start, pendingTail: '' };
46
+ }
47
+ const len = size - start;
48
+ const buf = Buffer.alloc(len);
49
+ let read = 0;
50
+ const fd = openSync(path, 'r');
51
+ try {
52
+ read = readSync(fd, buf, 0, len, start);
53
+ }
54
+ finally {
55
+ closeSync(fd);
56
+ }
57
+ const text = buf.subarray(0, read).toString('utf8');
58
+ // Find the last '\n' — anything after it is a partial line we shouldn't
59
+ // commit yet. Adjust newOffset to exclude the partial tail so the next
60
+ // drain re-reads it.
61
+ const lastNl = text.lastIndexOf('\n');
62
+ let toParse;
63
+ let pendingTail;
64
+ let newOffset;
65
+ if (lastNl < 0) {
66
+ // No complete line at all — treat the whole buffer as pending.
67
+ toParse = '';
68
+ pendingTail = text;
69
+ newOffset = start;
70
+ }
71
+ else {
72
+ toParse = text.substring(0, lastNl);
73
+ pendingTail = text.substring(lastNl + 1);
74
+ newOffset = start + Buffer.byteLength(text.substring(0, lastNl + 1), 'utf8');
75
+ }
76
+ const events = [];
77
+ if (toParse) {
78
+ for (const line of toParse.split('\n')) {
79
+ const trimmed = line.trim();
80
+ if (!trimmed)
81
+ continue;
82
+ try {
83
+ const obj = JSON.parse(trimmed);
84
+ if (obj && typeof obj === 'object')
85
+ events.push(obj);
86
+ }
87
+ catch {
88
+ // Malformed line — skip silently. Claude Code's writer is atomic per
89
+ // line, so this means a debug/non-JSON line snuck in; not our concern.
90
+ }
91
+ }
92
+ }
93
+ return { events, newOffset, pendingTail };
94
+ }
95
+ /**
96
+ * Filter to assistant text events. Returns only events where:
97
+ * - type === 'assistant' OR message.role === 'assistant'
98
+ * - content has at least one text block
99
+ * - uuid is present
100
+ *
101
+ * Sub-agent / sidechain events (isSidechain === true) are excluded so that
102
+ * spawn-internal Task agent chatter doesn't leak to Lark.
103
+ */
104
+ export function pickAssistantTextEvents(events) {
105
+ return events.filter(e => {
106
+ if (!e || typeof e !== 'object')
107
+ return false;
108
+ if (e.isSidechain === true)
109
+ return false;
110
+ const role = e.message?.role ?? e.type;
111
+ if (role !== 'assistant')
112
+ return false;
113
+ if (!e.uuid)
114
+ return false;
115
+ const content = e.message?.content;
116
+ if (!content)
117
+ return false;
118
+ if (typeof content === 'string')
119
+ return content.length > 0;
120
+ if (Array.isArray(content))
121
+ return content.some(b => b && b.type === 'text' && typeof b.text === 'string' && b.text.length > 0);
122
+ return false;
123
+ });
124
+ }
125
+ /**
126
+ * Extract the visible text from one assistant event. Walks all `type:'text'`
127
+ * blocks in `message.content` (or the bare string) and joins them with
128
+ * blank lines. Returns '' if no text blocks.
129
+ */
130
+ export function extractAssistantText(event) {
131
+ const content = event.message?.content;
132
+ if (!content)
133
+ return '';
134
+ if (typeof content === 'string')
135
+ return content;
136
+ if (!Array.isArray(content))
137
+ return '';
138
+ const parts = [];
139
+ for (const block of content) {
140
+ if (block && block.type === 'text' && typeof block.text === 'string' && block.text.length > 0) {
141
+ parts.push(block.text);
142
+ }
143
+ }
144
+ return parts.join('\n\n');
145
+ }
146
+ /** Convenience: filter+extract a list of events into a single concatenated string. */
147
+ export function joinAssistantText(events) {
148
+ return pickAssistantTextEvents(events)
149
+ .map(extractAssistantText)
150
+ .filter(s => s.length > 0)
151
+ .join('\n\n');
152
+ }
153
+ /** XML wrappers Claude Code uses for synthetic user events that aren't real
154
+ * prompts (slash command invocation, local-command output caveat, etc.).
155
+ * These should usually carry `isMeta:true` and we'd filter on that — this
156
+ * list is a defense-in-depth check for jsonls where the flag is absent. */
157
+ const SYNTHETIC_USER_PREFIXES = [
158
+ '<command-name>',
159
+ '<command-message>',
160
+ '<command-args>',
161
+ '<local-command-caveat>',
162
+ '<local-command-stdout>',
163
+ '<local-command-stderr>',
164
+ ];
165
+ /** True when a `type:'user'` (or `message.role:'user'`) event represents a
166
+ * *real* prompt the human typed — not Claude Code's internal machinery
167
+ * (tool_result, slash-command wrappers, isMeta/isCompactSummary markers,
168
+ * sidechain spawn events). The bridge attribution queue and the adopt
169
+ * preamble extractor share this predicate to ensure they're seeing the
170
+ * same notion of "user input". */
171
+ export function isMeaningfulUserEvent(ev) {
172
+ if (!ev || typeof ev !== 'object')
173
+ return false;
174
+ const role = ev.message?.role ?? ev.type;
175
+ if (role !== 'user')
176
+ return false;
177
+ const flags = ev;
178
+ if (flags.isMeta === true)
179
+ return false;
180
+ if (flags.isCompactSummary === true)
181
+ return false;
182
+ if (flags.isSidechain === true)
183
+ return false;
184
+ const content = ev.message?.content;
185
+ if (isPureToolResultUserEvent(content))
186
+ return false;
187
+ const text = normaliseForFingerprint(stringifyUserContent(content));
188
+ if (text.length === 0)
189
+ return false;
190
+ if (SYNTHETIC_USER_PREFIXES.some(p => text.startsWith(p)))
191
+ return false;
192
+ return true;
193
+ }
194
+ /** Walk the events forward and return the last *completed* user/assistant
195
+ * exchange. "Completed" here means: a meaningful user prompt followed by
196
+ * at least one assistant event with visible text. tool_use / tool_result
197
+ * events do NOT reset the turn — they're intra-turn machinery, so a
198
+ * prompt → tool_use → tool_result → assistant text sequence still counts
199
+ * as a single turn. Returns null when there's no meaningful user yet, or
200
+ * the last user wasn't followed by any visible assistant text (Claude is
201
+ * mid-tool-use when /adopt fired).
202
+ *
203
+ * Used by adopt-bridge to surface "the previous round" to the Lark thread
204
+ * so the user has context for continuing the conversation. */
205
+ export function extractLastAssistantTurn(events) {
206
+ let userText = null;
207
+ let assistantTexts = [];
208
+ for (const ev of events) {
209
+ if (!ev || typeof ev !== 'object')
210
+ continue;
211
+ if (isMeaningfulUserEvent(ev)) {
212
+ // New turn boundary — reset the assistant accumulator.
213
+ userText = stringifyUserContent(ev.message?.content);
214
+ assistantTexts = [];
215
+ continue;
216
+ }
217
+ const role = ev.message?.role ?? ev.type;
218
+ if (role !== 'assistant')
219
+ continue;
220
+ if (ev.isSidechain === true)
221
+ continue;
222
+ const text = extractAssistantText(ev);
223
+ if (text.length === 0)
224
+ continue;
225
+ if (userText !== null)
226
+ assistantTexts.push(text);
227
+ }
228
+ if (userText === null || assistantTexts.length === 0)
229
+ return null;
230
+ return {
231
+ userText,
232
+ assistantText: assistantTexts.join('\n\n'),
233
+ };
234
+ }
235
+ /**
236
+ * True when a user-role event carries ONLY tool_result blocks — Claude
237
+ * Code's representation of "tool returned this output" between an
238
+ * assistant tool_use and the assistant's continuation. Both the bridge
239
+ * attribution queue and the on-disk fingerprint search must skip these:
240
+ *
241
+ * - the queue would treat tool output as fresh local input and disable
242
+ * collection mid-turn,
243
+ * - the fingerprint search would false-positive on log content that
244
+ * happens to contain the Lark fingerprint substring (e.g. a short
245
+ * "hello" message hijacked by an unrelated jsonl whose tool_result
246
+ * dumped a log line containing "hello"). Re-exported by
247
+ * bridge-turn-queue.ts so both consumers share the same predicate
248
+ * and never drift apart.
249
+ */
250
+ export function isPureToolResultUserEvent(content) {
251
+ if (!Array.isArray(content) || content.length === 0)
252
+ return false;
253
+ return content.every((block) => block?.type === 'tool_result');
254
+ }
255
+ /**
256
+ * Stringify a transcript user event's content to a flat string. Handles
257
+ * both legacy bare-string content and the array-of-blocks form.
258
+ *
259
+ * Lives here (not in bridge-turn-queue.ts) so the in-process attribution
260
+ * state machine and the on-disk fingerprint search use *exactly* the
261
+ * same text — otherwise multi-line / array-content Lark messages stop
262
+ * matching one path or the other and bridges silently break.
263
+ */
264
+ export function stringifyUserContent(content) {
265
+ if (typeof content === 'string')
266
+ return content;
267
+ if (!Array.isArray(content))
268
+ return '';
269
+ const parts = [];
270
+ for (const block of content) {
271
+ if (typeof block?.text === 'string')
272
+ parts.push(block.text);
273
+ else if (typeof block?.content === 'string')
274
+ parts.push(block.content);
275
+ }
276
+ return parts.join('\n');
277
+ }
278
+ /**
279
+ * Collapse whitespace + trim. Same normalisation applied on both sides
280
+ * of the fingerprint compare (the Lark message that produces the
281
+ * fingerprint, and the transcript user content we search through),
282
+ * so newlines / tabs / double-spaces don't break the match.
283
+ */
284
+ export function normaliseForFingerprint(s) {
285
+ return s.replace(/\s+/g, ' ').trim();
286
+ }
287
+ /**
288
+ * Find the most recently-modified `.jsonl` file in a Claude Code project
289
+ * directory. Helper kept for diagnostics and tests. The adopt-bridge
290
+ * watcher does NOT use mtime to follow session switches — see
291
+ * `findJsonlContainingFingerprint` for the safer fingerprint-based variant
292
+ * that ignores unrelated panes writing in the same project directory.
293
+ *
294
+ * Returns null when the directory doesn't exist or has no jsonl files.
295
+ */
296
+ export function findLatestJsonl(dir) {
297
+ if (!existsSync(dir))
298
+ return null;
299
+ let entries;
300
+ try {
301
+ entries = readdirSync(dir);
302
+ }
303
+ catch {
304
+ return null;
305
+ }
306
+ let latestPath = null;
307
+ let latestMtime = -Infinity;
308
+ for (const name of entries) {
309
+ if (!name.endsWith('.jsonl'))
310
+ continue;
311
+ const full = join(dir, name);
312
+ try {
313
+ const st = statSync(full);
314
+ if (!st.isFile())
315
+ continue;
316
+ if (st.mtimeMs > latestMtime) {
317
+ latestMtime = st.mtimeMs;
318
+ latestPath = full;
319
+ }
320
+ }
321
+ catch {
322
+ // File disappeared between readdir and stat — ignore.
323
+ }
324
+ }
325
+ return latestPath;
326
+ }
327
+ /** Scan a single jsonl file's tail for a Lark message fingerprint. Same
328
+ * parsing rules as `findJsonlContainingFingerprint` (decode role:user content,
329
+ * optionally also queue-operation/enqueue, normalise whitespace, then
330
+ * substring-match the fingerprint). Used by the claude-code adapter when
331
+ * the pid resolver has just switched to a rotated jsonl that may already
332
+ * contain the just-submitted user event. */
333
+ export function jsonlContainsFingerprint(path, fingerprint, opts) {
334
+ if (fingerprint.length === 0 || !existsSync(path))
335
+ return false;
336
+ let size;
337
+ try {
338
+ size = statSync(path).size;
339
+ }
340
+ catch {
341
+ return false;
342
+ }
343
+ if (size === 0)
344
+ return false;
345
+ const includeQueueOps = opts?.includeQueueOperations ?? false;
346
+ const minEventTimestampMs = opts?.minEventTimestampMs;
347
+ const len = Math.min(size, 1024 * 1024);
348
+ let buf;
349
+ try {
350
+ const fd = openSync(path, 'r');
351
+ try {
352
+ buf = Buffer.alloc(len);
353
+ readSync(fd, buf, 0, len, size - len);
354
+ }
355
+ finally {
356
+ closeSync(fd);
357
+ }
358
+ }
359
+ catch {
360
+ return false;
361
+ }
362
+ const text = buf.toString('utf8');
363
+ const lines = text.split('\n');
364
+ // Skip the leading partial line when we read a strict tail (size > len).
365
+ const startIdx = size > len ? 1 : 0;
366
+ for (let i = startIdx; i < lines.length; i++) {
367
+ const line = lines[i].trim();
368
+ if (!line)
369
+ continue;
370
+ let ev;
371
+ try {
372
+ ev = JSON.parse(line);
373
+ }
374
+ catch {
375
+ continue;
376
+ }
377
+ if (!ev || typeof ev !== 'object')
378
+ continue;
379
+ // Per-event timestamp guard: short fingerprints would otherwise
380
+ // false-match old user events in unrelated sibling jsonls (file
381
+ // mtime can be recent if a sibling Claude pane is actively writing
382
+ // its own turns). We compare against `event.timestamp` rather than
383
+ // file mtime to be precise.
384
+ if (minEventTimestampMs !== undefined && typeof ev.timestamp === 'string') {
385
+ const evMs = Date.parse(ev.timestamp);
386
+ if (Number.isFinite(evMs) && evMs < minEventTimestampMs)
387
+ continue;
388
+ }
389
+ const role = ev.message?.role ?? ev.type;
390
+ let lineText = '';
391
+ if (role === 'user') {
392
+ // Skip pure tool_result events — Claude Code records them as
393
+ // role:user but they're internal turn machinery, not the user's
394
+ // actual prompt. A tool_result that dumps log output containing
395
+ // the fingerprint substring would otherwise hijack the search.
396
+ if (isPureToolResultUserEvent(ev.message?.content))
397
+ continue;
398
+ lineText = stringifyUserContent(ev.message?.content);
399
+ }
400
+ else if (includeQueueOps &&
401
+ ev.type === 'queue-operation' &&
402
+ ev.operation === 'enqueue') {
403
+ lineText = typeof ev.content === 'string' ? ev.content : stringifyUserContent(ev.content);
404
+ }
405
+ else {
406
+ continue;
407
+ }
408
+ const normalisedText = normaliseForFingerprint(lineText);
409
+ if (normalisedText.length > 0 && normalisedText.includes(fingerprint))
410
+ return true;
411
+ }
412
+ return false;
413
+ }
414
+ export function findJsonlContainingFingerprint(dir, fingerprint, excludePathOrOptions) {
415
+ if (!existsSync(dir) || fingerprint.length === 0)
416
+ return null;
417
+ const opts = typeof excludePathOrOptions === 'string'
418
+ ? { excludePath: excludePathOrOptions }
419
+ : (excludePathOrOptions ?? {});
420
+ let entries;
421
+ try {
422
+ entries = readdirSync(dir);
423
+ }
424
+ catch {
425
+ return null;
426
+ }
427
+ // Walk newest-first so a recently-rotated jsonl is found before older
428
+ // ones; if two files contain the fingerprint (rare, e.g. user pasted
429
+ // the same message into two panes) we prefer the more recent.
430
+ const candidates = [];
431
+ for (const name of entries) {
432
+ if (!name.endsWith('.jsonl'))
433
+ continue;
434
+ const full = join(dir, name);
435
+ if (opts.excludePath && full === opts.excludePath)
436
+ continue;
437
+ try {
438
+ const st = statSync(full);
439
+ if (!st.isFile())
440
+ continue;
441
+ if (opts.minMtimeMs !== undefined && st.mtimeMs < opts.minMtimeMs)
442
+ continue;
443
+ candidates.push({ path: full, mtime: st.mtimeMs });
444
+ }
445
+ catch { /* ignore */ }
446
+ }
447
+ candidates.sort((a, b) => b.mtime - a.mtime);
448
+ for (const { path } of candidates) {
449
+ try {
450
+ const fd = openSync(path, 'r');
451
+ try {
452
+ const size = statSync(path).size;
453
+ // Read at most the trailing 1MB — fingerprints land near the end
454
+ // of the jsonl when Claude just wrote them. Cheaper than reading
455
+ // an entire long-lived session.
456
+ const len = Math.min(size, 1024 * 1024);
457
+ const buf = Buffer.alloc(len);
458
+ readSync(fd, buf, 0, len, size - len);
459
+ const text = buf.toString('utf8');
460
+ // We must NOT do a raw includes() here: Claude writes user content
461
+ // as a JSON-encoded string, so any newline in the Lark message is
462
+ // serialized as `\n` on disk while our fingerprint has it
463
+ // collapsed to a single space. Parse each complete jsonl line,
464
+ // pick role:user events, and apply the same stringify+normalise
465
+ // we use in BridgeTurnQueue.ingest. Skip the leading partial line
466
+ // when we read a strict tail (size > len), since it likely begins
467
+ // mid-line.
468
+ const lines = text.split('\n');
469
+ const startIdx = size > len ? 1 : 0;
470
+ for (let i = startIdx; i < lines.length; i++) {
471
+ const line = lines[i].trim();
472
+ if (!line)
473
+ continue;
474
+ let ev;
475
+ try {
476
+ ev = JSON.parse(line);
477
+ }
478
+ catch {
479
+ continue;
480
+ }
481
+ if (!ev || typeof ev !== 'object')
482
+ continue;
483
+ // Per-event timestamp guard — see jsonlContainsFingerprint for
484
+ // the full rationale. Required to keep short fingerprints
485
+ // ("hello", "test") from matching old user lines in unrelated
486
+ // sibling jsonls.
487
+ if (opts.minEventTimestampMs !== undefined && typeof ev.timestamp === 'string') {
488
+ const evMs = Date.parse(ev.timestamp);
489
+ if (Number.isFinite(evMs) && evMs < opts.minEventTimestampMs)
490
+ continue;
491
+ }
492
+ const role = ev.message?.role ?? ev.type;
493
+ let text = '';
494
+ if (role === 'user') {
495
+ // Skip pure tool_result events — see jsonlContainsFingerprint
496
+ // for the full rationale; in short, tool_result content is
497
+ // log output, not user input, and would false-match short
498
+ // fingerprints like "hello" in unrelated jsonls.
499
+ if (isPureToolResultUserEvent(ev.message?.content))
500
+ continue;
501
+ text = stringifyUserContent(ev.message?.content);
502
+ }
503
+ else if (opts.includeQueueOperations &&
504
+ ev.type === 'queue-operation' &&
505
+ ev.operation === 'enqueue') {
506
+ text = typeof ev.content === 'string' ? ev.content : stringifyUserContent(ev.content);
507
+ }
508
+ else {
509
+ continue;
510
+ }
511
+ const normalisedText = normaliseForFingerprint(text);
512
+ if (normalisedText.length > 0 && normalisedText.includes(fingerprint))
513
+ return path;
514
+ }
515
+ }
516
+ finally {
517
+ closeSync(fd);
518
+ }
519
+ }
520
+ catch { /* unreadable — skip */ }
521
+ }
522
+ return null;
523
+ }
524
+ //# sourceMappingURL=claude-transcript.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude-transcript.js","sourceRoot":"","sources":["../../src/services/claude-transcript.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ import type { CodexBridgeEvent } from './codex-transcript.js';
2
+ export interface CodexPendingTurn {
3
+ turnId: string;
4
+ started: boolean;
5
+ contentFingerprint?: string;
6
+ /** Wall-clock millis when mark() was called. The emit gate uses this as
7
+ * the lower bound of the "did `botmux send` happen for this turn?"
8
+ * window. Optional only for legacy / test-injected turns. */
9
+ markTimeMs?: number;
10
+ /** Set once an assistant_final event closes this turn. */
11
+ finalText?: string;
12
+ }
13
+ export declare class CodexBridgeQueue {
14
+ private seen;
15
+ private queue;
16
+ private collecting;
17
+ /** Register events as historical without producing pending-turn side
18
+ * effects. Used at attach time when resume mode wants to swallow prior
19
+ * conversation as already-processed. */
20
+ absorb(events: CodexBridgeEvent[]): void;
21
+ /** Push a pending Lark turn anchored to the message text. The fingerprint
22
+ * derived from `message` is what the upcoming `user` event must contain
23
+ * to start this turn. Pre-path-known marking is allowed: the worker can
24
+ * call this before late-attach has located the rollout file, and the
25
+ * ingest call after attach will still match correctly. */
26
+ mark(turnId: string, message: string, markTimeMs?: number): void;
27
+ /** Drop all pending turns. Used when the worker decides it can't reliably
28
+ * attribute future events (e.g. a teardown). */
29
+ clearPending(): CodexPendingTurn[];
30
+ /** Process newly-appended events. Idempotent on uuid: events with seen
31
+ * uuids are skipped, so callers can replay safely. */
32
+ ingest(events: CodexBridgeEvent[]): void;
33
+ /** Pop FIFO any leading turn that is started AND has finalText. */
34
+ drainEmittable(): CodexPendingTurn[];
35
+ size(): number;
36
+ /** Test helper — peek the queue without mutating. */
37
+ peek(): readonly CodexPendingTurn[];
38
+ }
39
+ //# sourceMappingURL=codex-bridge-queue.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codex-bridge-queue.d.ts","sourceRoot":"","sources":["../../src/services/codex-bridge-queue.ts"],"names":[],"mappings":"AAyBA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAE9D,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;IACjB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B;;kEAE8D;IAC9D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,0DAA0D;IAC1D,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,IAAI,CAAqB;IACjC,OAAO,CAAC,KAAK,CAA0B;IACvC,OAAO,CAAC,UAAU,CAAiC;IAEnD;;6CAEyC;IACzC,MAAM,CAAC,MAAM,EAAE,gBAAgB,EAAE,GAAG,IAAI;IAIxC;;;;+DAI2D;IAC3D,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,GAAE,MAAmB,GAAG,IAAI;IAS5E;qDACiD;IACjD,YAAY,IAAI,gBAAgB,EAAE;IAMlC;2DACuD;IACvD,MAAM,CAAC,MAAM,EAAE,gBAAgB,EAAE,GAAG,IAAI;IA8BxC,mEAAmE;IACnE,cAAc,IAAI,gBAAgB,EAAE;IAYpC,IAAI,IAAI,MAAM;IAId,qDAAqD;IACrD,IAAI,IAAI,SAAS,gBAAgB,EAAE;CAGpC"}