Tools

Async Tool Dispatch

Tools marked with async: true return immediately with a taskId instead of blocking the agent loop. The tool executes in the background; the agent can check its result later using the built-in tool_poll tool.

How it works

  1. Agent calls an async tool
  2. A row is inserted into the tool_tasks table with status: 'running'
  3. The tool returns { taskId, status: "running" } instantly
  4. Execution continues in the background
  5. The agent calls tool_poll({ taskId }) to retrieve the result when needed

Declaring an async tool

typescript
export const myTool: ToolDefinition = {
  name: 'heavy_analysis',
  description: 'Run a long analysis job',
  async: true,   // marks this tool as async
  parameters: z.object({
    dataset: z.string(),
  }),
  execute: async ({ dataset }, ctx) => {
    // This runs in the background after the immediate return
    return await runAnalysis(dataset)
  },
}

Immediate response shape

typescript
// What the agent receives immediately
{
  taskId: "550e8400-e29b-41d4-a716-446655440000",
  status: "running",
  toolName: "heavy_analysis"
}

tool_tasks table schema

sql
CREATE TABLE tool_tasks (
  id          UUID PRIMARY KEY,
  session_id  TEXT NOT NULL,
  tool_name   TEXT NOT NULL,
  params      JSONB,
  status      TEXT NOT NULL,   -- 'running' | 'done' | 'error'
  result      JSONB,
  error_message TEXT,
  created_at  TIMESTAMPTZ DEFAULT NOW(),
  updated_at  TIMESTAMPTZ DEFAULT NOW()
);

Use async dispatch for tools that take more than 2-3 seconds. Short tools (search, read file) should stay synchronous — async adds round-trip overhead from tool_poll calls.