huskies: merge 643_story_web_ui_consumer_for_the_unified_status_broadcaster

This commit is contained in:
dave
2026-04-26 11:26:20 +00:00
parent f88bb5f486
commit 8673e563a9
13 changed files with 375 additions and 25 deletions
+48 -1
View File
@@ -108,7 +108,50 @@ export type WsResponse =
/** Final signal that the /btw side question has been fully answered. */
| { type: "side_question_done"; response: string }
/** A single server log entry (bulk on connect, then live). */
| { type: "log_entry"; timestamp: string; level: string; message: string };
| { type: "log_entry"; timestamp: string; level: string; message: string }
/** A structured pipeline status event from the status broadcaster. */
| { type: "status_update"; event: StatusEvent };
/**
* A structured pipeline status event emitted by the status broadcaster.
*
* The discriminant `type` field enables per-event-type rendering without
* parsing strings. All fields from the original event are preserved so
* future UI stories can add dedicated icons, banners, or filters.
*/
export type StatusEvent =
| {
type: "stage_transition";
story_id: string;
story_name: string | null;
from_stage: string;
to_stage: string;
}
| {
type: "merge_failure";
story_id: string;
story_name: string | null;
reason: string;
}
| {
type: "story_blocked";
story_id: string;
story_name: string | null;
reason: string;
}
| {
type: "rate_limit_warning";
story_id: string;
story_name: string | null;
agent_name: string;
}
| {
type: "rate_limit_hard_block";
story_id: string;
story_name: string | null;
agent_name: string;
reset_at: string;
};
export interface ProviderConfig {
provider: string;
@@ -478,6 +521,7 @@ export class ChatWebSocket {
level: string,
message: string,
) => void;
private onStatusUpdate?: (event: StatusEvent) => void;
private onConnected?: () => void;
private connected = false;
private closeTimer?: number;
@@ -573,6 +617,7 @@ export class ChatWebSocket {
this.onSideQuestionDone?.(data.response);
if (data.type === "log_entry")
this.onLogEntry?.(data.timestamp, data.level, data.message);
if (data.type === "status_update") this.onStatusUpdate?.(data.event);
if (data.type === "pong") {
window.clearTimeout(this.heartbeatTimeout);
this.heartbeatTimeout = undefined;
@@ -630,6 +675,7 @@ export class ChatWebSocket {
onSideQuestionToken?: (content: string) => void;
onSideQuestionDone?: (response: string) => void;
onLogEntry?: (timestamp: string, level: string, message: string) => void;
onStatusUpdate?: (event: StatusEvent) => void;
onConnected?: () => void;
},
wsPath = DEFAULT_WS_PATH,
@@ -650,6 +696,7 @@ export class ChatWebSocket {
this.onSideQuestionToken = handlers.onSideQuestionToken;
this.onSideQuestionDone = handlers.onSideQuestionDone;
this.onLogEntry = handlers.onLogEntry;
this.onStatusUpdate = handlers.onStatusUpdate;
this.onConnected = handlers.onConnected;
this.wsPath = wsPath;
this.shouldReconnect = true;