Skip to content

flows: agent-pty sidechannel — preserve live attach without journal coupling #334

Description

@kjgbot

Problem

v2 f.agent-spawned subprocesses have no broker seat, so agent-relay node agent attach can't reach them. Live inspection/steering — a real v1 UX (drive/view/passthrough modes, buffer preservation, SIGWINCH, session recording) — regresses.

Options considered + rejected

  • Broker-back the worker. Violates journal-truth covenant (RFC-0001 covenant 1). Couples exactly-once execution semantics to broker delivery (at-least-once with dedup). Makes a broker crash equivalent to an agent death. Rejected.
  • Native flows attach reimplementing pty. ~year of pty work in ../relay (mode negotiation, screen buffer preservation, SIGWINCH forwarding, keystroke injection, session recording) would need reinventing. Rejected.

Chosen design (sidechannel)

The worker (packages/sdk/src/worker.ts) opens a UNIX socket at <data-dir>/runs/<run-id>/steps/<step-id>/pty.sock when it spawns an agent CLI subprocess. Byte-forwarding proxy:

  • Everything the child writes to its pty goes to socket subscribers.
  • Anything a drive-mode subscriber writes goes to the child's stdin.

Sidechannel is entirely optional for flow correctness. Socket broken/absent/unattended → flow still runs to its journaled outcome. Attach just fails.

Companion relay-side work (separate follow-up, out of this PR's scope)

Small feature in ../relay: agent-relay node agent attach --external-pty <socket-path> mode. Reuses its existing pty client stack (drive/view/passthrough, buffer preservation, SIGWINCH, session recording) — just pointed at the flows socket instead of a broker-managed agent. Filed as sibling issue in AgentWorkforce/relay after this lands.

Journal semantics ("off-journal by construction")

The sidechannel is a byte-forwarding proxy with no journal hook. That is a structural fact, not a policy.

  • View mode is entirely passive → safe. Nothing about the run's outcome changes.
  • Drive mode injections influence the child's stdout → journal captures the influenced output, but not the operator input.

Consequence: drive-influenced runs are silently non-replayable unless we mark them. Therefore:

  • Worker marks step.completed.human_intervention: true on any drive-mode connect.
  • Replay/resume refuses to progress past such a step with a new refusal human_influenced_run unless the operator passes --allow-human-influenced on flows resume/flows replay.
  • View mode never sets the flag.

Acceptance

  1. Running a flow with an agent step: agent-relay node agent attach --external-pty <data-dir>/runs/<run-id>/steps/<step-id>/pty.sock in another terminal shows the agent's live pty.
  2. View mode: text appears, step.completed.human_intervention is absent (or false).
  3. Drive mode: keystrokes reach the child's stdin; step.completed.human_intervention == true on that run.
  4. Flow with agent step still runs to completion if nobody attaches — sidechannel is optional.
  5. Socket path is deterministic + printed by flows run after the worker spawns each agent step.
  6. Replay/resume across an intervention step refuses with human_influenced_run unless --allow-human-influenced is passed.

Files to touch (flows side)

  • packages/sdk/src/worker.ts — open + serve the pty socket alongside the child spawn
  • packages/sdk/src/authored-worker-step.ts — thread run-id + step-id into the worker so it knows the socket path
  • kernel/relayflowd-core/src/machine.rs — human_intervention field on step.completed
  • kernel/relayflowd-core/src/* — new refusal kind human_influenced_run for resume/replay
  • packages/sdk/src/journal-client.ts — protocol type for the new field + refusal
  • packages/sdk/src/cli/run.ts + resume.ts + replay.ts — --allow-human-influenced flag plumb-through
  • docs/SURFACE.md — new subsection (under §5 or a new §7) documenting the sidechannel invariants + human_intervention semantics
  • Tests: sidechannel view/drive smoke, replay refusal + override, run-to-completion without attach

Not in scope

  • Relay-side --external-pty client (sibling issue after this lands).
  • Cross-cell tunneling (existing agent-relay --node remote attach covers it).
  • Journaled agent-to-agent chatter (that's slice R, filed separately).

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