# Agent tasks

> Attach agent tasks via `config.tasks` so a live call can run a bounded job and get a typed result back as a tool's return value.

This page is about entering an agent task **from inside a conversation**, through the
agent's `config.tasks` field — not about running one on its own with `POST /v1/task-runs`
(that is [agent tasks overview](https://docs.talqing.com/tasks/overview.md)). Each attached task becomes a tool; when
the model calls it, the task takes over the same call, in the same voice, until it calls
`submit_result`, and its typed output comes back as the tool's result.

```json
{
  "tasks": [
    {
      "name": "collect_shipping_address",
      "task_id": "0b7d4c92-3e51-4a86-9f2d-71c0a4e58b13",
      "description": "Handoff to an agent task responsible for taking the caller's shipping address and confirming it back, once they have placed an order. Skip this if you already have their address.",
      "message": "Sure, let me take that down."
    }
  ]
}
```

Use a task for a bounded job with an answer — taking an address, qualifying a lead. Use a
[handoff](https://docs.talqing.com/teams/handoffs.md) when the caller should belong to another agent from then on; a
handoff does not come back.

## Fields

| Field | Type | Default | Meaning |
| --- | --- | --- | --- |
| `name` | string, required | — | Tool name the model sees (letters, digits, underscores). Independent of the task's own name; unique per agent |
| `task_id` | string \| null | `null` | A **published** task in the workspace. Exclusive with `task` |
| `task_version` | integer \| null | `null` | Set when the agent is published (pinned); null in a draft |
| `task` | object \| null | `null` | An inline `TaskConfig`. On agent endpoints it is created as a real task; on call endpoints it lives for that call |
| `description` | string, required | — | What the model decides on. Start with "Handoff to an agent task responsible for …", then when to enter and when to skip |
| `message` | string \| null | `null` | Spoken while the task loads |

`description` and `message` take the calling agent's template variables.

## What the task runs on

A task has no channel or media of its own. It uses the call's speech-to-text, voice, turn
handling and avatar, so one task works under voice, video and text agents. It keeps its own
`llm` and `timezone` — except **on a [realtime](https://docs.talqing.com/agents/realtime.md) agent, where the task runs
on the call's speech-to-speech model** and its `llm` is ignored.

## Which variables the model is asked for

The task's `vars` become the tool's parameters **minus every variable the call already
answers**: anything the calling agent declares (even if blank) or the session was started
with. Those are filled in automatically and never shown to the model.

Inside the task, `{{vars.*}}` resolves in order: the task's own `default`, then the calling
agent's `{{vars.*}}` bag, then the arguments the model supplied (only for parameters it was
shown). A `required` task variable is enforced only on the parameters the model is shown.

Declare a task variable only for what the caller or model must supply. Anything already in
`{{userdata.*}}`, `{{vars.*}}` or `{{system_vars.*}}` should be read in the task's prompt.
Publishing the agent **warns** when a task requires a variable the agent does not declare
and that has no default — the model would be asked to supply it and may invent one.

## Context in and out

Messages cross in both directions; tool calls never do.

- **In:** the task sees the calling agent's conversation (not its system prompt or tool
  calls). It speaks first, generating its opening from its own prompt — say in the prompt
  how to open and not to greet again.
- **Out:** everything said during the task merges into the agent's context, and the typed
  result is the tool's return value, as JSON (`{"status": "shipped", "eta": "Friday"}`).
  The agent does not greet again.

A task with an `untrusted` or `file` input cannot be attached: the agent's model would have
to pass that value as a tool argument. Run such a task with [`POST /v1/task-runs`](https://docs.talqing.com/tasks/running-and-runs.md).

`userdata` is shared: the task reads and writes the call's bag, which persists to the
contact record.

## Tools inside a task

| Operation in a task's tool | Behaviour |
| --- | --- |
| `say`, `generate_reply`, `set_variable`, `http`, `code`, `frontend_rpc` | As on the calling agent |
| `end_call`, `transfer` | Work and end the call; no result returns |
| `handoff` | Refused at publish |

**While the task holds the floor, the calling agent's tools are unavailable** — give the
task any tool it needs. A task cannot attach tasks of its own.

## Ending without a result

| Event | Result |
| --- | --- |
| Caller changes their mind | The model calls `finish_without_result(reason)`; the agent's tool call fails with that reason and the agent resumes with the conversation in context |
| `timeout_seconds` (task field, default 300, 10–600) elapses | Task interrupted; tool call fails. The only bound on how long a task holds a caller — keep it short for voice |
| Call ends | Task settles with an error |

`finish_without_result` exists only when a task is entered from a conversation and cannot
be removed. If the task gives up too readily, narrow `finish_without_result_description` on
the task — the tool description, not the prompt, is what the model weighs. A value it may
legitimately fail to find is better as an output field with `required: false` (null
allowed).

## Limits and behaviour

- **Steps:** the calling session's `max_steps` applies per reply (default 4 on voice and
  video, 25 on text), not the task's own `max_steps` (standalone runs only).
- Two task tools in one model message run sequentially.
- **Text:** an open task does not survive the chat going idle for about a minute; the task
  and its pending result are dropped and the calling agent answers the next message.
- **Transcript:** the sub-conversation is bracketed by *Started &lt;name&gt;* and *Back to
  &lt;agent&gt;* rows; the tool card shows arguments and result.
- **Billing:** no separate task run or bill — tokens and speech are the call's.
- **Recording:** a task inherits the call's recording; everything said in it is recorded
  and transcribed. Do not use it to capture card numbers.
- Deleting a task an agent still attaches is refused, naming the agents.

## Attaching one

**Step 1. Publish the task**

Only published tasks can be attached by `task_id`.
**Step 2. Attach it**

Agent editor → **Tasks**: give it a tool name, pick the task, write the description. The Inputs table marks each task variable **from the call** or **from the model**.
**Step 3. Publish the agent**

This pins the task version; republishing the task changes nothing until the agent is published again.

## Next

- [Inputs and output](https://docs.talqing.com/tasks/inputs-and-output.md): `vars`, `output` and `submit_result`.
- [Handoffs](https://docs.talqing.com/teams/handoffs.md): Passing the caller on for good.
