goal

package
v0.0.2 Latest Latest
Warning

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

Go to latest
Published: Sep 9, 2026 License: Apache-2.0 Imports: 24 Imported by: 0

Documentation

Overview

Package goal provides durable, workspace-isolated goal lifecycle storage.

Index

Constants

View Source
const (
	CreateToolName = "create_goal"
	GetToolName    = "get_goal"
	UpdateToolName = "update_goal"
)
View Source
const ContinuationEnvelopeSchema = "agent-harness.goal-continuation.v1"
View Source
const DefaultVerifierFailureStreak = 3

DefaultVerifierFailureStreak is the number of consecutive verifier errors tolerated before a goal blocks.

View Source
const GoalContinuationMetaKey = "goal_continuation"

Variables

View Source
var (
	ErrNoGoal            = errors.New("goal: no matching goal")
	ErrGoalExists        = errors.New("goal: generated goal ID already exists")
	ErrOpenGoalExists    = errors.New("goal: an open goal already exists")
	ErrMultipleOpenGoals = errors.New("goal: multiple open goals violate the session invariant")
	ErrInvalidTransition = errors.New("goal: invalid lifecycle transition")
)
View Source
var ErrCASRetryLimit = errors.New("goal: distributed lifecycle update retry limit exceeded")
View Source
var ErrCorruptLedger = errors.New("goal: corrupt continuation ledger")
View Source
var ErrCorruptStore = errors.New("goal: corrupt lifecycle store")
View Source
var ErrLedgerCASRetryLimit = errors.New("goal: distributed continuation ledger retry limit exceeded")

Functions

func IsContinuationMessage

func IsContinuationMessage(message protocol.ChatMessage) bool

IsContinuationMessage identifies the trusted internal envelope stamped by Runtime. Transport adapters still validate their durable task payload and metadata before admitting it to ingress.

func MarshalContinuationEnvelope

func MarshalContinuationEnvelope(admission ContinuationAdmission) ([]byte, error)

func RegisterTools

func RegisterTools(registry *tools.Registry, service *Service) error

RegisterTools exposes the portable goal service to the model. Creation is deliberately described as user-directed; calling the tool does not grant new authority beyond the current turn.

Types

type AtomicStore

type AtomicStore interface {
	Store
	MutateGoals(ctx context.Context, workspaceID, sessionID string, mutate Mutation) error
	AccountGoalUsage(ctx context.Context, workspaceID, sessionID, goalID, assistantMessageID string, tokenDelta uint64, updatedAt string) (spec.SessionGoalRecord, bool, error)
}

AtomicStore is the additive mutation surface used by the goal service. The original Store remains sufficient for read-only snapshot projection and compatibility with external backends.

type BoundedPolicy

type BoundedPolicy struct {
	MaxContinuations uint32
}

BoundedPolicy allows an initial goal turn plus at most MaxContinuations automatic follow-ups. A zero maximum disables follow-ups without changing an otherwise active goal; hosts normally configure a small positive bound.

func (BoundedPolicy) Decide

type CASLedger

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

func NewCASLedger

func NewCASLedger(config CASLedgerConfig) (*CASLedger, error)

func (*CASLedger) Append

func (l *CASLedger) Append(ctx context.Context, workspaceID, sessionID string, record DecisionRecord) (DecisionRecord, error)

func (*CASLedger) AppendFirstDecision

func (l *CASLedger) AppendFirstDecision(ctx context.Context, workspaceID, sessionID string, record DecisionRecord) (DecisionRecord, bool, error)

func (*CASLedger) List

func (l *CASLedger) List(ctx context.Context, workspaceID, sessionID string) ([]DecisionRecord, error)

type CASLedgerConfig

type CASLedgerConfig struct {
	Blobs      casblob.Store
	Prefix     string
	MaxRetries int
	Now        func() time.Time
}

type CASStore

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

CASStore persists one complete goal projection per workspace/session and uses optimistic compare-and-swap so independent agent processes cannot overwrite concurrent goal updates.

func NewCASStore

func NewCASStore(config CASStoreConfig) (*CASStore, error)

func (*CASStore) AccountGoalUsage

