Skip to content
10 min read · 1,981 words

Class: SpooledArtifact ​

Defined in: src/lib/classes/spooled_artifact.ts:291

A lazy, line-oriented view over an arbitrary backing store.

Remarks ​

All I/O methods are async to remain compatible with both in-memory and streaming @nhtio/adk!SpoolReader implementations. Token estimation delegates to @nhtio/adk!Tokenizable.estimateTokens — the same backends used elsewhere in the ADK.

The class is read-only by design: mutation of the underlying data is the responsibility of the producer that created the @nhtio/adk!SpoolReader, not the consumer reading from this artifact.

Extended by ​

Constructors ​

Constructor ​

ts
new SpooledArtifact(reader: SpoolReader): SpooledArtifact;

Defined in: src/lib/classes/spooled_artifact.ts:322

Parameters ​

ParameterTypeDescription
readerSpoolReaderThe backing store to read from.

Returns ​

SpooledArtifact

Throws ​

@nhtio/adk!E_NOT_A_SPOOL_READER when reader does not implement @nhtio/adk!SpoolReader.

Properties ​

PropertyModifierTypeDefault valueDescriptionDefined in
toolMethodsstaticreadonly ToolMethodDescriptor[]baseToolMethodsThe set of artifact-query methods this class surfaces via SpooledArtifact.forgeTools. Remarks The base set covers the generic line-oriented operations every artifact supports: artifact_head, artifact_tail, artifact_grep, artifact_cat, artifact_byte_length, artifact_line_count, artifact_estimate_tokens. Each toolMethods array lists only its own class's descriptors — subclasses do not concatenate inherited descriptors. The subclass instead overrides SpooledArtifact.forgeTools to merge the base registry (produced by SpooledArtifact.forgeTools(ctx)) with its own — see @nhtio/adk!SpooledJsonArtifact.forgeTools and @nhtio/adk!SpooledMarkdownArtifact.forgeTools for the canonical shape and the pattern downstream consumers should follow when building their own SpooledArtifact subclasses. Tool names are absolute (not subclass-prefixed). Forged tools carry Tool.onCollision = 'replace' so merging multiple subclasses' forgeTools() outputs is silent — every same-named tool dispatches the same method on whatever artifact the callId resolves to, so the overlap is behaviourally interchangeable. Frozen at module load.src/lib/classes/spooled_artifact.ts:313

Methods ​

[ENCODE_METHOD]() ​

ts
ENCODE_METHOD: unknown;

Defined in: src/lib/classes/spooled_artifact.ts:361

Serialise this SpooledArtifact into an @nhtio/encoder snapshot — the reader handle, not the bytes.

Returns ​

unknown

A snapshot consumed by SpooledArtifact.[DECODE_METHOD].

Remarks ​

Emits the backing reader's ReaderDescriptor; decode re-binds the reader through the registered resolver. Throws @nhtio/adk!E_READER_NOT_DESCRIBABLE when the reader cannot describe itself. Subclasses override this to include their own discriminators (e.g. @nhtio/adk!SpooledJsonArtifact adds format).


asString() ​

ts
asString(): Promise<string>;

Defined in: src/lib/classes/spooled_artifact.ts:677

Returns the full artifact body as a single byte-faithful string.

Returns ​

Promise<string>

The full content as a single string.

Remarks ​

Round-trip faithful to whatever bytes the @nhtio/adk!SpoolReader was constructed over — preserves trailing newlines and non-\n line terminators that SpooledArtifact.cat discards via its line-based view. This is the canonical primitive for "inline the artifact content directly into a message" use cases.

asString() and the static forgeTools(ctx) factory on each subclass are independent alternatives — a consumer chooses per turn whether to inline the body in a message (await tc.results.asString()) or hand the model query tools (SpooledArtifact.forgeTools(ctx)). Neither calls the other; either works with neither.


byteLength() ​

ts
byteLength(): Promise<number>;

Defined in: src/lib/classes/spooled_artifact.ts:548

Returns the total byte length of the underlying data.

Returns ​

Promise<number>

The byte length as reported by the @nhtio/adk!SpoolReader.


cat() ​

ts
cat(start?: number, end?: number): Promise<string[]>;

Defined in: src/lib/classes/spooled_artifact.ts:529

Returns lines from the artifact, optionally bounded to a range.

Parameters ​

ParameterTypeDescription
start?number0-based start line index (inclusive). Defaults to 0.
end?number0-based end line index (exclusive). Defaults to lineCount().

Returns ​

Promise<string[]>

Array of line strings in the requested range.

Remarks ​

Without arguments, returns all lines — equivalent to POSIX cat. With start and/or end, behaves like Array.prototype.slice: start defaults to 0, end defaults to the total line count, and only lines in [start, end) are fetched from the backing store. For large artifacts, prefer a bounded range or SpooledArtifact.head / SpooledArtifact.tail.


estimateHandleTokens() ​

ts
estimateHandleTokens(
   callId: string,
   encoding:
  | "gpt2"
  | "r50k_base"
  | "p50k_base"
  | "p50k_edit"
  | "cl100k_base"
  | "o200k_base"
  | "gemini"
  | "gemma"
  | "llama2"
  | "claude",
   renderer?: (input: {
  artifact: unknown;
  byteLength: number;
  callId: string;
  encoding?: string;
  estimatedTokens?: number;
  lineCount: number;
}) => string): number;

Defined in: src/lib/classes/spooled_artifact.ts:594

Estimates tokens for the exact handle-body metadata rendered for this artifact.

The fallback renderer is an interim core-safe implementation. Its output is deliberately specified here for fan-in parity: the lines are the fixed prose and metadata strings below, with one \n separator, and method entries formatted as - name — description (or - name). The canonical renderer in chat_common should eventually delegate to this builder rather than maintain a second copy.

Parameters ​

ParameterTypeDescription
callIdstringThe turn-local artifact identifier.
encoding| "gpt2" | "r50k_base" | "p50k_base" | "p50k_edit" | "cl100k_base" | "o200k_base" | "gemini" | "gemma" | "llama2" | "claude"The token encoding used for estimation.
renderer?(input: { artifact: unknown; byteLength: number; callId: string; encoding?: string; estimatedTokens?: number; lineCount: number; }) => stringOptional renderer overriding the interim default.

Returns ​

number

A synchronous token estimate.


estimateTokens() ​

ts
estimateTokens(encoding:
  | "gpt2"
  | "r50k_base"
  | "p50k_base"
  | "p50k_edit"
  | "cl100k_base"
  | "o200k_base"
  | "gemini"
  | "gemma"
  | "llama2"
| "claude"): Promise<number>;

Defined in: src/lib/classes/spooled_artifact.ts:656

Estimates the total token count of the artifact under encoding.

Parameters ​

ParameterTypeDescription
encoding| "gpt2" | "r50k_base" | "p50k_base" | "p50k_edit" | "cl100k_base" | "o200k_base" | "gemini" | "gemma" | "llama2" | "claude"The encoding identifier to use for counting.

Returns ​

Promise<number>

The estimated number of tokens.

Remarks ​

Reads the full byte-faithful content via SpooledArtifact.asString (which delegates to @nhtio/adk!SpoolReader.readAll) and delegates to @nhtio/adk!Tokenizable.estimateTokens. The estimate therefore reflects the actual source bytes — including trailing newlines and non-\n line terminators that the line-based SpooledArtifact.cat view would otherwise discard or misrepresent.


grep() ​

ts
grep(pattern: RegExp): Promise<string[]>;

Defined in: src/lib/classes/spooled_artifact.ts:501

Returns all lines that match pattern.

Parameters ​

ParameterTypeDescription
patternRegExpThe regular expression to test each line against.

Returns ​

Promise<string[]>

Array of matching line strings, in order.

Remarks ​

Behaves like POSIX grep: each line is tested against the pattern and included in the result when it matches. The pattern is applied as a JavaScript RegExp; flags (e.g. case- insensitivity) should be encoded in the expression itself.

Stateful flags (g, y) on the supplied RegExp would normally cause pattern.test() to advance lastIndex across calls, producing skipped matches and order-dependent results. To keep the per-line semantics stateless, grep resets pattern.lastIndex to 0 before each line test. The forged artifact_grep tool also rejects g and y flags up-front at schema validation time.


hasSizeHints() ​

ts
hasSizeHints(): boolean;

Defined in: src/lib/classes/spooled_artifact.ts:567

Returns whether producer-computed size metadata is available.

Returns ​

boolean

true when SpooledArtifact._setSizeHints has populated the cache.


ts
head(n?: number): Promise<string[]>;

Defined in: src/lib/classes/spooled_artifact.ts:448

Returns the first n lines of the artifact.

Parameters ​

ParameterTypeDefault valueDescription
nnumber10Number of lines to return. Defaults to 10.

Returns ​

Promise<string[]>

Array of line strings, without trailing newlines.

Remarks ​

If the artifact contains fewer than n lines, all available lines are returned. Matches the behaviour of POSIX head -n.


lineCount() ​

ts
lineCount(): Promise<number>;

Defined in: src/lib/classes/spooled_artifact.ts:576

Returns the total number of lines in the artifact.

Returns ​

Promise<number>

The line count as reported by the @nhtio/adk!SpoolReader.


tail() ​

ts
tail(n?: number): Promise<string[]>;

Defined in: src/lib/classes/spooled_artifact.ts:471

Returns the last n lines of the artifact.

Parameters ​

ParameterTypeDefault valueDescription
nnumber10Number of lines to return. Defaults to 10.

Returns ​

Promise<string[]>

Array of line strings, without trailing newlines.

Remarks ​

If the artifact contains fewer than n lines, all available lines are returned. Matches the behaviour of POSIX tail -n.


[DECODE_METHOD]() ​

ts
static DECODE_METHOD: SpooledArtifact;

Defined in: src/lib/classes/spooled_artifact.ts:375

Reconstruct a SpooledArtifact from an SpooledArtifact.[ENCODE_METHOD] snapshot.

Parameters ​

ParameterTypeDescription
dataunknownThe snapshot produced by SpooledArtifact.[ENCODE_METHOD].

Returns ​

SpooledArtifact

A fresh SpooledArtifact backed by a freshly-resolved reader.

Remarks ​

Re-binds the reader via @nhtio/adk!resolveSpoolReader; throws @nhtio/adk!E_NO_READER_RESOLVER when no resolver is registered for the descriptor's tag.


forgeTools() ​

ts
static forgeTools(ctx: DispatchContext): ToolRegistry;

Defined in: src/lib/classes/spooled_artifact.ts:741

Forges a fresh @nhtio/adk!ToolRegistry of ephemeral @nhtio/adk!ArtifactTool instances that let the LLM query artifacts already present in ctx.turnToolCalls.

Parameters ​

ParameterTypeDescription
ctxDispatchContextThe execution context whose turnToolCalls snapshot defines the callId enum.

Returns ​

ToolRegistry

A fresh ToolRegistry. Empty when turnToolCalls contains no compatible artifacts.

Remarks ​

