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.