thinkpool-pair 0.7.101 → 0.7.103

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
@@ -594,7 +594,7 @@ const announce = () =>
594
594
  // the chosen label instead of defaulting to Normal / unnamed. `spawned` (set
595
595
  // when an agent Dispatched this lane via spawn_terminal) lets the client keep
596
596
  // it OUT of the top tab strip and show it only in the Ensemble menu.
597
- ...[...sessions.entries()].map(([id, s]) => ({ id, cmd: s.cmd, kind: 'structured', alive: true, commands: s.commands, mode: s.mode || undefined, name: termNames[id] || undefined, ...(s.archiveOldestSeq != null ? { oldestSeq: s.archiveOldestSeq } : {}), ...(s.spawnedBy ? { spawned: true, spawnedBy: s.spawnedBy } : {}) })),
597
+ ...[...sessions.entries()].map(([id, s]) => ({ id, cmd: s.cmd, kind: 'structured', alive: true, commands: s.commands, mode: s.mode || undefined, name: termNames[id] || undefined, ...(s.archiveOldestSeq != null ? { oldestSeq: s.archiveOldestSeq } : {}), ...(s.spawnedBy ? { spawned: true, spawnedBy: s.spawnedBy } : {}), ...(s.flowSessionId ? { flowId: s.flowSessionId, flowRole: s.flowTaskKey ? 'lane' : 'conductor' } : {}) })),
598
598
  ],
599
599
  })
600
600
 
@@ -26,6 +26,8 @@ export { normalizePlanOutput }
26
26
  export const FLOW_CONDUCTOR_PROMPT = [
27
27
  'THINKPOOL FLOW CONDUCTOR — HARD RULES (read first, no exceptions): You are in PLAN MODE. Your ONLY output action is calling the ExitPlanMode tool EXACTLY ONCE with the task-graph JSON. You MUST NOT write or edit files, MUST NOT run bash/git, MUST NOT call AskUserQuestion, and MUST NOT build the project yourself — in plan mode those are blocked and will just stall you on a permission prompt. You do NOT build; you DECOMPOSE so the lanes build. A fully-specified build is STILL decomposed, never built here. If a detail is ambiguous, make a reasonable assumption and note it in the plan summary — do NOT ask. Think, then call ExitPlanMode. That is the whole job.',
28
28
 
29
+ 'DO NOT SPAWN SUBAGENTS. You MUST NOT use the Task/Agent tool or launch Explore / sub-agents — in plan mode every subagent spawn pops a permission card and stalls the whole flow (this is the #1 way the conductor hangs asking for permission). You almost never need to look at code to decompose: decompose from the build request itself. If you genuinely must inspect something first, read it YOURSELF with Read/Grep/Glob (those are allowed in plan mode without a prompt) — never delegate it to a spawned agent. Decompose, then ExitPlanMode.',
30
+
29
31
  'THINKPOOL FLOW — you are the CONDUCTOR of an ensemble build. Your job RIGHT NOW (Step 1) is to decompose the user\'s build request into a TASK-GRAPH: a set of RUNNABLE slices with explicit dependencies, then submit that plan for human approval. Do NOT start building and do NOT spawn lanes yet — that happens only after the user approves the plan (Step 2+).',
30
32
 
31
33
  'DECOMPOSE INTO RUNNABLE SLICES. Each slice must be something ONE lane can build, RUN in its own browser-side WebContainer sandbox, observe, and self-correct INDEPENDENTLY. A slice that cannot run on its own is not a slice — split it or merge it until it is. The runtime (a lane running what it built) is the moat; slices that can\'t run break the whole model.',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thinkpool-pair",
3
- "version": "0.7.101",
3
+ "version": "0.7.103",
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": {
package/service.mjs CHANGED
@@ -115,12 +115,25 @@ export function buildArtifact(platform, { room = null, cmdArgs = [], autoUpdate
115
115
  // retried on the EIO race → a final `print` so the command's exit code reports the
116
116
  // real loaded state (installService warns if it ends not-loaded).
117
117
  const lbl = label(room), dom = 'gui/$(id -u)'
118
+ const pr = `launchctl print ${dom}/${lbl} >/dev/null 2>&1`
118
119
  const post = [
119
- `launchctl bootout ${dom}/${lbl} 2>/dev/null`,
120
- `for i in $(seq 1 20); do launchctl print ${dom}/${lbl} >/dev/null 2>&1 || break; sleep 0.2; done`,
121
- `launchctl enable ${dom}/${lbl}`,
122
- `for i in $(seq 1 20); do launchctl bootstrap ${dom} "${file}" 2>/dev/null && break; sleep 0.3; done`,
123
- `launchctl print ${dom}/${lbl} >/dev/null 2>&1`,
120
+ // Evict any existing instance so the REWRITTEN plist (new pinned version) is what
121
+ // loads. bootout is async + occasionally sticky: if the old supervised process
122
+ // won't leave the domain, the follow-up bootstrap throws "Bootstrap failed: 5:
123
+ // Input/output error" and we'd SILENTLY keep running the OLD pinned version while
124
+ // reporting success — the 2026-06-30 "Restart & update" stuck-on-0.7.96 bug. So:
125
+ // bootout, wait for it to actually leave, and if it WON'T, SIGKILL the wedged pid
126
+ // then bootout again (KeepAlive can't relaunch a job that's booting out).
127
+ `launchctl bootout ${dom}/${lbl} 2>/dev/null || true`,
128
+ `for i in $(seq 1 25); do ${pr} || break; sleep 0.2; done`,
129
+ `if ${pr}; then p=$(launchctl print ${dom}/${lbl} 2>/dev/null | sed -n 's/.*[^a-z]pid = \\([0-9][0-9]*\\).*/\\1/p' | head -1); [ -n "$p" ] && kill -9 "$p" 2>/dev/null || true; launchctl bootout ${dom}/${lbl} 2>/dev/null || true; for i in $(seq 1 25); do ${pr} || break; sleep 0.2; done; fi`,
130
+ // Clear a stale `disabled` override (from a prior bootout / account-storm cleanup),
131
+ // then load the rewritten plist from disk, retried on the EIO teardown race.
132
+ `launchctl enable ${dom}/${lbl} 2>/dev/null || true`,
133
+ `ok=0; for i in $(seq 1 25); do launchctl bootstrap ${dom} "${file}" 2>/dev/null && { ok=1; break; }; sleep 0.3; done`,
134
+ // Be HONEST about the outcome: if it never loaded, say so + give the exact manual
135
+ // recovery — never let a failed reload masquerade as a successful update.
136
+ `if [ "$ok" != 1 ] && ! ${pr}; then echo " ⚠ thinkpool-pair: launchd reload FAILED — run by hand: launchctl bootout ${dom}/${lbl} ; launchctl bootstrap ${dom} \\"${file}\\"" >&2; fi`,
124
137
  ].join('; ')
125
138
  return { file, content, logDir, post: [post], note: 'Loaded as a LaunchAgent — starts at login, restarts on crash.' }
126
139
  }