Skip to content

Remove message_received event and simplify office UI#183

Merged
jlia0 merged 1 commit intomainfrom
jlia0/doc-sse-events
Mar 11, 2026
Merged

Remove message_received event and simplify office UI#183
jlia0 merged 1 commit intomainfrom
jlia0/doc-sse-events

Conversation

@jlia0
Copy link
Collaborator

@jlia0 jlia0 commented Mar 9, 2026

Summary

Removed the redundant message_received event and simplified the office chat UI to display only messages (user inputs and agent responses). The event was fired immediately before agent_routed and added no new information. The office page activity bar has been replaced with a cleaner message-only interface.

Changes

  • Remove message_received event emission from queue-processor
  • Update all frontend consumers (office page, logs page, API, visualizer, event dots) to use message_enqueued instead
  • Strip down office page to show only user messages and final responses as speech bubbles
  • Remove status bar activity feed and associated UI/state
  • Add comprehensive SSE-EVENTS.md documentation covering all 9 events with field details and lifecycle diagrams

Type of Change

  • Enhancement to existing feature
  • Documentation update
  • Refactor / code cleanup

Testing

  • Event stream still broadcasts 9 core events (message_enqueued, agent_routed, chain_step_start, chain_step_done, team_chain_start, chain_handoff, team_chain_end, response_ready, processor_start)
  • Office page renders messages from both events correctly

Checklist

  • No new TypeScript errors introduced
  • Documentation added for SSE events
  • Removed unused code (StatusEvent interface, statusColor function)

@greptile-apps
Copy link

greptile-apps bot commented Mar 9, 2026

Greptile Summary

This PR removes the redundant message_received SSE event (which fired immediately before agent_routed with no additional information) and replaces all consumer references with message_enqueued. It also strips the office page down to a cleaner two-bubble interface (user message + final agent response) and adds comprehensive SSE-EVENTS.md documentation.

Key changes:

  • src/queue-processor.ts: removes the emitEvent('message_received', …) call; message_enqueued is now emitted exclusively from the API route (src/server/routes/messages.ts), which all channel clients (WhatsApp, Telegram, Discord) already go through
  • tinyoffice/src/app/office/page.tsx: removes StatusEvent interface, statusColor helper, the status-bar UI, and chain_step_done bubble logic; replaces with message_enqueued (user bubble) and response_ready (agent bubble)
  • Bug: the response_ready guard if (msg && agentId) silently drops team conversation responses — agentId is not present in team response_ready events (confirmed in src/lib/conversation.ts), so the office page will never render a speech bubble for team replies
  • All other consumer updates (logs/page.tsx, page.tsx, team-visualizer.tsx, api.ts) are straightforward renames with no issues
  • docs/SSE-EVENTS.md is a well-structured new reference document; field tables match the implementation

Confidence Score: 3/5

  • Safe to merge for solo-agent setups, but introduces a regression that silently hides team conversation responses in the office UI.
  • The event rename itself is clean and consistent across all consumers. However, the simplified office page has a clear logic bug: response_ready events from team conversations omit agentId, so the if (msg && agentId) guard will always fail, leaving the office page blank after any team response. This is a functional regression for any user with team configurations.
  • tinyoffice/src/app/office/page.tsx — team response_ready bubble guard drops team replies; consider also src/lib/conversation.ts if fixing at the emission side.

Important Files Changed

Filename Overview
src/queue-processor.ts Correctly removes the redundant message_received emitEvent call. The message_enqueued event is now the canonical first-event in the lifecycle and is emitted from the API layer (messages.ts) for all channels including WhatsApp, Telegram, and Discord.
tinyoffice/src/app/office/page.tsx Removes status bar and simplifies the office page to show only user and agent speech bubbles. Contains a logic bug: team response_ready events lack agentId, causing the guard if (msg && agentId) to always fail for team conversations, so team final responses are never rendered.
tinyoffice/src/lib/api.ts Correctly removes message_received from the SSE listener list and keeps message_enqueued in the list of subscribed event types.
src/visualizer/team-visualizer.tsx Correctly updates the switch case from message_received to message_enqueued. The event payload fields (channel, sender, message) are all present in the new event, so the log line is unaffected.
docs/SSE-EVENTS.md New documentation file comprehensively covering all 9 SSE events with field tables, type annotations, and lifecycle diagrams for both solo and team message flows. Accurate and well-structured.

Sequence Diagram

sequenceDiagram
    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
Loading

Last reviewed commit: 91cfddf

Comment on lines +231 to 244
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));
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 responses

Solo 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:

Suggested change
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.

Copy link
Collaborator

@mczabca-boop mczabca-boop left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found two blocking issues in this PR:

  1. 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 for response_ready when agentId is present. However, team conversations emit response_ready without agentId from src/lib/conversation.ts, so the final team response is never rendered. I reproduced this manually: solo agent replies show up in /office, but @solo-team replies do not.

  2. Medium: this removes message_received from the SSE stream without a compatibility path. The queue processor no longer emits it, and the docs now treat message_enqueued as the replacement. Any existing SSE consumer or plugin listening for message_received will 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:

  1. New Chat appears 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.

  2. The Office view 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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants