backgroundtask

package
v0.10.0-alpha.27 Latest Latest
Warning

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

Go to latest
Published: Aug 31, 2026 License: Apache-2.0 Imports: 17 Imported by: 0

Documentation

Overview

Package backgroundtask provides a shared lifecycle registry for long-running executions (sub-agents, shell commands, ...) that may outlive the tool call that launched them.

Manager coordinates TaskStore-backed submission, execution, and control. It is deliberately non-generic so one instance can serve heterogeneous executor domains under one task-ID space.

TaskEvent is append-only progress. Spec.OutputFile and Task.OutputFileErr describe an optional derived transcript projection; transcript failure never changes authoritative lifecycle status or replaces terminal ResultData.

Persistence providers should run the reusable suites in adk/backgroundtask/storetest before deployment.

Index

Constants

View Source
const SessionEventTaskCreated adk.SessionEventKind = "x.eino.background_task.created"

SessionEventTaskCreated is appended to a parent session after its background task has been durably created.

Variables

View Source
var (
	// ErrNotFound reports that a task or notification record does not exist.
	ErrNotFound = errors.New("backgroundtask: task not found")
	// ErrAlreadyExists reports that a task or registry entry already exists.
	ErrAlreadyExists = errors.New("backgroundtask: task already exists")
	// ErrVersionConflict reports that ExpectedVersion no longer matches the stored record.
	ErrVersionConflict = errors.New("backgroundtask: task version conflict")
	// ErrLeaseLost reports that an operation is no longer authorized by its lease.
	ErrLeaseLost = errors.New("backgroundtask: lease lost")
	// ErrIllegalTransition reports that a requested lifecycle transition is invalid.
	ErrIllegalTransition = errors.New("backgroundtask: illegal state transition")
	// ErrInvalidExecutionResult reports that an executor result or TaskStore
	// transition payload violates lifecycle result invariants.
	ErrInvalidExecutionResult = errors.New("backgroundtask: invalid execution result")
	// ErrAlreadyTerminal reports that a task has already reached a terminal status.
	ErrAlreadyTerminal = errors.New("backgroundtask: task is already terminal")
	// ErrDrainCheckpointUnavailable reports that a planned drain could not
	// produce or locate a safe compatible checkpoint. Manager stops renewing the
	// current lease so expiry can redispatch from the last durable checkpoint.
	ErrDrainCheckpointUnavailable = errors.New("backgroundtask: drain checkpoint unavailable")
	// ErrCloseDeadlineRequired reports that Manager.Close was called with active
	// attempts but its context has no deadline. The Manager remains open.
	ErrCloseDeadlineRequired = errors.New("backgroundtask: close deadline is required while tasks are active")
	// ErrUnsupportedExecutorPayloadVersion reports that the selected executor
	// cannot decode the version of the persisted Spec.Payload envelope.
	ErrUnsupportedExecutorPayloadVersion = errors.New("backgroundtask: unsupported executor payload version")
	// ErrTaskEventIDConflict reports that one task-local EventID was replayed
	// with bytes different from the originally persisted event.
	ErrTaskEventIDConflict = errors.New("backgroundtask: task event id conflict")
	// ErrInvalidCursor reports that a pagination cursor is malformed or cannot
	// continue the requested task-event snapshot and ordering.
	ErrInvalidCursor = errors.New("backgroundtask: invalid cursor")
	// ErrNotificationUnavailable reports that the current context or task store
	// cannot route an application notification to a parent session.
	ErrNotificationUnavailable = errors.New(
		"backgroundtask: parent notification unavailable",
	)
	// ErrNotificationEventIDConflict reports that a task-local notification
	// EventID was replayed with different Kind or Data.
	ErrNotificationEventIDConflict = errors.New(
		"backgroundtask: notification event id conflict",
	)
	// ErrTaskCreatedEventUndelivered reports that Submit durably created the
	// task but failed to send the immediate parent-session TaskCreated event.
	// The durable notification outbox remains authoritative for recovery, so
	// callers must treat ownership as transferred when this wraps the error.
	ErrTaskCreatedEventUndelivered = errors.New(
		"backgroundtask: immediate task-created event was not delivered",
	)
)

Functions

func NotifyParent

func NotifyParent(ctx context.Context, req *NotifyParentRequest) error

NotifyParent emits one idempotent application notification using authority bound to the current managed attempt context. It returns ErrNotificationUnavailable outside a managed attempt or when the configured TaskStore lacks NotificationWriter. Store errors are returned unchanged.

func TaskCreatedSessionEventID

func TaskCreatedSessionEventID(taskID string) string

TaskCreatedSessionEventID returns the deterministic session-local EventID for taskID. Immediate Runner emission and outbox-based recovery must use the same ID so TaskCreated delivery is idempotent.

func TaskCreatedSessionEventSender

func TaskCreatedSessionEventSender[M adk.MessageType]() func(context.Context, *Task) error

TaskCreatedSessionEventSender creates a Config.SendTaskCreatedEvent callback. It emits through the active ChatModelAgent run so Runner remains the sole writer of the parent session timeline.

Types

type AckCancelRequest

