Architecture
The platform swaps and the brain swaps, but the core is one thing — with a human-held line in between.
An adapter has four obligations
A platform adapter owes exactly four things — normalize what came in, decide authorization, hand it to a job, return the answer.
Past that, the core has no idea whether it’s talking to Slack or Discord. Which is why adding Discord meant writing one adapter.
Four is the number that matters. Three and authorization leaks into the core; five and something platform-specific seeps in. A boundary must be neither wide nor narrow, and its width is set by how many things genuinely differ per platform.
Adding Discord surfaced one porting bug. The org system was checking user identifiers with a regex hardcoded to Slack’s format, and it rejected every one of Discord’s numeric identifiers. A place where the platform had seeped into a core I believed was platform-neutral.
The brain swaps too
The CLI that produces responses is swappable. The method is unusual: it spawns a CLI that is already logged in on this PC.
So whichever one you pick, the billing stays flat-rate. Plugging in API keys would mean the cost model changes every time you switch providers; calling a logged-in CLI means that person’s subscription just applies.
Sessions are filed per provider, because a session identifier is a receipt from that agent’s own store and mixing them is meaningless. Switch back and forth and both conversations survive.
There’s one exception: guest requests, and anything from the owner outside a DM, always go to the default brain. The permission rules are a file in that CLI’s format, and the others can’t carry them. If the safety mechanism can’t follow, neither should the switch.
What a dead provider taught
One provider closed off personal accounts — free tier and paid subscription alike.
The symptom was nasty. Login succeeds, and the first request dies. So it reads as “I configured it, why doesn’t it work.”
Filling that gap with a successor CLI meant stepping on three more traps.
- An argument that looks like the stdin idiom discarded stdin. Passing a
-p -shape made it take-as the prompt string and ignore standard input. A greeting came back. - A dead conversation id wasn’t an error. It warns, self-recovers into a new conversation, and hands back a new id. So what was needed wasn’t retry logic but code that saves the returned id.
- Installation put PATH only in the registry. An already-running worker can’t see that. Without a fallback path you get “I installed it and nothing happened.”
All three belong to the silent failure family. Not one of them throws.