thinkpool-pair 0.7.270 → 0.7.271
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/codex-session.mjs +34 -3
- package/package.json +1 -1
package/codex-session.mjs
CHANGED
|
@@ -543,8 +543,11 @@ export function startCodexSession({ cwd, model, effort: initialEffort = 'high',
|
|
|
543
543
|
|
|
544
544
|
async function runAppServer(prompt, options = {}) {
|
|
545
545
|
if (!await ensureAppServer()) return false
|
|
546
|
+
// Stop can land while the cold App Server / MCP bootstrap is still awaiting.
|
|
547
|
+
// The accepted turn was already closed at the room boundary; never start it
|
|
548
|
+
// after bootstrap finally resolves.
|
|
549
|
+
if (ended || aborted) return true
|
|
546
550
|
mapper.setUsageBaseline(sessionId ? readCodexThreadUsage(sessionId) : null)
|
|
547
|
-
aborted = false
|
|
548
551
|
turnActive = true
|
|
549
552
|
try {
|
|
550
553
|
activeTurnId = await appServer.startTurn({
|
|
@@ -556,6 +559,13 @@ export function startCodexSession({ cwd, model, effort: initialEffort = 'high',
|
|
|
556
559
|
approvalPolicy: modeConfig.approvalPolicy,
|
|
557
560
|
collaborationMode: codexDefaultCollaborationMode(activeModel, activeEffort),
|
|
558
561
|
})
|
|
562
|
+
// Stop may land after the RPC was written but before startTurn returns its
|
|
563
|
+
// id. abort() already closed the room boundary; interrupt the now-known
|
|
564
|
+
// native turn before waiting, and never let it continue invisibly.
|
|
565
|
+
if (aborted || ended) {
|
|
566
|
+
try { await appServer.interrupt({ threadId: sessionId, turnId: activeTurnId }) } catch { /* boundary is already closed */ }
|
|
567
|
+
return true
|
|
568
|
+
}
|
|
559
569
|
const completed = await appServer.waitForTurn(activeTurnId)
|
|
560
570
|
const status = completed?.turn?.status
|
|
561
571
|
if (aborted || status === 'interrupted') {
|
|
@@ -592,13 +602,17 @@ export function startCodexSession({ cwd, model, effort: initialEffort = 'high',
|
|
|
592
602
|
|
|
593
603
|
async function runAppServerReview(commandText) {
|
|
594
604
|
if (!await ensureAppServer()) return false
|
|
605
|
+
if (ended || aborted) return true
|
|
595
606
|
mapper.setUsageBaseline(sessionId ? readCodexThreadUsage(sessionId) : null)
|
|
596
|
-
aborted = false
|
|
597
607
|
turnActive = true
|
|
598
608
|
try {
|
|
599
609
|
const started = await appServer.startReview({ threadId: sessionId, target: codexReviewTarget(commandText) })
|
|
600
610
|
activeTurnId = started?.turn?.id || started?.turnId
|
|
601
611
|
if (!activeTurnId) throw new Error('Codex review/start returned no turn id')
|
|
612
|
+
if (aborted || ended) {
|
|
613
|
+
try { await appServer.interrupt({ threadId: sessionId, turnId: activeTurnId }) } catch { /* boundary is already closed */ }
|
|
614
|
+
return true
|
|
615
|
+
}
|
|
602
616
|
const completed = await appServer.waitForTurn(activeTurnId)
|
|
603
617
|
const status = completed?.turn?.status
|
|
604
618
|
if (aborted || status === 'interrupted') {
|
|
@@ -628,6 +642,7 @@ export function startCodexSession({ cwd, model, effort: initialEffort = 'high',
|
|
|
628
642
|
}
|
|
629
643
|
|
|
630
644
|
async function runExec(prompt, options = {}) {
|
|
645
|
+
if (ended || aborted) return
|
|
631
646
|
// First turn: fresh exec. Later turns: resume THIS lane's captured thread.
|
|
632
647
|
// `--last` is process-global and can cross-wire two concurrently-active Codex
|
|
633
648
|
// lanes, so it is never safe in a multi-lane bridge.
|
|
@@ -658,7 +673,6 @@ export function startCodexSession({ cwd, model, effort: initialEffort = 'high',
|
|
|
658
673
|
const opts = { cwd, env: childEnv, stdio: ['ignore', 'pipe', 'pipe'] }
|
|
659
674
|
if (stdinFd != null) opts.stdio = [stdinFd, 'pipe', 'pipe']
|
|
660
675
|
|
|
661
|
-
aborted = false
|
|
662
676
|
turnActive = true
|
|
663
677
|
child = spawnImpl('codex', args, opts)
|
|
664
678
|
if (stdinFd != null) { try { fs.closeSync(stdinFd) } catch { /* child owns its duplicated fd */ } }
|
|
@@ -777,18 +791,35 @@ export function startCodexSession({ cwd, model, effort: initialEffort = 'high',
|
|
|
777
791
|
return true
|
|
778
792
|
}
|
|
779
793
|
queue.push({ text, options, promptIndex, forceFullReminder: thisTurnForceFull })
|
|
794
|
+
// Match Claude and Hermes: accepting an idle turn claims the lane before
|
|
795
|
+
// any cold App Server, MCP, or exec bootstrap. bridge.mjs announces
|
|
796
|
+
// synchronously after sendTurn(), so this is the edge that makes Thinking,
|
|
797
|
+
// Stop, and the tab spinner appear at dispatch time rather than at the
|
|
798
|
+
// first provider event many seconds later.
|
|
799
|
+
if (!turnActive) {
|
|
800
|
+
aborted = false
|
|
801
|
+
turnActive = true
|
|
802
|
+
}
|
|
780
803
|
// start the pump if idle
|
|
781
804
|
if (queue.length === 1) pump()
|
|
782
805
|
return true
|
|
783
806
|
},
|
|
784
807
|
abort() {
|
|
808
|
+
const bootstrapOnly = turnActive && !activeTurnId && !child
|
|
785
809
|
aborted = true
|
|
786
810
|
queue.length = 0
|
|
787
811
|
if (appServer && activeTurnId) { void appServer.interrupt({ threadId: sessionId, turnId: activeTurnId }).catch(() => {}) ; return }
|
|
788
812
|
if (child) { try { child.kill('SIGTERM') } catch { /* noop */ } ; setTimeout(() => { try { child?.kill('SIGKILL') } catch { /* noop */ } }, 1500) }
|
|
813
|
+
else if (bootstrapOnly) {
|
|
814
|
+
// No native turn exists to produce a boundary. Close the optimistic
|
|
815
|
+
// accepted turn exactly once and let runAppServer's post-bootstrap
|
|
816
|
+
// aborted check discard the delayed startup.
|
|
817
|
+
emitTurnBoundary({ kind: 'result', subtype: 'aborted', sessionId, model: activeModel, costUsd: null, usage: null, numTurns: 0, durationMs: undefined, denials: 0, resultText: null })
|
|
818
|
+
}
|
|
789
819
|
},
|
|
790
820
|
end() {
|
|
791
821
|
ended = true
|
|
822
|
+
turnActive = false
|
|
792
823
|
queue.length = 0
|
|
793
824
|
if (child) { try { child.kill() } catch { /* noop */ } }
|
|
794
825
|
try { appServer?.end() } catch { /* noop */ }
|