cloudaxe-opencode/packages/opencode/src/tool/tool.ts

93 lines
3.1 KiB
TypeScript
Raw Normal View History

import z from "zod"
import type { MessageV2 } from "../session/message-v2"
import type { Agent } from "../agent/agent"
import type { Permission } from "../permission"
import type { SessionID, MessageID } from "../session/schema"
import { Truncate } from "./truncate"
2025-05-19 23:29:38 +00:00
2025-05-20 15:11:06 +00:00
export namespace Tool {
2025-06-11 16:44:17 +00:00
interface Metadata {
[key: string]: any
}
export interface InitContext {
agent?: Agent.Info
}
2025-06-18 17:40:21 +00:00
export type Context<M extends Metadata = Metadata> = {
sessionID: SessionID
messageID: MessageID
agent: string
2025-06-10 21:56:05 +00:00
abort: AbortSignal
2025-09-18 07:58:21 +00:00
callID?: string
extra?: { [key: string]: any }
messages: MessageV2.WithParts[]
metadata(input: { title?: string; metadata?: M }): void
ask(input: Omit<Permission.Request, "id" | "sessionID" | "tool">): Promise<void>
2025-06-02 23:51:26 +00:00
}
export interface Def<Parameters extends z.ZodType = z.ZodType, M extends Metadata = Metadata> {
description: string
parameters: Parameters
execute(
args: z.infer<Parameters>,
ctx: Context,
): Promise<{
title: string
metadata: M
output: string
attachments?: Omit<MessageV2.FilePart, "id" | "sessionID" | "messageID">[]
}>
formatValidationError?(error: z.ZodError): string
}
export interface Info<Parameters extends z.ZodType = z.ZodType, M extends Metadata = Metadata> {
2025-05-31 21:12:16 +00:00
id: string
init: (ctx?: InitContext) => Promise<Def<Parameters, M>>
2025-05-31 20:05:12 +00:00
}
2025-10-27 20:35:47 +00:00
export type InferParameters<T extends Info> = T extends Info<infer P> ? z.infer<P> : never
export type InferMetadata<T extends Info> = T extends Info<any, infer M> ? M : never
export function define<Parameters extends z.ZodType, Result extends Metadata>(
id: string,
init: Info<Parameters, Result>["init"] | Def<Parameters, Result>,
): Info<Parameters, Result> {
return {
id,
init: async (initCtx) => {
const toolInfo = init instanceof Function ? await init(initCtx) : { ...init }
const execute = toolInfo.execute
2026-01-07 08:01:23 +00:00
toolInfo.execute = async (args, ctx) => {
try {
toolInfo.parameters.parse(args)
} catch (error) {
if (error instanceof z.ZodError && toolInfo.formatValidationError) {
2025-11-15 19:19:24 +00:00
throw new Error(toolInfo.formatValidationError(error), { cause: error })
}
2025-11-15 19:19:24 +00:00
throw new Error(
`The ${id} tool was called with invalid arguments: ${error}.\nPlease rewrite the input so it satisfies the expected schema.`,
{ cause: error },
)
}
2026-01-07 08:01:23 +00:00
const result = await execute(args, ctx)
// skip truncation for tools that handle it themselves
if (result.metadata.truncated !== undefined) {
return result
}
const truncated = await Truncate.output(result.output, {}, initCtx?.agent)
2026-01-07 08:01:23 +00:00
return {
...result,
output: truncated.content,
metadata: {
...result.metadata,
truncated: truncated.truncated,
...(truncated.truncated && { outputPath: truncated.outputPath }),
2026-01-07 08:01:23 +00:00
},
}
}
return toolInfo
},
}
2025-05-20 15:11:06 +00:00
}
2025-05-19 23:29:38 +00:00
}