control

package
v0.3.0 Latest Latest
Warning

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

Go to latest
Published: Jun 8, 2026 License: MIT Imports: 6 Imported by: 0

Documentation

Overview

Package control is part of the GoFastr harness.

See docs/harness-architecture.md for the architecture this package implements.

Package control hosts the engine-as-a-service control plane.

This file defines the transport-agnostic wire protocol: the canonical event envelope, the Command and Event sealed unions, the handshake, and the JSON codec. Every transport carries the same canonical event JSON verbatim; envelopes are framing only.

See docs/harness-architecture.md § Control plane + § Protocol versioning & evolution.

Index

Constants

View Source
const (
	SchemaVersionTokenClaim = 1
	SchemaVersionProfile    = 1
	SchemaVersionSessionLog = 1
)

Schema versions of independently-evolving sub-schemas, surfaced in the handshake response.

View Source
const (
	ReasonHandshakeRequired        = "HandshakeRequired"
	ReasonHandshakeVersionMismatch = "HandshakeVersionMismatch"
	ReasonTurnInProgress           = "TurnInProgress"
	ReasonPermissionDenied         = "PermissionDenied"
	ReasonPermissionTimeout        = "PermissionTimeout"
	ReasonTokenExpired             = "TokenExpired"
	ReasonTokenRevoked             = "TokenRevoked"
	ReasonMCPServerSHA256Mismatch  = "MCPServerSHA256Mismatch"
	ReasonMCPServerUnavailable     = "MCPServerUnavailable"
	ReasonHookHashChanged          = "HookHashChanged"
	ReasonHookTimeout              = "HookTimeout"
	ReasonRateLimited              = "RateLimited"
	ReasonBashCancelledMidCommand  = "BashCancelledMidCommand"
	ReasonNonInteractiveAckRefused = "NonInteractiveAckRefused"
	ReasonCredentialHelperFailed   = "CredentialHelperFailed"
	ReasonInvalidCommand           = "InvalidCommand"
)

Stable Reason codes used in Error events. Strings are documented in docs/harness-architecture.md § User-facing errors.

View Source
const CanonicalFormVersion = 1

CanonicalFormVersion is the version of the internal Anthropic-shape canonical message form. Provider adapters target an explicit version; the engine asserts they match.

View Source
const ProtocolVersion = "0.1.0"

ProtocolVersion is the SemVer string identifying the wire protocol. Minor bumps are additive-only; major bumps require client re-implementation. See docs § Protocol versioning → Handshake.

View Source
const ResourceURIScheme = "harness/v1"

ResourceURIScheme is the URI scheme for MCP-server-exposed resources. Pinned to v1 so a v2 scheme can coexist later.

Variables

View Source
var ErrClientClosed = errors.New("control: client closed")

ErrClientClosed is returned when a client method is called after Close.

View Source
var FeaturesV01 = []string{
	"rest",
	"branching",
	"auto_approve",
	"plan_mode",
}

FeaturesV01 is the feature flag set advertised by v0.1 builds.

Per the build-order section of the architecture doc:

  • inproc + rest transports are first-class in v0.1
  • ws + mcpserver_stdio land in v0.2
  • mcpserver_http lands in v0.3
  • delegate ships with v0.3

Functions

func AllCommandKinds

func AllCommandKinds() []string

AllCommandKinds returns the closed set of built-in command kinds, in the order they appear in the handshake response.

func AllEventKinds

func AllEventKinds() []string

AllEventKinds returns the closed set of built-in event kinds.

func MarshalCommand

func MarshalCommand(c Command) ([]byte, error)

MarshalCommand encodes a Command to JSON with a "kind" discriminator.

Types

type AnswerPermission

type AnswerPermission struct {
	SessionID ids.SessionID `json:"sessionId"`
	CallID    ids.CallID    `json:"callId"`
	Decision  Decision      `json:"decision"`
	Scope     PermitScope   `json:"scope,omitempty"` // for argv-glob/tool/session allow buttons
}

func (AnswerPermission) CommandKind

func (AnswerPermission) CommandKind() string

type AttachSession

