import { Agent } from "@/agent/agent" import { KiloSessionPrompt } from "@/kilocode/session/prompt" // kilocode_change import { Provider } from "@/provider/provider" import { ProviderTransform } from "@/provider/transform" import { MCP } from "@/mcp" import { Permission } from "@/permission" import { Tool } from "@/tool/tool" import { ToolJsonSchema } from "@/tool/json-schema" import { ToolRegistry } from "@/tool/registry" import { Truncate } from "@/tool/truncate" import { ModelID } from "@/provider/schema" import { Plugin } from "@/plugin" import type { TaskPromptOps } from "@/tool/task" import { type Tool as AITool, tool, jsonSchema, type ToolExecutionOptions, asSchema } from "ai" import { Effect } from "effect" import { MessageV2 } from "./message-v2" import * as Session from "./session" import { SessionProcessor } from "./processor" import { PartID } from "./schema" import * as Log from "@opencode-ai/core/util/log" import { EffectBridge } from "@/effect/bridge" import * as SandboxPolicy from "@/kilocode/sandbox/policy" // kilocode_change const log = Log.create({ service: "session.tools" }) export const resolve = Effect.fn("SessionTools.resolve")(function* (input: { agent: Agent.Info model: Provider.Model session: Session.Info processor: Pick // kilocode_change bypassAgentCheck: boolean messages: MessageV2.WithParts[] promptOps: TaskPromptOps }) { using _ = log.time("resolveTools") const tools: Record = {} const run = yield* EffectBridge.make() const plugin = yield* Plugin.Service const permission = yield* Permission.Service // kilocode_change start const agents = yield* Agent.Service const sessions = yield* Session.Service // kilocode_change end const registry = yield* ToolRegistry.Service const mcp = yield* MCP.Service const truncate = yield* Truncate.Service const context = (args: Record, options: ToolExecutionOptions): Tool.Context => ({ sessionID: input.session.id, abort: options.abortSignal!, messageID: input.processor.message.id, callID: options.toolCallId, extra: { model: input.model, bypassAgentCheck: input.bypassAgentCheck, promptOps: input.promptOps }, agent: input.agent.name, messages: input.messages, // kilocode_change start metadata: (val) => input.processor.metadata(options.toolCallId, val), ask: (req) => KiloSessionPrompt.askPermission({ permission, agents, sessions, agent: input.agent, session: input.session, request: { ...req, sessionID: input.session.id, tool: { messageID: input.processor.message.id, callID: options.toolCallId }, }, }).pipe(Effect.orDie), }) // kilocode_change end for (const item of yield* registry.tools({ modelID: ModelID.make(input.model.api.id), providerID: input.model.providerID, agent: input.agent, })) { const schema = ProviderTransform.schema(input.model, ToolJsonSchema.fromTool(item)) tools[item.id] = tool({ description: item.description, inputSchema: jsonSchema(schema), execute(args, options) { return run.promise( Effect.gen(function* () { const ctx = context(args, options) yield* plugin.trigger( "tool.execute.before", { tool: item.id, sessionID: ctx.sessionID, callID: ctx.callID }, { args }, ) // kilocode_change start const result = yield* SandboxPolicy.executeTool(item, item.execute(args, ctx)) // kilocode_change end const output = { ...result, attachments: result.attachments?.map((attachment) => ({ ...attachment, id: PartID.ascending(), sessionID: ctx.sessionID, messageID: input.processor.message.id, })), } yield* plugin.trigger( "tool.execute.after", { tool: item.id, sessionID: ctx.sessionID, callID: ctx.callID, args }, output, ) if (options.abortSignal?.aborted) { yield* input.processor.completeToolCall(options.toolCallId, output) } return output }), ) }, }) } for (const [key, item] of Object.entries(yield* mcp.tools())) { const execute = item.execute if (!execute) continue const schema = yield* Effect.promise(() => Promise.resolve(asSchema(item.inputSchema).jsonSchema)) const transformed = ProviderTransform.schema(input.model, schema) item.inputSchema = jsonSchema(transformed) item.execute = (args, opts) => run.promise( Effect.gen(function* () { const ctx = context(args, opts) yield* plugin.trigger( "tool.execute.before", { tool: key, sessionID: ctx.sessionID, callID: opts.toolCallId }, { args }, ) // kilocode_change start const result: Awaited>> = yield* SandboxPolicy.executeMcp( item, Effect.gen(function* () { yield* ctx.ask({ permission: key, metadata: {}, patterns: ["*"], always: ["*"] }) return yield* Effect.promise(() => execute(args, opts)) }), ).pipe( // kilocode_change end Effect.withSpan("Tool.execute", { attributes: { "tool.name": key, "tool.call_id": opts.toolCallId, "session.id": ctx.sessionID, "message.id": input.processor.message.id, }, }), ) yield* plugin.trigger( "tool.execute.after", { tool: key, sessionID: ctx.sessionID, callID: opts.toolCallId, args }, result, ) const textParts: string[] = [] const attachments: Omit[] = [] for (const contentItem of result.content) { if (contentItem.type === "text") textParts.push(contentItem.text) else if (contentItem.type === "image") { attachments.push({ type: "file", mime: contentItem.mimeType, url: `data:${contentItem.mimeType};base64,${contentItem.data}`, }) } else if (contentItem.type === "resource") { const { resource } = contentItem if (resource.text) textParts.push(resource.text) if (resource.blob) { attachments.push({ type: "file", mime: resource.mimeType ?? "application/octet-stream", url: `data:${resource.mimeType ?? "application/octet-stream"};base64,${resource.blob}`, filename: resource.uri, }) } } } const truncated = yield* truncate.output(textParts.join("\n\n"), {}, input.agent) const metadata = { ...result.metadata, truncated: truncated.truncated, ...(truncated.truncated && { outputPath: truncated.outputPath }), } const output = { title: "", metadata, output: truncated.content, attachments: attachments.map((attachment) => ({ ...attachment, id: PartID.ascending(), sessionID: ctx.sessionID, messageID: input.processor.message.id, })), content: result.content, } if (opts.abortSignal?.aborted) { yield* input.processor.completeToolCall(opts.toolCallId, output) } return output }), ) tools[key] = item } return tools }) export * as SessionTools from "./tools"