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.
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.
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.
Index ¶
- Constants
- func AgentNameFromSession(session *types.Session) string
- func CompleteSessionArtifactMetadata(sessionID string) error
- func ComputeChecksum(path string) (string, error)
- func EnsureArtifactDir(sessionID string, source SourceType) error
- func EnsureScratchpadDir(sessionID string) error
- func ExtractArchive(archivePath, destDir string) ([]string, error)
- func ExtractSessionIDFromPath(path string) string
- func FilterPublicArtifactContext(m map[string]string) map[string]string
- func GenerateArtifactID() string
- func GetArtifactDir(sessionID string, source SourceType) (string, error)
- func GetArtifactsDir() (string, error)
- func GetScratchpadDir(sessionID string) (string, error)
- func IsArchive(contentType string) bool
- func SessionArtifactsRoot(sessionID string) (string, error)
- func SessionMetadataEnabled() bool
- func SetSessionMetadataEnabled(v bool)
- func StringFromContext(m map[string]interface{}, key string) (string, bool)
- func SyncSessionArtifactMetadata(ctx context.Context, session *types.Session) error
- func ValidateSessionID(sessionID string) error
- func WriteSessionArtifactMetadata(meta *SessionArtifactMetadata) error
- type AnalysisResult
- type Analyzer
- type Artifact
- type ArtifactStore
- type ArtifactUpdateCallback
- type Filter
- type SQLiteStore
- func (s *SQLiteStore) Close() error
- func (s *SQLiteStore) Delete(ctx context.Context, id string, hard bool) error
- func (s *SQLiteStore) Get(ctx context.Context, id string) (*Artifact, error)
- func (s *SQLiteStore) GetByName(ctx context.Context, name string, sessionID string) (*Artifact, error)
- func (s *SQLiteStore) GetStats(ctx context.Context) (*Stats, error)
- func (s *SQLiteStore) Index(ctx context.Context, artifact *Artifact) error
- func (s *SQLiteStore) List(ctx context.Context, filter *Filter) ([]*Artifact, error)
- func (s *SQLiteStore) RecordAccess(ctx context.Context, id string) error
- func (s *SQLiteStore) Search(ctx context.Context, query string, sessionID string, limit int) ([]*Artifact, error)
- func (s *SQLiteStore) Update(ctx context.Context, artifact *Artifact) error
- type SessionArtifactMetadata
- type SessionArtifactStats
- type SourceType
- type Stats
- type Watcher
- type WatcherConfig
Constants ¶
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
AgentNameFromSession returns a display name from session context keys agent_name / agentName.
func CompleteSessionArtifactMetadata ¶ added in v1.4.0
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 ¶
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
EnsureScratchpadDir creates scratchpad directory if it doesn't exist.
func ExtractArchive ¶
ExtractArchive extracts an archive to a destination directory. Returns a list of extracted file paths.
func ExtractSessionIDFromPath ¶ added in v1.1.0
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
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 ¶
GetArtifactsDir returns the artifacts directory path.
func GetScratchpadDir ¶ added in v1.1.0
GetScratchpadDir returns the scratchpad directory for a session. Scratchpad is ephemeral storage for notes and scratch work, not indexed.
func SessionArtifactsRoot ¶ added in v1.4.0
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
StringFromContext returns a non-empty trimmed string for key when present.
func SyncSessionArtifactMetadata ¶ added in v1.4.0
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
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 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 ¶
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) 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) RecordAccess ¶
func (s *SQLiteStore) RecordAccess(ctx context.Context, id string) error
RecordAccess updates the last accessed timestamp and access count.
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) 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.