fix(prometheus): prevent agent fallback to build in background tasks (#695)
This commit is contained in:
parent
2314a0d371
commit
8d65748ad3
@ -9,3 +9,23 @@ export function setMainSession(id: string | undefined) {
|
|||||||
export function getMainSessionID(): string | undefined {
|
export function getMainSessionID(): string | undefined {
|
||||||
return mainSessionID
|
return mainSessionID
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const sessionAgentMap = new Map<string, string>()
|
||||||
|
|
||||||
|
export function setSessionAgent(sessionID: string, agent: string): void {
|
||||||
|
if (!sessionAgentMap.has(sessionID)) {
|
||||||
|
sessionAgentMap.set(sessionID, agent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function updateSessionAgent(sessionID: string, agent: string): void {
|
||||||
|
sessionAgentMap.set(sessionID, agent)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getSessionAgent(sessionID: string): string | undefined {
|
||||||
|
return sessionAgentMap.get(sessionID)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function clearSessionAgent(sessionID: string): void {
|
||||||
|
sessionAgentMap.delete(sessionID)
|
||||||
|
}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
export { injectHookMessage, findNearestMessageWithFields } from "./injector"
|
export { injectHookMessage, findNearestMessageWithFields, findFirstMessageWithAgent } from "./injector"
|
||||||
export type { StoredMessage } from "./injector"
|
export type { StoredMessage } from "./injector"
|
||||||
export type { MessageMeta, OriginalMessageContext, TextPart } from "./types"
|
export type { MessageMeta, OriginalMessageContext, TextPart } from "./types"
|
||||||
export { MESSAGE_STORAGE } from "./constants"
|
export { MESSAGE_STORAGE } from "./constants"
|
||||||
|
|||||||
@ -48,6 +48,35 @@ export function findNearestMessageWithFields(messageDir: string): StoredMessage
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds the FIRST (oldest) message in the session with agent field.
|
||||||
|
* This is used to get the original agent that started the session,
|
||||||
|
* avoiding issues where newer messages may have a different agent
|
||||||
|
* due to OpenCode's internal agent switching.
|
||||||
|
*/
|
||||||
|
export function findFirstMessageWithAgent(messageDir: string): string | null {
|
||||||
|
try {
|
||||||
|
const files = readdirSync(messageDir)
|
||||||
|
.filter((f) => f.endsWith(".json"))
|
||||||
|
.sort() // Oldest first (no reverse)
|
||||||
|
|
||||||
|
for (const file of files) {
|
||||||
|
try {
|
||||||
|
const content = readFileSync(join(messageDir, file), "utf-8")
|
||||||
|
const msg = JSON.parse(content) as StoredMessage
|
||||||
|
if (msg.agent) {
|
||||||
|
return msg.agent
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
function generateMessageId(): string {
|
function generateMessageId(): string {
|
||||||
const timestamp = Date.now().toString(16)
|
const timestamp = Date.now().toString(16)
|
||||||
const random = Math.random().toString(36).substring(2, 14)
|
const random = Math.random().toString(36).substring(2, 14)
|
||||||
|
|||||||
@ -2,7 +2,8 @@ import type { PluginInput } from "@opencode-ai/plugin"
|
|||||||
import { existsSync, readdirSync } from "node:fs"
|
import { existsSync, readdirSync } from "node:fs"
|
||||||
import { join, resolve, relative, isAbsolute } from "node:path"
|
import { join, resolve, relative, isAbsolute } from "node:path"
|
||||||
import { HOOK_NAME, PROMETHEUS_AGENTS, ALLOWED_EXTENSIONS, ALLOWED_PATH_PREFIX, BLOCKED_TOOLS, PLANNING_CONSULT_WARNING } from "./constants"
|
import { HOOK_NAME, PROMETHEUS_AGENTS, ALLOWED_EXTENSIONS, ALLOWED_PATH_PREFIX, BLOCKED_TOOLS, PLANNING_CONSULT_WARNING } from "./constants"
|
||||||
import { findNearestMessageWithFields, MESSAGE_STORAGE } from "../../features/hook-message-injector"
|
import { findNearestMessageWithFields, findFirstMessageWithAgent, MESSAGE_STORAGE } from "../../features/hook-message-injector"
|
||||||
|
import { getSessionAgent } from "../../features/claude-code-session-state"
|
||||||
import { log } from "../../shared/logger"
|
import { log } from "../../shared/logger"
|
||||||
|
|
||||||
export * from "./constants"
|
export * from "./constants"
|
||||||
@ -61,10 +62,14 @@ function getMessageDir(sessionID: string): string | null {
|
|||||||
|
|
||||||
const TASK_TOOLS = ["sisyphus_task", "task", "call_omo_agent"]
|
const TASK_TOOLS = ["sisyphus_task", "task", "call_omo_agent"]
|
||||||
|
|
||||||
function getAgentFromSession(sessionID: string): string | undefined {
|
function getAgentFromMessageFiles(sessionID: string): string | undefined {
|
||||||
const messageDir = getMessageDir(sessionID)
|
const messageDir = getMessageDir(sessionID)
|
||||||
if (!messageDir) return undefined
|
if (!messageDir) return undefined
|
||||||
return findNearestMessageWithFields(messageDir)?.agent
|
return findFirstMessageWithAgent(messageDir) ?? findNearestMessageWithFields(messageDir)?.agent
|
||||||
|
}
|
||||||
|
|
||||||
|
function getAgentFromSession(sessionID: string): string | undefined {
|
||||||
|
return getSessionAgent(sessionID) ?? getAgentFromMessageFiles(sessionID)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createPrometheusMdOnlyHook(ctx: PluginInput) {
|
export function createPrometheusMdOnlyHook(ctx: PluginInput) {
|
||||||
|
|||||||
13
src/index.ts
13
src/index.ts
@ -49,6 +49,8 @@ import { getSystemMcpServerNames } from "./features/claude-code-mcp-loader";
|
|||||||
import {
|
import {
|
||||||
setMainSession,
|
setMainSession,
|
||||||
getMainSessionID,
|
getMainSessionID,
|
||||||
|
setSessionAgent,
|
||||||
|
clearSessionAgent,
|
||||||
} from "./features/claude-code-session-state";
|
} from "./features/claude-code-session-state";
|
||||||
import {
|
import {
|
||||||
builtinTools,
|
builtinTools,
|
||||||
@ -428,11 +430,22 @@ const OhMyOpenCodePlugin: Plugin = async (ctx) => {
|
|||||||
setMainSession(undefined);
|
setMainSession(undefined);
|
||||||
}
|
}
|
||||||
if (sessionInfo?.id) {
|
if (sessionInfo?.id) {
|
||||||
|
clearSessionAgent(sessionInfo.id);
|
||||||
await skillMcpManager.disconnectSession(sessionInfo.id);
|
await skillMcpManager.disconnectSession(sessionInfo.id);
|
||||||
await lspManager.cleanupTempDirectoryClients();
|
await lspManager.cleanupTempDirectoryClients();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (event.type === "message.updated") {
|
||||||
|
const info = props?.info as Record<string, unknown> | undefined;
|
||||||
|
const sessionID = info?.sessionID as string | undefined;
|
||||||
|
const agent = info?.agent as string | undefined;
|
||||||
|
const role = info?.role as string | undefined;
|
||||||
|
if (sessionID && agent && role === "user") {
|
||||||
|
setSessionAgent(sessionID, agent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (event.type === "session.error") {
|
if (event.type === "session.error") {
|
||||||
const sessionID = props?.sessionID as string | undefined;
|
const sessionID = props?.sessionID as string | undefined;
|
||||||
const error = props?.error;
|
const error = props?.error;
|
||||||
|
|||||||
@ -4,7 +4,9 @@ import { join } from "node:path"
|
|||||||
import type { BackgroundManager, BackgroundTask } from "../../features/background-agent"
|
import type { BackgroundManager, BackgroundTask } from "../../features/background-agent"
|
||||||
import type { BackgroundTaskArgs, BackgroundOutputArgs, BackgroundCancelArgs } from "./types"
|
import type { BackgroundTaskArgs, BackgroundOutputArgs, BackgroundCancelArgs } from "./types"
|
||||||
import { BACKGROUND_TASK_DESCRIPTION, BACKGROUND_OUTPUT_DESCRIPTION, BACKGROUND_CANCEL_DESCRIPTION } from "./constants"
|
import { BACKGROUND_TASK_DESCRIPTION, BACKGROUND_OUTPUT_DESCRIPTION, BACKGROUND_CANCEL_DESCRIPTION } from "./constants"
|
||||||
import { findNearestMessageWithFields, MESSAGE_STORAGE } from "../../features/hook-message-injector"
|
import { findNearestMessageWithFields, findFirstMessageWithAgent, MESSAGE_STORAGE } from "../../features/hook-message-injector"
|
||||||
|
import { getSessionAgent } from "../../features/claude-code-session-state"
|
||||||
|
import { log } from "../../shared/logger"
|
||||||
|
|
||||||
type OpencodeClient = PluginInput["client"]
|
type OpencodeClient = PluginInput["client"]
|
||||||
|
|
||||||
@ -63,6 +65,19 @@ export function createBackgroundTask(manager: BackgroundManager): ToolDefinition
|
|||||||
try {
|
try {
|
||||||
const messageDir = getMessageDir(ctx.sessionID)
|
const messageDir = getMessageDir(ctx.sessionID)
|
||||||
const prevMessage = messageDir ? findNearestMessageWithFields(messageDir) : null
|
const prevMessage = messageDir ? findNearestMessageWithFields(messageDir) : null
|
||||||
|
const firstMessageAgent = messageDir ? findFirstMessageWithAgent(messageDir) : null
|
||||||
|
const sessionAgent = getSessionAgent(ctx.sessionID)
|
||||||
|
const parentAgent = ctx.agent ?? sessionAgent ?? firstMessageAgent ?? prevMessage?.agent
|
||||||
|
|
||||||
|
log("[background_task] parentAgent resolution", {
|
||||||
|
sessionID: ctx.sessionID,
|
||||||
|
ctxAgent: ctx.agent,
|
||||||
|
sessionAgent,
|
||||||
|
firstMessageAgent,
|
||||||
|
prevMessageAgent: prevMessage?.agent,
|
||||||
|
resolvedParentAgent: parentAgent,
|
||||||
|
})
|
||||||
|
|
||||||
const parentModel = prevMessage?.model?.providerID && prevMessage?.model?.modelID
|
const parentModel = prevMessage?.model?.providerID && prevMessage?.model?.modelID
|
||||||
? { providerID: prevMessage.model.providerID, modelID: prevMessage.model.modelID }
|
? { providerID: prevMessage.model.providerID, modelID: prevMessage.model.modelID }
|
||||||
: undefined
|
: undefined
|
||||||
@ -74,7 +89,7 @@ export function createBackgroundTask(manager: BackgroundManager): ToolDefinition
|
|||||||
parentSessionID: ctx.sessionID,
|
parentSessionID: ctx.sessionID,
|
||||||
parentMessageID: ctx.messageID,
|
parentMessageID: ctx.messageID,
|
||||||
parentModel,
|
parentModel,
|
||||||
parentAgent: ctx.agent ?? prevMessage?.agent,
|
parentAgent,
|
||||||
})
|
})
|
||||||
|
|
||||||
ctx.metadata?.({
|
ctx.metadata?.({
|
||||||
|
|||||||
@ -1,8 +1,26 @@
|
|||||||
import { tool, type PluginInput, type ToolDefinition } from "@opencode-ai/plugin"
|
import { tool, type PluginInput, type ToolDefinition } from "@opencode-ai/plugin"
|
||||||
|
import { existsSync, readdirSync } from "node:fs"
|
||||||
|
import { join } from "node:path"
|
||||||
import { ALLOWED_AGENTS, CALL_OMO_AGENT_DESCRIPTION } from "./constants"
|
import { ALLOWED_AGENTS, CALL_OMO_AGENT_DESCRIPTION } from "./constants"
|
||||||
import type { CallOmoAgentArgs } from "./types"
|
import type { CallOmoAgentArgs } from "./types"
|
||||||
import type { BackgroundManager } from "../../features/background-agent"
|
import type { BackgroundManager } from "../../features/background-agent"
|
||||||
import { log } from "../../shared/logger"
|
import { log } from "../../shared/logger"
|
||||||
|
import { findFirstMessageWithAgent, findNearestMessageWithFields, MESSAGE_STORAGE } from "../../features/hook-message-injector"
|
||||||
|
import { getSessionAgent } from "../../features/claude-code-session-state"
|
||||||
|
|
||||||
|
function getMessageDir(sessionID: string): string | null {
|
||||||
|
if (!existsSync(MESSAGE_STORAGE)) return null
|
||||||
|
|
||||||
|
const directPath = join(MESSAGE_STORAGE, sessionID)
|
||||||
|
if (existsSync(directPath)) return directPath
|
||||||
|
|
||||||
|
for (const dir of readdirSync(MESSAGE_STORAGE)) {
|
||||||
|
const sessionPath = join(MESSAGE_STORAGE, dir, sessionID)
|
||||||
|
if (existsSync(sessionPath)) return sessionPath
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
type ToolContextWithMetadata = {
|
type ToolContextWithMetadata = {
|
||||||
sessionID: string
|
sessionID: string
|
||||||
@ -60,12 +78,29 @@ async function executeBackground(
|
|||||||
manager: BackgroundManager
|
manager: BackgroundManager
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
try {
|
try {
|
||||||
|
const messageDir = getMessageDir(toolContext.sessionID)
|
||||||
|
const prevMessage = messageDir ? findNearestMessageWithFields(messageDir) : null
|
||||||
|
const firstMessageAgent = messageDir ? findFirstMessageWithAgent(messageDir) : null
|
||||||
|
const sessionAgent = getSessionAgent(toolContext.sessionID)
|
||||||
|
const parentAgent = toolContext.agent ?? sessionAgent ?? firstMessageAgent ?? prevMessage?.agent
|
||||||
|
|
||||||
|
log("[call_omo_agent] parentAgent resolution", {
|
||||||
|
sessionID: toolContext.sessionID,
|
||||||
|
messageDir,
|
||||||
|
ctxAgent: toolContext.agent,
|
||||||
|
sessionAgent,
|
||||||
|
firstMessageAgent,
|
||||||
|
prevMessageAgent: prevMessage?.agent,
|
||||||
|
resolvedParentAgent: parentAgent,
|
||||||
|
})
|
||||||
|
|
||||||
const task = await manager.launch({
|
const task = await manager.launch({
|
||||||
description: args.description,
|
description: args.description,
|
||||||
prompt: args.prompt,
|
prompt: args.prompt,
|
||||||
agent: args.subagent_type,
|
agent: args.subagent_type,
|
||||||
parentSessionID: toolContext.sessionID,
|
parentSessionID: toolContext.sessionID,
|
||||||
parentMessageID: toolContext.messageID,
|
parentMessageID: toolContext.messageID,
|
||||||
|
parentAgent,
|
||||||
})
|
})
|
||||||
|
|
||||||
toolContext.metadata?.({
|
toolContext.metadata?.({
|
||||||
|
|||||||
@ -5,11 +5,12 @@ import type { BackgroundManager } from "../../features/background-agent"
|
|||||||
import type { SisyphusTaskArgs } from "./types"
|
import type { SisyphusTaskArgs } from "./types"
|
||||||
import type { CategoryConfig, CategoriesConfig, GitMasterConfig } from "../../config/schema"
|
import type { CategoryConfig, CategoriesConfig, GitMasterConfig } from "../../config/schema"
|
||||||
import { SISYPHUS_TASK_DESCRIPTION, DEFAULT_CATEGORIES, CATEGORY_PROMPT_APPENDS } from "./constants"
|
import { SISYPHUS_TASK_DESCRIPTION, DEFAULT_CATEGORIES, CATEGORY_PROMPT_APPENDS } from "./constants"
|
||||||
import { findNearestMessageWithFields, MESSAGE_STORAGE } from "../../features/hook-message-injector"
|
import { findNearestMessageWithFields, findFirstMessageWithAgent, MESSAGE_STORAGE } from "../../features/hook-message-injector"
|
||||||
import { resolveMultipleSkills } from "../../features/opencode-skill-loader/skill-content"
|
import { resolveMultipleSkills } from "../../features/opencode-skill-loader/skill-content"
|
||||||
import { createBuiltinSkills } from "../../features/builtin-skills/skills"
|
import { createBuiltinSkills } from "../../features/builtin-skills/skills"
|
||||||
import { getTaskToastManager } from "../../features/task-toast-manager"
|
import { getTaskToastManager } from "../../features/task-toast-manager"
|
||||||
import { subagentSessions } from "../../features/claude-code-session-state"
|
import { subagentSessions, getSessionAgent } from "../../features/claude-code-session-state"
|
||||||
|
import { log } from "../../shared/logger"
|
||||||
|
|
||||||
type OpencodeClient = PluginInput["client"]
|
type OpencodeClient = PluginInput["client"]
|
||||||
|
|
||||||
@ -147,7 +148,19 @@ export function createSisyphusTask(options: SisyphusTaskToolOptions): ToolDefini
|
|||||||
|
|
||||||
const messageDir = getMessageDir(ctx.sessionID)
|
const messageDir = getMessageDir(ctx.sessionID)
|
||||||
const prevMessage = messageDir ? findNearestMessageWithFields(messageDir) : null
|
const prevMessage = messageDir ? findNearestMessageWithFields(messageDir) : null
|
||||||
const parentAgent = ctx.agent ?? prevMessage?.agent
|
const firstMessageAgent = messageDir ? findFirstMessageWithAgent(messageDir) : null
|
||||||
|
const sessionAgent = getSessionAgent(ctx.sessionID)
|
||||||
|
const parentAgent = ctx.agent ?? sessionAgent ?? firstMessageAgent ?? prevMessage?.agent
|
||||||
|
|
||||||
|
log("[sisyphus_task] parentAgent resolution", {
|
||||||
|
sessionID: ctx.sessionID,
|
||||||
|
messageDir,
|
||||||
|
ctxAgent: ctx.agent,
|
||||||
|
sessionAgent,
|
||||||
|
firstMessageAgent,
|
||||||
|
prevMessageAgent: prevMessage?.agent,
|
||||||
|
resolvedParentAgent: parentAgent,
|
||||||
|
})
|
||||||
const parentModel = prevMessage?.model?.providerID && prevMessage?.model?.modelID
|
const parentModel = prevMessage?.model?.providerID && prevMessage?.model?.modelID
|
||||||
? { providerID: prevMessage.model.providerID, modelID: prevMessage.model.modelID }
|
? { providerID: prevMessage.model.providerID, modelID: prevMessage.model.modelID }
|
||||||
: undefined
|
: undefined
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user