PromptRunner.Control.Plane (PromptRunnerSDK v0.10.0)

Copy Markdown View Source

The runner's half of the control plane.

Holds one run's control state, maintains snapshot.json and the subscriber event stream, and consumes pending requests at event boundaries — never mid-event. A request that cannot be parsed, names an unknown command, or targets a run that is no longer current is logged and dropped; nothing arriving through this transport is ever fatal to a run.

Consumers use PromptRunner.Control, not this module.

A plan with no state directory — an in-memory API run — gets a disabled plane. Embedded use stays free of filesystem side effects, and every function here is a no-op returning the same state.

Summary

Functions

An event boundary: consume every pending request, then rewrite the snapshot.

Marks the run finished. The snapshot survives the runner, so a reader that arrives afterwards sees how it ended rather than a run apparently still in flight.

Folds one canonical event into the snapshot and appends it to the subscriber stream.

Opens the control plane for a run and writes its first snapshot.

Records that a prompt attempt has started, resetting the per-prompt counters.

Appends an entry to the control log.

Records a steer that was actually delivered, and counts it against the budget.

Records a steer the lane refused. Costs no budget and leaves no artifact.

Types

command()

@type command() :: {:set_view, map()} | {:steer, String.t(), String.t() | nil}

t()

@type t() :: %PromptRunner.Control.Plane{
  max_steers: non_neg_integer(),
  packet_dir: String.t() | nil,
  prompt_started_mono: integer() | nil,
  run_started_mono: integer() | nil,
  snapshot: PromptRunner.Control.Snapshot.t()
}

Functions

boundary(plane)

@spec boundary(t()) :: {t(), [command()]}

An event boundary: consume every pending request, then rewrite the snapshot.

Returns the commands the caller has to act on. Only the caller knows how to reach the live renderer, so this decides what was asked for and leaves how to the runner.

close(plane, status)

@spec close(t(), PromptRunner.Control.Snapshot.status()) :: t()

Marks the run finished. The snapshot survives the runner, so a reader that arrives afterwards sees how it ended rather than a run apparently still in flight.

enabled?(plane)

@spec enabled?(t()) :: boolean()

observe(plane, event)

@spec observe(t(), map()) :: t()

Folds one canonical event into the snapshot and appends it to the subscriber stream.

Deliberately not persisted here: boundary/1 writes the snapshot once per event, and rewriting it twice per event doubles the IO for nothing.

open(packet_dir, opts)

@spec open(
  String.t() | nil,
  keyword()
) :: t()

Opens the control plane for a run and writes its first snapshot.

packet_dir of nil disables the plane.

packet_dir(plane)

@spec packet_dir(t()) :: String.t() | nil

prompt_started(plane, prompt, mode, attempt, llm)

@spec prompt_started(t(), map(), atom(), pos_integer(), map()) :: t()

Records that a prompt attempt has started, resetting the per-prompt counters.

record(plane, command, params, opts)

@spec record(t(), String.t(), map(), keyword()) :: t()

Appends an entry to the control log.

run_id(plane)

@spec run_id(t()) :: String.t() | nil

steer_count(plane)

@spec steer_count(t()) :: non_neg_integer()

steer_delivered(plane, text, author, lane, delivery)

@spec steer_delivered(t(), String.t(), String.t() | nil, atom(), atom()) :: t()

Records a steer that was actually delivered, and counts it against the budget.

Called by the runner rather than by boundary/1, because only the runner knows whether the text reached the session — a steer refused by the lane must not spend budget or leave an artifact claiming the agent was told something.

steer_refused(plane, text, author, reason)

@spec steer_refused(t(), String.t(), String.t() | nil, term()) :: t()

Records a steer the lane refused. Costs no budget and leaves no artifact.

view(plane)