---
url: 'https://adk.nht.io/assembly/batteries-artifacts.md'
description: >-
  Structured queries over spooled artifacts: TOON, YAML, XML, and EcmaScript,
  plus bidirectional converters.
---

# Artifact batteries

## LLM summary — Artifact batteries

* Artifact batteries provide structured query tools over six spooled-artifact formats: JSON and Markdown (core), plus TOON, YAML, XML, and EcmaScript (batteries with optional peers). TOON's delimiter is not reported by the type query; JSONPath queries do not require it.
* Each battery exports a [`SpooledArtifact`](https://adk.nht.io/api/@nhtio/adk/spooled_artifact/classes/SpooledArtifact) subclass and, for TOON/YAML/XML, bidirectional converter tools (`toon_to_json`, `json_to_toon`, `yaml_to_json`, `json_to_yaml`, `xml_to_json`, `json_to_xml`).
* Core classes query via JSONPath: `artifact_X_get(path)`, `artifact_X_filter(path)`, `artifact_X_pluck(path)`. EcmaScript queries by AST: symbols, imports, exports, outline, signatures, JSDoc, syntactic references.
* Converters accept either inline text or a `call_id` naming an artifact from earlier in the turn, and return a new spooled artifact ready for query on the next iteration. TOON converters enable token-reduction workflows.
* Each peer is optional: a consumer who never imports a battery never installs its peer dependency.
* [`registerArtifactEncodables`](https://adk.nht.io/api/@nhtio/adk/batteries/artifacts/functions/registerArtifactEncodables) must be called once at startup to enable decoding; encoding requires no setup.

Structured queries over spooled artifacts let the model navigate formatted output without materialising full bytes. Six families: JSON and Markdown (core), plus TOON, YAML, XML, and EcmaScript (batteries).

## TOON artifacts

TOON is a token-efficient JSON encoding. [`SpooledToonArtifact`](https://adk.nht.io/api/@nhtio/adk/batteries/artifacts/toon/classes/SpooledToonArtifact) uses JSONPath for all queries (same surface as JSON).

Requires `@toon-format/toon@^4.1.1`.

| Method | Purpose |
| --- | --- |
| `artifact_toon_type` | TOON format |
| `artifact_toon_keys` | Top-level keys |
| `artifact_toon_length` | Element count |
| `artifact_toon_get(path)` | JSONPath evaluation |
| `artifact_toon_filter(path)` | JSONPath filter matches |
| `artifact_toon_slice(start?, end?)` | Array slice |
| `artifact_toon_pluck(path)` | Collect values at path |

### Converters

`toonToJsonTool` and `jsonToToonTool` accept either inline text or `call_id`, returning a new queryable artifact on the next iteration. Use `jsonToToonTool` to shrink large artifacts.

## YAML artifacts

[`SpooledYamlArtifact`](https://adk.nht.io/api/@nhtio/adk/batteries/artifacts/yaml/classes/SpooledYamlArtifact) queries both single and multi-document streams. Multi-document streams (delimited by `---`) report count via `yaml_length` and evaluate paths against all documents.

Requires `js-yaml@^4.1.1` (already core; install if missing).

| Method | Purpose |
| --- | --- |
| `artifact_yaml_type` | 'single-document' or 'multi-document' |
| `artifact_yaml_keys` | Top-level keys (deduplicated union for multi-document) |
| `artifact_yaml_length` | Document count |
| `artifact_yaml_get(path)` | JSONPath evaluation |
| `artifact_yaml_filter(path)` | JSONPath filter matches |
| `artifact_yaml_slice(start?, end?)` | Array slice |
| `artifact_yaml_pluck(path)` | Collect values at path |

### Converters

`yamlToJsonTool` and `jsonToYamlTool` accept inline text or `call_id`. Non-finite numbers (`.NaN`, `.inf`, `-.inf`) are preserved through conversion.

## XML artifacts

[`SpooledXmlArtifact`](https://adk.nht.io/api/@nhtio/adk/batteries/artifacts/xml/classes/SpooledXmlArtifact) parses to JSON with attributes prefixed `_` by default. An attribute `href="x"` becomes `_href`; use `$..["_href"]` for recursive queries.

The prefix is settable per instance via the constructor's `attributeNamePrefix`, but `@_` is a poor choice and the default deliberately avoids it. `jsonpath-plus` reads a leading `@` in a path segment as its type-selector sigil (`@string()`, `@number()`, `@path`), and it does so before honouring quotes — so `$..["@_href"]` throws `Unknown value type _hr` rather than matching. Quoting is not an escape: single quotes, double quotes and dot notation all fail identically.

Such a key is not wholly unreachable, but the workarounds are awkward enough to be worth avoiding. A filter expression works in every case, because inside it the key is an ordinary JavaScript string: `$..[?(@['@_href'])]`. A wildcard works only when it lands exactly one level above the key, which means the correct path depends on the document's shape — repeated sibling elements collapse into an array, so `$.root.a[*]["@_href"]` matches where `$.root.*["@_href"]` throws, and for non-repeated siblings it is the other way round. Recursive descent, the query style a model reaches for when exploring an unfamiliar document, never works. The restriction is specific to `@`: `#text`, `$ref` and `_href` all query normally.

Requires `fast-xml-parser@^5.11.1`.

| Method | Purpose |
| --- | --- |
| `artifact_xml_root` | Root element name |
| `artifact_xml_keys` | Top-level keys of root |
| `artifact_xml_tags` | All distinct tag names in document |
| `artifact_xml_length` | Element count |
| `artifact_xml_get(path)` | JSONPath evaluation |
| `artifact_xml_filter(path)` | JSONPath filter matches |
| `artifact_xml_pluck(path)` | Collect values at path |

### Converters

`xmlToJsonTool` and `jsonToXmlTool` accept inline text or `call_id`. Conversions are not lossless — XML attribute/element/text distinctions do not round-trip; assert what converters *do* rather than expecting fidelity.

## EcmaScript artifacts

[`SpooledEcmaScriptArtifact`](https://adk.nht.io/api/batteries/artifacts/ecmascript/classes/SpooledEcmaScriptArtifact) queries source code structure via TypeScript compiler API. No converters. Infers `scriptKind` from extension (`.mts`/`.cts` → `ts`, `.mjs`/`.cjs` → `js`); defaults to `ts`.

Requires `typescript@^5.9.3`.

| Method | Purpose |
| --- | --- |
| `artifact_es_symbols` | Top-level declarations with `{ kind, name, exported, startLine, endLine }`; optional kind filter |
| `artifact_es_imports` | Imports with `{ moduleSpecifier, named[], default?, namespace?, typeOnly, line }` |
| `artifact_es_exports` | Export declarations and re-exports |
| `artifact_es_outline` | Classes/interfaces with nested `members[]` |
| `artifact_es_signature(name)` | Signature text (parameters, return type, type parameters) |
| `artifact_es_jsdoc(name)` | Attached JSDoc comment |
| `artifact_es_references(name)` | Lines and columns where identifier appears (syntactic only, no scope resolution) |

## Import Paths

Import subclasses and converters from their respective battery barrels:

```typescript
import { SpooledToonArtifact, toonToJsonTool, jsonToToonTool } from '@nhtio/adk/batteries/artifacts/toon'
import { SpooledYamlArtifact, yamlToJsonTool, jsonToYamlTool } from '@nhtio/adk/batteries/artifacts/yaml'
import { SpooledXmlArtifact, xmlToJsonTool, jsonToXmlTool } from '@nhtio/adk/batteries/artifacts/xml'
import { SpooledEcmaScriptArtifact } from '@nhtio/adk/batteries/artifacts/ecmascript'
```

The aggregate battery barrel also re-exports all four classes:

```typescript
import {
  SpooledToonArtifact,
  SpooledYamlArtifact,
  SpooledXmlArtifact,
  SpooledEcmaScriptArtifact,
} from '@nhtio/adk/batteries/artifacts'
```

Converters are available from both the aggregate barrel and their format-specific subpaths; prefer the format-specific subpath to avoid bundling unused batteries' module graphs.

## Decoding

Call [`registerArtifactEncodables`](https://adk.nht.io/api/@nhtio/adk/batteries/artifacts/functions/registerArtifactEncodables) once at startup to enable decoding:

```typescript
import { registerArtifactEncodables } from '@nhtio/adk/batteries/artifacts'
await registerArtifactEncodables()
```

Idempotent; safe to call multiple times. Encoding requires no setup.

## Converter workflow

Converters accept **either** inline text **or** an artifact reference — provide exactly one non-empty value:

```typescript
{ text: 'inline content', call_id: 'call_xyz' }  // one or the other
```

The handler resolves, converts, and returns a new spooled artifact. When the model requests a converter, the executor invokes it and persists the result:

```typescript
// Inside the executor: model requested jsonToToonTool
const tool = ctx.tools.get('json_to_toon')
const result = await tool.executor(ctx)({ text: largeJson })
await ctx.storeToolCall(toolCall) // with result spooled

// Iteration 2: artifact is forged; model can query it
// artifact_toon_get, artifact_toon_filter, etc.
```

[`DispatchRunner`](https://adk.nht.io/api/@nhtio/adk/dispatch_runner/classes/DispatchRunner) picks up the new artifact on the next iteration and forges its query tools.

## Peer Dependencies

| Battery | Peer | Version |
| --- | --- | --- |
| TOON | `@toon-format/toon` | `^4.1.1` |
| YAML | `js-yaml` | already core |
| XML | `fast-xml-parser` | `^5.11.1` |
| EcmaScript | `typescript` | `^5.9.3` |

All peers optional. Query methods throw with installation instructions if peer is missing.
