Documentation
¶
Overview ¶
Package workflow defines types and logic for PromptPack workflow state machines (RFC 0005).
A workflow is an event-driven state machine layered over a PromptPack's prompts. Each state references a prompt_task and defines transitions via named events.
Index ¶
- Constants
- Variables
- func ArtifactMode(def *ArtifactDef) string
- func BuildArtifactToolDescriptor(spec *Spec) *tools.ToolDescriptor
- func BuildTransitionToolDescriptor(events []string) *tools.ToolDescriptor
- func IsTerminal(s *State) bool
- func MaxVisitsOf(s *State) int
- func OrchestrationOf(s *State) string
- func RegisterArtifactTool(registry *tools.Registry, spec *Spec)
- func RegisterTransitionTool(registry *tools.Registry, state *State)
- func SortedEvents(onEvent map[string]string) []string
- type ArtifactDef
- type ArtifactExecutor
- type ArtifactSnapshot
- type Budget
- type BudgetExhaustedError
- type Context
- func (ctx *Context) Clone() *Context
- func (ctx *Context) GetArtifact(name string) string
- func (ctx *Context) IncrementToolCalls(n int)
- func (ctx *Context) LastTransition() *StateTransition
- func (ctx *Context) RecordTransition(from, to, event string, ts time.Time)
- func (ctx *Context) SetArtifact(name, value, mode string)
- func (ctx *Context) TotalVisits() int
- func (ctx *Context) TransitionCount() int
- type MaxVisitsExceededError
- type Orchestration
- type PendingTransition
- type Persistence
- type Spec
- type State
- type StateMachine
- func (sm *StateMachine) Artifacts() map[string]string
- func (sm *StateMachine) AvailableEvents() []string
- func (sm *StateMachine) Context() *Context
- func (sm *StateMachine) CurrentPromptTask() string
- func (sm *StateMachine) CurrentState() string
- func (sm *StateMachine) IncrementToolCalls(n int)
- func (sm *StateMachine) IsTerminal() bool
- func (sm *StateMachine) ProcessEvent(event string) (*TransitionResult, error)
- func (sm *StateMachine) SetArtifact(name, value string)
- func (sm *StateMachine) WithTimeFunc(fn TimeFunc) *StateMachine
- type StateTransition
- type TimeFunc
- type TransitionExecutor
- func (e *TransitionExecutor) ClearPending()
- func (e *TransitionExecutor) CommitPending() (*TransitionResult, error)
- func (e *TransitionExecutor) Execute(_ context.Context, _ *tools.ToolDescriptor, args json.RawMessage) (json.RawMessage, error)
- func (e *TransitionExecutor) Name() string
- func (e *TransitionExecutor) Pending() *PendingTransition
- func (e *TransitionExecutor) RegisterForState(registry *tools.Registry, state *State)
- func (e *TransitionExecutor) SetOnCommit(fn func(*TransitionResult))
- func (e *TransitionExecutor) SetOnCommitError(fn func(event string, err error))
- func (e *TransitionExecutor) StateMachine() *StateMachine
- type TransitionResult
- type ValidationResult
Constants ¶
const ( BudgetLimitTotalVisits = "max_total_visits" BudgetLimitToolCalls = "max_tool_calls" BudgetLimitWallTimeSec = "max_wall_time_sec" )
Budget limit names, used by BudgetExhaustedError.Limit.
const ( ArtifactModeReplace = "replace" ArtifactModeAppend = "append" )
Artifact merge modes (schema $defs/ArtifactDef.mode).
const ( PersistenceTransient = "transient" PersistencePersistent = "persistent" )
Persistence values.
const ( OrchestrationInternal = "internal" OrchestrationExternal = "external" OrchestrationHybrid = "hybrid" // OrchestrationComposition runs a declarative composition step-graph for the // state instead of an LLM-driven turn (RFC 0010). OrchestrationComposition = "composition" )
Orchestration values.
const ArtifactExecutorMode = "workflow-artifact"
ArtifactExecutorMode is the executor name for Mode-based routing.
const ArtifactToolName = "workflow__set_artifact"
ArtifactToolName is the qualified name of the workflow set_artifact tool.
const MaxHistoryLength = 1000
MaxHistoryLength is the maximum number of state transitions retained in context history. When exceeded, the oldest transitions are discarded.
const TransitionExecutorMode = "workflow-transition"
TransitionExecutorMode is the executor name used for Mode-based routing.
const TransitionToolName = "workflow__transition"
TransitionToolName is the qualified name of the workflow transition tool.
Variables ¶
var ( // ErrInvalidEvent is returned when an event is not defined for the current state. ErrInvalidEvent = errors.New("invalid event for current state") // ErrTerminalState is returned when trying to process an event in a terminal state. ErrTerminalState = errors.New("current state is terminal (no outgoing transitions)") )
var ( // ErrMaxVisitsExceeded is returned when a state's max_visits limit is reached // and no on_max_visits fallback is configured. The concrete error returned // is typically a *MaxVisitsExceededError wrapping this sentinel; callers // wanting structured details should use errors.As. ErrMaxVisitsExceeded = errors.New("max visits exceeded") // ErrBudgetExhausted is returned when a workflow-level budget limit is // reached. The concrete error returned is typically a *BudgetExhaustedError // wrapping this sentinel; callers wanting structured details should use // errors.As. ErrBudgetExhausted = errors.New("workflow budget exhausted") )
Sentinel errors for workflow execution.
Functions ¶
func ArtifactMode ¶ added in v1.8.0
func ArtifactMode(def *ArtifactDef) string
ArtifactMode returns an artifact slot's merge mode, applying the spec default of "replace" when the pack did not set one.
func BuildArtifactToolDescriptor ¶ added in v1.3.24
func BuildArtifactToolDescriptor(spec *Spec) *tools.ToolDescriptor
BuildArtifactToolDescriptor creates a tools.ToolDescriptor for the workflow__set_artifact tool, with artifact names as an enum constraint.
func BuildTransitionToolDescriptor ¶
func BuildTransitionToolDescriptor(events []string) *tools.ToolDescriptor
BuildTransitionToolDescriptor creates a tools.ToolDescriptor for the workflow__transition tool with the given events as an enum constraint.
func IsTerminal ¶ added in v1.8.0
IsTerminal reports whether a state ends the workflow.
terminal is *bool on the generated type because the spec makes it optional, and absent means "not terminal" rather than "unset". A state with no outgoing events is terminal regardless of the flag.
func MaxVisitsOf ¶ added in v1.8.0
MaxVisitsOf returns a state's visit cap, or 0 when uncapped.
func OrchestrationOf ¶ added in v1.8.0
OrchestrationOf returns a state's control mode, defaulting to OrchestrationInternal when the state does not declare one.
func RegisterArtifactTool ¶ added in v1.3.24
RegisterArtifactTool registers the workflow__set_artifact tool in the given registry. Only registers if the spec declares artifacts on any state.
func RegisterTransitionTool ¶ added in v1.3.20
RegisterTransitionTool registers the workflow__transition tool in the given registry for the specified state's available events. Skips terminal states (no events) and externally orchestrated states.
Both the SDK (WorkflowCapability) and Arena (engine) use this function.
func SortedEvents ¶
SortedEvents returns a sorted copy of the event keys from an OnEvent map.
Types ¶
type ArtifactDef ¶ added in v1.3.24
type ArtifactDef = packspec.ArtifactDef
ArtifactDef declares a named artifact slot on a workflow state.
Generated from the schema: an ALIAS for packspec.ArtifactDef.
Mode is a *string, not a string: the spec defaults it to "replace", so "absent" and "explicitly empty" are different facts and a plain field would collapse them. Read it through ArtifactMode, which applies the default.
type ArtifactExecutor ¶ added in v1.3.24
type ArtifactExecutor struct {
// contains filtered or unexported fields
}
ArtifactExecutor implements tools.Executor for workflow__set_artifact. Unlike TransitionExecutor, artifact mutations are safe to apply immediately since they only modify the context map without closing/reopening conversations.
func NewArtifactExecutor ¶ added in v1.3.24
func NewArtifactExecutor(sm *StateMachine) *ArtifactExecutor
NewArtifactExecutor creates an ArtifactExecutor for the given state machine.
func (*ArtifactExecutor) Execute ¶ added in v1.3.24
func (e *ArtifactExecutor) Execute( _ context.Context, _ *tools.ToolDescriptor, args json.RawMessage, ) (json.RawMessage, error)
Execute implements tools.Executor. Sets the artifact value on the state machine.
func (*ArtifactExecutor) Name ¶ added in v1.3.24
func (e *ArtifactExecutor) Name() string
Name implements tools.Executor.
type ArtifactSnapshot ¶ added in v1.3.24
type ArtifactSnapshot struct {
FromState string `json:"from_state"`
ToState string `json:"to_state"`
Event string `json:"event"`
Values map[string]string `json:"values"`
Timestamp time.Time `json:"timestamp"`
}
ArtifactSnapshot captures artifact values at a specific state transition.
type Budget ¶ added in v1.3.24
type Budget = packspec.WorkflowBudget
Budget defines workflow-level resource limits from the engine block. Generated from the schema: an ALIAS for packspec.WorkflowBudget. Optional limits are pointers so "no limit" is distinct from a limit of zero.
type BudgetExhaustedError ¶ added in v1.4.5
type BudgetExhaustedError struct {
// Limit is one of BudgetLimitTotalVisits, BudgetLimitToolCalls,
// BudgetLimitWallTimeSec.
Limit string
// Current is the observed value at the time the limit was hit.
Current int
// Max is the configured limit.
Max int
// CurrentState is the state the workflow was in when the budget tripped.
CurrentState string
}
BudgetExhaustedError is the structured error returned from ProcessEvent when a workflow-level budget is reached. It wraps ErrBudgetExhausted so errors.Is still matches.
func (*BudgetExhaustedError) Error ¶ added in v1.4.5
func (e *BudgetExhaustedError) Error() string
Error returns a human-readable description.
func (*BudgetExhaustedError) Unwrap ¶ added in v1.4.5
func (e *BudgetExhaustedError) Unwrap() error
Unwrap returns the sentinel for errors.Is.
type Context ¶
type Context struct {
CurrentState string `json:"current_state"`
History []StateTransition `json:"history"`
Metadata map[string]any `json:"metadata,omitempty"`
VisitCounts map[string]int `json:"visit_counts,omitempty"` // RFC 0009: per-state visit counts
TotalToolCalls int `json:"total_tool_calls,omitempty"` // RFC 0009: workflow-wide tool call count
Artifacts map[string]string `json:"artifacts,omitempty"` // RFC 0009: current artifact values
ArtifactHistory []ArtifactSnapshot `json:"artifact_history,omitempty"` // RFC 0009: artifact values at each transition
StartedAt time.Time `json:"started_at"`
UpdatedAt time.Time `json:"updated_at"`
}
Context holds the runtime state of a workflow execution.
func NewContext ¶
NewContext creates a new Context initialized at the given entry state.
func (*Context) Clone ¶
Clone returns a deep copy of the Context. Nil collections on the source are preserved as nil on the clone so callers can distinguish "absent" from "present but empty" across the round-trip.
func (*Context) GetArtifact ¶ added in v1.3.24
GetArtifact returns an artifact value, or empty string if not set.
func (*Context) IncrementToolCalls ¶ added in v1.3.24
IncrementToolCalls adds n to the workflow-wide tool call counter.
func (*Context) LastTransition ¶
func (ctx *Context) LastTransition() *StateTransition
LastTransition returns the most recent transition, or nil if none.
func (*Context) RecordTransition ¶
RecordTransition records a state transition and updates the current state.
func (*Context) SetArtifact ¶ added in v1.3.24
SetArtifact sets an artifact value, respecting the mode (replace or append).
func (*Context) TotalVisits ¶ added in v1.3.24
TotalVisits returns the sum of all per-state visit counts.
func (*Context) TransitionCount ¶
TransitionCount returns the number of transitions recorded.
type MaxVisitsExceededError ¶ added in v1.4.5
type MaxVisitsExceededError struct {
// FromState is the state the transition was leaving.
FromState string
// OriginalTarget is the state whose max_visits was reached.
OriginalTarget string
// Event is the transition event that triggered the attempt.
Event string
// VisitCount is the number of times OriginalTarget had already been entered.
VisitCount int
// MaxVisits is the declared limit on OriginalTarget.
MaxVisits int
}
MaxVisitsExceededError is the structured error returned from ProcessEvent when a state has reached its max_visits cap and no on_max_visits fallback is configured. It wraps ErrMaxVisitsExceeded so errors.Is still matches.
func (*MaxVisitsExceededError) Error ¶ added in v1.4.5
func (e *MaxVisitsExceededError) Error() string
Error returns a human-readable description.
func (*MaxVisitsExceededError) Unwrap ¶ added in v1.4.5
func (e *MaxVisitsExceededError) Unwrap() error
Unwrap returns the sentinel for errors.Is.
type Orchestration ¶
type Orchestration string
Orchestration is the control mode for a workflow state.
type PendingTransition ¶ added in v1.3.24
type PendingTransition struct {
Event string `json:"event"`
ContextSummary string `json:"context"`
HostExtras tools.HostExtras `json:"host_extras,omitempty"`
}
PendingTransition captures a deferred workflow transition from a tool call.
type Spec ¶
type Spec = packspec.WorkflowConfig
Spec is a pack's workflow state-machine specification.
Generated. It was hand-written only because its states map held a hand-written State; it carries no methods of its own.
func ParseConfig ¶ added in v1.3.20
ParseConfig parses an untyped workflow config (typically from config.Workflow which is stored as interface{}) into a typed Spec. Returns nil, nil when raw is nil.
type State ¶
type State = packspec.WorkflowState
State is a single state within a workflow.
Generated. terminal, max_visits and orchestration are pointers on the generated type because the spec makes them optional — which is more correct than the values they replaced, since absent is now distinguishable from the zero. Use IsTerminal, MaxVisitsOf and OrchestrationOf rather than reading them directly; they resolve the documented default in one place.
type StateMachine ¶
type StateMachine struct {
// contains filtered or unexported fields
}
StateMachine manages workflow state transitions.
func NewStateMachine ¶
func NewStateMachine(spec *Spec) *StateMachine
NewStateMachine creates a state machine from a workflow spec. It initializes the context to the entry state.
func NewStateMachineFromContext ¶
func NewStateMachineFromContext(spec *Spec, ctx *Context) *StateMachine
NewStateMachineFromContext restores a state machine from persisted context.
func (*StateMachine) Artifacts ¶ added in v1.3.24
func (sm *StateMachine) Artifacts() map[string]string
Artifacts returns a snapshot of the current artifact values.
func (*StateMachine) AvailableEvents ¶
func (sm *StateMachine) AvailableEvents() []string
AvailableEvents returns the set of valid events for the current state, sorted.
func (*StateMachine) Context ¶
func (sm *StateMachine) Context() *Context
Context returns a snapshot of the current workflow context for persistence.
func (*StateMachine) CurrentPromptTask ¶
func (sm *StateMachine) CurrentPromptTask() string
CurrentPromptTask returns the prompt_task for the current state.
func (*StateMachine) CurrentState ¶
func (sm *StateMachine) CurrentState() string
CurrentState returns the name of the current state.
func (*StateMachine) IncrementToolCalls ¶ added in v1.3.24
func (sm *StateMachine) IncrementToolCalls(n int)
IncrementToolCalls adds n to the workflow-wide tool call counter. Thread-safe; intended to be called by the SDK after tool executions.
func (*StateMachine) IsTerminal ¶
func (sm *StateMachine) IsTerminal() bool
IsTerminal returns true if the current state is terminal. A state is terminal when explicitly marked (Terminal: true) or when it has no outgoing transitions (backward compatible).
func (*StateMachine) ProcessEvent ¶
func (sm *StateMachine) ProcessEvent(event string) (*TransitionResult, error)
ProcessEvent applies an event and transitions to the target state. Returns a TransitionResult describing the transition (including any max_visits redirect). Returns ErrMaxVisitsExceeded when the target state's visit limit is reached and no on_max_visits fallback is set. Returns ErrBudgetExhausted when a workflow-level budget limit is reached.
func (*StateMachine) SetArtifact ¶ added in v1.3.24
func (sm *StateMachine) SetArtifact(name, value string)
SetArtifact sets an artifact value on the workflow context. The mode is looked up from the spec's artifact definitions for the current state. Thread-safe.
func (*StateMachine) WithTimeFunc ¶
func (sm *StateMachine) WithTimeFunc(fn TimeFunc) *StateMachine
WithTimeFunc sets a custom time function for deterministic tests.
type StateTransition ¶
type StateTransition struct {
From string `json:"from"`
To string `json:"to"`
Event string `json:"event"`
Timestamp time.Time `json:"timestamp"`
}
StateTransition records a single state transition.
type TransitionExecutor ¶ added in v1.3.24
type TransitionExecutor struct {
// contains filtered or unexported fields
}
TransitionExecutor implements tools.Executor for workflow__transition.
It defers the state transition (ProcessEvent) until CommitPending is called, ensuring the full pipeline turn completes before state changes. The optional OnCommit callback is invoked after a successful commit so consumers (Arena, SDK) can run their post-commit work (re-register tool descriptor, update scenario TaskType, emit observability events) from a single hook.
func NewTransitionExecutor ¶ added in v1.3.24
func NewTransitionExecutor(sm *StateMachine, spec *Spec) *TransitionExecutor
NewTransitionExecutor creates a TransitionExecutor for the given state machine.
func (*TransitionExecutor) ClearPending ¶ added in v1.3.24
func (e *TransitionExecutor) ClearPending()
ClearPending discards any pending transition without committing.
func (*TransitionExecutor) CommitPending ¶ added in v1.3.24
func (e *TransitionExecutor) CommitPending() (*TransitionResult, error)
CommitPending applies the pending transition by calling ProcessEvent. Returns nil, nil if no transition is pending. After commit, the pending state is cleared. Thread-safe. Fires OnCommit on success.
func (*TransitionExecutor) Execute ¶ added in v1.3.24
func (e *TransitionExecutor) Execute( _ context.Context, _ *tools.ToolDescriptor, args json.RawMessage, ) (json.RawMessage, error)
Execute implements tools.Executor.
The request is stored as pending and CommitPending applies it after the pipeline turn finishes. This is RFC 0005's deferred-commit pattern.
The LLM's `context` argument is stored on the PendingTransition and surfaced to the new conversation as the `workflow_context` template variable when the consumer opens it.
func (*TransitionExecutor) Name ¶ added in v1.3.24
func (e *TransitionExecutor) Name() string
Name implements tools.Executor. Returns the mode name for registry routing.
func (*TransitionExecutor) Pending ¶ added in v1.3.24
func (e *TransitionExecutor) Pending() *PendingTransition
Pending returns the current pending transition, or nil if none.
func (*TransitionExecutor) RegisterForState ¶ added in v1.3.24
func (e *TransitionExecutor) RegisterForState(registry *tools.Registry, state *State)
RegisterForState registers the workflow__transition tool in the given registry for the specified state, with Mode set for executor routing.
When called with a terminal state (Terminal: true OR no on_event), the previous descriptor — if any — is removed from the registry instead of re-registered. Without this, the LLM would still see workflow__transition in the tool list after entering a terminal state and could call it with the previous state's now-stale events. Externally orchestrated states skip both register and unregister: the caller owns the descriptor's lifecycle.
func (*TransitionExecutor) SetOnCommit ¶ added in v1.4.9
func (e *TransitionExecutor) SetOnCommit(fn func(*TransitionResult))
SetOnCommit registers a callback fired after every successful commit. Pass nil to clear.
Callbacks run while the executor's internal lock is held; they must not re-enter the executor's public methods (Execute / CommitPending / etc.).
func (*TransitionExecutor) SetOnCommitError ¶ added in v1.4.9
func (e *TransitionExecutor) SetOnCommitError(fn func(event string, err error))
SetOnCommitError registers a callback fired when ProcessEvent fails during CommitPending. Consumers wire their workflow observability error emit (e.g., workflow.max_visits_exceeded, workflow.budget_exhausted) through this hook so deferred-commit failures are observable. Pass nil to clear.
Same locking contract as SetOnCommit: the callback runs while the executor's internal lock is held; do not re-enter the executor.
func (*TransitionExecutor) StateMachine ¶ added in v1.3.24
func (e *TransitionExecutor) StateMachine() *StateMachine
StateMachine returns the underlying state machine for metadata access.
type TransitionResult ¶ added in v1.3.24
type TransitionResult struct {
From string `json:"from"`
To string `json:"to"`
Event string `json:"event"`
Redirected bool `json:"redirected,omitempty"`
RedirectReason string `json:"redirect_reason,omitempty"`
OriginalTarget string `json:"original_target,omitempty"`
HostExtras tools.HostExtras `json:"host_extras,omitempty"`
}
TransitionResult is returned by ProcessEvent to communicate what happened. Redirects (e.g., max_visits exceeded → on_max_visits) are successful transitions, not errors.
type ValidationResult ¶
type ValidationResult struct {
Errors []string // Blocking: invalid references, missing fields
Warnings []string // Non-blocking: PascalCase violations, circular refs
}
ValidationResult holds errors and warnings from workflow validation.
func Validate ¶
func Validate(spec *Spec, promptKeys []string) *ValidationResult
Validate checks a Spec against the available prompt keys. It implements all 10 validation rules from RFC 0005.
func (*ValidationResult) HasErrors ¶
func (r *ValidationResult) HasErrors() bool
HasErrors returns true if there are blocking validation errors.