# Eve vs Flue Runtime Splits

> Contrasts Eve's three-way split—channel owns continuationToken, harness does one AI unit, runtime persists and streams—with Flue's harness → session → operation hierarchy and its explicit agents-versus-workflows boundary.

- Repository: vercel/eve-with-withastro-flue
- GitHub: https://github.com/vercel/eve
- Human wiki: https://www.grok-wiki.com/public/wiki/vercel-eve-with-withastro-flue-43b600348681
- Complete Markdown: https://www.grok-wiki.com/public/wiki/vercel-eve-with-withastro-flue-43b600348681/llms-full.txt

## Source Files

- `vercel-eve:README.md`
- `vercel-eve:docs/concepts/sessions-runs-and-streaming.md`
- `vercel-eve:packages/eve/src/shared/agent-definition.ts`
- `withastro-flue:AGENTS.md`
- `withastro-flue:packages/runtime/src/harness.ts`
- `withastro-flue:packages/runtime/src/runtime/flue-app.ts`
- `withastro-flue:packages/runtime/src/runtime/handle-agent.ts`

---

<details>
<summary>Relevant source files</summary>

The following files were used as context for generating this wiki page:

- [vercel-eve/README.md](vercel-eve/README.md)
- [vercel-eve/docs/concepts/sessions-runs-and-streaming.md](vercel-eve/docs/concepts/sessions-runs-and-streaming.md)
- [vercel-eve/docs/concepts/execution-model-and-durability.md](vercel-eve/docs/concepts/execution-model-and-durability.md)
- [vercel-eve/docs/channels/overview.mdx](vercel-eve/docs/channels/overview.mdx)
- [vercel-eve/packages/eve/src/harness/types.ts](vercel-eve/packages/eve/src/harness/types.ts)
- [vercel-eve/packages/eve/src/channel/types.ts](vercel-eve/packages/eve/src/channel/types.ts)
- [vercel-eve/packages/eve/src/execution/workflow-runtime.ts](vercel-eve/packages/eve/src/execution/workflow-runtime.ts)
- [withastro-flue/AGENTS.md](withastro-flue/AGENTS.md)
- [withastro-flue/packages/runtime/src/harness.ts](withastro-flue/packages/runtime/src/harness.ts)
- [withastro-flue/packages/runtime/src/types.ts](withastro-flue/packages/runtime/src/types.ts)
- [withastro-flue/packages/runtime/src/runtime/flue-app.ts](withastro-flue/packages/runtime/src/runtime/flue-app.ts)
- [withastro-flue/packages/runtime/src/runtime/handle-agent.ts](withastro-flue/packages/runtime/src/runtime/handle-agent.ts)
- [withastro-flue/packages/runtime/src/client.ts](withastro-flue/packages/runtime/src/client.ts)

</details>

# Eve vs Flue Runtime Splits

Both Eve and Flue separate *ingress*, *AI execution*, and *persistence/streaming* — but they draw the boundaries differently. Eve uses a three-way split tuned for durable, channel-driven agents on Vercel: the channel owns conversation resumption, the harness performs one checkpointed AI unit, and the runtime drives workflow-backed durability and event streaming. Flue uses a developer-facing hierarchy — harness → session → operation — and draws a hard line between persistent agent sessions and ephemeral workflow runs.

Understanding these splits matters when porting patterns between frameworks, designing HTTP clients, or deciding where to put transport logic versus execution logic.

## Eve: Channel, Harness, Runtime

Eve's README states the mental model explicitly:

| Layer | Owns | Does not own |
|-------|------|--------------|
| **Channel** | Inbound transport normalization, auth, delivery policy, `continuationToken` | Model steps, workflow primitives, event stream storage |
| **Harness** | One unit of AI work; returns `{ session, next }` | Transport, workflow lifecycle, stream endpoints |
| **Runtime** | State persistence, following `next`, event streaming, workflow primitives | Channel-specific delivery semantics |

