Introduction¶
Running a long task in DSH often involves dozens of tool calls, where the direction has long since drifted, yet every step looks “reasonable” in isolation. Changing the writing style of the same shell command repeatedly fails, without changing the approach. Hard-earned multi-step processes have to be started from scratch for similar tasks next time. self-evolve (maintainer NAS1112, community directory classification: Memory) is built for this: it reviews the task only after it is fully complete, writes truly worth-remembering experience into local memory or reusable skills, and the desktop pet in the bottom right corner announces this evolution. It doesn’t write a summary after every conversation ends, nor does it interrupt mid-stream.
What is this¶
One-sentence positioning: DSH plugin, Host half + Client half. The Host half is responsible for triggering judgment and persistence when the task ends, while the Client half is responsible for the desktop pet animation and card in the bottom right corner of the session page.
Its three hard constraints are clearly written in the README:
- No interruption mid-way: Review happens only when the task ends, not a single extra word in the conversation flow.
- Write by default (No): Only experience that will “make your future self do things differently” is persisted, max one per turn.
- Data is all local: Memory goes into
AGENTS.md, skills go into~/.agents/skills/, your handwritten content is never overwritten.
Maintainer is NAS1112. The version in package.json in the README is 0.5.2, but the zip in INSTALL.md is self-evolve-0.3.0.zip. When installing, rely on the actual downloaded directory.
Trigger Conditions: Length is not equal to Value¶
Based on DESIGN.md. The core judgment is “a normal development task can easily run over 15 steps”. If triggered by step count, the result is a review every round, filling memory with noise. Therefore, “how many steps ran” does not participate in triggering by default. Only three scenarios are evaluated once at the end of the task:
| Signal | Default Threshold |
|---|---|
| User explicitly asks to remember | Trigger on hit (remember / jot it down / from now on / I’m used to / I prefer / remember that / from now on) |
| A problem actually occurred | Any tool fails 2 times; bash / shell peak consecutive failure reaches 3 times |
| A complex workflow succeeded | 25 steps and 4 distinct tools |
A few details:
- Failure criteria covers
isError,[exit code: N](N≠0),[timed out after ...],[killed by signal: ...]; failure ofeditdue to file changes also counts. - Link counting is reset when the user sends a new message; thresholds measure “this round”; the same turn uses the turn number as an idempotent key, triggering only once.
- When user says “Remember X”, preference types directly point to
scope: "global", while repository conventions remain at project level. - To restore triggering by step count, set
triggerOnLengthOnlytotrue; triggers at fullchainStepsas well. - Turns ending after
ask_user_questionorexit_plan_modeare stopping to wait for the user mid-process; the plugin skips triggering and waits for the turn where the task truly finishes to evolve.
Trigger signals themselves produce no messages. Every step is evaluated in agent/pre-step; if conditions are met, mark that agent as “ready this round”. The protocol body is written into the system prompt via ctx.systemPrompt.section(), and the ready status is written into the dynamically refreshed context via ctx.systemPrompt.context()——only the model can see it, no extra text or messages appear on the user interface. DESIGN.md also notes the origin: earlier used agent.steer() to append a specific review step at turn end; that step’s reply became the “last assistant message of this round”, pushing the real conclusion into the collapsed “Called Tools” group, effectively hiding the answer.
What to Write, How Much¶
The protocol tone is default to writing nothing, and explicitly lists what not to record: what the task achieved, the original error text, another way of saying what is already in memory. If there is no qualified experience, end the round directly, without explaining the protocol to the user or marking it.
“Max one per turn” is written not just in the protocol but is also hard-limited on the tool side (maxRecordsPerTurn, default 1); excess calls return skipped directly without persisting.
Deduplication and merging are handed to the judgment model (optional). When a key is provided, besides judging if it’s worth writing, it also answers merge_v1 (is this repeating an existing entry), relation_v1 (is this updating an entry, can cross-level hit and replace), stale_v1 (which existing entry’s premise no longer holds after this write, mark “Premise Expired”). When judging, the title + first 160 chars of body + level of existing entries are passed to the model (max 12), so changing wording won’t stack another one. When no key is provided, judgment degrades to direct write (fail-open), and the plugin works normally.
Where Data Lands¶
| Content | Path |
|---|---|
| Project Memory | <workspace>/AGENTS.md (within the <!-- self-evolve:begin --> managed section) |
| Global Memory | $DSH_HOME/AGENTS.md |
| Skills written by this plugin | ~/.agents/skills/<name>/SKILL.md |
| Long experience details | $DSH_HOME/self-evolve/details/ |
| Judgment log | $DSH_HOME/self-evolve/judgements.jsonl |
| Last precipitation | $DSH_HOME/self-evolve/last-record.json |
The plugin only modifies the managed section it owns, not your handwritten content; your own written SKILL.md (without ownership markers) won’t be taken over. When self-built skills are written, they carry a line <!-- self-evolve:skill -->, and old files fall back to identification via the managed title in the body—this is also the “cumulative self-built skills” stance, other skills you wrote in the directory do not participate in the count.
Installation and Enablement¶
There is no official command like dsh plugin add; it uses the link: method attached to a profile. Taking $DSH_HOME/profiles/desktop as an example, other profiles just change the path.
- Unzip the plugin directory to a fixed location:
unzip self-evolve-0.3.0.zip -d ~/plugins
# Result: ~/plugins/self-evolve/
The directory should contain index.js (Host), client.js (Client), package.json, cordis.patch.yml, assets/pet/*.gif, locale/.
- Edit
profiles/desktop/package.json, add@local/self-evolvewithlink:todependencies, and put it indsh.profile.bundles(This line is mandatory, or the plugin won’t load with the profile):
{
"dependencies": {
"@local/self-evolve": "link:/Users/you/plugins/self-evolve"
},
"dsh": {
"profile": {
"bundles": [
"@local/self-evolve"
]
}
}
}
- Install dependencies or create symlink directly:
cd "$DSH_HOME/profiles/desktop"
pnpm install # Use this if you have pnpm
# If no pnpm, create the link directly:
ln -sfn ~/plugins/self-evolve node_modules/@local/self-evolve
- Reload: restart
dsh web, then refresh the page.
Optional configuration. Override section in profiles/desktop/cordis.patch.yml:
- id: self-evolve
name: "@local/self-evolve"
config:
shellFailureThreshold: 3
toolFailureThreshold: 2
triggerOnLengthOnly: false
chainSteps: 15
scope: auto
workflowMinSteps: 25
workflowMinDistinctTools: 4
maxRecordsPerTurn: 1
decision:
enabled: true
shadow: true
model: doubao-seed-2-1-lite-260915
baseURL: https://ark.cn-beijing.volces.com/api/v1
apiKey: '' # Leave empty = use key configured in UI, or read env var
apiKeyEnv: A_API_KEY
timeoutMs: 900
worthThreshold: 0.5
scopeMinConfidence: 0.6
disabled: false
It works without this section, using built-in defaults. The judgment model (Volcano Ark System One) is not strictly required: if no key is configured, judgment returns degraded, and the plugin writes normally. To enable judgment, fill in the key in the Experience Library ⚙ page, or write it into apiKey, or put it in an environment variable.
- Verify:
Settings → Plugins: self-evolve appears in the list
Desktop pet appears in the bottom right corner of the page
Click desktop pet → opens Experience Library, all four pages (Memory / Skills / Judgment Log / Settings) are accessible
Probe the route via command line:
curl -s -o /dev/null -w '%{http_code}\n' \
-H 'x-self-evolve: 1' \
"http://127.0.0.1:62100/api/self-evolve/pet/idle.gif"
# Expect 200
- Regression test (optional, plugin needs to be running):
cd self-evolve/test
pnpm install
pnpm test
test-routes.mjs defaults to http://127.0.0.1:62100; change the port using SELF_EVOLVE_BASE=http://127.0.0.1:<port>.
Daily Usage¶
After installation, nothing is needed. Usually it is completely invisible, only popping up a small card at the bottom right when the task ends:
- Click desktop pet to open Experience Library: view memory/skills, apply or discard skill proposals, edit/delete in place, use “Soup of Forgetfulness” to batch clear.
- Telling the model “Remember…” triggers a recording immediately, no need to wait for failure or long chains.
- Model side provides three tools:
self_evolve_record,self_evolve_propose,self_evolve_status.self_evolve_proposetarget relaxed from “only self-built skills” to any skill under the skills directory, including your own; the model can only submit proposals, persisting requires a person to click “Apply” in the Experience Library, and rewriting cannot secretly add ownership markers.
A few things to know about the interface shape: The desktop pet in the bottom right corner is always present; a purple badge in the top right shows cumulative evolution count for this session; starting from the 2nd evolution in the same session, it only appears as a small card automatically, not interrupting reading; clicking the small card upgrades to a full-screen ledger. The dock card has three stages—Burst (precipitating) → Flash (completion moment) → Settled (written); the ledger shows the trigger reason, judgment value, write target, and history for this session. The 1st evolution won’t interrupt a streaming output answer with a full-screen card—the answer would be covered—animation timing is controlled by the running gate, nothing is displayed while this round is still running.
Suitable Scenarios and Notes¶
Suitable for this approach: review only after task completion, default write nothing only write useful stuff, experience falls in local repo or home directory not cloud, want a non-interrupting reminder form. If you prefer manual summary every time, or collect experience in a centralized service, this might not fit.
A few boundaries that must be known:
- Run with current dsh process permissions. The plugin can read/write the workspace and home directory; before installing, check source code and license (README’s
package.jsonis marked MIT) to decide whether to attach it to the profile. - Global write relies on the approval channel. If the current combination has no available approval answerer, boundary-crossing writes will fail closed and return
FS_SANDBOX_DENIED; at this point, you can only switchscopeback toworkspace, or explicitly set the file strategy todanger-full-access. - “New workflow” is a heuristic judgment (steps + tool types), no semantic understanding. A long chain that is indeed repetitive labor for the same thing might be judged as a workflow; the prompt text already requires the model to skip and explain during “one-time processes”, so the cost of a misjudgment is a harmless self-explanation.
- Counting is stored in memory (
WeakMap), starting from zero after process restart or session switch. This is an indicator of “whether this round went off track”, and does not need to be persisted by design. - Review happens at
agent/turn-stopping; if the user cancels this round mid-process, this round won’t be reviewed—consistent with the semantics of “evolution only after task completion”, but work that was cancelled won’t accumulate experience. - If a skill is written wrongly (invalid frontmatter), it will be ignored by
dsh-skill-filesystemand warned in logs; this plugin only appends valid content, doesn’t modify existing frontmatter. - When installed via
link:, modifyingindex.jsmight not hot-reload immediately. Callself_evolve_statusbefore verifying to confirm theenvironmentfield exists—lack of this field means old code is running, need to turn the plugin off and on in the plugin page. - Client half (animation) loading timing differs from Host half: changes to
dsh.clientrequire reinstalling the bundle and refreshing the page to take effect. Just reloading the plugin is not enough for new registered slots to appear.
Trigger thresholds, implementation details of evolution animations, memory levels and deduplication, Experience Library, self-optimization proposals, judgment model, full configuration table, design trade-offs, and verification methods are all in DESIGN.md in the repository.
Conclusion¶
self-evolve does a narrow thing: review once after the task ends, write one truly useful experience into local memory or skill, then tell you via the desktop pet what this evolution is. It doesn’t rewrite the conversation flow, doesn’t overwrite your handwritten content, and doesn’t send experience elsewhere. If this is exactly what you lack, start here:
- Directory page: https://www.skillhub.cn/plugins/NAS1112/self-evolve
- GitHub: https://github.com/NAS1112/self-evolve