artifacts

package
v1.4.0 Latest Latest
Warning

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

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

Documentation

Overview

Copyright 2026 Teradata

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

Package artifacts provides session artifact directory layout, optional on-disk session attribution in metadata.json, and helpers to read or update that file safely.

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

Index

Constants

View Source
const SessionMetadataFileName = "metadata.json"

SessionMetadataFileName is the JSON file stored at the session artifact root. See https://github.com/teradata-labs/loom/issues/111

Variables

This section is empty.

Functions

func AgentNameFromSession added in v1.4.0

func AgentNameFromSession(session *types.Session) string

AgentNameFromSession returns a display name from session context keys agent_name / agentName.

func CompleteSessionArtifactMetadata added in v1.4.0

func CompleteSessionArtifactMetadata(sessionID string) error

CompleteSessionArtifactMetadata sets ended_at and status to "completed" when metadata.json exists. It is a no-op when SessionMetadataEnabled is false or sessionID is empty. If the file is missing or unreadable, it returns nil (backward compatible). Context fields are rewritten through FilterPublicArtifactContext before save. Serialized with the same per-session lock as SyncSessionArtifactMetadata.

func ComputeChecksum

func ComputeChecksum(path string) (string, error)

ComputeChecksum calculates the SHA256 checksum of a file.

func EnsureArtifactDir added in v1.1.0

func EnsureArtifactDir(sessionID string, source SourceType) error

EnsureArtifactDir creates artifact directory if it doesn't exist.

func EnsureScratchpadDir added in v1.1.0

func EnsureScratchpadDir(sessionID string) error

EnsureScratchpadDir creates scratchpad directory if it doesn't exist.

func ExtractArchive

func ExtractArchive(archivePath, destDir string) ([]string, error)

ExtractArchive extracts an archive to a destination directory. Returns a list of extracted file paths.

func ExtractSessionIDFromPath added in v1.1.0

func ExtractSessionIDFromPath(path string) string

ExtractSessionIDFromPath attempts to extract session ID from an artifact path. Returns empty string if path is not session-based. Example path: $LOOM_DATA_DIR/artifacts/sessions/<session-id>/agent/file.csv

func FilterPublicArtifactContext added in v1.4.0

func FilterPublicArtifactContext(m map[string]string) map[string]string

FilterPublicArtifactContext returns a copy of m containing only allowlisted non-empty keys. Used when merging on-disk metadata into API responses and when normalizing metadata before write.

func GenerateArtifactID

func GenerateArtifactID() string

GenerateArtifactID generates a unique artifact ID.

func GetArtifactDir added in v1.1.0

func GetArtifactDir(sessionID string, source SourceType) (string, error)

GetArtifactDir returns the artifact directory for a given context. Directory structure:

  • No session + user: $LOOM_DATA_DIR/artifacts/user/
  • No session + generated/agent: $LOOM_DATA_DIR/artifacts/temp/
  • Session + user: $LOOM_DATA_DIR/artifacts/sessions/<session-id>/user/
  • Session + generated/agent: $LOOM_DATA_DIR/artifacts/sessions/<session-id>/agent/

func GetArtifactsDir

func GetArtifactsDir() (string, error)

GetArtifactsDir returns the artifacts directory path.

func GetScratchpadDir added in v1.1.0

func GetScratchpadDir(sessionID string) (string, error)

GetScratchpadDir returns the scratchpad directory for a session. Scratchpad is ephemeral storage for notes and scratch work, not indexed.

func IsArchive

func IsArchive(contentType string) bool

IsArchive checks if the content type represents an archive format.

func SessionArtifactsRoot added in v1.4.0

func SessionArtifactsRoot(sessionID string) (string, error)

