thinkpool-pair 0.6.4 → 0.6.5

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
@@ -444,17 +444,29 @@ channel
444
444
  })
445
445
  .on('broadcast', { event: 'code-turn' }, ({ payload }) => {
446
446
  const s = payload?.term && sessions.get(payload.term)
447
- if (s && payload.text != null) {
448
- s.session.sendTurn(payload.text)
449
- // Echo the turn so BOTH readers (and late joiners, via the log) show who
450
- // said what — UNLESS silent (an @pool synthesis, which renders as its own
451
- // 'pool' line). The sender rendered it optimistically; partner gets this.
452
- if (!payload.silent) {
453
- const evt = { kind: 'you', text: payload.text, cid: payload.cid, by: payload.by }
454
- s.log.push(evt); if (s.log.length > STRUCTURED_LOG_MAX) s.log.shift()
455
- bcast('code-event', { term: payload.term, evt })
456
- }
447
+ if (!s || payload.text == null) return
448
+ const text = String(payload.text)
449
+ // Echo the turn so BOTH readers (and late joiners, via the log) show who
450
+ // said what — UNLESS silent (an @pool synthesis, which renders its own line).
451
+ const echoYou = () => {
452
+ if (payload.silent) return
453
+ const evt = { kind: 'you', text, cid: payload.cid, by: payload.by }
454
+ s.log.push(evt); if (s.log.length > STRUCTURED_LOG_MAX) s.log.shift()
455
+ bcast('code-event', { term: payload.term, evt })
456
+ }
457
+ // Most slash commands (/compact, /clear, …) are processed by the SDK when
458
+ // sent as a turn. Two need special handling:
459
+ // • /model <name> is disabled headless → route to the setModel control.
460
+ // • /clear also wipes the on-screen transcript (parity with the CLI).
461
+ const mm = text.match(/^\/model\b\s*(\S+)?/)
462
+ if (mm) { echoYou(); if (mm[1]) s.session.setModel(mm[1]); else s.session.listModels(); return }
463
+ if (/^\/clear\s*$/.test(text)) {
464
+ echoYou(); s.session.sendTurn(text)
465
+ s.log = []; bcast('code-event', { term: payload.term, evt: { kind: 'clear' } })
466
+ return
457
467
  }
468
+ s.session.sendTurn(text)
469
+ echoYou()
458
470
  })
459
471
  .on('broadcast', { event: 'code-perm' }, ({ payload }) => {
460
472
  const s = payload?.term && sessions.get(payload.term)
@@ -199,6 +199,19 @@ export function startClaudeSession({ cwd, model, resume, onEvent, requestPermiss
199
199
  try { await q?.setPermissionMode?.(m) } catch { /* only valid mid-stream */ }
200
200
  emit({ kind: 'mode', mode })
201
201
  },
202
+ // /model is disabled in headless SDK — switch via the setModel control
203
+ // instead, and echo a note line so both drivers see the change.
204
+ async setModel(m) {
205
+ try { await q?.setModel?.(m) } catch { /* may reject unknown model */ }
206
+ emit({ kind: 'note', text: `model → ${m}` })
207
+ },
208
+ async listModels() {
209
+ try {
210
+ const ms = await q?.supportedModels?.()
211
+ const names = (ms || []).map((x) => x.model || x.id || x.name).filter(Boolean)
212
+ emit({ kind: 'note', text: names.length ? `models: ${names.join(', ')}` : 'no model list available' })
213
+ } catch { emit({ kind: 'note', text: 'usage: /model <name>' }) }
214
+ },
202
215
  // Graceful interrupt (Esc / Stop) — stops the current turn but keeps the
203
216
  // session alive for the next one. ac.abort() is teardown only (end()).
204
217
  async abort() { try { await q?.interrupt?.() } catch { /* noop */ } },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thinkpool-pair",
3
- "version": "0.6.4",
3
+ "version": "0.6.5",
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": {