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
This commit is contained in:
Mohamed Boudra
2026-08-10 12:32:59 +02:00
committed by GitHub
parent b7f137c2a0
commit 05a0cabc07
49 changed files with 2475 additions and 3085 deletions
+21 -34
View File
@@ -1,6 +1,6 @@
---
title: Discord triggers
description: Configure Discord mentions as Hub triggers and route them into durable workflows.
description: Configure Discord mentions and replies in one workflow file.
nav: Discord
order: 69
category: Hub
@@ -8,47 +8,34 @@ category: Hub
# Discord triggers
## Events
`discord.mention` fires when the bot or a managed role is mentioned in a guild channel or thread.
| `on` | Fires when |
| ----------------- | ------------------------------------------------------------------------------ |
| `discord.mention` | The bot or one of its managed roles is mentioned in a guild channel or thread. |
## Filters
Discord filters use quoted snowflake IDs:
`.paseo/workflows/discord-help.yml`:
```yaml
name: discord-help
on: discord.mention
max_runtime: 1h
filters:
guild: "123456789012345678"
channels: ["234567890123456789"]
from_users: ["345678901234567890"]
steps:
- id: answer
environment: dev
max_runtime: 30m
idle_timeout: 5m
agent: codex
prompt:
- text: |
Answer with hub.reply, then call hub.finish_execution.
${{ paseo.prompt }}
allow_outputs:
- { type: discord.reply, max: 1, required: true }
```
Turn on Developer Mode to copy IDs. `from_users` matches the author's user id, `guild` matches the connected guild, and `channels` matches the channel or thread parent. `pattern` matches the start of the text after the mention. All filters must pass.
Turn on Discord Developer Mode to copy IDs. `from_users` matches the author; `guild` and `channels` constrain where the mention arrived. All filters must pass.
## Invocation
The reply posts in the triggering thread or channel. `discord.reply` grants `hub.reply`, but does not rewrite the prompt. A Discord trigger grants no GitHub credential; add a [`github` block](/docs/hub/github) to the step that needs one.
Put leading inputs directly after the mention:
```text
@Paseo repo=project investigate the failed sync
```
Hub consumes only declared consecutive headers and passes `investigate the failed sync` as `${{ paseo.prompt }}`. See [Hub workflows](/docs/hub/workflows) for the provider-neutral input contract.
## Replies and repository access
Put the reply capability on a step:
```yaml
allow_outputs:
- type: discord.reply
max: 5
```
The reply is posted in the triggering thread or channel. The declaration grants the `hub.reply` tool; the prompt has to tell the agent to call it. See [Tell the agent which tool to call](/docs/hub/workflows#tell-the-agent-which-tool-to-call).
A Discord trigger carries no implicit GitHub credential. A step that needs GitHub declares a [`github` block](/docs/hub/github).
See the [workflow scenarios](/docs/hub/workflows#scenarios) for complete step configurations.
Leading declared inputs follow the mention. Hub exposes the remaining text as `${{ paseo.prompt }}`. See [Workflows](/docs/hub/workflows).
+32 -32
View File
@@ -1,6 +1,6 @@
---
title: GitHub triggers
description: Configure GitHub events as Hub triggers and route them into durable workflows.
description: Configure GitHub events with explicit step-scoped GitHub authority.
nav: GitHub
order: 67
category: Hub
@@ -8,46 +8,46 @@ category: Hub
# GitHub triggers
## Events
| `on` | Event |
| ------------------------------------ | ------------------------------------ |
| `github.issue_comment` | Comment on an issue or pull request. |
| `github.issues` | Issue event. |
| `github.pull_request_review` | Pull request review. |
| `github.pull_request_review_comment` | Diff comment. |
| `on` | Event |
| ------------------------------------ | -------------------------------------- |
| `github.issue_comment` | A comment on an issue or pull request. |
| `github.issues` | An issue. |
| `github.pull_request_review` | A pull request review. |
| `github.pull_request_review_comment` | A comment on a diff. |
GitHub sends every subscribed action. Each delivery that passes the filters starts a run.
Hub does not filter by webhook action. GitHub delivers a subscribed event for every action — created, edited, closed, deleted — and each delivery that passes the trigger's filters starts a run.
Which repositories produce events is set on the GitHub App installation. `filters.repo` narrows a trigger to one `owner/name` repository.
## Filters
`.paseo/workflows/github-change.yml`:
```yaml
name: github-change
on: github.issue_comment
max_runtime: 2h
filters:
repo: example/project
contains: "@paseo"
from_users: [maintainer]
steps:
- id: implement
environment: dev
max_runtime: 90m
idle_timeout: 10m
agent: codex
github:
connection: example-github
repositories: [example/project]
permissions:
contents: write
pull_requests: write
prompt:
- text: |
Implement the request and open a pull request with gh.
Call hub.finish_execution when done.
${{ paseo.prompt }}
```
`from_users` matches the sender's GitHub login. `contains` checks the event text; `pattern` checks its start. The text is the comment body for comments and reviews, and the issue title plus body for issues. All filters must pass.
`from_users` matches the GitHub login. `contains` checks event text and `pattern` checks its start. Comment events use the comment body; issue events use title plus body.
## Invocation
A GitHub trigger grants no token. Authority is the `github` block on the step that needs it. GitHub has no `hub.reply` capability; the agent acts through `gh` within the declared connection, repositories, and permissions.
Put the configured marker in the message, then put leading inputs in the text parsed after that marker:
```text
@paseo repo=project agent=claude investigate the failed sync
```
Hub consumes only declared consecutive headers and passes `investigate the failed sync` as `${{ paseo.prompt }}`. See [Hub workflows](/docs/hub/workflows) for input types, defaults, choices, and rejected Activity records.
## Credentials and replies
A GitHub trigger grants nothing by itself. A step that should comment, push, or open a pull request declares a [`github` block](/docs/hub/github); without one the agent has no token and no git configuration.
The event's identifiers are not part of the prompt. `${{ paseo.prompt }}` carries the comment body for comment events and the title plus body for issues, so give the agent work it can complete from that text alone — opening a pull request, for example — rather than replying to a thread it has no way to locate.
On comment events, Hub reacts on the triggering comment: 👀 when it accepts the event, 🚀 when the agent starts, 👍 when the step completes, and 👎 when it fails. Plain issue events get no reaction; watch those runs in Project → Activity.
`hub.reply` covers Slack and Discord, not GitHub. See [Tell the agent which tool to call](/docs/hub/workflows#tell-the-agent-which-tool-to-call).
On comment events, Hub reacts with 👀 when accepted, 🚀 when the agent starts, 👍 on completion, and 👎 on failure. `${{ paseo.prompt }}` contains normalized request text, not event identifiers. Use `${{ paseo.context }}` in prompt text when the step explicitly needs provider context.
+19 -21
View File
@@ -10,30 +10,28 @@ category: Hub
A trigger says which provider event can start a workflow. The [Hub workflows](/docs/hub/workflows) page covers the steps, inputs, routing, prompts, and deadlines that run after a match.
`.paseo/workflows/github-mention.yml`:
```yaml
triggers:
- name: mention
on: github.issue_comment
filters:
repo: acme/api
contains: "@paseo"
from_users: [alice]
max_runtime: 2h
steps:
- id: work
environment: dev
max_runtime: 90m
idle_timeout: 10m
agent: { provider: codex, mode: full-access }
prompt:
- text: Call hub.finish_execution when the step is complete.
- text: |
<user-prompt>
${{ paseo.prompt }}
</user-prompt>
name: mention
on: github.issue_comment
filters:
repo: acme/api
contains: "@paseo"
from_users: [alice]
max_runtime: 2h
steps:
- id: work
environment: dev
max_runtime: 90m
idle_timeout: 10m
agent: codex
prompt:
- text: Call hub.finish_execution when the step is complete.
- text: ${{ paseo.prompt }}
```
Field-by-field detail is in the [`hub.yml` reference](/docs/hub/configuration/hub-yml).
Field-by-field detail is in the [configuration reference](/docs/hub/configuration/hub-yml).
## Events
+22 -30
View File
@@ -1,6 +1,6 @@
---
title: Slack triggers
description: Configure Slack mentions as Hub triggers and route them into durable workflows.
description: Configure Slack mentions and thread replies in one workflow file.
nav: Slack
order: 68
category: Hub
@@ -8,48 +8,40 @@ category: Hub
# Slack triggers
## Events
`slack.mention` fires when the bot is mentioned in a channel where it is present. Direct messages, slash commands, and interactive components do not produce this trigger.
| `on` | Fires when |
| --------------- | --------------------------------------------------------- |
| `slack.mention` | The bot is mentioned in a channel it has been invited to. |
The mention is required. Direct messages, slash commands, and interactive components do not produce this trigger.
## Filters
Slack filters use IDs, not display names:
`.paseo/workflows/slack-help.yml`:
```yaml
name: slack-help
on: slack.mention
max_runtime: 1h
filters:
workspace: T01234567
channels: [C01234567]
from_users: [U01234567]
pattern: "repo="
steps:
- id: answer
environment: dev
max_runtime: 30m
idle_timeout: 5m
agent: codex
prompt:
- text: |
Answer with hub.reply, then call hub.finish_execution.
${{ paseo.prompt }}
allow_outputs:
- { type: slack.reply, max: 1, required: true }
```
`from_users` matches the author's Slack user id. `workspace` is the team id. `channels` matches the channel id. `pattern` (or `contains`) matches the start of the text after the mention. All filters must pass.
Slack filters use IDs, not display names. `from_users` matches the author, `workspace` the team, and `channels` the channel. `pattern` checks the beginning of text after the mention; `contains` checks a substring. All filters must pass.
## Invocation
The reply posts in the triggering thread. A root message gets a thread; a threaded message stays there. `slack.reply` grants `hub.reply`, but does not add reply instructions to the prompt.
Put leading inputs directly after the mention:
Leading declared inputs follow the mention:
```text
@Paseo repo=project agent=claude investigate the failed sync
```
Hub consumes only declared consecutive headers and passes `investigate the failed sync` as `${{ paseo.prompt }}`. See [Hub workflows](/docs/hub/workflows) for input types, defaults, choices, and rejection behavior.
## Replies and workflow shape
Put the reply capability on a step:
```yaml
allow_outputs:
- type: slack.reply
max: 5
```
The reply is posted in the triggering thread. A root message gets a new thread; a threaded message stays in that thread.
The declaration grants the `hub.reply` tool; the prompt has to tell the agent to call it. See [Tell the agent which tool to call](/docs/hub/workflows#tell-the-agent-which-tool-to-call), and the shared [workflow scenarios](/docs/hub/workflows#scenarios) for complete configurations.
Hub consumes consecutive declared headers and exposes the remainder as `${{ paseo.prompt }}`. See [Workflows](/docs/hub/workflows).