type AckCancelRequest struct {
	TaskID          string
	ExpectedVersion int64
	// Reason is used when no durable cancellation reason was previously recorded.
	Reason string
}

AckCancelRequest records active-attempt acknowledgement of cancellation.

type AllocateTaskIDRequest

type AllocateTaskIDRequest struct {
	Kind string
}

AllocateTaskIDRequest describes the task category used by the default ID generator. Kind is not persisted independently and must be empty or a 64-byte ASCII identifier segment containing letters, digits, '-' or '_'.

type AppendTaskEventRequest

type AppendTaskEventRequest struct {
	TaskID  string
	Attempt int64
	EventID string
	Data    []byte
}

AppendTaskEventRequest appends one identified progress event for the active task attempt. EventID uniqueness is task-wide across attempts.

type AppendTaskEventResult

type AppendTaskEventResult struct {
	Event    *TaskEvent
	Inserted bool
}

AppendTaskEventResult reports whether the event was newly inserted. A byte-identical replay returns the original Event with Inserted false.

type CloseOption

type CloseOption func(*closeOptions)

CloseOption configures Manager shutdown.

func WithDrainReason

func WithDrainReason(reason string) CloseOption

WithDrainReason attaches an optional advisory reason to drain controls sent while closing a Manager. The reason is not persisted as terminal task state.

type CommitStartRequest

type CommitStartRequest struct {
	TaskID          string
	ExpectedVersion int64
	Checkpoint      []byte
}

CommitStartRequest records that the external operation for the current running attempt was established and persists its initial checkpoint.

type CompleteTaskRequest

type CompleteTaskRequest struct {
	TaskID          string
	ExpectedVersion int64
	Data            []byte
}

CompleteTaskRequest records successful task completion.

type Config

type Config struct {
	// Tasks is the authoritative task lifecycle provider. When nil, New installs
	// an in-memory reference provider. Manager also discovers the optional
	// NotificationWriter capability from this provider.
	Tasks TaskStore
	// TaskEvents persists append-only progress in the same task namespace and
	// must fence appends against the active attempt authorized by Tasks. When
	// nil, New reuses Tasks when it also implements TaskEventStore. If both are
	// nil, the same in-memory reference provider supplies both capabilities.
	TaskEvents TaskEventStore
	// Executors resolves serialized task intent to local implementations.
	Executors *ExecutorRegistry
	// SendTaskCreatedEvent emits a TaskCreated timeline event after a task is
	// durably created. It may be called concurrently. Tasks without a parent
	// SessionID do not emit this event. Use TaskCreatedSessionEventSender so the
	// active Runner assigns and persists the event in causal turn order.
	SendTaskCreatedEvent func(context.Context, *Task) error
	// IDGen, when set, decides the full ID of every task created by this Manager.
	// If nil, Manager uses its default task-type-prefixed Base64URL ID.
	//
	// IDGen may be called concurrently by task submitters. It
	// must return a non-empty ID. The returned ID must be unique among this
	// Manager's registered tasks; a duplicate fails task creation.
	IDGen IDGenerator
	// ContextSnapshotter optionally persists deployment-selected context values
	// for background execution and later recovery. Snapshots are captured during
	// Submit, Resume, and ReleaseSuspension, then restored before task execution.
	ContextSnapshotter ContextSnapshotter
}

Config configures a Manager.

type ContextSnapshotter

type ContextSnapshotter interface {
	CaptureContext(context.Context) ([]byte, error)
	RestoreContext(context.Context, []byte) (context.Context, error)
}

ContextSnapshotter captures deployment-owned context values into an opaque byte snapshot and restores them for later execution attempts. Implementations decide which values are serializable and stable enough to persist. Every worker that may execute tasks carrying a snapshot must configure a semantically equivalent snapshotter; otherwise execution fails instead of silently dropping the snapshot.

type ControlKind

type ControlKind string

ControlKind identifies a Manager control signal sent to an executor.

const (

	// ControlStop asks the executor to stop as soon as practical. Reason is the
	// optional durable cancellation reason.
	ControlStop ControlKind = "stop"
	// ControlDrain asks the executor to relinquish gracefully. The executor may
	// checkpoint and suspend or yield without a checkpoint according to its
	// recovery model. Reason is optional advisory operational context.
	ControlDrain ControlKind = "drain"
	// ControlTimeout asks the executor to fail with a non-empty deterministic reason.
	ControlTimeout ControlKind = "timeout"
)

type ControlRequest

type ControlRequest struct {
	Kind   ControlKind
	Reason string
}

ControlRequest carries a Manager control signal to an executor. For ControlStop, Reason is optional and sourced from durable cancellation intent. It is optional and advisory for ControlDrain, and always non-empty for ControlTimeout.

type CreateTaskRequest

type CreateTaskRequest struct {
	Spec              Spec
	LeaseExpiryPolicy LeaseExpiryPolicy
	Checkpoint        []byte
	// ContextSnapshot is an opaque manager-owned snapshot of selected context
	// values captured at submission time.
	ContextSnapshot []byte
}

CreateTaskRequest creates a task from immutable serialized intent and the recovery policy owned by its registered Executor. TaskStore assigns all lifecycle timestamps, including Task.CreatedAt.

