Files
paseo/public-docs/hub/workflows.md
T
Mohamed BoudraandGitHub 05a0cabc07 Deploy multi-file Hub workflows (#3128)
* feat(hub): deploy canonical workflow bundles

Keep CLI deployment aligned with Hub's authored bundle boundary so validation and activation receive the same deterministic source files. Public documentation now teaches that boundary as the only configuration format.

* test(hub): assert validation diagnostics directly

* refactor(hub): deepen HTTP client module

* feat(hub): validate named agent configurations

* docs(hub): align Claude mode example
2026-08-10 12:32:59 +02:00

6.4 KiB

title, description, nav, order, category
title description nav order category
Hub workflows Build ordered Hub workflows with prompts, routing, outputs, and provider authority. Workflows 65 Hub

Hub workflows

A workflow file contains one trigger and the ordered steps it starts. Files are discovered from .paseo/workflows/*.yml.

Your first workflow

Assume .paseo/hub.yml defines an environment named dev and an agent named codex. Add .paseo/workflows/slack-help.yml:

name: slack-help
on: slack.mention
max_runtime: 1h
filters:
  workspace: T01234567
  channels: [C01234567]
  from_users: [U01234567]
steps:
  - id: answer
    environment: dev
    max_runtime: 30m
    idle_timeout: 5m
    agent: codex
    prompt:
      - text: |
          Answer the request. Call hub.reply once, then call hub.finish_execution.

          <user-prompt>
          ${{ paseo.prompt }}
          </user-prompt>
    allow_outputs:
      - { type: slack.reply, max: 1, required: true }

Hub removes the mention and declared input headers before exposing the remaining text as ${{ paseo.prompt }}. The reply capability is explicit beside the Slack trigger. Discord uses discord.reply; GitHub uses a step-scoped github block, not hub.reply.

Choose where a step runs

A literal selects one named environment:

environment: dev

A finite input can select among complete named environments:

name: route-repository
on: manual.run
max_runtime: 1h
inputs:
  repo:
    type: string
    required: true
    choices: [paseo, hub]
steps:
  - id: work
    environment: ${{ paseo.inputs.repo }}
    max_runtime: 30m
    idle_timeout: 5m
    agent: codex
    prompt:
      - text: ${{ paseo.prompt }}

Activation checks every choices result. Environment objects are never merged or overridden by a workflow.

Choose an agent

A step may select a named agent:

agent: codex-safe

Or provide one complete static inline configuration:

agent:
  provider: claude
  mode: full-access

For dynamic routing, select complete named agents from a finite expression:

name: route-agent
on: manual.run
max_runtime: 1h
inputs:
  agent:
    type: string
    required: true
    choices: [codex-safe, claude]
steps:
  - id: work
    environment: paseo
    max_runtime: 30m
    idle_timeout: 5m
    agent: ${{ paseo.inputs.agent }}
    prompt:
      - text: ${{ paseo.prompt }}

If codex-safe contains structured sandbox options in hub.yml, selecting it carries those options unchanged. A dynamic inline object such as provider: ${{ paseo.inputs.agent }} is rejected.

Route from a classifier

An earlier step can return finite structured output. Later authority must be bounded by enum or const in that output schema.

name: classify-request
on: discord.mention
max_runtime: 2h
filters:
  guild: "123456789012345678"
  from_users: ["345678901234567890"]
values:
  selected_environment: ${{ steps.classify.outputs.environment }}
  selected_agent: ${{ steps.classify.outputs.agent }}
steps:
  - id: classify
    environment: hub
    max_runtime: 5m
    idle_timeout: 1m
    agent: claude
    prompt:
      - include: partials/classify.md
      - text: ${{ paseo.prompt }}
    output:
      schema:
        type: object
        required: [environment, agent]
        properties:
          environment: { enum: [paseo, hub] }
          agent: { enum: [codex-safe, claude] }
        additionalProperties: false
  - id: work
    environment: ${{ values.selected_environment }}
    max_runtime: 1h
    idle_timeout: 10m
    agent: ${{ values.selected_agent }}
    prompt:
      - text: |
          Complete the request. Call hub.reply once, then call hub.finish_execution.
      - text: ${{ paseo.prompt }}
    allow_outputs:
      - { type: discord.reply, max: 1, required: true }

.paseo/workflows/partials/classify.md:

Choose one configured repository environment and one complete named agent configuration.

The workflow keeps one classifier branch and one worker branch. It does not duplicate a worker step for every environment/provider pair.

Prompt and context

Prompt blocks remain ordered. Includes are literal file contents; Hub does not recursively scan partial text.

prompt:
  - include: partials/instructions.md
  - text: |
      Provider evidence:
      ${{ paseo.context }}

      <user-prompt>
      ${{ paseo.prompt }}
      </user-prompt>
  • ${{ paseo.prompt }} is normalized request text. It is always explicit in the authored prompt.
  • ${{ paseo.context }} opts this step into provider context materialization and inserts JSON. Without that expression, Hub does not fetch or inject ambient context.

Keep untrusted request text in a clearly delimited block. A partial is instruction text, not hidden authority.

Conditions and ordered output

Steps run in file order. if may read inputs, values, and prior step output:

name: conditional-review
on: manual.run
max_runtime: 1h
steps:
  - id: inspect
    environment: paseo
    max_runtime: 10m
    idle_timeout: 2m
    agent: codex-safe
    prompt:
      - text: ${{ paseo.prompt }}
    output:
      schema:
        type: object
        required: [needs_review]
        properties:
          needs_review: { type: boolean }
        additionalProperties: false
  - id: review
    if: ${{ steps.inspect.outputs.needs_review == true }}
    environment: paseo
    max_runtime: 30m
    idle_timeout: 5m
    agent: claude
    prompt:
      - text: Review the prior result and call hub.finish_execution.

A step cannot read a later step. Step IDs are unique within the workflow.

Tell the agent which tool to call

allow_outputs grants a capability; it does not rewrite the prompt. Name the required action:

prompt:
  - text: |
      Send the final answer with hub.reply.
      Then call hub.finish_execution.
allow_outputs:
  - type: slack.reply
    max: 1
    required: true

max defaults to 1. required: true prevents successful completion until the capability has been emitted. Keep Slack and Discord reply types in their own provider workflow files.

Deadlines

The workflow max_runtime limits the complete run. Every step has its own max_runtime and idle_timeout; remaining workflow time caps both. A timeout fails the run and stops later steps.

The configuration reference lists every field. Provider filters and invocation text are in Triggers.