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.
- package/dist/adapters/backend/pty-backend.d.ts +6 -0
- package/dist/adapters/backend/pty-backend.d.ts.map +1 -1
- package/dist/adapters/backend/pty-backend.js +6 -0
- package/dist/adapters/backend/pty-backend.js.map +1 -1
- package/dist/adapters/backend/tmux-backend.d.ts +16 -2
- package/dist/adapters/backend/tmux-backend.d.ts.map +1 -1
- package/dist/adapters/backend/tmux-backend.js +40 -10
- package/dist/adapters/backend/tmux-backend.js.map +1 -1
- package/dist/adapters/backend/tmux-pipe-backend.d.ts +59 -0
- package/dist/adapters/backend/tmux-pipe-backend.d.ts.map +1 -0
- package/dist/adapters/backend/tmux-pipe-backend.js +288 -0
- package/dist/adapters/backend/tmux-pipe-backend.js.map +1 -0
- package/dist/adapters/cli/claude-code.d.ts +15 -0
- package/dist/adapters/cli/claude-code.d.ts.map +1 -1
- package/dist/adapters/cli/claude-code.js +205 -24
- package/dist/adapters/cli/claude-code.js.map +1 -1
- package/dist/adapters/cli/codex.d.ts.map +1 -1
- package/dist/adapters/cli/codex.js +78 -16
- package/dist/adapters/cli/codex.js.map +1 -1
- package/dist/adapters/cli/types.d.ts +10 -0
- package/dist/adapters/cli/types.d.ts.map +1 -1
- package/dist/cli.js +63 -8
- package/dist/cli.js.map +1 -1
- package/dist/core/command-handler.d.ts +10 -0
- package/dist/core/command-handler.d.ts.map +1 -1
- package/dist/core/command-handler.js +29 -1
- package/dist/core/command-handler.js.map +1 -1
- package/dist/core/scheduler.d.ts +3 -0
- package/dist/core/scheduler.d.ts.map +1 -1
- package/dist/core/scheduler.js +3 -0
- package/dist/core/scheduler.js.map +1 -1
- package/dist/core/session-manager.d.ts +17 -0
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +51 -3
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/types.d.ts +4 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/core/worker-pool.d.ts +13 -1
- package/dist/core/worker-pool.d.ts.map +1 -1
- package/dist/core/worker-pool.js +115 -4
- package/dist/core/worker-pool.js.map +1 -1
- package/dist/daemon.d.ts.map +1 -1
- package/dist/daemon.js +59 -120
- package/dist/daemon.js.map +1 -1
- package/dist/im/lark/card-builder.d.ts +2 -1
- package/dist/im/lark/card-builder.d.ts.map +1 -1
- package/dist/im/lark/card-builder.js +23 -9
- package/dist/im/lark/card-builder.js.map +1 -1
- package/dist/im/lark/card-handler.d.ts.map +1 -1
- package/dist/im/lark/card-handler.js +26 -93
- package/dist/im/lark/card-handler.js.map +1 -1
- package/dist/im/lark/merge-forward.d.ts +32 -0
- package/dist/im/lark/merge-forward.d.ts.map +1 -0
- package/dist/im/lark/merge-forward.js +99 -0
- package/dist/im/lark/merge-forward.js.map +1 -0
- package/dist/services/bridge-fallback-gate.d.ts +42 -0
- package/dist/services/bridge-fallback-gate.d.ts.map +1 -0
- package/dist/services/bridge-fallback-gate.js +12 -0
- package/dist/services/bridge-fallback-gate.js.map +1 -0
- package/dist/services/bridge-turn-queue.d.ts +111 -0
- package/dist/services/bridge-turn-queue.d.ts.map +1 -0
- package/dist/services/bridge-turn-queue.js +213 -0
- package/dist/services/bridge-turn-queue.js.map +1 -0
- package/dist/services/claude-transcript.d.ts +168 -0
- package/dist/services/claude-transcript.d.ts.map +1 -0
- package/dist/services/claude-transcript.js +524 -0
- package/dist/services/claude-transcript.js.map +1 -0
- package/dist/services/codex-bridge-queue.d.ts +39 -0
- package/dist/services/codex-bridge-queue.d.ts.map +1 -0
- package/dist/services/codex-bridge-queue.js +116 -0
- package/dist/services/codex-bridge-queue.js.map +1 -0
- package/dist/services/codex-transcript.d.ts +35 -0
- package/dist/services/codex-transcript.d.ts.map +1 -0
- package/dist/services/codex-transcript.js +163 -0
- package/dist/services/codex-transcript.js.map +1 -0
- package/dist/services/schedule-store.d.ts +3 -0
- package/dist/services/schedule-store.d.ts.map +1 -1
- package/dist/services/schedule-store.js +6 -0
- package/dist/services/schedule-store.js.map +1 -1
- package/dist/services/session-store.d.ts +10 -0
- package/dist/services/session-store.d.ts.map +1 -1
- package/dist/services/session-store.js +40 -0
- package/dist/services/session-store.js.map +1 -1
- package/dist/skills/definitions.d.ts.map +1 -1
- package/dist/skills/definitions.js +2 -1
- package/dist/skills/definitions.js.map +1 -1
- package/dist/types.d.ts +22 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/render-dimensions.d.ts +48 -0
- package/dist/utils/render-dimensions.d.ts.map +1 -0
- package/dist/utils/render-dimensions.js +55 -0
- package/dist/utils/render-dimensions.js.map +1 -0
- package/dist/utils/terminal-renderer.d.ts.map +1 -1
- package/dist/utils/terminal-renderer.js +5 -2
- package/dist/utils/terminal-renderer.js.map +1 -1
- package/dist/worker.js +1317 -37
- package/dist/worker.js.map +1 -1
- 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"}
|