7.2 KiB
gori-agent
Multi-IM gateway that drives coding agents through the official Agent Client Protocol (ACP):
IM Adapter -> Gateway -> Role/Session Manager -> ACP Client -> kimi acp
The MVP backend is Kimi Code ACP. Gateway code contains no Kimi CLI prompt fallback and no Pi/Codex private protocol. Future agents must enter through an ACP adapter such as codex-acp or a Pi ACP adapter.
Requirements and quick start
Requires Node.js 20+ and an authenticated Kimi Code installation.
npm install
npm run build
./install.sh
gori-agent setup
gori-agent doctor --config ./config.json
gori-agent start --config ./config.json
setup probes ACP backends, creates an assistant role, optionally creates the ops role, preserves all existing platform settings, prints a migration summary, and only writes after confirmation. A v1 config is accepted at runtime only when its default agent is Kimi; other CLI agents require an explicit ACP backend.
Convenience wrapper commands:
./gori-agent.sh start
./gori-agent.sh start-daemon
./gori-agent.sh status
./gori-agent.sh logs
./gori-agent.sh stop
stop sends SIGTERM and waits up to 10 seconds for graceful shutdown. For production, use systemd with Restart=always and KillMode=control-group so an unexpected gateway crash also cleans up ACP children.
CLI
gori-agent setup [--config path]
gori-agent discover-backends [--json]
gori-agent start [--config path]
gori-agent status [--config path]
gori-agent doctor [--config path]
gori-agent print feishu [--config path]
discover-agents remains a deprecated alias for discover-backends. Discovery does not send a prompt. Kimi is ready when kimi acp is available; Codex and Pi report needs-adapter unless their ACP adapter executable exists.
Roles, sessions, and commands
A conversation binding is keyed by platform + chatId + role. Each binding stores the ACP backend, native session ID, workspace, and role fingerprint. The selected role and bindings survive gateway restart.
- A new session receives a hidden bootstrap prompt containing persona, workspace, skill content, and policy. The binding is saved only after bootstrap succeeds.
- A role persona, workspace, policy, or skill-content change changes its fingerprint and causes a new native session.
- Idle workers are stopped and later cold-resumed with
session/resume(orsession/loadwhen resume is unavailable). - Failed prompts are never replayed automatically.
/newcancels the current turn, unbinds the current chat/role, and creates a native session on the next prompt. It does not delete Kimi history./cancelbypasses the per-chat lock and sends ACPsession/cancel; an unresponsive worker is force-recycled after the grace period.
Chat commands:
/help/roles/role <id>/status/cancel/new/agentsand/agent <id>(deprecated aliases)
Messages in one chat are serialized; different chats run concurrently.
Configuration v2
See config.example.json. Important sections:
backends[]: ACP spawn command and arguments. The default is/home/ubuntu/.kimi-code/bin/kimi acp.roles[]: backend, absolute workspace, persona, skills, and permission policy.skills[]: readable skill files with a byte limit. Skill contents participate in the role fingerprint.acp.stateFile: defaults to$GORI_AGENT_HOME/state/acp-sessions.json.acp.initializeTimeoutMs,promptTimeoutMs,cancelGraceMs: request lifecycle limits.acp.idleTimeoutMs,sweepIntervalMs,maxProcesses: worker pool limits.policy.allowedUsers,allowedChats,requireMentionInGroup: inbound IM policy.platforms: Feishu, WeCom, QQ, generic webhook, and Weixin settings. Migration preserves this object, including credentials.
State writes use a serial promise queue, temporary file, fsync, and atomic rename. A single-instance lock protects the state file. Corrupt state is preserved and startup fails explicitly instead of overwriting it.
Permission policy
ACP permission requests are decided by role policy, not by persona:
deny: reject every permission request.allowlist: require both an allowed tool name and, for bash/terminal, a matching raw command pattern. If the ACP request lacks enough command detail, it is denied.auto: approve everything;doctorprints a high-risk warning.
The example ops role loads the existing read-only skill file at /home/ubuntu/gori-space/gori-deploy/.kimi-code/skills/gori-update/SKILL.md. Its allowlist covers selected git, build/deploy entry scripts, and read-only Docker status/log commands. sudo, force push, and arbitrary deletion are not allowed. Script-internal operations cannot be inspected by ACP once an explicitly allowed deployment script starts, so script paths must remain trusted.
Kimi 0.36.1 does not advertise a generic model config option during ACP initialize; this MVP uses the model configured as Kimi's default and doctor reports that limitation.
Platform behavior
| Platform | Inbound | Outbound | Notes |
|---|---|---|---|
| Feishu/Lark | Implemented | Implemented | im.message.receive_v1 and message reply API. |
| WeCom | 501 scaffold | Implemented | Inbound verification/encryption is not implemented. |
| Weixin | External webhook scaffold | Synchronous | Native personal WeChat is not included. |
| Implemented | Implemented | WebSocket gateway and HTTP callback modes. | |
| Generic webhook | Implemented | Synchronous JSON | HMAC-SHA256 signed JSON. |
QQ WebSocket mode uses intent 33554432 for GROUP_AT_MESSAGE_CREATE and C2C_MESSAGE_CREATE. The adapter supports top-level and nested author.user_openid fields, preserves callback ACK { "op": 12 } even if ACP work later fails, and logs receive/send routing. No external QQ message is sent by the automated tests.
Endpoints:
GET /health— aggregate ACP counters only; native session IDs are not exposed.GET /platformsPOST /webhook/feishuPOST /webhook/wecomPOST /webhook/qqPOST /webhook/genericPOST /webhook/weixin
Generic webhook payload:
{
"chat_id": "demo-chat",
"user_id": "demo-user",
"text": "/status",
"message_id": "optional",
"is_group": false,
"mentions_bot": true
}
When platforms.webhook.secret is set, provide X-Gori-Signature: sha256=<HMAC-SHA256 hex> over the exact JSON body.
Lifecycle and diagnostics
Shutdown order is QQ WebSocket stop, active ACP cancellation, worker termination, state flush/unlock. SIGINT and SIGTERM share the same idempotent shutdown path. Health statistics include active workers, in-flight turns, worker crashes, persisted bindings, and locked chats.
doctor checks role/workspace/skill validity, ACP initialize and restore capabilities, state directory access, permission warnings, configured platforms, and QQ requirements. It performs no prompt and no external QQ test.
Development
npm run typecheck
npm test
npm run build
git diff --check
Tests use Node node:test through tsx. The fake ACP subprocess covers initialize/new/resume/prompt/cancel, persistence, idle cold resume, Gateway commands, and QQ normalization/ACK behavior.
Legacy src/agents/* source remains only for compatibility/reference and is not imported by Gateway or Server. There is no runtime CliAgent fallback.