type ExecutionDirective

type ExecutionDirective string

ExecutionDirective is a non-lifecycle instruction returned by an Executor.

const (
	// ExecutionDirectiveYield relinquishes a recoverable active attempt while
	// the logical operation continues outside the current Worker.
	ExecutionDirectiveYield ExecutionDirective = "yield"
)

type ExecutionResult

type ExecutionResult struct {
	Directive  ExecutionDirective
	Status     Status
	Checkpoint []byte
	Data       []byte
	Error      string
}

ExecutionResult describes one legal executor outcome:

  • Yield: DirectiveYield plus an optional Checkpoint; all lifecycle fields empty.
  • Completed: StatusCompleted plus optional Data.
  • Failed or Canceled: the corresponding status plus Error.
  • WaitingInput or Suspended: the corresponding status plus Checkpoint.

Fields from different variants must not be combined.

type ExecutionRuntime

type ExecutionRuntime interface {
	// Controls returns a runtime-owned channel. Signals may be coalesced; the
	// executor must stop selecting it when the attempt context ends.
	Controls() <-chan ControlRequest
	// EmitProgress appends replayable progress. An empty event ID requests a
	// framework-generated stable ID. FirstEmission is false when the same ID and
	// bytes were already accepted for the task.
	EmitProgress(context.Context, string, []byte) (ProgressEmission, error)
	// ReportTranscriptFailure records the first non-lifecycle failure of the
	// optional derived transcript.
	ReportTranscriptFailure(context.Context, error) error
}

ExecutionRuntime exposes concurrency-safe, attempt-scoped capabilities. Storage fencing fields remain private to the runtime.

type Executor

type Executor interface {
	// Key is a stable persisted routing key.
	Key() string
	// LeaseExpiryPolicy is immutable for tasks created by this executor.
	LeaseExpiryPolicy() LeaseExpiryPolicy
	// ValidateSpec is repeatable, side-effect free, and runs before persistence.
	ValidateSpec(Spec) error
	// ValidateExecution performs side-effect-free validation immediately before
	// an attempt is claimed.
	ValidateExecution(context.Context, *Task) error
	// SupportsDrain reports whether Execute handles ControlDrain by returning a
	// resumable suspended or yielded result.
	SupportsDrain() bool
	// Execute owns the attempt until it returns. It must observe ctx and runtime
	// controls and return exactly one legal ExecutionResult variant.
	Execute(context.Context, *Task, ExecutionRuntime) (*ExecutionResult, error)
}

Executor reconstructs and runs durable work from a task Spec.

type ExecutorRegistry

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

ExecutorRegistry resolves executors by ExecutorKey.

func NewExecutorRegistry

func NewExecutorRegistry() *ExecutorRegistry

NewExecutorRegistry creates an empty executor registry.

func (*ExecutorRegistry) Keys

func (r *ExecutorRegistry) Keys() []string

Keys returns the registered executor keys.

func (*ExecutorRegistry) LoadOrRegister

func (r *ExecutorRegistry) LoadOrRegister(executor Executor) (Executor, bool, error)

LoadOrRegister atomically returns the executor registered under the candidate's key, registering the candidate when the key is not yet present.

func (*ExecutorRegistry) Register

func (r *ExecutorRegistry) Register(executor Executor) error

Register adds an executor keyed by executor.Key().

func (*ExecutorRegistry) Resolve

func (r *ExecutorRegistry) Resolve(key string) (Executor, bool)

Resolve returns the executor registered for key.

type FailTaskRequest

type FailTaskRequest struct {
	TaskID          string
	ExpectedVersion int64
	Error           string
}

FailTaskRequest records failed task completion.

type ForegroundTimeoutError

type ForegroundTimeoutError struct {
	// Timeout is the configured foreground observation budget.
	Timeout time.Duration
	// TaskID is the pre-allocated task ID. The task may not have been persisted.
	TaskID string
}

ForegroundTimeoutError reports that the foreground observation budget for a task expired. It unwraps to context.DeadlineExceeded for compatibility.

func (*ForegroundTimeoutError) Error

func (e *ForegroundTimeoutError) Error() string

func (*ForegroundTimeoutError) Unwrap

func (e *ForegroundTimeoutError) Unwrap() error

Unwrap makes errors.Is(err, context.DeadlineExceeded) report true.

type HeartbeatRequest

type HeartbeatRequest struct {
	TaskID          string
	ExpectedVersion int64
}

HeartbeatRequest reports liveness for an active attempt.

type IDGenerator

type IDGenerator func(ctx context.Context, request *AllocateTaskIDRequest) (string, error)

IDGenerator returns the complete ID for a new task.

The generator sees the allocation request before the task is registered and may return a business-side identifier. Manager does not add the task-type prefix when IDGen is configured; callers that want one should include it in the returned ID.

type InMemoryStore

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

InMemoryStore is a deterministic reference implementation of TaskStore, TaskEventStore, NotificationWriter, and NotificationOutbox. It is a state-machine test double, not a durable backend.

func NewInMemoryStore

func NewInMemoryStore(config *InMemoryStoreConfig) *InMemoryStore

NewInMemoryStore creates an in-memory reference task provider and outbox.

