|
@@ -51,6 +51,9 @@ import { BackgroundJob } from "@/background/job"
|
|
|
import { RuntimeFlags } from "@/effect/runtime-flags"
|
|
import { RuntimeFlags } from "@/effect/runtime-flags"
|
|
|
import { ProviderV2 } from "@opencode-ai/core/provider"
|
|
import { ProviderV2 } from "@opencode-ai/core/provider"
|
|
|
import { ModelV2 } from "@opencode-ai/core/model"
|
|
import { ModelV2 } from "@opencode-ai/core/model"
|
|
|
|
|
+import { MCP } from "@/mcp"
|
|
|
|
|
+import { PermissionV1 } from "@opencode-ai/core/v1/permission"
|
|
|
|
|
+import { McpCatalog } from "@/mcp/catalog"
|
|
|
|
|
|
|
|
export function webSearchEnabled(providerID: ProviderV2.ID, flags = { exa: false, parallel: false }) {
|
|
export function webSearchEnabled(providerID: ProviderV2.ID, flags = { exa: false, parallel: false }) {
|
|
|
return providerID === ProviderV2.ID.opencode || flags.exa || flags.parallel
|
|
return providerID === ProviderV2.ID.opencode || flags.exa || flags.parallel
|
|
@@ -74,6 +77,7 @@ export interface Interface {
|
|
|
providerID: ProviderV2.ID
|
|
providerID: ProviderV2.ID
|
|
|
modelID: ModelV2.ID
|
|
modelID: ModelV2.ID
|
|
|
agent: Agent.Info
|
|
agent: Agent.Info
|
|
|
|
|
+ permission?: PermissionV1.Ruleset
|
|
|
}) => Effect.Effect<Tool.Def[]>
|
|
}) => Effect.Effect<Tool.Def[]>
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -87,6 +91,7 @@ const layer = Layer.effect(
|
|
|
const agents = yield* Agent.Service
|
|
const agents = yield* Agent.Service
|
|
|
const truncate = yield* Truncate.Service
|
|
const truncate = yield* Truncate.Service
|
|
|
const flags = yield* RuntimeFlags.Service
|
|
const flags = yield* RuntimeFlags.Service
|
|
|
|
|
+ const mcp = yield* MCP.Service
|
|
|
|
|
|
|
|
const invalid = yield* InvalidTool
|
|
const invalid = yield* InvalidTool
|
|
|
const task = yield* TaskTool
|
|
const task = yield* TaskTool
|
|
@@ -105,6 +110,8 @@ const layer = Layer.effect(
|
|
|
const patchtool = yield* ApplyPatchTool
|
|
const patchtool = yield* ApplyPatchTool
|
|
|
const skilltool = yield* SkillTool
|
|
const skilltool = yield* SkillTool
|
|
|
const agent = yield* Agent.Service
|
|
const agent = yield* Agent.Service
|
|
|
|
|
+ const codeMode = flags.experimentalCodeMode ? yield* Effect.promise(() => import("./code-mode")) : undefined
|
|
|
|
|
+ const codeModeTool = codeMode ? yield* codeMode.CodeModeTool : undefined
|
|
|
|
|
|
|
|
const state = yield* InstanceState.make<State>(
|
|
const state = yield* InstanceState.make<State>(
|
|
|
Effect.fn("ToolRegistry.state")(function* (ctx) {
|
|
Effect.fn("ToolRegistry.state")(function* (ctx) {
|
|
@@ -211,6 +218,7 @@ const layer = Layer.effect(
|
|
|
question: Tool.init(question),
|
|
question: Tool.init(question),
|
|
|
lsp: Tool.init(lsptool),
|
|
lsp: Tool.init(lsptool),
|
|
|
plan: Tool.init(plan),
|
|
plan: Tool.init(plan),
|
|
|
|
|
+ ...(codeModeTool ? { execute: Tool.init(codeModeTool) } : {}),
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
return {
|
|
return {
|
|
@@ -230,6 +238,7 @@ const layer = Layer.effect(
|
|
|
tool.search,
|
|
tool.search,
|
|
|
tool.skill,
|
|
tool.skill,
|
|
|
tool.patch,
|
|
tool.patch,
|
|
|
|
|
+ ...(tool.execute ? [tool.execute] : []),
|
|
|
...(flags.experimentalLspTool ? [tool.lsp] : []),
|
|
...(flags.experimentalLspTool ? [tool.lsp] : []),
|
|
|
...(flags.experimentalPlanMode && flags.client === "cli" ? [tool.plan] : []),
|
|
...(flags.experimentalPlanMode && flags.client === "cli" ? [tool.plan] : []),
|
|
|
],
|
|
],
|
|
@@ -263,6 +272,20 @@ const layer = Layer.effect(
|
|
|
return ["Available agent types and the tools they have access to:", description].join("\n")
|
|
return ["Available agent types and the tools they have access to:", description].join("\n")
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
|
|
+ const describeCodeMode = Effect.fn("ToolRegistry.describeCodeMode")(function* (input: {
|
|
|
|
|
+ agent: Agent.Info
|
|
|
|
|
+ permission?: PermissionV1.Ruleset
|
|
|
|
|
+ }) {
|
|
|
|
|
+ if (!codeMode) return
|
|
|
|
|
+ const ruleset = Permission.merge(input.agent.permission, input.permission ?? [])
|
|
|
|
|
+ const tools = Permission.visibleTools(yield* mcp.tools(), ruleset)
|
|
|
|
|
+ if (Object.keys(tools).length === 0) return
|
|
|
|
|
+ return codeMode.describeCatalog(
|
|
|
|
|
+ tools,
|
|
|
|
|
+ Object.keys(yield* mcp.clients()).map(McpCatalog.sanitize),
|
|
|
|
|
+ )
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
const tools: Interface["tools"] = Effect.fn("ToolRegistry.tools")(function* (input) {
|
|
const tools: Interface["tools"] = Effect.fn("ToolRegistry.tools")(function* (input) {
|
|
|
const filtered = (yield* all()).filter((tool) => {
|
|
const filtered = (yield* all()).filter((tool) => {
|
|
|
if (tool.id === WebSearchTool.id) {
|
|
if (tool.id === WebSearchTool.id) {
|
|
@@ -277,8 +300,11 @@ const layer = Layer.effect(
|
|
|
return true
|
|
return true
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
|
|
+ const codeModeDescription = filtered.some((tool) => tool.id === "execute") ? yield* describeCodeMode(input) : undefined
|
|
|
|
|
+ const visible = filtered.filter((tool) => tool.id !== "execute" || codeModeDescription)
|
|
|
|
|
+
|
|
|
return yield* Effect.forEach(
|
|
return yield* Effect.forEach(
|
|
|
- filtered,
|
|
|
|
|
|
|
+ visible,
|
|
|
Effect.fnUntraced(function* (tool: Tool.Def) {
|
|
Effect.fnUntraced(function* (tool: Tool.Def) {
|
|
|
const output = {
|
|
const output = {
|
|
|
description: tool.description,
|
|
description: tool.description,
|
|
@@ -292,7 +318,11 @@ const layer = Layer.effect(
|
|
|
: undefined
|
|
: undefined
|
|
|
return {
|
|
return {
|
|
|
id: tool.id,
|
|
id: tool.id,
|
|
|
- description: [output.description, tool.id === TaskTool.id ? yield* describeTask(input.agent) : undefined]
|
|
|
|
|
|
|
+ description: [
|
|
|
|
|
+ output.description,
|
|
|
|
|
+ tool.id === TaskTool.id ? yield* describeTask(input.agent) : undefined,
|
|
|
|
|
+ tool.id === "execute" ? codeModeDescription : undefined,
|
|
|
|
|
+ ]
|
|
|
.filter(Boolean)
|
|
.filter(Boolean)
|
|
|
.join("\n"),
|
|
.join("\n"),
|
|
|
parameters: output.parameters,
|
|
parameters: output.parameters,
|
|
@@ -412,6 +442,7 @@ export const node = LayerNode.make({
|
|
|
Format.node,
|
|
Format.node,
|
|
|
Truncate.node,
|
|
Truncate.node,
|
|
|
RuntimeFlags.node,
|
|
RuntimeFlags.node,
|
|
|
|
|
+ MCP.node,
|
|
|
Database.node,
|
|
Database.node,
|
|
|
Ripgrep.node,
|
|
Ripgrep.node,
|
|
|
],
|
|
],
|