Storage batteries
These batteries persist artifact bytes only — not conversation state
The storage batteries implement the spool reader/writer pattern for SpooledArtifact persistence. They are NOT a replacement for the 27 TurnRunnerConfig storage callbacks. They do not persist Messages, Memories, or ToolCalls. Session state still requires all 27 callbacks. See Bring Your Own Storage.
They store artifact bytes. Nothing else.
When a tool handler returns a string or Uint8Array, the bundled OpenAI/WebLLM adapters spool it under the tool call's id and hand the model a SpooledArtifact. By default this goes to a fresh per-dispatch InMemorySpoolStore; pass the spoolStore option to back it with a durable store instead:
import { OpfsSpoolStore } from '@nhtio/adk/batteries/storage/opfs'
const adapter = new OpenAIChatCompletionsAdapter({
model: 'gpt-5.5',
spoolStore: new OpfsSpoolStore({ directory: async () => navigator.storage.getDirectory() }),
})If you are running in production, bytes must survive across requests, server restarts, or browser sessions. The storage batteries provide the persistence layer for these artifact bytes.
They do not persist Message records, Memory records, ToolCall metadata, or any of the other primitives in the 27-callback surface. Those are yours to implement. See Bring Your Own Storage.
import { InMemorySpoolStore } from '@nhtio/adk/batteries/storage/in_memory'
const store = new InMemorySpoolStore()import { Disk } from 'flydrive'
import { FSDriver } from 'flydrive/drivers/fs'
import { FlydriveSpoolStore } from '@nhtio/adk/batteries/storage/flydrive'
const disk = new Disk(new FSDriver({ location: '/var/app/artifacts', visibility: 'public' }))
const store = new FlydriveSpoolStore(disk)import { OpfsSpoolStore, type OpfsDirectoryHandle } from '@nhtio/adk/batteries/storage/opfs'
const store = new OpfsSpoolStore({
directory: async () => {
const root = await navigator.storage.getDirectory()
return (await root.getDirectoryHandle('agent-artifacts', { create: true })) as OpfsDirectoryHandle
},
keyPrefix: 'agent-runs-'
})In-Memory (@nhtio/adk/batteries/storage/in_memory)
Environment-neutral. Works in Node, browsers, edge runtimes, and workers. Stores bytes byte-faithfully in a Map<string, Uint8Array>.
Binary is stored faithfully — but it lives in process memory
InMemorySpoolStore stores raw bytes intact (binary payloads round-trip without corruption); InMemorySpoolReader decodes on demand for line/text reads. The catch is durability, not fidelity: everything lives in process memory and is lost on exit. For artifacts that must survive restarts, use the Flydrive or OPFS batteries.
import { InMemorySpoolStore } from '@nhtio/adk/batteries/storage/in_memory'
const store = new InMemorySpoolStore()
// Write artifact bytes and get a reader
const reader = await store.write(callId, artifactBytes)
// Read previously written bytes
const existingReader = store.read(callId) // returns InMemorySpoolReader | undefinedwrite() accepts a string (UTF-8-encoded), a Uint8Array (stored verbatim), or a ReadableStream<Uint8Array> (drained to a buffer — the stream form resolves asynchronously, so await the result), and returns an InMemorySpoolReader bound to the stored bytes. read() returns a reader for a previously stored call ID, or undefined if not found.
Each call to write() or read() returns a fresh reader instance.
Use this for:
- Unit and integration tests
- Prototypes and quick CLI spikes
- Ephemeral environments where artifact loss on process exit is irrelevant
Do not use this for:
- Production deployments where artifacts must survive restarts or scale across multiple server instances
Flydrive (@nhtio/adk/batteries/storage/flydrive)
Node/server runtime; not browser. Requires flydrive as a peer dependency. Backed by a flydrive Disk — which can point at the local filesystem or remote object storage backends (S3, GCS, etc.) via any supported flydrive driver.
Install the peer dependency first:
npm install flydriveimport { Disk } from 'flydrive'
import { FSDriver } from 'flydrive/drivers/fs'
import { FlydriveSpoolStore } from '@nhtio/adk/batteries/storage/flydrive'
const disk = new Disk(new FSDriver({ location: '/var/app/artifacts', visibility: 'public' }))
const store = new FlydriveSpoolStore(disk)
// Write artifact bytes to the backing store
const reader = await store.write(callId, artifactBytes)
// Read previously written bytes
const existingReader = await store.read(callId) // FlydriveSpoolReader | undefinedFlydriveSpoolStore is stateless — it owns no in-memory cache. Multiple store instances sharing the same Disk are safe.
FlydriveSpoolReader supports line access, byte-length reporting, line counts, and full decoded string reads. Large artifacts use streaming mode for line/index access, but readAll()/asString() loads the full decoded string into memory.
Use this for:
- Server-side production deployments
- Multi-process setups where artifacts need to be shared via a cloud storage driver
OPFS (@nhtio/adk/batteries/storage/opfs)
Browser only. Uses the Origin Private File System — a sandboxed filesystem API available in modern browsers. No network required. Data persists in the browser's origin-private storage.
import { OpfsSpoolStore, type OpfsDirectoryHandle } from '@nhtio/adk/batteries/storage/opfs'
const store = new OpfsSpoolStore({
directory: async () => {
const root = await navigator.storage.getDirectory()
return (await root.getDirectoryHandle('agent-artifacts', { create: true })) as OpfsDirectoryHandle
},
keyPrefix: 'agent-runs-'
})
// Write artifact bytes
const reader = await store.write(callId, artifactBytes)
// Read previously written bytes
const existingReader = await store.read(callId) // OpfsSpoolReader | undefinedCorrect API Usage
The constructor accepts the directory callback options configuration. Passing a directory handle to write() or read() is not supported; those methods do not accept it.
OpfsSpoolStore works on the main thread and in Web Worker threads, using different internal code paths for each context. The public API is identical in both.
Use this for:
- Browser-embedded agents that need artifact persistence across page reloads
- Progressive web apps or browser extension-based agents
Choosing the Right Battery
These recommendations apply only for spool-byte persistence in production. For the 27-callback storage adapter, go to Bring Your Own Storage.
| Environment | Battery |
|---|---|
| Tests and prototypes | InMemorySpoolStore |
| Node server (development) | InMemorySpoolStore |
| Node server (production) | FlydriveSpoolStore |
| Browser | OpfsSpoolStore |
| Edge / serverless | InMemorySpoolStore (if artifact loss is acceptable) |
If your production setup needs artifact bytes to survive across requests or processes, use FlydriveSpoolStore. If your agent runs in the browser, use OpfsSpoolStore. If bytes are ephemeral and you are okay losing them on restart, InMemorySpoolStore is valid for production too — it stores binary faithfully; the only caveat is durability.