Remove message_received event and simplify office UI#183
Conversation
Greptile SummaryThis PR removes the redundant Key changes:
Confidence Score: 3/5
Important Files Changed
Sequence DiagramsequenceDiagram
participant C as Channel Client<br/>(WA/TG/Discord/API)
participant API as POST /api/message
participant QP as Queue Processor
participant SSE as SSE Stream
participant UI as Office UI
C->>API: POST /api/message {message, sender, channel}
API->>SSE: message_enqueued {messageId, agent, channel, sender, message}
SSE-->>UI: bubble: _user_{sender}
API-->>C: {ok: true, messageId}
QP->>SSE: agent_routed {agentId, agentName, provider, model}
QP->>SSE: chain_step_start {agentId, agentName, fromAgent}
Note over QP: invokeAgent(...)
QP->>SSE: chain_step_done {agentId, responseText, responseLength}
alt Solo agent
QP->>SSE: response_ready {channel, sender, agentId, responseText, messageId}
SSE-->>UI: bubble: agentId ✅
else Team conversation
QP->>SSE: team_chain_start / chain_handoff / team_chain_end
QP->>SSE: response_ready {channel, sender, responseText, messageId}
Note over UI: ⚠️ agentId missing → bubble skipped
end
Last reviewed commit: 91cfddf |
| if (event.type === "response_ready") { | ||
| const msg = (e.responseText as string) || ""; | ||
| if (msg && agentId) { | ||
| const targets = extractTargets(msg); | ||
| const bubble: SpeechBubble = { | ||
| id: `${event.timestamp}-${Math.random().toString(36).slice(2, 6)}`, | ||
| agentId: `_user_${sender}`, | ||
| agentId, | ||
| message: msg, | ||
| timestamp: event.timestamp, | ||
| targetAgents: targets, | ||
| }; | ||
| setBubbles((prev) => [...prev, bubble].slice(-50)); | ||
| } | ||
| } |
There was a problem hiding this comment.
Team responses silently dropped in office UI
The condition if (msg && agentId) means that any response_ready event emitted by a team conversation will never produce a speech bubble. Looking at src/lib/conversation.ts line 223, completeConversation emits response_ready without an agentId:
emitEvent('response_ready', { channel, sender, responseLength, responseText, messageId });
// ^^^ no agentId for team responsesSolo agent responses (in queue-processor.ts line 221) include agentId, but team responses do not. SSE-EVENTS.md even documents this: "agentId — Responding agent (solo only)".
As a result, whenever a team conversation completes, agentId is undefined, the guard fails, and no bubble is ever rendered. This is a regression compared to the previous code, which used chain_step_done events (which always carry agentId) to surface agent speech bubbles.
A minimal fix is to add a fallback when agentId is absent:
| if (event.type === "response_ready") { | |
| const msg = (e.responseText as string) || ""; | |
| if (msg && agentId) { | |
| const targets = extractTargets(msg); | |
| const bubble: SpeechBubble = { | |
| id: `${event.timestamp}-${Math.random().toString(36).slice(2, 6)}`, | |
| agentId: `_user_${sender}`, | |
| agentId, | |
| message: msg, | |
| timestamp: event.timestamp, | |
| targetAgents: targets, | |
| }; | |
| setBubbles((prev) => [...prev, bubble].slice(-50)); | |
| } | |
| } | |
| // Agent/team final response | |
| if (event.type === "response_ready") { | |
| const msg = (e.responseText as string) || ""; | |
| const resolvedAgentId = agentId ?? "team"; | |
| if (msg) { | |
| const targets = extractTargets(msg); | |
| const bubble: SpeechBubble = { | |
| id: `${event.timestamp}-${Math.random().toString(36).slice(2, 6)}`, | |
| agentId: resolvedAgentId, | |
| message: msg, | |
| timestamp: event.timestamp, | |
| targetAgents: targets, | |
| }; | |
| setBubbles((prev) => [...prev, bubble].slice(-50)); | |
| } | |
| } |
Alternatively, the agentId field could be added to the team response_ready emit in conversation.ts (e.g. the leader agent's ID), which would let the correct sprite be positioned on screen.
mczabca-boop
left a comment
There was a problem hiding this comment.
I found two blocking issues in this PR:
-
High: team replies no longer appear in the Office view. In
tinyoffice/src/app/office/page.tsx, the new UI only renders a response bubble forresponse_readywhenagentIdis present. However, team conversations emitresponse_readywithoutagentIdfromsrc/lib/conversation.ts, so the final team response is never rendered. I reproduced this manually: solo agent replies show up in/office, but@solo-teamreplies do not. -
Medium: this removes
message_receivedfrom the SSE stream without a compatibility path. The queue processor no longer emits it, and the docs now treatmessage_enqueuedas the replacement. Any existing SSE consumer or plugin listening formessage_receivedwill silently stop receiving inbound-user events after upgrade. If this rename is intentional, it should either keep a temporary alias or be called out as a breaking change.
Additional UX issues noticed during manual testing, likely pre-existing rather than introduced by this PR:
-
New Chatappears to be ephemeral. After starting a conversation, navigating away, and returning, the chat view behaves like a fresh session and I could not find a UI entry point for reopening previous chats/history. -
The
Officeview cannot be used together with chat. The office scene is on its own route, while chat input lives on separate routes, so starting a chat navigates away from the office scene. In practice this makes the office visualization much less useful during active conversations.
- Simplify office page to messages-only (message_enqueued + response_ready) - Remove status bar activity feed, add compact chat input - Fix team response_ready missing agentId (now includes leader_agent) - Add SSE-EVENTS.md documenting all 10 events with payloads and lifecycle Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
552acaa to
527c6a0
Compare
Summary
Removed the redundant
message_receivedevent and simplified the office chat UI to display only messages (user inputs and agent responses). The event was fired immediately beforeagent_routedand added no new information. The office page activity bar has been replaced with a cleaner message-only interface.Changes
message_receivedevent emission from queue-processormessage_enqueuedinsteadType of Change
Testing
message_enqueued,agent_routed,chain_step_start,chain_step_done,team_chain_start,chain_handoff,team_chain_end,response_ready,processor_start)Checklist