attachment

package
v0.36.0 Latest Latest
Warning

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

Go to latest
Published: Sep 2, 2026 License: MIT Imports: 20 Imported by: 0

Documentation

Overview

Package attachment provides durable, content-addressed blob storage for session attachments.

Index

Constants

This section is empty.

Variables

View Source
var ErrNoScope = errors.New("attachment: no attachment scope (work inline)")

ErrNoScope is returned by Scope methods called on a nil Scope, i.e. by code that tried to produce an attachment reference without holding the capability.

View Source
var ErrScopeRevoked = errors.New("attachment: attachment scope revoked")

ErrScopeRevoked is returned after the owning session has been deleted.

Functions

func DefaultBaseDir

func DefaultBaseDir() (string, error)

DefaultBaseDir resolves the production attachments root. MOA_CONFIG_DIR is honored for container and custom deployments.

func WithScope added in v0.30.0

func WithScope(ctx context.Context, scope *Scope) context.Context

WithScope tags ctx with the attachment capability of the agent that is running, so shared tools (a `read` object built by the parent and reused by an ephemeral reviewer, an MCP wrapper) resolve it PER INVOCATION instead of capturing it at construction time.

Passing a nil scope is not a no-op and must never be optimized into one: it writes nil over any inherited value, hiding a parent's capability from an agent that must not externalize. See Agent.executeWithOptions.

Types

type Descriptor

type Descriptor struct {
	ID        string    `json:"id"`
	SHA256    string    `json:"sha256"`
	Name      string    `json:"name"`
	Mime      string    `json:"mime"`
	Size      int64     `json:"size"`
	Kind      string    `json:"kind"`
	Width     int       `json:"width,omitempty"`
	Height    int       `json:"height,omitempty"`
	CreatedAt time.Time `json:"created_at"`
}

Descriptor is the persisted, byte-free reference to a stored blob. It is what a message/session carries instead of base64.

type PutMeta

type PutMeta struct {
	Name   string
	Mime   string
	Kind   string
	Width  int
	Height int
}

PutMeta contains caller-validated metadata for a blob occurrence.

type Scope added in v0.30.0

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

Scope is the indivisible capability to externalize attachments for ONE owning session: it bundles the store, the owning session ID, the producer (PutRef) and the consumer (the materializer that turns references back into provider-ready bytes).

The bundling is the whole point. Producing references and being able to resolve them are two halves of the same capability: whoever holds one MUST hold the other, or a reference reaches the provider with no bytes behind it and the model silently sees an empty image. Keeping the halves in separate config fields (a store here, a materializer hook there) is exactly how that gap opens, so Scope has no exported fields and no partial constructor.

A nil *Scope is a valid, meaningful value: it means "no capability", i.e. work inline exactly as before. Every method is nil-safe.

func NewScope added in v0.30.0

func NewScope(store *Store, sessionID string) (*Scope, error)

NewScope builds the capability for sessionID on store. It rejects a nil store or an invalid session ID so a Scope can never exist half-built: if it exists, both halves work.

func ScopeFromContext added in v0.30.0

func ScopeFromContext(ctx context.Context) *Scope

ScopeFromContext returns the scope installed by WithScope, or nil when the caller holds no capability (work inline).

func (*Scope) Materializer added in v0.30.0

func (s *Scope) Materializer() func(context.Context, []core.Message) ([]core.Message, error)

Materializer returns the consumer half: the hook that expands references owned by this session back into inline bytes before a provider request. It is derived from the same value that produces the references, so the two can never disagree about the owner. Returns nil for a nil Scope.

func (*Scope) Put added in v0.30.0

func (s *Scope) Put(data []byte, meta PutMeta) (Descriptor, error)

Put stores data and registers the reference under the owning session in a single locked operation (see Store.PutRef). The owner is baked in: callers cannot pass a different one.

func (*Scope) Revoke added in v0.32.0

func (s *Scope) Revoke()

Revoke prevents a deleted session's in-flight tools from recreating its attachment index after it has been released. It is safe to call repeatedly.

func (*Scope) SessionID added in v0.30.0

func (s *Scope) SessionID() string

SessionID returns the owning session ID ("" for a nil Scope). Attachments produced through a Scope are ALWAYS owned by this session — for a subagent that is the parent's session ID, never the job ID, because the store's GC only knows about main sessions.

