diff --git a/src/hooks/directory-agents-injector/injector.ts b/src/hooks/directory-agents-injector/injector.ts index 3ff40784d..f05dc276f 100644 --- a/src/hooks/directory-agents-injector/injector.ts +++ b/src/hooks/directory-agents-injector/injector.ts @@ -26,6 +26,10 @@ export async function processFilePathForAgentsInjection(input: { sessionID: string; output: { title: string; output: string; metadata: unknown }; }): Promise { + // Guard: output.output may be non-string at runtime (e.g. MCP bridge format changes). + // Consistent with the pattern used in tool-output-truncator and other hooks. + if (typeof input.output.output !== "string") return; + const resolved = resolveFilePath(input.ctx.directory, input.filePath); if (!resolved) return; diff --git a/src/plugin/ultrawork-db-model-override.bun-sqlite-unavailable.test.ts b/src/plugin/ultrawork-db-model-override.bun-sqlite-unavailable.test.ts new file mode 100644 index 000000000..ce305ffc8 --- /dev/null +++ b/src/plugin/ultrawork-db-model-override.bun-sqlite-unavailable.test.ts @@ -0,0 +1,20 @@ +import { describe, expect, test } from "bun:test" + +describe("scheduleDeferredModelOverride bun:sqlite unavailable", () => { + test("#given source code #when inspected #then bun:sqlite is loaded dynamically with an unavailable-runtime fallback", async () => { + //#given + const source = await Bun.file(new URL("./ultrawork-db-model-override.ts", import.meta.url)).text() + + //#when + const hasStaticBunSqliteImport = source.includes('from "bun:sqlite"') + || source.includes("from 'bun:sqlite'") + || source.includes('import "bun:sqlite"') + || source.includes("import 'bun:sqlite'") + + //#then + expect(hasStaticBunSqliteImport).toBe(false) + expect(source).toContain('await import("bun:sqlite").catch(() => null)') + expect(source).toContain("bun:sqlite unavailable") + expect(source).toContain("return") + }) +}) diff --git a/src/plugin/ultrawork-db-model-override.ts b/src/plugin/ultrawork-db-model-override.ts index 8a36609d8..3d83dc276 100644 --- a/src/plugin/ultrawork-db-model-override.ts +++ b/src/plugin/ultrawork-db-model-override.ts @@ -1,9 +1,10 @@ -import { Database } from "bun:sqlite" import { join } from "node:path" import { existsSync } from "node:fs" import { getDataDir } from "../shared/data-path" import { log } from "../shared" +type BunDatabase = import("bun:sqlite").Database + function getDbPath(): string { return join(getDataDir(), "opencode", "opencode.db") } @@ -11,7 +12,7 @@ function getDbPath(): string { const MAX_MICROTASK_RETRIES = 10 function tryUpdateMessageModel( - db: InstanceType, + db: BunDatabase, messageId: string, targetModel: { providerID: string; modelID: string }, variant?: string, @@ -30,7 +31,7 @@ function tryUpdateMessageModel( } function retryViaMicrotask( - db: InstanceType, + db: BunDatabase, messageId: string, targetModel: { providerID: string; modelID: string }, variant: string | undefined, @@ -112,14 +113,21 @@ export function scheduleDeferredModelOverride( targetModel: { providerID: string; modelID: string }, variant?: string, ): void { - queueMicrotask(() => { + queueMicrotask(async () => { + const sqliteModule = await import("bun:sqlite").catch(() => null) + const Database = sqliteModule?.Database + if (typeof Database !== "function") { + log("[ultrawork-db-override] bun:sqlite unavailable, skipping deferred override", { messageId }) + return + } + const dbPath = getDbPath() if (!existsSync(dbPath)) { log("[ultrawork-db-override] DB not found, skipping deferred override") return } - let db: InstanceType + let db: BunDatabase try { db = new Database(dbPath) } catch (error) {