spool

package
v0.4.0 Latest Latest
Warning

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

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

Documentation

Overview

Package spool stores oversized content under a principal-scoped grant and hands the caller a bounded view plus a reference. See docs/plans/spool.md.

Index

Constants

View Source
const MoreMarker = "[more: offset=%d]"

MoreMarker ends a page with more bytes after it, naming the next offset.

Variables

View Source
var (
	// ErrUnknownRef is Load's error for a ref with no live grant, and
	// for a live grant whose ContentStore.Get fails.
	ErrUnknownRef = errors.New("spool: unknown ref")
	// ErrWrongPrincipal is Load's error when principal does not match
	// the grant's recorded principal.
	ErrWrongPrincipal = errors.New("spool: wrong principal")
	// ErrNoPrincipal is SpoolTool's error for a ctx with no principal
	// attached, when the inner result needs a grant.
	ErrNoPrincipal = errors.New("spool: no principal in context")
	// ErrNoBudget is NewSpool's error for a non-positive maxGrantBytes.
	ErrNoBudget = errors.New("spool: maxGrantBytes must be positive")
	// ErrGrantTooLarge is Spool's error when data alone exceeds
	// maxGrantBytes: no eviction can ever make room for it.
	ErrGrantTooLarge = errors.New("spool: content exceeds grant budget")
	// ErrPrincipalConflict is Spool's error when the store returns a
	// ref already granted to a different principal. A content-addressed
	// ContentStore returns the same ref for identical bytes regardless
	// of caller; without this check, a second principal spooling the
	// same content would silently take over the first principal's
	// grant.
	ErrPrincipalConflict = errors.New("spool: ref already granted to a different principal")
	// ErrExpired is Load's error for a grant whose expiry passed. The
	// grant drops on that Load, freeing its budget.
	ErrExpired = errors.New("spool: grant expired")
	// ErrInvalidExpiry is SpoolExpiring's error for a non-positive ttl.
	ErrInvalidExpiry = errors.New("spool: ttl must be positive")
	// ErrNilSpool is ReadOutputTool's error for a nil Spool.
	ErrNilSpool = errors.New("spool: spool is required")
	// ErrInvalidLimit is ReadOutputTool's error for a non-positive
	// maxPageBytes.
	ErrInvalidLimit = errors.New("spool: maxPageBytes must be positive")
	// ErrBadArguments is ReadOutputTool's error for a malformed
	// argument decode, a mistyped Run call, a negative offset, or a
	// negative limit.
	ErrBadArguments = errors.New("spool: bad arguments")
)

Sentinel errors for Spool and SpoolTool; test with errors.Is.

Functions

func PrincipalFrom

func PrincipalFrom(ctx context.Context) (string, bool)

PrincipalFrom reads the principal WithPrincipal attached to ctx. The second return is false when no principal was attached.

func ReadOutputTool

func ReadOutputTool(sp *Spool, maxPageBytes int) (tools.Tool, error)

ReadOutputTool builds the model-facing read-back tool over sp. The model pages a spooled body back by ref, offset, and limit. A nil sp wraps ErrNilSpool; a non-positive maxPageBytes wraps ErrInvalidLimit.

func SpoolTool

func SpoolTool(name string, maxBytes int, sp *Spool, inner tools.Tool) (tools.Tool, error)

SpoolTool wraps inner so any string result longer than maxBytes spools to store under the ctx principal (see WithPrincipal) instead of returning in full. The wrapped tool's Out.Value becomes the truncated view string; the reference is appended to the view text. A result that is not a string, or one at or under maxBytes, passes through unchanged. A call with no principal in ctx returns ErrNoPrincipal. The returned tools.Tool always implements tools.ProfiledTool, tools.ResultBudgetTool, tools.PrivilegedTool, and tools.SchemaTool. Each forwards through tools.ExecutionProfileOf, tools.ResultBudgetOf, tools.IsPrivileged, and tools.SchemaOf, so a caller reading inner's published values through those helpers sees no difference. tools.SchemaOf fails closed: a schema-less inner reports nil, false, so agentloop.Definitions skips the wrapper instead of offering a nil schema. SpoolTool changes only Run's result handling, not inner's declared execution class, result budget, privilege, or schema. A nil sp wraps ErrNilSpool. A negative maxBytes clamps to zero. Two or more SpoolTool calls sharing one sp share its grant budget and its Load-time principal checks. A caller pairs a SpoolTool call with a ReadOutputTool call by passing the same sp to both.

func WithPrincipal

func WithPrincipal(ctx context.Context, principal string) context.Context

WithPrincipal returns a context carrying principal for a later SpoolTool call to read.

Types

type ContentStore

type ContentStore interface {
	Put(content []byte) (ref string, err error)
	Get(ref string) ([]byte, error)
}

ContentStore is the storage a Spool writes spooled bytes to and reads them back from. memory.Store satisfies this interface with no import needed on either side; a caller wires the two together.

type Spool

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

Spool stores oversized content under a principal-scoped grant and returns a bounded view plus a reference to the full content. The zero value is not usable; create a Spool with NewSpool. Mutex-guarded, safe for concurrent use.

func NewSpool

func NewSpool(store ContentStore, maxGrantBytes int) (*Spool, error)

NewSpool creates a Spool backed by store, tracking grants under a maxGrantBytes budget. A non-positive maxGrantBytes wraps ErrNoBudget.

func (*Spool) Expire

func (s *Spool) Expire(ref string) error

Expire marks one live grant expired immediately. Unknown ref wraps ErrUnknownRef.

func (*Spool) GrantExpiry

func (s *Spool) GrantExpiry(ref string) (time.Time, bool)

GrantExpiry reports a grant's expiry. The second return is false for no live grant. A zero time means no expiry.

func (*Spool) Load

func (s *Spool) Load(ctx context.Context, principal, ref string) ([]byte, error)

Load returns the full bytes stored under ref. It wraps ErrUnknownRef when no live grant matches ref, ErrWrongPrincipal when principal does not match the grant's recorded principal, even on an expired grant, and ErrExpired when the right principal's grant expired: that grant drops on this Load, freeing its budget. When the grant is live but the underlying ContentStore.Get fails (for example, the store's own independent budget evicted the blob first), Load wraps that error under ErrUnknownRef too: a live grant whose bytes are gone is, from the caller's view, an unknown ref.

func (*Spool) Spool

func (s *Spool) Spool(ctx context.Context, principal string, data []byte) (view string, ref string, err error)

Spool writes data to the underlying store, grants principal the right to read it back, and returns a bounded view of data plus the content's reference. Spool evicts the oldest grants, by insertion order, until the new grant fits the byte budget. Spool wraps ErrGrantTooLarge when data alone exceeds maxGrantBytes, and ErrPrincipalConflict when the store's ref already belongs to a different principal's live grant.

func (*Spool) SpoolExpiring

func (s *Spool) SpoolExpiring(ctx context.Context, principal string, data []byte, ttl time.Duration) (view string, ref string, err error)

SpoolExpiring writes data, grants principal read-back, and sets a time-to-live on the grant. A non-positive ttl wraps ErrInvalidExpiry before any store write. Re-spooling an existing ref under the same principal refreshes the expiry, the same way Spool refreshes insertion order.

Jump to

Keyboard shortcuts

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