feat: normalized external-agent status (#10 scoped) (v2.7.6)
Scoped half of boocode_code_review_v2 §1 #10 — publish the agent status BooCoder already observes (the config-injection notify-hook is the documented follow-on, clean-room from superset ELv2). - agent_status_updated WS frame (working|blocked|idle|error), server+web parity. - Published from the dispatcher's turn boundaries (warm-acp/opencode/sdk/pty: working at start, idle/error at end) + the permission flow (blocked/working). Best-effort, never breaks a turn. - Clean-room normalizeAgentEvent helper (superset's vendor-event -> Start/blocked /Stop collapse, event names as facts) + 25 tests — reused by the follow-on. - AgentComposerBar status dot (distinct from the WS-liveness dot), tracked per (chat,agent) by a useAgentStatus map in CoderPane. Built by 2 parallel agents vs a pinned frame contract. Server 545 + coder 294 tests passing (25 new); web tsc + builds clean; ws-frames parity green. Clears the actionable review backlog (#1/#3/#4/#6-#12). Builds on v2.7.5. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -596,4 +596,16 @@ export type WsFrame =
|
||||
| { type: 'compacted'; session_id: string; chat_id: string; summary_message_id: string }
|
||||
// v1.8.2: `reason` discriminates structured failures (the UI prefers it
|
||||
// over `error` text when present).
|
||||
| { type: 'error'; message_id?: string; chat_id?: string; error: string; reason?: ErrorReason };
|
||||
| { type: 'error'; message_id?: string; chat_id?: string; error: string; reason?: ErrorReason }
|
||||
// agent-status-normalize (#10): BooCoder publishes a normalized per-(chat,agent)
|
||||
// lifecycle status for external coding agents on the per-session channel. The
|
||||
// CoderPane tracks the latest status per (chat_id, agent) and resets on chat
|
||||
// switch; AgentComposerBar renders the dot (distinct from the WS-liveness dot).
|
||||
| {
|
||||
type: 'agent_status_updated';
|
||||
chat_id: string;
|
||||
agent: string;
|
||||
status: 'working' | 'blocked' | 'idle' | 'error';
|
||||
reason?: string;
|
||||
at: string;
|
||||
};
|
||||
|
||||
@@ -39,6 +39,12 @@ const ChatStatusValue = z.enum([
|
||||
'error',
|
||||
]);
|
||||
|
||||
// agent-status-normalize (#10): normalized per-(chat,agent) lifecycle status for
|
||||
// external coding agents (warm-acp / opencode / claude-sdk / pty). Distinct from
|
||||
// ChatStatusValue (native-inference chat lifecycle) — published by BooCoder's
|
||||
// dispatcher + permission flow on the per-session channel.
|
||||
const AgentStatusValue = z.enum(['working', 'blocked', 'idle', 'error']);
|
||||
|
||||
const ErrorReasonValue = z.enum([
|
||||
'llm_provider_error',
|
||||
'doom_loop',
|
||||
@@ -301,6 +307,21 @@ export const AgentCommandsFrame = z.object({
|
||||
commands: z.array(AgentCommandShape),
|
||||
});
|
||||
|
||||
// agent-status-normalize (#10): published by BooCoder on the per-session channel
|
||||
// when an external agent's normalized status changes (turn start/end, permission
|
||||
// block/unblock). Keyed per (chat_id, agent); the frontend tracks the latest per
|
||||
// pair and resets on chat switch. `reason` is a free-form discriminator
|
||||
// (turn_start / turn_complete / failed / crashed / permission_request /
|
||||
// permission_resolved).
|
||||
export const AgentStatusUpdatedFrame = z.object({
|
||||
type: z.literal('agent_status_updated'),
|
||||
chat_id: Uuid,
|
||||
agent: z.string().min(1),
|
||||
status: AgentStatusValue,
|
||||
reason: z.string().optional(),
|
||||
at: IsoTimestamp,
|
||||
});
|
||||
|
||||
// ---- discriminated union ---------------------------------------------------
|
||||
|
||||
export const WsFrameSchema = z.discriminatedUnion('type', [
|
||||
@@ -320,6 +341,7 @@ export const WsFrameSchema = z.discriminatedUnion('type', [
|
||||
PermissionRequestedFrame,
|
||||
PermissionResolvedFrame,
|
||||
AgentCommandsFrame,
|
||||
AgentStatusUpdatedFrame,
|
||||
// per-user
|
||||
ChatStatusFrame,
|
||||
SessionUpdatedFrame,
|
||||
@@ -361,6 +383,7 @@ export const KNOWN_FRAME_TYPES: readonly WsFrame['type'][] = [
|
||||
'permission_requested',
|
||||
'permission_resolved',
|
||||
'agent_commands',
|
||||
'agent_status_updated',
|
||||
'chat_status',
|
||||
'session_updated',
|
||||
'session_renamed',
|
||||
|
||||
Reference in New Issue
Block a user