func (*InMemoryStore) Ack

Ack removes the notification authorized by a current unexpired receipt.

func (*InMemoryStore) AckCancel

func (s *InMemoryStore) AckCancel(_ context.Context, req *AckCancelRequest) (*Task, error)

AckCancel commits terminal acknowledgement of durable cancellation intent.

func (*InMemoryStore) AppendTaskEvent

AppendTaskEvent fences by attempt before task-wide EventID deduplication.

func (*InMemoryStore) CommitStart

func (s *InMemoryStore) CommitStart(
	_ context.Context,
	req *CommitStartRequest,
) (*Task, error)

CommitStart records the external-start boundary for the running attempt.

func (*InMemoryStore) Complete

func (s *InMemoryStore) Complete(_ context.Context, req *CompleteTaskRequest) (*Task, error)

Complete commits successful terminal output.

func (*InMemoryStore) Create

func (s *InMemoryStore) Create(_ context.Context, req *CreateTaskRequest) (*Task, error)

Create inserts one pending task and returns an independent snapshot.

func (*InMemoryStore) EnqueueTaskNotification

func (s *InMemoryStore) EnqueueTaskNotification(
	_ context.Context,
	taskID string,
	attempt int64,
	req *NotifyParentRequest,
) error

EnqueueTaskNotification fences by attempt before task-wide EventID replay detection and atomically records replay metadata with its outbox item.

func (*InMemoryStore) Fail

func (s *InMemoryStore) Fail(_ context.Context, req *FailTaskRequest) (*Task, error)

Fail commits terminal failure.

func (*InMemoryStore) Get

func (s *InMemoryStore) Get(_ context.Context, taskID string) (*Task, error)

Get returns an independent authoritative snapshot and resolves expired leases.

func (*InMemoryStore) Heartbeat

func (s *InMemoryStore) Heartbeat(_ context.Context, req *HeartbeatRequest) (*Task, error)

Heartbeat renews the current active attempt lease.

func (*InMemoryStore) ListPending

ListPending returns task-ID-ordered pending snapshots.

func (*InMemoryStore) ListSuspended

ListSuspended returns task-ID-ordered suspended snapshots.

func (*InMemoryStore) ListTaskEvents

ListTaskEvents returns one snapshot-stable append-order page.

func (*InMemoryStore) Receive

Receive leases visible notifications with fresh opaque receipts.

func (*InMemoryStore) ReleaseSuspension

func (s *InMemoryStore) ReleaseSuspension(_ context.Context, req *ReleaseSuspensionRequest) (*Task, error)

ReleaseSuspension returns a suspended task to pending.

func (*InMemoryStore) ReportTranscriptFailure

func (s *InMemoryStore) ReportTranscriptFailure(_ context.Context, req *ReportTranscriptFailureRequest) (*Task, error)

ReportTranscriptFailure records the first derived-transcript error.

func (*InMemoryStore) RequestCancel

func (s *InMemoryStore) RequestCancel(_ context.Context, req *RequestCancelRequest) (*Task, error)

RequestCancel records first-write cancellation intent.

func (*InMemoryStore) Resume

func (s *InMemoryStore) Resume(_ context.Context, req *ResumeRequest) (*Task, error)

Resume stores one opaque input and returns a waiting task to pending.

func (*InMemoryStore) Start

func (s *InMemoryStore) Start(_ context.Context, req *StartTaskRequest) (*Task, error)

Start claims a pending task and creates a fenced active attempt.

func (*InMemoryStore) Suspend

func (s *InMemoryStore) Suspend(_ context.Context, req *SuspendTaskRequest) (*Task, error)

Suspend commits a checkpointed planned pause.

func (*InMemoryStore) WaitForTaskVersion

func (s *InMemoryStore) WaitForTaskVersion(ctx context.Context, req *WaitForTaskVersionRequest) (*Task, error)

WaitForTaskVersion blocks until the stored task has a Version greater than req.AfterVersion. WaitForTaskVersion waits until Task.Version exceeds AfterVersion.

func (*InMemoryStore) WaitInput

func (s *InMemoryStore) WaitInput(_ context.Context, req *WaitInputTaskRequest) (*Task, error)

WaitInput commits a checkpointed wait for external input.

func (*InMemoryStore) Yield

func (s *InMemoryStore) Yield(_ context.Context, req *YieldTaskRequest) (*Task, error)

Yield relinquishes an attempt and returns the task to pending.

type InMemoryStoreConfig

type InMemoryStoreConfig struct {
	// ActiveAttemptTimeout defaults to 30 seconds.
	ActiveAttemptTimeout time.Duration
	// MaxValueBytes defaults to 1 MiB and bounds each checkpoint, successful
	// result, resume input, and task-event data value.
	MaxValueBytes int64
}

InMemoryStoreConfig configures the in-memory reference task provider.

type LeaseExpiryPolicy

type LeaseExpiryPolicy string

LeaseExpiryPolicy controls how TaskStore resolves an expired active attempt.

const (
	// LeaseExpiryRetry returns the task to pending for another durable attempt.
	LeaseExpiryRetry LeaseExpiryPolicy = "retry"
	// LeaseExpiryFail terminally fails work that cannot be reconstructed after process loss.
	LeaseExpiryFail LeaseExpiryPolicy = "fail"
)

