Lines 82-122javascript
82// a flow-revert kill and the next dispatch wave loses the pending resume record, so the task
83// re-dispatches COLD (fresh lane, no resume). That's safe: the healed transcript survives on
84// disk (sanitizeSession wrote it), so a cold re-dispatch can't 400 on a dangling tool_use — it
85// just loses the resume/revert-target carry. Not worth persisting for that narrow window.
86const flowRedispatch = new Map()
87// FL-B2 — per-flow HARD budget ledger (flowId → budget). Accumulates each lane's
88// completed-turn output tokens (onEvent 'result') so the autopilot cap stops the next
89// wave BEFORE overrun. Lives bridge-side because waves dispatch across separate
90// broadcasts; without persistent state the cap can never bite.
91const flowBudgets = new Map()
92import { formatPeek, PEEK, siblingsOf, resolveSibling, crossPostDecision, CROSSPOST, spawnDecision, SPAWN, CROSSROOM, formatPairRoster, crossRoomPostDecision, formatRoomNow } from './cross-terminal.mjs'
93import { turnInFlight } from './update-gate.mjs'
94import { saveSession, flushSession, deleteSession, loadAll, canResume, loadPtyId, savePtyId, loadNames, saveNames, appendDurableEvents, seedDurableEvents, readDurablePage, readDurableOldestSeq } from './session-store.mjs'
95import { stampEvent, makeSeqCounter, maxSeq, seqable, capReplayEvents, chunkReplayEvents, boundEventForBroadcast, inlineImageBlocks, usageReportLine } from './event-id.mjs'
96import { makeThrottledTrack } from './presence.mjs'
98// Public client creds (the same anon values the web app ships — safe to embed).
99// Override with TP_SUPABASE_URL / TP_SUPABASE_ANON if you ever need to.
100const SUPABASE_URL = process.env.TP_SUPABASE_URL || 'https://daytvtakmlixpfbbqzjd.supabase.co'
101const WEB_BASE = process.env.TP_WEB_BASE || 'https://thinkpool.io'
102const SUPABASE_ANON = process.env.TP_SUPABASE_ANON || 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImRheXR2dGFrbWxpeHBmYmJxempkIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NzgwNTMxNzEsImV4cCI6MjA5MzYyOTE3MX0.zYmz8bHUNEY4STUr2JuXChAsKwxNwkwFHe1Hd0Betqo'
104// Owner access token for AUTHED web writes (currently /api/code-mockup, whip L16).
105// The room-bridge is anon by design; the account supervisor (account.mjs) owns the
106// session, so it hands a fresh owner token in at spawn (TP_ACCESS_TOKEN) and pushes
107// a rotated one over IPC ({ t:'token' }) on each refresh. Stays null for a
108// directly-started bridge with no account login — those writes go unattributed,
109// never owner-spoofed (the server treats a missing/!owner token accordingly).
110let codeAuthToken = process.env.TP_ACCESS_TOKEN || null
112// Per-terminal rolling scrollback (raw chars). Replayed to joining clients.
113// Kept comfortably under realtime payload limits once base64'd.
114const SCROLLBACK_MAX = 120_000
116// node-pty is a native module — loaded lazily so a friendly message shows
117// if it isn't built yet.
118// Structured Claude (the default) runs through the Agent SDK and needs NO PTY,
119// so a failed node-pty build must not block the bridge — it only disables the
120// raw-PTY path (other CLIs / TP_PTY=1), which we flag clearly if it's used.
122try { pty = (await import('node-pty')).default ?? (await import('node-pty')) }