Repository navigation
[FEATURE]: add session.turn.completed bus event for plugin hooks #23503
Description
- I have searched the existing issues and confirmed this feature hasn't been implemented yet.
Related issues
- [FEATURE]: Add expanded hook events to the plugin system to enable more powerful plugin capabilities. #21075 proposes expanding plugin hooks broadly — this PR implements one specific hook from that vision.
- [FEATURE]: add session.stopping plugin hook to allow re-entering the agent loop #16626 proposes
session.stoppingfor re-entering the agent loop — complementary but different:turn.completedfires after the turn ends (for post-turn plugins), whilesession.stoppingfires before the turn ends (for loop re-entry).
Summary
Add a session.turn.completed bus event that fires at the end of each assistant turn (after all tool calls finish, before compaction). This is a +9 line change across two files.
Motivation
I'm building ScarletWitch, a Linux seccomp-based filesystem sandbox for AI coding agents. It intercepts all filesystem writes at the kernel level and redirects them to a copy-on-write (COW) layer, allowing users to review and selectively commit changes after each turn — without re-executing the agent.
The key integration point is: after a turn completes, query the COW state and show a review dialog. This requires knowing exactly when a turn ends.
Why this hook is necessary
opencode's existing plugin hooks don't cover this timing:
| Hook | Why it's not sufficient |
|---|---|
tool.execute.after |
Fires after each tool call, not after the entire turn. Showing a review dialog mid-turn would interrupt the LLM. |
event (generic) |
No existing event signals turn completion. Session status changes are not granular enough. |
chat.message |
Fires when a new message is received, not when tool execution finishes. |
The only reliable way to trigger post-turn logic (COW review, sandbox commit dialogs, etc.) is a dedicated event at the exact point where the assistant turn loop exits.
Why it benefits the ecosystem
This hook is useful beyond ScarletWitch — any plugin that needs post-turn processing can use it:
- Sandbox/review plugins (filesystem, network, etc.)
- Post-turn analytics (token usage, tool call summaries)
- Auto-snapshot plugins (git commit after each turn)
- Custom notification plugins (notify when turn completes)
Implementation
The change is minimal — 2 files, +9 lines, zero breaking changes:
session/session.ts (+6 lines): Define the event
TurnCompleted: BusEvent.define(
"session.turn.completed",
z.object({
sessionID: SessionID.zod,
}),
),session/prompt.ts (+3 lines): Publish after runLoop
// Notify plugins that the turn is complete
yield* bus.publish(Session.Event.TurnCompleted, { sessionID })Branch with the change: plugin-hooks
References
- ScarletWitch — the filesystem sandbox
- ScarletWitch-Opencode — the opencode plugin that uses this hook
Activity
[-]feat: add session.turn.completed bus event for plugin hooks[/-][+][FEATURE]: add session.turn.completed bus event for plugin hooks[/+]github-actions commentedon Apr 20, 2026
Thanks for updating the issue! It now meets our contributing guidelines — the
[FEATURE]:prefix, verification checkbox, and a clear description of the motivation and use case are all in place. We appreciate the detailed write-up!github-actions commentedon Jul 3, 2026
To stay organized issues are automatically closed after 60 days of no activity. If the issue is still relevant please open a new one.