From 9c046039c8308f0c55a68e5c60187d90988e4584 Mon Sep 17 00:00:00 2001 From: Shoubhit Dash Date: Fri, 15 May 2026 17:15:30 +0530 Subject: [PATCH 1/4] refactor(watcher): bridge native event callback --- packages/opencode/src/file/watcher.ts | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/packages/opencode/src/file/watcher.ts b/packages/opencode/src/file/watcher.ts index d940c7c4228f..6a118525ecd8 100644 --- a/packages/opencode/src/file/watcher.ts +++ b/packages/opencode/src/file/watcher.ts @@ -6,6 +6,7 @@ import { readdir, realpath } from "fs/promises" import path from "path" import { Bus } from "@/bus" import { BusEvent } from "@/bus/bus-event" +import { EffectBridge } from "@/effect/bridge" import { InstanceState } from "@/effect/instance-state" import { Flag } from "@opencode-ai/core/flag/flag" import { Git } from "@/git" @@ -88,20 +89,23 @@ export const layer = Layer.effect( if (!w) return log.info("watcher backend", { directory: ctx.directory, platform: process.platform, backend }) - + const bridge = yield* EffectBridge.make() const subs: ParcelWatcher.AsyncSubscription[] = [] yield* Effect.addFinalizer(() => Effect.promise(() => Promise.allSettled(subs.map((sub) => sub.unsubscribe()))), ) - const cb: ParcelWatcher.SubscribeCallback = InstanceState.bind((err, evts) => { + const cb: ParcelWatcher.SubscribeCallback = (err, evts) => { if (err) return for (const evt of evts) { - if (evt.type === "create") void Bus.publish(Event.Updated, { file: evt.path, event: "add" }) - if (evt.type === "update") void Bus.publish(Event.Updated, { file: evt.path, event: "change" }) - if (evt.type === "delete") void Bus.publish(Event.Updated, { file: evt.path, event: "unlink" }) + if (evt.type === "create") + bridge.fork(Effect.promise(() => Bus.publish(Event.Updated, { file: evt.path, event: "add" }))) + if (evt.type === "update") + bridge.fork(Effect.promise(() => Bus.publish(Event.Updated, { file: evt.path, event: "change" }))) + if (evt.type === "delete") + bridge.fork(Effect.promise(() => Bus.publish(Event.Updated, { file: evt.path, event: "unlink" }))) } - }) + } const subscribe = (dir: string, ignore: string[]) => { const pending = w.subscribe(dir, cb, { ignore, backend }) From a21c2730d0513d238a8f002655fe710bba41395e Mon Sep 17 00:00:00 2001 From: Shoubhit Dash Date: Fri, 15 May 2026 17:42:15 +0530 Subject: [PATCH 2/4] refactor(instance): remove remaining bind call sites --- packages/opencode/src/session/llm.ts | 14 ++++++++----- packages/opencode/src/storage/db.ts | 31 ++++++++++++++++++++++++---- 2 files changed, 36 insertions(+), 9 deletions(-) diff --git a/packages/opencode/src/session/llm.ts b/packages/opencode/src/session/llm.ts index 551787888852..36c89561eee3 100644 --- a/packages/opencode/src/session/llm.ts +++ b/packages/opencode/src/session/llm.ts @@ -256,7 +256,7 @@ const live: Layer.Layer< const bridge = yield* EffectBridge.make() const approvedToolsForSession = new Set() - workflowModel.approvalHandler = InstanceState.bind(async (approvalTools) => { + workflowModel.approvalHandler = async (approvalTools) => { const uniqueNames = [...new Set(approvalTools.map((t: { name: string }) => t.name))] as string[] // Auto-approve tools that were already approved in this session // (prevents infinite approval loops for server-side MCP tools) @@ -267,9 +267,13 @@ const live: Layer.Layer< const id = PermissionID.ascending() let unsub: (() => void) | undefined try { - unsub = Bus.subscribe(Permission.Event.Replied, (evt) => { - if (evt.properties.requestID === id) void evt.properties.reply - }) + unsub = await bridge.promise( + Effect.sync(() => + Bus.subscribe(Permission.Event.Replied, (evt) => { + if (evt.properties.requestID === id) void evt.properties.reply + }), + ), + ) const toolPatterns = approvalTools.map((t: { name: string; args: string }) => { try { const parsed = JSON.parse(t.args) as Record @@ -299,7 +303,7 @@ const live: Layer.Layer< } finally { unsub?.() } - }) + } } const tracer = cfg.experimental?.openTelemetry diff --git a/packages/opencode/src/storage/db.ts b/packages/opencode/src/storage/db.ts index 6cb819a6fd0f..a9612939dc85 100644 --- a/packages/opencode/src/storage/db.ts +++ b/packages/opencode/src/storage/db.ts @@ -11,9 +11,11 @@ import path from "path" import { readFileSync, readdirSync, existsSync } from "fs" import { Flag } from "@opencode-ai/core/flag/flag" import { InstallationChannel } from "@opencode-ai/core/installation/version" -import { InstanceState } from "@/effect/instance-state" +import { WorkspaceContext } from "@/control-plane/workspace-context" +import { InstanceRef, WorkspaceRef } from "@/effect/instance-ref" +import { context as instanceContext } from "@/project/instance-context" import { init } from "#db" -import { Effect, Schema } from "effect" +import { Context, Effect, Fiber, Schema } from "effect" declare const OPENCODE_MIGRATIONS: { sql: string; timestamp: number; name: string }[] | undefined @@ -167,7 +169,7 @@ export function use(callback: (trx: TxOrDb) => T): T { } export function effect(fn: () => any | Promise) { - const bound = InstanceState.bind(fn) + const bound = bindInstanceRef(fn) try { ctx.use().effects.push(bound) } catch { @@ -188,7 +190,7 @@ export function transaction( } catch (err) { if (err instanceof LocalContext.NotFound) { const effects: (() => void | Promise)[] = [] - const txCallback = InstanceState.bind((tx: TxOrDb) => ctx.provide({ tx, effects }, () => callback(tx))) + const txCallback = bindInstanceRef((tx: TxOrDb) => ctx.provide({ tx, effects }, () => callback(tx))) const result = Client().transaction(txCallback, { behavior: options?.behavior }) for (const effect of effects) effect() return result as NotPromise @@ -197,4 +199,25 @@ export function transaction( } } +function bindInstanceRef(fn: (...args: Args) => Result) { + const fiber = Fiber.getCurrent() + const instance = fiber + ? Context.getReferenceUnsafe(fiber.context, InstanceRef) + : (() => { + try { + return instanceContext.use() + } catch (err) { + if (!(err instanceof LocalContext.NotFound)) throw err + } + })() + const workspace = fiber ? Context.getReferenceUnsafe(fiber.context, WorkspaceRef) : WorkspaceContext.workspaceID + if (!instance && workspace === undefined) return fn + return (...args: Args) => { + const run = () => fn(...args) + const withInstance = instance ? () => instanceContext.provide(instance, run) : run + if (workspace === undefined) return withInstance() + return WorkspaceContext.restore(workspace, withInstance) + } +} + export * as Database from "./db" From 079fee628c7868f965a78fa61f0dd2f1a36318b5 Mon Sep 17 00:00:00 2001 From: Shoubhit Dash Date: Fri, 15 May 2026 18:04:09 +0530 Subject: [PATCH 3/4] refactor(instance): centralize callback binding --- packages/opencode/src/effect/bridge.ts | 42 +++++++++++++++++++------- packages/opencode/src/file/watcher.ts | 13 +++----- packages/opencode/src/session/llm.ts | 14 +++------ packages/opencode/src/storage/db.ts | 25 ++------------- 4 files changed, 44 insertions(+), 50 deletions(-) diff --git a/packages/opencode/src/effect/bridge.ts b/packages/opencode/src/effect/bridge.ts index e987c901316f..820a7009b8cb 100644 --- a/packages/opencode/src/effect/bridge.ts +++ b/packages/opencode/src/effect/bridge.ts @@ -1,4 +1,4 @@ -import { Effect, Exit, Fiber } from "effect" +import { Context, Effect, Exit, Fiber } from "effect" import { WorkspaceContext } from "@/control-plane/workspace-context" import { Instance } from "@/project/instance" import type { InstanceContext } from "@/project/instance-context" @@ -11,6 +11,7 @@ export interface Shape { readonly promise: (effect: Effect.Effect) => Promise readonly fork: (effect: Effect.Effect) => Fiber.Fiber readonly run: (effect: Effect.Effect) => Effect.Effect + readonly bind: (fn: (...args: Args) => Result) => (...args: Args) => Result } function restore(instance: InstanceContext | undefined, workspace: WorkspaceID | undefined, fn: () => R): R { @@ -22,6 +23,28 @@ function restore(instance: InstanceContext | undefined, workspace: WorkspaceI return fn() } +function captureSync() { + const fiber = Fiber.getCurrent() + const value = fiber ? Context.getReferenceUnsafe(fiber.context, InstanceRef) : undefined + const instance = + value ?? + (() => { + try { + return Instance.current + } catch (err) { + if (!(err instanceof LocalContext.NotFound)) throw err + } + })() + const workspace = (fiber ? Context.getReferenceUnsafe(fiber.context, WorkspaceRef) : undefined) ?? + WorkspaceContext.workspaceID + return { instance, workspace } +} + +export const bind = (fn: (...args: Args) => Result) => { + const captured = captureSync() + return (...args: Args) => restore(captured.instance, captured.workspace, () => fn(...args)) +} + /** * Bridge from Effect into a Promise-returning JS callback while installing * legacy `Instance.context` and `WorkspaceContext` AsyncLocalStorage for @@ -45,16 +68,9 @@ export function make(): Effect.Effect { return Effect.gen(function* () { const ctx = yield* Effect.context() const value = yield* InstanceRef - const instance = - value ?? - (() => { - try { - return Instance.current - } catch (err) { - if (!(err instanceof LocalContext.NotFound)) throw err - } - })() - const workspace = (yield* WorkspaceRef) ?? WorkspaceContext.workspaceID + const captured = captureSync() + const instance = value ?? captured.instance + const workspace = (yield* WorkspaceRef) ?? captured.workspace const attach = (effect: Effect.Effect) => attachWith(effect, { instance, workspace }) const wrap = (effect: Effect.Effect) => attach(effect).pipe(Effect.provide(ctx)) as Effect.Effect @@ -72,6 +88,10 @@ export function make(): Effect.Effect { ), ) }), + bind: + (fn: (...args: Args) => Result) => + (...args: Args) => + restore(instance, workspace, () => fn(...args)), } satisfies Shape }) } diff --git a/packages/opencode/src/file/watcher.ts b/packages/opencode/src/file/watcher.ts index 6a118525ecd8..6c3a611d2864 100644 --- a/packages/opencode/src/file/watcher.ts +++ b/packages/opencode/src/file/watcher.ts @@ -95,17 +95,14 @@ export const layer = Layer.effect( Effect.promise(() => Promise.allSettled(subs.map((sub) => sub.unsubscribe()))), ) - const cb: ParcelWatcher.SubscribeCallback = (err, evts) => { + const cb: ParcelWatcher.SubscribeCallback = bridge.bind((err, evts) => { if (err) return for (const evt of evts) { - if (evt.type === "create") - bridge.fork(Effect.promise(() => Bus.publish(Event.Updated, { file: evt.path, event: "add" }))) - if (evt.type === "update") - bridge.fork(Effect.promise(() => Bus.publish(Event.Updated, { file: evt.path, event: "change" }))) - if (evt.type === "delete") - bridge.fork(Effect.promise(() => Bus.publish(Event.Updated, { file: evt.path, event: "unlink" }))) + if (evt.type === "create") void Bus.publish(Event.Updated, { file: evt.path, event: "add" }) + if (evt.type === "update") void Bus.publish(Event.Updated, { file: evt.path, event: "change" }) + if (evt.type === "delete") void Bus.publish(Event.Updated, { file: evt.path, event: "unlink" }) } - } + }) const subscribe = (dir: string, ignore: string[]) => { const pending = w.subscribe(dir, cb, { ignore, backend }) diff --git a/packages/opencode/src/session/llm.ts b/packages/opencode/src/session/llm.ts index 36c89561eee3..0cf3a2398f9b 100644 --- a/packages/opencode/src/session/llm.ts +++ b/packages/opencode/src/session/llm.ts @@ -256,7 +256,7 @@ const live: Layer.Layer< const bridge = yield* EffectBridge.make() const approvedToolsForSession = new Set() - workflowModel.approvalHandler = async (approvalTools) => { + workflowModel.approvalHandler = bridge.bind(async (approvalTools) => { const uniqueNames = [...new Set(approvalTools.map((t: { name: string }) => t.name))] as string[] // Auto-approve tools that were already approved in this session // (prevents infinite approval loops for server-side MCP tools) @@ -267,13 +267,9 @@ const live: Layer.Layer< const id = PermissionID.ascending() let unsub: (() => void) | undefined try { - unsub = await bridge.promise( - Effect.sync(() => - Bus.subscribe(Permission.Event.Replied, (evt) => { - if (evt.properties.requestID === id) void evt.properties.reply - }), - ), - ) + unsub = Bus.subscribe(Permission.Event.Replied, (evt) => { + if (evt.properties.requestID === id) void evt.properties.reply + }) const toolPatterns = approvalTools.map((t: { name: string; args: string }) => { try { const parsed = JSON.parse(t.args) as Record @@ -303,7 +299,7 @@ const live: Layer.Layer< } finally { unsub?.() } - } + }) } const tracer = cfg.experimental?.openTelemetry diff --git a/packages/opencode/src/storage/db.ts b/packages/opencode/src/storage/db.ts index a9612939dc85..82d8a0d1865c 100644 --- a/packages/opencode/src/storage/db.ts +++ b/packages/opencode/src/storage/db.ts @@ -11,11 +11,9 @@ import path from "path" import { readFileSync, readdirSync, existsSync } from "fs" import { Flag } from "@opencode-ai/core/flag/flag" import { InstallationChannel } from "@opencode-ai/core/installation/version" -import { WorkspaceContext } from "@/control-plane/workspace-context" -import { InstanceRef, WorkspaceRef } from "@/effect/instance-ref" -import { context as instanceContext } from "@/project/instance-context" +import { EffectBridge } from "@/effect/bridge" import { init } from "#db" -import { Context, Effect, Fiber, Schema } from "effect" +import { Effect, Schema } from "effect" declare const OPENCODE_MIGRATIONS: { sql: string; timestamp: number; name: string }[] | undefined @@ -200,24 +198,7 @@ export function transaction( } function bindInstanceRef(fn: (...args: Args) => Result) { - const fiber = Fiber.getCurrent() - const instance = fiber - ? Context.getReferenceUnsafe(fiber.context, InstanceRef) - : (() => { - try { - return instanceContext.use() - } catch (err) { - if (!(err instanceof LocalContext.NotFound)) throw err - } - })() - const workspace = fiber ? Context.getReferenceUnsafe(fiber.context, WorkspaceRef) : WorkspaceContext.workspaceID - if (!instance && workspace === undefined) return fn - return (...args: Args) => { - const run = () => fn(...args) - const withInstance = instance ? () => instanceContext.provide(instance, run) : run - if (workspace === undefined) return withInstance() - return WorkspaceContext.restore(workspace, withInstance) - } + return EffectBridge.bind(fn) } export * as Database from "./db" From 2c940c2f9163ab5aae98c2c12ef9042b8d821677 Mon Sep 17 00:00:00 2001 From: Shoubhit Dash Date: Fri, 15 May 2026 18:06:16 +0530 Subject: [PATCH 4/4] refactor(db): inline bridge binding --- packages/opencode/src/storage/db.ts | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/packages/opencode/src/storage/db.ts b/packages/opencode/src/storage/db.ts index 82d8a0d1865c..06f1f84a9ae7 100644 --- a/packages/opencode/src/storage/db.ts +++ b/packages/opencode/src/storage/db.ts @@ -167,7 +167,7 @@ export function use(callback: (trx: TxOrDb) => T): T { } export function effect(fn: () => any | Promise) { - const bound = bindInstanceRef(fn) + const bound = EffectBridge.bind(fn) try { ctx.use().effects.push(bound) } catch { @@ -188,7 +188,7 @@ export function transaction( } catch (err) { if (err instanceof LocalContext.NotFound) { const effects: (() => void | Promise)[] = [] - const txCallback = bindInstanceRef((tx: TxOrDb) => ctx.provide({ tx, effects }, () => callback(tx))) + const txCallback = EffectBridge.bind((tx: TxOrDb) => ctx.provide({ tx, effects }, () => callback(tx))) const result = Client().transaction(txCallback, { behavior: options?.behavior }) for (const effect of effects) effect() return result as NotPromise @@ -197,8 +197,4 @@ export function transaction( } } -function bindInstanceRef(fn: (...args: Args) => Result) { - return EffectBridge.bind(fn) -} - export * as Database from "./db"