Sources: [vercel-eve/README.md:48-57](vercel-eve/README.md), [vercel-eve/docs/channels/overview.mdx:6-10](vercel-eve/docs/channels/overview.mdx)

### Two handles, two jobs

Eve's public HTTP contract deliberately splits resume from inspection:

- **`continuationToken`** — resume handle for the next user message. Owned by the channel. A stale token is rejected; only one active continuation exists per session.
- **`sessionId` / `runId`** — stream-and-inspect handle. Owned by the runtime. Used to attach to the NDJSON event stream and inspect durable history.

Sources: [vercel-eve/docs/concepts/sessions-runs-and-streaming.md:8-15](vercel-eve/docs/concepts/sessions-runs-and-streaming.md), [vercel-eve/packages/eve/src/channel/types.ts:296-339](vercel-eve/packages/eve/src/channel/types.ts)

This split lets a Slack channel mint transport-specific continuation tokens while the runtime exposes a stable `sessionId` for streaming regardless of which channel started the conversation.

### Harness: one step, one instruction

The harness boundary is defined in `StepResult`:

```135:138:vercel-eve/packages/eve/src/harness/types.ts
export interface StepResult {
  readonly next: StepNext;
  readonly session: HarnessSession;
}
```

`next` tells the runtime what to do after this step:

- A `StepFn` reference → call immediately (tool-loop continuation)
- `null` → park and wait for the next user message
- `StepDone` → conversation finished

`HarnessSession` is serializable plain data (history, tools schema, compaction config, `continuationToken`) — no resolved model instances or execute functions — so it survives workflow step checkpoints.

Sources: [vercel-eve/packages/eve/src/harness/types.ts:50-74](vercel-eve/packages/eve/src/harness/types.ts), [vercel-eve/packages/eve/src/harness/types.ts:123-144](vercel-eve/packages/eve/src/harness/types.ts)

### Runtime: workflow-backed durability

The runtime interface (`run`, `deliver`, `getEventStream`) is what routes and channels call. Internally it drives the Workflow SDK — `start`, `resumeHook`, `getRun` — via `workflow-runtime.ts`. Channels, tools, and hooks never touch workflow primitives directly; they interact only through the runtime surface.

Work nests as **session → turn → step**. Each turn is a durable workflow; steps checkpoint at model-call boundaries. Parked work (HITL approval, OAuth, subagent delegation) suspends the workflow until input arrives.

Sources: [vercel-eve/docs/concepts/execution-model-and-durability.md:8-28](vercel-eve/docs/concepts/execution-model-and-durability.md), [vercel-eve/packages/eve/src/execution/workflow-runtime.ts:1-26](vercel-eve/packages/eve/src/execution/workflow-runtime.ts)

```mermaid
flowchart TB
  subgraph ingress ["Channel layer"]
    CH["defineChannel / eve HTTP channel"]
    CT["continuationToken"]
  end

  subgraph exec ["Harness layer"]
    HS["HarnessSession (serializable)"]
    ST["StepFn — one AI unit"]
    NX["next: StepFn | null | StepDone"]
  end

  subgraph persist ["Runtime layer"]
    RT["Runtime.run / deliver / getEventStream"]
    WF["Workflow SDK (turnWorkflow)"]
    ES["NDJSON event stream (sessionId)"]
  end

  CH -->|"normalize + auth"| RT
  CT -->|"resume follow-up"| RT
  RT -->|"invoke step"| ST
  ST -->|"returns"| HS
  ST -->|"returns"| NX
  NX -->|"driver follows"| RT
  RT --> WF
  RT --> ES
```

## Flue: Harness → Session → Operation

Flue's terminology stack is documented in `AGENTS.md`:

```
AgentInstance (URL <id>)
 └─ Harness          — runtime-initialized agent environment
    └─ Session       — one harness.session(name?)
       └─ Operation — one session.prompt / skill / task / shell call
          └─ Turn    — one LLM round-trip inside pi-agent-core
```

