browser-flow のすべての名詞には定義が1つだけあります。このページは、残りのドキュメントが前提にするグロッサリです。データ型は Zod スキーマ(このプロジェクトの標準形式)として示します。Foo は z.infer<typeof FooSchema> を表します。
0. 共有スキーマ語彙
4つの小さなスキーマが全体で参照されます。ここで一度だけ定義します。
import { z } from "zod";
/**
* 小さな JSON Schema(Draft 2020-12)のサブセット: tool/flow の I/O slot が
* 取り得る型。スカラー、配列、オブジェクトを扱えるため、入力は単なる
* スカラーに限定されません(tool はオブジェクト body や配列などを宣言できます)。
*/
export type JsonSchema =
| { type: "string" | "number" | "boolean" | "null" }
| { type: "array"; items: JsonSchema }
| {
type: "object";
properties: Record<string, JsonSchema>;
required?: string[];
additionalProperties?: boolean;
};
export const JsonSchemaSchema: z.ZodType<JsonSchema> = z.lazy(() =>
z.union([
z.object({ type: z.enum(["string", "number", "boolean", "null"]) }).strict(),
z.object({ type: z.literal("array"), items: JsonSchemaSchema }).strict(),
z
.object({
type: z.literal("object"),
properties: z.record(z.string(), JsonSchemaSchema),
required: z.array(z.string()).optional(),
additionalProperties: z.boolean().optional(),
})
.strict(),
]),
);
/**
* 式: `${{ … }}` の内側(またはリテラル)に現れるソーステキスト。
* コンパイラが生の文字列と、解析・検証済みの式を区別できるよう
* branded されています。Grammar: [02 — Authoring](02-authoring.md#expression-language)。
*/
export const ExprSchema = z.string().brand<"Expr">();
export type Expr = z.infer<typeof ExprSchema>;
/** Step ごとの retry policy。デフォルトは tool の `kind` から導かれます([04 §4](04-execution.md#4-retries-timeouts-idempotency))。 */
export const RetryPolicySchema = z
.object({
max: z.number().int().nonnegative(),
backoff: z.enum(["exp", "fixed"]),
base_ms: z.number().int().positive(),
jitter: z.boolean(),
})
.strict();
export type RetryPolicy = z.infer<typeof RetryPolicySchema>;
/**
* pin された tool dependency: 正確な `name@version` ref と、解決済みの
* contract digest。これにより flow の identity は version label だけでなく
* tool の *behavior* まで閉じます([03 §5](03-ir.md#5-the-digest))。
*/
export const RequireSchema = z
.object({ ref: z.string().min(1), contract_digest: z.string().min(1) })
.strict();
export type Require = z.infer<typeof RequireSchema>;
1. 7つの名詞
| 名詞 | ひと言で | 作成者 | 存在する場所 |
|---|
| Tool | 型付き I/O contract を持つ決定論的な作業単位 | pack author | Pack |
| Step | Flow 内での Tool の1回の呼び出しと、その配線 | flow author | Flow |
| Flow | Steps の DAG、つまりワークフロー | flow author(SDK または YAML) | Pack / ファイル |
| FlowSpec | Flow がコンパイルされる、正規化された content-addressed IR | コンパイラ | registry / .flow.json |
| Run | FlowSpec の1回の実行。Journal を伴う | Rider(engine) | Store |
| Pack | Tools + Flows の、バージョン付きかつ content-addressed な bundle(“SDK”) | pack author | Registry |
| Registry | Packs が公開、バージョン管理、複製される場所 | サービス | self-host / cloud |
さらに2つの役割があります。Rider(実行エンジン)と、Rider を駆動するホストである Launcher(CLI、REST、MCP、cron など)です。
Tool は、Step が呼び出せる最小の再利用可能な単位です。Tool は contract によって定義されます。contract には、名前、version、型付き input、型付き output、宣言された capabilities、そして run 関数が含まれます。重要なのは、Tool が 入力と記録済み effect が与えられれば決定論的であることです。Tool は判断せず、実行します。
contract はこのプロジェクト内で一から定義されています。input は、型付きで、任意に secret にできる slot として宣言されます。
import { z } from "zod";
/**
* tool 上で宣言される1つの input slot。tool の caller と body の間の contract です。
* tool は input を名前と `schema` で読み取り、`secret: true` の slot は run boundary
* (§7)を決して出ません。`schema` は slot の JSON-Schema-subset 型です。
* スカラー **だけでなく** object/array も扱えるため、tool はたとえば object request body
* を宣言できます。YAML surface は `type: string|number|boolean` という shorthand を受け付け、
* canonicalization 中に `{ "type": … }` へ展開します
* ([02 §3](02-authoring.md#3-the-same-flow-three-ways))。
*/
export const InputDeclSchema = z
.object({
name: z.string().min(1),
schema: JsonSchemaSchema,
secret: z.boolean(),
})
.strict();
export type InputDecl = z.infer<typeof InputDeclSchema>;
/**
* tool の interface contract。`inputs`/`output` は型付けされるため、配線は
* author time にチェックされ、run time に検証されます。`capabilities` は tool が
* 使用を許可された effect を列挙します(T2: permission による effects)。
* Rider はそれらだけを注入し、それ以上は注入しません。`provenance.source_digest` は
* tool body を作成したもの(例: 記録済み browser session)の content-addressed
* fingerprint なので、tool は pin でき、監査できます。
*/
export const ToolContractSchema = z
.object({
schema_version: z.literal("1.0"),
name: z.string().min(1),
version: z.string().min(1), // semver
kind: z.enum(["browser", "http", "shell", "transform", "code", "model", "composite"]),
description: z.string().min(1),
inputs: z.array(InputDeclSchema),
output: JsonSchemaSchema, // 小さな JSON Schema(Draft 2020-12)のサブセット
capabilities: z.array(z.enum(["net", "fs", "browser", "clock", "random", "env", "spawn", "secrets"])),
provenance: z
.object({ source_digest: z.string().optional(), pack: z.string().optional() })
.strict(),
})
.strict();
export type ToolContract = z.infer<typeof ToolContractSchema>;
Tool の kind は標準 Pack に含まれます(Tools & Packs を参照)。
一度記録され、モデルなしでリプレイされる決定論的な Playwright オートメーション。これが代表的な tool kind です。
HTTP リクエストを発行する tool kind です。
shell コマンドを実行する tool kind です。
純粋な JSON→JSON 関数です。
capability によって sandbox される escape hatch です。
宣言され、記録された LLM 呼び出しです。author-time の利便性であり、runtime decision ではありません。
Tool として再公開された Flow です。flow を入れ子にする仕組みです。
composite tool は内側の flow から contract を継承します。その際、入れ子構造を正直に保つ2つのルールがあります。宣言される capabilities は、内側の flow が呼び出すすべての tool の capabilities の union です。入れ子は effect を隠しません。また、外側の composite の secret: true input は、内側の flow で対応する secret input への reference として渡され、最も内側の secrets capability を持つ tool でのみ解決されます。そのため、どの深さの入れ子でも boundary(§7)は維持されます。
3. Step — 1回の呼び出し
Step は Tool を Flow 内の位置に結び付けます。呼び出しに id を与え、Flow のデータを Tool の inputs(with)へ map し、依存関係を宣言し、必要に応じて retry / timeout / conditional policy を付与します。
export const StepSchema = z
.object({
id: z.string().min(1), // flow 内で一意。編集をまたいで stable
use: z.string().min(1), // "browser:search-price@1.2.0" | "http.post" | "flow:sub@2"
with: z.record(z.string(), ExprSchema).optional(), // input bindings(expression の場合があります)
needs: z.array(z.string()).optional(), // explicit deps。なければ refs から推論
when: ExprSchema.optional(), // これが truthy に評価された場合のみ run
retry: RetryPolicySchema.optional(),
timeout_ms: z.number().int().positive().optional(),
foreach: ExprSchema.optional(), // array に fan out(map)
output_as: z.string().optional(), // downstream references 用の alias
})
.strict();
export type Step = z.infer<typeof StepSchema>;
データは expression 内の references を通じて Step 間を流れます。
${{ steps.lookup.output.price }}
${{ input.query }}
${{ env.REGION }}
コンパイラはこれらの references を読んで依存関係の edge を構築するため、needs は通常、手で書かれずに推論されます。expression language については Authoring を、references が graph edges になる仕組みについては FlowSpec IR を参照してください。
4. Flow — ワークフロー
Flow は、型付き input contract と型付き output projection を持つ、名前付きの Steps の DAG です。control flow は宣言的に表現されます。
- branch — condition によって steps の sub-list を選ぶ(
when field、または fluent な .branch())。
- foreach — step(または sub-flow)を array に対して fan out し、結果を集める。
- parallel — edge のない独立した steps は、デフォルトで同時に実行される。concurrency は launcher によって制限される。
- nesting — Flow は
composite Tool として公開し、別の Flow 内の1つの Step として呼び出せるため、大きな process も読みやすく保てる。
Flow は作成対象そのものです。Flow は FlowSpec にコンパイルされます。
5. FlowSpec — 正規 IR
FlowSpec は、browser-flow において Trail が browser-flow に対して持つ関係と同じもの、つまり 単一の中間表現 です。どちらの authoring surface も FlowSpec を生成し、FlowSpec は YAML へ round-trip できます。FlowSpec は canonicalized(key の sort、expression の normalize、tool version の resolve)され、sha256 digest に hashed されます。この digest が、cache、pinning、replication における flow の identity です。
export const FlowSpecSchema = z
.object({
schema_version: z.literal("1.0"),
name: z.string().min(1),
description: z.string().optional(),
inputs: z.array(InputDeclSchema),
output: ExprSchema.optional(),
steps: z.array(StepSchema),
requires: z.array(RequireSchema), // pinned tool ref + resolved contract digest
digest: z.string().optional(), // canonical form の sha256(compiler が埋める)
})
.strict();
export type FlowSpec = z.infer<typeof FlowSpecSchema>;
canonicalization rules、digest algorithm、graph derivation の詳しい説明は FlowSpec IR にあります。
6. Run — 1回の実行
Run は、Rider が具体的な inputs に対して FlowSpec を実行することです。Run は Journal を生成します。Journal は、step lifecycle events(started、output、failed、retried、skipped)に加え、解決済み inputs と最終 output を含む、append-only で順序付きの log です。
export const RunSchema = z
.object({
id: z.string().min(1), // uuidv4
flow_digest: z.string().min(1), // 実行された FlowSpec を正確に示す
status: z.enum(["pending", "running", "succeeded", "failed", "canceled"]),
started_at: z.string(), // ISO-8601
finished_at: z.string().optional(),
journal: z.array(JournalEntrySchema),
output: z.unknown().optional(),
})
.strict();
export type Run = z.infer<typeof RunSchema>;
Journal によって、Run は 再開可能(完了済み steps を再実行せず log から replay する)かつ inspectable(CLI または UI で run を time-travel する)になります。Execution を参照してください。
7. Secrets — 境界
Secrets は、flow 全体にまたがる value ではなく boundary です。secret: true の input slot は次の性質を持ちます。
- FlowSpec、Journal、log line、公開済み Pack のいずれにも現れません。
secrets capability を持つ Tool が注入された Secrets provider を通じて解決する瞬間まで、reference({ slot, refers_to })としてのみ運ばれます。
- そうでなければ leak し得るすべての場所で
maskInputs によって masked されます。
flow author は ${{ secret.WEBHOOK_URL }} と書きます。その value が実体化するのは、secrets capability を宣言した step の内部だけ、かつ run time だけです。
taint rule(静的に強制)
secret. reference は opaque です。これは、変換されていない全体のまま、tool input への argument としてのみ bind できます。そして、その tool が secrets capability を宣言している場合に限られます。secret reference は次のことができません。
- operator、member access、built-in function のいずれも通過できません。
secret.x + y、json(secret.x)、default(secret.x, z)、secret.x.foo はすべて拒否されます。
secrets を宣言していない tool へ bind できません。
output のどこにも現れることはできません。
各違反は secret_leak diagnostic であり、compile time の single-pass taint check によって証明されます(FlowSpec IR §7)。得られる効果は機械的です。secret は expression layer を通過しないため、その value が URL、header、browser field に組み込まれるのは、解決する tool の 内部 だけです。そして、そこは effect を記録する layer そのものなので、Journal に到達する前に secret を redact できる layer でもあります(Execution §5a)。
8. Pack — デプロイ可能な SDK
Pack は、Tools と Flows のバージョン付きかつ content-addressed な bundle です。Pack は deployment(“deploy SDKs”)、versioning(semver + digest)、replication(“services to replicate them”)の単位です。Pack の manifest には、その tools(それぞれ自身の contract digest を持つ)と flows(それぞれ FlowSpec digest を持つ)が列挙されます。そのため Pack は全体として再現可能です。一度取得し、すべての digest を検証すれば、永久に offline で実行できます。
export const PackManifestSchema = z
.object({
schema_version: z.literal("1.0"),
name: z.string().min(1), // "@acme/pricing"
version: z.string().min(1), // semver
tools: z.array(z.object({ ref: z.string(), contract_digest: z.string() }).strict()),
flows: z.array(z.object({ name: z.string(), flow_digest: z.string() }).strict()),
dependencies: z.array(z.string()), // 他の packs。pinned
digest: z.string(), // canonical manifest の sha256
})
.strict();
export type PackManifest = z.infer<typeof PackManifestSchema>;
Packs と Registry の詳細は Tools & Packs と Control Plane にあります。
9. Registry、Rider、Launcher — runtime cast
- Registry — Packs が公開され、そこから解決される場所です。local registry は directory であり、team registry は control plane です。Registries は互いに(push / pull / scheduled で)replicate し、digest によって検証されます。そのため、ある environment で作成された Pack は、pin され完全性を保ったまま、別の environment に現れます。
- Rider — 実行エンジンです。FlowSpec + inputs + 注入された capabilities の set を受け取り、Run を生成します。何かを実行する 唯一 の component であり、model を実行しません。
- Launcher — environment 向けの capabilities を構築して Rider を呼び出す薄い host です。
browserflow run(CLI)、HTTP route(REST)、MCP tool(agents)、cron tick、CI step などです。同じ FlowSpec が、どの Launcher の下でも変更なしに実行されます。Launchers を参照してください。
10. すべての組み合わせ
オーサリング: SDK & YAML
TypeScript SDK と YAML surface の両方で同じ flow を書きます。
FlowSpec IR
正規化ルール、digest algorithm、graph derivation。