From 23b594de6ea248717640099cacfd8563d979ce36 Mon Sep 17 00:00:00 2001 From: Dax Date: Sun, 17 May 2026 12:56:42 -0400 Subject: [PATCH] fix: preserve bus instance context (#28051) --- packages/opencode/src/bus/index.ts | 5 +++- packages/opencode/src/cli/cmd/tui/app.tsx | 1 + packages/opencode/src/cli/upgrade.ts | 28 ++++++++++++++++++--- packages/opencode/src/config/agent.ts | 16 ++---------- packages/opencode/src/config/command.ts | 9 +------ packages/opencode/src/file/watcher.ts | 8 +++--- packages/opencode/src/lsp/lsp.ts | 7 +----- packages/opencode/src/session/compaction.ts | 12 --------- 8 files changed, 37 insertions(+), 49 deletions(-) diff --git a/packages/opencode/src/bus/index.ts b/packages/opencode/src/bus/index.ts index 449694a53a..5c4685f5ec 100644 --- a/packages/opencode/src/bus/index.ts +++ b/packages/opencode/src/bus/index.ts @@ -6,6 +6,8 @@ import { GlobalBus } from "./global" import { InstanceState } from "@/effect/instance-state" import { makeRuntime } from "@/effect/run-service" import { Identifier } from "@/id/id" +import type { InstanceContext } from "@/project/instance-context" +import { InstanceRef } from "@/effect/instance-ref" const log = Log.create({ service: "bus" }) @@ -185,11 +187,12 @@ export function createID() { } export async function publish( + ctx: InstanceContext, def: D, properties: BusProperties, options?: { id?: string }, ) { - return runPromise((svc) => svc.publish(def, properties, options)) + return runPromise((svc) => svc.publish(def, properties, options).pipe(Effect.provideService(InstanceRef, ctx))) } export function subscribe(def: D, callback: (event: Payload) => unknown) { diff --git a/packages/opencode/src/cli/cmd/tui/app.tsx b/packages/opencode/src/cli/cmd/tui/app.tsx index af9df4d42f..19a15a26ec 100644 --- a/packages/opencode/src/cli/cmd/tui/app.tsx +++ b/packages/opencode/src/cli/cmd/tui/app.tsx @@ -859,6 +859,7 @@ function App(props: { onSnapshot?: () => Promise }) { }) event.on("installation.update-available", async (evt) => { + console.log("installation.update-available", evt) const version = evt.properties.version const skipped = kv.get("skipped_version") diff --git a/packages/opencode/src/cli/upgrade.ts b/packages/opencode/src/cli/upgrade.ts index 9f71fcc067..62b230a633 100644 --- a/packages/opencode/src/cli/upgrade.ts +++ b/packages/opencode/src/cli/upgrade.ts @@ -1,9 +1,9 @@ -import { Bus } from "@/bus" import { Config } from "@/config/config" import { AppRuntime } from "@/effect/app-runtime" import { Flag } from "@opencode-ai/core/flag/flag" import { Installation } from "@/installation" import { InstallationVersion } from "@opencode-ai/core/installation/version" +import { GlobalBus } from "@/bus/global" export async function upgrade() { const config = await AppRuntime.runPromise(Config.Service.use((cfg) => cfg.getGlobal())) @@ -13,7 +13,13 @@ export async function upgrade() { if (!latest) return if (Flag.OPENCODE_ALWAYS_NOTIFY_UPDATE) { - await Bus.publish(Installation.Event.UpdateAvailable, { version: latest }) + GlobalBus.emit("event", { + directory: "global", + payload: { + type: Installation.Event.UpdateAvailable.type, + properties: { version: latest }, + }, + }) return } @@ -22,12 +28,26 @@ export async function upgrade() { const kind = Installation.getReleaseType(InstallationVersion, latest) if (config.autoupdate === "notify" || kind !== "patch") { - await Bus.publish(Installation.Event.UpdateAvailable, { version: latest }) + GlobalBus.emit("event", { + directory: "global", + payload: { + type: Installation.Event.UpdateAvailable.type, + properties: { version: latest }, + }, + }) return } if (method === "unknown") return await Installation.upgrade(method, latest) - .then(() => Bus.publish(Installation.Event.Updated, { version: latest })) + .then(() => + GlobalBus.emit("event", { + directory: "global", + payload: { + type: Installation.Event.Updated.type, + properties: { version: latest }, + }, + }), + ) .catch(() => {}) } diff --git a/packages/opencode/src/config/agent.ts b/packages/opencode/src/config/agent.ts index a6719e8674..56688f22cf 100644 --- a/packages/opencode/src/config/agent.ts +++ b/packages/opencode/src/config/agent.ts @@ -1,10 +1,8 @@ export * as ConfigAgent from "./agent" import { Exit, Schema, SchemaGetter } from "effect" -import { Bus } from "@/bus" import { PositiveInt } from "@opencode-ai/core/schema" import * as Log from "@opencode-ai/core/util/log" -import { NamedError } from "@opencode-ai/core/util/error" import { Glob } from "@opencode-ai/core/util/glob" import { configEntryNameFromPath } from "./entry-name" import * as ConfigMarkdown from "./markdown" @@ -112,12 +110,7 @@ export async function load(dir: string) { dot: true, symlink: true, })) { - const md = await ConfigMarkdown.parse(item).catch(async (err) => { - const message = ConfigMarkdown.FrontmatterError.isInstance(err) - ? err.data.message - : `Failed to parse agent ${item}` - const { Session } = await import("@/session/session") - void Bus.publish(Session.Event.Error, { error: new NamedError.Unknown({ message }).toObject() }) + const md = await ConfigMarkdown.parse(item).catch((err) => { log.error("failed to load agent", { agent: item, err }) return undefined }) @@ -144,12 +137,7 @@ export async function loadMode(dir: string) { dot: true, symlink: true, })) { - const md = await ConfigMarkdown.parse(item).catch(async (err) => { - const message = ConfigMarkdown.FrontmatterError.isInstance(err) - ? err.data.message - : `Failed to parse mode ${item}` - const { Session } = await import("@/session/session") - void Bus.publish(Session.Event.Error, { error: new NamedError.Unknown({ message }).toObject() }) + const md = await ConfigMarkdown.parse(item).catch((err) => { log.error("failed to load mode", { mode: item, err }) return undefined }) diff --git a/packages/opencode/src/config/command.ts b/packages/opencode/src/config/command.ts index fa64aef8c3..f6b93ead11 100644 --- a/packages/opencode/src/config/command.ts +++ b/packages/opencode/src/config/command.ts @@ -2,9 +2,7 @@ export * as ConfigCommand from "./command" import * as Log from "@opencode-ai/core/util/log" import { Cause, Exit, Schema } from "effect" -import { NamedError } from "@opencode-ai/core/util/error" import { Glob } from "@opencode-ai/core/util/glob" -import { Bus } from "@/bus" import { configEntryNameFromPath } from "./entry-name" import { InvalidError } from "./error" import * as ConfigMarkdown from "./markdown" @@ -32,12 +30,7 @@ export async function load(dir: string) { dot: true, symlink: true, })) { - const md = await ConfigMarkdown.parse(item).catch(async (err) => { - const message = ConfigMarkdown.FrontmatterError.isInstance(err) - ? err.data.message - : `Failed to parse command ${item}` - const { Session } = await import("@/session/session") - void Bus.publish(Session.Event.Error, { error: new NamedError.Unknown({ message }).toObject() }) + const md = await ConfigMarkdown.parse(item).catch((err) => { log.error("failed to load command", { command: item, err }) return undefined }) diff --git a/packages/opencode/src/file/watcher.ts b/packages/opencode/src/file/watcher.ts index 6c3a611d28..91ad9ff4de 100644 --- a/packages/opencode/src/file/watcher.ts +++ b/packages/opencode/src/file/watcher.ts @@ -96,11 +96,11 @@ export const layer = Layer.effect( ) const cb: ParcelWatcher.SubscribeCallback = bridge.bind((err, evts) => { - if (err) return + // 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") void Bus.publish(ctx, Event.Updated, { file: evt.path, event: "add" }) + if (evt.type === "update") void Bus.publish(ctx, Event.Updated, { file: evt.path, event: "change" }) + if (evt.type === "delete") void Bus.publish(ctx, Event.Updated, { file: evt.path, event: "unlink" }) } }) diff --git a/packages/opencode/src/lsp/lsp.ts b/packages/opencode/src/lsp/lsp.ts index ada9aba671..3117b834c5 100644 --- a/packages/opencode/src/lsp/lsp.ts +++ b/packages/opencode/src/lsp/lsp.ts @@ -13,11 +13,8 @@ import { InstanceState } from "@/effect/instance-state" import { containsPath } from "@/project/instance-context" import { NonNegativeInt } from "@opencode-ai/core/schema" import { RuntimeFlags } from "@/effect/runtime-flags" -import { InstanceRef } from "@/effect/instance-ref" -import { makeRuntime } from "@/effect/run-service" const log = Log.create({ service: "lsp" }) -const busRuntime = makeRuntime(Bus.Service, Bus.layer) export const Event = { Updated: BusEvent.define("lsp.updated", Schema.Struct({})), @@ -294,9 +291,7 @@ export const layer = Layer.effect( if (!client) continue result.push(client) - void busRuntime.runPromise((bus) => - bus.publish(Event.Updated, {}).pipe(Effect.provideService(InstanceRef, ctx)), - ) + await Bus.publish(ctx, Event.Updated, {}) } return result diff --git a/packages/opencode/src/session/compaction.ts b/packages/opencode/src/session/compaction.ts index bc3327c07d..ef007fe74d 100644 --- a/packages/opencode/src/session/compaction.ts +++ b/packages/opencode/src/session/compaction.ts @@ -16,10 +16,8 @@ import { Effect, Layer, Context, Schema } from "effect" import * as DateTime from "effect/DateTime" import { InstanceState } from "@/effect/instance-state" import { isOverflow as overflow, usable } from "./overflow" -import { makeRuntime } from "@/effect/run-service" import { serviceUse } from "@/effect/service-use" import { RuntimeFlags } from "@/effect/runtime-flags" -import { EventV2 } from "@opencode-ai/core/event" import { EventV2Bridge } from "@/event-v2-bridge" import { SessionEvent } from "@opencode-ai/core/session-event" @@ -638,14 +636,4 @@ export const defaultLayer = Layer.suspend(() => ), ) -const { runPromise } = makeRuntime(Service, defaultLayer) - -export async function isOverflow(input: { tokens: MessageV2.Assistant["tokens"]; model: Provider.Model }) { - return runPromise((svc) => svc.isOverflow(input)) -} - -export async function prune(input: { sessionID: SessionID }) { - return runPromise((svc) => svc.prune(input)) -} - export * as SessionCompaction from "./compaction"