thinkpool-pair 0.7.64 → 0.7.66
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/account.mjs +19 -1
- package/bridge.mjs +9 -0
- package/package.json +1 -1
package/account.mjs
CHANGED
|
@@ -270,9 +270,27 @@ export async function runAccount(SUPABASE_URL, SUPABASE_ANON) {
|
|
|
270
270
|
// just stops — restart is meaningful for the always-on bridge. Old bridges (pre-0.7.60)
|
|
271
271
|
// don't subscribe to this event, so the dashboard button is a harmless no-op there.
|
|
272
272
|
acct.on('broadcast', { event: 'restart' }, () => {
|
|
273
|
+
const supervised = process.env.THINKPOOL_PAIR_AUTOUPDATE === '1'
|
|
273
274
|
process.stderr.write('\n ◇ restart requested from the dashboard — bouncing bridge (sessions resume).\n')
|
|
274
275
|
restarting = true
|
|
275
|
-
setTimeout(() => {
|
|
276
|
+
setTimeout(() => {
|
|
277
|
+
try { releaseSingleton() } catch { /* noop */ }
|
|
278
|
+
// A SUPERVISED bridge (launchd KeepAlive / systemd Restart=always / the
|
|
279
|
+
// bridge.mjs --supervise wrapper) is respawned by its supervisor on exit, so a
|
|
280
|
+
// bare exit(1) bounces cleanly. A plain FOREGROUND bridge has NO supervisor —
|
|
281
|
+
// there exit = death and the dashboard "Restart" becomes a footgun (2026-06-25:
|
|
282
|
+
// Max's manual `npx thinkpool-pair` died on Restart and never came back). So when
|
|
283
|
+
// unsupervised, re-spawn ourselves DETACHED *after* releasing the machine lock
|
|
284
|
+
// (the child's acquireSingleton() then sees no live owner and takes it). The child
|
|
285
|
+
// has no TTY → bridge.mjs routes a bare invocation straight to account mode. Gated
|
|
286
|
+
// on `supervised` so we never double-spawn (supervisor + self) into two bridges.
|
|
287
|
+
if (!supervised) {
|
|
288
|
+
try {
|
|
289
|
+
spawn(process.execPath, [process.argv[1]], { detached: true, stdio: 'ignore', env: process.env }).unref()
|
|
290
|
+
} catch { /* respawn failed → exit anyway, no worse than the old behaviour */ }
|
|
291
|
+
}
|
|
292
|
+
process.exit(1)
|
|
293
|
+
}, 2500)
|
|
276
294
|
process.kill(process.pid, 'SIGTERM')
|
|
277
295
|
})
|
|
278
296
|
// Throttled so a realtime reconnect storm can't machine-gun track() into the
|
package/bridge.mjs
CHANGED
|
@@ -708,6 +708,15 @@ function printLocal(evt) {
|
|
|
708
708
|
// first). EVERY event that joins entry.log must go through here so the client's
|
|
709
709
|
// "drop ≤ seqHi" cursor stays correct (a logged event with no seq would break it).
|
|
710
710
|
function pushLog(entry, evt) {
|
|
711
|
+
// Single stamp point: guarantee ts+cid on EVERY logged event. echoYou() and the
|
|
712
|
+
// /model,/clear control lines called pushLog WITHOUT a prior stampEvent, so
|
|
713
|
+
// `you`/`control` were logged with a seq but NO ts. On a long session the
|
|
714
|
+
// client's capEvents evicts the agent turns below an old `you` line, leaving it
|
|
715
|
+
// with no lower-seq checkpoint → orderEvents pins the ts-less line to ts 0 and
|
|
716
|
+
// every old user message stacks at the TOP of the timeline (room XE3IQ, 2026-06-25).
|
|
717
|
+
// A real ts makes ordering wall-clock — immune to eviction AND /clear seq resets.
|
|
718
|
+
// stampEvent is idempotent, so the callers that already stamp are unaffected.
|
|
719
|
+
stampEvent(evt)
|
|
711
720
|
// Only TRANSCRIPT events consume a seq (seqable) — replay-chrome kinds the web
|
|
712
721
|
// filters out (system/thinking_tokens/effort) ride the log seq-less so they don't
|
|
713
722
|
// hole the client's applied sequence. See event-id.mjs NO_SEQ_KINDS.
|