thinkpool-pair 0.7.120 → 0.7.122

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 CHANGED
@@ -1178,7 +1178,7 @@ function openStructured({ id, model, resume, log, commands, mode, spawnedBy, rol
1178
1178
  // spawnedBy: set when this lane was Dispatched (spawn_terminal). Restored from the
1179
1179
  // session store so the Ensemble flag survives a bridge restart (else a respin
1180
1180
  // stripped it and the lane reverted to a plain tab — the t6 "no chip" bug).
1181
- const entry = { cmd: 'claude', kind: 'structured', log: Array.isArray(log) ? log.slice(-STRUCTURED_LOG_MAX) : [], pending: new Map(), session: null, recovered: false, commands: Array.isArray(commands) ? commands : undefined, mode, spawnedBy: spawnedBy || undefined, flowSessionId: flowSessionId || null, flowTaskKey: flowTaskKey || null, cwd: cwd || null }
1181
+ const entry = { cmd: 'claude', kind: 'structured', log: Array.isArray(log) ? log.slice(-STRUCTURED_LOG_MAX) : [], pending: new Map(), session: null, recovered: false, commands: Array.isArray(commands) ? commands : undefined, mode, model: model || null, spawnedBy: spawnedBy || undefined, flowSessionId: flowSessionId || null, flowTaskKey: flowTaskKey || null, cwd: cwd || null }
1182
1182
  // C1 (RT-2): per-term contiguous seq counter. Seeded from the restored log's max
1183
1183
  // so a bridge restart resumes ABOVE every persisted seq — fresh events never reuse
1184
1184
  // an old seq, which is what keeps the between-turns restart dup-free (see
@@ -1671,12 +1671,20 @@ function openStructured({ id, model, resume, log, commands, mode, spawnedBy, rol
1671
1671
  // 'suggestion' is Claude Code's predicted-next-prompt ghost text — ephemeral
1672
1672
  // composer UI, broadcast to both clients but never logged/persisted/replayed.
1673
1673
  const chrome = evt.kind === 'mode' || evt.kind === 'usage' || evt.kind === 'clear' || evt.kind === 'compact' || evt.kind === 'stalled' || evt.kind === 'suggestion'
1674
- // compact turn finished (or errored) → clear the pulsing indicator.
1675
- // No "done" ctl line — the SDK's own output in the transcript is the
1676
- // signal (compact summary on success, AbortError text on abort/too-small).
1674
+ // compact turn finished (or errored) → clear the pulsing indicator AND resolve the
1675
+ // "◈ compacting context…" line so it doesn't sit there forever looking stuck (Max asked
1676
+ // "is it compacted?" — 2026-07-02). On success post an explicit "◈ context compacted"
1677
+ // control line, paired with the start line; on error the SDK's AbortError/too-small
1678
+ // text is the signal, so no line. Same control-event shape as ctlLine (kind:'control').
1677
1679
  if ((evt.kind === 'result' || evt.kind === 'error') && entry.compacting) {
1678
1680
  entry.compacting = false
1679
1681
  bcast('code-event', { term: id, evt: { kind: 'compact', status: 'done', ts: Date.now() } })
1682
+ if (evt.kind === 'result') {
1683
+ const doneEvt = { kind: 'control', text: '◈ context compacted', by: entry.compactBy || null }
1684
+ pushLog(entry, doneEvt)
1685
+ bcast('code-event', { term: id, evt: doneEvt })
1686
+ }
1687
+ entry.compactBy = null
1680
1688
  }
1681
1689
  // Single tail: stamp into the log (transcript kinds only), broadcast live,
1682
1690
  // print locally, persist. Shared so a deferred image event re-enters it once
@@ -1903,8 +1911,12 @@ channel
1903
1911
  if (agent?.resume) { try { if (agent.resume.probe()) args = [...agent.resume.args] } catch { /* fresh */ } }
1904
1912
  }
1905
1913
  if (wantStructured(payload.cmd)) {
1906
- openStructured({ id: payload.id, mode: payload.mode })
1907
- process.stderr.write(`\n ◆ web opened a structured "${payload.cmd}" session (${payload.mode || 'default'}).\n`)
1914
+ // model: the web passes the model to open on — a NEW terminal inherits the
1915
+ // previous terminal's model (and the first uses the provider default). Falls
1916
+ // through to the SDK default when absent. Stamped on the entry so the announce
1917
+ // carries it at open (chip shows the model immediately, not the branch).
1918
+ openStructured({ id: payload.id, mode: payload.mode, model: payload.model || undefined })
1919
+ process.stderr.write(`\n ◆ web opened a structured "${payload.cmd}" session (${payload.mode || 'default'}${payload.model ? `, model ${payload.model}` : ''}).\n`)
1908
1920
  return
1909
1921
  }
1910
1922
  openTerm({ id: payload.id, cmd: payload.cmd, args })
@@ -656,6 +656,16 @@ export function startClaudeSession({ cwd, model, resume, env, mode: initialMode
656
656
  // count before compaction. Emit a real recap card the room can pin.
657
657
  if (m.subtype === 'compact_boundary') {
658
658
  emit({ kind: 'compaction', trigger: m.compact_metadata?.trigger || 'auto', preTokens: m.compact_metadata?.pre_tokens ?? null })
659
+ // Refresh the ctx% meter RIGHT AFTER compaction — it otherwise only updates at
660
+ // turn-end, so the mode row kept showing the stale PRE-compaction window
661
+ // ("ctx 90% even though we just compacted", Max 2026-07-02). Re-query the live
662
+ // context usage so the meter drops to the compacted size immediately.
663
+ ;(async () => {
664
+ try {
665
+ const c = await q?.getContextUsage?.()
666
+ if (c) { emit({ kind: 'usage', ctx: { used: c.totalTokens, max: c.maxTokens, pct: Math.round(c.percentage), model: c.model } }); if (c.model) curModel = c.model }
667
+ } catch { /* control req may be unavailable */ }
668
+ })()
659
669
  break
660
670
  }
661
671
  if (m.session_id) sessionId = m.session_id
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thinkpool-pair",
3
- "version": "0.7.120",
3
+ "version": "0.7.122",
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": {