- Replace standalone no-reply-api Docker service with unified sidecar (services/main.mjs) that routes /no-reply/* and /moderator/* and starts/stops with openclaw-gateway - Add moderator Discord Gateway client (services/moderator/index.mjs) for real-time MESSAGE_CREATE push instead of polling; notifies plugin via HTTP callback - Add plugin HTTP routes (plugin/web/dirigent-api.ts) for moderator → plugin callbacks (wake-from-dormant, interrupt tail-match) - Fix tool registration format: AgentTool requires execute: not handler:; factory form for tools needing ctx - Rename no-reply-process.ts → sidecar-process.ts, startNoReplyApi → startSideCar - Remove dead config fields from openclaw.plugin.json (humanList, agentList, listMode, channelPoliciesFile, endSymbols, waitIdentifier, multiMessage*, bypassUserIds, etc.) - Rename noReplyPort → sideCarPort - Remove docker-compose.yml, dev-up/down scripts, package-plugin.mjs, test-no-reply-api.mjs - Update install.mjs: clean dist before build, copy services/, drop dead config writes - Update README, Makefile, smoke script for new architecture Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Dirigent Plugin
Hook strategy
message:receivedcaches a per-session decision from deterministic rules.before_model_resolveappliesproviderOverride + modelOverridewhen decision says no-reply.before_prompt_buildprepends end-marker instruction + scheduling identifier instruction when decision allows speaking.
Rules (in order)
- non-discord -> skip
- bypass sender -> skip
- end symbol matched -> skip
- else -> no-reply override
Config
See docs/CONFIG.example.json.
Required:
noReplyProvidernoReplyModel
Optional:
enabled(default true)discordOnly(default true)listMode(human-list|agent-list, defaulthuman-list)humanList(default [])agentList(default [])channelPoliciesFile(per-channel overrides in a standalone JSON file)schedulingIdentifier(default➡️) — moderator handoff identifierenableDirigentPolicyTool(default true)multiMessageStartMarker(default↗️)multiMessageEndMarker(default↙️)multiMessagePromptMarker(default⤵️)
Unified optional tool:
dirigent_tools- Discord actions:
channel-private-create,channel-private-update,member-list - Policy actions:
policy-get,policy-set-channel,policy-delete-channel - Turn actions:
turn-status,turn-advance,turn-reset
- Discord actions:
bypassUserIds(deprecated alias ofhumanList)endSymbols(default ["🔚"])enableDiscordControlTool(default true)- Discord control actions are executed in-plugin via Discord REST API (no
discordControlApiBaseUrlneeded) discordControlApiTokendiscordControlCallerIdenableDebugLogs(default false)debugLogChannelIds(default [], empty = all channels when debug enabled)
Per-channel policy file example: docs/channel-policies.example.json.
Policy file behavior:
- loaded once on startup into memory
- runtime decisions read memory state only
- direct file edits do NOT affect memory state
dirigent_toolspolicy actions update memory first, then persist to file (atomic write)
Moderator handoff format
When the current speaker NO_REPLYs, the moderator bot sends: <@NEXT_USER_ID>➡️
This is a non-semantic scheduling message. The scheduling identifier (➡️ by default) carries no meaning — it simply signals the next agent to check chat history and decide whether to speak.
Multi-message mode / shuffle mode
- Human sends the configured start marker (default
↗️) → channel enters multi-message mode. - While active, agents are forced to no-reply and the moderator sends only the configured prompt marker (default
⤵️) after each additional human message. - Human sends the configured end marker (default
↙️) → channel exits multi-message mode and normal scheduling resumes. - No separate moderator "entered/exited mode" confirmation message is sent; the markers themselves are the protocol.
- The first moderator message after exit uses the normal scheduling handoff format:
<@NEXT_USER_ID>➡️. /dirigent turn-shuffling,/dirigent turn-shuffling on, and/dirigent turn-shuffling offcontrol per-channel reshuffling between completed rounds.
Slash command (Discord)
/dirigent status
/dirigent turn-status
/dirigent turn-advance
/dirigent turn-reset
/dirigent turn-shuffling
/dirigent turn-shuffling on
/dirigent turn-shuffling off
Debug logging:
- set
enableDebugLogs: trueto emit detailed hook diagnostics - optionally set
debugLogChannelIdsto only log selected channel IDs