SDK

runTask and client tools

runTask creates a task and drives it to a terminal status on its own, handling the client tool interrupt cycle: call the handler, send the result, continue.

Schema and handler in one place

Each tools item carries both the tool definition and its handler. The SDK builds the request’s client_tools for you.

ts
const task = await client.runTask(
  {
    assistant_id: 1,
    input: [textInput("What's the weather in Moscow?")],
  },
  {
    tools: [
      {
        name: 'get_weather',
        description: 'Current weather in a city.',
        input_schema: {
          type: 'object',
          properties: { city: { type: 'string' } },
          required: ['city'],
        },
        // return a string, { content } or { error, reason }
        handler: async (args) =>
          JSON.stringify({ city: args.city, temperature_c: 21 }),
      },
    ],
    onStatus: (t) => console.log(t.status),
  },
);

Handler map

Alternatively, tools accepts a { name: handler } map — then the tool definitions are taken from input.client_tools (useful when the schemas come from somewhere else).

ts
await client.runTask(
  { assistant_id: 1, input: […], client_tools: [/* definitions */] },
  { tools: { get_weather: async (args) => '…' } },
);

Handler result

Return valueMeaning
stringSuccess, the result body.
{ content: string }Success, explicit form.
{ error: string, reason?: string }Tool error.

runTask options

OptionPurpose
toolsClientTool[] or a { name: handler } map.
pollIntervalMsStatus polling interval. Default 1500.
timeoutMsOverall timeout for the whole run.
maxRoundsGuard against an endless interrupt cycle. Default 10.
onStatusCallback on every poll.
onMissingToolFallback when there’s no handler for the called tool.
signalAbortSignal to cancel the run.
No handler?
If tools has no handler for the called tool, MissingToolHandlerError is thrown — or onMissingTool is called, if you set it.

If the run breaks off

runTask drives the cycle to a terminal status on its own, but if a handler throws (or maxRounds kicks in), the task stays interrupted — and keeps occupying a slot in the concurrent task limit. Cancel it in catch:

ts
try {
    await client.runTask(input, { tools });
} catch (err) {
    await client.cancelInterruptedTasks({ reason: 'Handler crashed' });
    throw err;
}

For a single task — cancelTask(uuid, { reason }). Details on both endpoints are in Client tools.

Under the hood it’s the same cycle described in Client tools — the SDK just walks through it for you.