When the turn manager determines it's an agent's turn (checkTurn.allowed),
the rules engine's evaluateDecision() could still override the model to
no-reply with reason 'rule_match_no_end_symbol'. This happened because:
1. The sender of the triggering message (another agent) was not in the
humanList, so the rules fell through to the end-symbol check.
2. getLastChar() operates on the full prompt (including system content
like Runtime info), so it never found the end symbol even when the
actual message ended with one.
Fix: return early from before_model_resolve after the turn check passes,
skipping the rules-based no-reply override entirely. The turn manager is
the authoritative source for multi-agent turn coordination.
Tested: 3-agent counting chain ran successfully (3→11) with correct
NO_REPLY handling when count exceeded threshold.
Problems fixed:
1. before_message_write treated empty content (isEmpty) as NO_REPLY.
Tool-call-only assistant messages (thinking + toolCall, no text)
had empty extracted text, causing false NO_REPLY detection.
In single-agent channels this immediately set turn to dormant,
blocking all subsequent responses for the entire model run.
2. Added explicit toolCall/tool_call/tool_use detection in
before_message_write — skip turn processing for intermediate
tool-call messages entirely.
3. no-reply-api/server.mjs: default model name changed from
'dirigent-no-reply-v1' to 'no-reply' to match the configured
model id in openclaw.json, fixing model list discovery.
Changes:
- plugin/hooks/before-message-write.ts: toolCall detection + remove isEmpty
- plugin/hooks/message-sent.ts: remove isEmpty from wasNoReply
- no-reply-api/server.mjs: fix default model name
- dist/dirigent/index.ts: same fixes applied to monolithic build
- dist/no-reply-api/server.mjs: same model name fix
- Add waitIdentifier config (default: 👤) to DirigentConfig and plugin schema
- Prompt injection: tells agents to end with 👤 when they need a human reply,
warns to use sparingly (only when human is actively participating)
- Detection in before_message_write and message_sent hooks
- Turn manager: new waitingForHuman state
- checkTurn() blocks all agents when waiting
- onNewMessage() clears state on human message
- Non-human messages ignored while waiting
- resetTurn() also clears waiting state
- All agents routed to no-reply model during waiting state
- Update docs (FEAT.md, CHANGELOG.md, TASKLIST.md, README.md)
Feature 1: Split dirigent_tools
- Replace monolithic dirigent_tools (9 actions) with 9 individual tools
- Discord: dirigent_channel_create, dirigent_channel_update, dirigent_member_list
- Policy: dirigent_policy_get, dirigent_policy_set, dirigent_policy_delete
- Turn: dirigent_turn_status, dirigent_turn_advance, dirigent_turn_reset
- Extract shared executeDiscordAction() helper
Feature 2: Human @mention override
- When humanList user @mentions agents, temporarily override turn order
- Only mentioned agents cycle, ordered by their turn order position
- Original order restores when cycle returns to first agent or all NO_REPLY
- New: setMentionOverride(), hasMentionOverride(), extractMentionedUserIds()
- New: buildUserIdToAccountIdMap() for reverse userId→accountId resolution
Bump version to 0.3.0
- Use import.meta.url instead of api.resolvePath('.') to get script directory
- Add debug logging for no-reply-api and moderator bot startup
- Copy no-reply-api to dist during installation
- Replace import.meta.url with api.resolvePath('.') for reliable path resolution
- Fixes no-reply API not starting due to incorrect pluginDir calculation
- Added gateway_start hook: spawns no-reply API as child process, then starts moderator bot
- Added gateway_stop hook: kills no-reply API process, stops moderator bot
- No-reply API server.mjs is located relative to plugin dir via import.meta.url
- Moderator presence moved from register() to gateway_start for proper lifecycle
- Fix enableWhispergatePolicyTool → enableDirigentPolicyTool in config schema and example
- Fix whisper-gateway → dirigentway in install script
- Add v0.2.0 changelog entry
- Improve README with scheduling identifier docs and English text
- Clean up plugin README with moderator handoff format docs
- Reformat TASKLIST with cleaner done markers
- Task 1: Identity prompt now includes Discord userId
- Task 2: Added configurable schedulingIdentifier (default: ➡️)
- Task 3: Moderator handoff uses <@userId>+identifier instead of semantic messages
- Task 4: All prompts/comments/help text converted to English
- Task 5: Full project rename WhisperGate → Dirigent across all files
Breaking: config key changed from plugins.entries.whispergate to plugins.entries.dirigent
Breaking: channel policies file renamed to dirigent-channel-policies.json
Breaking: tool name changed from whispergate_tools to dirigent_tools