fix(queue): harden TUI dequeue display trim and override submit guard (#1183)
This commit is contained in:
@@ -1750,7 +1750,14 @@ export default function App({
|
||||
"queue-lifecycle",
|
||||
`dequeued batch_id=${batch.batchId} merged_count=${batch.mergedCount} queue_len_after=${batch.queueLenAfter}`,
|
||||
);
|
||||
setQueueDisplay((prev) => prev.slice(batch.mergedCount));
|
||||
// queueDisplay only tracks displayable items. If non-display barrier
|
||||
// kinds are ever consumed, avoid over-trimming by counting only
|
||||
// message/task_notification entries in the batch.
|
||||
const displayConsumedCount = batch.items.filter(
|
||||
(item) =>
|
||||
item.kind === "message" || item.kind === "task_notification",
|
||||
).length;
|
||||
setQueueDisplay((prev) => prev.slice(displayConsumedCount));
|
||||
},
|
||||
onBlocked: (reason, queueLen) =>
|
||||
debugLog(
|
||||
@@ -6502,6 +6509,7 @@ export default function App({
|
||||
async (message?: string): Promise<{ submitted: boolean }> => {
|
||||
const msg = message?.trim() ?? "";
|
||||
const overrideContentParts = overrideContentPartsRef.current;
|
||||
const hasOverrideContent = overrideContentParts !== null;
|
||||
if (overrideContentParts) {
|
||||
overrideContentPartsRef.current = null;
|
||||
}
|
||||
@@ -6512,7 +6520,7 @@ export default function App({
|
||||
taskNotifications.length > 0 && userTextForInput.length === 0;
|
||||
|
||||
// Handle profile load confirmation (Enter to continue)
|
||||
if (profileConfirmPending && !msg) {
|
||||
if (profileConfirmPending && !msg && !hasOverrideContent) {
|
||||
// User pressed Enter with empty input - proceed with loading
|
||||
const { name, agentId: targetAgentId, cmdId } = profileConfirmPending;
|
||||
const cmd = commandRunner.getHandle(cmdId, `/profile load ${name}`);
|
||||
@@ -6534,7 +6542,7 @@ export default function App({
|
||||
// Continue processing the new message
|
||||
}
|
||||
|
||||
if (!msg) return { submitted: false };
|
||||
if (!msg && !hasOverrideContent) return { submitted: false };
|
||||
|
||||
// If the user just cycled reasoning tiers, flush the final choice before
|
||||
// sending the next message so the upcoming run uses the selected tier.
|
||||
|
||||
@@ -37,6 +37,40 @@ describe("queue ordering wiring", () => {
|
||||
expect(segment).toContain("queuedOverlayAction,");
|
||||
});
|
||||
|
||||
test("queue display trim uses displayable-item count, not mergedCount", () => {
|
||||
const source = readAppSource();
|
||||
const start = source.indexOf("onDequeued: (batch) => {");
|
||||
const end = source.indexOf("onBlocked: (reason, queueLen) =>");
|
||||
|
||||
expect(start).toBeGreaterThan(-1);
|
||||
expect(end).toBeGreaterThan(start);
|
||||
|
||||
const segment = source.slice(start, end);
|
||||
expect(segment).toContain("const displayConsumedCount =");
|
||||
expect(segment).toContain('item.kind === "message"');
|
||||
expect(segment).toContain('item.kind === "task_notification"');
|
||||
expect(segment).toContain("prev.slice(displayConsumedCount)");
|
||||
});
|
||||
|
||||
test("onSubmit allows override-only queued submissions", () => {
|
||||
const source = readAppSource();
|
||||
const start = source.indexOf("const onSubmit = useCallback(");
|
||||
const end = source.indexOf(
|
||||
"// Process queued overlay actions when streaming ends",
|
||||
);
|
||||
|
||||
expect(start).toBeGreaterThan(-1);
|
||||
expect(end).toBeGreaterThan(start);
|
||||
|
||||
const segment = source.slice(start, end);
|
||||
expect(segment).toContain(
|
||||
"if (!msg && !hasOverrideContent) return { submitted: false };",
|
||||
);
|
||||
expect(segment).toContain(
|
||||
"if (profileConfirmPending && !msg && !hasOverrideContent)",
|
||||
);
|
||||
});
|
||||
|
||||
test("queued overlay effect only runs when idle and clears action before processing", () => {
|
||||
const source = readAppSource();
|
||||
const start = source.indexOf(
|
||||
|
||||
Reference in New Issue
Block a user