type Store

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

Store is a durable, content-addressed attachment blob store shared across sessions. Blobs are keyed by sha256 of their bytes and reference-counted by occurrence; a blob is deleted when no session references it.

func New

func New(baseDir string) (*Store, error)

New returns a Store rooted at baseDir, creating its directory tree if needed.

func (*Store) AddRef

func (s *Store) AddRef(sessionID string, d Descriptor) error

AddRef records one attachment occurrence in a session. It is idempotent for an existing (sessionID, descriptor ID) pair.

func (*Store) EnsureSessionViewDir

func (s *Store) EnsureSessionViewDir(sessionID string) (string, error)

EnsureSessionViewDir creates and returns the durable, session-scoped parent directory for attachment views. Callers may grant this directory to a tool path policy; it never exposes blobs or another session's views.

func (*Store) EnsureView

func (s *Store) EnsureView(sessionID, attID string) (string, error)

EnsureView returns a durable, session-scoped path for a session-owned attachment. Tool views are session-local read-only copies (0400), never hardlinks: the view is exposed to agent tools, and a hardlink would let a tool corrupt the shared immutable blob across sessions. A view duplicates its bytes on disk for integrity and is released with its session. An empty legacy descriptor name uses "file" plus a MIME-derived extension when available.

func (*Store) Lookup

func (s *Store) Lookup(sessionID, attID string) (Descriptor, bool)

Lookup returns the descriptor for a session-owned attachment occurrence.

func (*Store) MaterializeMessages

func (s *Store) MaterializeMessages(sessionID string, msgs []core.Message) ([]core.Message, error)

MaterializeMessages returns a copy of msgs in which referenced images become inline base64, while referenced documents become an advisory with a durable, session-scoped tool path. Messages/blocks without a reference are passed through unchanged. The input slice and its content are never mutated.

func (*Store) MaterializerFor

func (s *Store) MaterializerFor(sessionID string) func(context.Context, []core.Message) ([]core.Message, error)

MaterializerFor returns a per-session materializer closure suitable for the agent's MaterializeContent hook. sessionID is baked in.

func (*Store) Open

func (s *Store) Open(sessionID, attID string) (io.ReadCloser, Descriptor, error)

Open returns a read-only handle to an attachment only when the requested session owns the occurrence.

func (*Store) Put

func (s *Store) Put(data []byte, meta PutMeta) (Descriptor, error)

Put stores raw bytes, deduplicating by their SHA-256 hash. It creates an occurrence descriptor but does not attach that occurrence to a session.

func (*Store) PutRef

func (s *Store) PutRef(sessionID string, data []byte, meta PutMeta) (Descriptor, error)

PutRef publishes data (deduplicating by hash) AND records a provisional reference from sessionID in ONE locked operation, so the blob can never be garbage-collected between publication and referencing. This is the method callers should use when the owning session is already known (the normal ingestion path). Returns the minted Descriptor (already referenced).

func (*Store) Reconcile

func (s *Store) Reconcile(live map[string][]Descriptor) error

Reconcile rewrites the session ownership indexes and catalog from live descriptors, removes indexes absent from live, and garbage-collects old unreferenced blobs and stale staging files. It MUST run at startup (or in a maintenance window) with no concurrent uploads, AddRef, or PutRef for sessions absent from live: live is a snapshot and is not safe for an actively uploading store.

func (*Store) ReconcileExisting

func (s *Store) ReconcileExisting(liveSessionIDs map[string]bool) error

ReconcileExisting is the lightweight startup reconcile path. It trusts the store's per-session indexes and is O(sessions-with-attachments), rather than walking conversation transcripts. Indexes for sessions absent from live are released, then the catalog, orphan blobs, and stale staging are reconciled.

func (*Store) ReleaseSession

func (s *Store) ReleaseSession(sessionID string) error

ReleaseSession removes every occurrence owned by a session and unlinks blobs which are no longer referenced by any remaining session index.

func (*Store) RemoveRef

func (s *Store) RemoveRef(sessionID, attID string) error

RemoveRef removes one occurrence reference from a session. It is idempotent when the occurrence is not owned by the session.

Jump to

Keyboard shortcuts

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