Introduction¶
DSH’s @deepseek-ai/dsh-agent-loop is responsible for the main loop of “invoking the model, running tools, and repeating”. Its behavior is generic: there is no concept of a step budget; in long tasks, the model repeatedly re-reads and regenerates the entire file, and uses shell arguments to stuff large chunks of scripts, while the loop continues to cooperate.
dsh-agent-loop-experiment is an isolated experiment designed to address this phenomenon. It keeps the official loop implementation byte-for-byte unchanged, only adds convergence strategies on top of public prompts and agent hooks, and replaces the official loop entry as a bundle patch. The project is maintained by Jaylor-Wang, is MIT licensed, and has fixed support for DSH 0.2.2-rc.1.
Below is an introduction to its capabilities, installation methods, configuration, and verification process.
What is this¶
It is a convergence strategy experiment package, and also a bundle that can be directly swapped for the official loop.
It corresponds to four things in the official loop:
- Runtime strategy (fingerprinting): Observes operation fingerprints, intercepts consecutive progress-free repeated calls and overly large shell arguments.
- Finite recovery: Provides a short reference recovery for failed native writes in this round, only removing invalid
justificationparameters. - File mutation guards: Identifies risks of full file rewrites and large file rewrites, graded by
observe/enforce. - Soft tool requirements: Reminds the model to use expected tools, intercepts side-effects of tool bypassing.
The official loop implementation itself is not modified; src/index.js mounts the official loop and policies in the same scope. The replacement functionality is provided by dsh.bundle.patch and src/replacement.js, and the replacement class inherits from the official loop.
Version and Package Structure¶
The package name is dsh-agent-loop-experiment, version 0.2.2-rc.1, marked private, license MIT. engines.dsh constrains 0.2.2-rc.1, and engines.node requires >=24.0.0. The @deepseek-ai/dsh-* series in peerDependencies are all aligned to 0.2.2-rc.1.
The entry point exposes four segments via exports:
| Entry | Purpose |
|---|---|
. |
Package entry mounting official loop + policy |
./baseline |
Unmodified official baseline |
./policy |
Only loads convergence policy |
./replacement |
Single service replacement entry using official flat config |
This is a compiled artifact package; the experiment layer uses native ES modules and requires no compilation. The README lists 63 passing tests.
Convergence Strategy Configuration¶
Strategy configuration is centralized in src/convergence.js. The independent entry defaults to disabling the policy, but it is explicitly enabled when installed as a bundle.
| Config | Default | Meaning |
|---|---|---|
enabled |
false |
Explicitly enable convergence strategy |
softStepBudget |
8 |
After completing this many model steps, remind once in the next required request |
recoverFailedWrites |
true |
Short reference recovery for large write parameter errors, can be disabled independently |
runtimePolicy.mode |
enforce |
off / observe / enforce, under enforce intercepts no-progress and large Shell Payload |
runtimePolicy.noProgressLimit |
2 |
Tolerance threshold for consecutive repetitions of the same command fingerprint |
runtimePolicy.recoveryAttemptLimit |
1 |
Upper limit on number of times recovery reference can be attempted |
runtimePolicy.minLargeWriteBytes |
8192 |
Large write byte threshold |
runtimePolicy.maxShellCommandBytes |
4096 |
Shell command line byte limit (prevents spawn ENAMETOOLONG) |
runtimePolicy.maxInlineScriptBytes |
512 |
Single inline script byte limit (python -c / node -e) |
fileMutationPolicy.mode |
observe |
off / observe / enforce |
fileMutationPolicy.minLargeWriteBytes |
8192 |
Large file rewrite threshold (bytes) |
fileMutationPolicy.reminderLimit |
1 |
Upper limit on number of file rewrite risk reminders |
softToolRequirement.enabled |
true |
Soft tool requirement manager switch |
softToolRequirement.reminderLimit |
1 |
Upper limit on reminders per soft requirement |
softToolRequirement.maxViolations |
2 |
Maximum number of violations tolerated before blocking bypassing |
Soft tool requirements require the host to explicitly activate them via SoftToolRequirementManager.addRequirement(session, options) based on the task. The requirement status is only retained until the current turn ends. The rejection result is a JSON with code, nextAction, and retryable. Currently, there is no automatic forced tool choice, and the real model’s false rejection rate has not been verified; A/B testing is required before using enforce.
Installation and Enablement¶
Below are the official plugin management commands provided in EXPERIMENT.md. It uses the link: prefix to point to the local DSH installation directory on your machine. Replace the path according to your own machine; do not blindly copy the drive letter from the example.
dsh plugin --profile web add "link:E:/Github/dsh-agent-loop"
Pre-steps: execute in the package directory:
npm run setup:local
npm test
npm run inspect:profile
setup:local reuses the same version of dependencies from your local DSH installation. Missing dependencies are created as local directory links; it does not access the registry and does not write to the host installation. It will reject if existing dependency versions do not match, and will not automatically overwrite. You can run npm run inspect:profile at runtime. It synthesizes the replaced configuration in memory only; it does not start the service, does not read credentials, and does not modify the profile. When inspection passes, you can see the original loop is disabled, the new loop is enabled, the original config is retained, and there are 0 warnings.
Do not switch if inspect:profile fails. DSH’s config is a full replacement, so the overriding fragment must preserve agents and maxParallelToolCalls:
- id: dsh-agent-loop-experiment:loop
name: dsh-agent-loop-experiment/replacement
config:
agents: []
maxParallelToolCalls: 10
convergence:
enabled: true
softStepBudget: 8
After confirmation, start:
dsh web
This is server-side loop replacement; no need to rebuild the Web frontend, and it doesn’t take effect just by refreshing the page. You need to stop dsh web, install the link, then restart the process and create a new session. After installation, run npm run inspect:profile again in the package directory to confirm bundleAlreadySelected is true and activeLoopCount is 1, then start.
Rollback method: First stop dsh web, remove the manually added override fragment, then:
dsh plugin --profile web remove dsh-agent-loop-experiment
dsh web
After the bundle is removed, the patch that disables the official loop also disappears, and the original agent-loop entry is automatically restored. If you have a personal override in the profile with id: agent-loop / disabled: true, you must also remove it or change it to false to avoid the personal layer continuing to disable the official loop.
Typical Usage¶
Connecting to an independent host (not using a bundle):
import experiment from 'dsh-agent-loop-experiment';
await ctx.plugin(experiment, {
loop: { maxParallelToolCalls: 10, agents: [] },
convergence: { enabled: true, softStepBudget: 8 }
});
An independent host must provide official agents, sessions, llm, tools, systemPrompt, and sessionProjections services, and configure the tool executor and model adapter. Do not mount the package entry on top of an existing official loop.
For an independent host that already has an official loop, you can load the policy only by mounting it in the scope of agent setup, which only affects that agent:
import convergence from 'dsh-agent-loop-experiment/policy';
Before verifying real online behavior, use fixed responses for offline comparison first:
npm run experiment
It uses a fixed model response script; both groups are expected to have 4 requests and 4 tool calls, with the difference being that the convergence group adds one reminder. This verifies the strategy wiring, normal completion, and prefix retention, but cannot prove that the real model will take fewer steps.
Real comparison requires copying experiments/live.example.json to experiments/live.local.json, filling in your own model, protocol, and endpoint, and keeping the two sets of configuration consistent. Credentials are passed only through environment variables specified by apiKeyEnv, not written into the configuration. By default, only initialization is verified, and no model requests are sent:
npm run experiment:live -- --config experiments/live.local.json --variant baseline
npm run experiment:live -- --config experiments/live.local.json --variant convergence
Add --run only after you are ready to send model requests. Each command starts a new independent process and memory session, does not modify the GUI, and does not restore the current session.
Actual Behavior and Boundaries of the Strategy¶
When enabled, fixed guidance is added to the prompt starting from the first step, and the fixed guidance does not change with the steps. The content mainly includes: deciding a disk path before outputting large content; new files use native write, calculating priority to reuse existing tools or write reusable scripts then execute by path; full files and large scripts are not put into shell arguments like node -e, python -c, pwsh -Command; independent reads, searches, and checks can be batched in the same response, preserving dependency order and exclusive barriers; local modifications prioritize reusing existing file content, using only edit and unique matching modifications, rather than re-outputting the entire file; cleanup after resource usage, preserving order.
When the soft budget is reached, at most one reminder is appended to the tail of the history per round. Reminders are submitted by the official loop. The budget is calculated based on the count of step/start accepted in the current round; retries are not counted repeatedly and do not accumulate across the entire session. It is expected that short tasks do not produce reminders.
These guidelines apply to tasks such as code modification, data processing, document and content generation, without heuristic interception for specific task names, file types, calculation rounds, or output sizes. Read-only Q&A and review will not gain write authorization.
Explicitly not done: no limit on total steps, no automatic declaration of completion, no modification of provider/model/reasoningEffort, no calls with dependencies, and no rewriting of existing tool results. Natural termination, max-tokens, concludeTurn, and rejection all maintain official behavior. Trimming is still handled by dsh-tool-result-trimmer.
The trigger conditions for large write recovery are very narrow: it only accepts native root writes from this round, full content between 8 KiB and 1 MiB, and the error is exactly invalid escalation: justification is only valid together with sandbox_permissions; the original call must not have sandbox_permissions, only allowing the three parameters file_path / content / justification. The plugin does not parse shell and does not automatically retry. When the conditions are met, a notification with source.kind set to loop-write-recovery is appended, and the model can call retry_failed_write({"call_id":"original call ID"}) to reuse the original path and exactly the same content. You can disable this entry with recoverFailedWrites: false under convergence.
If the host uses a complete system prompt, the normal prompt section will be overridden by official rules, and convergence guidance may not enter the system prompt. Enabling or disabling during runtime changes the prompt at that time; comparison experiments should start from a fresh session; this experiment does not promise that cache prefixes remain unchanged during hot switching.
Applicable Scenarios and Notes¶
Suitable for: People who are already running long tasks on the DSH Web profile or independent hosts and want to compare the actual impact of convergence strategies on prompts and step counts; and maintainers who want to keep the official loop and only add an optional layer of strategies.
Notes:
- The plugin runs with the permissions of the current DSH process and can read/write the process’s working directory and credential environment. Read
src/convergence.js,src/replacement.js,src/failure-recovery.js, andcordis.patch.ymlbefore installation to confirm the patch scope. The license is MIT. - The
link:prefix in the install command points to a local directory; private packages can be installed locally without needing to publish to npm. Do not runnpm installorpnpm installinside the package, nor install the host’s existing Cordis or React just to eliminate peer warnings. - Fixed support for
0.2.2-rc.1; after the host upgrades, you need to re-runsetup:local, relevant verification, andinspect:profile; handle compatibility first if the version does not match. Current results do not constitute a compatibility guarantee for future versions. - Ensure only one loop service is enabled in the profile. Having both
agent-loopand the experiment entry enabled simultaneously will cause a conflict. - Changing
enabledtofalsecan keep the replacement loop but only turn off the convergence strategy, suitable for same-process comparison; a cleaner comparison is to revert to the official loop and then enable it.
Directory and Repository:
- https://www.skillhub.cn/plugins/Jaylor-Wang/dsh-agent-loop-experiment
- https://github.com/Jaylor-Wang/dsh-agent-loop-experiment