func (s *CASStore) AccountGoalUsage(ctx context.Context, workspaceID, sessionID, goalID, assistantMessageID string, tokenDelta uint64, updatedAt string) (spec.SessionGoalRecord, bool, error)

func (*CASStore) ListGoals

func (s *CASStore) ListGoals(ctx context.Context, workspaceID, sessionID string) ([]spec.SessionGoalRecord, error)

func (*CASStore) MutateGoals

func (s *CASStore) MutateGoals(ctx context.Context, workspaceID, sessionID string, mutate Mutation) error

MutateGoals applies one optimistic CAS mutation. The callback may run more than once when another replica wins the compare-and-swap.

func (*CASStore) PutGoal

func (s *CASStore) PutGoal(ctx context.Context, workspaceID, sessionID string, record spec.SessionGoalRecord) error

type CASStoreConfig

type CASStoreConfig struct {
	Blobs      casblob.Store
	Prefix     string
	MaxRetries int
}

type ContinuationAdmission

type ContinuationAdmission struct {
	WorkspaceID     string
	SessionID       string
	GoalID          string
	ParentTaskID    string
	ParentMessageID string
	Attempt         uint32
	Inbound         channel.Inbound
	Authority       tools.MemoryAuthority
}

ContinuationAdmission is the immutable request handed to a durable delivery backend. Authority is intentionally separate from Envelope: adapters may persist it in a typed, task-scoped authority field, but it must never be serialized into the portable payload or the private decision ledger.

func (ContinuationAdmission) Validate

func (a ContinuationAdmission) Validate() error

type ContinuationBackend

type ContinuationBackend interface {
	Admit(ctx context.Context, admission ContinuationAdmission) (ContinuationReceipt, error)
	Inspect(ctx context.Context, receipt ContinuationReceipt) (ContinuationState, error)
}

ContinuationBackend makes a host-created follow-up durable. Admit must be idempotent for the admission identity. Inspect is authoritative and does not mutate task state.

type ContinuationEnvelope

type ContinuationEnvelope struct {
	Schema          string          `json:"schema"`
	WorkspaceID     string          `json:"workspace_id"`
	SessionID       string          `json:"session_id"`
	GoalID          string          `json:"goal_id"`
	ParentTaskID    string          `json:"parent_task_id,omitempty"`
	ParentMessageID string          `json:"parent_message_id"`
	Attempt         uint32          `json:"attempt"`
	Inbound         channel.Inbound `json:"inbound"`
}

ContinuationEnvelope is the versioned, credential-free payload a durable backend stores and later reconstructs at assignment time.

func NewContinuationEnvelope

func NewContinuationEnvelope(admission ContinuationAdmission) (ContinuationEnvelope, error)

func ParseContinuationEnvelope

func ParseContinuationEnvelope(payload []byte) (ContinuationEnvelope, error)

func (ContinuationEnvelope) Clone

Clone returns an independently mutable copy suitable for ledger reads.

func (ContinuationEnvelope) Validate

func (e ContinuationEnvelope) Validate() error

type ContinuationLedger

type ContinuationLedger interface {
	Append(ctx context.Context, workspaceID, sessionID string, record DecisionRecord) (DecisionRecord, error)
	// AppendFirstDecision atomically admits the first decision for one
	// goal/assistant-turn pair. It returns the existing decision and false when
	// another process already decided that turn. Follow-up audit records for an
	// admitted plan use Append.
	AppendFirstDecision(ctx context.Context, workspaceID, sessionID string, record DecisionRecord) (DecisionRecord, bool, error)
	List(ctx context.Context, workspaceID, sessionID string) ([]DecisionRecord, error)
}

type ContinuationPolicy

type ContinuationPolicy interface {
	Decide(ctx context.Context, input PolicyInput) PolicyDecision
}

type ContinuationReceipt

type ContinuationReceipt struct {
	Backend string
	TaskID  string
}

ContinuationReceipt identifies one authoritative backend delivery.

func (ContinuationReceipt) Validate

func (r ContinuationReceipt) Validate() error

type ContinuationState

