botmux 2.11.1 → 2.12.0

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 (91) 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/schedule-store.d.ts +3 -0
  70. package/dist/services/schedule-store.d.ts.map +1 -1
  71. package/dist/services/schedule-store.js +6 -0
  72. package/dist/services/schedule-store.js.map +1 -1
  73. package/dist/services/session-store.d.ts +10 -0
  74. package/dist/services/session-store.d.ts.map +1 -1
  75. package/dist/services/session-store.js +40 -0
  76. package/dist/services/session-store.js.map +1 -1
  77. package/dist/skills/definitions.d.ts.map +1 -1
  78. package/dist/skills/definitions.js +2 -1
  79. package/dist/skills/definitions.js.map +1 -1
  80. package/dist/types.d.ts +22 -0
  81. package/dist/types.d.ts.map +1 -1
  82. package/dist/utils/render-dimensions.d.ts +48 -0
  83. package/dist/utils/render-dimensions.d.ts.map +1 -0
  84. package/dist/utils/render-dimensions.js +55 -0
  85. package/dist/utils/render-dimensions.js.map +1 -0
  86. package/dist/utils/terminal-renderer.d.ts.map +1 -1
  87. package/dist/utils/terminal-renderer.js +5 -2
  88. package/dist/utils/terminal-renderer.js.map +1 -1
  89. package/dist/worker.js +1068 -37
  90. package/dist/worker.js.map +1 -1
  91. package/package.json +1 -1
