compose

package
v0.1.0-beta.10 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 18, 2026 License: Apache-2.0 Imports: 10 Imported by: 0

Documentation

Overview

Package compose hosts the agent runtime's graph-level primitives that sit just above the eino adapter — primitives the rest of pkg/agent composes against without ever importing eino types directly. Phase E adds the approval gate: a node-shaped primitive that suspends a run to wait on a human decision delivered by CLI, web UI, or MCP.

The approval gate is structurally simple: a Gate ties an Approver (the sandbox-side interface) to a persist.Recorder (the run's JSONL ledger) and a Notifier (the channel that surfaces the request). Each approval gets a fresh, namespaced ID; pending gates live in a process-wide Registry so an out-of-band consumer (CLI subcommand, HTTP handler, MCP notification) can resolve them by ID without holding a reference to the running graph.

Index

Constants

View Source
const DefaultApprovalTimeout = 24 * time.Hour

DefaultApprovalTimeout is the wall-clock window the runtime waits for an approval response before auto-rejecting. 24h is the design's quoted default; operators can override per-call via Gate.WithTimeout.

View Source
const DefaultApprovalWarnFraction = 0.8

DefaultApprovalWarnFraction is the fraction of DefaultApprovalTimeout at which a "still pending" warning is emitted. 0.8 means the warning fires after 19h12m for the default 24h window.

Variables

View Source
var ErrAlreadyResolved = errors.New("compose: approval already resolved")

ErrAlreadyResolved is returned by Resolve when the same ID has been resolved already. Distinguished from ErrApprovalNotFound so callers can render different UX (404 vs 409 in HTTP, "already approved" vs "no such approval" in CLI).

View Source
var ErrApprovalNotFound = errors.New("compose: approval not found or already resolved")

ErrApprovalNotFound is returned by Resolve when the ID does not match a registered pending approval. Callers map this to HTTP 404 / CLI exit-2.

Functions

This section is empty.

Types

type ApprovalRequest

type ApprovalRequest struct {
	// RunID is the run that suspended on the gate.
	RunID string

	// ApprovalID is the handle a consumer responds against.
	ApprovalID string

	// Prompt is the human-readable description of what's being
	// approved.
	Prompt string

	// Skill is the unprefixed skill name the run is executing under,
	// when known. Empty for ad-hoc graph compositions.
	Skill string

	// Timeout is the wall-clock window before auto-rejection.
	Timeout time.Duration

	// CreatedAt is the wall-clock time the request was raised.
	CreatedAt time.Time
}

ApprovalRequest carries the data a Notifier renders when an approval gate fires. The shape mirrors persist.ApprovalRequestPayload but carries the run ID and skill explicitly so a notifier can render a CLI banner or web push without a second JSONL read.

type Gate

type Gate struct {
	// contains filtered or unexported fields
}

Gate is the runtime-side approval primitive. One Gate is created per run — its RunID anchors every event and notification it emits. Gate satisfies sandbox.Approver, so it drops in wherever the TS sandbox expects an Approver and gives the run author a single `approval(prompt)` call site that surfaces simultaneously to the CLI, the web UI, and any MCP consumer.

func NewGate

func NewGate(runID string, recorder *persist.Recorder, registry *Registry, notifier Notifier, opts ...GateOption) (*Gate, error)

NewGate constructs a Gate for a run. The recorder MUST be the run's JSONL writer; nil is rejected because the gate is not safe to use without persistence (a crashed approval would leave the CLI/web/MCP consumers unable to discover it on restart). Pass a SlogNotifier via WithNotifier to keep the operator-side audit line when no MCP/web notifier is wired.

func (*Gate) Approve

func (g *Gate) Approve(ctx context.Context, prompt string) (sandbox.ApprovalDecision, error)

Approve implements sandbox.Approver. The TS sandbox calls this when a skill invokes the approval(prompt) binding; a Go skill that needs the gate calls it directly. The method blocks until a decision is delivered through the Registry or the configured timeout elapses.

type GateOption

type GateOption func(*Gate)

GateOption configures a Gate at construction time.

func WithLogger

func WithLogger(logger *slog.Logger) GateOption

WithLogger sets the slog.Logger the gate uses for its warn-at-80% line. Nil falls back to slog.Default at warn time.

func WithSkill

func WithSkill(name string) GateOption

WithSkill records the skill name on the request so notifiers can render it without a second JSONL read.

func WithTimeout

func WithTimeout(d time.Duration) GateOption

WithTimeout overrides DefaultApprovalTimeout. A non-positive value is silently dropped so callers can pass through a config field that may be unset.

func WithTimeoutPolicy

func WithTimeoutPolicy(p TimeoutPolicy) GateOption

WithTimeoutPolicy overrides the default TimeoutReject behaviour.

func WithWarnFraction

func WithWarnFraction(f float64) GateOption

WithWarnFraction overrides DefaultApprovalWarnFraction. Values outside (0, 1) are clamped to the default to avoid pathological no-warning or warn-immediately behaviour.

type MultiNotifier

type MultiNotifier struct {
	Notifiers []Notifier
}

MultiNotifier fans an ApprovalRequest out to several notifiers in order. A non-nil error from any notifier is returned and stops the fan-out; in practice operators wire SlogNotifier first so the audit line lands even if a downstream notifier (MCP, web push) fails.

func (*MultiNotifier) NotifyApproval

func (m *MultiNotifier) NotifyApproval(ctx context.Context, req ApprovalRequest) error

NotifyApproval implements Notifier.

type Notifier

type Notifier interface {
	NotifyApproval(ctx context.Context, req ApprovalRequest) error
}

Notifier surfaces an ApprovalRequest to consumers (CLI banners, web UI banners, MCP notifications). Implementations MUST be non-blocking and idempotent — the gate fans out a single Notify call per request, then returns control to the run loop while it blocks on the decision channel.

type Pending

type Pending struct {
	ID        string
	RunID     string
	Skill     string
	Prompt    string
	CreatedAt time.Time
	Timeout   time.Duration
	// contains filtered or unexported fields
}

Pending is the registry-side view of an in-flight approval. List returns slices of these for `runs list --format json` and for the web UI's polling endpoint; the Resolve method on the registry drains the corresponding channel once.

type Registry

type Registry struct {
	// contains filtered or unexported fields
}

Registry is the process-wide pending-approvals table. Goroutines inside a Gate.Approve register a Pending; CLI / API / MCP consumers look up by ID and Resolve. Both sides are fully thread-safe.

func NewRegistry

func NewRegistry() *Registry

NewRegistry constructs an empty Registry.

func (*Registry) List

func (r *Registry) List() []Pending

List returns every currently pending approval. The slice is a snapshot; subsequent registrations or resolutions do not mutate the returned slice.

func (*Registry) Lookup

func (r *Registry) Lookup(id string) (Pending, bool)

Lookup returns a snapshot of a Pending by ID. The returned struct is safe to share — no mutable channel reference is exposed.

func (*Registry) Resolve

func (r *Registry) Resolve(id string, approved bool, reason, source string) error

Resolve delivers a decision to a pending approval. Source is one of "cli", "web", "mcp", or "timeout"; it is recorded verbatim in the JSONL ledger so the inspect view can render where the response came from.

type SlogNotifier

type SlogNotifier struct {
	// Logger receives the warning line. nil falls back to
	// slog.Default at call time.
	Logger *slog.Logger
}

SlogNotifier is the default Notifier: it emits a structured warning log line each time an approval gate fires. The CLI banner and web UI banner are surfaced separately by the API/CLI layers; the slog line is the operator-side audit trail.

func (*SlogNotifier) NotifyApproval

func (n *SlogNotifier) NotifyApproval(_ context.Context, req ApprovalRequest) error

NotifyApproval implements Notifier.

type TimeoutPolicy

type TimeoutPolicy int

TimeoutPolicy describes what happens when an approval window elapses without a response. The runtime always emits a "warn at 80%" log line first; the policy controls only the terminal action.

const (
	// TimeoutReject auto-rejects the request when the window
	// elapses. Default — matches the prompt's "approval gate timeout
	// transitions run to error" guidance.
	TimeoutReject TimeoutPolicy = iota

	// TimeoutBlock keeps the gate open indefinitely. Useful for
	// long-running interactive runs where the human is the
	// scheduling constraint.
	TimeoutBlock
)

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL