Skip to main content

Workflow: Agent Type

Introduction​

An Agent workflow wraps an LLM agent instead of a fixed sequence of steps. Where a standard workflow always runs the same activities in the same order, an Agent workflow gives a model a system prompt, a set of other workflows it can call as tools, and a goal — the model decides what to call and in what order, then reports back through a structured result.

Use workflowType: Agent when the task needs judgment over ambiguous or unstructured input: triage, summarization, exception handling, freeform Q&A. If the same inputs should always produce the same steps, use a standard workflow instead — Agent workflows cannot have activities; everything they do comes from agent.instructions, agent.tools, and the model's own reasoning.

An Agent workflow runs one of two kinds of session:

  • task — the agent works until it calls the built-in set_result tool, which ends the session and returns a result, transcript, and sessionId. Every session started by a direct invocation — a trigger, a schedule, Workflow/Execute@1, the workflow execution API, or another workflow's tools[]/agents[] — runs as task, including an agent whose YAML declares session.type: chat.
  • chat — an open-ended, multi-turn conversation with no set_result and no automatic end: an assistant turn that calls no tool is the answer. Every session started over the Agent Responses API runs as chat — including an agent whose YAML declares session.type: task.

In other words, the caller decides which kind of session actually runs, not agent.session.type: a workflow-triggered or tool-invoked run is always task; a Responses API conversation is always chat. agent.session.type is still validated (see AGT-005) and documents your intent, but it has no effect on which loop executes today.

YAML Structure​

workflow:
name: "Agents / Order Exception Triage"
workflowId: "6f1c2c1e-1b2a-4d3e-9f00-000000000001"
workflowType: "Agent"
executionMode: "Sync" # required
isActive: true

agent:
description: "Investigates a delayed order and proposes an action"
instructions: |
You are an operations analyst. Load the order first, then reason
about the delay, then call set_result with an action and a reason.
model:
fromConfig: "ai.default" # org config name; see Model Configuration below
name: "claude-opus-5" # optional override of the config's model
temperature: 0.2 # optional
session:
type: "task" # "task" | "chat"
maxTurns: 25 # default 20, max 100
timeout: 300 # seconds, default 300
result: # optional JSON Schema for set_result's argument
type: object
properties:
action: { type: string }
reason: { type: string }
required: [action, reason]
skills: ["shipping-terms", "exception-playbook"] # accepted now; runtime ships later
tools:
- workflow: "MCP / Get Order Status" # workflow name or workflowId
instructions: "Call first to load the order before reasoning."
- workflow: "Notifications / Notify Ops"
instructions: "Only after you have decided on an action."
mcp: # accepted now; runtime ships later
- fromConfig: "mcp.carrier-portal"
agents: # accepted now; runtime ships later
- agent: "Agents / Rate Lookup"
modes: ["task", "chat"]
- url: "https://partner.example.com/.well-known/agent.json"
modes: ["task"]

inputs:
- name: "orderNumber"
type: "string"
props: { required: true, description: "Order to triage" }

# outputs: not needed here — result, transcript, and sessionId are always produced

activities is not allowed on an Agent workflow — an activities: list next to agent: fails validation (see AGT-004). triggers and schedules are declared exactly as on any other workflow, and trigger variables such as entity and eventType are available to the instructions template — but see Triggers below for what running one synchronously means for an Agent workflow.

The agent Section​

Every property below lives under the top-level agent: key. agent itself has no additional properties beyond these.

PropertyTypeRequiredDefaultDescription
descriptionstringNo—What this agent does. Shown to callers, and used as the tool description when another agent lists this workflow under its own tools[].
instructionsstringYes (minLength: 1)—System instructions for the model. A Handlebars template evaluated over workflow variables and inputs, rendered once at session start.
modelobjectNo—Model selection. No properties beyond the three below.
model.fromConfigstringNoai.defaultOrganization config name holding provider, model, apiKey, endpoint.
model.namestringNo—Overrides the config's model name for this workflow only.
model.temperaturenumber (0–2)No—Sampling temperature.
model.contextWindowintegerNo—The model's context window in tokens, used to decide when to compress history. Set this when model.name overrides the config's model — the org config's own window describes its own model, not your override. See Model Configuration for the full resolution order.
sessionobjectNo—Session behavior. No properties beyond the three below.
session.typetask | chatNotaskDocuments intent only — see Introduction. The invoking surface (workflow vs. the Responses API), not this field, decides which kind of session actually runs.
session.maxTurnsinteger (1–100)No20Maximum agent turns before the session is force-ended.
session.timeoutinteger (>= 1)No300Session timeout, in seconds.
resultobject (JSON Schema, type required, must be object)No—The schema the set_result tool's argument must satisfy. Shapes the result output.
skillsarray of stringsNo—Installed skill names to enable. Accepted and validated now; runtime support ships in a later release.
toolsarray of objectsNo—Other workflows exposed to the agent as callable tools. See Tools.
tools[].workflowstring (minLength: 1)Yes, per entry—Workflow name or workflowId to expose as a tool.
tools[].instructionsstringNo—When and how the agent should use this tool; folded into the tool's description.
tools[].modeauto | approvalNoautoauto runs the tool the moment the model calls it. approval pauses the call for a person to approve or decline first. See Tool Approval.
mcparray of objectsNo—Outbound MCP server connections. Accepted and validated now; runtime support ships in a later release.
mcp[].fromConfigstringYes, per entry—Organization config name for the MCP connection.
agentsarray of objectsNo—Allow list of other agents this agent may invoke. Accepted and validated now; runtime support ships in a later release.
agents[].agentstringExactly one of agent/url, per entry—Name of another Agent workflow in this organization.
agents[].urlstring (format: uri)Exactly one of agent/url, per entry—Remote A2A agent card URL.
agents[].modesarray of task | chatNo—Which session modes this agent may be invoked in.

