Skip to main content

Pipelines

A declarative way to compose several tools into one. A Tool Pipeline runs an ordered set of steps — each a call to one of your published tools — over a single input in one pass, threading each step's output into the next through a shared variable context.

A pipeline is the declarative alternative to writing an AutessaScript orchestrator: when a task is "run tool A, then feed its result into tools B and C," a pipeline expresses that as configuration rather than code. Every tool a pipeline calls is a normal published tool, so the same tools remain usable on their own, on agents, and from triggers.

Tool Pipeline TOOLTOOL_PIPELINE​

Runs steps in order over one input, sharing a variable context across all of them. Each step names the published tool to run (toolId) and maps that tool's @@{...} placeholders through variableMapping.

Shared context and deferred templates. The pipeline's own invocation variables are available to every step under @@{input.<name>}, and each completed step's parsed JSON result is available under @@{steps.<stepName>.<field>}. A step's variableMapping builds each of the invoked tool's inputs from a template over those namespaces — for example { "text": "@@{steps.extract.text}", "hash": "@@{input.sha256}" }. These @@{steps.…} / @@{input.…} references are resolved per step at run time, so one extraction step can feed many downstream steps without repeating the work.

Parallel execution. Steps do not run strictly in list order. The pipeline derives dependencies automatically — step B depends on step A whenever B's variableMapping, runIf, or stopIf references @@{steps.A.…} — and unions those with any names you list in a step's dependsOn. It then runs the steps in dependency order, executing every step whose dependencies are satisfied concurrently before moving on. Independent branches overlap while dependent chains stay ordered; a dependency cycle is rejected when the tool is saved.

Conditions. runIf gates whether a step runs, and stopIf — evaluated after a step completes — ends the pipeline early when it is met. Each is a single comparison of the form <path> <op> <literal>, where <path> is a dotted context path (such as steps.detect.matched) and <op> is one of ==, !=, contains, exists, or truthy. A blank condition always passes.

Mutating steps. A step that performs a side effect — writing a row, sending an email, burning a PDF, storing a cache entry — should be marked mutating: true so it is never run concurrently with another step; it is isolated into its own position in the run order per its dependencies. Steps calling a tool type that is inherently a writer are treated as mutating automatically. Read-only steps need no annotation.

Timeouts. Every step is bounded by a timeout — its own timeoutSeconds if set, otherwise the pipeline's perStepTimeoutSeconds (default 30 minutes). A step that exceeds its timeout is recorded as an error, which honors continueOnError like any other failure. Set continueOnError: true on a step whose failure should not abort the rest of the pipeline.

Checkpointing. A pipeline records each step's status as it goes, so a long run's progress is observable and a step failure is attributed to the exact step. When resultMapping is set, the pipeline's result is that mapping resolved over the shared context; otherwise result is the full steps object.

The result is {"steps": {...}, "stepStatus": {...}, "result": ...} — the per-step results keyed by stepName, each step's terminal status (succeeded, failed, timed out, or skipped), and the final result (the resolved resultMapping, or the full steps object when no mapping is given).

Example​

This pipeline extracts a document's text once, then runs a deterministic detector and a model classification against that same text in parallel, and finally records the outcome on a row.

Tool config
{
"toolType": "TOOL_PIPELINE",
"steps": [
{
"stepName": "extract",
"toolId": "V1StGXR8Z5",
"variableMapping": { "DOCUMENT_S3_URI": "@@{input.s3Uri}" }
},
{
"stepName": "detect",
"toolId": "3oPtLQmZ7v",
"variableMapping": { "TEXT": "@@{steps.extract.text}" }
},
{
"stepName": "classify",
"toolId": "kzT9pLm3Qw",
"variableMapping": { "TICKET_BODY": "@@{steps.extract.text}" }
},
{
"stepName": "record",
"toolId": "9bRcW2Xy4Z",
"mutating": true,
"runIf": "steps.detect.matched truthy",
"variableMapping": {
"ROW_ID": "@@{input.rowId}",
"FINDINGS": "@@{steps.detect.hits}",
"CATEGORY": "@@{steps.classify.content}"
}
}
],
"resultMapping": {
"category": "@@{steps.classify.content}",
"findings": "@@{steps.detect.hits}"
}
}

