Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 48 additions & 51 deletions orchestrator-v2/README.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
# Orchestrator v2

A rebuild of the Bifrost orchestrator as a thin **get-work + dispatch** system. Execution happens on remote runners over a signed WebSocket RPC protocol. This monorepo contains the shared contracts and libraries that wire orchestrator, runners, and task sources together.
A rebuild of the Bifrost orchestrator as a thin **get-work + dispatch** system. Execution happens on remote runners over a signed WebSocket RPC protocol. This monorepo contains the shared contracts and libraries that wire orchestrator, runners, and work item sources together.

## Packages

| Package | Purpose |
| ------------------------------------ | --------------------------------------------------------------------- |
| `@bifrost-ai/interfaces-task` | Script task definition and result types |
| `@bifrost-ai/interfaces-task-source` | Task and `TaskSource` contracts |
| `@bifrost-ai/protocol` | Signed WebSocket RPC between orchestrator and runners |
| `@bifrost-ai/orchestrator` | Thin orchestrator: stream tasks, dispatch to runners, record outcomes |
| `@bifrost-ai/runner` | Remote script runner: config-driven dial, execute, report outcomes |
| `@bifrost-ai/engine` | Engine interface, types, and `TestEngine` for development/testing |
| `@bifrost-ai/agent-3-task` | Task Agent — single-shot LLM execution as a script |
| Package | Purpose |
| ----------------------------- | -------------------------------------------------------------------- |
| `@bifrost-ai/interfaces-work` | Work item, handler, and execution contracts |
| `@bifrost-ai/protocol` | Signed WebSocket RPC between orchestrator and runners |
| `@bifrost-ai/orchestrator` | Thin orchestrator: stream work items, dispatch, record outcomes |
| `@bifrost-ai/runner` | Remote runner: config-driven dial, execute handlers, report outcomes |
| `@bifrost-ai/engine` | Engine interface, types, and `TestEngine` for development/testing |
| `@bifrost-ai/agent-3-task` | Task Agent — single-shot LLM execution (`kind: "task"`) |

For design background and how each piece fits together, see [docs/](docs/).

Expand All @@ -35,61 +34,60 @@ vp run -r build # build all packages

## Usage

### Define a script task
### Define a work item handler

Scripts are plain async functions. There is no built-in LLM task type — higher-level agents (Task Agent, Workflow Agent) build on this interface.
Handlers are registered on the runner and executed when a matching work item is dispatched. Higher-level agents (Task Agent, Workflow Agent) build on this interface.

```typescript
import type { ScriptTaskDefinition } from "@bifrost-ai/interfaces-task";
import type { WorkItemHandler } from "@bifrost-ai/interfaces-work";

const echo: ScriptTaskDefinition = {
const echo: WorkItemHandler = {
kind: "script",
name: "echo",
async run(ctx) {
const message = ctx.metadata.message as string;
async run(workItem, ctx) {
const message = workItem.metadata.message as string;
await ctx.setState({ echoed: message });
return { outcome: "completed", message };
},
};
```

A script receives:
A handler receives:

- `taskId`, `agentType`, `agentName` — from the dispatched task
- `data` — `get(type)` returns a typed `Registry<T>`, then `.get(name)` for the instance
- `agents` — `get(agentType, name)` for other registered handlers
- `taskState` — mutable per-task state (persisted via the task source)
- `metadata` — read-only context attached when the task was created
- `setState(state)` — persist state updates back to the source
- `workItem` — the dispatched instance (`workItemId`, `kind`, `name`, `state`, `metadata`)
- `ctx.data` — `get(type)` returns a typed `Registry<T>`, then `.get(name)` for the instance
- `ctx.handlers` — `get(kind, name)` for other registered handlers
- `ctx.setState(state)` — persist state updates back to the work item source

It returns `{ outcome: "completed" | "failed" | "paused", message?, telemetry? }`. A thrown error is treated as `failed`.

### Implement a task source
### Implement a work item source

The orchestrator does not resolve dependencies or inspect task graphs. Your `TaskSource` implementation owns that logic and yields **already-resolved** tasks.
The orchestrator does not resolve dependencies or inspect work graphs. Your `WorkItemSource` implementation owns that logic and yields **already-resolved** work items.

