Documentation Index
Fetch the complete documentation index at: https://docs.agentium.in/llms.txt
Use this file to discover all available pages before exploring further.
Memory Pointer Pattern
The problem
When a tool returns 200KB of logs, or 5,000 database rows, or a full PDF, passing the raw output back into the LLM context causes three failures:- Token blowup. A 200KB log dump is ~50,000 tokens. At GPT-4o’s 0.13 per turn just to feed the model context it doesn’t need.
- Silent truncation. Most providers cap at 128K–200K tokens. A few big tool calls can break the conversation entirely.
- Worse answers. Models get distracted by huge irrelevant blobs (the “needle in a haystack” problem).
How it works
getArtifact(pointer) tool.
Enabling on an Agent
ArtifactsConfig fields
| Field | Type | Default | Meaning |
|---|---|---|---|
enabled | boolean | false | Master switch. When true, the auto-conversion runs after every tool call. |
maxToolOutputBytes | number | 51200 (50KB) | Threshold above which the result becomes an artifact. UTF-8 byte length of the stringified content. |
previewChars | number | 200 | How many chars of the original content are kept in the visible preview field. |
enabled: true, three tools are auto-injected into the agent’s tool list:
storeArtifact(name, value, contentType?)getArtifact(pointerOrName)listArtifacts()
The auto-converted result
When a tool exceeds the threshold, its result is replaced with a JSON string of this shape:preview field is critical — it lets the model decide whether the artifact is interesting before fetching the full value.
Manual artifact storage
Tools can opt into the pattern explicitly:getArtifact("report-2024-q4") (by name, not pointer) and gets the full text.
API reference
storeArtifact(ctx, value, opts?)
Stores a value and returns a pointer.
value can be any JSON-serializable object or a string. Objects are JSON.stringify’d for preview and size computation; the raw value is preserved for retrieval.
getArtifact(ctx, pointerOrName)
Looks up an artifact by either its art: pointer or its name. Returns null for missing.
listArtifacts(ctx)
Returns every artifact stored in the current RunContext, deduplicated (name aliases don’t double-count).
isPointer(value)
Helper for runtime checks:
approxByteSize(value)
Quick UTF-8 size estimate used by the executor:
Auto-injected tools (when artifacts.enabled)
| Tool name | Parameters | Returns |
|---|---|---|
storeArtifact | { name: string, value: string, contentType?: string } | JSON { pointer, preview, sizeBytes, name } |
getArtifact | { pointerOrName: string } | The raw value (or "[no artifact found for '...']") |
listArtifacts | {} | JSON array of { pointer, name, sizeBytes, preview, contentType, storedAt } |
storeArtifact calls would recursively wrap their own output.
Lifecycle and scope
Artifacts live onRunContext.sessionState["__artifacts"] as a Map<string, StoredArtifact>. That means:
- Per-run by default: A new
RunContextstarts with an empty map. - Per-session if you persist
sessionState: Pass the samesessionStatebetween runs (e.g. via your session manager) and artifacts carry forward. - Not persisted by default: The default
SessionManagerdoes writesessionStateto storage, but the map serializes to[]unless you use a JSON-aware codec. For durable artifacts, persist them explicitly to your own storage and re-hydrate.
When to use
- Database query tools that may return many rows
- Web scraping / page fetch tools
- Log search tools
- File reading tools where files can be > a few hundred KB
- Tool chains where output of step N is input to step N+1 but doesn’t need to pass through the LLM in between
When NOT to use
- Short status checks (“is X online?”) — overhead isn’t worth it
- Single-row lookups
- Anything the LLM legitimately needs to reason over inline (e.g. a small JSON config)
- Streaming output (the threshold check is one-shot on the final result)
See also
- Tool Polish —
toModelOutputis a more surgical way to shrink specific tool outputs. - Async HandleId Pattern — pair pointers with handles for long-running + large-output tools.