跳转到内容

spill 存储

saveText 是唯一的服务操作:原样持久保存 content,并返回不透明的定位符、后端提供的检索提示和准确字节数。请求携带保存时的存储命名空间(owner)、生成内容的工具和调用(source,用于命名和检查,而非访问控制)以及后端可用作命名提示的 suggestedName(它不是路径)。

/** One request to persist text to a spill artifact. */
interface SaveTextSpill {
owner: SpillOwner
source: SpillSource
/**
* A caller-suggested base name (e.g. `web_fetch.txt`). The backend sanitizes
* it to a single safe path segment before use — it is a hint, never a path.
*/
suggestedName: string
/** The full text to persist (UTF-8). */
content: string
}
/**
* Save-time storage namespace for a spilled artifact. The session id lets a
* backend group storage under the producing session, but the returned
* {@link SpillLocator} is the model-facing handle. Forked sessions inherit
* locators already present in the seeded log; those artifacts are not copied or
* re-owned, and spills produced after the fork use the child session id.
*/
interface SpillOwner {
sessionId: SessionId
}

SpillOwner.sessionId 是保存时的存储命名空间。fork 后的会话会从种子日志继承已有的 spill 定位符;这些产物不会被复制或重新取得所有权,fork 后产生的 spill 则使用子会话 id。保留期清理可以连同其他旧会话产物一起使旧定位符失效;spill seam 不定义逐会话的清理策略。

/**
* Tool and call that produced one spilled artifact — recorded by the backend for a readable
* filename and inspection. Not interpreted for access control; purely
* descriptive.
*/
interface SpillSource {
/** The tool whose result was spilled (e.g. `web_fetch`). */
toolName: string
/** The model-issued call id the result belongs to. */
callId: CallId
/** A short human label for the artifact (e.g. `result`). */
label: string
}
/** A saved spill artifact: its locator, byte length, and backend-specific retrieval guidance. */
interface SpillRef {
locator: SpillLocator
bytes: number
retrievalHint: string
}

SpillLocator 是后端返回的品牌化面向模型句柄。本地后端将它渲染为文件系统路径;远程或数据库后端可以渲染 URI、键或命令 token。消费方将它视为不透明值,并使用 retrievalHint 渲染,而不是假定 read 始终是正确的检索机制。

/**
* Opaque model-facing handle for one spilled artifact. A local backend may use a
* filesystem path; a remote or database backend may use a URI or key. Consumers
* render it with {@link SpillRef.retrievalHint}, but do not parse it.
*/
type SpillLocator = Branded<'SpillLocator'>

SpillStorectx.spillStore,定义于 packages/spill/spill/src/index.ts)是只有一个方法的抽象服务:saveText(input) → Promise<SpillRef>。它持久保存完整的 content,并在实际存储失败(权限、ENOSPC、后端不可用)时拒绝。该 seam 只负责存储:不负责保留策略、工具结果替换或检索/搜索 API。

本地后端(dsh-spill-local)写入 <root>/session-<hash>/<random>-<safeName>:根目录是已配置或延迟创建的私有(0700)目录,会话子目录采用 sha256(sessionId),并通过排他且仅所有者可访问的写入(open(path, 'wx', 0o600))防止预先植入的符号链接重定向写入。其 locator 是本地路径,retrievalHint 则告知模型在该路径上使用 readgrep。策略消费方(dsh-spill-policy)会把超过 maxInlineBytes 的纯文本最终结果替换为保留库生成的首尾预览和 spill 引用;该过程尽力而为:保存失败时保留原始内联结果,而不会把成功的调用变成 isError

Generated from source by scripts/gen-cordis-catalog.ts (verified fresh by pnpm run verify-cordis-catalog in doc-sync; regenerate with pnpm run gen-cordis-catalog) — this section is byte-identical in both language sides of the page. Signature blocks use a ts cordis-catalog fence and keep the original source JSDoc; dispatch modes are defined in the primer, and the framework-inherited ctx API lives in cordis-api/inherited.md.

ctx.spillStoreSpillStore (abstract seam)

Section titled “ctx.spillStore — SpillStore (abstract seam)”

Abstract spill storage service. Subclass, implement saveText, and load the subclass as a plugin — it registers as ctx.spillStore (one implementation per context; loading a second throws, cordis’ standard duplicate-service behavior).

Semantics every implementation must honor:

  • saveText persists the FULL content verbatim and returns an opaque locator, exact byte length, and model-facing retrieval guidance.
  • Storage is scoped by the request’s SaveTextSpill.owner session; the backend chooses a private (not world-readable) location and a collision-free name derived from — never equal to — the caller’s suggestedName.
  • saveText REJECTS on a real storage failure (permissions, ENOSPC, backend unavailable); the caller decides how to degrade (the spill policy treats a rejection as best-effort and keeps the inline result).
/**
* Persist `input.content` to a session-scoped spill artifact.
* @param input - the owner, caller-supplied source fields, suggested name, and full text to save.
* @returns the saved artifact's {@link SpillRef}; rejects on a storage failure.
*/
abstract saveText(input: SaveTextSpill): Promise<SpillRef>

Source: packages/spill/spill/src/index.ts:45