Skip to content

Extract stateless message handling into AgentMessageHandler (step 4a)#192

Merged
shellicar merged 2 commits intomainfrom
feature/agent-message-handler
Apr 6, 2026
Merged

Extract stateless message handling into AgentMessageHandler (step 4a)#192
shellicar merged 2 commits intomainfrom
feature/agent-message-handler

Conversation

@shellicar
Copy link
Copy Markdown
Owner

What

Seven SdkMessage cases move out of the port.on switch in runAgent.ts into AgentMessageHandler.handle().

Moved cases

query_summary, message_thinking, message_text, message_compaction_start, message_compaction, done, error

Stays in runAgent

Three stateful cases remain in the switch with explicit case labels:

  • tool_approval_request — async approval flow + usageBeforeTools snapshot
  • tool_error — moving with the tool cases in 4b
  • message_usage — delta annotation + lastUsage accumulator

Everything else falls to default: handler.handle(msg).

Known temporary regression

The [compacted at X/Y (Z%)] context-usage annotation on message_compaction is temporarily absent. It needs lastUsage, which is set by message_usage — a step 4b case. Compaction only fires at 150k input tokens so this is unlikely to surface in normal use. The annotation is restored in 4b.

Testing

16 unit tests covering each message type: block transition target, streamed text content, conditional streaming (done/end_turn), and query_summary parts formatting including the optional thinking-block count.

Seven SdkMessage cases move from the port.on switch in runAgent.ts into
AgentMessageHandler.handle(): query_summary, message_thinking,
message_text, message_compaction_start, message_compaction, done, error.

The switch in runAgent retains explicit cases for the three stateful
ones: tool_approval_request (async approval flow + usageBeforeTools
snapshot), tool_error, and message_usage (delta annotation + lastUsage
accumulator). Everything else falls to the default which calls
handler.handle(msg).

Known temporary regression: the 'compacted at X/Y (Z%)' context-usage
annotation on message_compaction is gone. It needs lastUsage, which
is set by message_usage — a step 4b case. The annotation comes back
when 4b lands.

16 tests cover each message type: block transition, streamed text,
conditional streaming (done/end_turn), and the query_summary parts
formatting.
@shellicar shellicar added this to the 1.0 milestone Apr 6, 2026
@shellicar shellicar added the enhancement New feature or request label Apr 6, 2026
@shellicar shellicar self-assigned this Apr 6, 2026
@shellicar shellicar requested a review from bananabot9000 April 6, 2026 11:12
@shellicar shellicar enabled auto-merge (squash) April 6, 2026 11:12
Copy link
Copy Markdown
Collaborator

@bananabot9000 bananabot9000 left a comment

Choose a reason for hiding this comment

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

Stateless router, clean extraction 🍌

AgentMessageHandler is truly stateless -- two constructor-injected dependencies (#layout, #logger), no mutable fields, no accumulators. Pure message routing: event in, layout/logger calls out.

7 cases moved verbatim from runAgent.ts: query_summary, message_thinking, message_text, message_compaction_start, message_compaction, done, error. All replaced with default: handler.handle(msg) in runAgent. Net -19 lines from the switch.

Documented regression is well-scoped: lastUsage compaction annotation deliberately left behind (stateful, depends on message_usage accumulator). Tracked for 4b. The right call -- dragging it in would break the stateless contract.

16 tests across 7 describe blocks. All branches covered. Test helpers are minimal. expected/actual pattern consistent with codebase conventions.

Observations (not blocking):

  • done doesn't call transitionBlock -- correct (response block already active), but means stop annotation appends to whatever block was last active. Worth noting for 4b.
  • Handler has no default case in its switch -- unhandled types silently no-op. Fine now, consider exhaustive check after 4b when more cases move over.

Good seam for the next step. The remaining runAgent cases (tool_approval_request, tool_error, message_usage) are the stateful ones that belong together in 4b.

Ship it 🚢🍌

@shellicar shellicar merged commit 81265cf into main Apr 6, 2026
4 checks passed
@shellicar shellicar deleted the feature/agent-message-handler branch April 6, 2026 11:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants