|
|
@@ -9,7 +9,7 @@ import { SessionRevert } from "./revert"
|
|
|
import { Session } from "."
|
|
|
import { Agent } from "../agent/agent"
|
|
|
import { Provider } from "../provider/provider"
|
|
|
-import { type Tool as AITool, tool, jsonSchema } from "ai"
|
|
|
+import { type Tool as AITool, tool, jsonSchema, type ToolCallOptions } from "ai"
|
|
|
import { SessionCompaction } from "./compaction"
|
|
|
import { Instance } from "../project/instance"
|
|
|
import { Bus } from "../bus"
|
|
|
@@ -596,6 +596,41 @@ export namespace SessionPrompt {
|
|
|
}) {
|
|
|
using _ = log.time("resolveTools")
|
|
|
const tools: Record<string, AITool> = {}
|
|
|
+
|
|
|
+ const context = (args: any, options: ToolCallOptions): Tool.Context => ({
|
|
|
+ sessionID: input.session.id,
|
|
|
+ abort: options.abortSignal!,
|
|
|
+ messageID: input.processor.message.id,
|
|
|
+ callID: options.toolCallId,
|
|
|
+ extra: { model: input.model },
|
|
|
+ agent: input.agent.name,
|
|
|
+ metadata: async (val: { title?: string; metadata?: any }) => {
|
|
|
+ const match = input.processor.partFromToolCall(options.toolCallId)
|
|
|
+ if (match && match.state.status === "running") {
|
|
|
+ await Session.updatePart({
|
|
|
+ ...match,
|
|
|
+ state: {
|
|
|
+ title: val.title,
|
|
|
+ metadata: val.metadata,
|
|
|
+ status: "running",
|
|
|
+ input: args,
|
|
|
+ time: {
|
|
|
+ start: Date.now(),
|
|
|
+ },
|
|
|
+ },
|
|
|
+ })
|
|
|
+ }
|
|
|
+ },
|
|
|
+ async ask(req) {
|
|
|
+ await PermissionNext.ask({
|
|
|
+ ...req,
|
|
|
+ sessionID: input.session.parentID ?? input.session.id,
|
|
|
+ tool: { messageID: input.processor.message.id, callID: options.toolCallId },
|
|
|
+ ruleset: input.agent.permission,
|
|
|
+ })
|
|
|
+ },
|
|
|
+ })
|
|
|
+
|
|
|
for (const item of await ToolRegistry.tools(input.model.providerID)) {
|
|
|
const schema = ProviderTransform.schema(input.model, z.toJSONSchema(item.parameters))
|
|
|
tools[item.id] = tool({
|
|
|
@@ -603,57 +638,25 @@ export namespace SessionPrompt {
|
|
|
description: item.description,
|
|
|
inputSchema: jsonSchema(schema as any),
|
|
|
async execute(args, options) {
|
|
|
+ const ctx = context(args, options)
|
|
|
await Plugin.trigger(
|
|
|
"tool.execute.before",
|
|
|
{
|
|
|
tool: item.id,
|
|
|
- sessionID: input.session.id,
|
|
|
- callID: options.toolCallId,
|
|
|
+ sessionID: ctx.sessionID,
|
|
|
+ callID: ctx.callID,
|
|
|
},
|
|
|
{
|
|
|
args,
|
|
|
},
|
|
|
)
|
|
|
- const ctx: Tool.Context = {
|
|
|
- sessionID: input.session.id,
|
|
|
- abort: options.abortSignal!,
|
|
|
- messageID: input.processor.message.id,
|
|
|
- callID: options.toolCallId,
|
|
|
- extra: { model: input.model },
|
|
|
- agent: input.agent.name,
|
|
|
- metadata: async (val: { title?: string; metadata?: any }) => {
|
|
|
- const match = input.processor.partFromToolCall(options.toolCallId)
|
|
|
- if (match && match.state.status === "running") {
|
|
|
- await Session.updatePart({
|
|
|
- ...match,
|
|
|
- state: {
|
|
|
- title: val.title,
|
|
|
- metadata: val.metadata,
|
|
|
- status: "running",
|
|
|
- input: args,
|
|
|
- time: {
|
|
|
- start: Date.now(),
|
|
|
- },
|
|
|
- },
|
|
|
- })
|
|
|
- }
|
|
|
- },
|
|
|
- async ask(req) {
|
|
|
- await PermissionNext.ask({
|
|
|
- ...req,
|
|
|
- sessionID: input.session.parentID ?? input.session.id,
|
|
|
- tool: { messageID: input.processor.message.id, callID: options.toolCallId },
|
|
|
- ruleset: input.agent.permission,
|
|
|
- })
|
|
|
- },
|
|
|
- }
|
|
|
const result = await item.execute(args, ctx)
|
|
|
await Plugin.trigger(
|
|
|
"tool.execute.after",
|
|
|
{
|
|
|
tool: item.id,
|
|
|
- sessionID: input.session.id,
|
|
|
- callID: options.toolCallId,
|
|
|
+ sessionID: ctx.sessionID,
|
|
|
+ callID: ctx.callID,
|
|
|
},
|
|
|
result,
|
|
|
)
|
|
|
@@ -667,30 +670,41 @@ export namespace SessionPrompt {
|
|
|
},
|
|
|
})
|
|
|
}
|
|
|
+
|
|
|
for (const [key, item] of Object.entries(await MCP.tools())) {
|
|
|
const execute = item.execute
|
|
|
if (!execute) continue
|
|
|
|
|
|
// Wrap execute to add plugin hooks and format output
|
|
|
item.execute = async (args, opts) => {
|
|
|
+ const ctx = context(args, opts)
|
|
|
+
|
|
|
await Plugin.trigger(
|
|
|
"tool.execute.before",
|
|
|
{
|
|
|
tool: key,
|
|
|
- sessionID: input.session.id,
|
|
|
+ sessionID: ctx.sessionID,
|
|
|
callID: opts.toolCallId,
|
|
|
},
|
|
|
{
|
|
|
args,
|
|
|
},
|
|
|
)
|
|
|
+
|
|
|
+ await ctx.ask({
|
|
|
+ permission: key,
|
|
|
+ metadata: {},
|
|
|
+ patterns: ["*"],
|
|
|
+ always: ["*"],
|
|
|
+ })
|
|
|
+
|
|
|
const result = await execute(args, opts)
|
|
|
|
|
|
await Plugin.trigger(
|
|
|
"tool.execute.after",
|
|
|
{
|
|
|
tool: key,
|
|
|
- sessionID: input.session.id,
|
|
|
+ sessionID: ctx.sessionID,
|
|
|
callID: opts.toolCallId,
|
|
|
},
|
|
|
result,
|