Documentation
¶
Overview ¶
Package goal provides durable, workspace-isolated goal lifecycle storage.
Index ¶
- Constants
- Variables
- func IsContinuationMessage(message protocol.ChatMessage) bool
- func MarshalContinuationEnvelope(admission ContinuationAdmission) ([]byte, error)
- func RegisterTools(registry *tools.Registry, service *Service) error
- type AtomicStore
- type BoundedPolicy
- type CASLedger
- func (l *CASLedger) Append(ctx context.Context, workspaceID, sessionID string, record DecisionRecord) (DecisionRecord, error)
- func (l *CASLedger) AppendFirstDecision(ctx context.Context, workspaceID, sessionID string, record DecisionRecord) (DecisionRecord, bool, error)
- func (l *CASLedger) List(ctx context.Context, workspaceID, sessionID string) ([]DecisionRecord, error)
- type CASLedgerConfig
- type CASStore
- func (s *CASStore) AccountGoalUsage(ctx context.Context, workspaceID, sessionID, goalID, assistantMessageID string, ...) (spec.SessionGoalRecord, bool, error)
- func (s *CASStore) ListGoals(ctx context.Context, workspaceID, sessionID string) ([]spec.SessionGoalRecord, error)
- func (s *CASStore) MutateGoals(ctx context.Context, workspaceID, sessionID string, mutate Mutation) error
- func (s *CASStore) PutGoal(ctx context.Context, workspaceID, sessionID string, ...) error
- type CASStoreConfig
- type ContinuationAdmission
- type ContinuationBackend
- type ContinuationEnvelope
- type ContinuationLedger
- type ContinuationPolicy
- type ContinuationReceipt
- type ContinuationState
- type CreateInput
- type DecisionAction
- type DecisionReason
- type DecisionRecord
- type FileLedger
- func (l *FileLedger) Append(ctx context.Context, workspaceID, sessionID string, record DecisionRecord) (DecisionRecord, error)
- func (l *FileLedger) AppendFirstDecision(ctx context.Context, workspaceID, sessionID string, record DecisionRecord) (DecisionRecord, bool, error)
- func (l *FileLedger) List(ctx context.Context, workspaceID, sessionID string) ([]DecisionRecord, error)
- type FileStore
- func (s *FileStore) AccountGoalUsage(ctx context.Context, workspaceID, sessionID, goalID, assistantMessageID string, ...) (spec.SessionGoalRecord, bool, error)
- func (s *FileStore) ListGoals(ctx context.Context, workspaceID, sessionID string) ([]spec.SessionGoalRecord, error)
- func (s *FileStore) MutateGoals(ctx context.Context, workspaceID, sessionID string, mutate Mutation) error
- func (s *FileStore) PutGoal(ctx context.Context, workspaceID, sessionID string, ...) error
- type Mutation
- type PolicyDecision
- type PolicyInput
- type Runtime
- func (r *Runtime) AfterTurn(ctx context.Context, addr protocol.MessageAddress, ...) (handled bool, err error)
- func (r *Runtime) BeginTurn(ctx context.Context, addr protocol.MessageAddress) (context.Context, error)
- func (r *Runtime) Decisions(ctx context.Context, workspaceID, sessionID string) ([]DecisionRecord, error)
- func (r *Runtime) InspectDecision(ctx context.Context, record DecisionRecord) (ContinuationState, error)
- func (r *Runtime) SetVerifier(verifier Verifier)
- type RuntimeConfig
- type Service
- func (s *Service) AccountUsage(ctx context.Context, workspaceID, sessionID, goalID, assistantMessageID string, ...) (spec.SessionGoalRecord, bool, error)
- func (s *Service) CreateGoal(ctx context.Context, workspaceID, sessionID string, input CreateInput) (spec.SessionGoalRecord, error)
- func (s *Service) CurrentGoal(ctx context.Context, workspaceID, sessionID string) (*spec.SessionGoalRecord, error)
- func (s *Service) Goal(ctx context.Context, workspaceID, sessionID, goalID string) (spec.SessionGoalRecord, error)
- func (s *Service) ListGoals(ctx context.Context, workspaceID, sessionID string) ([]spec.SessionGoalRecord, error)
- func (s *Service) OpenGoal(ctx context.Context, workspaceID, sessionID string) (*spec.SessionGoalRecord, error)
- func (s *Service) UpdateGoal(ctx context.Context, workspaceID, sessionID string, input UpdateInput) (spec.SessionGoalRecord, error)
- type ServiceConfig
- type Store
- type UpdateInput
- type VerificationRequest
- type VerificationResult
- type Verifier
Constants ¶
const ( CreateToolName = "create_goal" GetToolName = "get_goal" UpdateToolName = "update_goal" )
const ContinuationEnvelopeSchema = "agent-harness.goal-continuation.v1"
const DefaultVerifierFailureStreak = 3
DefaultVerifierFailureStreak is the number of consecutive verifier errors tolerated before a goal blocks.
const GoalContinuationMetaKey = "goal_continuation"
Variables ¶
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") )
var ErrCASRetryLimit = errors.New("goal: distributed lifecycle update retry limit exceeded")
var ErrCorruptLedger = errors.New("goal: corrupt continuation ledger")
var ErrCorruptStore = errors.New("goal: corrupt lifecycle store")
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)
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 ¶
func (p BoundedPolicy) Decide(_ context.Context, input PolicyInput) PolicyDecision
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)
type CASLedgerConfig ¶
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 ¶
type CASStoreConfig ¶
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 ¶
func (e ContinuationEnvelope) Clone() ContinuationEnvelope
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 ¶
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 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" 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 (*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.
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 ¶
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 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 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.