type ListPendingRequest

type ListPendingRequest struct {
	ExecutorKeys []string
	Cursor       string
	Limit        int
}

ListPendingRequest lists pending task candidates for the given executor keys. Results use stable task-ID order. Cursor continues after the previous page; it is scoped to the same provider and filter. Limit defaults to 100 and is capped at 1000.

type ListPendingResult

type ListPendingResult struct {
	Tasks      []*Task
	NextCursor string
}

ListPendingResult contains independent task snapshots. NextCursor is empty when the current traversal is exhausted.

type ListSuspendedRequest

type ListSuspendedRequest struct {
	ExecutorKeys []string
	Cursor       string
	Limit        int
}

ListSuspendedRequest lists suspended tasks with the same ordering, cursor, filter, and limit rules as ListPendingRequest.

type ListSuspendedResult

type ListSuspendedResult struct {
	Tasks      []*Task
	NextCursor string
}

ListSuspendedResult contains independent task snapshots. NextCursor is empty when the current traversal is exhausted.

type ListTaskEventsRequest

type ListTaskEventsRequest struct {
	TaskID      string
	Cursor      string
	Limit       int
	NewestFirst bool
}

ListTaskEventsRequest requests one snapshot-stable page of task events. NewestFirst selects reverse append order; Cursor continues the same task, direction, and snapshot established by the first page. Limit defaults to 100 and is capped at 1000; callers may change Limit between pages. An empty first page has no continuation cursor.

type ListTaskEventsResult

type ListTaskEventsResult struct {
	Events     []*TaskEvent
	NextCursor string
}

ListTaskEventsResult contains an independently owned page and an opaque continuation cursor. NextCursor is empty when the captured snapshot has been exhausted.

type Manager

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

Manager owns TaskStore-backed lifecycle and worker coordination.

func New

func New(_ context.Context, conf *Config) (*Manager, error)

New creates a Manager. A nil Config installs the in-memory reference stores and a new executor registry. When Tasks is supplied without TaskEvents, Tasks must also implement TaskEventStore. The context is reserved for constructor symmetry; Manager does not retain it or derive task lifetime from it.

func (*Manager) AllocateTaskID

func (m *Manager) AllocateTaskID(ctx context.Context, request *AllocateTaskIDRequest) (string, error)

AllocateTaskID allocates an opaque ID for a task category.

func (*Manager) Close

func (m *Manager) Close(ctx context.Context, options ...CloseOption) error

Close performs bounded graceful shutdown. When any attempt is active, ctx must have a deadline or Close returns ErrCloseDeadlineRequired without closing the Manager. Drainable attempts receive ControlDrain and may suspend or yield according to their executor contract; non-drainable attempts may finish until the deadline and are then durably canceled. Deadline expiry cannot force an uncooperative executor to return; Manager remains closed to new submissions, while read and cancellation methods remain available.

func (*Manager) Execute

func (m *Manager) Execute(ctx context.Context, taskID string) error

Execute claims and runs one pending task attempt on the current worker.

func (*Manager) Get

func (m *Manager) Get(ctx context.Context, taskID string) (*Task, error)

Get returns the authoritative task snapshot.

func (*Manager) ListPending

func (m *Manager) ListPending(ctx context.Context, req *ListPendingRequest) (*ListPendingResult, error)

ListPending is the read-only dispatch boundary. A worker may select and dispatch a task ID from this result; only Execute performs start authorization. Ordering, cursor, limit, and snapshot ownership follow ListPendingRequest.

func (*Manager) ListSuspended

func (m *Manager) ListSuspended(
	ctx context.Context,
	req *ListSuspendedRequest,
) (*ListSuspendedResult, error)

ListSuspended returns checkpointed tasks that require an explicit release before workers may claim them again. Pagination follows ListPendingRequest.

func (*Manager) ListTaskEvents

func (m *Manager) ListTaskEvents(
	ctx context.Context,
	req *ListTaskEventsRequest,
) (*ListTaskEventsResult, error)

ListTaskEvents reads one snapshot-stable page of task events.

func (*Manager) ReleaseSuspension

func (m *Manager) ReleaseSuspension(ctx context.Context, taskID string) (*Task, error)

ReleaseSuspension returns a suspended task to pending so a worker can claim a new attempt from its persisted checkpoint.

func (*Manager) RequestCancel

func (m *Manager) RequestCancel(
	ctx context.Context,
	taskID string,
	options ...RequestCancelOption,
) (*Task, error)

RequestCancel records cancellation intent and signals a local active attempt. An optional reason is durable and first-write across repeated requests. Process-local non-recoverable work may wait for terminal acknowledgement; recoverable work may return the still-running snapshot after intent is durable.

func (*Manager) Resume

func (m *Manager) Resume(ctx context.Context, req *ResumeRequest) (*Task, error)

Resume persists opaque input for a task waiting on external input. The concrete executor must defensively validate the persisted input before use; Manager intentionally does not know executor-specific resume schemas.

func (*Manager) Submit