```typescript
import type { Task, TaskSource } from "@bifrost-ai/interfaces-task-source";
import type { WorkItem, WorkItemSource } from "@bifrost-ai/interfaces-work";

const taskSource: TaskSource = {
async *watchTasks() {
const workItemSource: WorkItemSource = {
async *watchWorkItems() {
yield {
taskId: "task-1",
agentType: "script",
agentName: "echo",
taskState: {},
workItemId: "work-item-1",
kind: "script",
name: "echo",
state: {},
metadata: { message: "hello" },
} satisfies Task;
} satisfies WorkItem;
},
async completeTask(taskId) {
async completeWorkItem(workItemId) {
/* mark done */
},
async failTask(taskId, error) {
async failWorkItem(workItemId, error) {
/* mark failed */
},
async pauseTask(taskId) {
async pauseWorkItem(workItemId) {
/* mark paused */
},
async setState(taskId, taskState) {
async setState(workItemId, state) {
/* persist state */
},
};
Expand All @@ -111,7 +109,7 @@ const handle = await runOrchestrator({
authorizedRunners: loadAuthorizedRunners([
{ keyId: runnerIdentity.keyId, publicKeyPem: exportPublicKeyPem(runnerIdentity.publicKey) },
]),
taskSource,
workItemSource,
scheduler: {
async call(method, params) {
// workflow scheduling callbacks from runners
Expand All @@ -122,12 +120,12 @@ const handle = await runOrchestrator({
});

// handle.peer.address — WebSocket listen address
// handle.done — resolves when watchTasks() ends and in-flight work drains
// handle.done — resolves when watchWorkItems() ends and in-flight work drains
```

### Run a runner

Runners dial the orchestrator over WebSocket. With `runner.yaml` present, keys and URL load automatically — register scripts and start:
Runners dial the orchestrator over WebSocket. With `runner.yaml` present, keys and URL load automatically — register handlers and start:

```typescript
import { Runner, createDataRegistry } from "@bifrost-ai/runner";
Expand All @@ -138,7 +136,7 @@ const runner = new Runner({ data });

data.get("engine").register("claude", claudeEngine);
enrollTaskAgent(runner, reviewerAgent);
runner.registerAgent("script", echo);
runner.registerWorkItemHandler(echo);

await runner.start();
```
Expand All @@ -162,27 +160,26 @@ identity:
...
```

See [docs/runner.md](docs/runner.md) for config discovery, trust model, and plugin enrollment.
See [docs/runner.md](docs/runner.md) for config discovery, trust model, and handler enrollment.

### RPC methods exposed by the orchestrator

Runners call back into the orchestrator over the same signed WebSocket:

| Method | Params | Description |
| --------------------- | ----------------------- | ---------------------- |
| `task.complete` | `{ taskId }` | Mark task completed |
| `task.fail` | `{ taskId, message? }` | Mark task failed |
| `task.pause` | `{ taskId }` | Mark task paused |
| `taskSource.setState` | `{ taskId, taskState }` | Persist script state |
| `scheduler.call` | `{ method, args }` | Invoke scheduler proxy |
| Method | Params | Description |
| ------------------------- | -------------------------- | ------------------------ |
| `workItem.complete` | `{ workItemId }` | Mark work item completed |
| `workItem.fail` | `{ workItemId, message? }` | Mark work item failed |
| `workItem.pause` | `{ workItemId }` | Mark work item paused |
| `workItemSource.setState` | `{ workItemId, state }` | Persist handler state |
| `scheduler.call` | `{ method, args }` | Invoke scheduler proxy |

The orchestrator dispatches work with `dispatch` RPC requests containing a full `Task` object.
The orchestrator dispatches work with `dispatch` RPC requests containing a full `WorkItem` object.

## Documentation

- [docs/](docs/) — how the system works (architecture, design decisions)
- [packages/protocol/README.md](packages/protocol/README.md) — protocol implementation details
- [packages/interfaces-task-source/README.md](packages/interfaces-task-source/README.md) — task source contract details

## Related issues

Expand Down
51 changes: 25 additions & 26 deletions orchestrator-v2/docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ This folder describes **how the libraries work**: architecture, contracts, and d

| Document | Issue | Summary |
| ------------------------------------------ | ---------------------------------------------------- | ------------------------------------------------------------- |
| [script-tasks.md](script-tasks.md) | [#32](https://github.com/devzeebo/bifrost/issues/32) | Script execution primitive — the core task unit |
| [work-items.md](work-items.md) | [#32](https://github.com/devzeebo/bifrost/issues/32) | Work item execution primitive — the core orchestrator unit |
| [protocol.md](protocol.md) | [#33](https://github.com/devzeebo/bifrost/issues/33) | Signed WebSocket RPC between orchestrator and runners |
| [orchestrator.md](orchestrator.md) | [#35](https://github.com/devzeebo/bifrost/issues/35) | Thin get-work + dispatch loop |
| [runner.md](runner.md) | [#36](https://github.com/devzeebo/bifrost/issues/36) | Remote script runner |
Expand All @@ -17,9 +17,9 @@ This folder describes **how the libraries work**: architecture, contracts, and d

```mermaid
flowchart LR
subgraph source [Task Source]
TS[watchTasks]
CT[completeTask / failTask / pauseTask]
subgraph source [Work Item Source]
TS[watchWorkItems]
CT[completeWorkItem / failWorkItem / pauseWorkItem]
end

subgraph orch [Orchestrator]
Expand All @@ -30,28 +30,28 @@ flowchart LR

subgraph runner [Runner]
HB[heartbeat]
EXEC[script execution]
EXEC[work item execution]
end