type AttachSession struct {
	SessionID ids.SessionID `json:"sessionId"`
}

func (AttachSession) CommandKind

func (AttachSession) CommandKind() string

type CancelTurn

type CancelTurn struct {
	SessionID ids.SessionID `json:"sessionId"`
}

func (CancelTurn) CommandKind

func (CancelTurn) CommandKind() string

type Cancelled

type Cancelled struct {
	Turn   int          `json:"turn"`
	By     ids.ClientID `json:"by"`
	Reason string       `json:"reason"`
}

func (Cancelled) EventKind

func (Cancelled) EventKind() string

type Client

type Client interface {
	// ID returns the client's stable identifier for the lifetime of
	// the attach. Cross-references the OriginatorID embedded in
	// event envelopes the engine emits for turns this client started.
	ID() ids.ClientID

	// IdentityClass distinguishes human-driven clients from
	// agent-driven ones. Permission middleware honors this.
	IdentityClass() IdentityClass

	// Subscribe returns a receive-only channel of events broadcast
	// to this client. The channel is closed when the client is
	// detached or the context is done.
	Subscribe(ctx context.Context) <-chan EventEnvelope

	// Send delivers a command from the client to the multiplexer.
	// Returns an error if the transport is closed or the command
	// fails wire-format validation.
	Send(ctx context.Context, cmd Command) error

	// Close detaches the client. Detach is non-destructive at the
	// engine level — see § Multi-client semantics.
	Close() error
}

Client is the transport-agnostic abstraction the multiplexer uses to route commands and events. Every transport (inproc, rest, ws, mcpserver) provides a Client implementation.

Per hard rule 7, the engine sees only this interface — it never inspects how a client is wired.

type Command

type Command interface {

	// CommandKind returns the kind discriminator used on the wire
	// (matches handshake.command_kinds entries).
	CommandKind() string
	// contains filtered or unexported methods
}

Command is a sealed union of wire-level verbs sent from clients to the engine. Per hard rule 14, this set is closed in `control/`; plugins extend via CustomCommand.

func UnmarshalCommand

func UnmarshalCommand(data []byte) (Command, error)

UnmarshalCommand decodes a wire JSON envelope back into a typed Command. Unknown command kinds return an error (closed-union enforcement).

type CompactionTriggered

type CompactionTriggered struct {
	BeforeTokens int `json:"beforeTokens"`
	AfterTokens  int `json:"afterTokens"`
}

func (CompactionTriggered) EventKind

func (CompactionTriggered) EventKind() string

type ContentBlock

type ContentBlock struct {
	Type string `json:"type"`

	// Type-specific fields. Only the field matching Type is meaningful.
	Text       string          `json:"text,omitempty"`
	ToolUse    *ToolUse        `json:"tool_use,omitempty"`
	ToolResult *ToolResultBlk  `json:"tool_result,omitempty"`
	Thinking   json.RawMessage `json:"thinking,omitempty"` // opaque, provider-stamped
	Image      *ImageBlock     `json:"image,omitempty"`
	Yield      *YieldBlock     `json:"yield,omitempty"` // explicit end-turn signal
}

ContentBlock is a typed content item in a message. Blocks have a "type" discriminator on the wire.

type CostIncremented

type CostIncremented struct {
	Provider     string  `json:"provider"`
	Model        string  `json:"model"`
	InputTokens  int     `json:"inputTokens"`
	OutputTokens int     `json:"outputTokens"`
	CacheTokens  int     `json:"cacheTokens,omitempty"`
	USD          float64 `json:"usd"`
}

func (CostIncremented) EventKind

func (CostIncremented) EventKind() string

type CreateSession

type CreateSession struct {
	Profile string `json:"profile"`
	// Resume, when non-nil, attaches the new EngineRun to an existing LogID.
	Resume *ids.LogID `json:"resume,omitempty"`
}

func (CreateSession) CommandKind

func (CreateSession) CommandKind() string

type CustomCommand

type CustomCommand struct {
	SessionID ids.SessionID   `json:"sessionId"`
	Namespace string          `json:"namespace"` // matches a claimed slash-command namespace
	Verb      string          `json:"verb"`
	Payload   json.RawMessage `json:"payload"`
}