Sources: [withastro-flue/AGENTS.md:7-17](withastro-flue/AGENTS.md)

### Harness: session container, not AI step

`Harness` in `harness.ts` manages named sessions inside one agent instance. User code receives a `FlueSession` facade; the internal `Session` class (durable submission executor, abort/close, metadata) stays runtime-owned:

```84:87:withastro-flue/packages/runtime/src/harness.ts
		// User code only ever receives the FlueSession facade; the internal
		// Session (durable submission executor, abort/close, metadata) stays
		// runtime-owned.
		return createPublicSession(session);
```

A harness also exposes `sessions.get/create/delete`, `shell`, and `fs` outside any conversation transcript. Session operations for a given name are serialized through `runSessionOperation` to prevent concurrent open/load races.

Sources: [withastro-flue/packages/runtime/src/harness.ts:42-104](withastro-flue/packages/runtime/src/harness.ts)

### Session operations as the public execution API

`FlueSession` exposes four operation types, each returning a `CallHandle<T>`:

| Operation | Purpose |
|-----------|---------|
| `prompt(text)` | Model call with a text instruction |
| `skill(ref)` | Run a registered skill |
| `task(text)` | Delegate to a detached child session (subagent) |
| `shell(command)` | Shell command recorded in conversation state |

A **turn** is one LLM round-trip inside `pi-agent-core`, nested inside an operation. Operations can trigger multiple turns (tool loops, compaction, result-extraction retries).

Sources: [withastro-flue/packages/runtime/src/types.ts:535-604](withastro-flue/packages/runtime/src/types.ts)

### Agents vs workflows: explicit boundary

Flue draws a hard line that Eve does not mirror one-to-one:

> Runs are workflow-only. Direct HTTP/WebSocket agent prompts and dispatched agent inputs operate within persistent sessions and must not be described as runs.

| Concept | Identifier | HTTP route | Persistence model |
|---------|-----------|------------|-------------------|
| Agent session interaction | `submissionId` / instance `id` | `POST /agents/:name/:id` | Persistent session in harness |
| Async agent delivery | `dispatchId` | `dispatch()` API | Queued to existing session |
| Workflow execution | `runId` | `POST /workflows/:name` | One run per invocation; stream at `/runs/:runId` |

Agent prompts return 202 admission with stream coordinates (`streamUrl`, `offset`). Workflow runs get a fresh `runId` and a separate event stream. `dispatch()` admits input to a continuing session — it does not create workflow-run history.

Sources: [withastro-flue/AGENTS.md:20-21](withastro-flue/AGENTS.md), [withastro-flue/packages/runtime/src/runtime/flue-app.ts:163-179](withastro-flue/packages/runtime/src/runtime/flue-app.ts), [withastro-flue/packages/runtime/src/runtime/flue-app.ts:257-263](withastro-flue/packages/runtime/src/runtime/flue-app.ts), [withastro-flue/packages/runtime/src/runtime/handle-agent.ts:150-186](withastro-flue/packages/runtime/src/runtime/handle-agent.ts)

Workflows receive a `FlueContext` with `ctx.id === runId`. Agents are initialized inside that context via `ctx.init(agent)` when a workflow needs them, but direct agent HTTP traffic never creates a run record.

Sources: [withastro-flue/packages/runtime/src/client.ts:102-152](withastro-flue/packages/runtime/src/client.ts), [withastro-flue/packages/runtime/src/runtime/handle-agent.ts:189-216](withastro-flue/packages/runtime/src/runtime/handle-agent.ts)