Inputs and the First Message​

inputs: on an Agent workflow serves the same two roles it serves on an McpTool workflow:

  1. Direct invocation — when the workflow runs directly (API, schedule, entity trigger, or another workflow's Workflow/Execute@1), the input values are rendered into the agent's first user-turn message alongside anything the instructions template references.
  2. Invocation as a tool — when this workflow appears in another agent's tools[] (or agents[]), inputs (name, type, props.required, props.description) becomes the JSON Schema tool-call signature the calling model sees. Keep props.description populated — it's the parameter description the calling model reads when deciding how to fill the call.

A reserved input, __session, does not need to be declared under inputs: — see Session Limits and the __session Override.

Tools​

Each entry in agent.tools[] points at another workflow by workflow (name or workflowId) and turns it into a callable tool for the agent, alongside the built-in set_result tool described below.

  • Name. The tool's name is the target workflow's display name, sanitized to match ^[a-zA-Z0-9_-]{1,64}$: any character outside that set (spaces, /, punctuation) becomes _. If two tools sanitize to the same name, later collisions get a numeric suffix — the second becomes _2, the third _3 — applied within the 64-character limit, shortening the base name to make room if needed. Keep the workflows you expose to the same agent short and distinguishable after this substitution.
  • Description. Built from the target workflow's own description (or its agent.description, if the target is itself an Agent workflow), plus this entry's instructions — use instructions to say when the agent should reach for this tool and what to expect back.
  • Schema. The tool's argument schema is the target workflow's inputs, exactly as for an McpTool workflow.
  • Response. Calling the tool runs the target workflow synchronously. On success, the model receives the target's response output if it declares one, or the full outputs dictionary otherwise. If the target workflow fails, or its arguments don't bind against the schema, the model receives a JSON object { "error": "<message>" } as the tool result instead — the agent's session is not failed, so the model can see the error and react or retry.
  • Existence is checked at run time, not save time. Saving or validating the YAML only checks that tools[].workflow is present (see AGT-007); resolving the name or ID against an actual, active workflow happens when the agent calls the tool. A tool that references a workflow which doesn't exist yet — or is later deleted — passes validation and only surfaces as a run-time error result the model sees.

Tool Approval​

Set mode: approval on a tools[] entry to stop that tool from running the moment the model calls it — a person has to approve or decline the call first. mode: auto (the default, and the same as omitting mode) runs the tool immediately, as described above.

agent:
tools:
- workflow: "Orders / Get Status" # auto (default): runs immediately
- workflow: "Orders / Cancel Shipment"
mode: approval # waits for a person

What happens next depends on which kind of session is running (see Introduction):

  • In a chat session (the Agent Responses API), the call pauses instead of running: the response ends with an approval request item, and the session status becomes AwaitingApproval. The conversation resumes once every pending call has a decision — approved (the tool then runs) or declined (the model is told the person declined, and why). The full request/response shape, the pause/resume protocol, and how a client renders it live in Agent Responses API and GraphQL: Agent Sessions.
  • In a task session (run from a trigger, a schedule, or another workflow's tools[]) there is nobody to ask. The call is refused — the model receives "This tool requires human approval and cannot run in an unattended session." as the tool's error result — and the agent carries on; the session itself does not fail. Don't give a task-only agent an approval tool it actually needs to call to finish its job: it will never get approval.
  • Whoever starts or is otherwise allowed to see a chat session may decide its approvals — the same rule that governs who may continue the conversation. See Ownership and Sharing below.
  • The person who started a chat session gets a notification when it pauses for their approval — see Approval Notifications.

The set_result Tool and agent.result​

A task session gets a built-in set_result tool at run time — you never declare it under tools. Its argument schema is exactly agent.result (when omitted, { type: object }). Calling it ends the session and becomes the result output; it's the only way a task session (and therefore every workflow-triggered or tool-invoked agent run) finishes. A chat session has no set_result tool — an assistant turn with no tool calls is the answer instead, as described in Introduction.

At run time, only agent.result's top-level required list is enforced against the set_result call — a call missing one of those properties is returned to the model as an error result, and it may retry. Property types and any nested schema (properties, items, formats, and so on) are not validated in this release, so a call with a required property present but wrongly typed is still accepted.

Session Limits and the __session Override​

agent.session.maxTurns (default 20, max 100) and agent.session.timeout (default 300 seconds) bound how long a session can run before it's force-ended without a result.

A caller can override these two limits for a single invocation — without editing the YAML — by passing a reserved input named __session:

__session:
maxTurns: 40
timeout: 600
type: "task" # accepted but not yet used

__session is not a session id and does not resume or attach to a prior session — every run of an Agent workflow starts a brand-new session, whether or not __session is passed. It's an optional object of maxTurns/timeout overrides (type is also accepted on the object but not yet used by the runtime). __session does not need to be declared under inputs:, and it's stripped out before the remaining inputs are rendered into the agent's first message.

Outputs​

Outputs are fixed for Agent workflows. When the session completes by calling set_result, the engine always produces exactly these three, no matter what — or whether anything — is declared under outputs::

OutputDescription
resultThe argument passed to set_result, matching agent.result's schema.
transcriptThe full turn-by-turn conversation log: prompts, tool calls, tool results, and model responses.
sessionIdThe session identifier.

If a task session ends without calling set_result — it hits session.maxTurns, hits session.timeout, or stops responding with tool calls after a couple of nudges — the workflow fails instead of returning outputs: the engine raises an error naming the session id (for example, Agent session <sessionId> ended without a result: exhausted its turn budget (20)), and no result, transcript, or sessionId output is produced. Use the session id from that error to look up the AgentSession record and inspect the transcript of the failed run.

An outputs: section is not needed for an Agent workflow, and the engine ignores it for this workflow type — it never consults outputs: to decide what to produce. You may still add one (for example, to rename an output for a caller that expects a different name), and the usual rule that every entry needs a mapping still applies to it, but it has no effect on what the Agent runtime actually returns.

Model Configuration​

agent.model.fromConfig (default ai.default) names an organization configuration record shaped:

{
"provider": "Anthropic",
"model": "claude-sonnet-4-5",
"apiKey": "<your-api-key>",
"endpoint": "https://api.anthropic.com",
"contextWindow": 200000
}

provider is one of Anthropic, OpenAI, or AzureOpenAI. Set up ai.default once per organization — every Agent workflow that doesn't override model.name or model.temperature shares it — or add further named configs (ai.<name>) and point specific workflows at them with model.fromConfig.

If the organization has no matching config, the instance-level AI setting is used as a fallback. If neither is configured, the workflow fails when it tries to start a session rather than at save time.

Context window resolution. The window used for history compression is resolved in order: the agent's own model.contextWindow, else the organization config's contextWindow (only used when model.name does not override the config's model — the config's window describes its own model, not yours), else 256,000 tokens. If you set model.name, also set model.contextWindow: otherwise the runtime falls back to the 256,000-token default even for a model with a much smaller window, and history compression won't kick in until a call actually fails or runs slow from an oversized prompt.

History Compression​

A long-running chat can outgrow the model's context window. The engine compresses history automatically — you don't configure anything to enable it — whenever the previous call's reported input-plus-output tokens reach 80% of the resolved context window: older messages are replaced with a model-written summary, stored as a role: "summary" transcript message carrying summarizesThroughSequence, and the turn continues on the shortened history.

  • The summary call is charged against the session, but may use at most 50% of the remaining session time budget; if it fails, or runs out of that budget, the turn falls back to running on the full, uncompressed history rather than blocking the response.
  • A provider that reports no token usage for a call never triggers compression — there's nothing to compare against the 80% threshold.
  • The summary's own token usage is recorded in the transcript's usage list with "kind": "summary", and counted in the response's overall usage.
  • A response created over the internal Responses route streams response.tms.history_compressed with { "through_sequence": n } when a compression happens mid-response; see Agent Responses API.
  • Compression is invisible to the model's own reasoning — the summary stands in for what it replaces — but visible to a transcript viewer: render a summary message as a divider ("earlier messages summarized"), not as a chat bubble.

Session Ownership​

Every agent session — task or chat — has an owner scope (User, Division, or Organization) that governs who may see it, continue it, decide its approvals, and watch it live over onAgentSessionEvent. A system administrator can always see every session.

Defaults at creation, and they are defaults only — nothing in the YAML sets them:

  • A chat session (the Responses API) starts owned by User — the person who started it.
  • A task session (run from a workflow, with no person present) starts owned by Organization.

Change a session's owner — for example, to share a chat with a division or the whole organization so a teammate can pick up a pending approval — with the setAgentSessionOwner GraphQL mutation. The full scope rules, who may change ownership, and what changes when they do live in GraphQL: Agent Sessions.

Triggers​

An Agent workflow can carry a triggers: entry (for example type: Entity) just like a standard workflow. When it fires, the agent session runs synchronously inside the saving request: the request that added, modified, or deleted the triggering entity waits for the full agent session — potentially several model round-trips — to finish before it can complete, for up to agent.session.timeout (default 300 seconds), and it holds the workflow lock for that entity/organization for the whole duration.

For a trigger-bound agent, either:

  • keep agent.session.timeout short and agent.session.maxTurns low, so a slow or looping agent can't stall the triggering request for long, or
  • keep the trigger workflow itself lightweight (no agent: section) and have it invoke the Agent workflow asynchronously via Workflow/Execute@1 with executionMode: Async, so the triggering request returns immediately and the agent runs out-of-band.

Validation Rules​

Agent workflows are validated at save time:

CodeRule
AGT_001agent section is required for workflowType: Agent
AGT_002agent.instructions is required and non-empty
AGT_003executionMode must be Sync
AGT_004activities is not allowed on an Agent workflow
AGT_005agent.session.type must be task or chat
AGT_006agent.session.maxTurns must be 1–100; timeout must be positive
AGT_007agent.tools[].workflow is required on every entry
AGT_008agent.result, when present, must be a JSON Schema with type: object
AGT_009agent.agents[] entries need exactly one of agent/url; modes values must be task or chat
AGT_010agent.tools[].mode, when present, must be auto or approval

See Workflow Validation Errors for full messages, causes, and solutions.

Examples​

Order exception triage (task session)​

The agent from YAML Structure above, invoked directly or as another workflow's tool. It loads an order, reasons about the delay, and finishes by calling set_result — a typical task session run from a trigger or another workflow.

Customer-facing assistant with a guarded tool (chat session)​

An agent meant to be talked to over the Agent Responses API, with one read-only tool on auto and one side-effecting tool gated behind approval:

workflow:
name: "Agents / Order Assistant"
workflowId: "6f1c2c1e-1b2a-4d3e-9f00-000000000002"
workflowType: "Agent"
executionMode: "Sync"
isActive: true

agent:
description: "Answers questions about freight orders and can cancel a shipment with approval"
instructions: |
You are a freight operations assistant talking to a customer service rep.
Look up order status before answering. Only call Cancel Shipment when the
rep explicitly asks you to cancel — and expect it to need approval.
model:
fromConfig: "ai.default"
session:
type: "chat" # documents intent; a Responses API conversation runs as chat regardless
tools:
- workflow: "Orders / Get Status"
instructions: "Call before answering any question about an order."
- workflow: "Orders / Cancel Shipment"
mode: approval
instructions: "Only after the rep asks to cancel. Requires approval."

A rep talks to this agent through POST /api/organizations/{organizationId}/ai/responses with model: "Agents_Order_Assistant". Asking it to cancel a shipment pauses the conversation at AwaitingApproval until someone approves or declines the call — see Agent Responses API.

Best Practices​

  • Gate side-effecting tools behind mode: approval. Anything that cancels, refunds, deletes, sends, or otherwise changes state outside the conversation belongs behind approval; keep read-only lookups on auto (the default) so the agent isn't stalled waiting on every call.
  • Don't hand a task-only agent a tool it must call to finish. A task session has nobody to approve a call — the tool is refused and the agent has to work around it. Reserve approval tools for agents a person will actually be driving through a chat session.
  • Set model.contextWindow whenever you set model.name. Otherwise the runtime assumes the 256,000-token default, and history compression won't trigger at the right point for a smaller model.
  • Give agent.result a required list. It's the only part of the schema enforced at run time — a missing required property is returned to the model as a retryable error, so the agent can correct itself instead of returning half a result.
  • Write tools[].instructions for the calling model, not for a human reader. It's folded straight into the tool's description — say when to call it and what to expect back, not just what it does.
  • Keep trigger-bound agents short and bounded, or move them off the triggering request entirely — see Triggers. A slow agent on an entity trigger holds up the save and the workflow lock for the whole session timeout.
  • Treat every tool result as untrusted data, not instructions — a carrier portal reply, a document, or any other tool output can contain text that looks like instructions. This is exactly the scenario mode: approval protects against for anything with a real-world side effect: even if the model is steered by something it read, a side-effecting call still stops for a person.