CustomCommand is the open extension verb for plugin-defined wire commands. The engine routes by Namespace to the registered plugin handler.

func (CustomCommand) CommandKind

func (CustomCommand) CommandKind() string

type CustomEvent

type CustomEvent struct {
	Namespace string          `json:"namespace"`
	Kind      string          `json:"kind"`
	Payload   json.RawMessage `json:"payload"`
}

CustomEvent is the open extension event for plugin-defined wire events.

func (CustomEvent) EventKind

func (CustomEvent) EventKind() string

type Decision

type Decision string

Decision is the user's answer to a permission prompt.

const (
	DecisionAllow Decision = "allow"
	DecisionDeny  Decision = "deny"
)

type DetachSession

type DetachSession struct {
	SessionID ids.SessionID `json:"sessionId"`
}

func (DetachSession) CommandKind

func (DetachSession) CommandKind() string

type EnterPlanMode

type EnterPlanMode struct {
	SessionID ids.SessionID `json:"sessionId"`
}

func (EnterPlanMode) CommandKind

func (EnterPlanMode) CommandKind() string

type Error

type Error struct {
	Reason  string          `json:"reason"`  // stable error code
	Message string          `json:"message"` // human-friendly text
	Details json.RawMessage `json:"details,omitempty"`
}

func (Error) EventKind

func (Error) EventKind() string

type Event

type Event interface {
	EventKind() string
	// contains filtered or unexported methods
}

Event is a sealed union of engine-emitted events. Per hard rule 14, this set is closed in `control/`; plugins extend via CustomEvent.

func DecodeEvent

func DecodeEvent(env EventEnvelope) (Event, error)

DecodeEvent extracts the typed event payload from a canonical envelope. Unknown event kinds return an error.

type EventEnvelope

type EventEnvelope struct {
	ID         uint64          `json:"id"`   // monotonic per SessionID
	Kind       string          `json:"kind"` // matches handshake.event_kinds
	Session    ids.SessionID   `json:"session"`
	Originator ids.ClientID    `json:"originator,omitempty"`
	TS         time.Time       `json:"ts"`
	Payload    json.RawMessage `json:"payload"`
}

EventEnvelope is the canonical JSON wrapper used by every transport. Transports may add their own framing on top (SSE `id:`/`event:`/`data:`, WS `{"frame":"event","body":...}`, MCP `notifications/resources/updated`), but the body is always this envelope.

func EncodeEvent

func EncodeEvent(id uint64, e Event, session ids.SessionID, originator ids.ClientID, ts time.Time) (EventEnvelope, error)

EncodeEvent wraps a typed event payload in a canonical EventEnvelope.

type ExitPlanMode

type ExitPlanMode struct {
	SessionID ids.SessionID `json:"sessionId"`
	Approve   bool          `json:"approve"`
}

func (ExitPlanMode) CommandKind

func (ExitPlanMode) CommandKind() string

type Handshake

type Handshake struct {
	ProtocolVersion         string   `json:"protocol_version"`
	CanonicalFormVersion    int      `json:"canonical_form_version"`
	SchemaVersionTokenClaim int      `json:"schema_version_token_claim"`
	SchemaVersionProfile    int      `json:"schema_version_profile"`
	SchemaVersionSessionLog int      `json:"schema_version_session_log"`
	CommandKinds            []string `json:"command_kinds"`
	EventKinds              []string `json:"event_kinds"`
	Features                []string `json:"features"`
	ResourceURIScheme       string   `json:"resource_uri_scheme"`
}

Handshake is the response to GET /v1/handshake (or the first frame on a WS/MCP session). Required before any other command.

func CurrentHandshake

func CurrentHandshake(features []string) Handshake

CurrentHandshake returns the handshake advertised by this binary. The Features list is provided by the caller so different builds (e.g. v0.1 vs v0.2) can advertise different optional capabilities.

type HookError

type HookError struct {
	Event    string `json:"event"`
	Command  string `json:"command"`
	ExitCode int    `json:"exitCode"`
	Output   string `json:"output,omitempty"`
}

func (HookError) EventKind

