fix(ui): stream phase progress without gates
This commit is contained in:
@@ -4,6 +4,15 @@ const GENERIC_MODEL_FAILURE =
|
||||
"Model request failed. Check provider connectivity, then Resume the session.";
|
||||
const SUBSCRIPTION_MODEL_FAILURE =
|
||||
"The selected model is unavailable for the current subscription. Choose another model and start a new session.";
|
||||
const PHASE_STARTED_NOTIFICATION_PREFIX = "__tht_phase_started__:";
|
||||
|
||||
function phaseStartedNotification(message: unknown): string | null {
|
||||
if (typeof message !== "string" || !message.startsWith(PHASE_STARTED_NOTIFICATION_PREFIX)) {
|
||||
return null;
|
||||
}
|
||||
const phase = message.slice(PHASE_STARTED_NOTIFICATION_PREFIX.length);
|
||||
return /^F[1-8]$/.test(phase) ? phase : "";
|
||||
}
|
||||
|
||||
function safeModelFailure(error: unknown): string {
|
||||
const detail = typeof error === "string" ? error : "";
|
||||
@@ -36,7 +45,7 @@ export type ClientEvent =
|
||||
| { type: "activity_event"; activity: ToolActivity }
|
||||
| { type: "usage"; usage: TokenUsage }
|
||||
| { type: "info"; [k: string]: any }
|
||||
| { type: "system_event"; event: string };
|
||||
| { type: "system_event"; event: string; phase?: string };
|
||||
|
||||
export type TurnState = "idle" | "running" | "waiting" | "failed";
|
||||
|
||||
@@ -71,7 +80,11 @@ export class SessionBridge {
|
||||
});
|
||||
}
|
||||
} else if (m.type === "extension_ui_request" && m.method === "notify") {
|
||||
this.fan({ type: "info", level: m.notifyType ?? "info", text: m.message ?? "" });
|
||||
const phase = phaseStartedNotification(m.message);
|
||||
if (phase) this.fan({ type: "system_event", event: "phase_started", phase });
|
||||
else if (phase === null) {
|
||||
this.fan({ type: "info", level: m.notifyType ?? "info", text: m.message ?? "" });
|
||||
}
|
||||
} else if (m.type === "message_update" && m.assistantMessageEvent?.type === "text_delta") {
|
||||
this.fan({ type: "text_delta", text: m.assistantMessageEvent.delta ?? "" });
|
||||
} else if (m.type === "message_update" && m.assistantMessageEvent?.type === "thinking_delta") {
|
||||
|
||||
@@ -42,6 +42,29 @@ test("real Pi thinking_delta becomes a dedicated activity_delta to the FE", () =
|
||||
expect(seen).toEqual([{ type: "activity_delta", text: "Valuto le ambiguità" }]);
|
||||
});
|
||||
|
||||
test("a reserved phase notification becomes a structured phase_started event", () => {
|
||||
const { rpc, fire } = fakeRpc();
|
||||
const bridge = new SessionBridge(rpc);
|
||||
const seen: any[] = [];
|
||||
bridge.onClientEvent((event) => seen.push(event));
|
||||
|
||||
fire({
|
||||
type: "extension_ui_request",
|
||||
method: "notify",
|
||||
notifyType: "info",
|
||||
message: "__tht_phase_started__:F2",
|
||||
});
|
||||
fire({
|
||||
type: "extension_ui_request",
|
||||
method: "notify",
|
||||
notifyType: "info",
|
||||
message: "__tht_phase_started__:F9_DO_NOT_FORWARD",
|
||||
});
|
||||
|
||||
expect(seen).toEqual([{ type: "system_event", event: "phase_started", phase: "F2" }]);
|
||||
expect(JSON.stringify(seen)).not.toContain("DO_NOT_FORWARD");
|
||||
});
|
||||
|
||||
test("assistant message_end exposes sanitized token usage with the configured context window", () => {
|
||||
const { rpc, fire } = fakeRpc();
|
||||
const bridge = new SessionBridge(rpc);
|
||||
|
||||
Reference in New Issue
Block a user