2026-02-08 16:25:25 +09:00
|
|
|
import type { ToolDefinition } from "@opencode-ai/plugin"
|
2026-03-27 17:42:43 +09:00
|
|
|
import type { SkillLoadOptions } from "../tools/skill/types"
|
2026-02-08 16:25:25 +09:00
|
|
|
|
|
|
|
|
import type {
|
|
|
|
|
AvailableCategory,
|
|
|
|
|
} from "../agents/dynamic-agent-prompt-builder"
|
|
|
|
|
import type { OhMyOpenCodeConfig } from "../config"
|
2026-04-05 13:27:55 +09:00
|
|
|
import { isInteractiveBashEnabled } from "../create-runtime-tmux-config"
|
2026-04-07 22:49:37 +09:00
|
|
|
import * as openclawRuntimeDispatch from "../openclaw/runtime-dispatch"
|
2026-02-08 16:25:25 +09:00
|
|
|
import type { PluginContext, ToolsRecord } from "./types"
|
|
|
|
|
|
|
|
|
|
import {
|
|
|
|
|
builtinTools,
|
|
|
|
|
createBackgroundTools,
|
|
|
|
|
createCallOmoAgent,
|
|
|
|
|
createLookAt,
|
|
|
|
|
createSkillMcpTool,
|
2026-02-18 18:40:10 +08:00
|
|
|
createSkillTool,
|
2026-02-08 16:25:25 +09:00
|
|
|
createGrepTools,
|
|
|
|
|
createGlobTools,
|
|
|
|
|
createAstGrepTools,
|
|
|
|
|
createSessionManagerTools,
|
|
|
|
|
createDelegateTask,
|
|
|
|
|
discoverCommandsSync,
|
|
|
|
|
interactive_bash,
|
|
|
|
|
createTaskCreateTool,
|
|
|
|
|
createTaskGetTool,
|
|
|
|
|
createTaskList,
|
|
|
|
|
createTaskUpdateTool,
|
2026-02-16 16:32:33 +09:00
|
|
|
createHashlineEditTool,
|
2026-02-08 16:25:25 +09:00
|
|
|
} from "../tools"
|
|
|
|
|
import { getMainSessionID } from "../features/claude-code-session-state"
|
|
|
|
|
import { filterDisabledTools } from "../shared/disabled-tools"
|
2026-04-03 17:07:39 +09:00
|
|
|
import { isTaskSystemEnabled, log } from "../shared"
|
2026-02-08 16:25:25 +09:00
|
|
|
|
|
|
|
|
import type { Managers } from "../create-managers"
|
|
|
|
|
import type { SkillContext } from "./skill-context"
|
2026-03-08 02:21:42 +09:00
|
|
|
import { normalizeToolArgSchemas } from "./normalize-tool-arg-schemas"
|
2026-02-08 16:25:25 +09:00
|
|
|
|
2026-04-10 15:53:07 +09:00
|
|
|
type ToolRegistryFactories = {
|
|
|
|
|
builtinTools: typeof builtinTools
|
|
|
|
|
createBackgroundTools: typeof createBackgroundTools
|
|
|
|
|
createCallOmoAgent: typeof createCallOmoAgent
|
|
|
|
|
createLookAt: typeof createLookAt
|
|
|
|
|
createSkillMcpTool: typeof createSkillMcpTool
|
|
|
|
|
createSkillTool: typeof createSkillTool
|
|
|
|
|
createGrepTools: typeof createGrepTools
|
|
|
|
|
createGlobTools: typeof createGlobTools
|
|
|
|
|
createAstGrepTools: typeof createAstGrepTools
|
|
|
|
|
createSessionManagerTools: typeof createSessionManagerTools
|
|
|
|
|
createDelegateTask: typeof createDelegateTask
|
|
|
|
|
discoverCommandsSync: typeof discoverCommandsSync
|
|
|
|
|
interactive_bash: typeof interactive_bash
|
|
|
|
|
createTaskCreateTool: typeof createTaskCreateTool
|
|
|
|
|
createTaskGetTool: typeof createTaskGetTool
|
|
|
|
|
createTaskList: typeof createTaskList
|
|
|
|
|
createTaskUpdateTool: typeof createTaskUpdateTool
|
|
|
|
|
createHashlineEditTool: typeof createHashlineEditTool
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const defaultToolRegistryFactories: ToolRegistryFactories = {
|
|
|
|
|
builtinTools,
|
|
|
|
|
createBackgroundTools,
|
|
|
|
|
createCallOmoAgent,
|
|
|
|
|
createLookAt,
|
|
|
|
|
createSkillMcpTool,
|
|
|
|
|
createSkillTool,
|
|
|
|
|
createGrepTools,
|
|
|
|
|
createGlobTools,
|
|
|
|
|
createAstGrepTools,
|
|
|
|
|
createSessionManagerTools,
|
|
|
|
|
createDelegateTask,
|
|
|
|
|
discoverCommandsSync,
|
|
|
|
|
interactive_bash,
|
|
|
|
|
createTaskCreateTool,
|
|
|
|
|
createTaskGetTool,
|
|
|
|
|
createTaskList,
|
|
|
|
|
createTaskUpdateTool,
|
|
|
|
|
createHashlineEditTool,
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-08 16:25:25 +09:00
|
|
|
export type ToolRegistryResult = {
|
|
|
|
|
filteredTools: ToolsRecord
|
|
|
|
|
taskSystemEnabled: boolean
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-28 15:24:18 +09:00
|
|
|
const LOW_PRIORITY_TOOL_ORDER = [
|
|
|
|
|
"session_list",
|
|
|
|
|
"session_read",
|
|
|
|
|
"session_search",
|
|
|
|
|
"session_info",
|
|
|
|
|
"interactive_bash",
|
|
|
|
|
"look_at",
|
|
|
|
|
"call_omo_agent",
|
|
|
|
|
"task_create",
|
|
|
|
|
"task_get",
|
|
|
|
|
"task_list",
|
|
|
|
|
"task_update",
|
|
|
|
|
"background_output",
|
|
|
|
|
"background_cancel",
|
2026-03-31 16:59:04 -07:00
|
|
|
"edit",
|
2026-03-28 15:24:18 +09:00
|
|
|
"ast_grep_replace",
|
|
|
|
|
"ast_grep_search",
|
|
|
|
|
"glob",
|
|
|
|
|
"grep",
|
|
|
|
|
"skill_mcp",
|
|
|
|
|
"skill",
|
|
|
|
|
"task",
|
|
|
|
|
"lsp_rename",
|
|
|
|
|
"lsp_prepare_rename",
|
|
|
|
|
"lsp_find_references",
|
|
|
|
|
"lsp_goto_definition",
|
|
|
|
|
"lsp_symbols",
|
|
|
|
|
"lsp_diagnostics",
|
|
|
|
|
] as const
|
|
|
|
|
|
2026-03-31 16:59:04 -07:00
|
|
|
export function trimToolsToCap(filteredTools: ToolsRecord, maxTools: number): void {
|
2026-03-28 15:24:18 +09:00
|
|
|
const toolNames = Object.keys(filteredTools)
|
|
|
|
|
if (toolNames.length <= maxTools) return
|
|
|
|
|
|
|
|
|
|
const removableToolNames = [
|
|
|
|
|
...LOW_PRIORITY_TOOL_ORDER.filter((toolName) => toolNames.includes(toolName)),
|
|
|
|
|
...toolNames
|
|
|
|
|
.filter((toolName) => !LOW_PRIORITY_TOOL_ORDER.includes(toolName as (typeof LOW_PRIORITY_TOOL_ORDER)[number]))
|
|
|
|
|
.sort(),
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
let currentCount = toolNames.length
|
|
|
|
|
let removed = 0
|
|
|
|
|
|
|
|
|
|
for (const toolName of removableToolNames) {
|
|
|
|
|
if (currentCount <= maxTools) break
|
|
|
|
|
if (!filteredTools[toolName]) continue
|
|
|
|
|
delete filteredTools[toolName]
|
|
|
|
|
currentCount -= 1
|
|
|
|
|
removed += 1
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
log(
|
|
|
|
|
`[tool-registry] Trimmed ${removed} tools to satisfy max_tools=${maxTools}. Final plugin tool count=${currentCount}.`,
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-08 16:25:25 +09:00
|
|
|
export function createToolRegistry(args: {
|
|
|
|
|
ctx: PluginContext
|
|
|
|
|
pluginConfig: OhMyOpenCodeConfig
|
|
|
|
|
managers: Pick<Managers, "backgroundManager" | "tmuxSessionManager" | "skillMcpManager">
|
|
|
|
|
skillContext: SkillContext
|
|
|
|
|
availableCategories: AvailableCategory[]
|
2026-04-05 13:27:55 +09:00
|
|
|
interactiveBashEnabled?: boolean
|
2026-04-10 15:53:07 +09:00
|
|
|
toolFactories?: Partial<ToolRegistryFactories>
|
2026-02-08 16:25:25 +09:00
|
|
|
}): ToolRegistryResult {
|
2026-04-05 13:27:55 +09:00
|
|
|
const {
|
|
|
|
|
ctx,
|
|
|
|
|
pluginConfig,
|
|
|
|
|
managers,
|
|
|
|
|
skillContext,
|
|
|
|
|
availableCategories,
|
|
|
|
|
interactiveBashEnabled = isInteractiveBashEnabled(),
|
2026-04-10 15:53:07 +09:00
|
|
|
toolFactories,
|
2026-04-05 13:27:55 +09:00
|
|
|
} = args
|
2026-04-10 15:53:07 +09:00
|
|
|
const factories: ToolRegistryFactories = {
|
|
|
|
|
...defaultToolRegistryFactories,
|
|
|
|
|
...toolFactories,
|
|
|
|
|
}
|
|
|
|
|
const backgroundTools = factories.createBackgroundTools(managers.backgroundManager, ctx.client)
|
|
|
|
|
const callOmoAgent = factories.createCallOmoAgent(
|
2026-03-04 20:43:55 +01:00
|
|
|
ctx,
|
|
|
|
|
managers.backgroundManager,
|
|
|
|
|
pluginConfig.disabled_agents ?? [],
|
|
|
|
|
pluginConfig.agents,
|
|
|
|
|
pluginConfig.categories,
|
|
|
|
|
)
|
2026-02-08 16:25:25 +09:00
|
|
|
|
|
|
|
|
const isMultimodalLookerEnabled = !(pluginConfig.disabled_agents ?? []).some(
|
|
|
|
|
(agent) => agent.toLowerCase() === "multimodal-looker",
|
|
|
|
|
)
|
2026-04-10 15:53:07 +09:00
|
|
|
const lookAt = isMultimodalLookerEnabled ? factories.createLookAt(ctx) : null
|
2026-02-08 16:25:25 +09:00
|
|
|
|
2026-04-10 15:53:07 +09:00
|
|
|
const delegateTask = factories.createDelegateTask({
|
2026-02-08 16:25:25 +09:00
|
|
|
manager: managers.backgroundManager,
|
|
|
|
|
client: ctx.client,
|
|
|
|
|
directory: ctx.directory,
|
|
|
|
|
userCategories: pluginConfig.categories,
|
2026-02-18 15:00:09 +07:00
|
|
|
agentOverrides: pluginConfig.agents,
|
2026-02-08 16:25:25 +09:00
|
|
|
gitMasterConfig: pluginConfig.git_master,
|
|
|
|
|
sisyphusJuniorModel: pluginConfig.agents?.["sisyphus-junior"]?.model,
|
|
|
|
|
browserProvider: skillContext.browserProvider,
|
|
|
|
|
disabledSkills: skillContext.disabledSkills,
|
|
|
|
|
availableCategories,
|
|
|
|
|
availableSkills: skillContext.availableSkills,
|
2026-03-28 09:34:58 -07:00
|
|
|
sisyphusAgentConfig: pluginConfig.sisyphus_agent,
|
2026-02-27 13:54:50 +08:00
|
|
|
syncPollTimeoutMs: pluginConfig.background_task?.syncPollTimeoutMs,
|
2026-02-08 16:25:25 +09:00
|
|
|
onSyncSessionCreated: async (event) => {
|
|
|
|
|
log("[index] onSyncSessionCreated callback", {
|
|
|
|
|
sessionID: event.sessionID,
|
|
|
|
|
parentID: event.parentID,
|
|
|
|
|
title: event.title,
|
|
|
|
|
})
|
|
|
|
|
await managers.tmuxSessionManager.onSessionCreated({
|
|
|
|
|
type: "session.created",
|
|
|
|
|
properties: {
|
|
|
|
|
info: {
|
|
|
|
|
id: event.sessionID,
|
|
|
|
|
parentID: event.parentID,
|
|
|
|
|
title: event.title,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
})
|
2026-04-07 22:49:37 +09:00
|
|
|
|
|
|
|
|
if (pluginConfig.openclaw) {
|
|
|
|
|
await openclawRuntimeDispatch.dispatchOpenClawEvent({
|
|
|
|
|
config: pluginConfig.openclaw,
|
|
|
|
|
rawEvent: "session.created",
|
|
|
|
|
context: {
|
|
|
|
|
sessionId: event.sessionID,
|
|
|
|
|
projectPath: ctx.directory,
|
|
|
|
|
tmuxPaneId: managers.tmuxSessionManager.getTrackedPaneId?.(event.sessionID) ?? process.env.TMUX_PANE,
|
|
|
|
|
},
|
|
|
|
|
})
|
|
|
|
|
}
|
2026-02-08 16:25:25 +09:00
|
|
|
},
|
|
|
|
|
})
|
|
|
|
|
|
2026-04-02 15:45:39 +09:00
|
|
|
const getSessionIDForMcp = (): string | undefined => getMainSessionID()
|
2026-02-08 16:25:25 +09:00
|
|
|
|
2026-04-10 15:53:07 +09:00
|
|
|
const skillMcpTool = factories.createSkillMcpTool({
|
2026-02-08 16:25:25 +09:00
|
|
|
manager: managers.skillMcpManager,
|
|
|
|
|
getLoadedSkills: () => skillContext.mergedSkills,
|
|
|
|
|
getSessionID: getSessionIDForMcp,
|
|
|
|
|
})
|
|
|
|
|
|
2026-04-10 15:53:07 +09:00
|
|
|
const commands = factories.discoverCommandsSync(ctx.directory, {
|
2026-02-21 10:05:50 -05:00
|
|
|
pluginsEnabled: pluginConfig.claude_code?.plugins ?? true,
|
|
|
|
|
enabledPluginsOverride: pluginConfig.claude_code?.plugins_override,
|
|
|
|
|
})
|
2026-04-10 15:53:07 +09:00
|
|
|
const skillTool = factories.createSkillTool({
|
2026-02-08 16:25:25 +09:00
|
|
|
commands,
|
|
|
|
|
skills: skillContext.mergedSkills,
|
2026-02-09 23:51:57 +08:00
|
|
|
mcpManager: managers.skillMcpManager,
|
|
|
|
|
getSessionID: getSessionIDForMcp,
|
|
|
|
|
gitMasterConfig: pluginConfig.git_master,
|
2026-03-27 17:58:38 +09:00
|
|
|
browserProvider: skillContext.browserProvider,
|
2026-03-27 17:42:43 +09:00
|
|
|
nativeSkills: "skills" in ctx ? (ctx as { skills: SkillLoadOptions["nativeSkills"] }).skills : undefined,
|
2026-02-08 16:25:25 +09:00
|
|
|
})
|
|
|
|
|
|
2026-04-03 17:07:39 +09:00
|
|
|
const taskSystemEnabled = isTaskSystemEnabled(pluginConfig)
|
2026-02-08 16:25:25 +09:00
|
|
|
const taskToolsRecord: Record<string, ToolDefinition> = taskSystemEnabled
|
|
|
|
|
? {
|
2026-04-10 15:53:07 +09:00
|
|
|
task_create: factories.createTaskCreateTool(pluginConfig, ctx),
|
|
|
|
|
task_get: factories.createTaskGetTool(pluginConfig),
|
|
|
|
|
task_list: factories.createTaskList(pluginConfig),
|
|
|
|
|
task_update: factories.createTaskUpdateTool(pluginConfig, ctx),
|
2026-02-08 16:25:25 +09:00
|
|
|
}
|
|
|
|
|
: {}
|
|
|
|
|
|
2026-03-02 15:20:02 +09:00
|
|
|
const hashlineEnabled = pluginConfig.hashline_edit ?? false
|
2026-02-16 16:32:33 +09:00
|
|
|
const hashlineToolsRecord: Record<string, ToolDefinition> = hashlineEnabled
|
2026-04-10 15:53:07 +09:00
|
|
|
? { edit: factories.createHashlineEditTool(ctx) }
|
2026-02-16 16:32:33 +09:00
|
|
|
: {}
|
|
|
|
|
|
2026-02-08 16:25:25 +09:00
|
|
|
const allTools: Record<string, ToolDefinition> = {
|
2026-04-10 15:53:07 +09:00
|
|
|
...factories.builtinTools,
|
|
|
|
|
...factories.createGrepTools(ctx),
|
|
|
|
|
...factories.createGlobTools(ctx),
|
|
|
|
|
...factories.createAstGrepTools(ctx),
|
|
|
|
|
...factories.createSessionManagerTools(ctx),
|
2026-02-08 16:25:25 +09:00
|
|
|
...backgroundTools,
|
|
|
|
|
call_omo_agent: callOmoAgent,
|
|
|
|
|
...(lookAt ? { look_at: lookAt } : {}),
|
|
|
|
|
task: delegateTask,
|
|
|
|
|
skill_mcp: skillMcpTool,
|
2026-02-18 18:40:10 +08:00
|
|
|
skill: skillTool,
|
2026-04-10 15:53:07 +09:00
|
|
|
...(interactiveBashEnabled ? { interactive_bash: factories.interactive_bash } : {}),
|
2026-02-08 16:25:25 +09:00
|
|
|
...taskToolsRecord,
|
2026-02-16 16:32:33 +09:00
|
|
|
...hashlineToolsRecord,
|
2026-02-08 16:25:25 +09:00
|
|
|
}
|
|
|
|
|
|
2026-03-08 02:21:42 +09:00
|
|
|
for (const toolDefinition of Object.values(allTools)) {
|
|
|
|
|
normalizeToolArgSchemas(toolDefinition)
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-27 11:11:56 +09:00
|
|
|
const filteredTools: ToolsRecord = filterDisabledTools(allTools, pluginConfig.disabled_tools)
|
|
|
|
|
|
|
|
|
|
const maxTools = pluginConfig.experimental?.max_tools
|
|
|
|
|
if (maxTools) {
|
2026-03-28 15:24:18 +09:00
|
|
|
trimToolsToCap(filteredTools, maxTools)
|
2026-03-27 11:11:56 +09:00
|
|
|
}
|
2026-02-08 16:25:25 +09:00
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
filteredTools,
|
|
|
|
|
taskSystemEnabled,
|
|
|
|
|
}
|
|
|
|
|
}
|