browser-flow は、1つの信念に基づいて作られた、ポータブルで決定論的なワークフローエンジンです。プロセスの定義と、それが同じように実行されるという保証こそがプロダクトである、という信念です。それ以外のもの(ストレージ、トランスポート、UI、ホスティング)はすべてアダプターです。このページは全体像を示す地図です。エディター上のフローを再現可能な実行へ運ぶ3段階のパイプライン、実行時コンポーネントの小さな登場人物、それらの組み合わせ方、そして全体の整合性を保つパッケージ構成を説明します。

パイプライン: 作成、コンパイル、実行

すべては3つの段階を通ります。TypeScript SDK または YAML で Flow を一度作成すると、コンパイラーがそれを sha256 digest を持つ1つの正規化された FlowSpec に下げ、Rider がその FlowSpec をどこでも同一に実行します。2つのオーサリング面は対等な存在です(原則 T1)。どちらも同じ FlowSpec にコンパイルされ、その FlowSpec は YAML にラウンドトリップできます。
段階同士の境界になるのが FlowSpec の digest です。digest は、正規化された Flow その Flow が必要とするすべての Tool について解決済みの contract digest を閉じ込めます。そのため、digest と記録済み入力を固定すれば、どのマシンでも、オフラインでも実行を再現できます(原則 T3)。digest がバージョンそのものです。

作成

Flow を一度書きます。SDK と YAML は対等な存在で、どちらも1つの IR に下げられます。

コンパイル

パース、解決、型チェック、正規化を行い、安定した digest へハッシュします。

実行

Rider が DAG をスケジュールし、Tool を呼び出し、Journal を書き込みます。

実行時の登場人物

実行時に働く名前付きコンポーネントは3つで、それらをホストする Launcher レイヤーがあります。それぞれに詳説ページがあります。
コンポーネントそれは何か1つの役割
Rider実行エンジンFlowSpec + 入力 + 注入された capability を受け取り、Journal を持つ Run を生成します。何かを実行する唯一のコンポーネントであり、モデルは実行しません
Launcher薄いホスト環境に応じた capability を構築して Rider を呼び出します。browserflow run(CLI)、HTTPルート(REST)、MCPツール、cron tick、CI step などです。
RegistryPack が置かれる場所Pack を公開、バージョン管理、解決します。他の registry へ複製し、digest で検証します。
Rider は実行時にモデルを実行しません。LLM に制御フローを決めさせるコードパスはありません。AI は Flow の作成を支援することがありますが、Flow が実行時に何をするかは決して決定しません。Step からモデルを呼び出すことはできますが、それは記録済みの入力と出力を持つ、通常の宣言された model Tool としてだけです。

Rider — 唯一の実行コンポーネント

Rider は FlowSpec、具体的な入力、注入された capability の集合を受け取り、Run を生成します。必要なものはすべて RunDeps を通じて注入されます(原則 T4)。グローバルを読まず、クラウド SDK をインポートせず、時計、RNG、ネットワーク、ファイルシステム、シークレットにはこれらのインターフェースを通じてだけ触れます。
export interface Rider {
  run(spec: FlowSpec, inputs: JsonValue, deps: RunDeps): Promise<Run>;
  resume(runId: string, deps: RunDeps): Promise<Run>;
}

export interface RunDeps {
  store: Store;        // Run + Journal を永続化する(SQLite がデフォルト)
  journal: Journal;    // 追記専用のイベント sink
  tools: ToolHost;     // registry から ToolContract を解決して呼び出す
  clock: Clock;        // now(): string — リプレイ時は決定論的
  random: Random;      // next(): number — seed 済みで、リプレイ時は決定論的
  secrets: Secrets;    // resolve(ref): string — `secrets` capability を持つ tool のみ
  env: EnvView;        // allow-list された環境値
  logger: Logger;      // 構造化され、シークレットをマスクする
  signal?: AbortSignal;// キャンセル
}
実装を差し替えれば、同じ FlowSpec が別の場所で変更なしに実行されます。非決定性はこれらの注入された capability に閉じ込められます。それにより、Run は分析可能、サンドボックス化可能、リプレイ可能になります。スケジューリング、Step ライフサイクル、リトライ、再開、リプレイについては 実行と再現性 を参照してください。

Launcher — 薄いホスト

Launcher は engine.run() を包む薄いシェルです(原則 T5)。環境に適した capability を構築して Rider に渡します。同じ FlowSpec は、どの Launcher の下でも同じ結果で実行されます。ポータビリティ適合スイートは、まさにこの不変性を検証します。Launcher を参照してください。

Registry — Pack が置かれる場所

