Bundle
@xiyiyiru/dsh-state
mycel session state as a plain-text notebook: add_state / read_state / compact_state plus a focus task stack over per-session files under <cwd>/.mycel/state/<sessionId>/; survives compaction and resume, never auto-injected, writes zero session events
- Source
- xiyiyiru
- License
- MIT
- Updated
- Updated 6 hours ago
Readme
# @xiyiyiru/dsh-state
A plain-text session notebook for [dsh](https://github.com/deepseek-ai/deepseek-harness) agents: `add_state` / `read_state` / `compact_state` over per-session files, plus a two-tool task stack (`focus_task` / `focus_complete`).
> [中文 README](./README.zh.md)
>
> dsh agent 的会话状态笔记本:抗上下文压缩、跨 resume 存活的纯文本笔记层,外加一个任务栈。零自动注入——想看笔记就调 `read_state`,没有后台魔法。
## Why
Context compaction deletes working memory. An agent that just learned "the bug is in the retry loop, user confirmed fix #2" loses exactly that when the window compresses. Auto-injected state has the inverse failure: it burns the same tokens every request and trains the model to ignore it.
This plugin persists working facts as plain files under the workspace — they survive compaction and resume — and keeps the notebook out of every request unless the agent explicitly reads it. Five verbs, no storage to manage, no schema to migrate, zero session events (dsh session-log readers refuse event types outside the harness vocabulary, so since 0.2.0 the plugin touches no log at all).
## Storage
Everything lives under `<session-cwd>/.mycel/state/<sessionId>/`:
- `notebook.md` — the notebook, one appended paragraph per note
- `focus.json` — the task stack, bottom frame first
The session id keys the container: two conversations in one workspace never share state, and a resumed session (same id, same cwd) finds its files back.
## The notebook
| tool | verb | semantics |
|---|---|---|
| `add_state` | append | append one note. Mistakes are corrected by appending a correction line — there is no edit verb |
| `read_state` | read | full text + size; appends a compress hint once the notebook passes 8K |
| `compact_state` | replace | the compression verb: read first, then rewrite the whole file with a summary |
`add_state`'s model-facing guidance carries **hard triggers** (record the moment it happens, not at task end):
1. the user made a decision or correction ("yes", "no — do it this way" landing)
2. a key fact or root cause was established (a path, a cause, a number that would need re-discovering)
3. a multi-step task is starting (goal + acceptance criteria — recovery material after compaction)
4. a milestone completed (conclusion + artifact paths; work that leaves no trace didn't happen)
## The task stack
`focus.json`; frames are plain one-line task descriptions.
| tool | verb | semantics |
|---|---|---|
| `focus_task` | push | lock a multi-step task; the previous task is pushed down automatically |
| `focus_complete` | pop | declare done with a one-line conclusion and fall back to the previous task (or clear the stack). Call it **before** delivering the answer — the task's completion standard is the artifact landing |
`focus_task`'s guidance on when *not* to use it: single-turn Q&A, or a change of working *posture* within the same task (that is the mode plugin's business — the two are orthogonal by design: the stack restores your task, a re-switch restores your methodology; frames carry no mode snapshot).
Interrupted mid-task? Lock the blocker with `focus_task`, solve it, `focus_complete` back to the main task.
As a convenience, focus calls also append to `<session-cwd>/.mycel/focus.log` — best-effort, for grepping a workspace's task history; `focus.json` remains authoritative.
## What it does not do
- ❌ no auto-injection: the notebook never leaks into a request on its own
- ❌ no prompt section, no system-prompt contribution at all
- ❌ no mode knowledge, no snapshot riding frames (mode-agnostic since 2026-09-03)
- ❌ no edit/delete verbs — append or compact-replace, two verbs total
## Install
```bash
dsh plugin --profile <name> add @xiyiyiru/dsh-state
```
Peer dependencies (`@deepseek-ai/cordis`, `dsh-agent`, `dsh-session`, `dsh-system-prompt`, `dsh-tools`) resolve from your dsh installation automatically.
## API
```ts
import {
ADD_STATE, READ_STATE, COMPACT_STATE,
FOCUS_TASK, FOCUS_COMPLETE, FOCUS_TASK_DESCRIPTION, FOCUS_COMPLETE_DESCRIPTION,
SIZE_WARN_THRESHOLD,
} from '@xiyiyiru/dsh-state'
```
| export | what it is |
|---|---|
| `SIZE_WARN_THRESHOLD` | 8000 — where `read_state` starts appending the compress hint |
| `ADD_STATE` … `FOCUS_COMPLETE` | tool names; `*_DESCRIPTION` their model-facing descriptions |
The invariant companion (`@xiyiyiru/dsh-state/invariant`) still validates legacy `mycel/state` events in stored logs written before 0.2.0; the plugin itself writes zero session events.
## Design notes
- **Zero auto-injection** — reading is an explicit act; the notebook costs tokens only when spent deliberately.
- **Append-mostly** — two verbs (append / compact-replace) keep the semantics total; correction is a new line, not a rewrite.
- **Files are truth** — the state directory is the single source; `focus.log` is a convenience projection, lossy by design.
- **Zero session events** — nothing enters the session log, so no harness vocabulary or reader-compatibility surface is involved.
## License
MIT
Install
dsh plugin --profile web add github:xiyiyiru/dsh-state
Profile: web
With the hub plugin installed, ask your agent to install it by name — it resolves the same plan shown here.
dsh plugin --profile web add github:stvlynn/dsh.fish#path:packages/dsh-plugin-hub
install xiyiyiru-dsh-state from the hub
- This package builds from source on install. pnpm will ask you to allow its build script — that is permission to run the package’s code on your machine, outside the agent sandbox. Only allow sources you trust.
- This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.