botmux 2.60.0 → 2.60.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.
@@ -39,11 +39,14 @@
39
39
  * has finalText.
40
40
  */
41
41
  import { makeFingerprint, normaliseForFingerprint } from './bridge-turn-queue.js';
42
+ const UNMATCHED_REPLAY_WINDOW_MS = 5_000;
43
+ const MAX_BUFFERED_UNMATCHED_EVENTS = 20;
42
44
  export class CodexBridgeQueue {
43
45
  seen = new Set();
44
46
  queue = [];
45
47
  collecting = null;
46
48
  localTurnsEnabled = false;
49
+ bufferedUnmatched = [];
47
50
  /** Lower bound (ms) for synthesising local turns — protects against a
48
51
  * fresh-empty attach replaying historical iTerm conversation as
49
52
  * "live" local input. Typically set to the moment adopt was wired up. */
@@ -61,6 +64,8 @@ export class CodexBridgeQueue {
61
64
  setLocalTurns(enabled, lowerBoundMs = Date.now()) {
62
65
  this.localTurnsEnabled = enabled;
63
66
  this.localLowerBoundMs = lowerBoundMs;
67
+ if (enabled)
68
+ this.bufferedUnmatched = [];
64
69
  }
65
70
  /** Push a pending Lark turn anchored to the message text. The fingerprint
66
71
  * derived from `message` is what the upcoming `user` event must contain
@@ -74,6 +79,7 @@ export class CodexBridgeQueue {
74
79
  contentFingerprint: makeFingerprint(message),
75
80
  markTimeMs,
76
81
  });
82
+ this.replayBufferedUnmatched(markTimeMs);
77
83
  }
78
84
  /** Drop all pending turns. Used when the worker decides it can't reliably
79
85
  * attribute future events (e.g. a teardown). */
@@ -81,6 +87,7 @@ export class CodexBridgeQueue {
81
87
  const dropped = this.queue.splice(0);
82
88
  if (this.collecting && dropped.includes(this.collecting))
83
89
  this.collecting = null;
90
+ this.bufferedUnmatched = [];
84
91
  return dropped;
85
92
  }
86
93
  /** Process newly-appended events. Idempotent on uuid: events with seen
@@ -90,91 +97,117 @@ export class CodexBridgeQueue {
90
97
  if (!ev.uuid || this.seen.has(ev.uuid))
91
98
  continue;
92
99
  this.seen.add(ev.uuid);
93
- if (ev.kind === 'user') {
94
- // First decide whether this user event is a REAL turn-start: either it
95
- // matches the head pending Lark turn's fingerprint (and isn't tooOld),
96
- // or — in adopt mode — it synthesises a local turn. Both the HOL-drop
97
- // and the actual start key off this decision.
98
- const next = this.queue.find(t => !t.started);
99
- const tooOld = !!next && next.markTimeMs !== undefined && ev.timestampMs < next.markTimeMs - 5_000;
100
- let fingerprintOk = true;
101
- if (next?.contentFingerprint) {
102
- fingerprintOk = normaliseForFingerprint(ev.text).includes(next.contentFingerprint);
103
- }
104
- const willStartNext = !!next && !tooOld && fingerprintOk;
105
- const willSynthLocal = !willStartNext && this.localTurnsEnabled && ev.timestampMs >= this.localLowerBoundMs - 5_000;
106
- // HOL-block drop (codex 0.134.0 active-turn steer): when a real new
107
- // turn-start arrives while a turn is still collecting with no finalText,
108
- // codex steered/merged this input into the active turn — it processes
109
- // both as ONE turn and emits a single combined assistant_final, so the
110
- // collecting turn will NEVER get its own final. Drop it now, otherwise
111
- // it sits at the queue head forever and `drainEmittable()` wedges
112
- // (started, no finalText breaks the FIFO scan). Gating on "is a real
113
- // turn-start" reuses the tooOld/fingerprint freshness already proven for
114
- // turn-start, so the same 5s-skew invariant applies to both: a replayed
115
- // historical user event is tooOld → won't start a turn won't evict a
116
- // live collecting turn; and a non-matching stray user event (non-adopt)
117
- // is ignored rather than treated as a turn boundary. Mirrors Claude's
118
- // BridgeTurnQueue.handleTurnStart HOL drop (which keys off "no assistant
119
- // text yet" — the streaming-transcript equivalent of "no finalText").
120
- if ((willStartNext || willSynthLocal) && this.collecting && this.collecting.finalText === undefined) {
121
- const idx = this.queue.indexOf(this.collecting);
122
- if (idx >= 0)
123
- this.queue.splice(idx, 1);
124
- this.collecting = null;
125
- }
126
- if (willStartNext) {
127
- next.started = true;
128
- next.sourceSessionId = ev.sourceSessionId;
129
- // Anchor the bridge-fallback suppression window to when the turn
130
- // ACTUALLY started processing (the transcript user event's
131
- // timestamp), not when the worker marked it. With type-ahead the
132
- // worker marks turn N+1 immediately after turn N (both at flush
133
- // time), but CoCo only writes turn N+1's user event when it
134
- // dequeues it i.e. after turn N's assistant_final. Without this
135
- // override the [markTimeMs, nextTurn.markTimeMs) windows are all
136
- // bunched at flush time, so turn N's own `botmux send` (which
137
- // lands seconds later, after the model replies) falls OUTSIDE its
138
- // own window and the fallback isn't suppressed duplicate emit.
139
- // `max` (not bare assignment) keeps the lower bound from ever
140
- // moving backwards: a dequeue event can only be at or after the
141
- // mark, and the -5s tooOld tolerance must not be able to widen the
142
- // window into a previous turn's sends. Mirrors what Claude's
143
- // BridgeTurnQueue.handleTurnStart does with eventTimeMs.
144
- if (next.markTimeMs === undefined)
145
- next.markTimeMs = ev.timestampMs;
146
- else
147
- next.markTimeMs = Math.max(next.markTimeMs, ev.timestampMs);
148
- this.collecting = next;
149
- }
150
- else if (willSynthLocal) {
151
- // Adopt mode local input: user typed in iTerm, no Lark
152
- // fingerprint match. Synthesise a local turn so the assistant
153
- // reply still reaches Lark. Insert AHEAD of any unstarted Lark
154
- // turn so emit order matches when the event hit the transcript.
155
- const localTurn = {
156
- turnId: `codex-local-${ev.uuid}`,
157
- started: true,
158
- isLocal: true,
159
- userText: ev.text,
160
- markTimeMs: ev.timestampMs,
161
- sourceSessionId: ev.sourceSessionId,
162
- };
163
- const insertAt = this.queue.findIndex(t => !t.started);
164
- if (insertAt === -1)
165
- this.queue.push(localTurn);
166
- else
167
- this.queue.splice(insertAt, 0, localTurn);
168
- this.collecting = localTurn;
169
- }
100
+ this.ingestOne(ev, true);
101
+ }
102
+ }
103
+ replayBufferedUnmatched(markTimeMs) {
104
+ if (this.bufferedUnmatched.length === 0)
105
+ return;
106
+ const replay = this.bufferedUnmatched.filter(ev => ev.timestampMs >= markTimeMs - UNMATCHED_REPLAY_WINDOW_MS);
107
+ this.bufferedUnmatched = [];
108
+ for (const ev of replay)
109
+ this.ingestOne(ev, false);
110
+ }
111
+ rememberUnmatched(ev) {
112
+ this.bufferedUnmatched.push(ev);
113
+ if (this.bufferedUnmatched.length > MAX_BUFFERED_UNMATCHED_EVENTS) {
114
+ this.bufferedUnmatched.splice(0, this.bufferedUnmatched.length - MAX_BUFFERED_UNMATCHED_EVENTS);
115
+ }
116
+ }
117
+ ingestOne(ev, bufferUnmatched) {
118
+ if (ev.kind === 'user') {
119
+ // First decide whether this user event is a REAL turn-start: either it
120
+ // matches the head pending Lark turn's fingerprint (and isn't tooOld),
121
+ // or in adopt mode it synthesises a local turn. Both the HOL-drop
122
+ // and the actual start key off this decision.
123
+ const next = this.queue.find(t => !t.started);
124
+ const tooOld = !!next && next.markTimeMs !== undefined && ev.timestampMs < next.markTimeMs - UNMATCHED_REPLAY_WINDOW_MS;
125
+ let fingerprintOk = true;
126
+ if (next?.contentFingerprint) {
127
+ fingerprintOk = normaliseForFingerprint(ev.text).includes(next.contentFingerprint);
128
+ }
129
+ const willStartNext = !!next && !tooOld && fingerprintOk;
130
+ const willSynthLocal = !willStartNext && this.localTurnsEnabled && ev.timestampMs >= this.localLowerBoundMs - UNMATCHED_REPLAY_WINDOW_MS;
131
+ // HOL-block drop (codex 0.134.0 active-turn steer): when a real new
132
+ // turn-start arrives while a turn is still collecting with no finalText,
133
+ // codex steered/merged this input into the active turn — it processes
134
+ // both as ONE turn and emits a single combined assistant_final, so the
135
+ // collecting turn will NEVER get its own final. Drop it now, otherwise
136
+ // it sits at the queue head forever and `drainEmittable()` wedges
137
+ // (started, no finalText → breaks the FIFO scan). Gating on "is a real
138
+ // turn-start" reuses the tooOld/fingerprint freshness already proven for
139
+ // turn-start, so the same 5s-skew invariant applies to both: a replayed
140
+ // historical user event is tooOld won't start a turn → won't evict a
141
+ // live collecting turn; and a non-matching stray user event (non-adopt)
142
+ // is ignored rather than treated as a turn boundary. Mirrors Claude's
143
+ // BridgeTurnQueue.handleTurnStart HOL drop (which keys off "no assistant
144
+ // text yet" the streaming-transcript equivalent of "no finalText").
145
+ if ((willStartNext || willSynthLocal) && this.collecting && this.collecting.finalText === undefined) {
146
+ const idx = this.queue.indexOf(this.collecting);
147
+ if (idx >= 0)
148
+ this.queue.splice(idx, 1);
149
+ this.collecting = null;
150
+ }
151
+ if (willStartNext) {
152
+ next.started = true;
153
+ next.sourceSessionId = ev.sourceSessionId;
154
+ // Anchor the bridge-fallback suppression window to when the turn
155
+ // ACTUALLY started processing (the transcript user event's
156
+ // timestamp), not when the worker marked it. With type-ahead the
157
+ // worker marks turn N+1 immediately after turn N (both at flush
158
+ // time), but CoCo only writes turn N+1's user event when it
159
+ // dequeues it i.e. after turn N's assistant_final. Without this
160
+ // override the [markTimeMs, nextTurn.markTimeMs) windows are all
161
+ // bunched at flush time, so turn N's own `botmux send` (which
162
+ // lands seconds later, after the model replies) falls OUTSIDE its
163
+ // own window and the fallback isn't suppressed → duplicate emit.
164
+ // `max` (not bare assignment) keeps the lower bound from ever
165
+ // moving backwards: a dequeue event can only be at or after the
166
+ // mark, and the -5s tooOld tolerance must not be able to widen the
167
+ // window into a previous turn's sends. Mirrors what Claude's
168
+ // BridgeTurnQueue.handleTurnStart does with eventTimeMs.
169
+ if (next.markTimeMs === undefined)
170
+ next.markTimeMs = ev.timestampMs;
171
+ else
172
+ next.markTimeMs = Math.max(next.markTimeMs, ev.timestampMs);
173
+ this.collecting = next;
174
+ }
175
+ else if (willSynthLocal) {
176
+ // Adopt mode local input: user typed in iTerm, no Lark
177
+ // fingerprint match. Synthesise a local turn so the assistant
178
+ // reply still reaches Lark. Insert AHEAD of any unstarted Lark
179
+ // turn so emit order matches when the event hit the transcript.
180
+ const localTurn = {
181
+ turnId: `codex-local-${ev.uuid}`,
182
+ started: true,
183
+ isLocal: true,
184
+ userText: ev.text,
185
+ markTimeMs: ev.timestampMs,
186
+ sourceSessionId: ev.sourceSessionId,
187
+ };
188
+ const insertAt = this.queue.findIndex(t => !t.started);
189
+ if (insertAt === -1)
190
+ this.queue.push(localTurn);
191
+ else
192
+ this.queue.splice(insertAt, 0, localTurn);
193
+ this.collecting = localTurn;
194
+ }
195
+ else if (bufferUnmatched && !this.localTurnsEnabled) {
196
+ // Cursor can write the Lark/user line to JSONL before the daemon IPC
197
+ // that marks the turn reaches this worker. Keep a tiny recent buffer
198
+ // so mark() can replay it instead of losing the line to `seen`.
199
+ this.rememberUnmatched(ev);
200
+ }
201
+ }
202
+ else if (ev.kind === 'assistant_final') {
203
+ if (this.collecting) {
204
+ if (this.collecting.sourceSessionId && ev.sourceSessionId && this.collecting.sourceSessionId !== ev.sourceSessionId)
205
+ return;
206
+ this.collecting.finalText = ev.text;
207
+ this.collecting = null;
170
208
  }
171
- else if (ev.kind === 'assistant_final') {
172
- if (this.collecting) {
173
- if (this.collecting.sourceSessionId && ev.sourceSessionId && this.collecting.sourceSessionId !== ev.sourceSessionId)
174
- continue;
175
- this.collecting.finalText = ev.text;
176
- this.collecting = null;
177
- }
209
+ else if (bufferUnmatched && !this.localTurnsEnabled) {
210
+ this.rememberUnmatched(ev);
178
211
  }
179
212
  }
180
213
  }
@@ -1 +1 @@
1
- {"version":3,"file":"codex-bridge-queue.js","sourceRoot":"","sources":["../../src/services/codex-bridge-queue.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"codex-bridge-queue.js","sourceRoot":"","sources":["../../src/services/codex-bridge-queue.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,47 @@
1
+ import type { CodexBridgeEvent } from './codex-transcript.js';
2
+ /** Default `~/.cursor/projects` root. Overridable by callers (tests) so the
3
+ * scan doesn't depend on a real home directory. */
4
+ export declare function cursorProjectsRoot(): string;
5
+ /** Extract the chatId encoded in a Cursor store.db path of the shape
6
+ * `.../.cursor/chats/<projectHash>/<chatId>/store.db` (also matches the
7
+ * `-wal` / `-shm` sidecar files SQLite keeps open). The chatId is the same
8
+ * UUID used to name the agent-transcript JSONL, so it's the bridge between
9
+ * the open fd and the transcript file. Returns undefined for non-matching
10
+ * paths. */
11
+ export declare function cursorChatIdFromStoreDbPath(path: string): string | undefined;
12
+ /** Find the chatId of an externally-running cursor-agent process by reading
13
+ * the store.db file it keeps open. cursor-agent holds an fd on its current
14
+ * chat's SQLite store for the whole session lifetime, which makes this the
15
+ * authoritative pid→chatId binding — far more reliable than scanning chat
16
+ * dirs by mtime (which would race with sibling cursor-agent panes).
17
+ *
18
+ * Linux: `/proc/<pid>/fd/*` fast path. macOS / BSD: `lsof -p <pid> -Fn`
19
+ * fallback (same shape as codex-transcript.findCodexRolloutByPid). */
20
+ export declare function findCursorChatIdByPid(pid: number): string | undefined;
21
+ /** Locate the agent-transcript JSONL for a given chatId. The chatId is a
22
+ * globally-unique UUID, so a one-shot scan of the (small) projects root for
23
+ * `<slug>/agent-transcripts/<chatId>/<chatId>.jsonl` is unambiguous and
24
+ * avoids having to reproduce Cursor's opaque cwd→slug hashing. */
25
+ export declare function findCursorTranscriptByChatId(chatId: string, projectsRoot?: string): string | undefined;
26
+ /** Resolve the transcript path for an externally-running cursor-agent pid:
27
+ * pid → open store.db → chatId → agent-transcript JSONL. Returns both the
28
+ * path and the chatId so the caller can remember the chatId for a later
29
+ * retry if the JSONL isn't on disk yet. */
30
+ export declare function findCursorTranscriptByPid(pid: number, projectsRoot?: string): {
31
+ path: string;
32
+ chatId: string;
33
+ } | undefined;
34
+ export interface CursorDrainResult {
35
+ events: CodexBridgeEvent[];
36
+ /** Byte offset of the last fully-parsed line + its trailing \n. The next
37
+ * drain should pass this back as fromOffset. */
38
+ newOffset: number;
39
+ /** A line written without its terminating \n yet — informational; only
40
+ * complete lines produce events. */
41
+ pendingTail: string;
42
+ }
43
+ /** Increment-read the transcript from `fromOffset`. Mirrors the byte-offset
44
+ * contract of codex-transcript.drainCodexRollout so the worker can reuse the
45
+ * same fs.watch / poll wakeup machinery and the shared CodexBridgeQueue. */
46
+ export declare function drainCursorTranscript(path: string, fromOffset: number): CursorDrainResult;
47
+ //# sourceMappingURL=cursor-transcript.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cursor-transcript.d.ts","sourceRoot":"","sources":["../../src/services/cursor-transcript.ts"],"names":[],"mappings":"AAwDA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAM9D;oDACoD;AACpD,wBAAgB,kBAAkB,IAAI,MAAM,CAE3C;AAED;;;;;aAKa;AACb,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAI5E;AAED;;;;;;;uEAOuE;AACvE,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAgCrE;AAED;;;mEAGmE;AACnE,wBAAgB,4BAA4B,CAC1C,MAAM,EAAE,MAAM,EACd,YAAY,GAAE,MAA6B,GAC1C,MAAM,GAAG,SAAS,CASpB;AAED;;;4CAG4C;AAC5C,wBAAgB,yBAAyB,CACvC,GAAG,EAAE,MAAM,EACX,YAAY,GAAE,MAA6B,GAC1C;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAK9C;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,gBAAgB,EAAE,CAAC;IAC3B;qDACiD;IACjD,SAAS,EAAE,MAAM,CAAC;IAClB;yCACqC;IACrC,WAAW,EAAE,MAAM,CAAC;CACrB;AA8ED;;6EAE6E;AAC7E,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,iBAAiB,CA4DzF"}