From 0ceadaa87d5a093a529868731ba536dd7c9d98d5 Mon Sep 17 00:00:00 2001 From: "Maksym Hryzodub [DREAM]" Date: Fri, 14 Aug 2026 12:44:36 +0200 Subject: [PATCH 1/6] feat(bridle): accept and relay the agent 'thinking' event (CLEAN-10 US2) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mirrors the new wire types and adds @SubscribeMessage('thinking') → handleAgentEvent so live reasoning steps reach the addressed browser client. Opaque relay like 'stream'; visitor-safe payload, not admin-gated. Co-Authored-By: Claude Fable 5 --- api/src/slices/bridle/domain/bridle.types.ts | 43 ++++++++++++++++++- .../bridle/handlers/bridleAgentWs.handler.ts | 14 ++++++ 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/api/src/slices/bridle/domain/bridle.types.ts b/api/src/slices/bridle/domain/bridle.types.ts index 64c19b47..27c1a747 100644 --- a/api/src/slices/bridle/domain/bridle.types.ts +++ b/api/src/slices/bridle/domain/bridle.types.ts @@ -43,7 +43,14 @@ export interface IBridleIncomingMessage { /** Agent → Hub: events routed to browser clients */ export interface IBridleOutgoingEvent { - type: 'register' | 'message' | 'stream' | 'stream_end' | 'typing' | 'ping'; + type: + | 'register' + | 'message' + | 'stream' + | 'stream_end' + | 'typing' + | 'thinking' + | 'ping'; clientId?: string; text?: string; parts?: BridlePart[]; @@ -51,6 +58,40 @@ export interface IBridleOutgoingEvent { ts?: number; } +// ── Thinking (live reasoning steps) ────────────────────────── + +/** One published unit of agent work inside a thinking timeline. */ +export interface IBridleThinkingStep { + /** Stable per-step id — the `done` update reuses the `active` event's id. */ + id: string; + /** Human-readable, visitor-safe step name (e.g. "Search knowledge base"). */ + label: string; + /** Optional visitor-safe reasoning prose (markdown). Never raw tool + * params or prompts — this event is NOT admin-gated (unlike `debug`). */ + detail?: string; + state: 'active' | 'done'; +} + +/** + * Agent → Hub → Browser: live "what the agent is doing" feed, rendered by + * thinking-capable clients as a collapsible timeline while the answer is + * being prepared. Two shapes share the event: a step update (`step` set) + * and turn completion (`done: true`, no step) which closes the open block. + * The hub relays it to the addressed client like `stream`. Agents emit it + * only toward clients whose handshake `capabilities` include `'thinking'`. + */ +export interface IBridleThinkingEvent { + type: 'thinking'; + clientId: string; + /** Groups every step of one agent turn (minted per loop run). */ + turnId: string; + /** Present on step updates; absent on the terminal `done` event. */ + step?: IBridleThinkingStep; + /** True on the terminal event of a turn. */ + done?: boolean; + ts: number; +} + /** * Agent → Hub → Admin browsers only. * Carries a snapshot of what was sent to the LLM and what came back, for diff --git a/api/src/slices/bridle/handlers/bridleAgentWs.handler.ts b/api/src/slices/bridle/handlers/bridleAgentWs.handler.ts index 48ba509c..2890f38a 100644 --- a/api/src/slices/bridle/handlers/bridleAgentWs.handler.ts +++ b/api/src/slices/bridle/handlers/bridleAgentWs.handler.ts @@ -14,6 +14,7 @@ import { type IBridleOutgoingEvent, type IBridleSyncResponse, type IBridleDebugEvent, + type IBridleThinkingEvent, } from '../domain'; import { IAgentGateway } from '#/agent/agent/domain/agent.gateway'; import { IChatGateway, type IChatActivity } from '#/chat/domain'; @@ -33,6 +34,7 @@ import { IChatGateway, type IChatActivity } from '#/chat/domain'; * "stream" { clientId, text, messageId, ts } * "stream_end" { clientId, text, messageId, ts } * "typing" { clientId, ts } + * "thinking" { clientId, turnId, step?, done?, ts } * "sync_done" { requestId, pushed, error? } * "ping" {} * @@ -179,6 +181,18 @@ export class BridleAgentWsHandler } } + @SubscribeMessage('thinking') + handleThinking( + @ConnectedSocket() client: Socket, + @MessageBody() data: IBridleThinkingEvent, + ) { + this.ensureRegistered(client); + const agentId = client.data?.agentId as string; + if (data?.clientId && data?.turnId && agentId) { + this.hub.handleAgentEvent(agentId, { ...data, type: 'thinking' }); + } + } + @SubscribeMessage('sync_done') handleSyncDone( @ConnectedSocket() client: Socket, From 5a8895b3d83da53d94d9f4f0f75567ec5a8ee669 Mon Sep 17 00:00:00 2001 From: "Maksym Hryzodub [DREAM]" Date: Fri, 14 Aug 2026 12:48:56 +0200 Subject: [PATCH 2/6] feat(admin): live thinking timeline in the agent chat preview (CLEAN-10 US3) The admin bridle store now advertises the 'thinking' capability, folds incoming thinking events into per-turn blocks (freeze on terminal event, socket drop, or a 75s stale watchdog), and the chat preview renders the Rovo-style block: shimmer status line instead of the bouncing dots, collapsible step timeline with expandable markdown detail, interleaved with messages by timestamp. DebugPanel is untouched. Co-Authored-By: Claude Fable 5 --- .../bridle/components/bridle/Provider.vue | 177 ++++++++++++++++-- admin/slices/bridle/stores/bridle.ts | 110 ++++++++++- 2 files changed, 267 insertions(+), 20 deletions(-) diff --git a/admin/slices/bridle/components/bridle/Provider.vue b/admin/slices/bridle/components/bridle/Provider.vue index cf283aa3..89566c7f 100644 --- a/admin/slices/bridle/components/bridle/Provider.vue +++ b/admin/slices/bridle/components/bridle/Provider.vue @@ -1,15 +1,16 @@