@@ -0,0 +1,32 @@
1
+ import { createImgNumberer, type MessageResource } from './message-parser.js';
2
+ import { type ForwardedNode } from './forwarded-renderer.js';
3
+ import type { LarkMessage } from '../../types.js';
4
+ /**
5
+ * Extract a useful one-liner from an AxiosError / generic error.
6
+ * Lark's SDK rethrows AxiosError whose default toString just says
7
+ * "Request failed with status code 400" — hiding the actual server reason.
8
+ */
9
+ export declare function describeAxiosErr(err: any): string;
10
+ /**
11
+ * Build a tree of ForwardedNode + collect attachment resources from a
12
+ * merge_forward message. Lark's im.v1.message.get returns ALL descendants
13
+ * in one shot via the `items` array, each with `upper_message_id` pointing
14
+ * to its parent — so we make exactly one API call per top-level expand and
15
+ * walk the tree purely in memory. Recursing per nested merge_forward via
16
+ * separate API calls fails (Lark 230002 "Bot/User can NOT be out of the chat")
17
+ * because the bot may be in the outer chat but not in the original chat the
18
+ * nested merge_forward came from. The single-call design sidesteps that.
19
+ */
20
+ export declare function buildForwardedTree(larkAppId: string, rootMessageId: string, numberer: ReturnType<typeof createImgNumberer>, maxDepth: number): Promise<{
21
+ nodes: ForwardedNode[];
22
+ extraResources: MessageResource[];
23
+ }>;
24
+ /**
25
+ * Expand a merge_forward message by fetching sub-messages via Lark API.
26
+ * Replaces parsed.content with an XML-rendered forward tree (deduplicated
27
+ * participants, alias-referenced messages) and collects additional resources.
28
+ */
29
+ export declare function expandMergeForward(larkAppId: string, messageId: string, parsed: LarkMessage, numberer?: import("./message-parser.js").ImgNumberer): Promise<{
30
+ extraResources: MessageResource[];
31
+ }>;
32
+ //# sourceMappingURL=merge-forward.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"merge-forward.d.ts","sourceRoot":"","sources":["../../../src/im/lark/merge-forward.ts"],"names":[],"mappings":"AACA,OAAO,EAGL,iBAAiB,EAEjB,KAAK,eAAe,EACrB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAsB,KAAK,aAAa,EAAE,MAAM,yBAAyB,CAAC;AACjF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAGlD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,GAAG,GAAG,MAAM,CAGjD;AAED;;;;;;;;;GASG;AACH,wBAAsB,kBAAkB,CACtC,SAAS,EAAE,MAAM,EACjB,aAAa,EAAE,MAAM,EACrB,QAAQ,EAAE,UAAU,CAAC,OAAO,iBAAiB,CAAC,EAC9C,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC;IAAE,KAAK,EAAE,aAAa,EAAE,CAAC;IAAC,cAAc,EAAE,eAAe,EAAE,CAAA;CAAE,CAAC,CA2DxE;AAED;;;;GAIG;AACH,wBAAsB,kBAAkB,CACtC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EACzD,QAAQ,4CAAsB,GAC7B,OAAO,CAAC;IAAE,cAAc,EAAE,eAAe,EAAE,CAAA;CAAE,CAAC,CAYhD"}
@@ -0,0 +1,99 @@
1
+ import { getMessageDetail } from './client.js';
2
+ import { parseApiMessage, extractResources, createImgNumberer, unwrapUserDslContent, } from './message-parser.js';
3
+ import { renderForwardedXml } from './forwarded-renderer.js';
4
+ import { logger } from '../../utils/logger.js';
5
+ /**
6
+ * Extract a useful one-liner from an AxiosError / generic error.
7
+ * Lark's SDK rethrows AxiosError whose default toString just says
8
+ * "Request failed with status code 400" — hiding the actual server reason.
9
+ */
10
+ export function describeAxiosErr(err) {
11
+ const detail = err?.response?.data ?? err?.message ?? String(err);
12
+ return typeof detail === 'string' ? detail : JSON.stringify(detail);
13
+ }
14
+ /**
15
+ * Build a tree of ForwardedNode + collect attachment resources from a
16
+ * merge_forward message. Lark's im.v1.message.get returns ALL descendants
17
+ * in one shot via the `items` array, each with `upper_message_id` pointing
18
+ * to its parent — so we make exactly one API call per top-level expand and
19
+ * walk the tree purely in memory. Recursing per nested merge_forward via
20
+ * separate API calls fails (Lark 230002 "Bot/User can NOT be out of the chat")
21
+ * because the bot may be in the outer chat but not in the original chat the
22
+ * nested merge_forward came from. The single-call design sidesteps that.
23
+ */
24
+ export async function buildForwardedTree(larkAppId, rootMessageId, numberer, maxDepth) {
25
+ // user_card_content combined with merge_forward triggers HTTP 500 — keep
26
+ // it off. Sub-messages come back in the simplified "Format A" card shape
27
+ // which extractCardContent already handles.
28
+ const detail = await getMessageDetail(larkAppId, rootMessageId, { userCardContent: false });
29
+ const allItems = detail?.items ?? [];
30
+ const extraResources = [];
31
+ // Index children by upper_message_id for O(1) lookup during the walk.
32
+ const childrenByParent = new Map();
33
+ for (const msg of allItems) {
34
+ const parent = msg.upper_message_id ?? '';
35
+ if (!parent)
36
+ continue;
37
+ const list = childrenByParent.get(parent) ?? [];
38
+ list.push(msg);
39
+ childrenByParent.set(parent, list);
40
+ }
41
+ function walk(parentId, depth) {
42
+ const children = childrenByParent.get(parentId) ?? [];
43
+ const nodes = [];
44
+ for (const msg of children) {
45
+ const senderType = msg.sender?.sender_type === 'app' ? 'app'
46
+ : msg.sender?.sender_type === 'user' ? 'user'
47
+ : 'unknown';
48
+ const senderOpenId = msg.sender?.id ?? '';
49
+ // Interactive sub-messages arrive via REST as a simplified fallback.
50
+ // Lark's im.message.get never returns user_dsl (even for the bot's own
51
+ // messages), so we can only unwrap when a user_dsl somehow got through.
52
+ // For third-party cards whose simplified form is the "请升级至最新版本"
53
+ // fallback, the real body is unrecoverable from REST.
54
+ if (msg.msg_type === 'interactive') {
55
+ const unwrapped = unwrapUserDslContent(msg.body?.content ?? '');
56
+ if (unwrapped !== null) {
57
+ msg.body = { ...(msg.body ?? {}), content: unwrapped };
58
+ }
59
+ }
60
+ // Resources first so the numberer assigns [图片 N] in attachment order;
61
+ // text extraction below reuses those numbers. Do NOT override messageId —
62
+ // Lark requires the parent merge_forward's message_id to download
63
+ // resources (error 234003 if sub-message ID is used).
64
+ const subResources = extractResources(msg.msg_type ?? 'text', msg.body?.content ?? '', numberer);
65
+ extraResources.push(...subResources);
66
+ if (msg.msg_type === 'merge_forward' && depth < maxDepth) {
67
+ const inner = walk(msg.message_id, depth + 1);
68
+ nodes.push({ senderOpenId, senderType, children: inner });
69
+ }
70
+ else {
71
+ const sub = parseApiMessage(msg, numberer);
72
+ nodes.push({ senderOpenId, senderType, content: sub.content });
73
+ }
74
+ }
75
+ return nodes;
76
+ }
77
+ return { nodes: walk(rootMessageId, 0), extraResources };
78
+ }
79
+ /**
80
+ * Expand a merge_forward message by fetching sub-messages via Lark API.
81
+ * Replaces parsed.content with an XML-rendered forward tree (deduplicated
82
+ * participants, alias-referenced messages) and collects additional resources.
83
+ */
84
+ export async function expandMergeForward(larkAppId, messageId, parsed, numberer = createImgNumberer()) {
85
+ const MAX_DEPTH = 5;
86
+ try {
87
+ const { nodes, extraResources } = await buildForwardedTree(larkAppId, messageId, numberer, MAX_DEPTH);
88
+ if (nodes.length === 0)
89
+ return { extraResources };
90
+ parsed.content = renderForwardedXml(nodes);
91
+ parsed.msgType = 'merge_forward_expanded';
92
+ return { extraResources };
93
+ }
94
+ catch (err) {
95
+ logger.warn(`Failed to expand merge_forward ${messageId}: ${describeAxiosErr(err)}`);
96
+ return { extraResources: [] };
97
+ }
98
+ }
99
+ //# sourceMappingURL=merge-forward.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"merge-forward.js","sourceRoot":"","sources":["../../../src/im/lark/merge-forward.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Decision logic for "should the worker suppress its transcript-driven
3
+ * fallback emit for this Lark turn?"
4
+ *
5
+ * Pure function with no I/O — kept separate from worker.ts so the rules
6
+ * (including the type-ahead window and the adopt-vs-non-adopt branching)
7
+ * can be tested deterministically. The worker reads marker entries from
8
+ * disk and threads them through here.
9
+ *
10
+ * Rules:
11
+ * - Adopt mode never suppresses: in /adopt the model in the adopted
12
+ * session is unaware of botmux, so transcript drain is the ONLY
13
+ * channel from model to Lark. There's no `botmux send` to compete
14
+ * with, hence no marker to gate on.
15
+ * - Non-adopt + isLocal: suppress. A local-typing turn means the
16
+ * attribution queue saw a user event whose content didn't match any
17
+ * pending Lark fingerprint. In a worker-spawned CLI that's a Web
18
+ * terminal hand-typed input — the user is already looking at it, no
19
+ * reason to push it back to the Lark thread.
20
+ * - Non-adopt + send observed in window: suppress. The window is
21
+ * [turn.markTimeMs, nextBoundaryMs); any `botmux send` whose
22
+ * sentAtMs falls inside means the model already delivered this turn
23
+ * to Lark itself. Boundary handling intentionally also considers
24
+ * queue items that haven't reached "ready" yet (passed in via
25
+ * nextBoundaryMs) — without that, a model that's still mid-tool-use
26
+ * for turn N+1 could leak a send credit into turn N's window.
27
+ */
28
+ export interface BridgeSendMarker {
29
+ sentAtMs: number;
30
+ messageId?: string;
31
+ }
32
+ export interface BridgeGateInput {
33
+ /** When the user message was queued — defines the lower bound of the
34
+ * send window. Undefined for legacy turns; the gate degrades to
35
+ * "never suppress" in that case. */
36
+ markTimeMs: number | undefined;
37
+ /** Whether the queue synthesised this turn from a local-terminal event
38
+ * (no fingerprint match for a Lark message). */
39
+ isLocal: boolean | undefined;
40
+ }
41
+ export declare function shouldSuppressBridgeEmit(turn: BridgeGateInput, nextBoundaryMs: number | undefined, markers: readonly BridgeSendMarker[], adoptMode: boolean): boolean;
42
+ //# sourceMappingURL=bridge-fallback-gate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bridge-fallback-gate.d.ts","sourceRoot":"","sources":["../../src/services/bridge-fallback-gate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,eAAe;IAC9B;;yCAEqC;IACrC,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B;qDACiD;IACjD,OAAO,EAAE,OAAO,GAAG,SAAS,CAAC;CAC9B;AAED,wBAAgB,wBAAwB,CACtC,IAAI,EAAE,eAAe,EACrB,cAAc,EAAE,MAAM,GAAG,SAAS,EAClC,OAAO,EAAE,SAAS,gBAAgB,EAAE,EACpC,SAAS,EAAE,OAAO,GACjB,OAAO,CAOT"}
@@ -0,0 +1,12 @@
1
+ export function shouldSuppressBridgeEmit(turn, nextBoundaryMs, markers, adoptMode) {
2
+ if (adoptMode)
3
+ return false;
4
+ if (turn.isLocal)
5
+ return true;
6
+ if (turn.markTimeMs === undefined)
7
+ return false;
8
+ const lower = turn.markTimeMs;
9
+ const upper = nextBoundaryMs ?? Number.POSITIVE_INFINITY;
10
+ return markers.some(m => m.sentAtMs >= lower && m.sentAtMs < upper);
11
+ }
12
+ //# sourceMappingURL=bridge-fallback-gate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bridge-fallback-gate.js","sourceRoot":"","sources":["../../src/services/bridge-fallback-gate.ts"],"names":[],"mappings":"AA0CA,MAAM,UAAU,wBAAwB,CACtC,IAAqB,EACrB,cAAkC,EAClC,OAAoC,EACpC,SAAkB;IAElB,IAAI,SAAS;QAAE,OAAO,KAAK,CAAC;IAC5B,IAAI,IAAI,CAAC,OAAO;QAAE,OAAO,IAAI,CAAC;IAC9B,IAAI,IAAI,CAAC,UAAU,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC;IAChD,MAAM,KAAK,GAAG,IAAI,CAAC,UAAU,CAAC;IAC9B,MAAM,KAAK,GAAG,cAAc,IAAI,MAAM,CAAC,iBAAiB,CAAC;IACzD,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,IAAI,KAAK,IAAI,CAAC,CAAC,QAAQ,GAAG,KAAK,CAAC,CAAC;AACtE,CAAC"}
@@ -0,0 +1,111 @@
1
+ /**
2
+ * Adopt-bridge turn attribution state machine.
3
+ *
4
+ * Pure (no fs / IPC / timers) so the worker can wrap it with watchers and
5
+ * tests can drive it deterministically. The worker feeds it transcript
6
+ * events (already drained from JSONL) and Lark-message markers; this class
7
+ * decides which assistant uuids belong to which Lark turn.
8
+ *
9
+ * Attribution rule:
10
+ * - mark() — pushes a new pending turn entry (state: not started)
11
+ * - ingest(events) — for each new user/assistant event:
12
+ * * user event → the earliest unstarted pending turn whose fingerprint
13
+ * matches becomes 'started' (its assistantUuids will collect from
14
+ * now on). A user event that does NOT match any pending fingerprint
15
+ * (or arrives with no pending Lark turn at all) is treated as
16
+ * **local terminal input**: a synthetic local turn is created on
17
+ * the spot, started immediately, and inserted ahead of any
18
+ * still-unstarted Lark turns so emit ordering reflects when the
19
+ * user event actually landed in the transcript. The local turn is
20
+ * emitted with `isLocal: true` so the worker can format it with a
21
+ * "user typed in the terminal" marker for the Lark thread.
22
+ * * assistant text event (non-sidechain) → appended to the
23
+ * currently-collecting turn (Lark or local), if any.
24
+ * - drainEmittable() — pops any leading turn that has been started AND has
25
+ * accumulated at least one visible assistant-text uuid. Started turns with no text
26
+ * yet (Claude is mid-tool-use) stay queued for the next idle.
27
+ *
28
+ * Baseline (`absorb()`) takes a batch of historical events and registers
29
+ * their uuids as already-seen so future ingest doesn't double-attribute.
30
+ */
31
+ import { normaliseForFingerprint, type TranscriptEvent } from './claude-transcript.js';
32
+ export { normaliseForFingerprint };
33
+ export interface BridgePendingTurn {
34
+ turnId: string;
35
+ started: boolean;
36
+ assistantUuids: string[];
37
+ /** Set when this turn was synthesised from a local-terminal user event
38
+ * (no matching Lark fingerprint). Causes the worker emit path to format
39
+ * the Lark message with both user text and assistant text under a
40
+ * "🖥️ 终端本地对话" header — otherwise the user would see an orphan
41
+ * reply with no prompt for context. Lark-driven turns keep this unset. */
42
+ isLocal?: boolean;
43
+ /** Transcript uuid of the user event that started this turn. Stored for
44
+ * local turns so emit can fetch the user-typed content from the source
45
+ * jsonl alongside the assistant uuids. Lark turns don't need it because
46
+ * the user content is already known on the daemon side. */
47
+ userUuid?: string;
48
+ /** A short substring of the Lark message that we expect to find inside
49
+ * the next matching `user` event's content. When set, only a user event
50
+ * whose stringified content contains this fingerprint is allowed to
51
+ * start the turn. Local-terminal input (whose content won't contain
52
+ * the Lark fingerprint) leaves the turn unstarted. */
53
+ contentFingerprint?: string;
54
+ /** JSONL file the turn's user event was first seen in. Stamped by ingest()
55
+ * when the turn transitions to started. Lets the emit step re-read text
56
+ * from the original transcript even after a sessionId rotation has
57
+ * pointed bridgeJsonlPath at a *different* file — without this stamp,
58
+ * uuid → text resolution would fail and the reply would be silently
59
+ * dropped. */
60
+ sourceJsonlPath?: string;
61
+ /** Wall-clock millis when mark() was called. Lets the fingerprint-based
62
+ * rotation fallback bound its scan to events written after we marked
63
+ * the turn — short fingerprints ("hello", "test") would otherwise risk
64
+ * matching pre-existing user lines in unrelated sibling jsonls. */
65
+ markTimeMs?: number;
66
+ }
67
+ /** Trim a Lark message into a stable fingerprint. Keeps a leading window
68
+ * of non-whitespace-collapsed content; long enough to disambiguate, short
69
+ * enough that minor formatting differences (newlines, attachment hints
70
+ * appended below) don't break the match. */
71
+ export declare function makeFingerprint(message: string, len?: number): string | undefined;
72
+ export declare class BridgeTurnQueue {
73
+ private seen;
74
+ private queue;
75
+ private collecting;
76
+ /** Register events as historical — their uuids are now considered seen
77
+ * but no attribution happens. Used at attach time to baseline. */
78
+ absorb(events: TranscriptEvent[]): void;
79
+ /** Push a new pending turn for the next Lark message. `contentFingerprint`
80
+ * (when set) restricts which user event can start this turn — only a
81
+ * user event whose content contains the fingerprint qualifies. Pass
82
+ * `undefined` to start on the next user event regardless (legacy).
83
+ *
84
+ * `markTimeMs` is captured here so the rotation fallback can bound its
85
+ * fingerprint scan to events written after this point — protects short
86
+ * fingerprints from matching old history in unrelated sibling jsonls. */
87
+ mark(turnId: string, contentFingerprint?: string, markTimeMs?: number): void;
88
+ /** Drop all pending turns. Used when the worker discovers it can't
89
+ * reliably attribute future events (e.g. baseline raced with a turn
90
+ * already in flight) and wants to clear the slate. */
91
+ clearPending(): BridgePendingTurn[];
92
+ /** Process newly-appended events. Idempotent on uuid: events with seen
93
+ * uuids are skipped, so callers can safely replay.
94
+ *
95
+ * `sourceJsonlPath` (when provided) is stamped onto a turn at the moment
96
+ * it transitions from "pending" to "started" — so that emit-time text
97
+ * resolution reads the same transcript file the user/assistant uuids
98
+ * were originally observed in. Without this, a sessionId rotation
99
+ * between ingest and emit would silently drop the reply, since the
100
+ * global current jsonl path would no longer contain those uuids. */
101
+ ingest(events: TranscriptEvent[], sourceJsonlPath?: string): void;
102
+ /** Pop FIFO any leading turn that's started AND has assistant text.
103
+ * Returns the popped turns in order; the caller is responsible for
104
+ * rebuilding the text payload from the assistant uuids. */
105
+ drainEmittable(): BridgePendingTurn[];
106
+ /** Number of queued (not-yet-emitted) Lark turns. */
107
+ size(): number;
108
+ /** Test helper — peek the queue without mutating. */
109
+ peek(): readonly BridgePendingTurn[];
110
+ }
111
+ //# sourceMappingURL=bridge-turn-queue.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bridge-turn-queue.d.ts","sourceRoot":"","sources":["../../src/services/bridge-turn-queue.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,OAAO,EAAwB,uBAAuB,EAAyB,KAAK,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAIpI,OAAO,EAAE,uBAAuB,EAAE,CAAC;AAEnC,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;IACjB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB;;;;+EAI2E;IAC3E,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;gEAG4D;IAC5D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;2DAIuD;IACvD,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B;;;;;mBAKe;IACf,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;wEAGoE;IACpE,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAQD;;;6CAG6C;AAC7C,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,SAAK,GAAG,MAAM,GAAG,SAAS,CAK7E;AAED,qBAAa,eAAe;IAC1B,OAAO,CAAC,IAAI,CAAqB;IACjC,OAAO,CAAC,KAAK,CAA2B;IACxC,OAAO,CAAC,UAAU,CAAkC;IAEpD;uEACmE;IACnE,MAAM,CAAC,MAAM,EAAE,eAAe,EAAE,GAAG,IAAI;IAMvC;;;;;;;8EAO0E;IAC1E,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,kBAAkB,CAAC,EAAE,MAAM,EAAE,UAAU,GAAE,MAAmB,GAAG,IAAI;IAIxF;;2DAEuD;IACvD,YAAY,IAAI,iBAAiB,EAAE;IAMnC;;;;;;;;yEAQqE;IACrE,MAAM,CAAC,MAAM,EAAE,eAAe,EAAE,EAAE,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI;IAoFjE;;gEAE4D;IAC5D,cAAc,IAAI,iBAAiB,EAAE;IAYrC,qDAAqD;IACrD,IAAI,IAAI,MAAM;IAId,qDAAqD;IACrD,IAAI,IAAI,SAAS,iBAAiB,EAAE;CAGrC"}
@@ -0,0 +1,213 @@
1
+ /**
2
+ * Adopt-bridge turn attribution state machine.
3
+ *
4
+ * Pure (no fs / IPC / timers) so the worker can wrap it with watchers and
5
+ * tests can drive it deterministically. The worker feeds it transcript
6
+ * events (already drained from JSONL) and Lark-message markers; this class
7
+ * decides which assistant uuids belong to which Lark turn.
8
+ *
9
+ * Attribution rule:
10
+ * - mark() — pushes a new pending turn entry (state: not started)
11
+ * - ingest(events) — for each new user/assistant event:
12
+ * * user event → the earliest unstarted pending turn whose fingerprint
13
+ * matches becomes 'started' (its assistantUuids will collect from
14
+ * now on). A user event that does NOT match any pending fingerprint
15
+ * (or arrives with no pending Lark turn at all) is treated as
16
+ * **local terminal input**: a synthetic local turn is created on
17
+ * the spot, started immediately, and inserted ahead of any
18
+ * still-unstarted Lark turns so emit ordering reflects when the
19
+ * user event actually landed in the transcript. The local turn is
20
+ * emitted with `isLocal: true` so the worker can format it with a
21
+ * "user typed in the terminal" marker for the Lark thread.
22
+ * * assistant text event (non-sidechain) → appended to the
23
+ * currently-collecting turn (Lark or local), if any.
24
+ * - drainEmittable() — pops any leading turn that has been started AND has
25
+ * accumulated at least one visible assistant-text uuid. Started turns with no text
26
+ * yet (Claude is mid-tool-use) stay queued for the next idle.
27
+ *
28
+ * Baseline (`absorb()`) takes a batch of historical events and registers
29
+ * their uuids as already-seen so future ingest doesn't double-attribute.
30
+ */
31
+ import { stringifyUserContent, normaliseForFingerprint, isMeaningfulUserEvent } from './claude-transcript.js';
32
+ // Re-export so existing callers (worker.ts, tests) don't need to change
33
+ // their import path now that these helpers live in claude-transcript.ts.
34
+ export { normaliseForFingerprint };
35
+ function assistantHasVisibleText(content) {
36
+ if (typeof content === 'string')
37
+ return content.length > 0;
38
+ if (!Array.isArray(content))
39
+ return false;
40
+ return content.some((block) => block?.type === 'text' && typeof block.text === 'string' && block.text.length > 0);
41
+ }
42
+ /** Trim a Lark message into a stable fingerprint. Keeps a leading window
43
+ * of non-whitespace-collapsed content; long enough to disambiguate, short
44
+ * enough that minor formatting differences (newlines, attachment hints
45
+ * appended below) don't break the match. */
46
+ export function makeFingerprint(message, len = 30) {
47
+ if (typeof message !== 'string')
48
+ return undefined;
49
+ const collapsed = normaliseForFingerprint(message);
50
+ if (collapsed.length === 0)
51
+ return undefined;
52
+ return collapsed.substring(0, len);
53
+ }
54
+ export class BridgeTurnQueue {
55
+ seen = new Set();
56
+ queue = [];
57
+ collecting = null;
58
+ /** Register events as historical — their uuids are now considered seen
59
+ * but no attribution happens. Used at attach time to baseline. */
60
+ absorb(events) {
61
+ for (const ev of events) {
62
+ if (ev.uuid)
63
+ this.seen.add(ev.uuid);
64
+ }
65
+ }
66
+ /** Push a new pending turn for the next Lark message. `contentFingerprint`
67
+ * (when set) restricts which user event can start this turn — only a
68
+ * user event whose content contains the fingerprint qualifies. Pass
69
+ * `undefined` to start on the next user event regardless (legacy).
70
+ *
71
+ * `markTimeMs` is captured here so the rotation fallback can bound its
72
+ * fingerprint scan to events written after this point — protects short
73
+ * fingerprints from matching old history in unrelated sibling jsonls. */
74
+ mark(turnId, contentFingerprint, markTimeMs = Date.now()) {
75
+ this.queue.push({ turnId, started: false, assistantUuids: [], contentFingerprint, markTimeMs });
76
+ }
77
+ /** Drop all pending turns. Used when the worker discovers it can't
78
+ * reliably attribute future events (e.g. baseline raced with a turn
79
+ * already in flight) and wants to clear the slate. */
80
+ clearPending() {
81
+ const dropped = this.queue.splice(0);
82
+ if (this.collecting && dropped.includes(this.collecting))
83
+ this.collecting = null;
84
+ return dropped;
85
+ }
86
+ /** Process newly-appended events. Idempotent on uuid: events with seen
87
+ * uuids are skipped, so callers can safely replay.
88
+ *
89
+ * `sourceJsonlPath` (when provided) is stamped onto a turn at the moment
90
+ * it transitions from "pending" to "started" — so that emit-time text
91
+ * resolution reads the same transcript file the user/assistant uuids
92
+ * were originally observed in. Without this, a sessionId rotation
93
+ * between ingest and emit would silently drop the reply, since the
94
+ * global current jsonl path would no longer contain those uuids. */
95
+ ingest(events, sourceJsonlPath) {
96
+ for (const ev of events) {
97
+ const uuid = ev.uuid;
98
+ if (!uuid || this.seen.has(uuid))
99
+ continue;
100
+ this.seen.add(uuid);
101
+ const role = ev.message?.role ?? ev.type;
102
+ if (role === 'user') {
103
+ // Skip ALL non-meaningful user events: tool_result (intra-turn
104
+ // machinery), `<command-name>/clear</command-name>` and other
105
+ // slash-command wrappers (Claude rewrites them after /clear /
106
+ // /resume — same in-process rotation that broke bridge tracking
107
+ // before), isMeta / isCompactSummary markers, sidechain spawns,
108
+ // empty content. These are NOT real user input; treating them as
109
+ // turn boundaries would (a) drop `collecting` mid-stream and lose
110
+ // assistant text after them, and (b) let a synthetic line that
111
+ // accidentally contains the fingerprint substring start the
112
+ // wrong turn.
113
+ if (!isMeaningfulUserEvent(ev))
114
+ continue;
115
+ // Defensive: if the previous local turn never accumulated any
116
+ // assistant text (Claude crashed / was killed / user cancelled
117
+ // mid-response), drop it now so its empty `assistantUuids` doesn't
118
+ // head-of-line block every subsequent emit. Lark turns are NOT
119
+ // dropped here — they may still be in tool-use legitimately, and
120
+ // Lark-side timeout is tracked separately by the daemon.
121
+ if (this.collecting && this.collecting.isLocal && this.collecting.assistantUuids.length === 0) {
122
+ const idx = this.queue.indexOf(this.collecting);
123
+ if (idx >= 0)
124
+ this.queue.splice(idx, 1);
125
+ this.collecting = null;
126
+ }
127
+ const next = this.queue.find(t => !t.started);
128
+ let consumedNext = false;
129
+ if (next) {
130
+ // If this turn has a fingerprint, gate on a content match. Both
131
+ // sides are normalised (whitespace-collapsed + trimmed) before
132
+ // the substring check so a transcript line that preserved
133
+ // newlines still matches a fingerprint built from the same text.
134
+ if (next.contentFingerprint) {
135
+ const userText = normaliseForFingerprint(stringifyUserContent(ev.message?.content));
136
+ if (userText.includes(next.contentFingerprint)) {
137
+ next.started = true;
138
+ if (!next.sourceJsonlPath)
139
+ next.sourceJsonlPath = sourceJsonlPath;
140
+ this.collecting = next;
141
+ consumedNext = true;
142
+ }
143
+ // Mismatch falls through to the local-turn branch below.
144
+ }
145
+ else {
146
+ // Legacy mark() with no fingerprint — start on the next user.
147
+ next.started = true;
148
+ if (!next.sourceJsonlPath)
149
+ next.sourceJsonlPath = sourceJsonlPath;
150
+ this.collecting = next;
151
+ consumedNext = true;
152
+ }
153
+ }
154
+ if (!consumedNext) {
155
+ // Local-terminal input. Synthesise a started turn so the
156
+ // assistant text that follows is captured (and pushed to the
157
+ // Lark thread) instead of being silently dropped — that's the
158
+ // /adopt symptom users hit when typing directly in the iterm
159
+ // pane. Insert AHEAD of any still-unstarted Lark turn so
160
+ // chronological order is preserved: this user event landed in
161
+ // the transcript before the next Lark turn's user event will.
162
+ const localTurn = {
163
+ turnId: `local-${uuid}`,
164
+ started: true,
165
+ isLocal: true,
166
+ userUuid: uuid,
167
+ assistantUuids: [],
168
+ sourceJsonlPath,
169
+ markTimeMs: Date.now(),
170
+ };
171
+ const insertAt = this.queue.findIndex(t => !t.started);
172
+ if (insertAt === -1)
173
+ this.queue.push(localTurn);
174
+ else
175
+ this.queue.splice(insertAt, 0, localTurn);
176
+ this.collecting = localTurn;
177
+ }
178
+ }
179
+ else if (role === 'assistant') {
180
+ if (ev.isSidechain === true)
181
+ continue;
182
+ if (this.collecting && assistantHasVisibleText(ev.message?.content)) {
183
+ this.collecting.assistantUuids.push(uuid);
184
+ }
185
+ }
186
+ }
187
+ }
188
+ /** Pop FIFO any leading turn that's started AND has assistant text.
189
+ * Returns the popped turns in order; the caller is responsible for
190
+ * rebuilding the text payload from the assistant uuids. */
191
+ drainEmittable() {
192
+ const out = [];
193
+ while (this.queue.length > 0) {
194
+ const head = this.queue[0];
195
+ if (!head.started || head.assistantUuids.length === 0)
196
+ break;
197
+ this.queue.shift();
198
+ if (this.collecting === head)
199
+ this.collecting = null;
200
+ out.push(head);
201
+ }
202
+ return out;
203
+ }
204
+ /** Number of queued (not-yet-emitted) Lark turns. */
205
+ size() {
206
+ return this.queue.length;
207
+ }
208
+ /** Test helper — peek the queue without mutating. */
209
+ peek() {
210
+ return this.queue;
211
+ }
212
+ }
213
+ //# sourceMappingURL=bridge-turn-queue.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bridge-turn-queue.js","sourceRoot":"","sources":["../../src/services/bridge-turn-queue.ts"],"names":[],"mappings":"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"}