# Conflicts: # packages/util/brand/README.i18n.yaml # packages/util/brand/README.md # packages/util/brand/README.zh.md
description, kind
| description | kind |
|---|---|
| The spill storage service: how deployments and plugin authors save oversized tool text and get back a retrievable locator. | package-reference |
@deepseek-ai/dsh-spill
English | 中文
Summary
dsh-spill lets any plugin or tool save oversized text through ctx.spillStore and receive an opaque locator, the exact byte count, and retrieval guidance the model can act on. It defines what a spill backend does, not how it stores — a deployment mounts a backend such as dsh-spill-local for real persistence, and the dsh-spill-policy plugin decides when a tool result is too large. Choose it when a deployment must keep oversized tool output retrievable without flooding the model's context. The service owns storage only: no retention policy, no tool-result replacement, and no retrieval or search API. A real storage failure rejects loudly, so the caller decides how to degrade.
Table of Contents
- Use this package
- Understand the implementation
- Further Exploration
- Model Experience
- Known Limitations and Deferred Work
- Dev Note
Use this package
A composition that spills tool output mounts one spill backend — this package alone stores nothing — and the dsh-spill-policy plugin decides when to spill. Plugin and tool authors call ctx.spillStore.saveText() directly to persist text under the current session.
When to choose it
Choose spill storage when a deployment needs to keep oversized tool output retrievable after the model has only seen a bounded preview — for example a fetched page body the model may want to read or grep later. You do not need this package when no tool in the composition produces results large enough to matter, or when the deployment has no local filesystem the model's tools can read; a backend whose locator is meaningful in that environment is a prerequisite.
Smallest working composition
Mount a backend and the policy together; with maxInlineBytes set, any oversized plain-text tool result becomes a preview plus a locator automatically.
- name: '@deepseek-ai/dsh-spill-local'
- name: '@deepseek-ai/dsh-spill-policy'
config:
maxInlineBytes: 50000
Saving text
With a backend mounted, call ctx.spillStore.saveText() with the owning session, a source description, a suggested file name, and the full text:
const ref = await ctx.spillStore.saveText({
owner: { sessionId: 'session-1' },
source: { toolName: 'web_fetch', callId: 'call-1', label: 'result' },
suggestedName: 'web_fetch.txt',
content: fullText,
})
The returned SpillRef carries three fields: locator, an opaque model-facing handle the backend produces (a local file path for dsh-spill-local, possibly a URI or key for another backend); bytes, the exact UTF-8 byte count written; and retrievalHint, the guidance a consumer shows the model — for the local backend, read or grep the path. Consumers render the locator with the hint and never parse the locator itself.
Ownership and boundaries
Storage is grouped by the owning session: forked sessions inherit existing locators from the seeded log without copying or re-owning them, and new spills after a fork use the child session id. suggestedName is only a hint — backends sanitize it to one safe segment and never trust it as a path. The service deliberately excludes what other packages own: retention and preview decisions (dsh-output-retention), when to spill (dsh-spill-policy), and retrieval or search (the backend's retrievalHint tells the model what to do with the locator).
Failures and recovery
saveText rejects only on a real storage failure — permissions, no space left, or a backend that is down. The caller decides how to degrade: the shipped policy treats a rejection as best-effort, logs a warning, and keeps the original inline result, so a spill failure never turns a successful tool call into an error or hides content. If no backend is mounted, there is nothing to save; load dsh-spill-local or another backend in the composition.
Understand the implementation
Implementation internals — click to expand
This section explains the design decisions behind the service; the observable behavior is fully covered in Use this package.
Design philosophy
The package is built on one separation and a deliberate minimum:
- Contract, implementation, and policy stay separate. This package defines what a backend does (
saveText);dsh-spill-localimplements it;dsh-spill-policydecides when. Each concern evolves and swaps independently. - One method, nothing else. The seam owns no retention policy, no result replacement, and no retrieval or search API — those have owning packages.
- Reject, never silently degrade at the seam. The caller owns degradation; the seam reports real storage failures.
Source map
| File | Role |
|---|---|
src/index.ts |
Plugin entry: the abstract SpillStore service and its saveText contract |
src/types.ts |
Vocabulary: SaveTextSpill, SpillRef, branded SpillLocator, SpillOwner, SpillSource |
src/invariant.ts |
Invariant companion (no runtime invariant; contracts are enforced at the seam) |
Data model
SaveTextSpill (owner, source, suggestedName, content) is the request; SpillRef (locator, bytes, retrievalHint) is the result. SpillLocator is a branded string so consumers cannot treat it as a path without the backend's intent; SpillOwner.sessionId is the save-time storage namespace, and SpillSource records the producing tool, call id, and label for readable filenames — descriptive only, never access control.
Lifecycle
A backend subclasses SpillStore and loads as a plugin, registering as ctx.spillStore; one implementation per context, and a second load fails. Disposal releases the service. The abstract class itself registers nothing — this package contributes the contract and vocabulary only.
Further Exploration
Read these pages when the package-level contract is not enough. They move from the shared vocabulary to the shipped backend, the policy, and the design rationale.
- Spill subsystem — the exhaustive vocabulary, ownership, and backend relationships.
- Spill package map — the three-package family and each role.
- dsh-spill-local — the shipped local filesystem backend.
- dsh-spill-policy — the policy that decides when a final result is too large.
- dsh-output-retention — the preview mechanics behind the policy.
- Tool output spill decision — the capability boundary and design rationale.
Model Experience
Indirectly, through spill consumers, which render the backend's locator and retrieval guidance to the model.
KV Cache effect
No direct invalidation; the named consumer owns any request-prefix changes.
Known Limitations and Deferred Work
These limits define when the spill storage service is incomplete on its own. They are current package constraints.
- No retrieval or deletion API — consumers can only render the backend's locator and guidance; lifecycle and access semantics remain backend-specific.
- Storage is not access control — the owner session namespaces writes but does not authorize reads of a locator; each backend and retrieval consumer must enforce its own boundary.
Dev Note
Working context for maintainers — click to expand
This Dev Note is working context for maintainers: undecided directions and open questions. It is explicitly non-authoritative.
Future: executor spill-file integration
The seam has only saveText; a save-file or link/copy path for existing executor spill files (for example normalizing bash temp files) and tool-owned spill for subagent rollouts remain deferred, per the tool output spill decision.
Future: non-local backends and cleanup
Remote or database backends for ACP or remote environments, and a cleanup or retention policy for old spill files (likely tied to session cleanup), remain open. A predictable, world-readable spill root would let other local users read spilled tool output, which is why the shipped backend keeps files private.