Files
deepseek-harness/docs/core-data-structures/filesystem.md
T

4.8 KiB

Filesystem

The filesystem execution seam is split across three packages: interface (dsh-fs, ctx.fs), implementation (dsh-fs-local, local disk), and consumer (dsh-tool-fs, the model-facing read/write/edit tools). Filesystem access is an optional capability, not part of the agent-loop spine, so its vocabulary lives here rather than in core.md. A sandboxed, remote, virtual, or project-scoped backend can implement the same FileSystem service without changing the tool schemas.

Source: packages/fs/fs/src/types.ts and packages/fs/fs/src/index.ts

Execution context and target identity

The filesystem seam needs just enough execution context to derive the observed-file owner. ToolExecution satisfies this shape, so dsh-tool-fs passes its execution object through without making dsh-fs import the tool, agent, or session packages.

interface FsExecContext {
  agent?: {
    session?: object
  }
}

Every operation resolves a user-supplied path to an opaque backend target first. Consumers may display displayPath, but must not parse targetKey or assume it is a local absolute path.

interface FsTarget {
  inputPath: string
  targetKey: string
  displayPath: string
}

The backend also owns file-version tokens. ctx.fs stores them for stale checks; consumers do not interpret them.

type FsVersion = string

Reads and editable views

A text read is bounded by line window, byte cap, and backend limits. The returned view records whether the owner saw the whole file or only a partial page; only a full view authorizes later write/edit.

interface FsReadRequest {
  offset: number
  limit: number
}
interface FsTextLine {
  number: number
  text: string
}
type FsView = 'full' | 'partial'
interface FsReadOutcome {
  offset: number
  limit: number
  lines: FsTextLine[]
  totalLines: number
  truncatedByBytes?: true
  version: FsVersion
  view: FsView
}

Write and edit guards

The base FileSystem service converts recorded state into an FsExpectation before calling the backend. observed carries the stale guard, partial means the owner saw a non-editable view, and unobserved allows create-if-absent but rejects blind overwrite.

type FsExpectation =
  | { kind: 'observed'; version: FsVersion }
  | { kind: 'partial'; version: FsVersion }
  | { kind: 'unobserved' }
interface FsWriteOutcome {
  operation: 'create' | 'update'
  version: FsVersion
}

Literal edit is a backend operation, not a read plus write composed in the tool wrapper. That keeps matching, line-ending handling, stale checks, and atomic replacement inside the filesystem seam.

interface FsEditRequest {
  oldString: string
  newString: string
  replaceAll: boolean
}
interface FsEditOutcome {
  replacements: number
  replaceAll: boolean
  version: FsVersion
}

Observed-file state

Observed state is keyed inside the service by owner object and FsTarget.targetKey. The owner is normally exec.agent.session, but dsh-fs treats it as opaque and never reads its fields. A successful read/write/edit refreshes this state for that owner.

type FsStateSource = 'read' | 'write' | 'edit'
interface FileState {
  targetKey: string
  displayPath: string
  version: FsVersion
  view: FsView
  updatedAt: number
  source: FsStateSource
}

Error taxonomy

Filesystem failures use stable FsErrorCode strings carried by FsError (HarnessError). The tool registry preserves { name, code } on error results, so retry, permission, and UI layers can branch without parsing text.

type FsErrorCode =
  | 'FS_NOT_FOUND'
  | 'FS_NOT_TEXT'
  | 'FS_NOT_REGULAR_FILE'
  | 'FS_STALE_VERSION'
  | 'FS_NOT_OBSERVED'
  | 'FS_PARTIAL_OBSERVATION'
  | 'FS_AMBIGUOUS_EDIT'
  | 'FS_EDIT_NOT_FOUND'
  | 'FS_ABORTED'

FS_NOT_OBSERVED means no usable prior observation exists. FS_PARTIAL_OBSERVATION means the owner saw only a partial read. FS_STALE_VERSION means there was a prior full observation, but the backend version no longer matches.

The service

FileSystem (ctx.fs, abstract) owns the shared orchestration: resolve, readPage, createOrReplace, and applyEdit are backend primitives; public read, write, and edit derive/record owner state and enforce the read-before-write/edit policy before delegating to the backend. The generated wiring catalog shows the exact service signatures on events-and-services.md.