Feature request: fire an existing local trigger over HTTP #5681
Replies: 1 comment
|
Thanks for the detailed proposal. I originally designed this as a webhook, then narrowed it down to local triggers intentionally. The idea is for nanobot to own the session binding, queueing, and execution, while developers choose how to invoke a trigger: HTTP, a script, or another event source. For this use case, a small local HTTP service that calls I'd prefer to keep the HTTP adapter outside nanobot for now rather than add a built-in endpoint. If building an adapter exposes gaps in the CLI that make integration difficult, I'd be interested in improving those. |
Uh oh!
There was an error while loading. Please reload this page.
Summary
Local triggers can be created, listed, renamed, paused, and deleted from the WebUI — but the only way to actually fire one is the
nanobot triggerCLI command. That makes triggers hard to use from anything that isn't a shell on the same host.I'd like to propose a small addition: an HTTP route on the existing API server that enqueues a delivery for an existing trigger. No new channel, no new server, no new config.
Why the CLI-only path is limiting
Triggers are the one mechanism that delivers a reply back through a real chat channel.
_deliver_delivery()innanobot/triggers/local_runner.pybuilds anInboundMessagefrom the trigger's storedchannel,chat_id, andsession_key, so a trigger created from a Telegram topic produces a reply in that Telegram chat, with that session's history. That is exactly the behavior an external integration wants.But firing one requires shell access to the gateway's host or container. In a Docker deployment that means either mounting the Docker socket into whatever service wants to send the message (root-equivalent on the host), running a sidecar purely to hold the CLI, or running a supervisor inside the nanobot container. All three are a lot of machinery around a queue write.
The gap, concretely
LocalTriggerStore.enqueue()innanobot/triggers/local_store.pyhas exactly one non-test caller: thetriggercommand innanobot/cli/commands.py. The WebUI automations routes cover the trigger lifecycle but not firing.Worth noting how little
enqueue()actually does — it writes one JSON file into<workspace>/triggers/inbox/under aFileLockand returns. It never contacts the gateway. So exposing it over HTTP doesn't require any new coupling between processes; the gateway keeps consuming the queue exactly as it does today, and the existing at-least-once semantics and audit records under<workspace>/triggers/runsare unchanged.Why the existing HTTP surface doesn't cover this
/v1/chat/completionsis a great programmatic entry point, but as documented indocs/openai-api.md, requests run in the syntheticapichannel, so replies don't reach a chat channel on their own. The documented workaround is to have the agent call themessagetool with an explicitchannelandchat_id.That works, but it's prompt-dependent rather than structural, and it puts delivery at the model's discretion on every call. There's at least one report of an
OutboundMessagebeing created with the rightchannelandchat_idwithout the platform send happening (Discussion #2764). A trigger has the target bound at creation time, so there's nothing for the model to get wrong.The two are complementary:
/v1/chat/completionsfor "call the agent and read the response," a trigger endpoint for "deliver this into an existing chat session."Proposed change
A single route on the app built in
nanobot/api/server.py:The handler resolves the workspace from
agent_loop.workspace(the same waynanobot/command/builtin.pydoes), constructs aLocalTriggerStore, and callsenqueue(). It reuses the existing Bearer-tokenauth_middlewareand the existing loopback guard in theservecommand that refuses a non-loopback bind without anapi_key, so it inherits the current security posture rather than introducing a second one.I have this running locally as roughly 50 added lines in one file, with no signature changes to
create_appor anything else. Happy to open a PR if the direction is welcome.Relationship to existing requests
There are several open threads about inbound HTTP (#1118 for a webhook receiver, #2602 for an HTTP streaming channel, PRs #722, #985, #1861 for various API/webhook channels), and the roadmap in Discussion #431 lists an HTTP server component as mid-term.
This proposal is deliberately much smaller and I don't think it competes with any of them. Those are about adding a new channel or a general-purpose webhook receiver — real architectural decisions with a design space to work through. This is just exposing an internal mechanism that already exists, on a server that already exists, for triggers that already have a lifecycle in the WebUI. If a full webhook channel eventually lands, this route stays useful for a different job.
Open questions
serveandgatewayare separate processes, and the file-based queue means it works from either. The API server is the smaller change; the gateway is arguably the more logical owner, and would avoid booting anAgentLoopjust to write a file.?wait=truemight be useful.api_key?All reactions