Skip to content

Plugin: fix /codex_resume race in brand-new Discord threads #30

@huntharo

Description

@huntharo

Summary

In a brand-new Discord thread, sending a first normal message like hi before the agent has replied can break or delay /codex_resume.

Observed behavior suggests a race where the new thread is not fully recognized/bound yet, or the slash interaction is queued behind the first message long enough that it falls back into generic assistant behavior instead of promptly showing the Codex resume picker.

Suspected Repro

  1. Create a brand-new Discord thread.
  2. Immediately send a normal message like hi.
  3. Before the assistant has replied, invoke /codex_resume in that same thread.
  4. Observe that the first /codex_resume attempt may not behave like a real plugin command in that thread.

Observed

  • The first /codex_resume does not reliably work in the new thread.
  • In at least one repro, the user got a generic assistant-style reply about there being no active Codex sessions instead of the plugin resume picker.
  • The eventual real plugin handling of /codex_resume happened much later, after the initial message flow finished.

Expected

  • /codex_resume should resolve the new Discord thread identity immediately and show the Codex picker on the first try.
  • A first normal message in a newly created thread should not prevent or delay plugin command handling for tens of seconds.

Findings

  • Discord command routing in the plugin assumes ctx.from already contains the real thread/channel target and ignores slash: values in ctx.to. See src/controller.ts:230.
  • Inbound Discord message routing separately reconstructs conversation identity from inbound event metadata (conversationId, parentConversationId, guildId, isGroup). See src/controller.ts:269.
  • The existing Discord resume test only covers the happy path where the slash command already resolves to the real channel target and the picker is sent immediately. There is no coverage for a brand-new thread where a first message is already in flight. See src/controller.test.ts:274.

Sanitized Log Snippets

These are the relevant pieces from the reported repro, with ids anonymized:

15:20:14 embedded run start ... session=discord:channel:<thread-channel-id>
15:20:40 embedded run tool start ... tool=sessions_list
15:20:48 embedded run prompt end ... durationMs=34670
15:20:48 lane wait exceeded: lane=session:agent:main:discord:channel:<thread-channel-id> waitedMs=34700
15:20:51 message processed: channel=discord chatId=slash:<user-id> sessionKey=agent:main:discord:slash:<user-id> outcome=completed duration=39646ms
15:20:51 [EventQueue] Slow listener detected: InteractionEventListener took 40051ms for event INTERACTION_CREATE
15:21:37 [plugins] codex discord command /codex_resume from=discord:channel:<thread-channel-id> to=slash:<user-id> conversation=channel:<thread-channel-id>
15:21:37 [plugins] codex discord picker send conversation=channel:<thread-channel-id> rows=6

And the incorrect generic assistant output during the failure window looked like:

No active Codex / coding-agent sessions found. The `/codex_resume` command is meant for resuming a previous Codex session ...

Likely Shape

This looks like one of:

  • a race between brand-new-thread message processing and slash-command handling
  • a Discord thread identity gap before the thread is fully established in OpenClaw/core
  • queueing behavior that lets the first message block /codex_resume long enough for the interaction path to degrade into generic assistant handling

Proposed Scope

  • Reproduce this specifically against a brand-new Discord thread with one in-flight first message.
  • Confirm whether the slash interaction is being routed late, misrouted, or both.
  • Distinguish plugin fault from OpenClaw/core event-ordering fault.
  • Add a regression test or fixture for "new Discord thread + first message in flight + immediate /codex_resume".

Acceptance

  • /codex_resume works on the first attempt in a newly created Discord thread, even if a first normal message is already being processed.
  • The user does not get a generic assistant fallback reply for /codex_resume.
  • The resume picker appears promptly in the correct Discord thread.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Status

    Inbox

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions