fix(coding-agent-turn-interrupt/queue-ux): resolved steering abort state
- Replaced queued-message interrupt flow with session abort calls on empty submit and escape. - Removed interrupting state and notifyInterrupting teardown paths from abort handling. - Updated AgentSession queue operations to use shared steering and follow-up queue views. - Propagated isAborting through session state and collab payloads to suppress late updates.
This commit is contained in:
@@ -1,9 +1,8 @@
|
||||
/**
|
||||
* Contract: a guest prompt that arrives while the host agent is streaming is
|
||||
* steered AND becomes visible as a queued message — the host registers the
|
||||
* pending-display twin (so `queuedMessageCount` covers it, feeding the web
|
||||
* composer's "queued ×N" badge and the TUI pending bar) and tags the custom
|
||||
* message so the entry is dequeued when the agent consumes it.
|
||||
* steered AND becomes visible as a queued message. The host sends the guest text
|
||||
* as `queueChipText` on the queued custom message; the session derives
|
||||
* `queuedMessageCount` from the agent-core queue for host and guest UI state.
|
||||
*/
|
||||
import { afterEach, describe, expect, it } from "bun:test";
|
||||
import { importRoomKey } from "@oh-my-pi/pi-coding-agent/collab/crypto";
|
||||
@@ -76,20 +75,19 @@ function startTestRelay(): { url: string; stop(): void } {
|
||||
}
|
||||
|
||||
interface CapturedPrompt {
|
||||
details?: { from?: string; __pendingDisplayTag?: string };
|
||||
details?: { from?: string };
|
||||
options?: { streamingBehavior?: "steer"; queueChipText?: string };
|
||||
}
|
||||
|
||||
interface StreamingHostHarness {
|
||||
ctx: InteractiveModeContext;
|
||||
prompts: CapturedPrompt[];
|
||||
enqueued: { text: string; mode: string; tag: string }[];
|
||||
nextPrompt(): Promise<CapturedPrompt>;
|
||||
}
|
||||
|
||||
/** Context double for a host whose agent is mid-turn (isStreaming === true). */
|
||||
function makeStreamingHostContext(): StreamingHostHarness {
|
||||
const prompts: CapturedPrompt[] = [];
|
||||
const enqueued: { text: string; mode: string; tag: string }[] = [];
|
||||
const promptWaiters: ((prompt: CapturedPrompt) => void)[] = [];
|
||||
const ctx = {
|
||||
settings: { get: () => "" },
|
||||
@@ -105,20 +103,16 @@ function makeStreamingHostContext(): StreamingHostHarness {
|
||||
session: {
|
||||
isStreaming: true,
|
||||
get queuedMessageCount(): number {
|
||||
return enqueued.length;
|
||||
return prompts.filter(prompt => prompt.options?.queueChipText).length;
|
||||
},
|
||||
isAborting: false,
|
||||
sessionName: "test",
|
||||
model: undefined,
|
||||
thinkingLevel: undefined,
|
||||
subscribe: () => () => {},
|
||||
emitNotice: () => {},
|
||||
enqueueCustomMessageDisplay: (text: string, mode: string) => {
|
||||
const tag = `tag-${enqueued.length + 1}`;
|
||||
enqueued.push({ text, mode, tag });
|
||||
return tag;
|
||||
},
|
||||
promptCustomMessage: (message: CapturedPrompt) => {
|
||||
const captured: CapturedPrompt = { details: message.details };
|
||||
promptCustomMessage: (message: CapturedPrompt, options?: CapturedPrompt["options"]) => {
|
||||
const captured: CapturedPrompt = { details: message.details, options };
|
||||
prompts.push(captured);
|
||||
for (const waiter of promptWaiters.splice(0)) waiter(captured);
|
||||
return Promise.resolve();
|
||||
@@ -140,7 +134,7 @@ function makeStreamingHostContext(): StreamingHostHarness {
|
||||
promptWaiters.push(resolve);
|
||||
return promise;
|
||||
};
|
||||
return { ctx, prompts, enqueued, nextPrompt };
|
||||
return { ctx, prompts, nextPrompt };
|
||||
}
|
||||
|
||||
interface TestGuest {
|
||||
@@ -197,10 +191,8 @@ describe("collab mid-turn guest prompts", () => {
|
||||
guest.socket.send({ t: "prompt", text: "steer the host" });
|
||||
const prompt = await prompted;
|
||||
|
||||
// Display twin registered before dispatch, tag forwarded in details so
|
||||
// the session dequeues the entry when the agent consumes the message.
|
||||
expect(harness.enqueued).toEqual([{ text: "steer the host", mode: "steer", tag: "tag-1" }]);
|
||||
expect(prompt.details).toEqual({ from: "writer", __pendingDisplayTag: "tag-1" });
|
||||
expect(prompt.details).toEqual({ from: "writer" });
|
||||
expect(prompt.options).toEqual({ streamingBehavior: "steer", queueChipText: "steer the host" });
|
||||
|
||||
// The queued steer must reach guests through state.queuedMessageCount —
|
||||
// that field drives the web composer's "queued ×N" badge.
|
||||
|
||||
Reference in New Issue
Block a user