LemonRouter

lemon_router owns request normalization, conversation identity, queue semantics, and semantic output tracking. It sits between channel transports and lemon_gateway.

Current Flow

Channel transport or gateway-native ingress
-> LemonRouter.Router.handle_inbound/1
-> LemonRouter.RunOrchestrator
-> LemonRouter.SessionCoordinator
-> LemonRouter.RunProcess
-> configured LemonCore.EngineRuntime.submit_execution/1
-> gateway runtime scheduler / thread worker / run
-> LemonCore.Bus run events
-> LemonRouter semantic coalescers / output tracking
-> LemonCore.DeliveryIntent
-> LemonChannels.Dispatcher
-> channel-specific renderer / outbox

Ownership

Key Modules

ModuleResponsibility
LemonRouter.RouterMain inbound entrypoint, session-key resolution, pending-compaction application, control-plane abort/keepalive hooks
LemonRouter.RunOrchestrator (internal)Builds router-owned submissions from LemonCore.RunRequest and hands them to SessionCoordinator
LemonRouter.SessionCoordinatorSingle owner of per-conversation queue semantics and active-run handoff
Router internal session read modelInternal read model over coordinator-owned active session state
LemonRouter.ConversationKeyCanonical conversation-key selection from structured resume or session key
LemonRouter.ResumeResolverStructured resume resolution before runtime submission
LemonRouter.RunProcessActive-run lifecycle wrapper around one execution
LemonRouter.MediaJobRecorderRecords generated final-answer files into redacted media job metadata before channel delivery
LemonRouter.ChannelsDelivery (internal)Narrow bridge from router-adjacent automation delivery requests into LemonChannels; must not construct OutboundPayload or own channel rendering
LemonRouter.StreamCoalescerSemantic answer coalescing that emits DeliveryIntent snapshots/finalization
LemonRouter.ToolStatusCoalescerSemantic tool-status coalescing that emits DeliveryIntent snapshots/finalization
LemonRouter.PendingCompactionStoreRouter-owned typed wrapper for pending-compaction markers
LemonRouter.AgentEndpointStoreRouter-owned typed wrapper for persistent endpoint aliases
LemonRouter.AgentInboxBEAM-local send API with selectors, fanout, and queue-mode selection
LemonRouter.AgentDirectoryActive/durable session discovery
LemonRouter.AgentEndpointsPersistent route aliases

SurfaceManager.finalize_answer/3 dispatches :stream_finalize first for runs that already streamed answer deltas, then finalizes StreamCoalescer state so late flushes cannot overwrite the final. For non-streamed completions it still finalizes through StreamCoalescer, with a direct :final_text fallback if the coalescer finalize call exits or times out. Completion-time artifact metadata enrichment is best-effort and must not block the final answer path. RunProcess.ArtifactTracker also supports Hermes-style final-answer media directives: a line containing MEDIA:<project-relative-path> is converted into an explicit auto_send_files entry after existing-file, cwd, and symlink escape checks pass. The directive line is removed from the final text before channel rendering, and Telegram/Discord delivery still goes through the normal attachment policy.

Important Contracts

Session And Queue Semantics

SessionCoordinator serializes by conversation key:

Queue-mode behavior lives here:

Output Semantics

Router coalescers only track semantic state:

Channels decides:

Testing

Run the app suite from the umbrella root:

mix test apps/lemon_router

Useful focused suites during refactors:

mix test apps/lemon_router/test/lemon_router/router_test.exs
mix test apps/lemon_router/test/lemon_router/run_orchestrator_test.exs
mix test apps/lemon_router/test/lemon_router/session_coordinator_test.exs
mix test apps/lemon_router/test/lemon_router/run_process_test.exs
mix test apps/lemon_router/test/lemon_router/stream_coalescer_test.exs
mix test apps/lemon_router/test/lemon_router/tool_status_coalescer_test.exs

Run architecture checks after boundary changes:

mix lemon.quality