thinkpool-pair 0.7.104 → 0.7.105

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
@@ -1322,6 +1322,11 @@ function openStructured({ id, model, resume, log, commands, mode, spawnedBy, rol
1322
1322
  })
1323
1323
  entry.session = startClaudeSession({
1324
1324
  cwd: cwd || process.cwd(), model, resume, mode, rolePrompt,
1325
+ // A Flow CONDUCTOR (flowSessionId set, no flowTaskKey) must DECOMPOSE, not explore via
1326
+ // a fan-out of Task/Explore subagents — those raise permission cards in plan mode (which
1327
+ // now land on the hidden conductor terminal) and the model ignores the prompt rule not
1328
+ // to. Hard-block the Task tool for conductors so it reads with Read/Grep/Glob + ExitPlanMode.
1329
+ blockSubagents: !!flowSessionId && !flowTaskKey,
1325
1330
  mcpServers: { thinkpool: peekServer },
1326
1331
  // Tier C precheck — the PreToolUse gate calls this BEFORE raising a card, so a
1327
1332
  // disabled/looping/over-cap post never bothers a person. Closes over `entry`.
@@ -120,7 +120,7 @@ const simplifyBlocks = (blocks = []) => blocks.map((b) => {
120
120
  */
121
121
  const MODES = new Set(['default', 'acceptEdits', 'plan', 'bypassPermissions'])
122
122
 
123
- export function startClaudeSession({ cwd, model, resume, env, mode: initialMode = 'default', onEvent, requestPermission, mcpServers, crossPostGate, crossRoomPostGate, rolePrompt }) {
123
+ export function startClaudeSession({ cwd, model, resume, env, mode: initialMode = 'default', onEvent, requestPermission, mcpServers, crossPostGate, crossRoomPostGate, rolePrompt, blockSubagents = false }) {
124
124
  const ac = new AbortController()
125
125
  let input = makeInputStream() // `let`: auto-restart swaps in a fresh stream
126
126
  let sessionId = resume || null
@@ -248,6 +248,15 @@ export function startClaudeSession({ cwd, model, resume, env, mode: initialMode
248
248
  // Surface it to the room when it changes; absent on models without effort.
249
249
  const lvl = hookInput.effort?.level
250
250
  if (lvl && lvl !== effort) { effort = lvl; emit({ kind: 'effort', level: effort }) }
251
+ // Flow CONDUCTOR hard-stop: a conductor must DECOMPOSE, never fan out Task/Explore
252
+ // subagents. The prompt asks it not to, but the model's parallelize instinct wins and
253
+ // it spawns Explore agents anyway — which (in plan mode) raise permission cards on the
254
+ // now-hidden conductor terminal and hang the whole flow. Deny the Task tool outright and
255
+ // tell it to read with Read/Grep/Glob + ExitPlanMode. (blockSubagents is set only for
256
+ // the conductor session — lanes are governed by their own one-hop prompt rule.)
257
+ if (blockSubagents && (toolName === 'Task' || toolName === 'Agent')) {
258
+ return { continue: true, hookSpecificOutput: { hookEventName: 'PreToolUse', permissionDecision: 'deny', permissionDecisionReason: 'Flow conductors do NOT spawn subagents — it stalls the flow on a permission card. If you must inspect the codebase, read it yourself with Read/Grep/Glob (allowed here, no prompt). Then DECOMPOSE and call ExitPlanMode once. Do not call Task/Agent again.' } }
259
+ }
251
260
  // ThinkPool cross-terminal READ (B2) is read-only + within-room — never
252
261
  // prompt, in any mode. It still surfaces as a tool card so the room sees the peek.
253
262
  if (toolName === 'mcp__thinkpool__read_terminal') {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thinkpool-pair",
3
- "version": "0.7.104",
3
+ "version": "0.7.105",
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": {