docs(hub): document durable workflow routing (#2943)

* docs(hub): document durable workflow routing

* docs(hub): make workflow guide progressive
This commit is contained in:
Mohamed Boudra
2026-08-06 16:07:41 +02:00
committed by GitHub
parent bb55e5bc0d
commit deb5e5c729
13 changed files with 693 additions and 578 deletions
+21 -87
View File
@@ -1,6 +1,6 @@
---
title: Discord triggers
description: Discord mentions as Hub triggers, snowflake filters, thread context, and the full paseo.event.discord data reference.
description: Configure Discord mentions as Hub triggers and route them into durable workflows.
nav: Discord
order: 67
category: Hub
@@ -10,15 +10,13 @@ category: Hub
## Events
| `on` | Fires when |
| ----------------- | ------------------------------------------------- |
| `discord.mention` | The bot is mentioned in a guild channel or thread |
Mentioning a role the bot holds counts as mentioning the bot.
| `on` | Fires when |
| ----------------- | ------------------------------------------------------------------------------ |
| `discord.mention` | The bot or one of its managed roles is mentioned in a guild channel or thread. |
## Filters
Discord filters take snowflake IDs. Turn on **Developer Mode** in Discord's advanced settings, then right-click a channel or user to copy its ID.
Discord filters use quoted snowflake IDs:
```yaml
filters:
@@ -27,92 +25,28 @@ filters:
from_users: ["345678901234567890"]
```
Quote them. Unquoted snowflakes parse as numbers and lose precision.
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.
- `from_users` matches the author's user id.
- `guild` is resolved to the connection on activation.
- `channels` matches the channel the message was posted in; in a thread, the thread's parent channel counts.
- `pattern` matches the start of the text after the mention.
## Invocation
## Replying
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
```
One plain-text reply per execution, in the triggering thread or channel.
The reply is posted in the triggering thread or channel. A Discord trigger has no implicit GitHub credential. If a step needs another connection, configure that connection through the supported step environment for your deployment; Hub does not guess between GitHub installations.
## Repository access
A Discord trigger has no implicit GitHub credential. Name the connection you want:
```yaml
env:
GH_TOKEN: ${{ paseo.connections.acme-github.token }}
```
Hub will not guess between several GitHub installations.
## Data reference
The triggering message is `paseo.event.discord.trigger_message`:
| Path | Value |
| -------------------- | ----------------------------------------------------- |
| `id` | Message snowflake |
| `content` | Raw text, including the mention |
| `body` | Text with the mention stripped, usually what you want |
| `url` | Canonical message link |
| `author` | Author identity, including the user id |
| `channel` | Channel identity |
| `thread` | Thread identity, or `null` outside a thread |
| `created_at` | Message creation time |
| `attachments` | Typed list, see below |
| `referenced_message` | Identity of a replied-to message, or `null` |
Each attachment has `id`, `filename`, `url`, `content_type`, and `size`. A `referenced_message` carries `id`, `channel_id`, and `guild_id`, without the message content.
Also available: `paseo.event.discord.event_type` and `paseo.event.discord.guild.id`.
### Thread context
In a thread, `paseo.event.discord.trigger_thread_context.messages` holds the preceding messages, **oldest first**, never including the triggering message. Each entry exposes the same identity, content, author, channel, creation time, attachment, and reference fields as `trigger_message`.
This is how you give an agent the conversation rather than one line:
```yaml
prompt: |
Thread so far:
${{ paseo.event.discord.trigger_thread_context.messages }}
Latest request:
${{ paseo.event.discord.trigger_message.body }}
```
Outside a thread the list is empty.
## Example
```yaml
triggers:
- name: discord-fix
on: discord.mention
environment: dev
filters:
guild: "123456789012345678"
channels: ["234567890123456789"]
from_users: ["345678901234567890"]
agent:
provider: codex
mode: full-access
env:
GH_TOKEN: ${{ paseo.connections.acme-github.token }}
allow_outputs:
- type: discord.reply
prompt: |
${{ paseo.event.discord.trigger_message.body }}
Context: ${{ paseo.event.discord.trigger_message.url }}
Reply in the thread when you're done.
```
See the [workflow examples](/docs/hub/configuration/examples) for complete step configurations.
+24 -84
View File
@@ -1,6 +1,6 @@
---
title: GitHub triggers
description: GitHub events Hub can trigger on, how filters match them, and the full paseo.event.github data reference.
description: Configure GitHub events as Hub triggers and route them into durable workflows.
nav: GitHub
order: 65
category: Hub
@@ -10,104 +10,44 @@ category: Hub
## Events
| `on` | Fires when |
| ------------------------------------ | ------------------------------------- |
| `github.issue_comment` | A comment on an issue or pull request |
| `github.issues` | An issue is opened or edited |
| `github.pull_request_review` | A review is submitted |
| `github.pull_request_review_comment` | A comment on a diff |
| `on` | Fires when |
| ------------------------------------ | ------------------------------------------------- |
| `github.issue_comment` | A comment on an issue or pull request is created. |
| `github.issues` | An issue is opened or edited. |
| `github.pull_request_review` | A review is submitted. |
| `github.pull_request_review_comment` | A comment is added to a diff. |
Which repositories produce events is set on the GitHub connection's installation, not in Paseo.
Which repositories produce events is set on the GitHub App installation. `filters.repo` narrows a trigger to one `owner/name` repository.
## Filters
```yaml
filters:
repo: acme/api
repo: example/project
contains: "@paseo"
from_users: [alice, bob]
from_users: [maintainer]
```
- `from_users` matches the GitHub **login** of the event's sender.
- `repo` is `owner/name`, resolved to the repository's immutable id on activation.
- `contains` is a substring test; `pattern` must match at the start.
`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.
The text those two search depends on the event:
## Invocation
| Event | Text searched |
| ------------------------------------ | -------------------- |
| `github.issue_comment` | The comment body |
| `github.issues` | Issue title and body |
| `github.pull_request_review` | The review body |
| `github.pull_request_review_comment` | The comment body |
Put the configured marker in the message, then put leading inputs in the text parsed after that marker:
## Tokens
```text
@paseo repo=project agent=claude investigate the failed sync
```
A GitHub-triggered execution automatically receives a `GH_TOKEN` scoped to the triggering repository, with `contents`, `issues`, and `pull_requests` write access. It is minted per execution and revoked when the execution ends. You do not map it in `env`.
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.
For a token from a _different_ connection, or from a non-GitHub trigger, name it explicitly:
## Credentials and replies
GitHub-triggered steps receive a scoped `GH_TOKEN` for the triggering repository. The agent can use `gh` to comment, push, and open a pull request. A step may also declare:
```yaml
env:
GH_TOKEN: ${{ paseo.connections.acme-github.token }}
allow_outputs:
- type: github.reply
max: 5
```
## Data reference
GitHub's webhook payload is available directly under `paseo.event.github`, unflattened. Anything GitHub sends, you can read:
```yaml
prompt: |
${{ paseo.event.github.issue.title }}
by ${{ paseo.event.github.sender.login }}
on ${{ paseo.event.github.repository.full_name }}
```
Common paths by event:
| Event | Useful paths |
| ------------------------------------ | ------------------------------------------------------------------------------- |
| `github.issue_comment` | `comment.body`, `comment.html_url`, `issue.number`, `issue.title`, `issue.body` |
| `github.issues` | `issue.number`, `issue.title`, `issue.body`, `action` |
| `github.pull_request_review` | `review.body`, `review.state`, `pull_request.number` |
| `github.pull_request_review_comment` | `comment.body`, `comment.path`, `comment.diff_hunk`, `pull_request.number` |
All events also carry `sender.login`, `repository.full_name`, and `repository.default_branch`.
Hub adds an envelope alongside GitHub's fields:
| Path | Value |
| ---------------------- | ---------------------------------------------- |
| `delivery_id` | GitHub's webhook delivery id |
| `event_name` | `issue_comment`, `issues`, … |
| `repository_full_name` | `owner/name` |
| `installation_id` | The installation that delivered the event |
| `received_at` | When Hub accepted it |
| `trigger_url` | Canonical URL of the comment, issue, or review |
`trigger_url` is only present when the event has one. It is the field you usually want in a prompt.
## Example
```yaml
triggers:
- name: review-fix
on: github.pull_request_review_comment
environment: dev
filters:
repo: acme/api
contains: "@paseo"
from_users: [alice]
agent:
provider: codex
mode: full-access
prompt: |
${{ paseo.event.github.sender.login }} left this on PR
#${{ paseo.event.github.pull_request.number }},
in ${{ paseo.event.github.comment.path }}:
${{ paseo.event.github.comment.body }}
Fix it and push. Reply on the thread with gh when you're done.
${{ paseo.event.github.trigger_url }}
```
See the [workflow examples](/docs/hub/configuration/examples) for review and PR workflows.
+12 -15
View File
@@ -2,27 +2,31 @@
title: Hub triggers
description: How Hub matches an inbound event to a trigger: events, filters, and the allowlist that gates every execution.
nav: Triggers
order: 64
order: 65
category: Hub
---
# Triggers
A trigger says: when this event arrives, from this source, from these people, run this agent with this prompt.
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.
```yaml
triggers:
- name: mention
on: github.issue_comment
environment: dev
filters:
repo: acme/api
contains: "@paseo"
from_users: [alice]
agent:
provider: codex
mode: full-access
prompt: ${{ paseo.event.github.comment.body }}
max_runtime: 2h
steps:
- id: work
environment: dev
max_runtime: 90m
idle_timeout: 10m
agent: { provider: codex, mode: full-access }
prompt:
- text: ${{ paseo.prompt }}
```
Field-by-field detail is in the [`hub.yml` reference](/docs/hub/configuration/hub-yml). This page covers matching.
@@ -84,11 +88,4 @@ Both run. Triggers are not ordered and do not shadow each other, in one configur
## Replying
`allow_outputs` gives the agent a single-use `reply` tool for the conversation that triggered it:
```yaml
allow_outputs:
- type: slack.reply
```
Only `slack.reply` and `discord.reply` exist. GitHub-triggered agents reply with the scoped `GH_TOKEN` they already have, so `gh issue comment` works.
Put `allow_outputs` on the step that should reply. The available provider reply types are `slack.reply`, `discord.reply`, and `github.reply`; set `max` when a step needs more than one update. GitHub-triggered agents also receive a scoped GitHub credential for `gh`.
+22 -67
View File
@@ -1,6 +1,6 @@
---
title: Slack triggers
description: Slack mentions as Hub triggers, ID-based filters, replies, and the full paseo.event.slack data reference.
description: Configure Slack mentions as Hub triggers and route them into durable workflows.
nav: Slack
order: 66
category: Hub
@@ -10,89 +10,44 @@ category: Hub
## Events
| `on` | Fires when |
| --------------- | -------------------------------------------------------- |
| `slack.mention` | The bot is mentioned in a channel it has been invited to |
| `on` | Fires when |
| --------------- | --------------------------------------------------------- |
| `slack.mention` | The bot is mentioned in a channel it has been invited to. |
The bot must be in the channel. `/invite @Paseo` first, or nothing arrives.
The mention is required. Direct messages, slash commands, and interactive components do not produce this trigger.
## Filters
Slack filters take IDs, never display names. Copy a channel ID from the channel's details; copy a user ID from a member's profile menu.
Slack filters use IDs, not display names:
```yaml
filters:
workspace: T01234567
channels: [C01234567]
from_users: [U01234567]
pattern: "repo="
```
- `from_users` matches the Slack user id of the author.
- `workspace` is the team id, resolved to the connection on activation.
- `channels` matches the channel id the message was posted in.
- `pattern` matches the start of the text **after** the mention, and must end on a word boundary. `contains` is treated the same as `pattern` here.
`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.
The mention itself is always required. A message that does not mention the bot never produces an event.
## Invocation
## Replying
Put leading inputs directly after 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
```
That gives the agent one plain-text reply, posted in the thread. A root message gets a new thread; a threaded message gets a reply in the same thread.
## Data reference
Everything lives under `paseo.event.slack`.
| Path | Value |
| ------------ | --------------------------------- |
| `event_type` | `mention` |
| `event_id` | Slack's Events API event id |
| `event_ts` | Event timestamp from the envelope |
| `event_time` | Envelope delivery time |
| `team` | Workspace identity |
| `app` | The installed app identity |
The message itself is `paseo.event.slack.trigger_message`:
| Path | Value |
| ------------ | ----------------------------------------------------- |
| `ts` | Native message timestamp, Slack's message id |
| `content` | Raw text, including the `<@BOT>` mention |
| `body` | Text with the mention stripped, usually what you want |
| `author` | Author identity, including the user id |
| `channel` | Channel identity |
| `thread` | Thread identity, or `null` for a root message |
| `created_at` | Derived from the message `ts` |
Use `body`, not `content`, unless you specifically need the mention text.
## Example
```yaml
triggers:
- name: slack-mention
on: slack.mention
environment: dev
filters:
workspace: T01234567
channels: [C01234567]
from_users: [U01234567]
agent:
provider: codex
mode: full-access
idle_timeout: 10m
allow_outputs:
- type: slack.reply
prompt: |
${{ paseo.event.slack.trigger_message.body }}
Reply in the thread when you're done.
```
## Limits
Direct messages, slash commands, and interactive components do not produce triggers. Enterprise Grid org-wide installs are not supported.
The reply is posted in the triggering thread. A root message gets a new thread; a threaded message stays in that thread. Use the shared [workflow examples](/docs/hub/configuration/examples) for complete configurations.