type ContinuationState string
const (
	ContinuationAdmitted  ContinuationState = "admitted"
	ContinuationRunning   ContinuationState = "running"
	ContinuationCompleted ContinuationState = "completed"
	ContinuationFailed    ContinuationState = "failed"
	ContinuationCancelled ContinuationState = "cancelled"
)

type CreateInput

type CreateInput struct {
	Objective   string
	TokenBudget *uint64
}

type DecisionAction

type DecisionAction string
const (
	DecisionContinuationPlanned  DecisionAction = "continuation_planned"
	DecisionContinuationAdmitted DecisionAction = "continuation_admitted"
	DecisionContinuationEnqueued DecisionAction = "continuation_enqueued"
	DecisionStopped              DecisionAction = "stopped"
	DecisionCompleted            DecisionAction = "completed"
)

type DecisionReason

type DecisionReason string
const (
	ReasonActiveGoal        DecisionReason = "active_goal"
	ReasonVerifierRevision  DecisionReason = "verifier_revision"
	ReasonVerifierSatisfied DecisionReason = "verifier_satisfied"
	ReasonVerifierFailed    DecisionReason = "verifier_failed"
	ReasonTokenBudget       DecisionReason = "token_budget"
	// ReasonTokenBudgetWrapUp is the single final round granted when the token
	// budget runs out: the goal is not cut off mid-thought, it is asked to land.
	// Exactly one such round is ever planned per goal, after which the budget
	// blocks under ReasonTokenBudget.
	ReasonTokenBudgetWrapUp  DecisionReason = "token_budget_wrap_up"
	ReasonMaxContinuations   DecisionReason = "max_continuations"
	ReasonGoalTerminal       DecisionReason = "goal_terminal"
	ReasonEnqueueUnavailable DecisionReason = "enqueue_unavailable"
	ReasonEnqueueFailed      DecisionReason = "enqueue_failed"
	ReasonAdmissionFailed    DecisionReason = "admission_failed"
)

type DecisionRecord

type DecisionRecord struct {
	SchemaVersion     uint32         `json:"schema_version"`
	Sequence          uint64         `json:"sequence"`
	WorkspaceID       string         `json:"workspace_id"`
	SessionID         string         `json:"session_id"`
	GoalID            string         `json:"goal_id"`
	TurnMessageID     string         `json:"turn_message_id"`
	Action            DecisionAction `json:"action"`
	Reason            DecisionReason `json:"reason"`
	Detail            string         `json:"detail,omitempty"`
	CreatedAt         string         `json:"created_at"`
	TokenUsage        uint64         `json:"token_usage,omitempty"`
	TokenBudget       *uint64        `json:"token_budget,omitempty"`
	ContinuationsUsed uint32         `json:"continuations_used,omitempty"`
	Evidence          []string       `json:"evidence,omitempty"`
	// ContinuationMessageID and ContinuationRequestID are minted before the
	// plan is committed. Backend and task identity appear only after durable
	// admission succeeds. None of these fields carries task authority.
	ContinuationMessageID string `json:"continuation_message_id,omitempty"`
	ContinuationRequestID string `json:"continuation_request_id,omitempty"`
	ContinuationBackend   string `json:"continuation_backend,omitempty"`
	ContinuationTaskID    string `json:"continuation_task_id,omitempty"`
	// Continuation is stored once on the planned record so a durable backend
	// can reconstruct credential-free ingress after a process restart even
	// when its task API does not expose the original assignment payload.
	// Authority is deliberately absent from ContinuationEnvelope.
	Continuation *ContinuationEnvelope `json:"continuation,omitempty"`
}

DecisionRecord is one immutable explanation of why goal execution continued or stopped. Sequence is assigned atomically by the ledger.

type FileLedger

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

func NewFileLedger

func NewFileLedger(stateDir string, now func() time.Time) (*FileLedger, error)

func (*FileLedger) Append

func (l *FileLedger) Append(ctx context.Context, workspaceID, sessionID string, record DecisionRecord) (DecisionRecord, error)

func (*FileLedger) AppendFirstDecision

func (l *FileLedger) AppendFirstDecision(ctx context.Context, workspaceID, sessionID string, record DecisionRecord) (DecisionRecord, bool, error)