func (m *Manager) Submit(ctx context.Context, req *SubmitRequest) (*Task, error)

Submit validates serialized intent, persists a pending background task, and attempts to emit its low-latency TaskCreated parent-session event before returning. Create atomically writes the durable TaskCreated outbox record; if only the immediate event send fails, Submit returns the non-nil task with an error wrapping ErrTaskCreatedEventUndelivered. Callers must treat that case as ownership transferred and rely on the outbox for recovery.

func (*Manager) WaitForTaskVersion

func (m *Manager) WaitForTaskVersion(ctx context.Context, req *WaitForTaskVersionRequest) (*Task, error)

WaitForTaskVersion blocks until the authoritative task snapshot has a Version greater than req.AfterVersion. Task progress events do not advance Version and therefore do not satisfy the wait.

type Notification

type Notification struct {
	ID        string
	TaskID    string
	SessionID string
	Version   int64
	Kind      NotificationKind
	Data      []byte
	CreatedAt time.Time
}

Notification is one durable session-routed lifecycle or application event. Lifecycle records have empty Data; consumers load authoritative task state by TaskID when needed. Application Data is opaque and independently owned.

type NotificationDelivery

type NotificationDelivery struct {
	Record  Notification
	Receipt NotificationReceipt
}

NotificationDelivery contains a notification and its acknowledgement receipt.

type NotificationKind

type NotificationKind string

NotificationKind identifies the lifecycle transition that created a notification.

const (
	// NotificationTaskCreated reports that a parent-owned task was created. It
	// is the durable recovery source for TaskCreated session-event delivery.
	NotificationTaskCreated NotificationKind = "task_created"
	// NotificationWaitingInput reports that a task is paused waiting for resume input.
	NotificationWaitingInput NotificationKind = "waiting_input"
	// NotificationCompleted reports that a task completed successfully.
	NotificationCompleted NotificationKind = "completed"
	// NotificationFailed reports that a task failed.
	NotificationFailed NotificationKind = "failed"
	// NotificationCanceled reports that a task was canceled.
	NotificationCanceled NotificationKind = "canceled"
)

type NotificationOutbox

NotificationOutbox leases task notifications for dispatch. The NotificationTaskCreated record is the durable recovery source for reconciling a TaskCreated parent-session event if the creating process exits before Runner persists its immediate timeline emission. Ack must accept only the opaque receipt for the notification's current unexpired lease; an expired or superseded receipt must not acknowledge the notification. Receive normalizes limits to default 100 and maximum 1000. Both sides copy receipt bytes, and successful notification records remain until acknowledged.

type NotificationReceipt

type NotificationReceipt []byte

NotificationReceipt is an opaque token authorizing acknowledgement of one notification during its current lease. Callers and providers must copy its bytes and must not mutate a receipt after passing it across the SPI.

type NotificationWriter

type NotificationWriter interface {
	EnqueueTaskNotification(
		ctx context.Context,
		taskID string,
		attempt int64,
		req *NotifyParentRequest,
	) error
}

NotificationWriter atomically authorizes and enqueues application notifications from the exact active task attempt. Implementations derive the immutable parent SessionID from the stored Spec, fence attempt, lease, and cancellation before replay lookup, and retain replay metadata for at least the task lifetime. Notification.Version captures the current Task version without advancing it. Implementations copy request Data before retaining it.

type NotifyParentRequest

type NotifyParentRequest struct {
	EventID string
	Kind    NotificationKind
	Data    []byte
}

NotifyParentRequest describes one application notification emitted by the current durable attempt. EventID is task-local, idempotent, required, and limited to 1024 bytes. Kind is required, limited to 64 bytes, and must not use a lifecycle kind or the reserved "eino." prefix. Data is opaque and limited to 256 KiB. Bounds are measured in bytes.

type ProgressEmission

type ProgressEmission struct {
	EventID string
	// FirstEmission is false for an idempotent replay of an event already
	// accepted for this task.
	FirstEmission bool
}

ProgressEmission reports the stable identity and replay status of one executor progress event.

type ReceiveNotificationsRequest

type ReceiveNotificationsRequest struct {
	Limit         int
	LeaseDuration time.Duration
}

ReceiveNotificationsRequest leases visible notifications from an outbox. Limit defaults to 100 and is capped at 1000. LeaseDuration must be positive.

type ReceiveNotificationsResult

type ReceiveNotificationsResult struct {
	Deliveries []NotificationDelivery
}

ReceiveNotificationsResult contains leased notification deliveries.

type ReleaseSuspensionRequest

type ReleaseSuspensionRequest struct {
	TaskID          string
	ExpectedVersion int64
	// ContextSnapshot is Manager-owned. Manager callers should leave it empty;
	// Manager captures it from Config.ContextSnapshotter. TaskStore
	// implementations replace the task's stored context snapshot when this field
	// is non-nil. An empty non-nil slice clears it.
	ContextSnapshot []byte
}

ReleaseSuspensionRequest returns a suspended task to pending.

type ReportTranscriptFailureRequest

type ReportTranscriptFailureRequest struct {
	TaskID          string
	ExpectedVersion int64
	Error           string
}

ReportTranscriptFailureRequest records the first failure of an optional derived transcript. It does not change task lifecycle status; later reports preserve the first recorded error.

