thinkpool-pair 0.7.191 → 0.7.192
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 +3 -3
- package/codex-session.mjs +10 -11
- package/package.json +1 -1
package/bridge.mjs
CHANGED
|
@@ -50,7 +50,7 @@ import { validateProviderSwitch, providerSwitchPlan, BUILTIN_PROVIDER } from './
|
|
|
50
50
|
import { createSdkMcpServer, tool } from '@anthropic-ai/claude-agent-sdk'
|
|
51
51
|
import { z } from 'zod'
|
|
52
52
|
import { startClaudeSession } from './claude-session.mjs'
|
|
53
|
-
import { startCodexSession, readCodexDefaultModel } from './codex-session.mjs'
|
|
53
|
+
import { startCodexSession, readCodexDefaultModel, readCodexModels } from './codex-session.mjs'
|
|
54
54
|
import { FLOW_CONDUCTOR_PROMPT, FLOW_LANE_PROMPT, buildConductorEnv, assembleCrossWaveContext, buildLanePrompt } from './flow-conductor.mjs'
|
|
55
55
|
import { normalizePlanOutput, laneModelFor } from './flow-task-graph.mjs' // FL-B1 — validate the conductor's submit_flow_plan task-graph; laneModelFor — per-slice model tier (2026-07-03-flow-lane-model-tiers)
|
|
56
56
|
// S1 (context-offload) — durable digest store. mark_flow_done digests a closed slice in;
|
|
@@ -1070,7 +1070,7 @@ const announce = () => {
|
|
|
1070
1070
|
// turnActive). Authoritative — the bridge owns the turn — so the /code
|
|
1071
1071
|
// dashboard reads "N agents working" off the roster instead of sniffing
|
|
1072
1072
|
// the live code-event stream (which a passive dashboard channel misses).
|
|
1073
|
-
...[...sessions.entries()].map(([id, s]) => ({ id, cmd: s.cmd, kind: 'structured', runtime: s.runtime || 'claude', alive: true, busy: s.session?.turnActive ?? false, commands: s.commands, mode: s.mode || undefined, name: termNames[id] || undefined, model: s.model || undefined, ...(s.archiveOldestSeq != null ? { oldestSeq: s.archiveOldestSeq } : {}), ...(s.spawnedBy ? { spawned: true, spawnedBy: s.spawnedBy } : {}), ...(s.flowSessionId ? { flowId: s.flowSessionId, flowRole: s.flowTaskKey ? 'lane' : 'conductor' } : {}),
|
|
1073
|
+
...[...sessions.entries()].map(([id, s]) => ({ id, cmd: s.cmd, kind: 'structured', runtime: s.runtime || 'claude', alive: true, busy: s.session?.turnActive ?? false, commands: s.commands, mode: s.mode || undefined, name: termNames[id] || undefined, model: s.model || undefined, ...(s.runtime === 'codex' ? { approvalPolicy: 'never', models: s.models || [] } : {}), ...(s.archiveOldestSeq != null ? { oldestSeq: s.archiveOldestSeq } : {}), ...(s.spawnedBy ? { spawned: true, spawnedBy: s.spawnedBy } : {}), ...(s.flowSessionId ? { flowId: s.flowSessionId, flowRole: s.flowTaskKey ? 'lane' : 'conductor' } : {}),
|
|
1074
1074
|
// provider: the registered LLM-provider this lane runs on, NAME-ONLY {id,name}
|
|
1075
1075
|
// (NEVER the key or baseUrl). Additive; older clients ignore it. Omitted for the
|
|
1076
1076
|
// built-in/default Claude path (no badge). Makes the lane's provider badge +
|
|
@@ -1580,7 +1580,7 @@ function openStructured({ id, runtime = 'claude', model, resume, log, commands,
|
|
|
1580
1580
|
// spawnedBy: set when this lane was Dispatched (spawn_terminal). Restored from the
|
|
1581
1581
|
// session store so the Ensemble flag survives a bridge restart (else a respin
|
|
1582
1582
|
// stripped it and the lane reverted to a plain tab — the t6 "no chip" bug).
|
|
1583
|
-
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,
|
|
1583
|
+
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, models: runtime === 'codex' ? readCodexModels() : undefined,
|
|
1584
1584
|
// model: truthful active-model label — now the SAME `laneModel` the SDK is given, so the
|
|
1585
1585
|
// chip cannot disagree with the wire. When this lane runs on a custom (non-anthropic)
|
|
1586
1586
|
// registered provider the SDK id is impersonated (see the onEvent guard below), so
|
package/codex-session.mjs
CHANGED
|
@@ -43,6 +43,16 @@ export function readCodexDefaultModel({ home = process.env.CODEX_HOME || path.jo
|
|
|
43
43
|
} catch { return null }
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
+
export function readCodexModels({ home = process.env.CODEX_HOME || path.join(os.homedir(), '.codex'), fsImpl = fs } = {}) {
|
|
47
|
+
try {
|
|
48
|
+
const data = JSON.parse(fsImpl.readFileSync(path.join(home, 'models_cache.json'), 'utf8'))
|
|
49
|
+
return (Array.isArray(data?.models) ? data.models : [])
|
|
50
|
+
.filter((m) => m?.slug && m.visibility !== 'hide')
|
|
51
|
+
.sort((a, b) => (Number(a.priority) || 999) - (Number(b.priority) || 999))
|
|
52
|
+
.map((m) => ({ value: m.slug, displayName: m.display_name || m.slug, description: m.description || '' }))
|
|
53
|
+
} catch { return [] }
|
|
54
|
+
}
|
|
55
|
+
|
|
46
56
|
export function normalizeCodexSandbox(value) {
|
|
47
57
|
return SAFE_SANDBOXES.has(value) ? value : DEFAULT_SANDBOX
|
|
48
58
|
}
|
|
@@ -97,8 +107,6 @@ export function startCodexSession({ cwd, model, resume, env, sandbox = DEFAULT_S
|
|
|
97
107
|
|
|
98
108
|
const mapper = new CodexEventMapper({ onEvent, model: model || null })
|
|
99
109
|
|
|
100
|
-
const note = (text) => { try { onEvent?.({ kind: 'note', text }) } catch { /* noop */ } }
|
|
101
|
-
|
|
102
110
|
function runExec(prompt) {
|
|
103
111
|
// First turn: fresh exec. Later turns: resume THIS lane's captured thread.
|
|
104
112
|
// `--last` is process-global and can cross-wire two concurrently-active Codex
|
|
@@ -123,19 +131,12 @@ export function startCodexSession({ cwd, model, resume, env, sandbox = DEFAULT_S
|
|
|
123
131
|
child = spawn('codex', args, opts)
|
|
124
132
|
if (stdinFd != null) { try { fs.closeSync(stdinFd) } catch { /* child owns its duplicated fd */ } }
|
|
125
133
|
|
|
126
|
-
// G1 mitigation: while codex composes (before the first item event), show life.
|
|
127
|
-
let sawFirst = false
|
|
128
|
-
const composingTimer = setTimeout(() => {
|
|
129
|
-
if (!sawFirst) note('codex composing…')
|
|
130
|
-
}, 1200)
|
|
131
|
-
|
|
132
134
|
const rl = readline.createInterface({ input: child.stdout, crlfDelay: Infinity })
|
|
133
135
|
rl.on('line', (line) => {
|
|
134
136
|
if (!line.trim()) return
|
|
135
137
|
let ev
|
|
136
138
|
try { ev = JSON.parse(line) } catch { return /* non-JSON progress line */
|
|
137
139
|
}
|
|
138
|
-
if (!sawFirst && ev.type && ev.type.startsWith('item')) sawFirst = true
|
|
139
140
|
if (ev.type === 'thread.started' && ev.thread_id) sessionId = sessionId || ev.thread_id
|
|
140
141
|
if (ev.type === 'turn.completed' || ev.type === 'turn.failed') sawTerminalResult = true
|
|
141
142
|
mapper.push(ev)
|
|
@@ -149,7 +150,6 @@ export function startCodexSession({ cwd, model, resume, env, sandbox = DEFAULT_S
|
|
|
149
150
|
const finish = (code) => {
|
|
150
151
|
if (settled) return
|
|
151
152
|
settled = true
|
|
152
|
-
clearTimeout(composingTimer)
|
|
153
153
|
child = null
|
|
154
154
|
turnActive = false
|
|
155
155
|
if (aborted && !sawTerminalResult) {
|
|
@@ -162,7 +162,6 @@ export function startCodexSession({ cwd, model, resume, env, sandbox = DEFAULT_S
|
|
|
162
162
|
child.on('error', (e) => {
|
|
163
163
|
if (settled) return
|
|
164
164
|
settled = true
|
|
165
|
-
clearTimeout(composingTimer)
|
|
166
165
|
try { onEvent?.({ kind: 'error', message: `codex failed to start: ${e.message}`, recoverable: true }) } catch { /* noop */ }
|
|
167
166
|
child = null
|
|
168
167
|
turnActive = false
|