fix(server,web): steering survives reloads with visible content, and file attachments steer too (#157)

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Yaowei Zheng
2026-08-03 15:35:32 +08:00
committed by GitHub
parent 835dfb5c5a
commit a0968496b7
17 changed files with 554 additions and 68 deletions
+23 -1
View File
@@ -17,7 +17,12 @@ import {
withOrigin,
} from "@prismshadow/penguin-core/omnimessage";
import type { OmniMessage, TokenCounts } from "@prismshadow/penguin-core/omnimessage";
import type { MessagesLiveTail, ServerEvent, SessionStatus } from "@prismshadow/penguin-server/api";
import type {
MessagesLiveTail,
PendingSteeringInfo,
ServerEvent,
SessionStatus,
} from "@prismshadow/penguin-server/api";
import { createStreamController } from "../src/lib/omni/stream-controller";
import type { StreamController } from "../src/lib/omni/stream-controller";
import { approvalKey, findToolCard } from "../src/lib/omni/stream-model";
@@ -38,6 +43,7 @@ const flush = () => new Promise<void>((resolve) => setTimeout(resolve, 0));
interface Harness {
controller: StreamController;
states: SessionStatus[];
pendingSteering: PendingSteeringInfo[][];
errors: Array<string | null>;
loadings: boolean[];
loadCalls: () => number;
@@ -59,6 +65,7 @@ function createHarness(): Harness {
reject: (e: unknown) => void;
}> = [];
const states: SessionStatus[] = [];
const pendingSteering: PendingSteeringInfo[][] = [];
const errors: Array<string | null> = [];
const loadings: boolean[] = [];
let calls = 0;
@@ -73,6 +80,7 @@ function createHarness(): Harness {
pendingLoads.push({ resolve, reject });
}),
onTaskState: (s) => states.push(s),
onPendingSteering: (items) => pendingSteering.push(items),
onLoading: (l) => loadings.push(l),
onError: (e) => errors.push(e),
onModelChange: () => {},
@@ -82,6 +90,7 @@ function createHarness(): Harness {
return {
controller,
states,
pendingSteering,
errors,
loadings,
loadCalls: () => calls,
@@ -173,6 +182,19 @@ describe("in-stream task_state is the authoritative running state (history-closi
expect(h.states).toEqual(["running"]);
});
it("reports the pending-steering mirror from task_state, and its absence as empty", async () => {
const h = createHarness();
void h.controller.load();
h.controller.handleServer({
type: "task_state",
state: "running",
pendingSteering: [{ text: "hold on", images: 0, files: 1 }],
});
// A later event without the field means "none left" — reported as empty, not skipped.
h.controller.handleServer({ type: "task_state", state: "running" });
expect(h.pendingSteering).toEqual([[{ text: "hold on", images: 0, files: 1 }], []]);
});
it("closes the current Task before an auto-started queued follow-up begins", async () => {
const h = createHarness();
const p = h.controller.load();