Skip to content

Commit 41277ae

Browse files
authored
Merge pull request #74 from browser-use/alex/await-disposal-plugin-events
fix(plugin): await session.idle + server.instance.disposed handlers
2 parents c5b4f26 + e008c31 commit 41277ae

1 file changed

Lines changed: 23 additions & 3 deletions

File tree

packages/opencode/src/plugin/index.ts

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -244,12 +244,32 @@ export const layer = Layer.effect(
244244
}).pipe(Effect.ignore)
245245
}
246246

247-
// Subscribe to bus events, fiber interrupted when scope closes
247+
// Subscribe to bus events, fiber interrupted when scope closes.
248+
// session.idle and server.instance.disposed are plugins' only chance to
249+
// drain async work (e.g. OTel span exporters) before src/index.ts's
250+
// top-level finally runs forceFlush and calls process.exit() — await
251+
// those handlers; keep the rest fire-and-forget for throughput.
248252
yield* bus.subscribeAll().pipe(
249253
Stream.runForEach((input) =>
250-
Effect.sync(() => {
254+
Effect.promise(async () => {
255+
const awaitHook = input.type === "server.instance.disposed" || input.type === "session.idle"
251256
for (const hook of hooks) {
252-
void hook["event"]?.({ event: input as any })
257+
try {
258+
const ret = hook["event"]?.({ event: input as any })
259+
if (awaitHook && ret) {
260+
await ret
261+
} else if (ret) {
262+
// Fire-and-forget path: surface async failures to logs instead of letting them
263+
// become unhandledRejections that hide which plugin/event broke.
264+
void Promise.resolve(ret).catch((err) =>
265+
log.error("plugin event hook failed", { error: err }),
266+
)
267+
}
268+
} catch (err) {
269+
// Catches sync throws + awaited async rejections so one bad plugin can't kill
270+
// the subscription fiber and silently disable every other plugin.
271+
log.error("plugin event hook failed", { error: err })
272+
}
253273
}
254274
}),
255275
),

0 commit comments

Comments
 (0)