Reorder execute params: (toolCallId, params, onUpdate, ctx, signal?)

Optional signal now at the end for cleaner API
This commit is contained in:
Mario Zechner 2025-12-31 12:14:28 +01:00
parent 4c9c453646
commit 19c4182c21
9 changed files with 15 additions and 15 deletions

View file

@ -50,7 +50,7 @@
- `ToolAPI` renamed to `CustomToolAPI`
- `ToolContext` renamed to `CustomToolContext`
- `ToolSessionEvent` renamed to `CustomToolSessionEvent`
- `execute()` signature changed: now takes `(toolCallId, params, signal, onUpdate, ctx: CustomToolContext)`
- `execute()` signature changed: now takes `(toolCallId, params, onUpdate, ctx: CustomToolContext, signal?)`
- `onSession()` signature changed: now takes `(event: CustomToolSessionEvent, ctx: CustomToolContext)`
- `CustomToolSessionEvent` simplified: only has `reason` and `previousSessionFile` (use `ctx.sessionManager.getBranch()` to get entries)
- `CustomToolContext` provides `sessionManager: ReadonlySessionManager`, `modelRegistry`, and `model`

View file

@ -659,7 +659,7 @@ const factory: CustomToolFactory = (pi) => ({
name: Type.String({ description: "Name to greet" }),
}),
async execute(toolCallId, params, signal, onUpdate, ctx) {
async execute(toolCallId, params, onUpdate, ctx, signal) {
const { name } = params as { name: string };
return {
content: [{ type: "text", text: `Hello, ${name}!` }],

View file

@ -36,7 +36,7 @@ const factory: CustomToolFactory = (pi) => ({
name: Type.String({ description: "Name to greet" }),
}),
async execute(toolCallId, params, signal, onUpdate, ctx) {
async execute(toolCallId, params, onUpdate, ctx, signal) {
const { name } = params as { name: string };
return {
content: [{ type: "text", text: `Hello, ${name}!` }],
@ -112,7 +112,7 @@ const factory: CustomToolFactory = (pi) => ({
text: Type.Optional(Type.String()),
}),
async execute(toolCallId, params, signal, onUpdate, ctx) {
async execute(toolCallId, params, onUpdate, ctx, signal) {
// signal - AbortSignal for cancellation
// onUpdate - Callback for streaming partial results
// ctx - CustomToolContext with sessionManager, modelRegistry, model
@ -181,7 +181,7 @@ Always check `pi.hasUI` before using UI methods.
Pass the `signal` from `execute` to `pi.exec` to support cancellation:
```typescript
async execute(toolCallId, params, signal, onUpdate, ctx) {
async execute(toolCallId, params, onUpdate, ctx, signal) {
const result = await pi.exec("long-running-command", ["arg"], { signal });
if (result.killed) {
return { content: [{ type: "text", text: "Cancelled" }] };
@ -262,7 +262,7 @@ const factory: CustomToolFactory = (pi) => {
onSession: reconstructState,
async execute(toolCallId, params, signal, onUpdate, ctx) {
async execute(toolCallId, params, onUpdate, ctx, signal) {
// Modify items...
items.push("new item");
@ -384,7 +384,7 @@ If `renderCall` or `renderResult` is not defined or throws an error:
## Execute Function
```typescript
async execute(toolCallId, args, signal, onUpdate, ctx) {
async execute(toolCallId, args, onUpdate, ctx, signal) {
// Type assertion for params (TypeBox schema doesn't flow through)
const params = args as { action: "list" | "add"; text?: string };

View file

@ -9,7 +9,7 @@ const factory: CustomToolFactory = (_pi) => ({
name: Type.String({ description: "Name to greet" }),
}),
async execute(_toolCallId, params) {
async execute(_toolCallId, params, _onUpdate, _ctx, _signal) {
const { name } = params as { name: string };
return {
content: [{ type: "text", text: `Hello, ${name}!` }],

View file

@ -24,7 +24,7 @@ const factory: CustomToolFactory = (pi) => {
description: "Ask the user a question and let them pick from options. Use when you need user input to proceed.",
parameters: QuestionParams,
async execute(_toolCallId, params) {
async execute(_toolCallId, params, _onUpdate, _ctx, _signal) {
if (!pi.hasUI) {
return {
content: [{ type: "text", text: "Error: UI not available (running in non-interactive mode)" }],

View file

@ -433,7 +433,7 @@ const factory: CustomToolFactory = (pi) => {
},
parameters: SubagentParams,
async execute(_toolCallId, params, signal, onUpdate, _ctx) {
async execute(_toolCallId, params, onUpdate, _ctx, signal) {
const agentScope: AgentScope = params.agentScope ?? "user";
const discovery = discoverAgents(pi.cwd, agentScope);
const agents = discovery.agents;

View file

@ -78,7 +78,7 @@ const factory: CustomToolFactory = (_pi) => {
// Called on session start/switch/branch/clear
onSession: reconstructState,
async execute(_toolCallId, params) {
async execute(_toolCallId, params, _onUpdate, _ctx, _signal) {
switch (params.action) {
case "list":
return {

View file

@ -84,7 +84,7 @@ export type CustomToolResult<TDetails = any> = AgentToolResult<TDetails>;
* description: "Does something useful",
* parameters: Type.Object({ input: Type.String() }),
*
* async execute(toolCallId, params, signal, onUpdate, ctx) {
* async execute(toolCallId, params, onUpdate, ctx, signal) {
* // Access session state via ctx.sessionManager
* // Access model registry via ctx.modelRegistry
* // Current model via ctx.model
@ -114,16 +114,16 @@ export interface CustomTool<TParams extends TSchema = TSchema, TDetails = any> {
* Execute the tool.
* @param toolCallId - Unique ID for this tool call
* @param params - Parsed parameters matching the schema
* @param signal - AbortSignal for cancellation
* @param onUpdate - Callback for streaming partial results (for UI, not LLM)
* @param ctx - Context with session manager, model registry, and current model
* @param signal - Optional abort signal for cancellation
*/
execute(
toolCallId: string,
params: Static<TParams>,
signal: AbortSignal | undefined,
onUpdate: AgentToolUpdateCallback<TDetails> | undefined,
ctx: CustomToolContext,
signal?: AbortSignal,
): Promise<AgentToolResult<TDetails>>;
/** Called on session lifecycle events - use to reconstruct state or cleanup resources */

View file

@ -16,7 +16,7 @@ export function wrapCustomTool(tool: CustomTool, getContext: () => CustomToolCon
description: tool.description,
parameters: tool.parameters,
execute: (toolCallId, params, signal, onUpdate) =>
tool.execute(toolCallId, params, signal, onUpdate, getContext()),
tool.execute(toolCallId, params, onUpdate, getContext(), signal),
};
}