TS --> DISP
DISP -->|signed dispatch RPC| WS
WS --> runner
runner -->|task.complete / fail / pause| RPC
runner -->|workItem.complete / fail / pause| RPC
RPC --> CT
runner -->|taskSource.setState| RPC
runner -->|workItemSource.setState| RPC
```

### Design principles

1. **One execution primitive** — scripts only. LLM and workflow logic are agent packages built on top, not first-class task types.
1. **One execution primitive** — work items only. LLM and workflow logic are agent packages built on top, not first-class work item types.
2. **One transport** — runners always connect over signed WebSocket. No in-process direct-call shortcut.
3. **Thin orchestrator** — no dependency resolution, hooks, engines, or prompt rendering. The task source owns graph logic.
3. **Thin orchestrator** — no dependency resolution, hooks, engines, or prompt rendering. The work item source owns graph logic.
4. **Static runner trust** — authorized runner public keys are loaded from config at startup. Adding a runner requires a restart.
5. **No hooks** — v1 lifecycle hooks are removed entirely.

### Agent lifecycles

Higher-level agents are built on the [script task interface](script-tasks.md), but they behave very differently:
Higher-level agents are built on the [work item interface](work-items.md), but they behave very differently:

| | Task Agent | Workflow Agent |
| -------------- | ------------------------ | ------------------------------------------------------ |
Expand All @@ -65,27 +65,26 @@ See [agent-3-task.md](agent-3-task.md) and [agent-4-workflow.md](agent-4-workflo
### Package boundaries

```
interfaces-task Pure types for script definitions and results
interfaces-task-source Task + TaskSource contracts
interfaces-work Work item types, WorkItemSource, and handler contracts
protocol Wire format, signing, WebSocket peers
orchestrator Dispatch loop, peer registry, RPC routing
runner Script execution, config, heartbeat, dispatch handling
runner Work item execution, config, heartbeat, dispatch handling
engine Engine interface, types, and TestEngine
agent-3-task Task Agent — single-shot engine execution as a leaf script
agent-4-workflow Workflow Agent — DAG scheduling as a script (planned)
agent-3-task Task Agent — single-shot engine execution as a leaf handler
agent-4-workflow Workflow Agent — DAG scheduling as a handler (planned)
```

The runner package consumes `protocol` and `interfaces-task` to execute scripts remotely.
The runner package consumes `protocol` and `interfaces-work` to execute work items remotely.

### Current status

| Component | Status |
| ------------------------------------------------ | -------------------------------------------------------------- |
| Script task types (`interfaces-task`) | Done |
| Protocol + signing (`protocol`) | Done |
| Task source interface (`interfaces-task-source`) | Done |
| Thin orchestrator (`orchestrator`) | Done |
| Runner package | Done |
| Bifrost task source adapter | Planned ([#40](https://github.com/devzeebo/bifrost/issues/40)) |
| Task Agent (`agent-3-task`) | Done ([#37](https://github.com/devzeebo/bifrost/issues/37)) |
| Workflow Agent (`agent-4-workflow`) | Planned ([#39](https://github.com/devzeebo/bifrost/issues/39)) |
| Component | Status |
| ---------------------------------------------- | -------------------------------------------------------------- |
| Work item types (`interfaces-work`) | Done |
| Protocol + signing (`protocol`) | Done |
| Work item source interface (`interfaces-work`) | Done |
| Thin orchestrator (`orchestrator`) | Done |
| Runner package | Done |
| Bifrost work item source adapter | Planned ([#40](https://github.com/devzeebo/bifrost/issues/40)) |
| Task Agent (`agent-3-task`) | Done ([#37](https://github.com/devzeebo/bifrost/issues/37)) |
| Workflow Agent (`agent-4-workflow`) | Planned ([#39](https://github.com/devzeebo/bifrost/issues/39)) |
2 changes: 1 addition & 1 deletion orchestrator-v2/docs/agent-3-task.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,5 +115,5 @@ For the full coordinator lifecycle, see [agent-4-workflow.md](agent-4-workflow.m

## Related

- [Script tasks](script-tasks.md) — the execution primitive underneath all agents
- [Work items](work-items.md) — the execution primitive underneath all agents
- [Workflow Agent](agent-4-workflow.md) — schedules Task Agents as children
4 changes: 2 additions & 2 deletions orchestrator-v2/docs/agent-4-workflow.md
Original file line number Diff line number Diff line change
Expand Up @@ -209,5 +209,5 @@ When a child exhausts all retries and permanently fails, the workflow needs a di
## Related

- [Task Agent](agent-3-task.md) — the leaf agent that does LLM work
- [Script tasks](script-tasks.md) — the execution primitive underneath all agents
- [Task source](../packages/interfaces-task-source/README.md) — owns dependency resolution and draft/live gating
- [Work items](work-items.md) — the execution primitive underneath all agents
- [Work item source](orchestrator.md) — owns dependency resolution and draft/live gating
Loading