thinkpool-pair 0.7.237 → 0.7.238
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 +6 -6
- package/package.json +1 -1
- package/runtime-registry.mjs +10 -1
package/bridge.mjs
CHANGED
|
@@ -51,7 +51,7 @@ import { createSdkMcpServer, tool } from '@anthropic-ai/claude-agent-sdk'
|
|
|
51
51
|
import { z } from 'zod'
|
|
52
52
|
import { readCodexDefaultModel, readCodexModels, codexConfigForMode, codexThreadCanResume } from './codex-session.mjs'
|
|
53
53
|
import { startStructuredSession } from './runtime-session.mjs'
|
|
54
|
-
import { defaultStructuredMode, structuredRuntimeForCommand, structuredRuntimeMetadata, structuredRuntimeSupportsMode } from './runtime-registry.mjs'
|
|
54
|
+
import { defaultStructuredMode, shouldDeferStructuredRuntime, structuredRuntimeForCommand, structuredRuntimeMetadata, structuredRuntimeSupportsMode } from './runtime-registry.mjs'
|
|
55
55
|
import { probeHermesRuntime } from './hermes-probe.mjs'
|
|
56
56
|
import { canonicalRoomFilePath, waitForNativeImages } from './codex-images.mjs'
|
|
57
57
|
import { createManagedLaneWorktree, removeManagedLaneWorktree } from './lane-worktree.mjs'
|
|
@@ -1694,7 +1694,7 @@ function worktreeSnapshot(cwd) {
|
|
|
1694
1694
|
// relay STRUCTURED events. onEvent → broadcast `code-event` + print locally +
|
|
1695
1695
|
// persist to the host file; tool calls round-trip through the perm card; the
|
|
1696
1696
|
// rolling log replays to joiners and survives bridge restarts (session-store).
|
|
1697
|
-
function openStructured({ id, runtime = 'claude', model, effort, resume, log, commands, mode, spawnedBy, spawnDepth, cascadeRole, hop, sideParent, sideTask, pendingSideContexts, rolePrompt, flowSessionId, flowTaskKey, flowRole, flowReviewTarget, flowReviewTargets, flowReviewRound, dispatchBaseSha, revertTarget, cwd, managedWorktree, reviewSliceRoots, openedAt, defer, provider, carryRecap, lastUsage }) {
|
|
1697
|
+
function openStructured({ id, runtime = 'claude', model, models, effort, resume, log, commands, mode, spawnedBy, spawnDepth, cascadeRole, hop, sideParent, sideTask, pendingSideContexts, rolePrompt, flowSessionId, flowTaskKey, flowRole, flowReviewTarget, flowReviewTargets, flowReviewRound, dispatchBaseSha, revertTarget, cwd, managedWorktree, reviewSliceRoots, openedAt, defer, provider, carryRecap, lastUsage }) {
|
|
1698
1698
|
if (sessions.has(id)) return
|
|
1699
1699
|
runtime = structuredRuntimeMetadata(runtime) ? runtime : 'claude'
|
|
1700
1700
|
// No explicit mode → a sensible default per runtime (see defaultModeForRuntime):
|
|
@@ -1733,7 +1733,7 @@ function openStructured({ id, runtime = 'claude', model, effort, resume, log, co
|
|
|
1733
1733
|
? spawnDepth
|
|
1734
1734
|
: (sideParent || (spawnedBy && !String(spawnedBy).startsWith('flow:')) ? 1 : 0)
|
|
1735
1735
|
const initialHop = Number.isInteger(hop) && hop >= 0 ? hop : structuralDepth
|
|
1736
|
-
const entry = { cmd: runtime, runtime, 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, effort, models: runtime === 'codex' ? codexModels : undefined,
|
|
1736
|
+
const entry = { cmd: runtime, runtime, 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, effort, models: runtime === 'codex' ? codexModels : runtime === 'hermes' && Array.isArray(models) ? models : undefined,
|
|
1737
1737
|
// model: truthful active-model label — now the SAME `laneModel` the SDK is given, so the
|
|
1738
1738
|
// chip cannot disagree with the wire. When this lane runs on a custom (non-anthropic)
|
|
1739
1739
|
// registered provider the SDK id is impersonated (see the onEvent guard below), so
|
|
@@ -1996,7 +1996,7 @@ function openStructured({ id, runtime = 'claude', model, effort, resume, log, co
|
|
|
1996
1996
|
// restart. Without this, sessionData omitted it → on restart the resumed session
|
|
1997
1997
|
// re-launched on the host default (Opus) regardless of the last switch, and the
|
|
1998
1998
|
// switch looked like it "never changed the model" (Max 2026-07-02). Restored below.
|
|
1999
|
-
const sessionData = () => ({ sessionId: entry.session?.sessionId || null, runtime: entry.runtime, log: entry.log, commands: entry.commands, mode: entry.mode, effort: entry.effort, model: entry.model || null, provider: entry.provider || null, spawnedBy: entry.spawnedBy, spawnDepth: entry.spawnDepth || 0, cascadeRole: entry.cascadeRole || null, hop: entry.hop || 0, sideParent: entry.sideParent, sideTask: entry.sideTask, pendingSideContexts: entry.pendingSideContexts, flowSessionId: entry.flowSessionId, flowTaskKey: entry.flowTaskKey, flowRole: entry.flowRole || null, flowReviewTargets: entry.flowReviewTargets || [], flowReviewRound: entry.flowReviewRound || 0, dispatchBaseSha: entry.dispatchBaseSha || null, cwd: entry.cwd, managedWorktree: entry.managedWorktree, rolePrompt: entry.rolePrompt, flowReviewTarget: entry.flowReviewTarget || null, revertTarget: entry.revertTarget || null, reviewSliceRoots: entry.reviewSliceRoots || [], openedAt: entry.openedAt || null, lastUsage: entry.lastUsage || null, carryRecap: entry.pendingRecap || null })
|
|
1999
|
+
const sessionData = () => ({ sessionId: entry.session?.sessionId || null, runtime: entry.runtime, log: entry.log, commands: entry.commands, mode: entry.mode, effort: entry.effort, model: entry.model || null, models: entry.runtime === 'hermes' ? (entry.models || []) : undefined, provider: entry.provider || null, spawnedBy: entry.spawnedBy, spawnDepth: entry.spawnDepth || 0, cascadeRole: entry.cascadeRole || null, hop: entry.hop || 0, sideParent: entry.sideParent, sideTask: entry.sideTask, pendingSideContexts: entry.pendingSideContexts, flowSessionId: entry.flowSessionId, flowTaskKey: entry.flowTaskKey, flowRole: entry.flowRole || null, flowReviewTargets: entry.flowReviewTargets || [], flowReviewRound: entry.flowReviewRound || 0, dispatchBaseSha: entry.dispatchBaseSha || null, cwd: entry.cwd, managedWorktree: entry.managedWorktree, rolePrompt: entry.rolePrompt, flowReviewTarget: entry.flowReviewTarget || null, revertTarget: entry.revertTarget || null, reviewSliceRoots: entry.reviewSliceRoots || [], openedAt: entry.openedAt || null, lastUsage: entry.lastUsage || null, carryRecap: entry.pendingRecap || null })
|
|
2000
2000
|
const persist = () => saveSession(room, id, sessionData())
|
|
2001
2001
|
// Synchronous flush of this session's record. Used on open (so a brand-new session
|
|
2002
2002
|
// has a file under its id BEFORE its first event — surviving a restart inside the
|
|
@@ -2486,7 +2486,7 @@ function openStructured({ id, runtime = 'claude', model, effort, resume, log, co
|
|
|
2486
2486
|
},
|
|
2487
2487
|
// defer: a restored-idle terminal cold-boots its query only on first use — restore is
|
|
2488
2488
|
// then instant for display, and only used terminals pay the ~50s MCP boot (Max, 2026-07-02).
|
|
2489
|
-
lazy:
|
|
2489
|
+
lazy: shouldDeferStructuredRuntime({ runtime, defer, cwd, flowSessionId, flowTaskKey, models: entry.models }),
|
|
2490
2490
|
reviewGate,
|
|
2491
2491
|
// A Flow CONDUCTOR (flowSessionId set, no flowTaskKey) must DECOMPOSE, not explore via
|
|
2492
2492
|
// a fan-out of Task/Explore subagents, and must not build. Hard-block Task/Bash/Write for
|
|
@@ -3808,7 +3808,7 @@ channel
|
|
|
3808
3808
|
// FL-M6 — restore the flow context (id/role/cwd) so an in-flight flow survives a
|
|
3809
3809
|
// bridge restart: the conductor keeps its subagent-block + plan interception, and
|
|
3810
3810
|
// lanes keep their worktree cwd + the ability to mark done.
|
|
3811
|
-
openStructured({ id: rec.id, runtime: rec.runtime || 'claude', model: rec.model || undefined, effort: rec.effort, provider: rec.runtime === 'claude' ? rec.provider || undefined : undefined, resume: resumable ? rec.sessionId : undefined, log: rec.log, commands: rec.commands, mode: rec.mode, spawnedBy: rec.spawnedBy, spawnDepth: rec.spawnDepth, cascadeRole: rec.cascadeRole, hop: rec.hop, sideParent: rec.sideParent, sideTask: rec.sideTask, pendingSideContexts: rec.pendingSideContexts, flowSessionId: rec.flowSessionId, flowTaskKey: rec.flowTaskKey, flowRole: rec.flowRole, flowReviewTarget: rec.flowReviewTarget, flowReviewTargets: rec.flowReviewTargets, flowReviewRound: rec.flowReviewRound, dispatchBaseSha: rec.dispatchBaseSha, revertTarget: rec.revertTarget, cwd: rec.cwd, managedWorktree: rec.managedWorktree, rolePrompt: rec.rolePrompt, reviewSliceRoots: rec.reviewSliceRoots, openedAt: rec.openedAt, lastUsage: rec.lastUsage, carryRecap: wasInterrupted ? recoveryRecap : rec.carryRecap,
|
|
3811
|
+
openStructured({ id: rec.id, runtime: rec.runtime || 'claude', model: rec.model || undefined, models: rec.models, effort: rec.effort, provider: rec.runtime === 'claude' ? rec.provider || undefined : undefined, resume: resumable ? rec.sessionId : undefined, log: rec.log, commands: rec.commands, mode: rec.mode, spawnedBy: rec.spawnedBy, spawnDepth: rec.spawnDepth, cascadeRole: rec.cascadeRole, hop: rec.hop, sideParent: rec.sideParent, sideTask: rec.sideTask, pendingSideContexts: rec.pendingSideContexts, flowSessionId: rec.flowSessionId, flowTaskKey: rec.flowTaskKey, flowRole: rec.flowRole, flowReviewTarget: rec.flowReviewTarget, flowReviewTargets: rec.flowReviewTargets, flowReviewRound: rec.flowReviewRound, dispatchBaseSha: rec.dispatchBaseSha, revertTarget: rec.revertTarget, cwd: rec.cwd, managedWorktree: rec.managedWorktree, rolePrompt: rec.rolePrompt, reviewSliceRoots: rec.reviewSliceRoots, openedAt: rec.openedAt, lastUsage: rec.lastUsage, carryRecap: wasInterrupted ? recoveryRecap : rec.carryRecap,
|
|
3812
3812
|
// Lazy-boot restored terminals that were IDLE + not part of a flow: their transcript
|
|
3813
3813
|
// shows immediately; the query boots on first turn. Mid-turn + flow terminals boot now
|
|
3814
3814
|
// (mid-turn needs auto-resume; flow needs its lane live).
|
package/package.json
CHANGED
package/runtime-registry.mjs
CHANGED
|
@@ -13,7 +13,7 @@ const RUNTIMES = Object.freeze({
|
|
|
13
13
|
}),
|
|
14
14
|
hermes: Object.freeze({
|
|
15
15
|
id: 'hermes', command: 'thinkpool', label: 'Hermes Agent', protocol: 'acp',
|
|
16
|
-
structured: true, flow: false, canSteer: true, images: true, nativeModelCatalog: true, effortControl: false, defaultMode: 'default',
|
|
16
|
+
structured: true, flow: false, canSteer: true, images: true, nativeModelCatalog: true, catalogRequiresSession: true, effortControl: false, defaultMode: 'default',
|
|
17
17
|
modes: Object.freeze(['default', 'acceptEdits']),
|
|
18
18
|
beta: true,
|
|
19
19
|
}),
|
|
@@ -28,3 +28,12 @@ export const structuredRuntimeForCommand = (command) => {
|
|
|
28
28
|
export const defaultStructuredMode = (runtime) => structuredRuntimeMetadata(runtime)?.defaultMode || 'default'
|
|
29
29
|
export const structuredRuntimeSupportsMode = (runtime, mode) => structuredRuntimeMetadata(runtime)?.modes?.includes(mode) === true
|
|
30
30
|
export const structuredRuntimeSupportsFlow = (runtime) => structuredRuntimeMetadata(runtime)?.flow === true
|
|
31
|
+
export const shouldDeferStructuredRuntime = ({ runtime, defer, cwd, flowSessionId, flowTaskKey, models } = {}) => {
|
|
32
|
+
const metadata = structuredRuntimeMetadata(runtime)
|
|
33
|
+
// Some ACP agents publish their model catalog only from session/new or
|
|
34
|
+
// session/resume. A brand-new lane must perform that zero-inference handshake
|
|
35
|
+
// before its first prompt or the model picker has no truthful choices to show.
|
|
36
|
+
// Once the exact catalog is persisted, the normal lazy-start policy resumes.
|
|
37
|
+
if (metadata?.catalogRequiresSession && !(Array.isArray(models) && models.length)) return false
|
|
38
|
+
return !!defer || (!cwd && !flowSessionId && !flowTaskKey)
|
|
39
|
+
}
|