---
url: 'https://adk.nht.io/api/@nhtio/adk/spooled_artifact/classes/SpooledArtifact.md'
description: 'A lazy, line-oriented view over an arbitrary backing store.'
---

# Class: SpooledArtifact

Defined in: [lib/classes/spooled\_artifact.ts:169](https://github.com/NHTIO/ADK/blob/v1.20260605.0/src/lib/classes/spooled_artifact.ts#L169)

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](../interfaces/SpoolReader.md) implementations. Token estimation delegates to
[@nhtio/adk!Tokenizable.estimateTokens](../../common/classes/Tokenizable.md#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](../interfaces/SpoolReader.md), not the consumer reading from this artifact.

## Extended by

* [`SpooledJsonArtifact`](SpooledJsonArtifact.md)
* [`SpooledMarkdownArtifact`](SpooledMarkdownArtifact.md)

## Constructors

### Constructor

```ts
new SpooledArtifact(reader: SpoolReader): SpooledArtifact;
```

Defined in: [lib/classes/spooled\_artifact.ts:199](https://github.com/NHTIO/ADK/blob/v1.20260605.0/src/lib/classes/spooled_artifact.ts#L199)

#### Parameters

| Parameter | Type                                          | Description                     |
| --------- | --------------------------------------------- | ------------------------------- |
| `reader`  | [`SpoolReader`](../interfaces/SpoolReader.md) | The backing store to read from. |

#### Returns

`SpooledArtifact`

#### Throws

[@nhtio/adk!E\_NOT\_A\_SPOOL\_READER](../../exceptions/variables/E_NOT_A_SPOOL_READER.md) when `reader` does not implement [@nhtio/adk!SpoolReader](../interfaces/SpoolReader.md).

## Properties

| Property                                        | Modifier | Type                                                                                 | Default value     | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 | Defined in                                                                                                                      |
| ----------------------------------------------- | -------- | ------------------------------------------------------------------------------------ | ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
|  `toolMethods` | `static` | readonly [`ToolMethodDescriptor`](../../common/interfaces/ToolMethodDescriptor.md)\[] | `baseToolMethods` | The set of artifact-query methods this class surfaces via [SpooledArtifact.forgeTools](#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](#forgetools) to merge the base registry (produced by `SpooledArtifact.forgeTools(ctx)`) with its own — see [@nhtio/adk!SpooledJsonArtifact.forgeTools](SpooledJsonArtifact.md#forgetools) and [@nhtio/adk!SpooledMarkdownArtifact.forgeTools](SpooledMarkdownArtifact.md#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. | [lib/classes/spooled\_artifact.ts:191](https://github.com/NHTIO/ADK/blob/v1.20260605.0/src/lib/classes/spooled_artifact.ts#L191) |

## Methods

### asString()

```ts
asString(): Promise<string>;
```

Defined in: [lib/classes/spooled\_artifact.ts:421](https://github.com/NHTIO/ADK/blob/v1.20260605.0/src/lib/classes/spooled_artifact.ts#L421)

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](../interfaces/SpoolReader.md) was constructed over —
preserves trailing newlines and non-`\n` line terminators that [SpooledArtifact.cat](#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: [lib/classes/spooled\_artifact.ts:374](https://github.com/NHTIO/ADK/blob/v1.20260605.0/src/lib/classes/spooled_artifact.ts#L374)

Returns the total byte length of the underlying data.

#### Returns

`Promise`<`number`>

The byte length as reported by the [@nhtio/adk!SpoolReader](../interfaces/SpoolReader.md).

***

### cat()

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

Defined in: [lib/classes/spooled\_artifact.ts:355](https://github.com/NHTIO/ADK/blob/v1.20260605.0/src/lib/classes/spooled_artifact.ts#L355)

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

#### Parameters

| Parameter | Type     | Description                                                    |
| --------- | -------- | -------------------------------------------------------------- |
| `start?`  | `number` | 0-based start line index (inclusive). Defaults to `0`.         |
| `end?`    | `number` | 0-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](#head) / [SpooledArtifact.tail](#tail).

***

### estimateTokens()

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

Defined in: [lib/classes/spooled\_artifact.ts:400](https://github.com/NHTIO/ADK/blob/v1.20260605.0/src/lib/classes/spooled_artifact.ts#L400)

Estimates the total token count of the artifact under `encoding`.

#### Parameters

| Parameter  | Type                                                                                                                                          | Description                                  |
| ---------- | --------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------- |
| `encoding` | | `"gpt2"` | `"r50k_base"` | `"p50k_base"` | `"p50k_edit"` | `"cl100k_base"` | `"o200k_base"` | `"gemini"` | `"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](#asstring) (which delegates to
[@nhtio/adk!SpoolReader.readAll](../interfaces/SpoolReader.md#readall)) and delegates to [@nhtio/adk!Tokenizable.estimateTokens](../../common/classes/Tokenizable.md#estimatetokens). The estimate
therefore reflects the actual source bytes — including trailing newlines and non-`\n` line
terminators that the line-based [SpooledArtifact.cat](#cat) view would otherwise discard or
misrepresent.

***

### grep()

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

Defined in: [lib/classes/spooled\_artifact.ts:327](https://github.com/NHTIO/ADK/blob/v1.20260605.0/src/lib/classes/spooled_artifact.ts#L327)

Returns all lines that match `pattern`.

#### Parameters

| Parameter | Type     | Description                                       |
| --------- | -------- | ------------------------------------------------- |
| `pattern` | `RegExp` | The 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.

***

### head()

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

Defined in: [lib/classes/spooled\_artifact.ts:274](https://github.com/NHTIO/ADK/blob/v1.20260605.0/src/lib/classes/spooled_artifact.ts#L274)

Returns the first `n` lines of the artifact.

#### Parameters

| Parameter | Type     | Default value | Description                                |
| --------- | -------- | ------------- | ------------------------------------------ |
| `n`       | `number` | `10`          | Number 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: [lib/classes/spooled\_artifact.ts:383](https://github.com/NHTIO/ADK/blob/v1.20260605.0/src/lib/classes/spooled_artifact.ts#L383)

Returns the total number of lines in the artifact.

#### Returns

`Promise`<`number`>

The line count as reported by the [@nhtio/adk!SpoolReader](../interfaces/SpoolReader.md).

***

### tail()

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

Defined in: [lib/classes/spooled\_artifact.ts:297](https://github.com/NHTIO/ADK/blob/v1.20260605.0/src/lib/classes/spooled_artifact.ts#L297)

Returns the last `n` lines of the artifact.

#### Parameters

| Parameter | Type     | Default value | Description                                |
| --------- | -------- | ------------- | ------------------------------------------ |
| `n`       | `number` | `10`          | Number 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`.

***

### forgeTools()

```ts
static forgeTools(ctx: DispatchContext): ToolRegistry;
```

Defined in: [lib/classes/spooled\_artifact.ts:485](https://github.com/NHTIO/ADK/blob/v1.20260605.0/src/lib/classes/spooled_artifact.ts#L485)

Forges a fresh [@nhtio/adk!ToolRegistry](../../forge/classes/ToolRegistry.md) of ephemeral [@nhtio/adk!ArtifactTool](../../forge/classes/ArtifactTool.md) instances that let the
LLM query artifacts already present in `ctx.turnToolCalls`.

#### Parameters

| Parameter | Type                                                           | Description                                                                     |
| --------- | -------------------------------------------------------------- | ------------------------------------------------------------------------------- |
| `ctx`     | [`DispatchContext`](../../types/interfaces/DispatchContext.md) | The execution context whose `turnToolCalls` snapshot defines the `callId` enum. |

#### Returns

[`ToolRegistry`](../../forge/classes/ToolRegistry.md)

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](SpooledJsonArtifact.md#forgetools) and
[@nhtio/adk!SpooledMarkdownArtifact.forgeTools](SpooledMarkdownArtifact.md#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 `ToolCall`s whose `results instanceof SpooledArtifact`.
   `ToolCall`s flagged `fromArtifactTool === true` are excluded — they carry a
   [@nhtio/adk!Tokenizable](../../common/classes/Tokenizable.md), 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](../../forge/classes/ArtifactTool.md) 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](../../forge/classes/ToolRegistry.md#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

* [@nhtio/adk!ToolRegistry.bindContext](../../forge/classes/ToolRegistry.md#bindcontext)
* [@nhtio/adk!ToolRegistry.merge](../../forge/classes/ToolRegistry.md#merge)
* [@nhtio/adk!DispatchContext.onAck](../../types/interfaces/DispatchContext.md#onack)

***

### isSpooledArtifact()

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

Defined in: [lib/classes/spooled\_artifact.ts:234](https://github.com/NHTIO/ADK/blob/v1.20260605.0/src/lib/classes/spooled_artifact.ts#L234)

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

#### Parameters

| Parameter | Type      | Description        |
| --------- | --------- | ------------------ |
| `value`   | `unknown` | The value to test. |

#### Returns

`value is SpooledArtifact`

`true` when `value` is a SpooledArtifact instance.

#### Remarks

Uses the cross-realm-safe [@nhtio/adk!isInstanceOf](../../guards/functions/isInstanceOf.md) guard: `instanceof` first, then
`Symbol.hasInstance`, then a `constructor.name` fallback. Subclass instances (e.g.
[@nhtio/adk!SpooledJsonArtifact](SpooledJsonArtifact.md)) 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: [lib/classes/spooled\_artifact.ts:251](https://github.com/NHTIO/ADK/blob/v1.20260605.0/src/lib/classes/spooled_artifact.ts#L251)

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

#### Parameters

| Parameter | Type      | Description        |
| --------- | --------- | ------------------ |
| `value`   | `unknown` | The 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](../../forge/classes/Tool.md) 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).
