fix(plugin): run idle hooks for synthetic status idle

OpenCode now treats session.status idle as the durable completion boundary, but the plugin only dispatched the synthetic session.idle through the main hook chain. Idle-only side effects such as tmux forwarding and team member idle continuations were skipped.

Route synthetic idle through the same idle-only hook path used by real session.idle events and pin the behavior with a regression test.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
YeonGyu-Kim
2026-05-20 14:34:42 +09:00
parent faf2b02163
commit 4a72729acc
2 changed files with 59 additions and 4 deletions
+49
View File
@@ -208,6 +208,55 @@ describe("createEventHandler - idle deduplication", () => {
expect(onEvent.mock.calls[0]?.[0]).toEqual(idleEvent.event)
})
it("#given tmux integration enabled #when session.status reports idle #then synthetic idle forwards to tmuxSessionManager.onEvent", async () => {
//#given
const onEvent = mock<(event: EventInput["event"]) => void>(() => {})
const eventHandler = createEventHandler({
ctx: asEventHandlerContext({
directory: "/tmp",
client: {
session: {},
},
}),
pluginConfig: asPluginConfig({
tmux: { enabled: true },
}),
firstMessageVariantGate: {
markSessionCreated: () => {},
clear: () => {},
},
managers: createEventHandlerManagers({
tmuxSessionManager: {
onEvent,
onSessionCreated: async () => {},
onSessionDeleted: async () => {},
},
}),
hooks: createEventHandlerHooks({}),
})
//#when
await eventHandler(asEventHandlerInput({
event: {
type: "session.status",
properties: {
sessionID: "ses_tmux_synthetic_idle",
status: { type: "idle" },
},
},
}))
//#then
expect(onEvent).toHaveBeenCalledTimes(1)
expect(onEvent.mock.calls[0]?.[0]).toEqual({
type: "session.idle",
properties: {
sessionID: "ses_tmux_synthetic_idle",
synthetic: true,
},
})
})
it("#given a readiness retry is pending #when session.idle arrives through the plugin handler #then tmux retry spawns the pane", async () => {
//#given
const sessionStatusData: Record<string, { type: string }> = {}