Coding 工具 · 04
OpenCode
使用 OpenCode plugin 监听 session.idle,读取主会话最后一条 assistant message ID,并把它作为稳定的本轮身份。
当前官方事件
session.idle 是候选完成信号
OpenCode plugin 的 event 回调可以收到 session.idle。它表示某个 session 进入 idle,不保证它一定是你想提醒的顶层用户任务;复杂工作流里也可能包含子 session。
步骤 1
创建 bluebell plugin
bluebell.js
export const BluebellPlugin = async ({ client, directory }) => {
const cli = "/Applications/bluebellMac.app/Contents/Helpers/bluebellctl"
return {
event: async ({ event }) => {
if (event.type !== "session.idle") return
const sessionID = event.properties.sessionID
const sessionResult = await client.session.get({ path: { id: sessionID } })
const session = sessionResult.data
if (!session || session.parentID) return
const messageResult = await client.session.messages({ path: { id: sessionID } })
const messages = messageResult.data || []
const lastAssistant = [...messages].reverse().find(
(item) => item.info && item.info.role === "assistant"
)
const messageID = lastAssistant && lastAssistant.info.id
if (!messageID) return
const workspace = directory.split("/").filter(Boolean).pop() || "OpenCode"
const process = Bun.spawn([
cli, "emit",
"--source", "OpenCode",
"--workspace-name", workspace,
"--session-id", sessionID,
"--turn-id", messageID,
"--dedupe-key", "opencode:" + sessionID + ":" + messageID,
"--local",
], { stdout: "ignore", stderr: "ignore" })
await process.exited
},
}
}示例通过 OpenCode SDK 读取 session 与 message 列表,只发送 ID 和工作区显示名,不把消息正文交给 bluebell。先保留 --local。
步骤 2
选择项目级或全局安装
| 位置 | 作用范围 |
|---|---|
.opencode/plugins/bluebell.js | 只对当前项目生效,适合先做 POC。 |
~/.config/opencode/plugins/bluebell.js | 对本机 OpenCode 项目全局生效。 |
不要让同一 plugin 同时出现在项目级和全局位置,否则同一 idle 可能触发两次命令。即使 dedupeKey 能合并,也会制造不必要的调用。
步骤 3
分别测试根 session 与子任务
- 完成一轮没有子任务的短工作,确认最近提醒只有一条 OpenCode 事件。
- 运行一个会创建子 session 的工作流,确认子 session 没有产生提醒。
- 在同一 session 发起第二轮,确认最后 assistant message ID 改变。
- 验证后删除
--local,再检查 Mac 总推送与真实设备。
来源
OpenCode 官方资料
- OpenCode pluginsPlugin 位置、event callback 与 session.idle
- OpenCode SDKsession 与 messages API