From “What’s the Market?” to a Routed Decision
For CloddsBot, the interesting part of a user message is not only the words it contains but the route those words take before they reach a model or an exchange. This chapter follows that route through the gateway, session manager, command registry, and agent manager so the reader can see where identity, context, rate limits, and deterministic decisions enter. The result is a practical way to distinguish a conversational request from an external action.
Ingress turns a message into a stable request
CloddsBot accepts messages from several kinds of channels, but the application does not let each channel define its own meaning of a request. An adapter receives a platform-specific event and passes it into a common incoming-message shape. That normalization step gives later code a stable set of concepts: platform, user, chat, message identifier, chat type, text, timestamp, and optional thread or account information.
The benefit is easy to miss because normalization looks administrative. It is actually the first control over action. A Telegram update, a WebChat message, and a Slack event arrive with different payloads, identifiers, and delivery rules. If each path interpreted them independently, a later agent could not reliably tell whether two messages came from the same user, the same conversation, or two accounts on the same platform. A common shape does not make the channels identical, but it gives the gateway one place to decide what identity and conversation state should cross the boundary.
The gateway then applies a short-lived duplicate guard. It builds a key from the platform and message identifier, remembers recently processed keys, and suppresses a repeated event within the configured window. This is a modest mechanism with a high-stakes implication. Messaging systems retry deliveries, reconnect, and sometimes expose overlapping listeners. Without a deduplication boundary, a harmless repeated “show me the market” request would be inconvenient; a repeated request that eventually reaches an order tool could be consequential. Deduplication is not an authorization check and it cannot solve a genuinely repeated user instruction. It addresses a narrower problem: the same delivered event should not be interpreted twice merely because the transport delivered it twice.
After deduplication, CloddsBot can apply channel-level rate limiting. The gateway chooses a limiter for the platform and, depending on configuration, keys it globally or per user. A message that exceeds the limit receives a response rather than entering the rest of the pipeline. The channel adapter has another layer of protection for outbound communication: a token-bucket limiter controls send volume, while a circuit breaker can stop attempts after repeated failures. Health checks and reconnect logic address the fact that a messaging channel is an external service with its own availability and quotas.
These controls solve different problems. A rate limiter limits how quickly requests enter or leave. A circuit breaker stops repeatedly failing work. A health check asks whether a connection appears usable. Deduplication prevents a transport duplicate from becoming a second logical event. Treating them as interchangeable would create false confidence. A system can be healthy but receive an ambiguous request; it can be rate-limited but still be authorized to perform an unsafe action; it can reconnect successfully while a market feed remains stale.
Once the event passes these boundaries, the session manager finds or creates a session. CloddsBot’s default application scope isolates a direct conversation by channel and user, while group conversations receive their own key. The session stores the relationship between the message and the local conversation: the user, channel, chat, account, recent context, preferences, and timestamps. It is the owner of continuity for the agent turn. The gateway does not simply hand raw text to Claude and ask the model to infer which previous conversation it belongs to.
The next decision is whether the message is an explicit command. CloddsBot’s command registry is shared across channels, so a slash command can be recognized before the LLM is called. A request such as a market lookup or a safety-status command can follow a deterministic handler and return its result directly. The gateway sends that response back through the originating channel and ends the path. This is more than a shortcut. It reserves model interpretation for requests that need it, reduces unnecessary model cost and latency, and makes the behavior of selected operations easier to inspect.
If the command registry does not handle the message, the event becomes an agent turn. That branch is flexible, but it is not an unbounded jump from text to execution. The session and the normalized message travel together, so the agent manager can apply user-specific checks and assemble the context that belongs to that conversation.
The sequence shows why the gateway is a control boundary rather than a mere web server. It owns transitions between transport, identity, conversation, and interpretation. It also owns the decision to stop at a deterministic result or continue into a model-mediated turn. The channel remains responsible for delivery; the session manager remains responsible for continuity; the command registry remains responsible for explicit routing; and the agent manager receives a prepared request instead of reconstructing all of that context from scratch.
The agent turn adds context without owning everything
The agent manager begins by checking whether the session user is allowed to proceed. CloddsBot has an access-control layer with allowlists, blocklists, and optional pairing, while channel-specific policies can require a pairing step for direct messages. The effective result depends on configuration. In the default application setup, gateway and pairing options are not one universal switch, so an operator must understand which layer is active rather than assume that the presence of a pairing service means every endpoint is paired.
A second, per-user rate limit protects the agent turn itself. This limit is separate from the channel limiter because a message can be valid at the transport layer yet still be too frequent for model processing. The agent path can then trigger a before-message hook. Hooks may cancel or modify the message before the model sees it, which creates an extension point for policy, auditing, or preprocessing. It also creates a responsibility: a hook that changes text changes the request the model interprets, so its ownership and logs matter.
The manager next builds a trading context for the user. That context can contain the user’s available credentials, a session key, a maximum order size, and a dry-run setting. The important design choice is that handlers receive a context object rather than requiring every conversational turn to carry raw secrets in its prompt. Credentials are resolved at execution time and can be placed in a cooldown after repeated authentication failures. This is a boundary between the model’s reasoning surface and the data needed to call a venue, although it is not a complete secret-management solution by itself.
Conversation history is then combined with the current message. Earlier turns provide references such as “that market” or “the position we discussed,” but the history used for a model call is bounded. CloddsBot stores a fuller transcript separately and supplies a compact recent context, with summaries and memory available when the conversation grows. The result is not perfect recall. It is a deliberate compromise between continuity and a prompt that remains small enough to process. A user can have a long persisted conversation without every old message being present, verbatim, in every model request.
Memory adds another layer of context. The system can inject durable facts, preferences, notes, and relevant semantic matches into the agent’s dynamic context. It can also capture memory asynchronously after a turn, while filtering common credential and secret patterns. This makes the interaction feel less stateless, but the distinction between memory and authoritative state remains essential. A remembered preference can help interpret “use the conservative setting.” It should not be treated as proof of a current balance, open order, or settlement status. Those facts belong to live feeds, execution responses, and durable trading records.
The manager selects a model using the session’s override or the configured/adaptive model path. It constructs a system prompt from stable instructions plus dynamic skills and memory. Then the model can return text or request a tool. Tool calls are executed by the application, not by the model directly. The result is placed back into the conversation so the model can interpret it and decide whether it has enough information to answer or needs another tool. This loop is where CloddsBot’s conversational convenience meets its application boundary: the model chooses among capabilities, but the handler code owns what a capability actually does.
That distinction matters most for a trading request. The agent might use a market-search tool to resolve a question, an orderbook tool to inspect prices, a portfolio tool to read positions, and an execution tool to submit an order. Each result can change the next decision. A natural-language answer may end after the search. A trade-related turn can continue through several tool calls before the user sees a conclusion. The existence of the loop does not mean every tool is available or every action is permitted; capability loading, credentials, configured limits, and later execution gates still decide what happens.
CloddsBot can stream responses and, for supported channels, edit an earlier message as new text arrives. That improves the experience of a slow model or tool call, because the user sees progress instead of waiting for one opaque final response. It also introduces coordination work. The gateway must associate partial output with the right message, tolerate a channel that does not support editing, avoid sending updates too quickly, and ensure that a delivery failure does not change the underlying trade state. Streaming is therefore a presentation feature sitting on top of an asynchronous system, not evidence that the underlying action has completed.
The same principle applies to channel breadth and recovery. Every additional adapter brings its own connection lifecycle, rate limits, error categories, health checks, and reconnection behavior. A common interface reduces duplication, but it cannot remove differences in what a platform accepts or how quickly it confirms delivery. CloddsBot’s base adapter centralizes operational patterns while leaving platform-specific work in each adapter. That is a reasonable division of responsibility, but it means the system’s reliability is an emergent property of the shared layer, the individual adapter, and the external service.
At the end of the turn, CloddsBot saves the assistant response to the session and can capture selected durable memory asynchronously. The user experiences one conversation, but several state transitions have occurred: an event became a normalized message, a session was selected, a command or agent path was chosen, context was assembled, tools may have been called, and a response was delivered. The separation is valuable because each transition can be inspected and constrained. It is also the source of hidden coupling: a change to session scope affects context; a change to a hook affects interpretation; a change to an adapter affects delivery; a change to a handler affects external behavior.
A routed decision is therefore not yet a trade decision. The gateway has established who is speaking, which conversation they are in, and which software path will interpret the request. The next layer must decide how that path acquires specialized capabilities without presenting the model with an indiscriminate catalogue of executable actions.