botmux 2.9.0 → 2.9.2
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/README.en.md +140 -76
- package/README.md +134 -75
- 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 +10 -0
- package/dist/adapters/backend/pty-backend.js.map +1 -1
- package/dist/adapters/backend/session-backend-selector.d.ts +11 -0
- package/dist/adapters/backend/session-backend-selector.d.ts.map +1 -0
- package/dist/adapters/backend/session-backend-selector.js +26 -0
- package/dist/adapters/backend/session-backend-selector.js.map +1 -0
- package/dist/adapters/backend/tmux-backend.d.ts +80 -3
- package/dist/adapters/backend/tmux-backend.d.ts.map +1 -1
- package/dist/adapters/backend/tmux-backend.js +301 -49
- package/dist/adapters/backend/tmux-backend.js.map +1 -1
- package/dist/adapters/backend/tmux-pipe-backend.d.ts +100 -0
- package/dist/adapters/backend/tmux-pipe-backend.d.ts.map +1 -0
- package/dist/adapters/backend/tmux-pipe-backend.js +473 -0
- package/dist/adapters/backend/tmux-pipe-backend.js.map +1 -0
- package/dist/adapters/cli/aiden.d.ts.map +1 -1
- package/dist/adapters/cli/aiden.js +5 -0
- package/dist/adapters/cli/aiden.js.map +1 -1
- package/dist/adapters/cli/claude-code.d.ts +40 -1
- package/dist/adapters/cli/claude-code.d.ts.map +1 -1
- package/dist/adapters/cli/claude-code.js +470 -49
- package/dist/adapters/cli/claude-code.js.map +1 -1
- package/dist/adapters/cli/coco.d.ts.map +1 -1
- package/dist/adapters/cli/coco.js +191 -9
- package/dist/adapters/cli/coco.js.map +1 -1
- package/dist/adapters/cli/codex.d.ts.map +1 -1
- package/dist/adapters/cli/codex.js +94 -17
- package/dist/adapters/cli/codex.js.map +1 -1
- package/dist/adapters/cli/shared-hints.d.ts +2 -2
- package/dist/adapters/cli/shared-hints.d.ts.map +1 -1
- package/dist/adapters/cli/shared-hints.js +7 -5
- package/dist/adapters/cli/shared-hints.js.map +1 -1
- package/dist/adapters/cli/types.d.ts +38 -1
- package/dist/adapters/cli/types.d.ts.map +1 -1
- package/dist/autostart.d.ts +14 -0
- package/dist/autostart.d.ts.map +1 -0
- package/dist/autostart.js +357 -0
- package/dist/autostart.js.map +1 -0
- package/dist/bot-registry.d.ts +29 -3
- package/dist/bot-registry.d.ts.map +1 -1
- package/dist/bot-registry.js +91 -12
- package/dist/bot-registry.js.map +1 -1
- package/dist/cli/arg-utils.d.ts +11 -0
- package/dist/cli/arg-utils.d.ts.map +1 -0
- package/dist/cli/arg-utils.js +25 -0
- package/dist/cli/arg-utils.js.map +1 -0
- package/dist/cli/create-group-resolver.d.ts +32 -0
- package/dist/cli/create-group-resolver.d.ts.map +1 -0
- package/dist/cli/create-group-resolver.js +70 -0
- package/dist/cli/create-group-resolver.js.map +1 -0
- package/dist/cli/quoted-render.d.ts +30 -0
- package/dist/cli/quoted-render.d.ts.map +1 -0
- package/dist/cli/quoted-render.js +29 -0
- package/dist/cli/quoted-render.js.map +1 -0
- package/dist/cli.js +916 -272
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +6 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +18 -8
- package/dist/config.js.map +1 -1
- package/dist/core/command-handler.d.ts +43 -0
- package/dist/core/command-handler.d.ts.map +1 -1
- package/dist/core/command-handler.js +167 -64
- package/dist/core/command-handler.js.map +1 -1
- package/dist/core/dashboard-events.d.ts +57 -0
- package/dist/core/dashboard-events.d.ts.map +1 -0
- package/dist/core/dashboard-events.js +23 -0
- package/dist/core/dashboard-events.js.map +1 -0
- package/dist/core/dashboard-ipc-server.d.ts +43 -0
- package/dist/core/dashboard-ipc-server.d.ts.map +1 -0
- package/dist/core/dashboard-ipc-server.js +481 -0
- package/dist/core/dashboard-ipc-server.js.map +1 -0
- package/dist/core/dashboard-locate.d.ts +20 -0
- package/dist/core/dashboard-locate.d.ts.map +1 -0
- package/dist/core/dashboard-locate.js +26 -0
- package/dist/core/dashboard-locate.js.map +1 -0
- package/dist/core/dashboard-rows.d.ts +31 -0
- package/dist/core/dashboard-rows.d.ts.map +1 -0
- package/dist/core/dashboard-rows.js +65 -0
- package/dist/core/dashboard-rows.js.map +1 -0
- package/dist/core/inherit-peer.d.ts +14 -0
- package/dist/core/inherit-peer.d.ts.map +1 -0
- package/dist/core/inherit-peer.js +32 -0
- package/dist/core/inherit-peer.js.map +1 -0
- package/dist/core/scheduler.d.ts +24 -0
- package/dist/core/scheduler.d.ts.map +1 -1
- package/dist/core/scheduler.js +93 -2
- package/dist/core/scheduler.js.map +1 -1
- package/dist/core/session-activity.d.ts +3 -0
- package/dist/core/session-activity.d.ts.map +1 -0
- package/dist/core/session-activity.js +20 -0
- package/dist/core/session-activity.js.map +1 -0
- package/dist/core/session-discovery.d.ts +39 -0
- package/dist/core/session-discovery.d.ts.map +1 -1
- package/dist/core/session-discovery.js +114 -21
- package/dist/core/session-discovery.js.map +1 -1
- package/dist/core/session-manager.d.ts +72 -0
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +396 -106
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/types.d.ts +27 -2
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js +14 -3
- package/dist/core/types.js.map +1 -1
- package/dist/core/worker-pool.d.ts +72 -3
- package/dist/core/worker-pool.d.ts.map +1 -1
- package/dist/core/worker-pool.js +459 -38
- package/dist/core/worker-pool.js.map +1 -1
- package/dist/daemon.d.ts.map +1 -1
- package/dist/daemon.js +601 -309
- package/dist/daemon.js.map +1 -1
- package/dist/dashboard/aggregator.d.ts +41 -0
- package/dist/dashboard/aggregator.d.ts.map +1 -0
- package/dist/dashboard/aggregator.js +125 -0
- package/dist/dashboard/aggregator.js.map +1 -0
- package/dist/dashboard/auth.d.ts +23 -0
- package/dist/dashboard/auth.d.ts.map +1 -0
- package/dist/dashboard/auth.js +66 -0
- package/dist/dashboard/auth.js.map +1 -0
- package/dist/dashboard/operator-selector.d.ts +20 -0
- package/dist/dashboard/operator-selector.d.ts.map +1 -0
- package/dist/dashboard/operator-selector.js +39 -0
- package/dist/dashboard/operator-selector.js.map +1 -0
- package/dist/dashboard/registry.d.ts +35 -0
- package/dist/dashboard/registry.d.ts.map +1 -0
- package/dist/dashboard/registry.js +74 -0
- package/dist/dashboard/registry.js.map +1 -0
- package/dist/dashboard/web/app.d.ts +2 -0
- package/dist/dashboard/web/app.d.ts.map +1 -0
- package/dist/dashboard/web/app.js +45 -0
- package/dist/dashboard/web/app.js.map +1 -0
- package/dist/dashboard/web/bot-defaults.d.ts +2 -0
- package/dist/dashboard/web/bot-defaults.d.ts.map +1 -0
- package/dist/dashboard/web/bot-defaults.js +201 -0
- package/dist/dashboard/web/bot-defaults.js.map +1 -0
- package/dist/dashboard/web/groups.d.ts +16 -0
- package/dist/dashboard/web/groups.d.ts.map +1 -0
- package/dist/dashboard/web/groups.js +584 -0
- package/dist/dashboard/web/groups.js.map +1 -0
- package/dist/dashboard/web/schedules.d.ts +2 -0
- package/dist/dashboard/web/schedules.d.ts.map +1 -0
- package/dist/dashboard/web/schedules.js +105 -0
- package/dist/dashboard/web/schedules.js.map +1 -0
- package/dist/dashboard/web/sessions.d.ts +2 -0
- package/dist/dashboard/web/sessions.d.ts.map +1 -0
- package/dist/dashboard/web/sessions.js +374 -0
- package/dist/dashboard/web/sessions.js.map +1 -0
- package/dist/dashboard/web/store.d.ts +23 -0
- package/dist/dashboard/web/store.d.ts.map +1 -0
- package/dist/dashboard/web/store.js +82 -0
- package/dist/dashboard/web/store.js.map +1 -0
- package/dist/dashboard-web/app.js +263 -0
- package/dist/dashboard-web/index.html +23 -0
- package/dist/dashboard-web/style.css +93 -0
- package/dist/dashboard.d.ts +2 -0
- package/dist/dashboard.d.ts.map +1 -0
- package/dist/dashboard.js +639 -0
- package/dist/dashboard.js.map +1 -0
- package/dist/im/lark/card-builder.d.ts +18 -1
- package/dist/im/lark/card-builder.d.ts.map +1 -1
- package/dist/im/lark/card-builder.js +70 -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 +123 -109
- package/dist/im/lark/card-handler.js.map +1 -1
- package/dist/im/lark/client.d.ts +36 -0
- package/dist/im/lark/client.d.ts.map +1 -1
- package/dist/im/lark/client.js +119 -13
- package/dist/im/lark/client.js.map +1 -1
- package/dist/im/lark/event-dispatcher.d.ts +92 -8
- package/dist/im/lark/event-dispatcher.d.ts.map +1 -1
- package/dist/im/lark/event-dispatcher.js +410 -89
- package/dist/im/lark/event-dispatcher.js.map +1 -1
- package/dist/im/lark/forwarded-renderer.d.ts +23 -0
- package/dist/im/lark/forwarded-renderer.d.ts.map +1 -0
- package/dist/im/lark/forwarded-renderer.js +105 -0
- package/dist/im/lark/forwarded-renderer.js.map +1 -0
- package/dist/im/lark/md-card.d.ts +73 -0
- package/dist/im/lark/md-card.d.ts.map +1 -0
- package/dist/im/lark/md-card.js +332 -0
- package/dist/im/lark/md-card.js.map +1 -0
- 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 +110 -0
- package/dist/im/lark/merge-forward.js.map +1 -0
- package/dist/im/lark/message-parser.d.ts +9 -3
- package/dist/im/lark/message-parser.d.ts.map +1 -1
- package/dist/im/lark/message-parser.js +48 -13
- package/dist/im/lark/message-parser.js.map +1 -1
- package/dist/im/lark/quote-hint.d.ts +18 -0
- package/dist/im/lark/quote-hint.d.ts.map +1 -0
- package/dist/im/lark/quote-hint.js +23 -0
- package/dist/im/lark/quote-hint.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-rotation-policy.d.ts +139 -0
- package/dist/services/bridge-rotation-policy.d.ts.map +1 -0
- package/dist/services/bridge-rotation-policy.js +125 -0
- package/dist/services/bridge-rotation-policy.js.map +1 -0
- package/dist/services/bridge-turn-queue.d.ts +154 -0
- package/dist/services/bridge-turn-queue.d.ts.map +1 -0
- package/dist/services/bridge-turn-queue.js +316 -0
- package/dist/services/bridge-turn-queue.js.map +1 -0
- package/dist/services/chat-first-seen-store.d.ts +27 -0
- package/dist/services/chat-first-seen-store.d.ts.map +1 -0
- package/dist/services/chat-first-seen-store.js +114 -0
- package/dist/services/chat-first-seen-store.js.map +1 -0
- package/dist/services/claude-transcript.d.ts +268 -0
- package/dist/services/claude-transcript.d.ts.map +1 -0
- package/dist/services/claude-transcript.js +798 -0
- package/dist/services/claude-transcript.js.map +1 -0
- package/dist/services/coco-transcript.d.ts +35 -0
- package/dist/services/coco-transcript.d.ts.map +1 -0
- package/dist/services/coco-transcript.js +192 -0
- package/dist/services/coco-transcript.js.map +1 -0
- package/dist/services/codex-bridge-queue.d.ts +56 -0
- package/dist/services/codex-bridge-queue.d.ts.map +1 -0
- package/dist/services/codex-bridge-queue.js +150 -0
- package/dist/services/codex-bridge-queue.js.map +1 -0
- package/dist/services/codex-transcript.d.ts +84 -0
- package/dist/services/codex-transcript.d.ts.map +1 -0
- package/dist/services/codex-transcript.js +298 -0
- package/dist/services/codex-transcript.js.map +1 -0
- package/dist/services/group-creator.d.ts +23 -0
- package/dist/services/group-creator.d.ts.map +1 -0
- package/dist/services/group-creator.js +75 -0
- package/dist/services/group-creator.js.map +1 -0
- package/dist/services/groups-store.d.ts +98 -0
- package/dist/services/groups-store.d.ts.map +1 -0
- package/dist/services/groups-store.js +213 -0
- package/dist/services/groups-store.js.map +1 -0
- package/dist/services/oncall-store.d.ts +80 -8
- package/dist/services/oncall-store.d.ts.map +1 -1
- package/dist/services/oncall-store.js +265 -55
- package/dist/services/oncall-store.js.map +1 -1
- package/dist/services/project-scanner.d.ts +1 -2
- package/dist/services/project-scanner.d.ts.map +1 -1
- package/dist/services/project-scanner.js +118 -68
- package/dist/services/project-scanner.js.map +1 -1
- package/dist/services/schedule-store.d.ts +5 -0
- package/dist/services/schedule-store.d.ts.map +1 -1
- package/dist/services/schedule-store.js +77 -1
- package/dist/services/schedule-store.js.map +1 -1
- package/dist/services/session-store.d.ts +22 -0
- package/dist/services/session-store.d.ts.map +1 -1
- package/dist/services/session-store.js +62 -4
- package/dist/services/session-store.js.map +1 -1
- package/dist/setup/bots-store.d.ts +3 -0
- package/dist/setup/bots-store.d.ts.map +1 -0
- package/dist/setup/bots-store.js +24 -0
- package/dist/setup/bots-store.js.map +1 -0
- package/dist/setup/detect-platform.d.ts +14 -0
- package/dist/setup/detect-platform.d.ts.map +1 -0
- package/dist/setup/detect-platform.js +139 -0
- package/dist/setup/detect-platform.js.map +1 -0
- package/dist/setup/ensure-fonts.d.ts +13 -0
- package/dist/setup/ensure-fonts.d.ts.map +1 -0
- package/dist/setup/ensure-fonts.js +225 -0
- package/dist/setup/ensure-fonts.js.map +1 -0
- package/dist/setup/ensure-tmux.d.ts +60 -0
- package/dist/setup/ensure-tmux.d.ts.map +1 -0
- package/dist/setup/ensure-tmux.js +236 -0
- package/dist/setup/ensure-tmux.js.map +1 -0
- package/dist/setup/index.d.ts +9 -0
- package/dist/setup/index.d.ts.map +1 -0
- package/dist/setup/index.js +46 -0
- package/dist/setup/index.js.map +1 -0
- package/dist/setup/lark-scopes.json +301 -0
- package/dist/setup/register-app.d.ts +52 -0
- package/dist/setup/register-app.d.ts.map +1 -0
- package/dist/setup/register-app.js +91 -0
- package/dist/setup/register-app.js.map +1 -0
- package/dist/setup/verify-permissions.d.ts +115 -0
- package/dist/setup/verify-permissions.d.ts.map +1 -0
- package/dist/setup/verify-permissions.js +207 -0
- package/dist/setup/verify-permissions.js.map +1 -0
- package/dist/skills/definitions.d.ts +4 -0
- package/dist/skills/definitions.d.ts.map +1 -1
- package/dist/skills/definitions.js +133 -19
- package/dist/skills/definitions.js.map +1 -1
- package/dist/skills/installer.d.ts +3 -1
- package/dist/skills/installer.d.ts.map +1 -1
- package/dist/skills/installer.js +18 -3
- package/dist/skills/installer.js.map +1 -1
- package/dist/types.d.ts +44 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/bot-routing.d.ts +6 -0
- package/dist/utils/bot-routing.d.ts.map +1 -0
- package/dist/utils/bot-routing.js +50 -0
- package/dist/utils/bot-routing.js.map +1 -0
- package/dist/utils/file-lock.d.ts +2 -0
- package/dist/utils/file-lock.d.ts.map +1 -0
- package/dist/utils/file-lock.js +114 -0
- package/dist/utils/file-lock.js.map +1 -0
- package/dist/utils/font-installer.js +1 -1
- package/dist/utils/font-installer.js.map +1 -1
- package/dist/utils/idle-detector.d.ts +6 -0
- package/dist/utils/idle-detector.d.ts.map +1 -1
- package/dist/utils/idle-detector.js +25 -4
- package/dist/utils/idle-detector.js.map +1 -1
- package/dist/utils/logger.d.ts +10 -0
- package/dist/utils/logger.d.ts.map +1 -1
- package/dist/utils/logger.js +60 -8
- package/dist/utils/logger.js.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/screen-analyzer.d.ts.map +1 -1
- package/dist/utils/screen-analyzer.js +24 -0
- package/dist/utils/screen-analyzer.js.map +1 -1
- package/dist/utils/screenshot-renderer.d.ts.map +1 -1
- package/dist/utils/screenshot-renderer.js +67 -23
- package/dist/utils/screenshot-renderer.js.map +1 -1
- package/dist/utils/terminal-renderer.d.ts +16 -0
- package/dist/utils/terminal-renderer.d.ts.map +1 -1
- package/dist/utils/terminal-renderer.js +40 -23
- package/dist/utils/terminal-renderer.js.map +1 -1
- package/dist/utils/transient-snapshot.d.ts +28 -0
- package/dist/utils/transient-snapshot.d.ts.map +1 -0
- package/dist/utils/transient-snapshot.js +96 -0
- package/dist/utils/transient-snapshot.js.map +1 -0
- package/dist/worker.js +2220 -83
- package/dist/worker.js.map +1 -1
- package/package.json +12 -5
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure rotation-policy gate for the Claude bridge watcher.
|
|
3
|
+
*
|
|
4
|
+
* Lives outside `worker.ts` so tests can import without dragging worker-level
|
|
5
|
+
* fs / IPC side-effects.
|
|
6
|
+
*/
|
|
7
|
+
export type PidFollowResult = 'unavailable' | 'same' | 'switched';
|
|
8
|
+
/**
|
|
9
|
+
* Decide whether `bridgeIngest` should fall through to the directory-mtime
|
|
10
|
+
* `maybeFollowQuietRotation` heuristic this tick.
|
|
11
|
+
*
|
|
12
|
+
* Inputs:
|
|
13
|
+
* - `pidFollow`: result of the authoritative pid-state probe
|
|
14
|
+
* (`maybeFollowSessionRotationViaPid`). `'switched'` means it already
|
|
15
|
+
* moved us; `'same'` means the pid file's `sessionId` matches our
|
|
16
|
+
* current path; `'unavailable'` means the pid file was unreadable
|
|
17
|
+
* (non-Linux, no `~/.claude/sessions/<pid>.json`, validation failure).
|
|
18
|
+
* - `switched`: whether ANY earlier rotation step (pid resolver OR
|
|
19
|
+
* fingerprint fallback) already moved the watcher this tick.
|
|
20
|
+
*
|
|
21
|
+
* Returns true only when there was no earlier switch AND the pid resolver
|
|
22
|
+
* gave no opinion.
|
|
23
|
+
*
|
|
24
|
+
* Trade-off: pid resolver `'same'` is NOT proof that no rotation happened
|
|
25
|
+
* — Claude Code 2.1.123 writes `sessionId` ONCE at process start and the
|
|
26
|
+
* in-pane `/clear` path does not refresh it. We still skip the mtime
|
|
27
|
+
* heuristic on `'same'` because the alternative is sibling-pane hijack:
|
|
28
|
+
* any other Claude pane in the same cwd gets a busier jsonl and the
|
|
29
|
+
* heuristic picks it. The cost is that a pure-local `/clear` with no
|
|
30
|
+
* pending Lark turn won't auto-follow until the user sends a Lark
|
|
31
|
+
* message (which arms fingerprint fallback). The Lark-message path is
|
|
32
|
+
* the dominant /clear recovery flow in practice; sibling-pane
|
|
33
|
+
* corruption would silently break every multi-pane adopt setup.
|
|
34
|
+
*
|
|
35
|
+
* `--resume` is a fresh spawn and rewrites the pid file's sessionId, so
|
|
36
|
+
* it surfaces here as `'switched'`, not `'same'` — it's not affected by
|
|
37
|
+
* this gate.
|
|
38
|
+
*/
|
|
39
|
+
export declare function shouldRunQuietRotation(pidFollow: PidFollowResult, switched: boolean): boolean;
|
|
40
|
+
export interface PidResolverPullbackInput {
|
|
41
|
+
/** sessionId reported by the pid file this tick. */
|
|
42
|
+
resolvedCliSessionId: string;
|
|
43
|
+
/** The pid file's full jsonl path (derived from sessionId + cwd). */
|
|
44
|
+
resolvedPath: string;
|
|
45
|
+
/** Current bridge jsonl path. */
|
|
46
|
+
currentBridgeJsonlPath: string | undefined;
|
|
47
|
+
/** Sid recorded as "stale" by the fingerprint fallback the last time it
|
|
48
|
+
* accepted a candidate the pid file disagreed about. Undefined when no
|
|
49
|
+
* fingerprint accept has overridden the pid file. */
|
|
50
|
+
stalePidStateSessionId: string | undefined;
|
|
51
|
+
}
|
|
52
|
+
export interface PidResolverPullbackDecision {
|
|
53
|
+
/** True ⇒ pid resolver should report 'same' rather than rotate the
|
|
54
|
+
* watcher back to `resolvedPath`. */
|
|
55
|
+
suppress: boolean;
|
|
56
|
+
/** True ⇒ caller should clear `stalePidStateSessionId`: a fresh sid
|
|
57
|
+
* (different from the stale one) appeared in the pid file, meaning a
|
|
58
|
+
* real rotation has happened (`--resume` / fresh spawn) and the prior
|
|
59
|
+
* fingerprint accept is no longer relevant. */
|
|
60
|
+
clearStale: boolean;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Decide whether the pid resolver should pull the watcher back to a path
|
|
64
|
+
* that disagrees with the current bridgeJsonlPath, given the worker's
|
|
65
|
+
* staleness bookkeeping.
|
|
66
|
+
*
|
|
67
|
+
* Rules:
|
|
68
|
+
* - No `stalePidStateSessionId` recorded ⇒ honour pid resolver as before.
|
|
69
|
+
* - Recorded sid matches pid file's current sid ⇒ suppress: this is the
|
|
70
|
+
* spawn-time sid that fingerprint fallback already overrode for an
|
|
71
|
+
* in-pane /clear that pid file can't see.
|
|
72
|
+
* - Recorded sid differs from pid file's current sid ⇒ a NEW rotation has
|
|
73
|
+
* happened (`--resume` / fresh spawn / Claude restart with new pid file
|
|
74
|
+
* contents). Clear the stale flag and let pid resolver switch normally.
|
|
75
|
+
*/
|
|
76
|
+
export declare function evaluatePidResolverPullback(input: PidResolverPullbackInput): PidResolverPullbackDecision;
|
|
77
|
+
/** UUID-shaped Claude jsonl filename (sessionId.jsonl). Duplicated from
|
|
78
|
+
* `src/adapters/cli/claude-code.ts:SESSION_UUID_RE` to keep this module
|
|
79
|
+
* free of adapter imports. Keep the two patterns in sync; both gate
|
|
80
|
+
* trust-set membership and fingerprint-fallback candidate eligibility. */
|
|
81
|
+
export declare const SESSION_ID_FILENAME_RE: RegExp;
|
|
82
|
+
/** Extract the sessionId portion of a `<sid>.jsonl` path (basename minus
|
|
83
|
+
* the `.jsonl` extension). Returns the empty string when the path lacks
|
|
84
|
+
* the expected suffix; callers should treat that as "untrusted". */
|
|
85
|
+
export declare function sessionIdFromJsonlPath(path: string): string;
|
|
86
|
+
export interface FingerprintSwitchInput {
|
|
87
|
+
/** Substring fingerprint to feed Phase 1's scanner. Required. */
|
|
88
|
+
contentFingerprint: string;
|
|
89
|
+
/** Full normalised content of the Lark turn. Phase 2 (unknown-sid
|
|
90
|
+
* exact-content recovery) is skipped when this is missing or empty —
|
|
91
|
+
* short content can't anchor a recovery without ambiguity. */
|
|
92
|
+
contentNormalized?: string;
|
|
93
|
+
/** Trust set populated from initial attach, pid resolver hits, fd probes. */
|
|
94
|
+
knownSessionIds: ReadonlySet<string>;
|
|
95
|
+
/** Phase 1 scanner: substring fingerprint search. Caller wires it to
|
|
96
|
+
* `findJsonlContainingFingerprint`; this helper only sees the result. */
|
|
97
|
+
findSubstring: (acceptCandidate: (path: string) => boolean) => string | null;
|
|
98
|
+
/** Phase 2 scanner: exact-content search returning ALL matches in
|
|
99
|
+
* mtime-descending order. Caller wires it to
|
|
100
|
+
* `findJsonlsContainingExactContent`. */
|
|
101
|
+
findExact: (acceptCandidate: (path: string) => boolean) => string[];
|
|
102
|
+
}
|
|
103
|
+
export type FingerprintSwitchDecision = {
|
|
104
|
+
action: 'switch';
|
|
105
|
+
path: string;
|
|
106
|
+
reason: 'known-sid-substring' | 'unknown-sid-exact';
|
|
107
|
+
} | {
|
|
108
|
+
action: 'abstain';
|
|
109
|
+
reason: 'multiple-unknown-exact';
|
|
110
|
+
candidates: string[];
|
|
111
|
+
} | {
|
|
112
|
+
action: 'no-match';
|
|
113
|
+
};
|
|
114
|
+
/**
|
|
115
|
+
* Two-phase candidate selection for the bridge fingerprint fallback.
|
|
116
|
+
*
|
|
117
|
+
* Phase 1 (known-sid substring): the cheap path. Run the substring
|
|
118
|
+
* fingerprint scanner with an acceptCandidate predicate that requires
|
|
119
|
+
* `(UUID-shaped sid) AND (sid in knownSessionIds)`. Sibling panes are
|
|
120
|
+
* UUID-shaped but not in our trust set — their fingerprint hits are
|
|
121
|
+
* rejected. UUID gate also blocks accidental non-Claude jsonls.
|
|
122
|
+
*
|
|
123
|
+
* Phase 2 (unknown-sid exact-content recovery): only runs when
|
|
124
|
+
* `contentNormalized` is non-empty. Fires when Phase 1 found no match,
|
|
125
|
+
* which is the worst-case in-pane `/clear` scenario where the new sid
|
|
126
|
+
* never reaches our trust set (pid file lags, fd probe missed the open
|
|
127
|
+
* window). Run the exact-content scanner with predicate
|
|
128
|
+
* `(UUID-shaped sid) AND (sid NOT in knownSessionIds)`. Decision:
|
|
129
|
+
* - 0 matches → no-match
|
|
130
|
+
* - 1 match → switch (the post-/clear file we couldn't otherwise see)
|
|
131
|
+
* - ≥2 matches → abstain; multiple untrusted files normalise to the
|
|
132
|
+
* same Lark content, so we cannot pick one without further evidence.
|
|
133
|
+
* Caller is expected to log and surface a diagnostic.
|
|
134
|
+
*
|
|
135
|
+
* Pure: never touches fs / IPC / module state. Tests inject fakes for
|
|
136
|
+
* `findSubstring` and `findExact` to exercise every branch.
|
|
137
|
+
*/
|
|
138
|
+
export declare function decideFingerprintSwitch(input: FingerprintSwitchInput): FingerprintSwitchDecision;
|
|
139
|
+
//# sourceMappingURL=bridge-rotation-policy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bridge-rotation-policy.d.ts","sourceRoot":"","sources":["../../src/services/bridge-rotation-policy.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,MAAM,eAAe,GAAG,aAAa,GAAG,MAAM,GAAG,UAAU,CAAC;AAElE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,wBAAgB,sBAAsB,CACpC,SAAS,EAAE,eAAe,EAC1B,QAAQ,EAAE,OAAO,GAChB,OAAO,CAGT;AAID,MAAM,WAAW,wBAAwB;IACvC,oDAAoD;IACpD,oBAAoB,EAAE,MAAM,CAAC;IAC7B,qEAAqE;IACrE,YAAY,EAAE,MAAM,CAAC;IACrB,iCAAiC;IACjC,sBAAsB,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3C;;0DAEsD;IACtD,sBAAsB,EAAE,MAAM,GAAG,SAAS,CAAC;CAC5C;AAED,MAAM,WAAW,2BAA2B;IAC1C;0CACsC;IACtC,QAAQ,EAAE,OAAO,CAAC;IAClB;;;oDAGgD;IAChD,UAAU,EAAE,OAAO,CAAC;CACrB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,2BAA2B,CACzC,KAAK,EAAE,wBAAwB,GAC9B,2BAA2B,CAQ7B;AAID;;;2EAG2E;AAC3E,eAAO,MAAM,sBAAsB,QAAoE,CAAC;AAExG;;qEAEqE;AACrE,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAG3D;AAED,MAAM,WAAW,sBAAsB;IACrC,iEAAiE;IACjE,kBAAkB,EAAE,MAAM,CAAC;IAC3B;;mEAE+D;IAC/D,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,6EAA6E;IAC7E,eAAe,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IACrC;8EAC0E;IAC1E,aAAa,EAAE,CAAC,eAAe,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,KAAK,MAAM,GAAG,IAAI,CAAC;IAC7E;;8CAE0C;IAC1C,SAAS,EAAE,CAAC,eAAe,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,KAAK,MAAM,EAAE,CAAC;CACrE;AAED,MAAM,MAAM,yBAAyB,GACjC;IAAE,MAAM,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,qBAAqB,GAAG,mBAAmB,CAAA;CAAE,GACvF;IAAE,MAAM,EAAE,SAAS,CAAC;IAAC,MAAM,EAAE,wBAAwB,CAAC;IAAC,UAAU,EAAE,MAAM,EAAE,CAAA;CAAE,GAC7E;IAAE,MAAM,EAAE,UAAU,CAAA;CAAE,CAAC;AAE3B;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,sBAAsB,GAC5B,yBAAyB,CAoB3B"}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure rotation-policy gate for the Claude bridge watcher.
|
|
3
|
+
*
|
|
4
|
+
* Lives outside `worker.ts` so tests can import without dragging worker-level
|
|
5
|
+
* fs / IPC side-effects.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Decide whether `bridgeIngest` should fall through to the directory-mtime
|
|
9
|
+
* `maybeFollowQuietRotation` heuristic this tick.
|
|
10
|
+
*
|
|
11
|
+
* Inputs:
|
|
12
|
+
* - `pidFollow`: result of the authoritative pid-state probe
|
|
13
|
+
* (`maybeFollowSessionRotationViaPid`). `'switched'` means it already
|
|
14
|
+
* moved us; `'same'` means the pid file's `sessionId` matches our
|
|
15
|
+
* current path; `'unavailable'` means the pid file was unreadable
|
|
16
|
+
* (non-Linux, no `~/.claude/sessions/<pid>.json`, validation failure).
|
|
17
|
+
* - `switched`: whether ANY earlier rotation step (pid resolver OR
|
|
18
|
+
* fingerprint fallback) already moved the watcher this tick.
|
|
19
|
+
*
|
|
20
|
+
* Returns true only when there was no earlier switch AND the pid resolver
|
|
21
|
+
* gave no opinion.
|
|
22
|
+
*
|
|
23
|
+
* Trade-off: pid resolver `'same'` is NOT proof that no rotation happened
|
|
24
|
+
* — Claude Code 2.1.123 writes `sessionId` ONCE at process start and the
|
|
25
|
+
* in-pane `/clear` path does not refresh it. We still skip the mtime
|
|
26
|
+
* heuristic on `'same'` because the alternative is sibling-pane hijack:
|
|
27
|
+
* any other Claude pane in the same cwd gets a busier jsonl and the
|
|
28
|
+
* heuristic picks it. The cost is that a pure-local `/clear` with no
|
|
29
|
+
* pending Lark turn won't auto-follow until the user sends a Lark
|
|
30
|
+
* message (which arms fingerprint fallback). The Lark-message path is
|
|
31
|
+
* the dominant /clear recovery flow in practice; sibling-pane
|
|
32
|
+
* corruption would silently break every multi-pane adopt setup.
|
|
33
|
+
*
|
|
34
|
+
* `--resume` is a fresh spawn and rewrites the pid file's sessionId, so
|
|
35
|
+
* it surfaces here as `'switched'`, not `'same'` — it's not affected by
|
|
36
|
+
* this gate.
|
|
37
|
+
*/
|
|
38
|
+
export function shouldRunQuietRotation(pidFollow, switched) {
|
|
39
|
+
if (switched)
|
|
40
|
+
return false;
|
|
41
|
+
return pidFollow === 'unavailable';
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Decide whether the pid resolver should pull the watcher back to a path
|
|
45
|
+
* that disagrees with the current bridgeJsonlPath, given the worker's
|
|
46
|
+
* staleness bookkeeping.
|
|
47
|
+
*
|
|
48
|
+
* Rules:
|
|
49
|
+
* - No `stalePidStateSessionId` recorded ⇒ honour pid resolver as before.
|
|
50
|
+
* - Recorded sid matches pid file's current sid ⇒ suppress: this is the
|
|
51
|
+
* spawn-time sid that fingerprint fallback already overrode for an
|
|
52
|
+
* in-pane /clear that pid file can't see.
|
|
53
|
+
* - Recorded sid differs from pid file's current sid ⇒ a NEW rotation has
|
|
54
|
+
* happened (`--resume` / fresh spawn / Claude restart with new pid file
|
|
55
|
+
* contents). Clear the stale flag and let pid resolver switch normally.
|
|
56
|
+
*/
|
|
57
|
+
export function evaluatePidResolverPullback(input) {
|
|
58
|
+
if (input.stalePidStateSessionId === undefined) {
|
|
59
|
+
return { suppress: false, clearStale: false };
|
|
60
|
+
}
|
|
61
|
+
if (input.resolvedCliSessionId === input.stalePidStateSessionId) {
|
|
62
|
+
return { suppress: true, clearStale: false };
|
|
63
|
+
}
|
|
64
|
+
return { suppress: false, clearStale: true };
|
|
65
|
+
}
|
|
66
|
+
// ─── Two-phase fingerprint-fallback decision ───────────────────────────────
|
|
67
|
+
/** UUID-shaped Claude jsonl filename (sessionId.jsonl). Duplicated from
|
|
68
|
+
* `src/adapters/cli/claude-code.ts:SESSION_UUID_RE` to keep this module
|
|
69
|
+
* free of adapter imports. Keep the two patterns in sync; both gate
|
|
70
|
+
* trust-set membership and fingerprint-fallback candidate eligibility. */
|
|
71
|
+
export const SESSION_ID_FILENAME_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
72
|
+
/** Extract the sessionId portion of a `<sid>.jsonl` path (basename minus
|
|
73
|
+
* the `.jsonl` extension). Returns the empty string when the path lacks
|
|
74
|
+
* the expected suffix; callers should treat that as "untrusted". */
|
|
75
|
+
export function sessionIdFromJsonlPath(path) {
|
|
76
|
+
const base = path.split('/').pop() ?? '';
|
|
77
|
+
return base.endsWith('.jsonl') ? base.slice(0, -'.jsonl'.length) : '';
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Two-phase candidate selection for the bridge fingerprint fallback.
|
|
81
|
+
*
|
|
82
|
+
* Phase 1 (known-sid substring): the cheap path. Run the substring
|
|
83
|
+
* fingerprint scanner with an acceptCandidate predicate that requires
|
|
84
|
+
* `(UUID-shaped sid) AND (sid in knownSessionIds)`. Sibling panes are
|
|
85
|
+
* UUID-shaped but not in our trust set — their fingerprint hits are
|
|
86
|
+
* rejected. UUID gate also blocks accidental non-Claude jsonls.
|
|
87
|
+
*
|
|
88
|
+
* Phase 2 (unknown-sid exact-content recovery): only runs when
|
|
89
|
+
* `contentNormalized` is non-empty. Fires when Phase 1 found no match,
|
|
90
|
+
* which is the worst-case in-pane `/clear` scenario where the new sid
|
|
91
|
+
* never reaches our trust set (pid file lags, fd probe missed the open
|
|
92
|
+
* window). Run the exact-content scanner with predicate
|
|
93
|
+
* `(UUID-shaped sid) AND (sid NOT in knownSessionIds)`. Decision:
|
|
94
|
+
* - 0 matches → no-match
|
|
95
|
+
* - 1 match → switch (the post-/clear file we couldn't otherwise see)
|
|
96
|
+
* - ≥2 matches → abstain; multiple untrusted files normalise to the
|
|
97
|
+
* same Lark content, so we cannot pick one without further evidence.
|
|
98
|
+
* Caller is expected to log and surface a diagnostic.
|
|
99
|
+
*
|
|
100
|
+
* Pure: never touches fs / IPC / module state. Tests inject fakes for
|
|
101
|
+
* `findSubstring` and `findExact` to exercise every branch.
|
|
102
|
+
*/
|
|
103
|
+
export function decideFingerprintSwitch(input) {
|
|
104
|
+
const matchedKnown = input.findSubstring((path) => {
|
|
105
|
+
const sid = sessionIdFromJsonlPath(path);
|
|
106
|
+
return SESSION_ID_FILENAME_RE.test(sid) && input.knownSessionIds.has(sid);
|
|
107
|
+
});
|
|
108
|
+
if (matchedKnown) {
|
|
109
|
+
return { action: 'switch', path: matchedKnown, reason: 'known-sid-substring' };
|
|
110
|
+
}
|
|
111
|
+
if (!input.contentNormalized || input.contentNormalized.length === 0) {
|
|
112
|
+
return { action: 'no-match' };
|
|
113
|
+
}
|
|
114
|
+
const exact = input.findExact((path) => {
|
|
115
|
+
const sid = sessionIdFromJsonlPath(path);
|
|
116
|
+
return SESSION_ID_FILENAME_RE.test(sid) && !input.knownSessionIds.has(sid);
|
|
117
|
+
});
|
|
118
|
+
if (exact.length === 0)
|
|
119
|
+
return { action: 'no-match' };
|
|
120
|
+
if (exact.length > 1) {
|
|
121
|
+
return { action: 'abstain', reason: 'multiple-unknown-exact', candidates: exact };
|
|
122
|
+
}
|
|
123
|
+
return { action: 'switch', path: exact[0], reason: 'unknown-sid-exact' };
|
|
124
|
+
}
|
|
125
|
+
//# sourceMappingURL=bridge-rotation-policy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bridge-rotation-policy.js","sourceRoot":"","sources":["../../src/services/bridge-rotation-policy.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,MAAM,UAAU,sBAAsB,CACpC,SAA0B,EAC1B,QAAiB;IAEjB,IAAI,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC3B,OAAO,SAAS,KAAK,aAAa,CAAC;AACrC,CAAC;AA4BD;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,2BAA2B,CACzC,KAA+B;IAE/B,IAAI,KAAK,CAAC,sBAAsB,KAAK,SAAS,EAAE,CAAC;QAC/C,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC;IAChD,CAAC;IACD,IAAI,KAAK,CAAC,oBAAoB,KAAK,KAAK,CAAC,sBAAsB,EAAE,CAAC;QAChE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC;IAC/C,CAAC;IACD,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC;AAC/C,CAAC;AAED,8EAA8E;AAE9E;;;2EAG2E;AAC3E,MAAM,CAAC,MAAM,sBAAsB,GAAG,iEAAiE,CAAC;AAExG;;qEAEqE;AACrE,MAAM,UAAU,sBAAsB,CAAC,IAAY;IACjD,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC;IACzC,OAAO,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AACxE,CAAC;AAyBD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,UAAU,uBAAuB,CACrC,KAA6B;IAE7B,MAAM,YAAY,GAAG,KAAK,CAAC,aAAa,CAAC,CAAC,IAAI,EAAE,EAAE;QAChD,MAAM,GAAG,GAAG,sBAAsB,CAAC,IAAI,CAAC,CAAC;QACzC,OAAO,sBAAsB,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,eAAe,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC5E,CAAC,CAAC,CAAC;IACH,IAAI,YAAY,EAAE,CAAC;QACjB,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,EAAE,MAAM,EAAE,qBAAqB,EAAE,CAAC;IACjF,CAAC;IACD,IAAI,CAAC,KAAK,CAAC,iBAAiB,IAAI,KAAK,CAAC,iBAAiB,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACrE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;IAChC,CAAC;IACD,MAAM,KAAK,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,IAAI,EAAE,EAAE;QACrC,MAAM,GAAG,GAAG,sBAAsB,CAAC,IAAI,CAAC,CAAC;QACzC,OAAO,sBAAsB,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,eAAe,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC7E,CAAC,CAAC,CAAC;IACH,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;IACtD,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACrB,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,wBAAwB,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC;IACpF,CAAC;IACD,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,mBAAmB,EAAE,CAAC;AAC3E,CAAC"}
|
|
@@ -0,0 +1,154 @@
|
|
|
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
|
+
/** Full normalised content of the Lark message. Used by the rotation
|
|
55
|
+
* fallback's recovery path to gate a switch into an UNKNOWN sessionId
|
|
56
|
+
* on exact equality with a user/queue event in that file — much
|
|
57
|
+
* stronger than the substring fingerprint check, which can't tell
|
|
58
|
+
* "test" from "run tests" across sibling panes. Stored in addition to
|
|
59
|
+
* `contentFingerprint` (not instead of) because in-pane known-sid
|
|
60
|
+
* candidates still benefit from the cheaper substring path. */
|
|
61
|
+
contentNormalized?: string;
|
|
62
|
+
/** JSONL file the turn's user event was first seen in. Stamped by ingest()
|
|
63
|
+
* when the turn transitions to started. Lets the emit step re-read text
|
|
64
|
+
* from the original transcript even after a sessionId rotation has
|
|
65
|
+
* pointed bridgeJsonlPath at a *different* file — without this stamp,
|
|
66
|
+
* uuid → text resolution would fail and the reply would be silently
|
|
67
|
+
* dropped. */
|
|
68
|
+
sourceJsonlPath?: string;
|
|
69
|
+
/** Wall-clock millis when mark() was called. Lets the fingerprint-based
|
|
70
|
+
* rotation fallback bound its scan to events written after we marked
|
|
71
|
+
* the turn — short fingerprints ("hello", "test") would otherwise risk
|
|
72
|
+
* matching pre-existing user lines in unrelated sibling jsonls. */
|
|
73
|
+
markTimeMs?: number;
|
|
74
|
+
}
|
|
75
|
+
/** Trim a Lark message into a stable fingerprint. Keeps a leading window
|
|
76
|
+
* of non-whitespace-collapsed content; long enough to disambiguate, short
|
|
77
|
+
* enough that minor formatting differences (newlines, attachment hints
|
|
78
|
+
* appended below) don't break the match. */
|
|
79
|
+
export declare function makeFingerprint(message: string, len?: number): string | undefined;
|
|
80
|
+
export declare class BridgeTurnQueue {
|
|
81
|
+
private seen;
|
|
82
|
+
private queue;
|
|
83
|
+
private collecting;
|
|
84
|
+
/** Register events as historical — their uuids are now considered seen
|
|
85
|
+
* but no attribution happens. Used at attach time to baseline. */
|
|
86
|
+
absorb(events: TranscriptEvent[]): void;
|
|
87
|
+
/** Push a new pending turn for the next Lark message. `contentFingerprint`
|
|
88
|
+
* (when set) restricts which user event can start this turn — only a
|
|
89
|
+
* user event whose content contains the fingerprint qualifies. Pass
|
|
90
|
+
* `undefined` to start on the next user event regardless (legacy).
|
|
91
|
+
*
|
|
92
|
+
* `markTimeMs` is captured here so the rotation fallback can bound its
|
|
93
|
+
* fingerprint scan to events written after this point — protects short
|
|
94
|
+
* fingerprints from matching old history in unrelated sibling jsonls. */
|
|
95
|
+
mark(turnId: string, contentFingerprint?: string, markTimeMs?: number, contentNormalized?: string): string;
|
|
96
|
+
/** Drop all pending turns. Used when the worker discovers it can't
|
|
97
|
+
* reliably attribute future events (e.g. baseline raced with a turn
|
|
98
|
+
* already in flight) and wants to clear the slate. */
|
|
99
|
+
clearPending(): BridgePendingTurn[];
|
|
100
|
+
/** Drop a specific pending turn by turnId iff it has not yet started
|
|
101
|
+
* collecting assistant text. Returns the dropped turn or null if not
|
|
102
|
+
* found / already started. Used by the worker when a writeInput's
|
|
103
|
+
* deferred recheck conclusively fails — the user has been notified
|
|
104
|
+
* the message was lost, so keeping a fingerprint-bearing mark around
|
|
105
|
+
* only fuels the per-tick rotation-fallback scan that already
|
|
106
|
+
* spammed 99% CPU once (no jsonl line will ever match). */
|
|
107
|
+
dropPendingTurn(turnId: string): BridgePendingTurn | null;
|
|
108
|
+
/** Sweep pending (unstarted) turns whose mark is older than `maxAgeMs`.
|
|
109
|
+
* Returns the dropped turns for logging. Belt-and-braces backstop for
|
|
110
|
+
* any future code path that leaves an unstarted mark stranded — without
|
|
111
|
+
* it, `maybeSwitchBridgeJsonl` would keep doing full-directory jsonl
|
|
112
|
+
* scans every poll tick until the worker restarts. Started turns are
|
|
113
|
+
* never expired here: once Claude actually wrote the user line, the
|
|
114
|
+
* turn is collecting assistant text and we want to wait however long
|
|
115
|
+
* the model takes. */
|
|
116
|
+
pruneExpired(maxAgeMs: number, now?: number): BridgePendingTurn[];
|
|
117
|
+
/** Process newly-appended events. Idempotent on uuid: events with seen
|
|
118
|
+
* uuids are skipped, so callers can safely replay.
|
|
119
|
+
*
|
|
120
|
+
* `sourceJsonlPath` (when provided) is stamped onto a turn at the moment
|
|
121
|
+
* it transitions from "pending" to "started" — so that emit-time text
|
|
122
|
+
* resolution reads the same transcript file the user/assistant uuids
|
|
123
|
+
* were originally observed in. Without this, a sessionId rotation
|
|
124
|
+
* between ingest and emit would silently drop the reply, since the
|
|
125
|
+
* global current jsonl path would no longer contain those uuids. */
|
|
126
|
+
ingest(events: TranscriptEvent[], sourceJsonlPath?: string): void;
|
|
127
|
+
/** Shared turn-start handler. Called for both `role:user` and
|
|
128
|
+
* `attachment(queued_command)` events once meaningfulness has been
|
|
129
|
+
* established by the caller. Encapsulates:
|
|
130
|
+
* 1. HOL-block drop of the previous collecting turn when it got no
|
|
131
|
+
* assistant text (Claude moved on).
|
|
132
|
+
* 2. Fingerprint-gated start of the earliest unstarted Lark turn,
|
|
133
|
+
* falling through to local-turn synthesis on mismatch.
|
|
134
|
+
* 3. markTimeMs override to the transcript event's own timestamp —
|
|
135
|
+
* critical for type-ahead, where the original markTimeMs (set when
|
|
136
|
+
* the worker wrote to PTY) can be many seconds earlier than the
|
|
137
|
+
* moment Claude actually dequeues and starts processing the turn.
|
|
138
|
+
* The bridge-fallback gate's [markTimeMs, nextBoundaryMs) window
|
|
139
|
+
* MUST anchor on the latter, otherwise a `botmux send` from the
|
|
140
|
+
* previous turn can leak into the next turn's window and the
|
|
141
|
+
* suppression decision flips to the wrong turn (real reply
|
|
142
|
+
* suppressed, fallback shown — exactly what the type-ahead-disable
|
|
143
|
+
* in commit b2d9791 was protecting against). */
|
|
144
|
+
private handleTurnStart;
|
|
145
|
+
/** Pop FIFO any leading turn that's started AND has assistant text.
|
|
146
|
+
* Returns the popped turns in order; the caller is responsible for
|
|
147
|
+
* rebuilding the text payload from the assistant uuids. */
|
|
148
|
+
drainEmittable(): BridgePendingTurn[];
|
|
149
|
+
/** Number of queued (not-yet-emitted) Lark turns. */
|
|
150
|
+
size(): number;
|
|
151
|
+
/** Test helper — peek the queue without mutating. */
|
|
152
|
+
peek(): readonly BridgePendingTurn[];
|
|
153
|
+
}
|
|
154
|
+
//# 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,EAAE,uBAAuB,EAA0E,KAAK,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAI/J,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;;;;;;oEAMgE;IAChE,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B;;;;;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,EAAE,iBAAiB,CAAC,EAAE,MAAM,GAAG,MAAM;IAYtH;;2DAEuD;IACvD,YAAY,IAAI,iBAAiB,EAAE;IAMnC;;;;;;gEAM4D;IAC5D,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,iBAAiB,GAAG,IAAI;IAOzD;;;;;;;2BAOuB;IACvB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,GAAE,MAAmB,GAAG,iBAAiB,EAAE;IAY7E;;;;;;;;yEAQqE;IACrE,MAAM,CAAC,MAAM,EAAE,eAAe,EAAE,EAAE,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI;IAgEjE;;;;;;;;;;;;;;;;0DAgBsD;IACtD,OAAO,CAAC,eAAe;IA0DvB;;gEAE4D;IAC5D,cAAc,IAAI,iBAAiB,EAAE;IAYrC,qDAAqD;IACrD,IAAI,IAAI,MAAM;IAId,qDAAqD;IACrD,IAAI,IAAI,SAAS,iBAAiB,EAAE;CAGrC"}
|