SessionArtifactsRoot returns $LOOM_DATA_DIR/artifacts/sessions/<sessionID>. sessionID originates from API callers: ValidateSessionID enforces a strict [A-Za-z0-9._-] allowlist (no separators, no ".."), and filepath.IsLocal additionally rejects anything absolute, escaping, or reserved (Windows). IsLocal must guard sessionID itself, not a value derived from it — CodeQL credits the barrier only on the exact guarded expression, which is why an earlier filepath.Rel-based containment check left go/path-injection alerts (#666-#669) open.

func SessionMetadataEnabled added in v1.4.0

func SessionMetadataEnabled() bool

SessionMetadataEnabled reports whether session artifact metadata.json integration is enabled. When false, SyncSessionArtifactMetadata and CompleteSessionArtifactMetadata are no-ops and callers that gate on this flag skip merging disk metadata into API responses.

func SetSessionMetadataEnabled added in v1.4.0

func SetSessionMetadataEnabled(v bool)

SetSessionMetadataEnabled sets the process-wide metadata.json feature flag. The looms binary sets this from artifacts.session_metadata_enabled (or env LOOM_ARTIFACTS_SESSION_METADATA_ENABLED) during serve startup. The value is safe to read concurrently; typical usage is set once at startup rather than toggling at runtime.

func StringFromContext added in v1.4.0

func StringFromContext(m map[string]interface{}, key string) (string, bool)

StringFromContext returns a non-empty trimmed string for key when present.

func SyncSessionArtifactMetadata added in v1.4.0

func SyncSessionArtifactMetadata(ctx context.Context, session *types.Session) error

SyncSessionArtifactMetadata builds metadata from session and atomically writes metadata.json. It is a no-op when SessionMetadataEnabled is false, when session is nil, or when session.ID is empty. If metadata.json already records status "completed", that status and ended_at are preserved so later saves do not resurrect an ended session; optional artifact stats are preserved. A per-session mutex serializes read-modify-write with CompleteSessionArtifactMetadata. If ctx is non-nil, ctx.Done() is checked once before disk work; cancellation is not polled mid-write.

func ValidateSessionID added in v1.4.0

func ValidateSessionID(sessionID string) error

ValidateSessionID rejects session IDs that could escape the artifacts directory when joined into a filesystem path. Session IDs are normally server-generated UUIDs; path separators or traversal sequences indicate hostile input from an API caller.

func WriteSessionArtifactMetadata added in v1.4.0

func WriteSessionArtifactMetadata(meta *SessionArtifactMetadata) error

WriteSessionArtifactMetadata writes metadata.json under the session artifact root. The session directory is created if missing. Uses atomic replace (temp + rename).

Types

type AnalysisResult

type AnalysisResult struct {
	ContentType string
	SizeBytes   int64
	Checksum    string
	Tags        []string
	Metadata    map[string]string
}

AnalysisResult contains the results of file analysis.

type Analyzer

type Analyzer struct{}

Analyzer analyzes files and extracts metadata.

func NewAnalyzer

func NewAnalyzer() *Analyzer

NewAnalyzer creates a new file analyzer.

func (*Analyzer) Analyze

func (a *Analyzer) Analyze(path string) (*AnalysisResult, error)

Analyze inspects a file and returns analysis results.

type Artifact

type Artifact struct {
	ID             string
	Name           string
	Path           string
	Source         SourceType
	SourceAgentID  string
	Purpose        string
	ContentType    string
	SizeBytes      int64
	Checksum       string
	CreatedAt      time.Time
	UpdatedAt      time.Time
	LastAccessedAt *time.Time
	AccessCount    int
	Tags           []string
	Metadata       map[string]string
	DeletedAt      *time.Time
	SessionID      string // Session this artifact belongs to (nullable for backward compatibility)
}

Artifact represents a file artifact with metadata.

type ArtifactStore

type ArtifactStore interface {
	// Index adds or updates an artifact in the catalog.
	Index(ctx context.Context, artifact *Artifact) error

	// Get retrieves artifact metadata by ID.
	Get(ctx context.Context, id string) (*Artifact, error)

	// GetByName retrieves artifact by file name within a session.
	// If sessionID is empty, searches in user artifacts (backward compatibility).
	GetByName(ctx context.Context, name string, sessionID string) (*Artifact, error)

	// List returns all artifacts matching filters.
	List(ctx context.Context, filter *Filter) ([]*Artifact, error)

	// Search performs FTS5 full-text search.
	// If sessionID is non-empty, results are scoped to that session.
	Search(ctx context.Context, query string, sessionID string, limit int) ([]*Artifact, error)

	// Update updates artifact metadata.
	Update(ctx context.Context, artifact *Artifact) error

	// Delete soft-deletes or hard-deletes an artifact.
	Delete(ctx context.Context, id string, hard bool) error

	// RecordAccess updates last_accessed_at and access_count.
	RecordAccess(ctx context.Context, id string) error

	// GetStats returns storage statistics.
	GetStats(ctx context.Context) (*Stats, error)

	// Close closes the store.
	Close() error
}

ArtifactStore defines the interface for artifact storage operations.

type ArtifactUpdateCallback

type ArtifactUpdateCallback func(artifact *Artifact, eventType string)

ArtifactUpdateCallback is called when an artifact is created, modified, or deleted. Parameters: artifact metadata, event type (create/modify/delete).

type Filter

type Filter struct {
	Source         *SourceType
	ContentType    *string
	Tags           []string
	MinSize        *int64
	MaxSize        *int64
	AfterDate      *time.Time
	BeforeDate     *time.Time
	IncludeDeleted bool
	Limit          int
	Offset         int
	SessionID      *string // Filter by session (nullable)
}

Filter defines filtering options for listing artifacts.

type SQLiteStore

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

SQLiteStore implements ArtifactStore with SQLite backend.

func NewSQLiteStore

func NewSQLiteStore(dbPath string, tracer observability.Tracer) (*SQLiteStore, error)

NewSQLiteStore creates a new SQLite-backed artifact store. It reuses the existing loom.db database and creates the artifacts table if needed.

func (*SQLiteStore) Close

func (s *SQLiteStore) Close() error

Close closes the database connection.

func (*SQLiteStore) Delete

func (s *SQLiteStore) Delete(ctx context.Context, id string, hard bool) error

Delete soft-deletes or hard-deletes an artifact.

func (*SQLiteStore) Get

func (s *SQLiteStore) Get(ctx context.Context, id string) (*Artifact, error)

Get retrieves an artifact by ID.

func (*SQLiteStore) GetByName

func (s *SQLiteStore) GetByName(ctx context.Context, name string, sessionID string) (*Artifact, error)

GetByName retrieves an artifact by name.

func (*SQLiteStore) GetStats

func (s *SQLiteStore) GetStats(ctx context.Context) (*Stats, error)

GetStats returns artifact storage statistics.

func (*SQLiteStore) Index

func (s *SQLiteStore) Index(ctx context.Context, artifact *Artifact) error

Index adds or updates an artifact in the database.

func (*SQLiteStore) List

func (s *SQLiteStore) List(ctx context.Context, filter *Filter) ([]*Artifact, error)

List returns artifacts matching the filter.

func (*SQLiteStore) RecordAccess

func (s *SQLiteStore) RecordAccess(ctx context.Context, id string) error

RecordAccess updates the last accessed timestamp and access count.

func (*SQLiteStore) Search

func (s *SQLiteStore) Search(ctx context.Context, query string, sessionID string, limit int) ([]*Artifact, error)

Search performs FTS5 full-text search on artifacts.

func (*SQLiteStore) Update

func (s *SQLiteStore) Update(ctx context.Context, artifact *Artifact) error

Update updates artifact metadata.

type SessionArtifactMetadata added in v1.4.0

type SessionArtifactMetadata struct {
	SessionID string `json:"session_id"`
	AgentID   string `json:"agent_id,omitempty"`
	AgentName string `json:"agent_name,omitempty"`
	StartedAt string `json:"started_at"` // RFC3339 UTC
	EndedAt   string `json:"ended_at,omitempty"`
	Status    string `json:"status,omitempty"`
	// Context holds non-sensitive attribution keys only (IDs, not secrets).
	Context map[string]string `json:"context,omitempty"`
	// Artifacts is optional; populated when callers compute counts (e.g. future work).
	Artifacts *SessionArtifactStats `json:"artifacts,omitempty"`
}

SessionArtifactMetadata is persisted next to agent/, user/, and scratchpad/ under $LOOM_DATA_DIR/artifacts/sessions/<session_id>/.

func BuildSessionArtifactMetadata added in v1.4.0

func BuildSessionArtifactMetadata(session *types.Session) (*SessionArtifactMetadata, error)

BuildSessionArtifactMetadata maps a conversation session into filesystem metadata. Only whitelisted context keys are copied to avoid persisting secrets from Context. Status is initialized to "active" for live sessions; SyncSessionArtifactMetadata preserves an existing on-disk "completed" status and ended_at so completed sessions are not resurrected.

func ReadSessionArtifactMetadata added in v1.4.0

func ReadSessionArtifactMetadata(sessionID string) (*SessionArtifactMetadata, error)

ReadSessionArtifactMetadata reads and parses metadata.json under the session artifact root. Path containment is enforced by SessionArtifactsRoot, which every metadata path derives from. If the file is missing, the error typically wraps os.ErrNotExist.

type SessionArtifactStats added in v1.4.0

type SessionArtifactStats struct {
	Created        int   `json:"created"`
	TotalSizeBytes int64 `json:"total_size_bytes"`
}

SessionArtifactStats summarizes indexed artifacts for a session (optional).

type SourceType

type SourceType string

SourceType defines the source of an artifact.

const (
	SourceUser      SourceType = "user"
	SourceGenerated SourceType = "generated"
	SourceAgent     SourceType = "agent"
)

type Stats

type Stats struct {
	TotalFiles     int
	TotalSizeBytes int64
	UserFiles      int
	GeneratedFiles int
	DeletedFiles   int
}

Stats holds artifact storage statistics.

type Watcher

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

Watcher manages hot-reload for artifacts directory.

func NewWatcher

func NewWatcher(store ArtifactStore, config WatcherConfig) (*Watcher, error)

NewWatcher creates a new hot-reload watcher for the artifacts directory.

func (*Watcher) Start

func (w *Watcher) Start(ctx context.Context) error

Start begins watching for artifact file changes.

func (*Watcher) Stop

func (w *Watcher) Stop() error

Stop stops the watcher.

func (*Watcher) WithTracer

func (w *Watcher) WithTracer(tracer observability.Tracer) *Watcher

WithTracer sets the observability tracer for the watcher.

type WatcherConfig

type WatcherConfig struct {
	Enabled    bool                   // Enable hot-reload
	DebounceMs int                    // Debounce delay in milliseconds (default: 500ms)
	Logger     *zap.Logger            // Logger for events
	OnCreate   ArtifactUpdateCallback // Callback for new artifacts (optional)
	OnModify   ArtifactUpdateCallback // Callback for modified artifacts (optional)
	OnDelete   ArtifactUpdateCallback // Callback for deleted artifacts (optional)
}

WatcherConfig configures hot-reload behavior for artifact directory.

Jump to

Keyboard shortcuts

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