func (HookError) EventKind() string

type HookTimeout

type HookTimeout struct {
	Event   string        `json:"event"`
	Command string        `json:"command"`
	After   time.Duration `json:"after"`
}

func (HookTimeout) EventKind

func (HookTimeout) EventKind() string

type IdentityClass

type IdentityClass uint8

IdentityClass distinguishes human-driven clients from agent-driven ones. See hard rule 11 in the architecture doc.

const (
	IdentityHuman IdentityClass = iota
	IdentityAgent
)

func (IdentityClass) MarshalJSON

func (c IdentityClass) MarshalJSON() ([]byte, error)

MarshalJSON encodes the IdentityClass as its lowercase wire string.

func (IdentityClass) String

func (c IdentityClass) String() string

String returns the wire-format string for the identity class.

func (*IdentityClass) UnmarshalJSON

func (c *IdentityClass) UnmarshalJSON(data []byte) error

UnmarshalJSON decodes the IdentityClass from its lowercase wire string.

type ImageBlock

type ImageBlock struct {
	MediaType string `json:"media_type"`
	Data      string `json:"data"`
}

ImageBlock is a base64-encoded image content block.

type MCPServerDown

type MCPServerDown struct {
	Name     string `json:"name"`
	Reason   string `json:"reason"`
	Attempts int    `json:"attempts"`
}

func (MCPServerDown) EventKind

func (MCPServerDown) EventKind() string

type PermissionRequested

type PermissionRequested struct {
	CallID     ids.CallID      `json:"callId"`
	Tool       string          `json:"tool"`
	Args       json.RawMessage `json:"args"`
	Originator ids.ClientID    `json:"originator"`
	Reason     string          `json:"reason,omitempty"` // e.g. why this is being asked
}

func (PermissionRequested) EventKind

func (PermissionRequested) EventKind() string

type PermitScope

type PermitScope string

PermitScope describes how broadly an "allow" decision applies.

const (
	ScopeOnce        PermitScope = "once"
	ScopeArgvGlob    PermitScope = "argv_glob"
	ScopeTool        PermitScope = "tool"
	ScopeSessionWide PermitScope = "session"
	// ScopeAlways persists the rule to disk so subsequent harness
	// runs honor it without re-prompting. The persistence layer
	// (XDG_CONFIG_HOME/gofastr/harness/permissions.json) is loaded
	// on Engine boot.
	ScopeAlways PermitScope = "always"
)

type SendInput

type SendInput struct {
	SessionID ids.SessionID  `json:"sessionId"`
	Content   []ContentBlock `json:"content"`
	// Wait controls REST/MCP synchronous vs streaming behavior.
	// "" / "none" → returns immediately; "turn" → blocks until TurnEnded.
	Wait string `json:"wait,omitempty"`
}

func (SendInput) CommandKind

func (SendInput) CommandKind() string

type SessionEnded

type SessionEnded struct {
	Reason string `json:"reason"` // "idle" | "user" | "error" | "binary-shutdown"
}

func (SessionEnded) EventKind

func (SessionEnded) EventKind() string

type SetModel

type SetModel struct {
	SessionID ids.SessionID `json:"sessionId"`
	Model     string        `json:"model"`
}

func (SetModel) CommandKind

func (SetModel) CommandKind() string

type StreamGap

type StreamGap struct {
	From   uint64 `json:"from"`
	To     uint64 `json:"to"`
	Reason string `json:"reason"` // "ttl" | "compaction"
}

func (StreamGap) EventKind

func (StreamGap) EventKind() string

type TextDelta

type TextDelta struct {
	Text string `json:"text"`
}

func (TextDelta) EventKind

func (TextDelta) EventKind() string

type ThinkingDelta

type ThinkingDelta struct {
	Block json.RawMessage `json:"block"` // opaque, provider-stamped
}

func (ThinkingDelta) EventKind

func (ThinkingDelta) EventKind() string

type TokenExpiring

type TokenExpiring struct {
	JTI   ids.JTI   `json:"jti"`
	ExpAt time.Time `json:"expAt"`
}

func (TokenExpiring) EventKind

