May 17, 2026
Rebuilding the Wire: Steering, Sockets, and a Single Source of Config
The communication layer got rebuilt from the socket up, and mid-conversation steering finally became fully traceable.
Today was about pulling out load-bearing scaffolding that had quietly gone fragile and replacing it with something more honest about how data actually moves through Chalie. Three structural changes shipped together: steering got rebuilt as a real, auditable action; the WebSocket connection got demoted to what it should have been from day one, a server-to-client push channel; and the last per-agent config files got deleted in favor of one database row every model call reads from.
Steering — interjecting mid-response while Chalie is still thinking or acting — used to work by quietly appending a note to an internal queue and hoping the response loop noticed it before finishing. That’s an implicit path that breaks under load: a fast double-tap could start two turns instead of injecting one steer, and if the model’s last move produced no further actions, an injected steer could get dropped entirely, along with whatever you’d just said. The fix treats steering as a first-class action rather than a side channel: it’s now dispatched the same way any other tool call is, so it shows up in the same audit trail as everything else Chalie does, with a real record of what was said and when. The response loop’s exit condition was rewritten to account for these injected calls, closing the case where a message could vanish because the model had already moved on.
The WebSocket cleanup goes with it. For a while the socket carried both directions — chat going out and coming back over the same pipe as connection state and live updates — so every new feature had to reckon with an already-busy channel. As of today the socket only pushes: connection status, capability alerts, and a heartbeat. Sending a chat message, taking an action, or uploading a file now goes over plain HTTP, each with its own endpoint and request/response lifecycle. It’s a smaller surface with a clearer contract, and it removes a class of bugs where a slow or reconnecting socket could silently swallow something you sent.
The third piece, cutting the last per-agent JSON configuration files, closes out a running cleanup: every model call — chat, background reasoning, compaction, subagents — now resolves its provider from a single database row instead of a scattered set of files that could drift out of sync. Alongside it, internal usage labels got renamed to be purely observational rather than doubling as configuration keys, so background compaction no longer gets misattributed as user chat spend in the logs, and subagent and scheduled-task calls get their own labels instead of inheriting the wrong default.
The rest of the day was disciplined cleanup in the same spirit: a new internal/system distinction for infrastructure-level actions so steering never accidentally needs explicit policy approval (itself capable of hanging a request behind a permission prompt nobody could see); a single choke point for surfacing a locked-vault error back to the model instead of failing silently; and a broad pass through code complexity, naming consistency, and security hardening across backend and frontend, alongside pruning over a thousand lines of tests that exercised mocks rather than real behavior. None of it is visible on its own, but it’s the housekeeping that keeps bigger structural changes from rotting under their own weight.
None of this changes what Chalie can do today, but it changes how much I trust the plumbing while building the next layer on top of it. A comms stack with one obvious path per direction, and a config surface with exactly one place to look, is what makes it safe to keep building fast without second-guessing every wire underneath.
-
Steering is now a real, auditable action instead of a side-channel queue — no more dropped or misrouted mid-turn messages
-
WebSocket reduced to a pure server-to-client push channel; chat, actions, and uploads all move over HTTP
-
All model calls now resolve provider config from a single database source instead of scattered per-agent files
-
Internal/system actions are auto-excluded from policy prompts, preventing invisible permission hangs
-
Vault-locked errors now surface cleanly to the model via one dispatcher instead of failing silently