thinkpool-pair 0.7.169 → 0.7.170

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/claude-session.mjs +18 -10
  2. package/package.json +1 -1
@@ -660,15 +660,11 @@ export function startClaudeSession({ cwd, model, resume, env, mode: initialMode
660
660
  // documented override. opts.env REPLACES the child env, so spread the inherited one.
661
661
  opts.env = { ...(env || process.env), CLAUDE_CODE_ENABLE_PROMPT_SUGGESTION: '1' }
662
662
 
663
- // Before the SDK replays this transcript, heal any cross-provider tool blocks
664
- // it left behind (e.g. a room that ran on Z.ai GLM, now back on anthropic).
665
- // Anthropic 400s on a `server_tool_use` block carrying a foreign id/name, which
666
- // wedges the room on every send. Reclassifying those to plain `tool_use` clears
667
- // it; idempotent + no-op on clean transcripts. See transcript-sanitize.mjs.
668
- if (resume) {
669
- const healed = sanitizeSession(cwd || process.cwd(), resume)
670
- if (healed.blocks) process.stderr.write(`\n ◆ healed ${healed.blocks} malformed tool block(s) in the transcript before resume.\n`)
671
- }
663
+ // Transcript healing lives at the TRUE resume point — see runQuery, right before
664
+ // query() is (re)built. The old factory-time heal was gated on the closure constant
665
+ // `resume`, so it ran only on the first open; a `/model` switch or a restart re-points
666
+ // the live `sessionId` and re-enters runQuery, resuming a transcript that was never
667
+ // re-healed (finding FBL-1). Healing inside runQuery covers EVERY resume path.
672
668
 
673
669
  // One-shot Haiku fallback suggestion. Runs on the SAME subscription auth as the room
674
670
  // (opts.env carries the OAuth — no API key needed), but as a BARE model call: no
@@ -719,6 +715,18 @@ export function startClaudeSession({ cwd, model, resume, env, mode: initialMode
719
715
  const onParentAbort = () => { try { myAc.abort() } catch { /* noop */ } }
720
716
  ac.signal.addEventListener('abort', onParentAbort, { once: true })
721
717
  try {
718
+ // Heal the transcript we're about to REPLAY — at the true resume point, keyed on the
719
+ // LIVE `sessionId` (not the factory-time `resume` constant). `sessionId` mutates:
720
+ // recreateForSwitch re-points it to persistedSessionId and the restart / bad-resume
721
+ // forks re-point it too, each re-entering runQuery. A `/model` switch (or restart) that
722
+ // resumed a corrupted transcript — a dangling tool_use / tool_result-in-assistant left
723
+ // by an aborted mid-tool turn — used to hit an Anthropic 400 (H42 class) and wedge the
724
+ // lane on every send (finding FBL-1). sanitizeSession is idempotent + a no-op on clean
725
+ // transcripts, so healing before EVERY resume is free. See transcript-sanitize.mjs.
726
+ if (sessionId) {
727
+ const healed = sanitizeSession(cwd || process.cwd(), sessionId)
728
+ if (healed.blocks) process.stderr.write(`\n ◆ healed ${healed.blocks} malformed tool block(s) in the transcript before resume.\n`)
729
+ }
722
730
  q = query({ prompt: input.stream, options: { ...opts, abortController: myAc, ...(sessionId ? { resume: sessionId } : {}) } })
723
731
  for await (const m of q) {
724
732
  if (closed) break
@@ -756,7 +764,7 @@ export function startClaudeSession({ cwd, model, resume, env, mode: initialMode
756
764
  // Cold-start timing — the init system message is when the terminal is
757
765
  // usable, so spawn→here IS the latency the user waits on. One line per
758
766
  // open. Compare a normal open vs one with TP_MCP_STRICT=1 to read off how
759
- // many ms the host MCP servers cost. (resume opens also pay sanitizeSession.)
767
+ // many ms the host MCP servers cost. (each resume also pays a sanitizeSession heal — see runQuery.)
760
768
  if (!readyLogged) {
761
769
  readyLogged = true
762
770
  process.stderr.write(`\n ◆ session ready in ${Date.now() - spawnT0}ms — MCP ${process.env.TP_MCP_STRICT === '1' ? 'OFF' : 'on'}${resume ? ', resume' : ', fresh'}.\n`)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thinkpool-pair",
3
- "version": "0.7.169",
3
+ "version": "0.7.170",
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": {