From 291a3edc712b90efe357856b358c14bb8a63ff04 Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Sun, 15 Feb 2026 15:09:59 +0900 Subject: [PATCH] feat: migrate tool callers to SDK message finders on SQLite backend --- .../create-background-task.test.ts | 36 ++++++++++++------- .../background-task/create-background-task.ts | 28 ++++++++++++--- .../background-agent-executor.test.ts | 36 ++++++++++++------- .../delegate-task/parent-context-resolver.ts | 29 ++++++++++++--- src/tools/delegate-task/tools.ts | 2 +- 5 files changed, 96 insertions(+), 35 deletions(-) diff --git a/src/tools/background-task/create-background-task.test.ts b/src/tools/background-task/create-background-task.test.ts index 5cfd07c49..2afc20a0f 100644 --- a/src/tools/background-task/create-background-task.test.ts +++ b/src/tools/background-task/create-background-task.test.ts @@ -1,20 +1,32 @@ +/// + import { describe, test, expect, mock } from "bun:test" import type { BackgroundManager } from "../../features/background-agent" +import type { PluginInput } from "@opencode-ai/plugin" import { createBackgroundTask } from "./create-background-task" describe("createBackgroundTask", () => { + const launchMock = mock(() => Promise.resolve({ + id: "test-task-id", + sessionID: null, + description: "Test task", + agent: "test-agent", + status: "pending", + })) + const getTaskMock = mock() + const mockManager = { - launch: mock(() => Promise.resolve({ - id: "test-task-id", - sessionID: null, - description: "Test task", - agent: "test-agent", - status: "pending", - })), - getTask: mock(), + launch: launchMock, + getTask: getTaskMock, } as unknown as BackgroundManager - const tool = createBackgroundTask(mockManager) + const mockClient = { + session: { + messages: mock(() => Promise.resolve({ data: [] })), + }, + } as unknown as PluginInput["client"] + + const tool = createBackgroundTask(mockManager, mockClient) const testContext = { sessionID: "test-session", @@ -31,14 +43,14 @@ describe("createBackgroundTask", () => { test("detects interrupted task as failure", async () => { //#given - mockManager.launch.mockResolvedValueOnce({ + launchMock.mockResolvedValueOnce({ id: "test-task-id", sessionID: null, description: "Test task", agent: "test-agent", status: "pending", }) - mockManager.getTask.mockReturnValueOnce({ + getTaskMock.mockReturnValueOnce({ id: "test-task-id", sessionID: null, description: "Test task", @@ -53,4 +65,4 @@ describe("createBackgroundTask", () => { expect(result).toContain("Task entered error state") expect(result).toContain("test-task-id") }) -}) \ No newline at end of file +}) diff --git a/src/tools/background-task/create-background-task.ts b/src/tools/background-task/create-background-task.ts index a7a365d2f..22adff8c6 100644 --- a/src/tools/background-task/create-background-task.ts +++ b/src/tools/background-task/create-background-task.ts @@ -1,13 +1,19 @@ -import { tool, type ToolDefinition } from "@opencode-ai/plugin" +import { tool, type PluginInput, type ToolDefinition } from "@opencode-ai/plugin" import type { BackgroundManager } from "../../features/background-agent" import type { BackgroundTaskArgs } from "./types" import { BACKGROUND_TASK_DESCRIPTION } from "./constants" -import { findFirstMessageWithAgent, findNearestMessageWithFields } from "../../features/hook-message-injector" +import { + findFirstMessageWithAgent, + findFirstMessageWithAgentFromSDK, + findNearestMessageWithFields, + findNearestMessageWithFieldsFromSDK, +} from "../../features/hook-message-injector" import { getSessionAgent } from "../../features/claude-code-session-state" import { storeToolMetadata } from "../../features/tool-metadata-store" import { log } from "../../shared/logger" import { delay } from "./delay" import { getMessageDir } from "./message-dir" +import { isSqliteBackend } from "../../shared/opencode-storage-detection" type ToolContextWithMetadata = { sessionID: string @@ -18,7 +24,10 @@ type ToolContextWithMetadata = { callID?: string } -export function createBackgroundTask(manager: BackgroundManager): ToolDefinition { +export function createBackgroundTask( + manager: BackgroundManager, + client: PluginInput["client"] +): ToolDefinition { return tool({ description: BACKGROUND_TASK_DESCRIPTION, args: { @@ -35,8 +44,17 @@ export function createBackgroundTask(manager: BackgroundManager): ToolDefinition try { const messageDir = getMessageDir(ctx.sessionID) - const prevMessage = messageDir ? findNearestMessageWithFields(messageDir) : null - const firstMessageAgent = messageDir ? findFirstMessageWithAgent(messageDir) : null + + const [prevMessage, firstMessageAgent] = isSqliteBackend() + ? await Promise.all([ + findNearestMessageWithFieldsFromSDK(client, ctx.sessionID), + findFirstMessageWithAgentFromSDK(client, ctx.sessionID), + ]) + : [ + messageDir ? findNearestMessageWithFields(messageDir) : null, + messageDir ? findFirstMessageWithAgent(messageDir) : null, + ] + const sessionAgent = getSessionAgent(ctx.sessionID) const parentAgent = ctx.agent ?? sessionAgent ?? firstMessageAgent ?? prevMessage?.agent diff --git a/src/tools/call-omo-agent/background-agent-executor.test.ts b/src/tools/call-omo-agent/background-agent-executor.test.ts index 2c080e7e8..d27575c15 100644 --- a/src/tools/call-omo-agent/background-agent-executor.test.ts +++ b/src/tools/call-omo-agent/background-agent-executor.test.ts @@ -1,17 +1,22 @@ +/// import { describe, test, expect, mock } from "bun:test" import type { BackgroundManager } from "../../features/background-agent" +import type { PluginInput } from "@opencode-ai/plugin" import { executeBackgroundAgent } from "./background-agent-executor" describe("executeBackgroundAgent", () => { + const launchMock = mock(() => Promise.resolve({ + id: "test-task-id", + sessionID: null, + description: "Test task", + agent: "test-agent", + status: "pending", + })) + const getTaskMock = mock() + const mockManager = { - launch: mock(() => Promise.resolve({ - id: "test-task-id", - sessionID: null, - description: "Test task", - agent: "test-agent", - status: "pending", - })), - getTask: mock(), + launch: launchMock, + getTask: getTaskMock, } as unknown as BackgroundManager const testContext = { @@ -25,18 +30,25 @@ describe("executeBackgroundAgent", () => { description: "Test background task", prompt: "Test prompt", subagent_type: "test-agent", + run_in_background: true, } + const mockClient = { + session: { + messages: mock(() => Promise.resolve({ data: [] })), + }, + } as unknown as PluginInput["client"] + test("detects interrupted task as failure", async () => { //#given - mockManager.launch.mockResolvedValueOnce({ + launchMock.mockResolvedValueOnce({ id: "test-task-id", sessionID: null, description: "Test task", agent: "test-agent", status: "pending", }) - mockManager.getTask.mockReturnValueOnce({ + getTaskMock.mockReturnValueOnce({ id: "test-task-id", sessionID: null, description: "Test task", @@ -45,11 +57,11 @@ describe("executeBackgroundAgent", () => { }) //#when - const result = await executeBackgroundAgent(testArgs, testContext, mockManager) + const result = await executeBackgroundAgent(testArgs, testContext, mockManager, mockClient) //#then expect(result).toContain("Task failed to start") expect(result).toContain("interrupt") expect(result).toContain("test-task-id") }) -}) \ No newline at end of file +}) diff --git a/src/tools/delegate-task/parent-context-resolver.ts b/src/tools/delegate-task/parent-context-resolver.ts index 1eea7b7a5..4a1eda9c0 100644 --- a/src/tools/delegate-task/parent-context-resolver.ts +++ b/src/tools/delegate-task/parent-context-resolver.ts @@ -1,14 +1,33 @@ import type { ToolContextWithMetadata } from "./types" +import type { OpencodeClient } from "./types" import type { ParentContext } from "./executor-types" -import { findNearestMessageWithFields, findFirstMessageWithAgent } from "../../features/hook-message-injector" +import { + findFirstMessageWithAgent, + findFirstMessageWithAgentFromSDK, + findNearestMessageWithFields, + findNearestMessageWithFieldsFromSDK, +} from "../../features/hook-message-injector" import { getSessionAgent } from "../../features/claude-code-session-state" import { log } from "../../shared/logger" -import { getMessageDir } from "../../shared" +import { getMessageDir } from "../../shared/opencode-message-dir" +import { isSqliteBackend } from "../../shared/opencode-storage-detection" -export function resolveParentContext(ctx: ToolContextWithMetadata): ParentContext { +export async function resolveParentContext( + ctx: ToolContextWithMetadata, + client: OpencodeClient +): Promise { const messageDir = getMessageDir(ctx.sessionID) - const prevMessage = messageDir ? findNearestMessageWithFields(messageDir) : null - const firstMessageAgent = messageDir ? findFirstMessageWithAgent(messageDir) : null + + const [prevMessage, firstMessageAgent] = isSqliteBackend() + ? await Promise.all([ + findNearestMessageWithFieldsFromSDK(client, ctx.sessionID), + findFirstMessageWithAgentFromSDK(client, ctx.sessionID), + ]) + : [ + messageDir ? findNearestMessageWithFields(messageDir) : null, + messageDir ? findFirstMessageWithAgent(messageDir) : null, + ] + const sessionAgent = getSessionAgent(ctx.sessionID) const parentAgent = ctx.agent ?? sessionAgent ?? firstMessageAgent ?? prevMessage?.agent diff --git a/src/tools/delegate-task/tools.ts b/src/tools/delegate-task/tools.ts index cfa01ebec..763b09f0e 100644 --- a/src/tools/delegate-task/tools.ts +++ b/src/tools/delegate-task/tools.ts @@ -129,7 +129,7 @@ Prompts MUST be in English.` return skillError } - const parentContext = resolveParentContext(ctx) + const parentContext = await resolveParentContext(ctx, options.client) if (args.session_id) { if (runInBackground) {