func (TokenExpiring) EventKind() string

type ToolCallProgress

type ToolCallProgress struct {
	CallID  ids.CallID `json:"callId"`
	Partial string     `json:"partial"`
}

func (ToolCallProgress) EventKind

func (ToolCallProgress) EventKind() string

type ToolCallStarted

type ToolCallStarted struct {
	CallID   ids.CallID      `json:"callId"`
	Tool     string          `json:"tool"`
	Args     json.RawMessage `json:"args"`
	Mutating bool            `json:"mutating"`
}

func (ToolCallStarted) EventKind

func (ToolCallStarted) EventKind() string

type ToolResult

type ToolResult struct {
	CallID  ids.CallID     `json:"callId"`
	Content []ContentBlock `json:"content"`
	IsError bool           `json:"isError,omitempty"`
}

func (ToolResult) EventKind

func (ToolResult) EventKind() string

type ToolResultBlk

type ToolResultBlk struct {
	ToolUseID string         `json:"tool_use_id"`
	Content   []ContentBlock `json:"content"`
	IsError   bool           `json:"is_error,omitempty"`
}

ToolResultBlk is the result of a tool invocation, fed back to the model on the next turn.

type ToolTimingEntry

type ToolTimingEntry struct {
	CallID   ids.CallID    `json:"callId"`
	Tool     string        `json:"tool"`
	Duration time.Duration `json:"duration"`
}

type ToolUse

type ToolUse struct {
	ID    string          `json:"id"`    // matches a tool_use id later in ToolResultBlk
	Name  string          `json:"name"`  // tool name
	Input json.RawMessage `json:"input"` // tool-call arguments
}

ToolUse is a model-emitted tool invocation.

type TurnEnded

type TurnEnded struct {
	Turn   int    `json:"turn"`
	Reason string `json:"reason"` // "complete" | "cancelled" | "error" | "yield"
}

func (TurnEnded) EventKind

func (TurnEnded) EventKind() string

type TurnStarted

type TurnStarted struct {
	Turn       int          `json:"turn"`
	Originator ids.ClientID `json:"originator"`
	// Content is the input that started this turn. Lets non-originator
	// clients (browser sidecar attached to the same session as the TUI,
	// or vice-versa) render the user message without round-tripping
	// through a separate "echo" event. Optional for backwards
	// compatibility — older publishers may omit it.
	Content []ContentBlock `json:"content,omitempty"`
}

func (TurnStarted) EventKind

func (TurnStarted) EventKind() string

type TurnTiming

type TurnTiming struct {
	Turn       int                      `json:"turn"`
	Components map[string]time.Duration `json:"components"`
	ToolCalls  []ToolTimingEntry        `json:"toolCalls,omitempty"`
}

TurnTiming is emitted at TurnEnded with per-component duration data. Operators answer "where was the time?" from a single event.

func (TurnTiming) EventKind

func (TurnTiming) EventKind() string

type YieldBlock

type YieldBlock struct {
	Reason string `json:"reason,omitempty"`
}

YieldBlock signals an explicit end-of-turn from the provider.

Directories

Path Synopsis
Package auth implements the capability-token model: claim set, internal JWT-like encoding (no third-party dep), revocation list, and the issuance flow with TTY/notification confirmation.
Package auth implements the capability-token model: claim set, internal JWT-like encoding (no third-party dep), revocation list, and the issuance flow with TTY/notification confirmation.
Package conformance is the cross-transport parity test framework.
Package conformance is the cross-transport parity test framework.
Package inproc is part of the GoFastr harness.
Package inproc is part of the GoFastr harness.
Package mcpserver will expose the harness engine as an MCP server.
Package mcpserver will expose the harness engine as an MCP server.
Package multiplex is part of the GoFastr harness.
Package multiplex is part of the GoFastr harness.
Package resources is part of the GoFastr harness.
Package resources is part of the GoFastr harness.
Package rest is part of the GoFastr harness.
Package rest is part of the GoFastr harness.
Package ws will implement the WebSocket transport for the control plane.
Package ws will implement the WebSocket transport for the control plane.

Jump to

Keyboard shortcuts

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