func (*FileLedger) List

func (l *FileLedger) List(ctx context.Context, workspaceID, sessionID string) ([]DecisionRecord, error)

type FileStore

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

FileStore stores one goal set per workspace/session. It is concurrency-safe within one process; a state directory has one writing process.

func NewFileStore

func NewFileStore(stateDir string) (*FileStore, error)

func (*FileStore) AccountGoalUsage

func (s *FileStore) AccountGoalUsage(ctx context.Context, workspaceID, sessionID, goalID, assistantMessageID string, tokenDelta uint64, updatedAt string) (spec.SessionGoalRecord, bool, error)

AccountGoalUsage atomically charges one assistant message at most once. The message IDs remain private store metadata rather than leaking through the portable SessionGoalRecord projection.

func (*FileStore) ListGoals

func (s *FileStore) ListGoals(ctx context.Context, workspaceID, sessionID string) ([]spec.SessionGoalRecord, error)

ListGoals returns a deterministic deep copy for snapshot projection.

func (*FileStore) MutateGoals

func (s *FileStore) MutateGoals(ctx context.Context, workspaceID, sessionID string, mutate Mutation) error

MutateGoals applies one atomic projection mutation under the file store's process lock and persists it only after the complete state validates.

func (*FileStore) PutGoal

func (s *FileStore) PutGoal(ctx context.Context, workspaceID, sessionID string, record spec.SessionGoalRecord) error

PutGoal atomically creates or replaces one stable goal record.

type Mutation

type Mutation func(state *spec.SessionGoalsState) error

Mutation updates one complete goal projection atomically. Implementations may retry the callback after an optimistic-concurrency conflict, so it must not perform external side effects.

type PolicyDecision

type PolicyDecision struct {
	Continue bool
	Block    bool
	Reason   DecisionReason
	Detail   string
}

type PolicyInput

type PolicyInput struct {
	Goal              spec.SessionGoalRecord
	ContinuationsUsed uint32
	Verification      *VerificationResult
	// WrapUpUsed reports that this goal has already been granted its one
	// budget-exhaustion wrap-up round, so the next budget check must block
	// rather than grant another.
	WrapUpUsed bool
}

type Runtime

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

Runtime connects durable goals to host-owned turn continuation. It is safe to share across concurrently served workspaces and sessions.

func NewRuntime

func NewRuntime(config RuntimeConfig) (*Runtime, error)

func (*Runtime) AfterTurn

func (r *Runtime) AfterTurn(ctx context.Context, addr protocol.MessageAddress, transcript []protocol.ChatMessage, assistant protocol.ChatMessage) (handled bool, err error)

AfterTurn accounts the finalized assistant message and, when policy permits, durably plans then enqueues one follow-up. handled reports that the turn was associated with a goal, allowing the runner to suppress a second rubric loop.

func (*Runtime) BeginTurn

func (r *Runtime) BeginTurn(ctx context.Context, addr protocol.MessageAddress) (context.Context, error)

BeginTurn resolves the active goal after the runner has resolved the session ID. The returned context lets goal tools and final accounting share the same identity even in legacy mode, whose transcript address leaves workspace empty.

func (*Runtime) Decisions

func (r *Runtime) Decisions(ctx context.Context, workspaceID, sessionID string) ([]DecisionRecord, error)

Decisions exposes the private append-only execution audit for operator and embedding surfaces. It is not part of the client/session wire protocol.

func (*Runtime) InspectDecision

func (r *Runtime) InspectDecision(ctx context.Context, record DecisionRecord) (ContinuationState, error)

InspectDecision resolves a ledgered backend receipt to authoritative task state. Local queue records have no backend receipt and cannot be inspected through this method.

func (*Runtime) SetVerifier

func (r *Runtime) SetVerifier(verifier Verifier)

SetVerifier connects an optional verifier after construction. NewRunner uses this to share its configured RubricVerifier with goal continuation instead of launching a competing rubric retry loop.

type RuntimeConfig