Standard subclass extension pattern — each class owns only its own toolMethods and its own forgeTools. The base SpooledArtifact.forgeTools(ctx) narrows the callId enum to any tc.results instanceof SpooledArtifact (so subclass instances are included — that's the whole point of inheritance) and dispatches the seven base methods (head, tail, grep, cat, byteLength, lineCount, estimateTokens) on the resolved artifact. Subclasses override forgeTools to call this static first and then register their own tools on the returned registry — see @nhtio/adk!SpooledJsonArtifact.forgeTools and @nhtio/adk!SpooledMarkdownArtifact.forgeTools for the canonical shape. There is no requiresSubclass field, no helper indirection, and no this-based class narrowing — just plain instanceof ThisClass at each subclass's own filter site.

For each descriptor in this class's toolMethods, the factory:

  1. Walks ctx.turnToolCalls to find ToolCalls whose results instanceof SpooledArtifact. ToolCalls flagged fromArtifactTool === true are excluded — they carry a @nhtio/adk!Tokenizable, not a SpooledArtifact, and including them would let the model artifact_grep on a previous artifact_grep result (an infinite-recursion hazard with no semantic value).
  2. Returns an empty registry if no compatible callIds are found — no point shipping tools whose callId enum is empty.
  3. Otherwise mints an @nhtio/adk!ArtifactTool with ephemeral: true and onCollision: 'replace' so multiple Subclass.forgeTools(ctx) outputs merge silently. The tool's inputSchema includes a required callId field with .valid(...compatibleIds), plus the descriptor's own argsSchema fields.

The handler resolves the artifact via [...ctx.turnToolCalls].find(t => t.id === callId), dispatches the descriptor's method, and serialises the return value (string → as-is; string[] → newline-join; number → String(n); otherwise JSON.stringify(value, null, 2); descriptor.serialise overrides the defaults). grep is special-cased: the handler constructs new RegExp(pattern, flags ?? '') before invoking the artifact's grep method.

The returned registry must be merged into the consumer's main registry and the main registry must be bound to ctx via @nhtio/adk!ToolRegistry.bindContext:

ts
const executor: DispatchExecutorFn = async (ctx) => {
  const forged = SpooledArtifact.forgeTools(ctx)
  const merged = ToolRegistry.merge([main, forged])
  main.bindContext(ctx)
  const result = await llm.invoke({ tools: merged.all(), ... })
  ctx.ack() // ← ephemeral cleanup fires here
}

​

WARNING

You must call registry.bindContext(ctx) on the registry hosting these tools, or ephemeral cleanup will not run and the callId enum in subsequent executor calls will be stale (excluding new tool calls produced in the meantime).

See ​


isSpooledArtifact() ​

ts
static isSpooledArtifact(value: unknown): value is SpooledArtifact;

Defined in: src/lib/classes/spooled_artifact.ts:408

Returns true if value is a SpooledArtifact instance (including any subclass).

Parameters ​

ParameterTypeDescription
valueunknownThe value to test.

Returns ​

value is SpooledArtifact

true when value is a SpooledArtifact instance.

Remarks ​

Uses the cross-realm-safe @nhtio/adk!isInstanceOf guard: instanceof first, then Symbol.hasInstance, then a constructor.name fallback. Subclass instances (e.g. @nhtio/adk!SpooledJsonArtifact) satisfy this guard because instanceof walks the prototype chain. The fallbacks handle the dual-module-copy case where two distinct SpooledArtifact classes coexist in the same realm (e.g. one bundled into a downstream library, one in the consumer's node_modules).


isSpooledArtifactConstructor() ​

ts
static isSpooledArtifactConstructor(value: unknown): value is SpooledArtifactConstructor<SpooledArtifact>;

Defined in: src/lib/classes/spooled_artifact.ts:425

Returns true if value is a constructor function whose prototype chain includes SpooledArtifact (including SpooledArtifact itself).

Parameters ​

ParameterTypeDescription
valueunknownThe value to test.

Returns ​

value is SpooledArtifactConstructor<SpooledArtifact>

true when value is a constructor for SpooledArtifact or a subclass.

Remarks ​

Used by @nhtio/adk!Tool to validate the optional artifactConstructor field. Performs an instanceof-based check on the prototype chain; falls back to a duck-type test that looks for the canonical SpooledArtifact instance methods on value.prototype for cross-realm safety (constructors passed from a different module copy or VM context).