Appearance
Hooka Agent Bridge
hooka-agent-bridge is a standalone worker that keeps Hooka as the notification bus and delegates coding-agent execution to an external harness.
Hooka owns:
- topic history and SSE;
- Telegram/webhook routing;
- delivery retries and idempotency;
- scoped tokens and ACLs.
The harness owns:
- model credentials;
- sessions and memory;
- tool execution;
- workspace mutation.
Flow
text
Hooka input topic
-> hooka-agent-bridge
-> OpenCode or Pi
-> Hooka output topic
-> Telegram / webhook / SSE subscribersFor a voice-controlled coding loop, run hooka-hub on the Linux host next to your microphone/speaker. It records a command, transcribes it with a configured local command, publishes to the bridge input topic, waits for the bridge output topic, then speaks the result by TTS (hooka-hub --help lists its HOOKA_HUB_* settings).
Build
bash
go build -o hooka-agent-bridge ./cmd/hooka-agent-bridgeDocker:
bash
docker build -f Dockerfile.agent-bridge -t hooka-agent-bridge .Deploy
Run the bridge as its own service or container next to the Hooka server. Hooka remains the public web service; the bridge is a private worker that keeps an outbound SSE connection open to Hooka and publishes responses back to it. It does not listen on a port, so disable any HTTP healthcheck your platform applies by default. Build the image from the repo root (Dockerfile.agent-bridge) so the build can see go.mod, cmd/, and internal/.
Set these variables on the bridge service:
dotenv
HOOKA_URL=https://hooka.example.com
HOOKA_API_KEY=your-scoped-agent-bridge-token
HOOKA_INPUT_TOPIC=agent_in
HOOKA_OUTPUT_TOPIC=agent_out
HARNESS_BACKEND=opencode
OPENCODE_URL=http://your-opencode-service:4096Optional bridge variables:
dotenv
HOOKA_BATCH_LIMIT=100
HOOKA_STREAM_TAGS=
HOOKA_INCLUDE_PAYLOAD=false
HOOKA_SKIP_ASSISTANT_TAG=true
HOOKA_FORWARD_TELEGRAM_TARGET=false
OPENCODE_SERVER_USERNAME=
OPENCODE_SERVER_PASSWORD=
OPENCODE_AGENT=
OPENCODE_PROVIDER=
OPENCODE_MODEL=
OPENCODE_SESSION_TITLE=For OpenCode, run OpenCode as another service reachable from the bridge, then set OPENCODE_URL to that service URL. Prefer a private network between the two; otherwise use the public OpenCode URL with the Basic auth variables.
For Pi, the generic bridge image is not enough by itself because Dockerfile.agent-bridge only contains the bridge binary. Use a custom Dockerfile that starts from it and installs Pi and any persona files, then set:
dotenv
HARNESS_BACKEND=pi
PI_COMMAND=pi
PI_ARGS=--mode rpc --system-prompt /app/persona.md
PI_WORKDIR=/workspace
FIREWORKS_API_KEY=
# or DEEPSEEK_API_KEY=, OPENAI_API_KEY=, ANTHROPIC_API_KEY=The start command can stay empty because Dockerfile.agent-bridge defines the bridge ENTRYPOINT. If you override it to pass flags instead of variables, wrap it in a shell so the variables expand:
bash
sh -lc 'hooka-agent-bridge --backend "$HARNESS_BACKEND" --hooka-url "$HOOKA_URL"'After deploy, verify the bridge from Hooka:
bash
hooka publish agent_in \
--title "Bridge smoke" \
--text "Reply with a one-line status." \
--tags agent,user
hooka messages agent_out --limit 5Expected bridge logs include:
text
hooka-agent-bridge: starting backend=opencode input=agent_in output=agent_outHooka Setup
Create a scoped token that can read the input topic and publish to the output topic:
bash
curl -sS -X POST "$HOOKA_URL/tokens" \
-H "Authorization: Bearer $HOOKA_ADMIN_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name":"agent-bridge","can_read":true,"can_publish":true,"topic_pattern":"agent_*"}'Route output wherever you want responses:
bash
hooka route-add agent_out --provider telegram --chat-id "$TELEGRAM_CHAT_ID"Publish prompts to the input topic:
bash
hooka publish agent_in \
--title "Agent prompt" \
--text "Inspect the repo and summarize failing tests" \
--tags agent,userOpenCode Backend
Start OpenCode separately:
bash
opencode serve --hostname 0.0.0.0 --port 4096Run the bridge:
bash
hooka-agent-bridge \
--backend opencode \
--hooka-url "$HOOKA_URL" \
--hooka-api-key "$HOOKA_API_KEY" \
--input-topic agent_in \
--output-topic agent_out \
--opencode-url http://localhost:4096Optional OpenCode flags/env:
--opencode-username/OPENCODE_SERVER_USERNAME--opencode-password/OPENCODE_SERVER_PASSWORD--opencode-agent/OPENCODE_AGENT--opencode-provider/OPENCODE_PROVIDER--opencode-model/OPENCODE_MODEL--opencode-title/OPENCODE_SESSION_TITLE
Pi Backend
Run Pi as a long-lived RPC subprocess:
bash
hooka-agent-bridge \
--backend pi \
--hooka-url "$HOOKA_URL" \
--hooka-api-key "$HOOKA_API_KEY" \
--input-topic agent_in \
--output-topic agent_out \
--pi-command pi \
--pi-args "--mode rpc"You can pass a persona or specialized system prompt through Pi args:
bash
--pi-args "--mode rpc --system-prompt /app/persona.md"Control messages on the input topic:
/stop: abort current harness run./new: start a new harness session./status: publish current harness status.
Docker Smoke Test
The repo includes a Docker Compose smoke test that runs:
- a fake Hooka SSE/publish server;
- a fake OpenCode server;
- the compiled bridge image;
- a verifier that checks the output publish.
Run:
bash
./examples/agent-harness-bridge/docker-smoke.shThis validates the container wiring without requiring live Telegram, OpenCode, Pi, or model credentials.
Operational Notes
- Keep input and output topics separate to avoid feedback loops.
- Normal agent responses are published with a
finishedtag. Runner errors and responses that ask for user input are published withneeds_input, which lets clients (for example an iOS app using store-only iOS alerts) show a distinct interrupt. - Prefer scoped Hooka tokens over the master API key.
- Use deterministic Hooka idempotency keys; the bridge uses source message ids.
- For multi-session routing today, prefer topic-per-session because Hooka topic SSE exposes
StoredMessagefields, not raw payload metadata. - Run the bridge in a separate container, tmux session, or host process. Do not embed agent execution inside the Hooka server.