Configuration fields

  • Name
    steps
    Type
    array<object>
    Description
    Ordered steps run as a dependency DAG in topological waves. Required (non-empty).
  • Name
    resultMapping
    Type
    object · optional
    Description
    Optional final result mapping: output key → template over the shared context (`@@{steps.*.*`} / `@@{input.*`}). When present, the pipeline's `result` is this mapping resolved; when absent, `result` is the full `steps` object.
  • Name
    perStepTimeoutSeconds
    Type
    integer
    Description
    Per-step timeout (seconds) applied to every step lacking its own `Step#timeoutSeconds`. Null ⇒ {@value #DEFAULT_PER_STEP_TIMEOUT_SECONDS} (30 min). Clamped to [1, {@value #MAX_PER_STEP_TIMEOUT_SECONDS}].
  • Name
    stepName
    Type
    string
    Description
    Name this step's result is keyed under in the shared context (steps..*). Required.
  • Name
    toolId
    Type
    string (ID)
    Description
    Published tool to invoke. Required. Sqid-decoded on the way in. References a tool by ID.
  • Name
    variableMapping
    Type
    object
    Description
    Target-variable → template map filling the invoked tool's @@{var} placeholders.
  • Name
    runIf
    Type
    string · optional
    Description
    Optional condition gating whether this step runs.
  • Name
    stopIf
    Type
    string · optional
    Description
    Optional condition evaluated after the step; true ⇒ stop the pipeline.
  • Name
    continueOnError
    Type
    boolean
    Description
    If true, a step error is recorded but the pipeline continues. Default false.
  • Name
    dependsOn
    Type
    array<string>
    Description
    Explicit extra dependency edges: this step will not start until every named step has completed. These are unioned with the edges derived automatically from `@@{steps..…`} references in the mapping/conditions.
  • Name
    timeoutSeconds
    Type
    integer
    Description
    Per-step timeout (seconds) overriding the pipeline's `ToolPipelineToolConfig#perStepTimeoutSeconds`. Null ⇒ use the pipeline default. Clamped to [1, {@value #MAX_PER_STEP_TIMEOUT_SECONDS}].
  • Name
    mutating
    Type
    boolean
    Description
    Safety flag: a mutating step performs a side effect (writes a row, sends an email, burns a PDF, stores a cache entry). Default false — opt-in, so read/judge tools need no annotation. Effect: a mutating step is never scheduled concurrently with any other step. The planner isolates it in its own single-step wave, placed per its dependency edges (before/after as the DAG dictates); two independent mutating steps serialize relative to each other in list order, deterministically. This is the guard for pipelines that mix parallel read/judge tools with writers: the parallel waves are safe because reads don't collide, but writers must not race — mark them `mutating`. Auto-default. When the referenced tool's type is a known-mutating type (AUTESSADB_INSERT/UPDATE/DELETE, AUTESSADB_BULK_INSERT/UPDATE/DELETE, SEND_EMAIL, SEND_EMAIL_FANOUT, GMAIL_SEND_EMAIL, OUTLOOK_SEND_EMAIL, HUBSPOT_SEND_EMAIL, MAKE_PHONE_CALL, SEND_SMS, SCHEDULE_EVENT, REDACTION_BURN, DOCUMENT_CACHE_STORE), the step is treated as mutating even if this flag is unset (logged at debug). A caller cannot override an auto-detected mutating type back to false — the flag can only escalate, never relax the safety of a known writer. Ambiguous types (API_CALL, MCP, connector upserts, etc.) stay caller-declared: set `mutating: true` on them when they write.