thinkpool-pair 0.7.33 → 0.7.35

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.
Files changed (2) hide show
  1. package/bridge.mjs +20 -2
  2. package/package.json +1 -1
package/bridge.mjs CHANGED
@@ -650,7 +650,13 @@ function openStructured({ id, model, resume, log, commands, mode }) {
650
650
  if (entry.log.length) process.stderr.write(`\n ◆ restored ${entry.log.length} prior events (${id.slice(0, 8)})${resume ? ' + resuming live context' : ''}.\n`)
651
651
  // Persist the permission mode alongside the transcript so a bridge restart
652
652
  // restores the session in the SAME mode (a bypass room stays bypass on resume).
653
- const persist = () => saveSession(room, id, { sessionId: entry.session?.sessionId || resume || null, log: entry.log, commands: entry.commands, mode: entry.mode })
653
+ const sessionData = () => ({ sessionId: entry.session?.sessionId || resume || null, log: entry.log, commands: entry.commands, mode: entry.mode })
654
+ const persist = () => saveSession(room, id, sessionData())
655
+ // Synchronous flush of this session's record. Used on open (so a brand-new session
656
+ // has a file under its id BEFORE its first event — surviving a restart inside the
657
+ // 1.5s saveSession debounce window) and on shutdown (so events since the last
658
+ // debounced write aren't lost). Contract #2: restart resumes, no lost messages.
659
+ entry.flush = () => flushSession(room, id, sessionData())
654
660
  entry.session = startClaudeSession({
655
661
  cwd: process.cwd(), model, resume, mode,
656
662
  env: { ...process.env, TP_MOCKUP_OUTBOX: mockupOutbox },
@@ -708,6 +714,10 @@ function openStructured({ id, model, resume, log, commands, mode }) {
708
714
  : `\n ${A.yel}● permission: ${req.toolName}${argStr(req.input)} — approve in the room.${A.rst}\n`)
709
715
  }),
710
716
  })
717
+ // Brand-new session (no restored log): write its record now so a restart that lands
718
+ // before the first debounced save still restores this id instead of dropping it (the
719
+ // room would otherwise show a fresh empty terminal + strand the transcript).
720
+ if (!entry.log.length) entry.flush()
711
721
  announce()
712
722
  if (!entry.log.length) process.stderr.write(`\n ◆ structured Claude session (${id.slice(0, 8)}) — driven from the room.\n`)
713
723
  return entry
@@ -1002,7 +1012,11 @@ channel
1002
1012
  // only runs on a fresh start (guard above), so a network re-subscribe
1003
1013
  // with live sessions won't re-spawn them.
1004
1014
  if (wantStructured(startCmd)) {
1005
- const all = loadAll(room).filter((r) => r.log?.length || r.sessionId)
1015
+ // Restore EVERY saved rec a freshly opened session (persisted on open) may
1016
+ // have an empty log + no sessionId yet and must still come back under its id.
1017
+ // Closed sessions are unlinked by deleteSession, so a file existing on disk ==
1018
+ // a session that was live at shutdown (never an explicitly closed one).
1019
+ const all = loadAll(room)
1006
1020
  if (all.length) for (const rec of all) openStructured({ id: rec.id, resume: canResume(rec) ? rec.sessionId : undefined, log: rec.log, commands: rec.commands, mode: rec.mode })
1007
1021
  // Fresh open: ONLY for an explicit `-- <claude>` share. In account mode
1008
1022
  // (autoAgent set, no attachedCmd) the web's `?new=1` flow opens the first
@@ -1171,6 +1185,10 @@ async function shutdown(code = 0, farewell = true) {
1171
1185
  // correct and must not hang on the (possibly dead) realtime socket. This is the
1172
1186
  // bug behind the watchdog/auto-update raw process.exit() paths, which skipped
1173
1187
  // teardown entirely and orphaned agent processes + left stale presence.
1188
+ // Flush structured session state synchronously BEFORE ending sessions. saveSession
1189
+ // debounces ~1.5s, so without this any events since the last write are lost on exit
1190
+ // (Contract #2). writeFileSync, well inside the 1500ms hard-exit backstop above.
1191
+ for (const s of sessions.values()) { try { s.flush?.() } catch { /* noop */ } }
1174
1192
  for (const t of terms.values()) { try { t.term.kill() } catch { /* noop */ } }
1175
1193
  for (const s of sessions.values()) { try { s.session.end() } catch { /* noop */ } }
1176
1194
  detachLocal()
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thinkpool-pair",
3
- "version": "0.7.33",
3
+ "version": "0.7.35",
4
4
  "description": "Share a local coding-agent CLI (Claude Code, Codex, Gemini, Aider, …) into a ThinkPool Code room, live.",
5
5
  "type": "module",
6
6
  "bin": {