Add the dsh-workspace-changes Host plugin that snapshots the working tree at turn start and end through a private git index, diffs the two trees, folds in file-tool hunks for ignored and out-of-tree files, and appends a log-only workspace/changes event. The Web deliverables plugin renders that event as the changed-files card in place of the mutation-call row, with default-app and common-folder opening through a new authenticated route.
description, kind
| description | kind |
|---|---|
| Record each top-level turn's changed files from git working-tree snapshots as a durable workspace/changes Session event; configuration, repository tiers, and coverage rules. | package-reference |
@deepseek-ai/dsh-workspace-changes
English | 中文
Summary
This plugin records which files each top-level turn changed, with per-file added and deleted line counts, as a workspace/changes Session event. It snapshots the working tree with git when a turn starts and when it ends, diffs the two snapshots, and adds the file-tool edits that git does not cover. The Web changed-files card renders the event; the model never sees it.
Table of Contents
- Use this package
- Understand the implementation
- Further Exploration
- Model Experience
- Known Limitations and Deferred Work
- Dev Note
Use this package
The shipped Web bundle mounts this plugin. Mount it in any composition with the subprocess capability and a git executable on the Host:
- name: '@deepseek-ai/dsh-workspace-changes'
config:
maxFiles: 500
| Field | Default | Meaning |
|---|---|---|
dshHome |
$DSH_HOME, then ~/.dsh |
Harness home whose workspace-changes/ directory holds shadow repositories |
timeoutMs |
30000 |
Milliseconds one git command may run before the turn's record is abandoned |
outputMaxBytes |
8388608 |
Bytes of git output retained per command; a larger diff listing abandons the record |
maxFiles |
500 |
Maximum files carried by one event; total still reports the complete count |
shadowExcludes |
.git/, .dsh/, node_modules/, build and cache directories |
info/exclude patterns for shadow repositories |
Every Session with a working directory and no subagent origin is recorded; subagent Sessions are not. A working directory inside a git repository snapshots into that repository's object store through a private index, so the repository's own index, work tree, and refs stay untouched and the user's earlier uncommitted changes never enter a summary. A working directory outside any repository snapshots into a shadow repository under the Harness home whose work tree is the working directory itself; the shadowExcludes patterns replace the missing .gitignore, and a Harness home inside the working directory is excluded automatically. Nested repositories and submodules inside the working directory are recorded as gitlinks, so their internal changes do not appear. Without git — or, on macOS, with only the developer-tools stub at /usr/bin/git — the plugin records nothing and logs that once.
Files the file tools changed but the snapshots do not cover are added from the hunks those tools persist with their results: files matching an ignore pattern and files outside the repository or shadow work tree. Files under /tmp or the platform temporary directory are excluded unless they lie inside the working directory. Line counts for these files sum over the recorded hunks, so repeated edits to one file in a turn can count a line more than once. Changes made through shell commands outside the snapshot coverage are not recorded.
Each file carries a durable path — relative to the working directory inside it, absolute elsewhere — and a display path used for ordering and labels: the relative path, a ../ path for repository files above the working directory, a ~ path under the home directory, otherwise the absolute path. Files sort by display in code-unit order, which lists parent and absolute paths before the working directory's own files. The event also carries the two snapshot tree ids.
Understand the implementation
Implementation internals — click to expand
One TurnRecorder per Session serializes its git work. turn/start queues the baseline: rev-parse locates the repository or initializes the shadow one, then add --all into a temporary index seeded from the persisted index and write-tree produce the tree id. Every tools/pre-execute waits for that queue before a tool runs, so no mutation can precede its baseline. tool/result events collect the file-tool hunks. agent/turn-stopping records inside the turn: a second snapshot, diff-tree -r -M --numstat between the two trees, check-ignore for hunk paths inside the work tree, and the appended event. turn/end records again only when tool results settled after the last record, which covers aborted, failed, and steered turns; an empty list after an earlier record supersedes it. A shadow repository keeps its refreshed index for the next snapshot; a real repository's index is only read.
Git runs through the subprocess capability with a scrubbed environment, GIT_TERMINAL_PROMPT=0, GIT_OPTIONAL_LOCKS=0, the configured timeout, and bounded output. A failing step abandons that turn's record with a warning; the next turn starts afresh. Session disposal and plugin disposal abort queued work.
Runtime invariant: No companion is published. Event listeners are effect-owned, the Session log owns the recorded summaries, and the git object store owns snapshot trees; no independent observation can diverge from them.
Further Exploration
- Web deliverables — the changed-files card that renders this event and opens its files.
- Subprocess capability — the seam git runs through.
- Turn changed-files card decision — snapshot design, coverage rules, and rejected alternatives.
Model Experience
None, as the recorder appends a log-only workspace/changes event that only clients read and registers nothing model-facing.
KV Cache effect
Nothing here enters a model request, so provider cache reuse is unaffected.
Known Limitations and Deferred Work
- Snapshot trees are unreferenced objects, so a repository's garbage collection removes them after its prune expiry; the recorded counts survive, the trees needed for a later full-file comparison do not.
- Edits the user makes during a turn are attributed to that turn.
- Hunk-based counts for files outside snapshot coverage are sums over edits, not a first-to-last diff, and cover file tools only.
- Windows paths keep native separators in
path;displayis always slash-separated.
Dev Note
Working context for maintainers — click to expand
None.