type RequestCancelOption

type RequestCancelOption func(*requestCancelOptions)

RequestCancelOption configures durable cancellation intent.

func WithCancellationReason

func WithCancellationReason(reason string) RequestCancelOption

WithCancellationReason records an optional durable reason for stopping a task. The first cancellation request wins.

type RequestCancelRequest

type RequestCancelRequest struct {
	TaskID          string
	ExpectedVersion int64
	// Reason is optional and first-write for repeated cancellation requests.
	Reason string
}

RequestCancelRequest records durable cancellation intent. Active work remains running until its attempt acknowledges cancellation. Retry-capable work whose lease was lost remains pending until a recovery attempt stops the operation.

type ResumeRequest

type ResumeRequest struct {
	TaskID          string
	ExpectedVersion int64
	Data            []byte
	// ContextSnapshot is Manager-owned. Manager callers should leave it empty;
	// Manager captures it from Config.ContextSnapshotter. TaskStore
	// implementations replace the task's stored context snapshot when this field
	// is non-nil. An empty non-nil slice clears it.
	ContextSnapshot []byte
}

ResumeRequest stores input for the current waiting checkpoint and returns the task to pending. ExpectedVersion binds the input to that exact request. The command remains durable across retry-capable attempt loss until execution reaches another waiting or suspended checkpoint, or a terminal state.

type Spec

type Spec struct {
	ID          string
	ExecutorKey string
	Kind        string
	Payload     []byte

	Description string
	OutputFile  string
	// SessionID is an optional session notification target. It is routing
	// metadata, not the task's execution identity and not necessarily the
	// session of an agent executing the task.
	SessionID string
	// NotifySession enables waiting and terminal lifecycle notifications when
	// SessionID is non-empty. Tasks do not otherwise require a session.
	NotifySession bool
}

Spec is immutable serialized task intent supplied by the caller. Payload and all returned byte slices must be copied across provider boundaries. OutputFile names an optional derived transcript destination; it is not authoritative task output. Providers may impose documented size bounds such as InMemoryStoreConfig.

type StartCommitRuntime

type StartCommitRuntime interface {
	CommitStart(context.Context, []byte) error
}

StartCommitRuntime is an optional execution capability for atomically recording that an executor established its external operation. Manager runtimes implement it; keeping it separate preserves ExecutionRuntime source compatibility for custom executors and test doubles.

type StartTaskRequest

type StartTaskRequest struct {
	TaskID          string
	ExpectedVersion int64
}

StartTaskRequest asks the TaskStore to authorize a new active attempt.

type Status

type Status string

Status represents the durable lifecycle status of a task.

const (
	// StatusPending indicates the task is durable and available for claim.
	StatusPending Status = "pending"
	// StatusRunning indicates the task is currently executing.
	StatusRunning Status = "running"
	// StatusWaitingInput indicates execution is checkpointed pending external input.
	StatusWaitingInput Status = "waiting_input"
	// StatusSuspended indicates execution is checkpointed for a planned pause.
	// It is not claimable until Manager.ReleaseSuspension returns it to pending.
	StatusSuspended Status = "suspended"
	// StatusCompleted indicates the task finished successfully.
	StatusCompleted Status = "completed"
	// StatusFailed indicates the task terminated with an error.
	StatusFailed Status = "failed"
	// StatusCanceled indicates the task acknowledged an external stop request.
	StatusCanceled Status = "canceled"
)

type SubmitRequest

type SubmitRequest struct {
	Spec              Spec
	InitialCheckpoint []byte
}

SubmitRequest describes one background task submission. InitialCheckpoint is copied into Task.Checkpoint in the same atomic create operation as the task record and TaskCreated outbox entry. It is opaque to Manager.

type SuspendTaskRequest

type SuspendTaskRequest struct {
	TaskID          string
	ExpectedVersion int64
	Checkpoint      []byte
}

SuspendTaskRequest checkpoints a task for a planned suspension.

type Task

type Task struct {
	// Spec is the immutable serialized intent for this task.
	Spec Spec
	// LeaseExpiryPolicy is the immutable recovery policy selected by the
	// registered Executor when the task is created.
	LeaseExpiryPolicy LeaseExpiryPolicy
	// Status is the current lifecycle status.
	Status Status
	// Checkpoint is the latest durable executor checkpoint.
	Checkpoint []byte
	// ResultData is the terminal successful output. It is meaningful only when
	// Status is StatusCompleted.
	ResultData []byte
	// ResultError is the terminal failure or cancellation reason. It is meaningful
	// only when Status is StatusFailed or StatusCanceled.
	ResultError string
	// OutputFileErr records the first failure while producing the optional output transcript.
	OutputFileErr string
	// PendingResume is the durable resume command for the current checkpoint.
	// Retry-capable attempt loss or yield preserves it for idempotent replay. A
	// subsequent wait-input or suspended checkpoint, or a terminal transition,
	// consumes it.
	PendingResume []byte
	// ContextSnapshot is an opaque manager-owned snapshot used to restore
	// deployment-selected context values for future execution attempts. It is
	// captured only when Manager is configured with a ContextSnapshotter.
	ContextSnapshot []byte
	// Version is the CAS version of this durable record.
	Version int64
	// Attempt counts successful claims.
	Attempt int64
	// CancelRequestedAt records durable explicit stop intent while Status remains
	// StatusRunning until the active attempt acknowledges it as StatusCanceled.
	CancelRequestedAt *time.Time
	// CancelReason is the optional first-write reason accompanying durable stop
	// intent. It becomes ResultError when the task reaches StatusCanceled.
	CancelReason string
	// CreatedAt is the TaskStore-assigned creation time.
	CreatedAt time.Time
	// UpdatedAt is the TaskStore mutation time.
	UpdatedAt time.Time
	// DoneAt is the time the task reached a terminal state. Nil if still running.
	DoneAt *time.Time
}