ローカル Registry はディレクトリで、チーム Registry はコントロールプレーンです。Registry は互いに複製します(push、pull、scheduled)。各転送は digest で検証されるため、ある環境で作成された Pack は、別の環境でも固定され、完全な状態で現れます。Tool はコンパイル時に Pack から解決され、実行時に Pack から呼び出されます。Tool と Pack を参照してください。

すべての組み合わせ方

標準の実行例で各部品を動かしてみます。daily-price-watch Flow です。browser tool search-price@1.2.0 が価格を調べ、transform がそれを整形し、条件付きの http.post が、価格が下がったときだけシークレット webhook に通知します。
1

Pack が Tool と Flow を束ねる

search-price@1.2.0 browser tool、http.post、そして daily-price-watch Flow は、バージョン付きの content-addressed な Pack の中で一緒に存在します。これがデプロイ可能な「SDK」です。
2

FlowSpec + digest にコンパイルする

Flow の lookup Step と notify Step はそれらの Tool を参照します。コンパイラーは各 Tool を固定された ref とその contract_digest に解決し、Flow を正規化して、sha256 digest を持つ1つの FlowSpec を出力します。
3

Launcher が Rider の下で実行する

Launcher は環境に応じた capability を構築し、FlowSpec と入力を渡して Rider を呼び出します。
4

Rider が capability を注入し、Journal を書き込む

Rider は各 Tool が宣言した capability だけを、過不足なく注入し、DAG をスケジュールし、Step イベントと最終出力を含む追記専用 Journal を書き込みます。モデルは呼び出されません。
シークレット webhook URL は、参照として以外、この図には決して含まれません。secret: true とマークされた入力は FlowSpec、Journal、ログ、公開された Pack のいずれにも入りません。その値が実体化するのは、secrets capability を宣言した http.post Step の内部だけであり、実行時だけです(原則 T8)。正確に名前付けされたすべての名詞については ドメインモデル を、境界と taint rule については シークレット を参照してください。

パッケージ構成: 矢印は core から外へ向かない

同じ構成上の規律はコードベースにも現れます。エンジン、つまり FlowSpec、Tool contract、コンパイラー、Rider、Journal は core にあり、自身のインターフェースだけに依存します。すべての launcher と backend はそれらのインターフェースの実装です。エンジンがクラウド SDK をインポートすることはありません
packages/
  core/          FlowSpec, Tool contract, compiler, Rider, Journal  (エンジン)
  sdk/           fluent builder (method chaining) → FlowSpec
  yaml/          YAML parser + canonical emitter (ラウンドトリップ)
  std/           standard tool packs: http, transform, shell, fs, control, model
  browser/       browser tool: record (オーサリング時) + replay (実行時)
  cli/           `browserflow` binary (上記を束ねる composition root)
  registry/      pack format, publish/resolve, replication, browserflow.lock
  web-api/       Hono on Bun control-plane API (registry, runs, schedules, secrets)
apps/
  web/           Next.js run inspector + canvas/YAML editor
ルールは一文です。core は自分自身のインターフェース以外に何も依存しません。sdkyamlcore に依存します。それ以外はすべて内側に依存します。依存の矢印が core から外へ向かうことはありません。 これにより、同じ FlowSpec をどこでも実行し、再現できます。
それがポータビリティを支える仕組みだからです。core は時計、RNG、ネットワーク、ファイルシステム、シークレット、store、journal に、注入されたインターフェース(StoreClockRandomSecretsLoggerToolHostJournal)を通じてだけ到達します。そのため、Flow を実行する場所は、エンジンに触れるのではなく、実装を差し替えることで変えられます。クラウドはアダプターであり、依存関係ではありません(原則 T4)。
同じレイヤリングから来ます。Rider には、制御フローを決めるためにモデルを呼び出すコードパスがありません。また、core はそのような経路を追加し得るものを何もインポートしません。model Tool が存在する場合でも、それは記録済みの入力と出力を持つ通常の宣言された Tool です。オーサリング時の利便性であり、実行時の意思決定者ではありません。
Flow は composite Tool として再公開でき、別の Flow の中で単一の Step として呼び出せます。ネストは効果を隠しません。composite が宣言する capability は、内側の Flow が呼び出すすべての Tool の和集合です。また、シークレット入力は参照として渡され、最も内側の secrets capability を持つ Tool でだけ解決されます。ドメインモデル を参照してください。

関連

ドメインモデル

Tool、Step、Flow、FlowSpec、Run、Pack、Registry という7つの名詞を、それぞれ一度だけ定義します。

FlowSpec & IR

正規化、digest アルゴリズム、参照がグラフの辺になる仕組み。

実行

スケジューリング、Step ライフサイクル、リトライ、冪等性、再開、リプレイ。

Launcher

CLI、REST、MCP、cron、CI にまたがって Rider を変更せずに実行する薄いホスト。