thinkpool-pair 0.7.168 → 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.
- package/bridge.mjs +22 -2
- package/claude-session.mjs +18 -10
- package/package.json +2 -2
package/bridge.mjs
CHANGED
|
@@ -1510,7 +1510,16 @@ function openStructured({ id, model, resume, log, commands, mode, spawnedBy, rol
|
|
|
1510
1510
|
// spawnedBy: set when this lane was Dispatched (spawn_terminal). Restored from the
|
|
1511
1511
|
// session store so the Ensemble flag survives a bridge restart (else a respin
|
|
1512
1512
|
// stripped it and the lane reverted to a plain tab — the t6 "no chip" bug).
|
|
1513
|
-
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,
|
|
1513
|
+
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,
|
|
1514
|
+
// model: truthful active-model label. When this lane runs on a custom (non-anthropic)
|
|
1515
|
+
// registered provider the SDK id is impersonated (see the onEvent guard below), so seed
|
|
1516
|
+
// from THAT provider's CONFIGURED model — glm-5.2, not a Claude label — before any turn.
|
|
1517
|
+
// No configured model → the provider's name, never a false Claude id. Built-in Anthropic
|
|
1518
|
+
// keeps the passed model. Keyed on the lane's own provider id, not the bridge default.
|
|
1519
|
+
model: (!provider || provider === 'anthropic')
|
|
1520
|
+
? (model || null)
|
|
1521
|
+
: (resolveProviderEnv(provider)?.ANTHROPIC_MODEL || providerNameMap()[provider] || model || provider),
|
|
1522
|
+
provider: provider || null, spawnedBy: spawnedBy || undefined, flowSessionId: flowSessionId || null, flowTaskKey: flowTaskKey || null, cwd: cwd || null,
|
|
1514
1523
|
// Stable creation order — persisted so a bridge restart restores tabs in the SAME
|
|
1515
1524
|
// order (not readdir/filesystem order). Legacy recs (no openedAt) derive it from the
|
|
1516
1525
|
// first transcript event ts, so even the first post-fix restart is ordered right.
|
|
@@ -2108,7 +2117,18 @@ function openStructured({ id, model, resume, log, commands, mode, spawnedBy, rol
|
|
|
2108
2117
|
// LIST (from the `models` event) so the announce carries them to EVERY device.
|
|
2109
2118
|
// Re-announce on change so a switch / late device sees it without a turn (the
|
|
2110
2119
|
// old localStorage cache was per-device; branch showed on every other device).
|
|
2111
|
-
|
|
2120
|
+
// When THIS lane runs on a NON-anthropic provider (a registered GLM/Z.ai,
|
|
2121
|
+
// OpenRouter, any Anthropic-compatible proxy — entry.provider is a custom
|
|
2122
|
+
// registry id) the SDK reads the endpoint's response metadata, which IMPERSONATES
|
|
2123
|
+
// a Claude model id (z.ai reports "claude-sonnet-…" while actually serving glm-5.2).
|
|
2124
|
+
// Trusting evt.model made the chip lie "Sonnet 5" on a GLM lane. The truthful label
|
|
2125
|
+
// is the configured provider model (seeded at openStructured) or an explicit /model
|
|
2126
|
+
// pick, both of which live on entry.model already — so DON'T let the impersonated
|
|
2127
|
+
// SDK id overwrite it. Only trust the SDK's model on the built-in Anthropic path.
|
|
2128
|
+
// Keyed on the LANE's provider (entry.provider), not the bridge default — lanes can
|
|
2129
|
+
// run on different registered providers. (bridge-truth slice.)
|
|
2130
|
+
{ const onBuiltin = !entry.provider || entry.provider === 'anthropic'
|
|
2131
|
+
const m = onBuiltin ? (evt.model || evt.ctx?.model) : null
|
|
2112
2132
|
if (m && m !== entry.model) { entry.model = m; announce() }
|
|
2113
2133
|
if (evt.kind === 'models' && Array.isArray(evt.models) && evt.models.length) { roomModels = evt.models; announce() } }
|
|
2114
2134
|
// FL-B2 — fold this flow lane's completed-turn output tokens into its budget so the
|
package/claude-session.mjs
CHANGED
|
@@ -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
|
-
//
|
|
664
|
-
//
|
|
665
|
-
//
|
|
666
|
-
//
|
|
667
|
-
//
|
|
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
|
|
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,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "thinkpool-pair",
|
|
3
|
-
"version": "0.7.
|
|
4
|
-
"description": "Share a local coding-agent CLI (Claude Code, Codex, Gemini, Aider,
|
|
3
|
+
"version": "0.7.170",
|
|
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": {
|
|
7
7
|
"thinkpool-pair": "bridge.mjs"
|