Skip to content

Commit 8310743

Browse files
committed
fix(tui): skip occupancy flush while the operator pause holds
Pause already holds the delivery queue. Occupancy and ask-wake still started a new primary after Ctrl+C. Stall and expire do not pause, so they still flush.
1 parent 2f36e57 commit 8310743

2 files changed

Lines changed: 53 additions & 1 deletion

File tree

‎src/tui/agent-ask-wake.test.ts‎

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,10 @@ import {
1111
createDeliveryGeneration,
1212
createLeftoverSend,
1313
createSessionOperationQueue,
14+
isPaused,
1415
routeQueuedDelivery,
1516
} from "./delivery-queue.js";
17+
import { handleCtrlC } from "./shell/keys.js";
1618
import { ingestOperatorPrompt } from "./prompt-attachments.js";
1719
import {
1820
armFeedbackCapture,
@@ -623,6 +625,50 @@ describe("agent ask wake delivery", () => {
623625
});
624626
}
625627

628+
test("operator pause does not flush a stashed ask or mailbox occupancy", async () => {
629+
await withAppShell(
630+
async (shell) => {
631+
const sends: string[] = [];
632+
const bridge = attachSessionBridge(
633+
shell,
634+
createLiveSessionPort({
635+
send: (text) => {
636+
sends.push(text);
637+
},
638+
deliver: (text) => {
639+
sends.push(text);
640+
},
641+
interrupt: () => undefined,
642+
}),
643+
);
644+
try {
645+
const order = trackMailOrder(bridge, true);
646+
bridge.handle({ type: "inference.start", data: {} });
647+
bridge.handle({
648+
type: "inference.text.delta",
649+
data: { token: "ok" },
650+
});
651+
bridge.handle({ type: "agent-ask", asks: [wake("a1", "q1")] });
652+
expect(sends).toEqual([]);
653+
654+
handleCtrlC(shell);
655+
expect(isPaused(shell.session)).toBe(true);
656+
expect(sends).toEqual([]);
657+
expect(order).toEqual([]);
658+
expect(bridge.turn.isProcessing).toBe(false);
659+
660+
bridge.handle({ type: "agent-ask", asks: [wake("a2", "q2")] });
661+
expect(sends).toEqual([]);
662+
expect(order).toEqual([]);
663+
expect(bridge.turn.isProcessing).toBe(false);
664+
} finally {
665+
bridge.dispose();
666+
}
667+
},
668+
{ shell: { run: "busy" } },
669+
);
670+
});
671+
626672
test("a wake question with bracket lines does not spoof attachment-echo matching", async () => {
627673
await withWakeBridge((bridge, sends, shell) => {
628674
const ask = {

‎src/tui/runtime-bridge.ts‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2093,6 +2093,7 @@ export function attachSessionBridge(
20932093
});
20942094
};
20952095
const flushMailboxMail = (): boolean => {
2096+
if (isPaused(shell.session)) return false;
20962097
if (bag.disposed || bag.turn.isProcessing) return false;
20972098
try {
20982099
if (bag.mailboxMailDriver?.() === true) return true;
@@ -2104,6 +2105,7 @@ export function attachSessionBridge(
21042105
bag.flushMailboxMail = flushMailboxMail;
21052106

21062107
const flushPendingAskWake = (): void => {
2108+
if (isPaused(shell.session)) return;
21072109
if (bag.disposed || bag.turn.isProcessing || bag.turn.blockedGateCount > 0)
21082110
return;
21092111
if (mailboxMailDriveClaimed(bag.mailboxMailDriver)) return;
@@ -2133,6 +2135,7 @@ export function attachSessionBridge(
21332135
bag.flushPendingAskWake = flushPendingAskWake;
21342136

21352137
const flushOccupancyThenWake = (): void => {
2138+
if (isPaused(shell.session)) return;
21362139
if (flushMailboxMail() || bag.turn.isProcessing) return;
21372140
flushPendingAskWake();
21382141
};
@@ -2188,7 +2191,10 @@ export function attachSessionBridge(
21882191
bag.askWakeTurnArmed = false;
21892192
bag.turn = turnStateOnInterrupt(bag.turn, now());
21902193
paintPhase();
2191-
flushOccupancyThenWake();
2194+
// Operator pause holds occupancy/ask-wake: a stashed mailbox or ask must
2195+
// not start a new primary until an explicit send. Stall/expire do not
2196+
// set paused, so they still flush here.
2197+
if (!isPaused(shell.session)) flushOccupancyThenWake();
21922198
};
21932199

21942200
/**

0 commit comments

Comments
 (0)