artifact

package
v0.7.0 Latest Latest
Warning

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

Go to latest
Published: Aug 28, 2026 License: MIT Imports: 12 Imported by: 0

Documentation

Overview

Package artifact persists oversized tool output to disk instead of forcing it either into RAM/context unbounded or truncating it away — the two bad options a tool result otherwise has. A large result is saved once, given a short id, and made available for the model to read back in bounded slices (artifact_read) instead of ever appearing in the conversation transcript in full.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Artifact

type Artifact struct {
	ID         string `json:"id"`
	SessionID  string `json:"session_id,omitempty"`
	ToolCallID string `json:"tool_call_id,omitempty"`
	ToolName   string `json:"tool_name"`
	CreatedAt  int64  `json:"created_at"`
	Size       int64  `json:"size"`
	SHA256     string `json:"sha256"`
}

Artifact is one persisted tool result's metadata — the content itself lives in a sibling file, not in this struct, so listing/inspecting metadata never requires reading a potentially huge payload.

type SpillWriter

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

SpillWriter is an io.Writer that buffers up to threshold bytes in memory, then — the moment a write would cross that line — switches to streaming straight to a temp file for everything after. This exists to fix a real bug class: a tool that captured output as `var out bytes.Buffer` and only checked a size cap *after* cmd.Run() returned had already let an unbounded amount of memory accumulate for however long the command produced output. Capping only the *reported* size never capped the actual RAM used to get there. Using SpillWriter as cmd.Stdout/Stderr bounds memory use for the entire lifetime of the command, not just the final result.

func NewSpillWriter

func NewSpillWriter(threshold int) *SpillWriter

NewSpillWriter returns a writer that keeps up to threshold bytes in memory before spilling the rest to disk.

func (*SpillWriter) Bytes

func (w *SpillWriter) Bytes() []byte

Bytes returns everything written so far, if it never spilled. Calling it after Spilled() is true returns nothing useful — read TempPath() instead.

func (*SpillWriter) Close

func (w *SpillWriter) Close() error

Close closes (but does not remove) the underlying temp file, if any — callers that don't persist the spill into the artifact store should os.Remove(w.TempPath()) themselves after Close.

func (*SpillWriter) Spilled

func (w *SpillWriter) Spilled() bool

Spilled reports whether output crossed the threshold and moved to disk.

func (*SpillWriter) TempPath

func (w *SpillWriter) TempPath() string

TempPath is the spill file's path once Spilled() is true, "" otherwise. The caller owns cleanup: either persist it into the artifact store (Store.SaveFile removes the source on success) or remove it directly.

func (*SpillWriter) Total

func (w *SpillWriter) Total() int64

Total is how many bytes were written in all, spilled or not.

func (*SpillWriter) Write

func (w *SpillWriter) Write(p []byte) (int, error)

type Store

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

Store persists artifacts under <workspace>/.kram/artifacts — project- scoped like everything else in .kram, and daemon-lifetime rather than session-lifetime (an artifact created by one session should stay readable if referenced from another, same reasoning as background.go's processManager).

func Open

func Open(workspace string) *Store

Open returns a Store rooted at workspace's .kram/artifacts directory. It doesn't touch disk until Save/Read actually needs to.

func (*Store) GC

func (s *Store) GC(maxAge time.Duration)

GC deletes artifacts whose metadata is older than maxAge — best-effort disk hygiene, never correctness-critical: a garbage-collected artifact simply becomes an unresolvable id if referenced later, the same outcome as if it had never been created. Called once at Registry construction (daemon startup), not on a timer — a long-lived daemon process restarts often enough in practice (every workspace re-open) that this is enough to keep .kram/artifacts from growing forever.

func (*Store) Preview

func (s *Store) Preview(id string, n int) (string, error)

Preview returns the first n bytes of an artifact's content — the "too large, here's a taste" text shown right after a spill.

func (*Store) Read

func (s *Store) Read(id string, offset, limit int) (string, Artifact, error)

Read returns a slice of one artifact's content: up to limit bytes starting at offset (both clamped to sane values — offset < 0 becomes 0, limit <= 0 or too large becomes defaultReadLimit), plus its metadata.

func (*Store) Save

func (s *Store) Save(sessionID, toolCallID, toolName string, r io.Reader) (Artifact, error)

Save persists r's content as a new artifact and returns its metadata. The sha256 is computed while streaming, not as a second pass.

func (*Store) SaveFile

func (s *Store) SaveFile(sessionID, toolCallID, toolName, srcPath string) (Artifact, error)

SaveFile persists the content of an existing file — typically a SpillWriter's temp file — as a new artifact, and removes the source file on success: the source is consumed, not left behind for the caller to also clean up.

Jump to

Keyboard shortcuts

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