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 ¶
- type Artifact
- type SpillWriter
- type Store
- func (s *Store) GC(maxAge time.Duration)
- func (s *Store) Preview(id string, n int) (string, error)
- func (s *Store) Read(id string, offset, limit int) (string, Artifact, error)
- func (s *Store) Save(sessionID, toolCallID, toolName string, r io.Reader) (Artifact, error)
- func (s *Store) SaveFile(sessionID, toolCallID, toolName, srcPath string) (Artifact, error)
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.
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
Save persists r's content as a new artifact and returns its metadata. The sha256 is computed while streaming, not as a second pass.