Task represents one independently owned snapshot. Providers and callers must deep-copy mutable slices and time pointers when snapshots cross their boundary; mutating a returned Task must never alter persisted state.

type TaskCreatedSessionEvent

type TaskCreatedSessionEvent struct {
	TaskID      string `json:"task_id"`
	Description string `json:"description"`
}

TaskCreatedSessionEvent is the extension payload for SessionEventTaskCreated.

type TaskEvent

type TaskEvent struct {
	EventID   string
	TaskID    string
	Data      []byte
	CreatedAt time.Time
}

TaskEvent is one immutable task-progress event. EventID is an opaque, task-local replay identity and does not encode event chronology.

type TaskEventStore

type TaskEventStore interface {
	AppendTaskEvent(context.Context, *AppendTaskEventRequest) (*AppendTaskEventResult, error)
	ListTaskEvents(context.Context, *ListTaskEventsRequest) (*ListTaskEventsResult, error)
}

TaskEventStore persists append-ordered task progress independently from lifecycle snapshots. AppendTaskEvent must fence writes by the active attempt before task-wide EventID replay detection, retain replay metadata across attempts for at least the task lifetime, and not advance Task.Version. ListTaskEvents must keep each cursor on the snapshot captured by its first page and order events by append position, reversed when NewestFirst is true. Event data and result pages are independently owned. Successful events and cursor positions remain readable for at least the lifetime of their task.

type TaskStore

TaskStore persists authoritative task snapshots and semantic lifecycle transitions.

Every returned Task and mutable field is independently owned by the caller. ListPending and ListSuspended follow their request ordering, cursor, and limit contracts; malformed cursors return ErrInvalidCursor. When the provider also implements NotificationOutbox, Create atomically enqueues NotificationTaskCreated for every task with a parent SessionID.

RequestCancel on active work keeps StatusRunning, sets CancelRequestedAt and the first-write optional CancelReason, and advances Version. Once cancellation is requested, Heartbeat, Complete, Fail, WaitInput, Suspend, and Yield must reject the attempt; only AckCancel may terminally acknowledge it. CommitStart records the successful external-start boundary while retaining StatusRunning, requires a non-empty initial checkpoint envelope, advances Version, and must reject a second start commit while that checkpoint remains present. Yield changes running to pending, stores its optional checkpoint atomically, preserves PendingResume for idempotent replay, and emits no lifecycle notification. Retry-capable lease expiry also preserves PendingResume. A later WaitInput, Suspend, or terminal transition consumes it. On retry-capable work, cancel intent that outlives an attempt remains pending so a recovery attempt can stop the external operation before acknowledging cancellation. Non-recoverable lease expiry resolves cancellation directly.

type WaitForTaskVersionRequest

type WaitForTaskVersionRequest struct {
	TaskID       string
	AfterVersion int64
}

WaitForTaskVersionRequest identifies a task and the latest snapshot version observed by the caller. Task progress events do not advance Version.

type WaitInputTaskRequest

type WaitInputTaskRequest struct {
	TaskID          string
	ExpectedVersion int64
	Checkpoint      []byte
}

WaitInputTaskRequest checkpoints a task that is waiting for external input.

type YieldTaskRequest

type YieldTaskRequest struct {
	TaskID          string
	ExpectedVersion int64
	Checkpoint      []byte
}

YieldTaskRequest relinquishes an active recoverable attempt and returns the task to pending without implying that the underlying operation was suspended. An empty Checkpoint retains the task's latest boundary checkpoint. Any pending resume command is also retained for idempotent replay.

Directories

Path Synopsis
Package local executes non-serializable process-local closures as managed tasks.
Package local executes non-serializable process-local closures as managed tasks.
Package shell adapts recoverable logical commands to managed background tools.
Package shell adapts recoverable logical commands to managed background tools.
Package storetest provides reusable conformance suites for background-task persistence providers.
Package storetest provides reusable conformance suites for background-task persistence providers.
Package subagent provides a durable backgroundtask executor for ADK sub-agent runs.
Package subagent provides a durable backgroundtask executor for ADK sub-agent runs.
Package tool adapts explicitly capable external tools to durable background tasks.
Package tool adapts explicitly capable external tools to durable background tasks.
tooltest
Package tooltest provides conformance checks for managed background tool implementations.
Package tooltest provides conformance checks for managed background tool implementations.

Jump to

Keyboard shortcuts

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