You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
hold has no approval dialog and held messages never expire #5
Recorded in the README's "Differences from Claude Code".
Current behaviour
Setting OPENCODE_CROSS_SESSION_INBOUND=hold writes an incoming message to the receiver's inbox file. It is injected at the start of that session's next turn. That is all.
Claude Code's hold is richer:
It opens an approval dialog in the receiving session showing the sender and a preview, with Approve and Deny
An unanswered dialog expires after dialogExpiry, five minutes by default, and the message is dropped
The sender is told what happened: a notice when the message is held, and a follow-up when it is later delivered, denied, or expired
If the session's permission mode changes while messages are held, the rules are re-applied
So "hold" here means "wait for the next turn", not "wait for your approval". A user who never starts another turn in that session accumulates messages that are never seen and never expire, and the sender is told only that it was queued.
What is achievable
Approval dialog: plugins cannot open arbitrary prompt UI. The nearest primitives are the permission.ask hook and POST /tui/show-toast. A toast can notify but cannot collect Approve/Deny. Worth investigating whether a held message can be surfaced as a real permission request; if not, this part is not implementable as a plugin and should be documented rather than faked.
Expiry: straightforward. Stamp each held entry (already carries ts) and drop anything older than a configurable deadline on drain, defaulting to something long enough to be useful. Cheap, and removes the silent unbounded pile.
Sender notices: only possible with a return path. Today delivery is fire-and-forget over HTTP. Inbound refuse/hold are cooperative, not enforced by the receiver #3's receiver-owned socket would give the receiver a channel to report delivered/held/denied/expired back to the sender.
Suggested order
Add expiry to held messages. Small, self-contained, no design questions.
Investigate the approval dialog separately, and document it as not implementable if that is the answer.
Meanwhile
The README should say what hold actually does — waits for the next turn, no approval, no expiry — rather than leaving the Claude Code semantics implied.
Recorded in the README's "Differences from Claude Code".
Current behaviour
Setting
OPENCODE_CROSS_SESSION_INBOUND=holdwrites an incoming message to the receiver's inbox file. It is injected at the start of that session's next turn. That is all.Claude Code's
holdis richer:dialogExpiry, five minutes by default, and the message is droppedSo "hold" here means "wait for the next turn", not "wait for your approval". A user who never starts another turn in that session accumulates messages that are never seen and never expire, and the sender is told only that it was queued.
What is achievable
permission.askhook andPOST /tui/show-toast. A toast can notify but cannot collect Approve/Deny. Worth investigating whether a held message can be surfaced as a real permission request; if not, this part is not implementable as a plugin and should be documented rather than faked.ts) and drop anything older than a configurable deadline on drain, defaulting to something long enough to be useful. Cheap, and removes the silent unbounded pile.Suggested order
Meanwhile
The README should say what
holdactually does — waits for the next turn, no approval, no expiry — rather than leaving the Claude Code semantics implied.