54 lines
2.1 KiB
TypeScript
54 lines
2.1 KiB
TypeScript
// opencode agent-state plugin: mirrors the session state into the tmux pane opencode runs in via
|
|
// ~/.config/tmux/scripts/agent-state.sh (oc busy|done|input|error). No-op outside tmux.
|
|
//
|
|
// Runs in the SERVER process, so TMUX_PANE is only right when the TUI in the pane spawned the
|
|
// server (plain `opencode`), not for `opencode serve` / `opencode attach`. Under the bwrap
|
|
// `opencodes` wrapper the tmux socket dir must be bound in, or the script gives up quietly.
|
|
// Child (subagent) sessions are skipped so a subagent finishing does not flag "done".
|
|
import type { Plugin } from "@opencode-ai/plugin"
|
|
|
|
export const AgentState: Plugin = async ({ client, $ }) => {
|
|
const script = `${process.env.HOME}/.config/tmux/scripts/agent-state.sh`
|
|
const set = async (state: string) => {
|
|
if (!process.env.TMUX_PANE) return
|
|
try {
|
|
await $`${script} oc ${state}`.quiet().nothrow()
|
|
} catch {}
|
|
}
|
|
const childCache = new Map<string, boolean>()
|
|
const isChild = async (sessionID?: string) => {
|
|
if (!sessionID) return false
|
|
if (!childCache.has(sessionID)) {
|
|
try {
|
|
const r = await client.session.get({ path: { id: sessionID } })
|
|
childCache.set(sessionID, Boolean(r.data?.parentID))
|
|
} catch {
|
|
childCache.set(sessionID, false)
|
|
}
|
|
}
|
|
return childCache.get(sessionID) ?? false
|
|
}
|
|
return {
|
|
event: async ({ event }) => {
|
|
const p = (event as { properties?: Record<string, any> }).properties ?? {}
|
|
switch (event.type) {
|
|
case "message.updated": // a new user message = a prompt went in
|
|
if (p.info?.role === "user" && !(await isChild(p.info?.sessionID))) await set("busy")
|
|
break
|
|
case "permission.asked":
|
|
if (!(await isChild(p.sessionID))) await set("input")
|
|
break
|
|
case "permission.replied":
|
|
if (!(await isChild(p.sessionID))) await set("busy")
|
|
break
|
|
case "session.idle":
|
|
if (!(await isChild(p.sessionID))) await set("done")
|
|
break
|
|
case "session.error":
|
|
if (!(await isChild(p.sessionID))) await set("error")
|
|
break
|
|
}
|
|
},
|
|
}
|
|
}
|