Desktop: a turn a plugin starts with $.prompt.submit on an idle session is not shown until the user sends a message

Status Open
Reported on v2.1.280
Maintainer reply None cached
Activity 0 comments · opened Sep 23, 2026

What happens

In the Desktop app, when a plugin's function hook calls $.prompt.submit while the session is idle, the engine starts a turn and the model answers, but the Desktop shows nothing: no user row, no running indicator, no streamed reply. The turn only shows up after the user sends their own next message, and then it is drawn below that message, so it reads as if the plugin's prompt arrived after the user's.

For a plugin that forwards messages from other agents into the session, this looks like the message never arrived: the user thinks they need to type something to "unstick" it.

Environment

  • Claude desktop 2.7032.0 (macOS 26.5.1), bundled Claude Code 2.1.280
  • Plugin with a function-hooks module (the hooks field in .claude-plugin/plugin.json)

How we hit it

The module takes a message off the session's inbox in session.receive and submits it again as the plugin's own prompt:

on('session.receive', async ($, e, next) => {
  if (e.origin?.kind !== 'peer') return next(e)
  const r = await $.prompt.submit({ text: e.text })
  if ('drop' in r && r.drop) return next(e)
  return { consumed: 'relayed as the plugin\'s own prompt' }
})

Then something sends that session a message while it is idle.

Timeline from one occurrence (seconds relative to the submit)

| t | Source | Event |
|---|---|---|
| 0.0 | transcript | queue-operation enqueue + dequeue, then the user row with origin: {kind: "plugin"} |
| 0.1 | our turn.start hook | turn starts |
| 0 → 18 | Desktop window | nothing on screen |
| 18 | Desktop main.log | LocalSessions.sendMessage (the user types a message, which queues behind the running turn) |
| 29 | transcript | assistant reply to the plugin's prompt |
| 31 | Desktop main.log | [Stop hook] Query completed, then [CCD CycleHealth] healthy cycle … (13s, hadFirstResponse=true) |
| 31 | transcript | the user's message is dequeued and gets its own turn |

main.log has no cycle start at t=0, only a Mapping internal session … to CLI session … line (from system/init). The one cycle it records is 13 s long, counted from the user's send, so the Desktop seems to charge the plugin's turn to the user's message.

Contrast

The same plugin prompt is shown live when it is queued while a Desktop-started turn is running and dequeued right when that turn ends. Only a turn that starts while the session is idle goes missing. Before we moved to $.prompt.submit, the same messages came in as peer inbox messages and were shown as "Received message from…" cards (seen on earlier builds; not re-checked on 2.1.280).

Expected

A turn started by $.prompt.submit while the session is idle is shown in the Desktop as it runs, like a peer message or a scheduled task, with the plugin's prompt row placed before any message the user sends during it.

View original on GitHub ↗