From 2bf8b15f2475557f14d013c8975fcc9eb8dd0cfd Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Sun, 15 Feb 2026 14:56:58 +0900 Subject: [PATCH] feat: migrate hook callers to SDK message finders on SQLite backend --- src/hooks/atlas/event-handler.ts | 2 +- src/hooks/atlas/recent-model-resolver.ts | 16 +++++++--- src/hooks/atlas/session-last-agent.ts | 27 +++++++++++++---- .../prometheus-md-only/agent-resolution.ts | 30 +++++++++++++++++-- src/hooks/prometheus-md-only/hook.ts | 2 +- .../continuation-injection.ts | 11 +++++-- 6 files changed, 71 insertions(+), 17 deletions(-) diff --git a/src/hooks/atlas/event-handler.ts b/src/hooks/atlas/event-handler.ts index 0857c3db8..68c89e485 100644 --- a/src/hooks/atlas/event-handler.ts +++ b/src/hooks/atlas/event-handler.ts @@ -87,7 +87,7 @@ export function createAtlasEventHandler(input: { return } - const lastAgent = getLastAgentFromSession(sessionID) + const lastAgent = await getLastAgentFromSession(sessionID, ctx.client) const requiredAgent = (boulderState.agent ?? "atlas").toLowerCase() const lastAgentMatchesRequired = lastAgent === requiredAgent const boulderAgentWasNotExplicitlySet = boulderState.agent === undefined diff --git a/src/hooks/atlas/recent-model-resolver.ts b/src/hooks/atlas/recent-model-resolver.ts index ccaed01c7..a8509c322 100644 --- a/src/hooks/atlas/recent-model-resolver.ts +++ b/src/hooks/atlas/recent-model-resolver.ts @@ -1,6 +1,9 @@ import type { PluginInput } from "@opencode-ai/plugin" -import { findNearestMessageWithFields } from "../../features/hook-message-injector" -import { getMessageDir } from "../../shared" +import { + findNearestMessageWithFields, + findNearestMessageWithFieldsFromSDK, +} from "../../features/hook-message-injector" +import { getMessageDir, isSqliteBackend } from "../../shared" import type { ModelInfo } from "./types" export async function resolveRecentModelForSession( @@ -28,8 +31,13 @@ export async function resolveRecentModelForSession( // ignore - fallback to message storage } - const messageDir = getMessageDir(sessionID) - const currentMessage = messageDir ? findNearestMessageWithFields(messageDir) : null + let currentMessage = null + if (isSqliteBackend()) { + currentMessage = await findNearestMessageWithFieldsFromSDK(ctx.client, sessionID) + } else { + const messageDir = getMessageDir(sessionID) + currentMessage = messageDir ? findNearestMessageWithFields(messageDir) : null + } const model = currentMessage?.model if (!model?.providerID || !model?.modelID) { return undefined diff --git a/src/hooks/atlas/session-last-agent.ts b/src/hooks/atlas/session-last-agent.ts index 4afbf3e44..6ddbbacb6 100644 --- a/src/hooks/atlas/session-last-agent.ts +++ b/src/hooks/atlas/session-last-agent.ts @@ -1,9 +1,24 @@ -import { findNearestMessageWithFields } from "../../features/hook-message-injector" -import { getMessageDir } from "../../shared" +import type { PluginInput } from "@opencode-ai/plugin" + +import { findNearestMessageWithFields } from "../../features/hook-message-injector" +import { findNearestMessageWithFieldsFromSDK } from "../../features/hook-message-injector" +import { getMessageDir, isSqliteBackend } from "../../shared" + +type OpencodeClient = PluginInput["client"] + +export async function getLastAgentFromSession( + sessionID: string, + client?: OpencodeClient +): Promise { + let nearest = null + + if (isSqliteBackend() && client) { + nearest = await findNearestMessageWithFieldsFromSDK(client, sessionID) + } else { + const messageDir = getMessageDir(sessionID) + if (!messageDir) return null + nearest = findNearestMessageWithFields(messageDir) + } -export function getLastAgentFromSession(sessionID: string): string | null { - const messageDir = getMessageDir(sessionID) - if (!messageDir) return null - const nearest = findNearestMessageWithFields(messageDir) return nearest?.agent?.toLowerCase() ?? null } diff --git a/src/hooks/prometheus-md-only/agent-resolution.ts b/src/hooks/prometheus-md-only/agent-resolution.ts index c6adf2e89..22dc9cae0 100644 --- a/src/hooks/prometheus-md-only/agent-resolution.ts +++ b/src/hooks/prometheus-md-only/agent-resolution.ts @@ -1,9 +1,29 @@ +import type { PluginInput } from "@opencode-ai/plugin" + import { findNearestMessageWithFields, findFirstMessageWithAgent } from "../../features/hook-message-injector" +import { + findFirstMessageWithAgentFromSDK, + findNearestMessageWithFieldsFromSDK, +} from "../../features/hook-message-injector" import { getSessionAgent } from "../../features/claude-code-session-state" import { readBoulderState } from "../../features/boulder-state" import { getMessageDir } from "../../shared/opencode-message-dir" +import { isSqliteBackend } from "../../shared/opencode-storage-detection" + +type OpencodeClient = PluginInput["client"] + +async function getAgentFromMessageFiles( + sessionID: string, + client?: OpencodeClient +): Promise { + if (isSqliteBackend() && client) { + const firstAgent = await findFirstMessageWithAgentFromSDK(client, sessionID) + if (firstAgent) return firstAgent + + const nearest = await findNearestMessageWithFieldsFromSDK(client, sessionID) + return nearest?.agent + } -function getAgentFromMessageFiles(sessionID: string): string | undefined { const messageDir = getMessageDir(sessionID) if (!messageDir) return undefined return findFirstMessageWithAgent(messageDir) ?? findNearestMessageWithFields(messageDir)?.agent @@ -21,7 +41,11 @@ function getAgentFromMessageFiles(sessionID: string): string | undefined { * - Message files return "prometheus" (oldest message from /plan) * - But boulder.json has agent: "atlas" (set by /start-work) */ -export function getAgentFromSession(sessionID: string, directory: string): string | undefined { +export async function getAgentFromSession( + sessionID: string, + directory: string, + client?: OpencodeClient +): Promise { // Check in-memory first (current session) const memoryAgent = getSessionAgent(sessionID) if (memoryAgent) return memoryAgent @@ -33,5 +57,5 @@ export function getAgentFromSession(sessionID: string, directory: string): strin } // Fallback to message files - return getAgentFromMessageFiles(sessionID) + return await getAgentFromMessageFiles(sessionID, client) } diff --git a/src/hooks/prometheus-md-only/hook.ts b/src/hooks/prometheus-md-only/hook.ts index b0b5a01af..846238ba1 100644 --- a/src/hooks/prometheus-md-only/hook.ts +++ b/src/hooks/prometheus-md-only/hook.ts @@ -15,7 +15,7 @@ export function createPrometheusMdOnlyHook(ctx: PluginInput) { input: { tool: string; sessionID: string; callID: string }, output: { args: Record; message?: string } ): Promise => { - const agentName = getAgentFromSession(input.sessionID, ctx.directory) + const agentName = await getAgentFromSession(input.sessionID, ctx.directory, ctx.client) if (!isPrometheusAgent(agentName)) { return diff --git a/src/hooks/todo-continuation-enforcer/continuation-injection.ts b/src/hooks/todo-continuation-enforcer/continuation-injection.ts index 3f44db3a0..ded4ad3dd 100644 --- a/src/hooks/todo-continuation-enforcer/continuation-injection.ts +++ b/src/hooks/todo-continuation-enforcer/continuation-injection.ts @@ -3,9 +3,11 @@ import type { PluginInput } from "@opencode-ai/plugin" import type { BackgroundManager } from "../../features/background-agent" import { findNearestMessageWithFields, + findNearestMessageWithFieldsFromSDK, type ToolPermission, } from "../../features/hook-message-injector" import { log } from "../../shared/logger" +import { isSqliteBackend } from "../../shared/opencode-storage-detection" import { CONTINUATION_PROMPT, @@ -78,8 +80,13 @@ export async function injectContinuation(args: { let tools = resolvedInfo?.tools if (!agentName || !model) { - const messageDir = getMessageDir(sessionID) - const previousMessage = messageDir ? findNearestMessageWithFields(messageDir) : null + let previousMessage = null + if (isSqliteBackend()) { + previousMessage = await findNearestMessageWithFieldsFromSDK(ctx.client, sessionID) + } else { + const messageDir = getMessageDir(sessionID) + previousMessage = messageDir ? findNearestMessageWithFields(messageDir) : null + } agentName = agentName ?? previousMessage?.agent model = model ??