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