---
title: "トリガー"
description: "トリガーは、スケジュール、Webhook、アプリイベント、またはモニターによってセッションを開始します。"
---

トリガーは、人がいない状態で[セッション](/docs/ja/work/sessions)を開始します。トリガーを使うと、定期的な作業やイベント駆動型の作業を自動化できます。

1つのセッションのタスクを後で確認する場合（「明日、返信があったか確認する」など）は、代わりに[リマインダー](/docs/ja/connect/reminders)を使います。リマインダーは、そのセッションに限定されたトリガーです。`beeuni.yaml`ではなく、データベースに保存されます。

<a id="trigger-types"></a>

## トリガーの種類

Beeuniは4種類のトリガーに対応しています。

- **cron** — 設定したスケジュールで実行します。
- **webhook** — 外部サービスがプロジェクトのWebhook URLに署名付きリクエストを送信すると実行します。
- **event** — 接続済みアプリで新しいプルリクエストやメールの受信などが発生すると実行します。[アプリイベントトリガー](#app-event-triggers)を参照してください。
- **monitor** — リポジトリのコマンドを24時間365日実行します。コマンドがstdoutに出力する各行でトリガーが発火します。試験運用中です。[モニター](#monitors)を参照してください。

トリガーはプロジェクトマニフェスト`beeuni.yaml`で定義します。各トリガーには、発火したセッションの最初のメッセージとして表示されるプロンプトがあります。最終発火時刻やステータスなどの実行時の状態は、マニフェストではなくデータベースに保存されます。トリガーが発火してもコミットは作成されません。

ダッシュボードからトリガーを作成、更新、削除すると、デフォルトブランチに直接書き込まれます。[変更リクエスト](/docs/ja/work/change-requests)（CR）は経由しません。セッション内で`beeuni.yaml`を編集して`beeuni ship`を実行する場合は、通常どおりブランチとCRのフローに従います。

<a id="set-up-a-cron-trigger"></a>

## cronトリガーを設定する

1. **トリガーを追加**

    ```sh
    beeuni triggers add daily-digest --type cron \
      --cron "0 0 9 * * 1-5" --timezone America/Los_Angeles \
      --prompt "Summarize yesterday's activity and save it as a daily note."
    ```

    `cron`は6フィールドの式です。順に、秒、分、時、日、月、曜日です。トリガーの発火は最大で1分に1回です。`0 */30 * * * *`は30分ごとに実行され、`*/30 * * * * *`（30秒ごと）は拒否されます。このコマンドが編集するのは、ローカルの`beeuni.yaml`のみです。

2. **反映する**

    ```sh
    beeuni ship
    ```

    `beeuni ship`は`beeuni.yaml`をコミットしてプッシュします。プロジェクトのデフォルトブランチに反映されると、スケジュールが有効になります。

3. **実行を確認する**

    ```sh
    beeuni triggers ls
    ```

    一覧には、各トリガーのスラッグ、状態、最終発火時刻が表示されます。スケジュールを待たずに今すぐ発火させるには、`beeuni triggers fire daily-digest`を実行します。このコマンドは実行結果を待ち、失敗した場合は失敗内容を表示してゼロ以外の終了コードで終了します。`--wait <dur>`は待機時間の上限を設定します（デフォルトは`90s`）。

<a id="set-up-a-webhook-trigger"></a>

## Webhookトリガーを設定する

Webhookトリガーにはシークレットが必要です。Beeuniはこれを使って、受信リクエストごとに署名を検証します。

1. **シークレットを追加**

    ```sh
    beeuni secrets set WEBHOOK_SECRET=<a-random-value>
    ```

    シークレットについて詳しくは、[シークレット](/docs/ja/project/secrets)を参照してください。

2. **トリガーを追加**

    ```sh
    beeuni triggers add new-lead --type webhook \
      --secret-env WEBHOOK_SECRET \
      --prompt "A new lead arrived: {{ body.name }} ({{ body.email }}). Add it to the CRM."
    ```

    `--secret-env`には、このトリガーへのリクエストの署名に使うシークレットを指定します。

3. **反映する**

    ```sh
    beeuni ship
    ```

4. <a id="send-it-a-request"></a>

    ### リクエストを送信する

    Beeuniは、プロジェクトIDとトリガーのスラッグからWebhook URLを作成します。

    ```
    POST /v1/webhooks/projects/<project-id>/<slug>
    ```

    外部サービスからこのURLに署名付き`POST`リクエストを送信します。Beeuniは`WEBHOOK_SECRET`を使って署名を検証し、リクエスト本文をプロンプトから参照できる状態でセッションを開始します。正確なヘッダーと形式については、以下の[Webhook署名](#webhook-signature)を参照してください。
デフォルトでは、発火するたびに新しいブランチで新しいセッションが開始されます。トリガーでは、セッションを再利用または固定することもできます。また、Webhookトリガーでは、どのペイロードでセッションを開始するかをフィルタリングできます。以下の[セッション戦略](#session-strategy)と[ペイロードテンプレート](#payload-templating)を参照してください。

<a id="app-event-triggers"></a>

## アプリイベントトリガー

イベントトリガーは、プルリクエストが開かれたとき、メールが届いたとき、課題の状態が変わったときなど、アプリイベントが発生するとエージェントを実行します。Webhook、シークレット、署名を設定する必要はありません。Beeuniが代わりにイベントを購読するため、署名を手動で行う必要はありません。

イベントは**イベントソース**（アプリのイベントを配信するアダプター）から届きます。`GITHUB_PULL_REQUEST_CREATED`などのイベントIDは、そのソースが対応するイベントを識別します。イベントカタログからコネクターに対応するイベントを選択してください。

<a id="set-up-in-the-web-app"></a>

### Webアプリで設定する

1. プロジェクトを開き、**Triggers**、**New trigger**の順に選択します。
2. **App event**を選択します。
3. アプリを選びます。その下でコネクターと共有アカウントを1つ選ぶか、別のアカウントを接続します。別のアカウントを選ばない場合は、**Default**と表示されたアカウントが使われます。
4. イベントを選びます。リポジトリなど、そのイベントの設定フォームが下に表示されます。
5. プロンプトを入力して保存します。

バッジが**Live**になると、トリガーは有効です。それ以外のバッジは、**Activating**、**Needs connection**、**Error**です。これらは、以下の`pending`、`needs_connection`、`error`のステータスに対応します。

<a id="see-every-app-event-trigger"></a>

### すべてのアプリイベントトリガーを表示する

Webアプリで**Triggers**を開き、**App events**を選択します。フィルター行には、件数とともに**All**、**Schedules**、**App events**、**Webhooks**が表示されます。選択内容は`?type=event`としてアドレスに保存されるため、リンクで共有できます。行はアプリごとにグループ化されます。その下の**Apps with events**には、エージェントを起動できるすべてのアプリが表示され、プロジェクトで使用中のアプリが先に並びます。アプリを選ぶと、そのアプリを選択した状態で新しいトリガーのフォームが開きます。

コネクターのウィンドウには**Triggers**タブがあり、そのコネクターで利用できるアプリイベントが表示されます。このタブはイベントがあるアプリにのみ表示されます。

CLIを使う場合:

```bash
beeuni triggers ls --type event                 # every app event trigger, grouped by app
beeuni triggers ls --connector github-work      # the app events on one connector
beeuni triggers ls --type event --json          # the same list as JSON
```

`--type`では`cron`、`webhook`、`monitor`も指定できます。`--connector`と`--type`は組み合わせて使えます。

<a id="set-up-with-the-cli"></a>

### CLIで設定する

エージェントまたは人が、以下の手順を順番に実行します。各手順は、次の手順に必要な情報を出力します。

```sh
beeuni triggers events --apps
beeuni triggers events --app github
beeuni connectors add github --provider composio --app github --apply
beeuni connectors connect github --owner project
beeuni triggers events --connector github
beeuni triggers events --connector github --event GITHUB_PULL_REQUEST_CREATED
beeuni triggers add pr-review --type event \
  --connector github --event GITHUB_PULL_REQUEST_CREATED \
  --config repo=hekmat/api \
  --prompt "Review {{ event.data.html_url }}" --apply
beeuni triggers info pr-review
```

1. `triggers events --apps`はイベントをトリガーできるアプリの一覧を表示します。アプリはイベントソースごとにグループ化されます（`source: composio`）。各アプリにはコネクター（プロファイル）と共有アカウントが表示されます。`STATE`列には`connected`または`needs account`と表示され、コネクターのないアプリは`No connector yet`の下に表示されます。
2. `connectors add`は、アプリにコネクターがない場合に追加します。`CONNECTOR`が設定されている場合は、この手順を省略します。
3. `connectors connect <slug> --owner project`はリンクを出力します。人がリンクを開いて共有アカウントを接続します。メンバーの個人アカウントは対象になりません。2つ目の共有アカウントを追加するには、もう一度実行してから`connectors rename <id> <label>`でラベルを付けます。
4. `triggers events --connector <slug>`はイベントの一覧を表示します。イベントの設定と`{{ event.data.* }}`変数を確認するには、`--event <TYPE>`を追加します。各設定の説明を確認してください。`repo: hekmat/api`のように、1つの設定に完全な名前を指定するイベントもあり、同じ値を`owner`と`repo`に分けて指定すると拒否されます。
5. `triggers add --apply`はトリガーをすぐに作成し、ステータスと次の手順を表示します。設定がない場合や無効な場合は、終了コード2で終了し、すべての問題を一覧表示します。`--apply`を指定しない場合、CLIはブロックをローカルの`beeuni.yaml`に書き込みます。反映するには`beeuni ship`を実行します。
6. `triggers info <slug>`はステータスを表示します。`live`と表示されるまで繰り返します。

`--source <adapter>`はイベントソースを指定するもので、省略可能です。省略すると、トリガーはコネクターのプロバイダーを使います。コネクターはそのソースに属している必要があります。別のプロバイダーのコネクターに`composio`ソースを指定すると、`error`と表示されます。`Connector "<slug>" is a <provider> connector; source "composio" needs a composio connector.`不明なソースを指定すると、`Unknown event source "<x>". Sources: composio.`で拒否されます。`beeuni triggers set <slug> --source <adapter>`で変更でき、`--connector`を変更するとソースがクリアされます。

`--config key=value`は設定ごとに1回繰り返され、設定の型に変換されます。`--config-json '{"limit": 5}'`は型付きの値を渡します。`beeuni triggers set <slug> --config key=value`は現在の設定にマージし、`--config-json`は置き換えます。両方を渡さないでください。イベントトリガーには、`--cron`、`--run-at`、`--timezone`、`--secret-env`、`--run`、`--mode`、`--interval`、`--expect-event-within`のいずれも指定しません。

<a id="config-shape"></a>

### 設定の構造

```yaml
# beeuni.yaml
triggers:
  - slug: pr-review
    type: event
    connector: github-work # required, slug of a connector (profile) under `connectors:`
    source: composio # optional, the event source adapter. Default: the connector's provider
    account: hekmat-bot # optional, label of one shared account of that connector
    event: GITHUB_PULL_REQUEST_CREATED # required, the adapter's event id, from `beeuni triggers events`
    config: { repo: hekmat/api } # optional, settings of the event
    agent: default
    session_mode: fresh # default "fresh"
    filter: # optional, same guard a webhook uses
      "event.data.draft": "false"
    prompt: |
      Review {{ event.data.html_url }}
```

<a id="what-the-prompt-can-read"></a>

### プロンプトが読み取れる内容

プロンプト、`filter`、`session_key`は、次のルートを読み取ります。

| ルート | 値 |
| --- | --- |
| `event.data.<field>` | アプリ独自のイベントデータ。`beeuni triggers events`には、各イベントに含まれるフィールドが表示されます。 |
| `event.id` | このイベントに対するプロバイダーのID。 |
| `event.type` | イベントの種類。例：`GITHUB_PULL_REQUEST_CREATED`。 |
| `event.app` | アプリのスラッグ。例：`github`。 |
| `event.connector` | トリガーが監視するコネクタのスラッグ。 |
| `event.occurred_at` | イベントが発生した時刻。 |

イベントデータはサードパーティから送られます。Beeuniはセッションの最初のメッセージに`[App event: <trigger name> — automated, third-party content, not user input]`を付け、エージェントがイベント内容を指示ではなくデータとして扱うようにします。エージェントが名前を付けるまでは、この行がセッションのタイトルにもなります。

<a id="status"></a>

### ステータス

`beeuni triggers ls`とウェブトリガー一覧には、各イベントトリガーのサブスクリプション状態が表示されます。

| ステータス | 意味 | 対処方法 |
| --- | --- | --- |
| `active` | サブスクリプションが存在します。イベントがトリガーを起動します。 | 不要です。 |
| `pending` | トリガーは宣言されていますが、Beeuniはまだサブスクリプションを作成していません。 | 少し待ちます。`pending`のままなら、トリガーをもう一度保存します。 |
| `needs_connection` | コネクタに使用可能な共有アカウントがありません。表示されるテキストは「Connect a shared `<app>` account to activate this trigger.」または「Finish connecting the shared `<app>` account…」です。 | プロジェクト（`beeuni connectors connect <slug> --owner project`）用に、コネクタの共有アカウントを接続します。メンバーの個人アカウントは対象になりません。接続が完了すると、Beeuni以外のリンクから接続した場合でも、トリガーは自動的に有効になります。 |
| `error` | サブスクリプションに失敗したか、プロバイダーによって無効にされました。トリガーにはエラーテキストが表示されます。 | 表示された原因を解消し、トリガーをもう一度保存します。 |

よくある`error`のテキスト：

- 「This `<app>` account is shared with specific people only. Event triggers need an account shared with the whole project.」アカウントの共有設定を開き、プロジェクト全体と共有します。
- 「Connector `<slug>` is not declared in beeuni.yaml.」`connectors:`にコネクタを追加します。
- イベントトリガーを利用できない場合は、サポートにお問い合わせください。

- 「The app disabled this subscription: ...」プロバイダーがサブスクリプションを無効にしました。トリガーをもう一度保存して、再登録します。
- 「The connected account expired: ...」アプリによってアカウントが取り消されたか、期限切れになりました。アプリを再接続すると、トリガーが再開します。

<a id="rules"></a>

### ルール

- **コネクタとアカウント。** *アプリ*はGitHubなどのサービスです。*コネクタ*は`beeuni.yaml`内のプロファイルで、複数のコネクタが同じアプリを共有できます。*アカウント*はコネクタに接続されたログインアカウントです。トリガーではコネクタを指定します。`account: <label>`を設定しない限り、そのコネクタのデフォルトの共有アカウントが使われます。`account`を設定するのは、コネクタに複数の共有アカウントがある場合だけです。CLIでは`--account <label>`で設定し、`triggers set`の`--default-account`で解除します。共有アカウントに一致するラベルがない場合、`needs_connection`となり、「Connect a shared `<app>` account labelled "`<label>`" on `<connector>`.」と表示されます。
- **使用できるアカウントは1つ。** トリガーに使用できるのは、プロジェクト共有され、プロジェクト全体に公開されているアカウントのみです。トリガーは誰もいない状態でも実行され、イベントデータはセッションプロンプトに入ります。特定の人とのみ共有されているアカウントや個人アカウントは使用できません。
- **各イベントは1回だけ起動。** トリガーはプロバイダーのイベントごとに1回起動します。再配信されたイベントによって2つ目のセッションが開始されることはありません。同じイベントに対する2つのトリガーは、それぞれ1回ずつ起動します。
- **一時停止すると停止。** トリガーまたはプロジェクトが一時停止中の場合、イベントはスキップされます。スキップされたイベントはキューに入りません。
- **編集すると再登録。** `connector`、`source`、`account`、`event`、`config`を変更すると、サブスクリプションが置き換えられます。トリガーを削除すると、サブスクリプションも削除されます。

<a id="monitors"></a>

## モニター

**試験運用中。** モニターは`monitors`機能フラグが有効な場合にのみ実行されます。このフラグはデフォルトでオフです。オフの間、プラットフォームはモニターボックスをプロビジョニングせず、モニターイベントも発生させません。

モニターは、Webhookで通知できず、スケジュールにも適さない対象（ライブログ、キューの深さ、変化するページ、価格など）を監視します。Beeuniはプロジェクトのモニターボックスでコマンドを24時間365日実行します。モニターボックスはプロジェクトごとに1つの永続的なmicroVMで、セッションのサンドボックスと同じ隔離境界とプロジェクトシークレットを使用します。監視は決定論的なコードが行い、コマンドが行を出力したときだけエージェントが起動します。

次の4つのルールで仕様が定まります。

- **イベントになるのは標準出力の行のみです。** 標準エラー出力は診断情報で、モニターのログには表示されますが、トリガーは起動しません。
- 各行はWebhookと同じ経路で、トリガーをちょうど1回起動します。`filter` → プロンプトテンプレート → `session_mode`。
- **モニターが気付かれずに失敗することはありません。** プロセスの終了、再起動回数の上限到達、`expect_event_within`を超える無出力はそれぞれ、プラットフォームが記述したライフサイクルイベントを同じストリーム内で発生させます。
- モニターでは`session_mode`のデフォルトは`reuse`であり、`fresh`ではありません。モニターは設計上、繰り返しイベントを発生させるため、`fresh`ではイベントごとにセッションが1つ作られてしまいます。

1. **モニターを追加**

    ```sh
    beeuni triggers add checkout-errors --type monitor \
      --run "./monitors/checkout-errors.ts" \
      --mode poll --interval 60s --expect-event-within 24h \
      --prompt "Checkout monitor emitted: {{ line }}"
    ```

    `--mode poll`は`--interval`ごとにコマンドを再実行し、終了することを想定します。`--mode stream`はコマンドを1回だけ実行して動作を維持し、ストリームには`--interval`は不要です。どちらの形式も行を出力し、それ以降の処理からは区別できません。モニターでは`cron`、`run_at`、`timezone`、`secret_env`は拒否されます。

2. **リリース**

    ```sh
    beeuni ship
    ```

    デフォルトブランチに反映されると、プラットフォームがモニターボックスを起動します。

3. **動作を確認**

    ```sh
    beeuni triggers ls
    beeuni triggers info checkout-errors
    ```

    `ls`には、cronのスケジュールの代わりにモニターのモードと間隔が表示されます。`info`には`run`、`mode`、`interval`、`expect_event_within`が表示されます。

<a id="monitor-limits"></a>

### モニターの制限

以下の上限はすべてプラットフォームによって適用されます。

| 上限 | 値 |
| --- | --- |
| プロジェクトあたりのモニター数 | 有効なもの10個 |
| ポーリング間隔 | 30秒以上 |
| `expect_event_within` | 5分以上 |
| モニターあたりのイベントレート | 継続時は1時間あたり60件、バースト時は30件。超過するとモニターは10分間抑制され、24時間以内に3回抑制されると無効になります。 |
| 行の長さ | 8 KiB。`truncated: true`マーカーで切り詰められます |
| 再起動回数の上限 | 10分間に5回再起動。その後`restart_budget_exhausted`ライフサイクルイベントが発生し、15分間のバックオフに入ります |
| イベントの保持期間 | 30日 |
| 月間ボックス予算 | デフォルトで75ドル。超過するとボックスが停止し、`budget_exceeded`ライフサイクルイベントが1回発生します。 |

モニターボックスには、永続的なサンドボックスをサポートするプロバイダーが必要です。プロジェクトのプロバイダーが永続的なサンドボックスに対応していない場合、`monitors`フラグは利用不可と報告します。

<a id="config-shape-1"></a>

## 設定の構造

```yaml
# beeuni.yaml
triggers:
  - slug: daily-digest # required, lowercase + dashes, unique per project
    name: Daily digest # optional, defaults to slug
    type: cron # "cron" | "webhook" | "event" | "monitor", required
    agent: beeuni # optional, defaults to "default"
    model: anthropic/claude-sonnet-4-5 # optional, resolves at fire time if unset
    enabled: true # optional, default true
    cron: "0 0 9 * * 1-5" # 6-field expression, mutually exclusive with run_at
    timezone: America/Los_Angeles # IANA name, default UTC
    session_mode: reuse # "fresh" | "reuse" | "pinned" | "keyed", default "fresh"
    filter: # optional, webhook payload guard
      "body.data.direction": "inbound"
    prompt: "Summarize {{ body.text }}" # required, template string
```

モニターでは、スケジュールのフィールドをコマンドと形式に置き換えます。

```yaml
# beeuni.yaml
triggers:
  - slug: checkout-errors
    type: monitor
    run: ./monitors/checkout-errors.ts # required, repo-relative command
    mode: poll # "poll" | "stream", required
    interval: 60s # required on poll, invalid on stream
    expect_event_within: 24h # optional silence watchdog
    agent: oncall
    session_mode: reuse # the monitor default
    filter: # optional, same guard a webhook uses
      "line.severity": "error"
    prompt: "Checkout monitor emitted: {{ line }}"
```

イベントトリガーでは、スケジュールのフィールドを監視対象のアプリとイベントに置き換えます。[アプリのイベントトリガー](#app-event-triggers)を参照してください。

従来の`beeuni.toml`では、異なるコンテナ内で同じフィールドを使用します。[従来のTOML](/docs/ja/project/legacy-toml)を参照してください。

<a id="fields"></a>

## フィールド

| フィールド | 必須 | デフォルト | 備考 |
| --- | --- | --- | --- |
| `slug` | はい | — | `[a-z0-9][a-z0-9_-]{0,127}`。プロジェクトごとに一意です。 |
| `type` | はい | — | `cron`、`webhook`、または`monitor`。 |
| `prompt` | はい | — | テンプレート文字列。セッションの最初のメッセージとして表示されます。 |
| `name` | いいえ | `slug` | 人が読むためのラベルです。 |
| `agent` | いいえ | `default_agent` | `agents:`内のキーを指定する必要があります。省略すると`default_agent`が使われます。文字列`default`は指定しないでください。 |
| `model` | いいえ | 発火時に解決 | ワイヤ形式は`provider/model`です。例：`anthropic/claude-sonnet-4-5`。 |
| `enabled` | いいえ | `true` | `false`の場合、スケジューラーとWebhookレシーバーはこのエントリをスキップします。 |
| `session_mode` | いいえ | `fresh`、またはモニターでは`reuse` | [セッション戦略](#session-strategy)を参照してください。 |
| `session_id` | `pinned`では必須 | — | 再プロンプトする対象のセッションを正確に指定します。 |
| `session_key` | `keyed`では必須 | — | テンプレート文字列。これだけを設定すると`session_mode: keyed`が暗黙的に指定されます。 |
| `filter` | いいえ | — | ドット区切りのパス → 期待する文字列。一致しないWebhook配信は`200`を返し、セッションを開始しません。 |
| `cron` | `type: cron`では`cron`/`run_at`のいずれか | — | 6フィールドの式：秒、分、時、日、月、曜日。発火は60秒に1回までです。これより短い間隔の式は`beeuni validate`とAPIで拒否されます。 |
| `run_at` | `type: cron`では`cron`/`run_at`のいずれか | — | ISO-8601タイムスタンプ。1回発火した後は休止状態になります。 |
| `timezone` | いいえ、cronのみ | `UTC` | IANA名。 |
| `secret_env` | 必須、Webhookのみ | — | 署名キーを保持するプロジェクトシークレットの名前。シークレットでは、`connector`コンシューマーを使用する`broker`配信を使う必要があります。 |
| `run` | 必須、モニターのみ | — | 標準出力の各行がイベントとなる、リポジトリ相対のコマンド。1行あたり最大1024文字です。 |
| `mode` | 必須、モニターのみ | — | `poll`は`interval`ごとに`run`を再実行します。`stream`は1回実行し、そのまま稼働させます。 |
| `interval` | `mode: poll`では必須 | — | 期間リテラル（`30s`、`5m`、`24h`、`7d`）。最小値は`30s`です。`mode: stream`では無効です。 |
| `expect_event_within` | いいえ、モニターのみ | — | 期間リテラル。最小値は`5m`です。この時間を超えて無音状態が続くと、ライフサイクルイベントが発火します。 |

cronトリガーでは`cron`または`run_at`が必要です。両方は指定できません。`secret_env`のないWebhookトリガーは拒否されます — 認証なしのWebhookはありません。モニターでは`run`と`mode`が必要です。また、`cron`、`run_at`、`timezone`、`secret_env`は無条件で拒否されます — モニターランナーが読み取らないスケジュールを記載したマニフェストは虚偽です。

Webhookトリガーを作成または更新する前に、署名シークレットを設定してください。

```bash
beeuni secrets set WEBHOOK_SECRET=-
beeuni secrets delivery WEBHOOK_SECRET broker --consumer connector
```

値は最初のコマンドに標準入力で渡してください。シークレットで以前sandbox配信を使っていた場合は、配信方式を変更した後にローテーションしてください。既存のsandboxに以前の値が残っている可能性があります。

<a id="session-strategy"></a>

## セッション戦略

`session_mode`は、発火時にどのセッションへ再プロンプトするかを制御します。Beeuniは以下のモードを順に試し、各段階で失敗すると次に進みます。

1. **`pinned`** — 指定された`session_id`に再プロンプトします。そのセッションが存在しないか、失敗した場合は次に進みます。
2. **`keyed`** — ペイロードに対して`session_key`をレンダリングし、その完全一致キーが以前スタンプされた、直近の失敗していないセッションを検索します。キーが空になるか、一致するセッションがない場合は、新しいセッションに進みます。別のキーのセッションに切り替わることはありません。
3. **`reuse`** — このトリガーが以前作成した、直近の失敗していないセッションに再プロンプトします。固定セッションを指定したトリガーも、さらに先へ進む前にここへフォールバックします。
4. **`fresh`** — 新しいsandboxとブランチを作成します。これがデフォルトであり、すべてのモードにおける最終フォールバックです。新しいセッションは、今後の`reuse`および`keyed`の発火に使う、このトリガーのセッションになります。

`reuse`または`keyed`セッションの履歴が、自動圧縮後もモデルの上限に収まらない場合、そのセッションは終了扱いになります。次回の発火時に新しいセッションが開始されます。それ以外の失敗では、次回の発火に備えてセッションが保持されます。

頻繁に発火するcronトリガーで、増え続ける履歴を毎回送り直したくない場合は、暦の区切りでセッションを切り替えられます。`session_key: "merge-{{ cron.scheduled_date }}"`はUTC日ごとに1つのシリアル化されたセッションを保持し、`{{ cron.scheduled_hour }}`はUTC時間ごとに1つ保持します。

<a id="failed-runs"></a>

## 実行の失敗

トリガーが開始した実行がエラーで終了すると、Beeuniはトリガーにその失敗を記録します。

- `last_status`が`failed`になり、`last_error`に理由が記録されます。例：`Out of credits: Payment Required: Insufficient credits.`
- Scheduleページではトリガーに「前回の実行は完了しませんでした」と表示され、パネルに理由が表示されます。
- 最初の実行が失敗すると、アカウント所有者にモバイルプッシュ通知が1回送信されます。その後の実行が失敗しても、理由が更新されるだけで、追加の通知は送信されません。
- 発火し続ける限り、トリガーは`failed`のままです。新たに発火したからといって、その実行が正常に動作したことにはなりません。

次の実行が完了すると、失敗状態は解除されます。自分で停止した実行、サブエージェント、再試行されたモデル呼び出しは、実行結果には含まれません。セッションを作成できないなど、実行開始前に発火が失敗した場合は、次に発火が成功した時点で失敗状態が解除されます。

<a id="session-access"></a>

## セッションへのアクセス

トリガーが作成するセッションには、デフォルトで非公開ポリシーが適用されます。所有者はトリガーエージェントのサービスアカウントです。エージェントのIDは`service_account`プリンシパルなので、ほかのプリンシパルと同様に、セッションを所有して割り当てを保持できます。プロジェクトマネージャーはいつでもセッションを開けます。アカウント所有者とアカウント管理者はすべてのプロジェクトでマネージャー相当のアクセス権を持つため、同様にセッションを開けます。トリガーを設定した人や手動で発火させた人は、これらのロールのいずれかを持たない限り、その操作によってアクセス権を得ることはありません。

トリガー設定では、次の3つのポリシーを選択できます。

- **トリガーエージェントとプロジェクトマネージャー** — 一般のプロジェクトメンバーはセッションを開けません。
- **選択したチームメイト** — トリガーエージェント、プロジェクトマネージャー、および選択したプロジェクトメンバーまたはアカウントグループが対象です。
- **プロジェクト全体** — すべてのプロジェクトメンバーが対象です。

このポリシーは、ロールモデルに追加されるリソース単位の可視性設定であり、ロールではありません。1つのトリガーのセッションを誰が開けるかを決定します。ロール判定で拒否される権限が付与されることはありません。[アカウントとアクセス](/docs/ja/accounts#per-feature-access-settings)を参照してください。

アクセス ポリシーは、アカウント内のランタイム状態です。プリンシパルIDはアカウントごとに異なるため、ポータブルな`beeuni.yaml`マニフェストには含まれません。設定にはダッシュボードを使用してください。
このポリシーのみを更新しても、Gitコミットは作成されません。ポリシーを保存すると、そのトリガーが以前に作成したセッションにも適用されます。

固定セッションは、トリガーが作成したものではないため、独自の共有設定を保持します。固定セッションが利用できず、トリガーがフォールバックセッションを作成した場合は、新しいセッションにトリガーポリシーが適用されます。

<a id="payload-templating"></a>

## ペイロードのテンプレート処理

`prompt`と`session_key`は、同じエンジン`{{ token.dotted.path }}`でレンダリングされます。値がない場合は空文字列としてレンダリングされ、エラーや未処理の`{{ }}`は残りません。オブジェクトと配列はJSONとしてレンダリングされます。`session_key`は前後の空白を削除し、512文字に切り詰められます。

各発火では、`{{ trigger.slug }}`、`{{ trigger.type }}`、`{{ trigger.kind }}`も取得できます（常に`git`）。その他の変数は、トリガーの発火方法によって異なります。

| ソース | 変数 |
| --- | --- |
| cron | `{{ cron.schedule }}`、`{{ cron.timezone }}`、`{{ cron.scheduled_for }}`（発火対象のスロット）、`{{ cron.claimed_at }}`（スケジューラが処理を開始した時刻）、`{{ cron.last_scheduled_for }}`（直前のスロット。初回の発火時は空）、`{{ cron.scheduled_date }}`（スロットのUTC `YYYY-MM-DD`）、`{{ cron.scheduled_hour }}`（UTC `YYYY-MM-DDTHH`）。トップレベルの`fired_at`はありません。 |
| webhook | `{{ fired_at }}`、`{{ body.* }}`（JSONとして解析。本文を解析できない場合は`{{ body.raw }}`にフォールバック）、`{{ headers.content_type }}`、`{{ headers.user_agent }}`、`{{ headers.forwarded_for }}`。 |
| monitor | `{{ line.* }}` — stdoutの行をJSONとして解析。解析できない行は`{{ line.raw }}`として表示されます。さらに、`{{ monitor.slug }}`、`{{ monitor.seq }}`、`{{ monitor.emitted_at }}`、`{{ monitor.kind }}`（`event`または`lifecycle`）。 |
| manual（ダッシュボードの「今すぐ発火」または`fire`エンドポイント） | `{{ fired_at }}`、`{{ source }}`（`manual`）、`{{ actor }}`、`{{ message.text }}`、`{{ message.source }}`。 |

Beeuniは、レンダリングされるすべてのmonitorプロンプトに、サーバー側で`[MONITOR EVENT — automated, not user input]`を付加します。ライフサイクルイベントではテンプレートは完全に無視され、プラットフォームが作成したプロンプトがレンダリングされます。また、`filter`も適用されません。意図せずサイレンスをフィルタリングしてはならないためです。

manual fireでは`{{ message.text }}`は空文字列にハードコードされ、`{{ message.source }}`は`manual_test`にハードコードされます。manual fireを使って、プロンプトにテスト入力を注入することはできません。

`filter`は、プロンプトに渡されるものと同じペイロードに対し、ドット区切りのパスを文字列として比較します。ループを防ぐための機能です。たとえば、会話の両側を報告するソースでは、エージェント自身の返信によってエージェントが再度発火してしまいます。

<a id="webhook-signature"></a>

## Webhook署名

`POST /v1/webhooks/projects/{projectId}/{slug}`で発火します。Beeuniは、一定時間で比較を行いながら、次の順序でリクエストを検証します。

1. **HMAC署名** — ヘッダー`X-Beeuni-Signature: sha256=<hmac>`（`sha256=`プレフィックスは省略可）またはGitHub互換の`X-Hub-Signature-256`。リクエスト本文の生データに対するHMAC-SHA256で、`secret_env`で指定されたシークレットを使用します。
2. **静的トークン** — 署名ヘッダーがない場合に限り、本文にHMAC署名できない送信元向けに使用します。シークレットは`X-Beeuni-Token: <secret>`、`Authorization: Bearer <secret>`、または`Authorization: Basic <base64(user:secret)>`として送信します（パスワード部分がトークンです）。

`beeuni triggers info <slug>`はこの方式を出力します。ヘッダー、アルゴリズム、`$SECRET`のプレースホルダーで署名したリクエスト例が含まれます。`--json`ではこれを`webhook_signing`として渡します。ダッシュボードのトリガーシートにも同じリクエスト例が表示されます。

| ステータス | 意味 |
| --- | --- |
| 202 | 署名またはトークンが有効です。本文は`{ status: "fired" \| "queued" \| "deduped", session_id, ... }`です。 |
| 200 | 有効ですが、スキップされました。プロジェクトが一時停止中か、配信が`filter`に一致しませんでした。 |
| 400 | URLのプロジェクトIDまたはスラッグが不正です。 |
| 401 | 署名とトークンの両方がないか、どちらも正しくありません。 |
| 404 | トリガーが見つからない、無効になっている、webhookではない、またはプロジェクトがアクティブではありません。 |
| 409 | 署名シークレットがない、無効、利用不可、または`connector`コンシューマーの認証に使用できません。レスポンスには`webhook_secret_*`コードと対処方法が含まれます。 |
| 500 | 認証には成功しましたが、セッションの発火に失敗しました。 |

<a id="endpoints"></a>

## エンドポイント

| メソッド + パス | 必要なもの | 備考 |
| --- | --- | --- |
| `GET /v1/projects/{projectId}/triggers` | `project.trigger.read` | トリガー、実行時の状態（`last_fired_at`、`last_status`、`last_error`、`last_attempt_at`）、マニフェストの解析エラーを一覧表示します。不正なエントリは`errors[]`に表示されます。他のトリガーには影響しません。 |
| `POST /v1/projects/{projectId}/triggers` | `project.trigger.create` | トリガーを作成します。マニフェストに直接コミットします。 |
| `PATCH /v1/projects/{projectId}/triggers/{slug}` | `project.trigger.update` | 現在のエントリにマージする部分更新です。 |
| `DELETE /v1/projects/{projectId}/triggers/{slug}` | `project.trigger.delete` | トリガーの実行時状態もクリアします。 |
| `PATCH /v1/projects/{projectId}/triggers/activation` | `project.trigger.update` | 本文`{ paused: boolean }`。[一時停止と再開](#pause-and-resume)を参照してください。 |
| `POST /v1/projects/{projectId}/triggers/{slug}/fire` | `project.trigger.fire` | 手動で発火します。組み込みのプロジェクト`member`ロールに`project.trigger.fire`があるため、一般メンバーもトリガーを発火できます。 |
| `POST /v1/webhooks/projects/{projectId}/{slug}` | 署名またはトークン | webhookシークレットで保護された公開URLです。 |

<a id="pause-and-resume"></a>

## 一時停止と再開

プロジェクトレベルのスイッチを使うと、各トリガー固有の`enabled`フィールドに関係なく、プロジェクト内のすべてのトリガーを一度に停止できます。一時停止中はスケジューラがプロジェクトをスキップし、受信したwebhookは`{ status: "skipped" }`を伴う`200`を返します。セッションは発火しません。手動での発火は引き続き可能です。同じリポジトリを2つのコントロールプレーン（たとえば開発環境と本番環境）で実行し、cronが二重に発火しないようにする場合に使います。CLI: `beeuni triggers pause`および`beeuni triggers resume`。コマンドグループ`beeuni triggers`の全容については[CLI](/docs/ja/cli)を参照してください。

<a id="limits-and-reliability"></a>

## 制限と信頼性

- スケジューラは約1秒ごとにポーリングします（デフォルトは1,000 ms。`BEEUNI_TRIGGER_SCHEDULER_INTERVAL_MS`で設定可能）。cronの実行はスケジュール上の時刻ちょうどには開始されません。トリガーごとに最大で周期の4分の1、最長30分の固定オフセットが設定されるため、同じ分に設定された多数のトリガーが一斉に開始することはありません。トリガーの詳細と`GET /triggers`（`next_fire_at`）に、次回の実行開始時刻が表示されます。
- 発火に失敗すると、エラーとともにトリガーの最終ステータスがfailedに設定されます。cron、manual、webhook、app event、monitorのすべてのソースが対象です。次回の発火が成功すると、失敗ステータスはクリアされます。失敗した実行は、[失敗した実行](#failed-runs)の説明に従ってクリアされます。
- デフォルトでは、各プロジェクトで同時にプロビジョニングできるトリガー経由のセッションは3つまでです。上限を超えた発火は失敗せずに`queued`（`202`）を返し、いずれかのプロビジョニングが完了すると実行されます。同時に実行できるセッション数に上限はありません。
- manualまたはwebhookによる発火のタイムアウトは45秒です。マニフェストの読み込みは30秒でタイムアウトします。
- cronによる発火は、実行予定のスケジュールスロットをキーにします。そのため、タイムアウトしたものの実際には到達していた発火が、再試行時に重複することはありません。
- webhookの配信はイベントごとに1回だけ実行されます。BeeuniはイベントIDを`X-Beeuni-Delivery-Id`、`X-GitHub-Delivery`、`X-Gitlab-Event-UUID`、`Linear-Delivery`、`webhook-id`、`svix-id`の順に読み取ります。同じIDで再試行すると`202 { status: "deduped" }`が返され、何も実行されません。`X-Request-Id`はイベントIDではありません。イベントIDがない場合、同一の本文と署名は10分以内であれば同じイベントとして扱われます。それ以降は再度実行されるため、デプロイごとに同じ静的な本文を送信するジョブは、デプロイのたびに実行されます。イベントごとに新しい値を指定した`X-Beeuni-Delivery-Id`を送信してください（リクエスト例では`$(uuidgen)`を使用しています）。
- 前回の試行が恒久的に失敗した（たとえば、アカウントのクレジットが不足していた）場合や、セッションが削除されていた場合、同じIDで再配信すると再度実行されます。重複配信によって`last_fired_at`が変わることはありません。