type RuntimeConfig struct {
	Service             *Service
	Ledger              ContinuationLedger
	Policy              ContinuationPolicy
	ContinuationBackend ContinuationBackend
	Enqueuer            channel.Enqueuer
	Verifier            Verifier
	AuthHandoff         *authhandoff.Store
	DefaultWorkspaceID  string
	NewID               func(prefix string) (string, error)
	// VerifierFailureStreak is how many CONSECUTIVE verifier errors must occur
	// before a goal is blocked. 0 uses DefaultVerifierFailureStreak; 1 restores
	// the original block-on-first-error behavior.
	//
	// A verifier error is fail-closed by design, but failing closed on a single
	// error conflates "this goal cannot succeed" with "the verifier had a bad
	// minute" — a network blip or a model hiccup would permanently kill a goal
	// that was making progress. Requiring the condition to persist keeps the
	// fail-closed guarantee for real faults and drops it for transient ones.
	VerifierFailureStreak uint32
}

type Service

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

Service is the portable host API shared by model-facing tools and runtime continuation accounting. Mutations require an AtomicStore so one session cannot acquire competing open goals under concurrent replicas.

func NewService

func NewService(config ServiceConfig) (*Service, error)

func (*Service) AccountUsage

func (s *Service) AccountUsage(ctx context.Context, workspaceID, sessionID, goalID, assistantMessageID string, tokenDelta uint64) (spec.SessionGoalRecord, bool, error)

AccountUsage charges one finalized assistant message at most once, including a turn that explicitly completed the goal before its terminal response.

func (*Service) CreateGoal

func (s *Service) CreateGoal(ctx context.Context, workspaceID, sessionID string, input CreateInput) (spec.SessionGoalRecord, error)

CreateGoal creates the one open goal allowed in a workspace/session. Terminal historical goals remain available through ListGoals.

func (*Service) CurrentGoal

func (s *Service) CurrentGoal(ctx context.Context, workspaceID, sessionID string) (*spec.SessionGoalRecord, error)

CurrentGoal returns the unique open goal, or the most recently updated terminal goal when the session has no open work. A nil record means the session has never had a goal.

func (*Service) Goal

func (s *Service) Goal(ctx context.Context, workspaceID, sessionID, goalID string) (spec.SessionGoalRecord, error)

Goal returns a goal by stable ID.

func (*Service) ListGoals

func (s *Service) ListGoals(ctx context.Context, workspaceID, sessionID string) ([]spec.SessionGoalRecord, error)

ListGoals returns the durable deterministic history for one session.

func (*Service) OpenGoal

func (s *Service) OpenGoal(ctx context.Context, workspaceID, sessionID string) (*spec.SessionGoalRecord, error)

OpenGoal returns the unique non-terminal goal, if any.

func (*Service) UpdateGoal

func (s *Service) UpdateGoal(ctx context.Context, workspaceID, sessionID string, input UpdateInput) (spec.SessionGoalRecord, error)

UpdateGoal applies a validated explicit lifecycle transition. Objective, budget, and usage are host-owned after creation.

type ServiceConfig

type ServiceConfig struct {
	Store Store
	Now   func() time.Time
	NewID func() (string, error)
}

type Store

type Store interface {
	PutGoal(ctx context.Context, workspaceID, sessionID string, record spec.SessionGoalRecord) error
	ListGoals(ctx context.Context, workspaceID, sessionID string) ([]spec.SessionGoalRecord, error)
}

Store is the typed durable goal surface used by hosts and session snapshots.

type UpdateInput

type UpdateInput struct {
	ID            string
	Status        spec.SessionGoalStatus
	Evidence      []string
	BlockedReason string
}

type VerificationRequest

type VerificationRequest struct {
	Goal       spec.SessionGoalRecord
	Transcript []protocol.ChatMessage
}

type VerificationResult

type VerificationResult struct {
	Satisfied      bool
	Feedback       string
	FailedCriteria []string
	Evidence       []string
}

type Verifier

type Verifier interface {
	VerifyGoal(ctx context.Context, request VerificationRequest) (VerificationResult, error)
}

Verifier is an optional, backend-neutral quality check. A verifier error is a fail-closed continuation outcome, never an implicit pass.

Jump to

Keyboard shortcuts

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