Skip to content

flows: agent-to-agent effect channel via broker transport (SURFACE covenant clarification) #335

Description

@kjgbot

Problem

v2 spec is silent on how agents within a flow talk to each other while running. v1 had this natively via the broker. Sequential handoff (output-to-input) and `f.dispatch` (child flow) cover most cases, but true async mid-step chatter is unsupported — a fan-out of parallel agent steps that need to consult each other cannot express that in the current spec.

Options considered + rejected

  • New in-process channel primitive. Duplicates the messaging layer we've built in `../relay` (DMs, channels, inbox, threads, reactions, dead-letter). Not going to reinvent a year of pty/message plumbing.
  • Free-for-all broker access. Agents get workspace credentials and post/subscribe freely. Violates journal-as-truth (RFC-0001 covenant 1): broker delivery is at-least-once with dedup-by-message-id, and any broker DM that influenced an agent's output is invisible to `step.completed`. Replay lies.

Chosen design — broker-as-effect-transport with journaled boundaries

  • Each flow-run gets an ephemeral broker workspace, short-lived, workspace-key seeded from `run_id` (deterministic).
  • Agents get workspace credentials as part of their step setup.
  • Every message send/receive is journaled as an effect step via the existing effect record/confirm claim path (`docs/RFC-0001` Appendix A).
  • Idempotency key = message id, so replay's effect-claim mechanism dedupes automatically. No message is delivered twice on resume.
  • Broker is transport, journal is truth.

New covenant clause (proposed for SURFACE.md §6 / §7)

Broker interactions with a v2 flow are legal only as journaled effect steps. Any bypass of that journaling makes the run non-replayable and must set `step.completed.human_intervention: true`.

Author-facing surface

New primitive on `Ctx`:

```ts
const channel = f.channel('review-panel');
await channel.post('security-lens', 'this looks off at line 42'); // journaled effect
const msg = await channel.recv('maintainability-lens', { timeout: '30s' }); // journaled effect
```

Both `post` and `recv` compile to the kernel `effect` primitive (closed-vocabulary preserved).

Ephemeral workspace lifecycle

  • Created on first `f.channel` use in a run; workspace-key seeded deterministically from `run_id`.
  • Cleaned up when run reaches terminal state.
  • If run parks, workspace persists until resume or lease expiry.
  • Broker crash: workspace state can be reconstructed from journal (effect records are the truth).

Acceptance

  1. Two agents in one flow post/recv on a shared channel; journal contains exactly one effect step per message boundary (send + receive).
  2. SIGKILL mid-flow after post-but-before-recv: resume replays effect record, receiver sees the same message once (idempotent by message id).
  3. `flows check` refuses `f.channel` calls with unresolved participants (agents referenced but not spawned).
  4. Concurrent posts serialize deterministically per channel (journal ordering).
  5. Workspace credentials never leak into step outputs (redacted at effect boundary).

Files to touch

  • `packages/surface/src/channel.ts` (new — `ChannelHandle`, `ChannelParticipant` types)
  • `packages/surface/src/context.ts` (extend `Ctx` with `channel(name)`)
  • `packages/sdk/src/authored-flow-executor.ts` (route `f.channel` calls to effect steps)
  • `packages/sdk/src/effect-channel.ts` (new — broker-adapter with per-run workspace lifecycle + idempotency key management)
  • `packages/sdk/src/preflight.ts` (channel participants must resolve to declared agents)
  • `kernel/relayflowd-core/**` (`effect` step accepts `channel` kind; small extension)
  • `docs/SURFACE.md` (subsection under §2 rule 3 for `f.channel` + new §7 covenant clause "Broker as effect transport")
  • Integration tests (packages/sdk/tests/) using a local agent-relay broker fixture

Not-in-scope this slice

  • Persistent workspaces across runs
  • Cross-flow channels (a channel spans one run only)
  • Admin/audit UI for channel history

Depends on

Follow-ups

  • Cross-flow persistent channels (future spec slice)
  • Per-channel policy (rate limits, participant allow-lists)
  • Auditor UI reading channel history from journal

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions