Artifacts
An artifact is the ADK's answer to a tool that produces more output than belongs in a context window. A build log, a generated file, a fetched document, a query result with thousands of rows — these are the outputs every honest agent eventually has to deal with, and inlining them into the message stream is the wrong answer to almost all of them. SpooledArtifact is the alternative: a typed handle the model can hold, with a small library of query operations the model can use to look at the parts of the result it actually needs, leaving the rest of the bytes where they are.
Don't blow up the context window
Inlining every tool's full output into the message stream is the most common way a working agent silently turns into a broken one. Latency climbs, costs climb, the model's attention thins out across material it did not need, and the failure mode that surfaces first is "the agent got worse at the task" rather than "we exceeded a budget" — which makes it expensive to diagnose and easy to misattribute. This is not a theoretical concern; it is one of the most frequent failure modes across every agentic framework in production, including the well-established ones. The ADK's response is unconditional in shape but split across two layers: a Tool handler that returns string or Uint8Array carries no artifact yet — the consumer's executor is responsible for taking that return value and wrapping it via tool.artifactConstructor?.() ?? SpooledArtifact before it can become a ToolCall result. That executor wrap is the spool gate, and once it happens the rest of the loop sees a SpooledArtifact instead of raw bytes. There are two carve-outs — ArtifactTool results (which would otherwise recurse: an artifact_grep on a grep result, spooled, then grepped again) and Media / Media[] returns (the explicit-modality path for bytes the provider renders inline as a native content block, where "spool and forge handle tools" is the wrong shape). Both are documented under What a Tool is. From there you make an explicit choice: query the handle, summarise it, persist it, or deliberately inline it with SpooledArtifact.asString. What you do not get to do is accidentally pour a multi-megabyte log into the next prompt because nobody touched a flag. The executor wrap turns raw bytes into a handle-shaped artifact; abusing that handle is on you.
What SpooledArtifact is
A SpooledArtifact is the text handle. It gives the model SpooledArtifact.head/SpooledArtifact.tail/SpooledArtifact.grep/SpooledArtifact.cat instead of a wall of bytes. If the model needs the whole body, it asks for the whole body via SpooledArtifact.asString. Nothing gets dumped into the prompt by accident. The full POSIX-shaped surface is SpooledArtifact.head, SpooledArtifact.tail, SpooledArtifact.grep, SpooledArtifact.cat, SpooledArtifact.byteLength, SpooledArtifact.lineCount, SpooledArtifact.estimateTokens, SpooledArtifact.asString; the reader is structural so the backing store can be in-memory, on disk, or paged across the network.
→ Continue reading: What SpooledArtifact is
Subclasses and the closed set of methods
SpooledJsonArtifact and SpooledMarkdownArtifact add typed query surfaces over their parsed bodies. Each class owns its own toolMethods array; forgeTools is what does the merging, not the descriptor array.
→ Continue reading: Subclasses and the closed set of methods
forgeTools(ctx) and the ephemeral lifecycle
SpooledArtifact.forgeTools walks DispatchContext.turnToolCalls, mints one ArtifactTool per descriptor against the matching artifacts, and returns an ephemeral ToolRegistry. ToolRegistry.bindContext wires the cleanup so the forged tools are pruned on ack.
→ Continue reading: forgeTools(ctx) in depth
Subclass extension pattern
Each SpooledArtifact subclass owns its own toolMethods array and overrides forgeTools to call the base first, then merge its own descriptors. Narrowing is plain isInstanceOf(tc.results, ThisClass.name, ThisClass) at each subclass's own filter site.
→ Continue reading: Subclass extension pattern
ArtifactTool
ArtifactTool is the Tool subclass that backs every forged artifact-query tool. It returns string or Tokenizable rather than another artifact, and its schema forbids artifactConstructor to keep the recursion break clean.
→ Continue reading: ArtifactTool
What artifacts do not do
The artifact surface is data shape, not policy. It does not authorise, deduplicate, impose size policy, or auto-stream content into messages.
→ Continue reading: What artifacts do not do
Sibling: Media
SpooledArtifact is the line-indexed text handle citizen. Media is the binary streaming sibling — same handle-pattern posture, deliberately disjoint reader contract, because text and binary have nothing useful to share at the surface.
→ Continue reading: Sibling: Media