- TypeScript 78.2%
- Shell 21.8%
OpenAI系のツール/関数呼び出しはツール名を ^[a-zA-Z0-9_-]+$ でしか受け付けず、 daemon.status のようなドット入り名だとリクエスト全体が拒否される。他プロバイダ 経由では素通りしていたが、Responses APIを使うモデル(gpt-5.6-luna等)で顕在化した。 |
||
|---|---|---|
| dist | ||
| scripts | ||
| src | ||
| .gitignore | ||
| package-lock.json | ||
| package.json | ||
| README.md | ||
| tsconfig.json | ||
agmsg-daemon-plugin
OpenCode plugin that sets up a systemd auto-reply daemon for agmsg agents (cross-agent messaging via SQLite). One line in opencode.json → daemon running.
What this solves
You run multiple opencode agents (deepseek, mimo, etc.) that listen for inbound agmsg messages. Each needs a polling daemon that:
- Watches the agmsg SQLite inbox every 5 seconds
- Injects unread messages into a fresh
opencode runturn - Marks messages read and sends auto-ack
Without this plugin, you'd hand-write systemd unit files with correct paths, environment variables, and override.conf for every agent and every machine.
Typical use cases
Claude Code → opencode sub-agent
Claude sends a research task via agmsg → opencode daemon picks it up, runs the task, replies with results. No need to keep a terminal session open.
Multi-agent swarm
deepseek + mimo + other opencode agents running in parallel, each with its own daemon, all on the same agmsg team.
CI / ephemeral machines
Spin up a new dev machine → git clone agmsg && git clone this plugin → daemon running in 10 seconds.
Headless agents
Agents that run autonomously even when no one is logged into a terminal session.
Prerequisites
This plugin only automates the systemd wiring for agents that already exist in agmsg. It does not create agmsg teams or agents — that step must be done first, manually, with agmsg itself.
- Linux with systemd (
systemctl --usermust work) - agmsg installed at
~/.agents/skills/agmsg/ - The target agent has already joined a team as
codextype — runjoin.shyourself first (see Example workflow below). Without this, the plugin finds nothing and does nothing. opencodeCLI in PATH
Install
This package is not published to npm (no registry account needed / nothing to authenticate). Install it as a local file-based opencode plugin instead — clone the repo and point opencode.json at the built dist/index.js with a file:// URL. dist/ is already committed pre-built, so no build step is required.
git clone https://git.answer.mydns.jp/kaz/agmsg-daemon.git
cd agmsg-daemon/agmsg-daemon-plugin
// opencode.json (global: ~/.config/opencode/opencode.json,
// or project: .opencode/opencode.json)
{
"$schema": "https://opencode.ai/config.json",
"plugin": ["file:///absolute/path/to/agmsg-daemon-plugin/dist/index.js"]
}
Use an absolute path. Restart opencode. On load, the plugin scans ~/.agents/skills/agmsg/teams/*/config.json for agents registered as codex type (agents already joined via join.sh — see Prerequisites above), generates systemd units, and starts them.
Options
These options live entirely in opencode.json — they configure the daemon's own opencode run subprocess for the target agent (e.g. deepseek, mimo), and have nothing to do with Claude Code or the model Claude Code itself is running as.
{
"plugin": [
["file:///absolute/path/to/agmsg-daemon-plugin/dist/index.js", {
"models": {
"deepseek": "opencode-go/deepseek-v4-flash",
"mimo": "opencode-go/mimo-v2.5"
},
"agmsgDir": "/path/to/agmsg",
"storagePath": "/path/to/agmsg/db",
"disableAutoSetup": false
}]
]
}
| Key | Type | Description |
|---|---|---|
models |
Record<string, string> |
Per-agent model to pass as -m to opencode run |
agmsgDir |
string |
agmsg installation directory (default: ~/.agents/skills/agmsg) |
storagePath |
string |
Directory containing messages.db (default: <agmsgDir>/db) |
disableAutoSetup |
boolean |
Skip auto-setup on plugin load; use daemon_setup manually |
Custom tools
Tool names use underscores, not dots — OpenAI-style function/tool calling only
accepts ^[a-zA-Z0-9_-]+$ for tool names, and a dotted name gets the whole
request rejected with a 400 on models that go through the stricter Responses
API (found 2026-08-03 via gpt-5.6-luna on opencode-go).
| Tool | Description |
|---|---|
daemon_status |
Show status of all daemons (or specific agent with agent arg) |
daemon_start |
Start daemon for a specific agent |
daemon_stop |
Stop daemon for a specific agent |
daemon_restart |
Restart daemon for a specific agent |
daemon_remove |
Remove systemd unit for a specific agent |
daemon_setup |
(Re)run setup for all agents or a specific one |
Example workflow
1. Join agmsg team
bash ~/.agents/skills/agmsg/scripts/join.sh lab1 deepseek codex /home/user/project
2. Add plugin & restart
{ "plugin": ["file:///absolute/path/to/agmsg-daemon-plugin/dist/index.js"] }
Plugin auto-detects codex-type agents, generates systemd units, and starts them.
3. Verify
daemon_status
→ deepseek@lab1: RUNNING pid=247808
mimo@lab1: RUNNING pid=247807
4. Done
When an agmsg message arrives for deepseek@lab1, the daemon picks it up, runs opencode run, marks it read, and sends auto-ack.
What gets created
~/.config/systemd/user/agmsg-daemon-{agent}.service— systemd unit~/.config/systemd/user/agmsg-daemon-{agent}.service.d/override.conf— PROJECT_DIR, AGMSG_STORAGE_PATH<agmsgDir>/scripts/agmsg-daemon.sh— self-contained daemon script~/.config/opencode/agmsg-daemon/setup-state.json— tracks which project setup ran for
Uninstall
daemon_remove deepseek
daemon_remove mimo
Remove the file://...agmsg-daemon-plugin/dist/index.js entry from opencode.json's plugin array.
How it works
opencode restart
└─ plugin loads
└─ scans teams/*/config.json for codex agents
└─ for each agent:
├─ generate systemd unit
├─ copy daemon.sh to agmsg/scripts/
└─ systemctl --user enable --now
daemon.sh (5s loop)
├─ SELECT COUNT(*) FROM messages WHERE read_at IS NULL
├─ if unread > 0: opencode run "process these"
└─ mark read + INSERT auto-ack
The daemon is fully self-contained: it resolves the SQLite path directly (no dependency on agmsg's storage.sh or send.sh), and uses inline SQL INSERT for auto-ack.
License
MIT