fix: implement turn gate and handoff improvements #8

Merged
hzhang merged 6 commits from fix/turn-gate-and-handoff into feat/turn-based-speaking 2026-03-02 10:22:12 +00:00
Collaborator

Changes

1. Fix channelId priority

  • Use ctx.channelId > conv.chat_id > conv.channel_id
  • Fixes turn gate not working due to empty/wrong channelId

2. Add sessionAllowed state

  • Track if session was allowed to speak (true) or forced no-reply (false)
  • Used to distinguish between forced NO_REPLY and real NO_REPLY

3. Add sessionInjected Set

  • Implement one-time prependContext injection
  • Prevents repeated injection on every user message

4. Add before_message_write hook

  • Detect NO_REPLY and handle turn advancement:
    • forced no-reply: do NOT advance turn
    • allowed + NO_REPLY: advance turn + trigger handoff
    • dormant state (all agents NO_REPLY): do NOT trigger handoff

5. message_sent

  • Continues to handle real messages with end symbols
  • NO_REPLY no longer depends on this hook

Design Consensus with Orion

  • Confirmed NO_REPLY is session-level sentinel, not Discord message
  • before_message_write ensures correct turn → handoff ordering
  • Prevents b receiving message before turn is updated
## Changes ### 1. Fix channelId priority - Use ctx.channelId > conv.chat_id > conv.channel_id - Fixes turn gate not working due to empty/wrong channelId ### 2. Add sessionAllowed state - Track if session was allowed to speak (true) or forced no-reply (false) - Used to distinguish between forced NO_REPLY and real NO_REPLY ### 3. Add sessionInjected Set - Implement one-time prependContext injection - Prevents repeated injection on every user message ### 4. Add before_message_write hook - Detect NO_REPLY and handle turn advancement: - forced no-reply: do NOT advance turn - allowed + NO_REPLY: advance turn + trigger handoff - dormant state (all agents NO_REPLY): do NOT trigger handoff ### 5. message_sent - Continues to handle real messages with end symbols - NO_REPLY no longer depends on this hook ## Design Consensus with Orion - Confirmed NO_REPLY is session-level sentinel, not Discord message - before_message_write ensures correct turn → handoff ordering - Prevents b receiving message before turn is updated
zhi added 2 commits 2026-02-28 21:45:11 +00:00
This reverts commit 6a81f75fd0, reversing
changes made to 86fdc63802.
1. Fix channelId priority: use ctx.channelId > conv.chat_id > conv.channel_id
2. Add sessionAllowed state: track if session was allowed (true) or forced no-reply (false)
3. Add sessionInjected Set: implement one-time prependContext injection
4. Add before_message_write hook: detect NO_REPLY and handle turn advancement
   - forced no-reply: don't advance turn
   - allowed + NO_REPLY: advance turn + handoff
   - dormant state: don't trigger handoff
5. message_sent: continues to handle real messages with end symbols
zhi added 1 commit 2026-03-01 04:59:53 +00:00
- Remove async/await from before_message_write hook
- Use fire-and-forget for sendModeratorMessage (void ... .catch())
- OpenClaw's before_message_write is synchronous, not async
zhi added 1 commit 2026-03-01 09:13:11 +00:00
- Add sessionChannelId Map to track sessionKey -> channelId
- Save channelId in before_model_resolve when we have derived.channelId
- Fix message_sent to use sessionChannelId fallback when ctx.channelId is undefined
- Add debug logging to message_sent
zhi added 1 commit 2026-03-01 09:17:12 +00:00
- Add sessionAccountId Map to track sessionKey -> accountId
- Save accountId in before_model_resolve when resolving accountId
- Use sessionChannelId/sessionAccountId fallback in before_message_write
zhi added 1 commit 2026-03-01 11:08:47 +00:00
- Fix channelId extraction: ctx.channelId is platform name ('discord'), not
  the Discord channel snowflake. Now extracts from conversation_label field
  ('channel id:123456') and sessionKey fallback (':channel:123456').

- Fix extractDiscordChannelId: support 'discord:channel:xxx' format in
  addition to 'channel:xxx' for conversationId/event.to fields.

- Fix sender identification in message_received: event.from returns channel
  target, not sender ID. Now uses event.metadata.senderId for humanList
  matching so human messages correctly reset turn order.

- Fix per-channel turn order: was using all server-wide bot accounts from
  bindings, causing deadlock when turn landed on bots not in the channel.
  Now dynamically tracks which bot accounts are seen per channel via
  message_received and only includes those in turn order.

- Always save sessionChannelId/sessionAccountId mappings in before_model_resolve
  regardless of turn check result, so downstream hooks can use them.

- Add comprehensive debug logging to message_sent hook.
hzhang merged commit 788b9c5552 into feat/turn-based-speaking 2026-03-02 10:22:12 +00:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: nav/Dirigent#8