```mermaid
flowchart TB
  subgraph agentPath ["Agent path (persistent)"]
    HTTP_A["POST /agents/:name/:id"]
    ADM["admitAttachedSubmission"]
    HAR["Harness"]
    SES["FlueSession"]
    OP["Operation: prompt | skill | task | shell"]
    TURN["Turn (pi-agent-core)"]
    DS_A["DS stream at agent URL"]
  end

  subgraph workflowPath ["Workflow path (ephemeral run)"]
    HTTP_W["POST /workflows/:name"]
    CTX["FlueContext (runId)"]
    WH["workflow handler"]
    DS_W["DS stream at /runs/:runId"]
  end

  HTTP_A --> ADM --> HAR --> SES --> OP --> TURN
  ADM --> DS_A
  HTTP_W --> CTX --> WH
  WH -.->|"init(agent) when needed"| HAR
  CTX --> DS_W
```

## Side-by-side comparison

| Dimension | Eve | Flue |
|-----------|-----|------|
| **Top-level split** | Channel / Harness / Runtime (3 layers) | Harness / Session / Operation (nested hierarchy) |
| **Ingress owner** | Channel (`defineChannel`, platform adapters) | HTTP routes + optional `channels/:name` handlers in `flue-app.ts` |
| **Resume handle** | `continuationToken` (channel-owned) | Persistent session keyed by `instanceId` + harness name + session name; no separate token type |
| **Stream handle** | `sessionId` (runtime-owned) | DS stream URL + offset at admission (agent URL or `/runs/:runId`) |
| **Smallest AI unit** | Harness step (`StepFn`) — internal framework boundary | Operation (`prompt`/`skill`/`task`/`shell`) — public developer API |
| **Durability engine** | Workflow SDK (`turnWorkflow`, `resumeHook`) | Session store + submission store + optional Durable Objects (Cloudflare) |
| **Workflow concept** | Every turn is a durable workflow; workflow primitives are runtime-internal | Workflows are a separate module type (`workflows/<name>.ts`); runs are workflow-only |
| **Subagent model** | Child durable session with own sandbox; parent gets `childSessionId` on stream | `session.task()` creates detached child session; history parent-owned until parent deleted |
| **Developer touches workflow code?** | No — runtime owns it | Yes — `export run(ctx)` in workflow modules |

## Where the designs converge

Both frameworks keep transport concerns out of the AI loop and make session state durable across process restarts. Both emit structured event streams for client attachment and replay. Both support compaction, subagent delegation, and provider-neutral model configuration (Eve via AI SDK `LanguageModel`; Flue via model specifier strings resolved at init).

The portable pattern is **split resume from observation**: Eve makes this explicit with two HTTP handles; Flue achieves a similar effect with admission receipts that carry `streamUrl` and `offset` separately from the conversational state key (`instanceId` + session name).

## Where they diverge — and what to borrow

**From Eve → Flue thinking:**
- Channel-owned continuation tokens decouple transport identity from runtime session identity. Useful when one agent serves Slack, HTTP, and webhooks with different resume semantics.
- Harness `next` as a declarative driver instruction (`continue | park | done`) keeps the execution loop testable without simulating HTTP.

**From Flue → Eve thinking:**
- Public operation types (`prompt`, `skill`, `task`, `shell`) give developers a stable, typed API surface rather than relying on implicit turn/step boundaries.
- Hard agents-vs-workflows separation prevents conflating persistent chat sessions with one-shot automation runs — a distinction Eve handles implicitly (everything is session-scoped) but Flue enforces in terminology, routes, and CLI (`flue logs` inspects workflow runs only).

## Summary

Eve optimizes for **filesystem-first, channel-driven agents** where the channel owns conversation resumption (`continuationToken`), the harness performs one serializable AI step returning `{ session, next }`, and the runtime wraps every turn in Workflow SDK durability while streaming on `sessionId`. Flue optimizes for **compiled deployable artifacts** with a developer-facing **harness → session → operation** hierarchy, using `pi-agent-core` turns inside explicit operations, and a strict boundary where workflow `runId`s and agent session interactions never share the same persistence or inspection path. Choosing between them is less about which split is "correct" and more about whether your primary abstraction is a durable channel conversation (Eve) or a typed operation inside a named session within an optional workflow run (Flue).
