thinkpool-pair 0.7.131 → 0.7.132
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/bridge.mjs +37 -0
- package/claude-session.mjs +4 -0
- package/package.json +1 -1
package/bridge.mjs
CHANGED
|
@@ -656,6 +656,32 @@ const sessions = new Map()
|
|
|
656
656
|
let attachedId = null
|
|
657
657
|
let shuttingDown = false
|
|
658
658
|
|
|
659
|
+
// Background warmer (2026-07-02): after a restart, proactively cold-boot the lazily-
|
|
660
|
+
// restored IDLE terminals — ready-gated to ≤WARM_CONCURRENCY at once so there's no
|
|
661
|
+
// boot-storm (the contention that made restore take minutes) — so they're warm BEFORE
|
|
662
|
+
// the user clicks them, erasing the lazy-boot first-turn penalty. A user turn to a
|
|
663
|
+
// still-cold terminal boots it on demand as the fallback. warmComplete frees a slot when
|
|
664
|
+
// a terminal's init `system` event proves it live (called from onEvent).
|
|
665
|
+
const warmQueue = []
|
|
666
|
+
let warmingCount = 0
|
|
667
|
+
const WARM_CONCURRENCY = 3
|
|
668
|
+
function pumpWarm() {
|
|
669
|
+
while (warmingCount < WARM_CONCURRENCY && warmQueue.length) {
|
|
670
|
+
const e = warmQueue.shift()
|
|
671
|
+
if (!e || e.warmed || e.warming || !e.session || e.session.started) continue
|
|
672
|
+
e.warming = true; warmingCount++
|
|
673
|
+
try { e.session.warm?.() } catch { e.warming = false; warmingCount--; continue }
|
|
674
|
+
// Free the slot if a warm never signals ready (stuck boot) so the queue still drains.
|
|
675
|
+
e.warmTimer = setTimeout(() => { if (e.warming) { e.warming = false; warmingCount = Math.max(0, warmingCount - 1); pumpWarm() } }, 100000)
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
const warmComplete = (e) => {
|
|
679
|
+
if (!e || !e.warming) return
|
|
680
|
+
e.warming = false; e.warmed = true; warmingCount = Math.max(0, warmingCount - 1)
|
|
681
|
+
try { clearTimeout(e.warmTimer) } catch { /* noop */ }
|
|
682
|
+
pumpWarm()
|
|
683
|
+
}
|
|
684
|
+
|
|
659
685
|
// One send door for every broadcast. supabase-js ≥2.10x deprecation-warns
|
|
660
686
|
// (console.warn) whenever send() silently falls back to REST because the
|
|
661
687
|
// socket can't push — at the flush timer's 28×/s that's a wall of warnings
|
|
@@ -1625,6 +1651,7 @@ function openStructured({ id, model, resume, log, commands, mode, spawnedBy, rol
|
|
|
1625
1651
|
return { allow: true, reason: 'no reviewed slice targeted' }
|
|
1626
1652
|
}
|
|
1627
1653
|
: null
|
|
1654
|
+
entry.deferred = !!defer // a lazily-booted idle terminal — the background warmer will boot it
|
|
1628
1655
|
entry.session = startClaudeSession({
|
|
1629
1656
|
cwd: cwd || process.cwd(), model, resume, mode, rolePrompt,
|
|
1630
1657
|
// defer: a restored-idle terminal cold-boots its query only on first use — restore is
|
|
@@ -1728,6 +1755,9 @@ function openStructured({ id, model, resume, log, commands, mode, spawnedBy, rol
|
|
|
1728
1755
|
try { entry.session?.sendTurn('continue') } catch { /* not resumable — stays idle, user can continue manually */ }
|
|
1729
1756
|
process.stderr.write(`\n ◆ auto-resumed interrupted turn (${id.slice(0, 8)}) — sent continue.\n`)
|
|
1730
1757
|
}
|
|
1758
|
+
// A background-warmed terminal is now live — free its warmer slot so the next queued
|
|
1759
|
+
// idle terminal starts booting.
|
|
1760
|
+
if (evt.kind === 'system' && entry.warming) warmComplete(entry)
|
|
1731
1761
|
// Chrome events (mode / usage / clear / compact) are transient state, not
|
|
1732
1762
|
// transcript — broadcast + print them but keep out of the persisted/replayed log.
|
|
1733
1763
|
// 'stalled' is transient turn-liveness state (would replay as a stale frozen
|
|
@@ -2355,6 +2385,13 @@ Output:`
|
|
|
2355
2385
|
// saved-session restore above still runs unconditionally, so a bridge
|
|
2356
2386
|
// restart resumes backgrounded sessions regardless of this guard.
|
|
2357
2387
|
else if (attachedCmd) openStructured({ id: randomUUID() })
|
|
2388
|
+
// Background-warm the lazily-restored idle terminals shortly after restore, so
|
|
2389
|
+
// they're ready before the user clicks them (no first-turn penalty). Delayed so the
|
|
2390
|
+
// eagerly-booted (mid-turn / auto-resumed) sessions get the machine to themselves first.
|
|
2391
|
+
setTimeout(() => {
|
|
2392
|
+
for (const [, e] of sessions) if (e.deferred && !e.warmed && !e.warming && !e.session?.started) warmQueue.push(e)
|
|
2393
|
+
if (warmQueue.length) { process.stderr.write(`\n ◆ background-warming ${warmQueue.length} idle terminal(s) (≤${WARM_CONCURRENCY} at a time)…\n`); pumpWarm() }
|
|
2394
|
+
}, 6000)
|
|
2358
2395
|
}
|
|
2359
2396
|
else {
|
|
2360
2397
|
// Reuse the prior auto-opened PTY id across restarts so a reconnecting
|
package/claude-session.mjs
CHANGED
|
@@ -861,6 +861,10 @@ export function startClaudeSession({ cwd, model, resume, env, mode: initialMode
|
|
|
861
861
|
// A lazy (restored-idle) session cold-boots the query on its first turn. input.push
|
|
862
862
|
// is queue-backed, so the pushed turn buffers and runs once the query is ready.
|
|
863
863
|
sendTurn(text) { if (!closed) { if (!started) runQuery(); turnActive = true; sawSuggestion = false; if (sugTimer) { clearTimeout(sugTimer); sugTimer = null } lastEvtTs = Date.now(); stalledSent = false; input.push([{ type: 'text', text: String(text) }, { type: 'text', text: `<system-reminder>\n${TP_ROOM_REMINDER}\n</system-reminder>` }]) } },
|
|
864
|
+
// Cold-boot the query WITHOUT sending a turn — the background warmer calls this on
|
|
865
|
+
// lazily-restored idle terminals so they're ready before the user clicks them.
|
|
866
|
+
warm() { if (!started && !closed) runQuery() },
|
|
867
|
+
get started() { return started },
|
|
864
868
|
// Set the permission mode — Claude Code's ⇧⇥ cycle. setPermissionMode is a
|
|
865
869
|
// streaming control request (drives plan-mode behaviour SDK-side); the local
|
|
866
870
|
// `mode` drives our PreToolUse auto-approve policy. Echo so the room syncs.
|