dsh-loop-engine 0.1.5-rc3 → 0.1.5-rc5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +44 -187
- package/README.zh.md +45 -103
- package/lib/client.js +887 -266
- package/lib/index.js +2216 -915
- package/lib/invariant.js +43 -45
- package/lib/types/agent-preset-ids.d.ts +303 -0
- package/lib/types/client/LoopEngineBadge.d.ts +44 -17
- package/lib/types/client/LoopEngineComposerSelect.d.ts +79 -13
- package/lib/types/client/LoopEngineSection.d.ts +5 -4
- package/lib/types/client/locales.d.ts +133 -7
- package/lib/types/client/reload.d.ts +135 -0
- package/lib/types/client/session-engine.d.ts +474 -0
- package/lib/types/client/store.d.ts +1 -1
- package/lib/types/client/turn-status.d.ts +112 -10
- package/lib/types/client/use-session-engine.d.ts +66 -0
- package/lib/types/commands.d.ts +11 -3
- package/lib/types/driver-core/host-servers.d.ts +106 -0
- package/lib/types/driver-core/hosted-engine-runtime.d.ts +190 -0
- package/lib/types/driver-core/hosted-tool-vocabulary.d.ts +72 -0
- package/lib/types/driver-core/model-handover.d.ts +134 -0
- package/lib/types/driver-core/ownership.d.ts +6 -5
- package/lib/types/driver-core/prompt.d.ts +32 -0
- package/lib/types/driver-core/session-lifetime.d.ts +62 -0
- package/lib/types/driver-core/session-model.d.ts +82 -0
- package/lib/types/engine-claude/agent.d.ts +23 -3
- package/lib/types/engine-claude/loop.d.ts +16 -15
- package/lib/types/engine-codex/agent.d.ts +22 -3
- package/lib/types/engine-codex/appserver/client.d.ts +15 -2
- package/lib/types/engine-codex/loop.d.ts +13 -15
- package/lib/types/engine-codex/model-handover.d.ts +56 -0
- package/lib/types/engine-kimi/acp/client.d.ts +10 -0
- package/lib/types/engine-kimi/agent.d.ts +19 -2
- package/lib/types/engine-kimi/commands.d.ts +18 -14
- package/lib/types/engine-kimi/loop.d.ts +14 -16
- package/lib/types/engine-kimi/model-handover.d.ts +32 -0
- package/lib/types/engine-kimi/process.d.ts +2 -2
- package/lib/types/engine-kimi/types.d.ts +1 -1
- package/lib/types/engine-of-session.d.ts +97 -0
- package/lib/types/engine-pi/agent.d.ts +25 -23
- package/lib/types/engine-pi/loop.d.ts +13 -23
- package/lib/types/engine-pi/model-handover.d.ts +39 -0
- package/lib/types/engine-pi/types.d.ts +2 -2
- package/lib/types/engine-remote.d.ts +192 -0
- package/lib/types/engine-surface.d.ts +36 -0
- package/lib/types/index.d.ts +51 -50
- package/lib/types/invariant.d.ts +8 -5
- package/lib/types/model-selection-reset.d.ts +271 -0
- package/lib/types/patch-manager.d.ts +57 -39
- package/lib/types/preset.d.ts +39 -26
- package/lib/types/provider-route.d.ts +83 -36
- package/lib/types/router-loop.d.ts +406 -0
- package/lib/types/session-engine-store.d.ts +138 -0
- package/lib/types/settings.d.ts +12 -11
- package/package.json +109 -104
package/README.md
CHANGED
|
@@ -2,10 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/dsh-loop-engine)
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
"Loop engine" dropdown in Settings chooses which driver runs your agents — the
|
|
7
|
-
built-in in-process loop, the Claude Code CLI, the Codex CLI, the Pi CLI, or the
|
|
8
|
-
Kimi Code CLI — without changing anything in the main repository.
|
|
5
|
+
Pick the agent loop engine of **dsh web** the same way you pick a model — **per session**: the built-in `in-process` loop (the default), or one of the four hosted engines `claude-code`, `codex`, `pi`, `kimi`. The engine is the plugin's own per-session record, so **any open, idle session can be moved to another engine** — between two hosted engines it is an **in-place handover**, and a move involving `in-process` releases that session's agent and reloads the page (the `dsh web` process never restarts). Sessions are independent, so one chat can run Codex while another runs Kimi. None of this changes anything in the main repository.
|
|
9
6
|
|
|
10
7
|
## Install
|
|
11
8
|
|
|
@@ -13,205 +10,65 @@ Kimi Code CLI — without changing anything in the main repository.
|
|
|
13
10
|
dsh plugin --profile web add dsh-loop-engine
|
|
14
11
|
```
|
|
15
12
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
> Switching engines rewrites a small managed block in `cordis.patch.yml`.
|
|
19
|
-
> Everything else you wrote in that file is preserved; only the plugin's own
|
|
20
|
-
> span changes.
|
|
21
|
-
|
|
22
|
-
> **pnpm users:** pnpm 10+ blocks dependency build scripts by default, so the
|
|
23
|
-
> install may fail with `ERR_PNPM_IGNORED_BUILDS` naming `esbuild`,
|
|
24
|
-
> `@google/genai`, and `protobufjs` (all reached through the engine SDKs). This
|
|
25
|
-
> is expected — allow them and retry, either interactively with
|
|
26
|
-
> `pnpm approve-builds`, or by declaring them in the installing project's
|
|
27
|
-
> `pnpm-workspace.yaml`:
|
|
28
|
-
>
|
|
29
|
-
> ```yaml
|
|
30
|
-
> allowBuilds:
|
|
31
|
-
> esbuild: true
|
|
32
|
-
> '@google/genai': true
|
|
33
|
-
> protobufjs: true
|
|
34
|
-
> ```
|
|
35
|
-
>
|
|
36
|
-
> Only the installing project can grant this; the plugin cannot pre-approve its
|
|
37
|
-
> own dependencies. Note that `allowBuilds` is the pnpm 11 spelling — pnpm 11
|
|
38
|
-
> **deletes** the legacy `onlyBuiltDependencies` (and `neverBuiltDependencies`,
|
|
39
|
-
> `ignoredBuiltDependencies`) keys from `package.json` and no longer honors
|
|
40
|
-
> them, so putting them there silently does nothing.
|
|
13
|
+
Boot `dsh web` so the profile recomposes with the plugin, then open **Settings → Loop engine**. One boot is enough: the router retries for a bounded window while the base bundle's `agent-loop` row still holds the factory slot.
|
|
41
14
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
The install above assumes a **published** dsh (`npx @deepseek-ai/dsh`) and needs
|
|
45
|
-
no extra setup. Booting the harness from its **source checkout**
|
|
46
|
-
(`cd deepseek-harness && pnpm dsh web`) takes one more step, because the two
|
|
47
|
-
halves then resolve harness packages to different files:
|
|
48
|
-
|
|
49
|
-
| Side | `@deepseek-ai/dsh-scope` resolves to |
|
|
50
|
-
|---|---|
|
|
51
|
-
| Source-launched harness | `packages/core/scope/src/index.ts` (via tsconfig `paths`) |
|
|
52
|
-
| Installed plugin (its tarball ships only `lib/`) | `packages/core/scope/lib/index.js` |
|
|
53
|
-
|
|
54
|
-
That is one package loaded as two module instances. `dsh-scope` tags a context
|
|
55
|
-
with a module-local `Symbol('dsh.scope')`, so a scope minted through one instance
|
|
56
|
-
is invisible to the other, and resuming a session fails with:
|
|
57
|
-
|
|
58
|
-
```
|
|
59
|
-
agent-presets: refusing to compose an unscoped context;
|
|
60
|
-
the scope key is what joins an agent to its preset
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
Bridge the profile's peers to the harness source so both halves share one
|
|
64
|
-
instance. Set `HARNESS` to the harness checkout **as a `file://` URL**, then run
|
|
65
|
-
this from the profile directory:
|
|
15
|
+
> Installing rewrites one **engine-agnostic** managed block in `cordis.patch.yml`: it disables the base bundle's `agent-loop` row so the plugin's own router can own the process's single agent-factory slot, and every other byte you wrote in that file is preserved. Upgrading from an earlier release needs no hand edit — a block that named the one pinned engine is rewritten to this form on the next boot, and seeds the Settings default with that engine.
|
|
66
16
|
|
|
67
|
-
|
|
68
|
-
HARNESS=file:///path/to/deepseek-harness # e.g. file:///D:/repos/deepseek-harness
|
|
69
|
-
cd "$DSH_HOME/profiles/web" && mkdir -p shims
|
|
70
|
-
while IFS='|' read -r name rel; do
|
|
71
|
-
mkdir -p "shims/$name"
|
|
72
|
-
printf '{"name":"@deepseek-ai/%s","version":"0.0.0","private":true,"type":"module","main":"index.mjs"}\n' \
|
|
73
|
-
"$name" > "shims/$name/package.json"
|
|
74
|
-
printf "export * from '%s/%s'\nimport * as mod from '%s/%s'\nexport default mod.default\n" \
|
|
75
|
-
"$HARNESS" "$rel" "$HARNESS" "$rel" > "shims/$name/index.mjs"
|
|
76
|
-
done <<EOF
|
|
77
|
-
cordis|vendor/cordis/src/index.ts
|
|
78
|
-
schemastery|vendor/schemastery/src/index.ts
|
|
79
|
-
dsh-agent|packages/core/agent/src/index.ts
|
|
80
|
-
dsh-scope|packages/core/scope/src/index.ts
|
|
81
|
-
dsh-session|packages/core/session/src/index.ts
|
|
82
|
-
dsh-session-persistence|packages/session/session-persistence/src/index.ts
|
|
83
|
-
dsh-settings|packages/settings/settings/src/index.ts
|
|
84
|
-
dsh-subprocess|packages/subprocess/subprocess/src/index.ts
|
|
85
|
-
dsh-timeout|packages/util/timeout/src/index.ts
|
|
86
|
-
dsh-llm|packages/llm/llm/src/index.ts
|
|
87
|
-
dsh-invariants|packages/runtime-diagnostics/invariants/src/index.ts
|
|
88
|
-
dsh-home-paths|packages/util/home-paths/src/index.ts
|
|
89
|
-
EOF
|
|
90
|
-
```
|
|
91
|
-
|
|
92
|
-
Then point the profile's `package.json` at them and reinstall:
|
|
93
|
-
|
|
94
|
-
```sh
|
|
95
|
-
node -e 'const f="package.json",j=require("./"+f),d=j.dependencies??={}
|
|
96
|
-
for(const n of ["cordis","schemastery","dsh-agent","dsh-scope","dsh-session","dsh-session-persistence","dsh-settings","dsh-subprocess","dsh-timeout","dsh-llm","dsh-invariants","dsh-home-paths"])
|
|
97
|
-
d["@deepseek-ai/"+n]="file:./shims/"+n
|
|
98
|
-
require("fs").writeFileSync(f,JSON.stringify(j,null,2)+"\n")'
|
|
99
|
-
pnpm install
|
|
100
|
-
```
|
|
17
|
+
> **pnpm users:** pnpm 10+ blocks dependency build scripts by default, so the install may fail with `ERR_PNPM_IGNORED_BUILDS` naming `esbuild`, `@google/genai`, and `protobufjs` (all reached through the engine SDKs). Allow them and retry — `pnpm approve-builds`, or `allowBuilds` in `pnpm-workspace.yaml`. Only the installing project can grant this; the plugin cannot pre-approve its own dependencies.
|
|
101
18
|
|
|
102
|
-
|
|
103
|
-
subpath, also give that shim an `invariant.mjs` (`export * from
|
|
104
|
-
'$HARNESS/packages/core/scope/src/invariant.ts'`) and add
|
|
105
|
-
`"./invariant": "./invariant.mjs"` to its `exports`.
|
|
19
|
+
### Running against a harness source checkout
|
|
106
20
|
|
|
107
|
-
|
|
108
|
-
> when the checkout sits beside the harness repo it inherits the harness's own
|
|
109
|
-
> `tsconfig.json` and with it the same `paths` mapping. The split only appears
|
|
110
|
-
> when a *packed* plugin (npm or tarball) meets a *source* harness.
|
|
21
|
+
A **published** dsh needs no extra setup. Booting the harness from its **source checkout** (`cd deepseek-harness && pnpm dsh web`) takes one more step: bridge the profile's harness peer packages to the checkout source with `file:` shims, so both halves share one module instance (a scope minted through one instance is invisible to the other, and session resume fails with `agent-presets: refusing to compose an unscoped context`). The peer list includes `@deepseek-ai/dsh-agent-loop`. The runnable shim steps are in [docs/source-checkout.md](docs/source-checkout.md); why every `@deepseek-ai/*` package must stay a single instance is in [docs/architecture.md](docs/architecture.md). Installing the plugin as a local **`link:`** checkout sidesteps this entirely.
|
|
111
22
|
|
|
112
23
|
## Version compatibility
|
|
113
24
|
|
|
114
|
-
dsh-loop-engine is versioned **in lockstep with the harness it targets**: the
|
|
115
|
-
version is the harness version plus a plugin release counter (`0.1.5-rc1` and
|
|
116
|
-
`0.1.5-rc2` target harness `0.1.5-rc.1`; `0.1.5-rc3` targets harness
|
|
117
|
-
`0.1.5-rc.2`), and every harness package it consumes is pinned exactly in
|
|
118
|
-
`peerDependencies`. The two must be matched — a mismatch fails loudly at boot
|
|
119
|
-
or session resume:
|
|
120
|
-
|
|
121
|
-
| dsh-loop-engine | Requires harness |
|
|
122
|
-
|---|---|
|
|
123
|
-
| 0.1.5-rc3 | **0.1.5-rc.2** |
|
|
124
|
-
| 0.1.5-rc1, 0.1.5-rc2 | 0.1.5-rc.1 |
|
|
125
|
-
| 1.0.0-rc8 … 1.0.0-rc15 | 0.1.2-rc.1 |
|
|
126
|
-
| 1.0.0-rc7 and earlier | 0.1.1-rc.2 |
|
|
127
|
-
|
|
128
|
-
- **Each 0.1.5-rcN release requires the 0.1.5 patch it was built for.**
|
|
129
|
-
`0.1.5-rc1`/`0.1.5-rc2` require harness `0.1.5-rc.1`; `0.1.5-rc3` requires
|
|
130
|
-
harness `0.1.5-rc.2`. All three use the 0.1.5
|
|
131
|
-
assistant-stream contract (`assistant/message` embeds its exact timed
|
|
132
|
-
`stream` and rejects `sourceEventSeqs`), the driver-owned `Inbox` interface,
|
|
133
|
-
the two-argument `AgentSetup`, and the `SessionPersistence.create` / `open`
|
|
134
|
-
handle seam. `0.1.5-rc.2` is a client-UI/docs backport that leaves those
|
|
135
|
-
seams untouched, so the driver code is identical across `0.1.5-rc.1` and
|
|
136
|
-
`0.1.5-rc.2`.
|
|
137
|
-
- Releases up to `1.0.0-rc15` used the plugin's own version series and target
|
|
138
|
-
harness `0.1.2-rc.1`; they are not compatible with harness `0.1.5-rc.1`.
|
|
139
|
-
- To use the plugin with an older harness, install the release matching it
|
|
140
|
-
(e.g. `npm i dsh-loop-engine@1.0.0-rc15` for harness 0.1.2-rc.1).
|
|
141
|
-
- The GitHub Release body of each tag states the harness version it targets.
|
|
25
|
+
dsh-loop-engine is versioned **in lockstep with the harness it targets**: `<harness version>-rcN`. `0.1.5-rc3` targets harness `0.1.5-rc.2`; `0.1.5-rc1`/`0.1.5-rc2` target `0.1.5-rc.1`; `1.0.0-rc8` … `1.0.0-rc15` target `0.1.2-rc.1`; `1.0.0-rc7` and earlier target `0.1.1-rc.2`. Every harness package it consumes is pinned exactly in `peerDependencies`, and the two must be matched — a mismatch fails loudly at boot or session resume. To use the plugin with an older harness, install the release matching it (each GitHub Release states the harness version it targets).
|
|
142
26
|
|
|
143
27
|
### Requirements
|
|
144
28
|
|
|
145
|
-
-
|
|
146
|
-
|
|
147
|
-
-
|
|
148
|
-
|
|
149
|
-
-
|
|
150
|
-
`~/.pi/agent/auth.json` or the provider's API-key environment variable such as
|
|
151
|
-
`ANTHROPIC_API_KEY`).
|
|
152
|
-
- For the Kimi Code engine: the `kimi` CLI installed and logged in on the host
|
|
153
|
-
(e.g. `kimi login`), and reachable on `PATH` (or pinned to an absolute path
|
|
154
|
-
via `kimiBin` in the composition entry).
|
|
29
|
+
- **Claude Code**: the Claude Code CLI installed and logged in on the host.
|
|
30
|
+
- **Codex**: authenticated via `codex login` on the host, or a `CODEX_API_KEY` environment entry.
|
|
31
|
+
- **Pi**: authenticated the way `pi` expects (its own `~/.pi/agent/auth.json`, or the provider's API-key environment variable such as `ANTHROPIC_API_KEY`).
|
|
32
|
+
- **Kimi Code**: the `kimi` CLI installed and logged in (e.g. `kimi login`), and reachable on `PATH` (or pinned to an absolute path via `kimiBin` in the composition entry).
|
|
33
|
+
- Running the harness from a source checkout needs the extra `dsh-agent-loop` `file:` shim above.
|
|
155
34
|
|
|
156
35
|
## Usage
|
|
157
36
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
37
|
+
**The engine is chosen per session.** Pick `Claude Code`, `Codex`, `Pi`, or `Kimi Code` on the new-session screen's preset chip (beside the workspace picker); keep the deployment's own default (usually `standard`) to run the built-in in-process loop. Different sessions can run different engines at the same time, and a child agent inherits the engine of the agent that spawned it.
|
|
38
|
+
|
|
39
|
+
- **Settings → Loop engine** sets the default for **new** sessions only — it lands immediately, with no restart and no page reload, and sessions already running are unaffected. Choosing **In-process** restores whatever default the plugin replaced.
|
|
40
|
+
- **Moving a session you are in** uses the composer's engine selector (enable *Show the engine selector in the chat page*), which calls the plugin's own `remote.loopEngine.select`. Any open, idle session can move — including one that has already run many turns.
|
|
41
|
+
- **Between two hosted engines the move is an in-place handover**: the session stays open, only the agent is replaced — it applies on the spot, with nothing to confirm.
|
|
42
|
+
- **A move involving `in-process` cannot be handed over in place** (the harness loop neither accepts a session it did not create nor hands a live one over), so the composer asks first: the switch reloads the page, and this page's scroll position and unsent draft go with it (the conversation record does not). On confirm the host RELEASES that session's agent and answers `reload: true`; the page reloads itself and reopens the same session, which is what makes the host build it on the recorded engine. The process never restarts.
|
|
43
|
+
- A session that is **mid-turn is refused** rather than interrupted, and a **subagent's** session cannot be moved at all.
|
|
44
|
+
- **Models**: every hosted engine shares ONE `external` group in the model menu, holding exactly one entry, `default` — meaning "the engine decides". Picking a **real dsh model** hands it to the engine **with its endpoint and credential** (whether the engine can serve it is the engine's business — a refusal is reported, not swallowed). `in-process` sessions use dsh's models as usual.
|
|
45
|
+
- **Uninstall**: `dsh plugin --profile web remove dsh-loop-engine`, then manually delete the plugin's managed block from the profile's `cordis.patch.yml` — the block outlives the plugin, and while it is present the base `agent-loop` row stays disabled, leaving the profile with no agent factory.
|
|
163
46
|
|
|
164
47
|
### What a hosted engine takes over
|
|
165
48
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
to the git root, plus `~/.codex/AGENTS.md`.
|
|
190
|
-
- The Pi driver runs `pi --mode rpc`; Pi has no permission system, so the whole
|
|
191
|
-
child is sandboxed through the dsh subprocess service (default `read-only`).
|
|
192
|
-
Its context files (`AGENTS.md`/`CLAUDE.md` with `AGENTS.override.md`
|
|
193
|
-
preferred, plus the user-level file under the pi config dir) and its
|
|
194
|
-
`skills/` catalogs (`~/.pi/agent/skills/` and `.pi/skills/`) are surfaced
|
|
195
|
-
through the dsh skill-injection seam.
|
|
196
|
-
- The Kimi Code driver runs a persistent `kimi acp` child (Agent Client
|
|
197
|
-
Protocol over stdio) and speaks one stateless `session/new` + `session/prompt`
|
|
198
|
-
per dsh step; the durable dsh session log is the sole model context. It streams
|
|
199
|
-
assistant text (`agent_message_chunk`) and thinking (`agent_thought_chunk`)
|
|
200
|
-
incrementally as live `agent/assistant-stream` frames, and the step's durable
|
|
201
|
-
`assistant/message` embeds that exact timed stream; it maps tool calls/streams
|
|
202
|
-
(`tool_call` / `tool_call_update`) into `tool/call` + `tool/result`. ACP surfaces tool
|
|
203
|
-
approvals as `session/request_permission`, which the driver answers from the
|
|
204
|
-
session's dsh approval knobs (an `ask` policy denies, fail-closed). The child
|
|
205
|
-
is spawned through the dsh subprocess seam — the only privilege boundary
|
|
206
|
-
(default read-only sandbox). Its project `AGENTS.md` chain (cwd→git root) and
|
|
207
|
-
`.kimi-code/skills/` catalogs (user and project) are surfaced through the dsh
|
|
208
|
-
skill-injection seam, and its slash commands are bridged (built-ins forward the
|
|
209
|
-
raw `/name` line back to the engine, which expands it). The prompt is an ACP
|
|
210
|
-
request body — not an argv positional — so there is no command-line length
|
|
211
|
-
ceiling. Note Kimi's remaining slash-command surface is TUI-only
|
|
212
|
-
(`/login`, `/provider`, `/settings`, `/sessions`, …); those are not bridged
|
|
213
|
-
because the ACP prompt surface does not expand them, but `skill:` commands are
|
|
214
|
-
carried by the skill seam and Kimi's own shorthand.
|
|
49
|
+
- Its preset is a copy of `standard` minus the dsh-native rows an external engine replaces — dsh's `/plan`, `/compact` (and auto-compaction), the model-facing goal tool, the human `/goal` command, and the dsh skill rows (one stripped preset per engine, under `$DSH_HOME/.agent-presets/loop-engine-<engine>/`).
|
|
50
|
+
- The engine's own slash commands and skill catalog are registered into **that agent's own scope**, so two sessions on different engines never see each other's menus, and the whole surface is released with the agent.
|
|
51
|
+
- Engine-agnostic dsh commands (`/export`, `/feedback`, `/permission`) keep working and stay.
|
|
52
|
+
|
|
53
|
+
## Known limitations
|
|
54
|
+
|
|
55
|
+
- **The engine record lives in the plugin's sidecar, `$DSH_HOME/.loop-engine/engines.json`, not in the session log.** Changing machine or `DSH_HOME` loses it, and the session falls back gracefully to the preset mapping. See [docs/per-session-engine.md](docs/per-session-engine.md) §5.5 and [docs/architecture.md](docs/architecture.md) §3.9.
|
|
56
|
+
- **A move involving `in-process` reloads the page**, because the harness loop neither hands over a live session nor adopts one it did not create. See [docs/per-session-engine.md](docs/per-session-engine.md) §5.2/§5.4.
|
|
57
|
+
- **The engine is settled at session creation / blank period**; once a turn has run, changing engines rebuilds that session's agent (idle only). See [docs/per-session-engine.md](docs/per-session-engine.md) §5.
|
|
58
|
+
- **Old sessions carrying the legacy single preset id (`loop-engine`)** show as "legacy hosted engine" and need one rebuild before the new semantics take over. See [docs/per-session-engine.md](docs/per-session-engine.md) §7.
|
|
59
|
+
- **Under a hosted engine a dsh model pick matters only if you pick a real dsh model** — `default` means "hand it back to the engine". See [docs/per-session-engine.md](docs/per-session-engine.md) §5.2.
|
|
60
|
+
- **Same-engine sessions share that CLI's own auth directory**, with no lock added by the plugin. See [docs/per-session-engine.md](docs/per-session-engine.md) §6.
|
|
61
|
+
- **Switching to `in-process` leaves the shared `external` provider group in the model menu** (the catalog is not scoped per session). See [docs/architecture.md](docs/architecture.md) §3.6.
|
|
62
|
+
|
|
63
|
+
## Where the details live
|
|
64
|
+
|
|
65
|
+
- [docs/per-session-engine.md](docs/per-session-engine.md) — the full user-visible behavior of per-session engines.
|
|
66
|
+
- [docs/source-checkout.md](docs/source-checkout.md) — the `file:` shims a source-launched harness needs.
|
|
67
|
+
- [docs/architecture.md](docs/architecture.md) — plugin core: the single factory slot, the managed block, routing, per-session engine facts, the provider route.
|
|
68
|
+
- [docs/driver-core.md](docs/driver-core.md) — the shared driver infrastructure.
|
|
69
|
+
- [docs/engine-claude.md](docs/engine-claude.md), [docs/engine-codex.md](docs/engine-codex.md), [docs/engine-kimi.md](docs/engine-kimi.md), [docs/engine-pi.md](docs/engine-pi.md) — per-engine internals.
|
|
70
|
+
- [docs/optimization-backlog.md](docs/optimization-backlog.md) — known issues and the optimization list.
|
|
71
|
+
- [docs/proposals/](docs/proposals/) — main-repo proposals: `append-ignorable-events.md`, `harness-agent-handover.md`, and the two model-selection ones (`dsh-model-into-hosted-engines.md`, `per-session-model-for-hosted-engines.md`).
|
|
215
72
|
|
|
216
73
|
## License
|
|
217
74
|
|
package/README.zh.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/dsh-loop-engine)
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
像选模型一样选 **dsh web** 的 agent 循环引擎——**按会话**选:内置的 `in-process`(默认),或四个托管引擎 `claude-code`、`codex`、`pi`、`kimi`。引擎本身是本插件的逐会话记录,所以**只要会话处于打开且空闲状态,任何时候都能换成别的引擎**——两个托管引擎之间是**原地换手**,而有一边是 `in-process` 时,宿主会释放这条会话的 agent 并让页面重新载入(`dsh web` 进程本身**不重启**)。会话之间互不影响:一个对话跑 Codex,另一个可以同时跑 Kimi。以上**无需改动主仓库**。
|
|
6
6
|
|
|
7
7
|
## 安装
|
|
8
8
|
|
|
@@ -10,123 +10,65 @@
|
|
|
10
10
|
dsh plugin --profile web add dsh-loop-engine
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
> 切换引擎会重写 `cordis.patch.yml` 中一小段受管理的内容,文件里你写的其它部分都会保留,只改动插件自己的区间。
|
|
16
|
-
|
|
17
|
-
> **pnpm 用户:** pnpm 10+ 默认拦截依赖的 build script,安装可能以
|
|
18
|
-
> `ERR_PNPM_IGNORED_BUILDS` 失败,并列出 `esbuild`、`@google/genai`、
|
|
19
|
-
> `protobufjs`(都经引擎 SDK 传递而来)。这是预期行为——放行后重试即可:可用
|
|
20
|
-
> `pnpm approve-builds` 交互放行,或在安装项目的 `pnpm-workspace.yaml` 里声明:
|
|
21
|
-
>
|
|
22
|
-
> ```yaml
|
|
23
|
-
> allowBuilds:
|
|
24
|
-
> esbuild: true
|
|
25
|
-
> '@google/genai': true
|
|
26
|
-
> protobufjs: true
|
|
27
|
-
> ```
|
|
28
|
-
>
|
|
29
|
-
> 只有安装方能授予该权限,插件无法预先放行自己的依赖。注意 `allowBuilds` 是
|
|
30
|
-
> pnpm 11 的写法——pnpm 11 会**删除** `package.json` 里遗留的
|
|
31
|
-
> `onlyBuiltDependencies`(以及 `neverBuiltDependencies`、`ignoredBuiltDependencies`)
|
|
32
|
-
> 且不再识别它们,写在那里会静默失效。
|
|
13
|
+
启动 `dsh web` 让 profile 重新组合出这个插件,然后打开 **Settings → Loop engine**。启动一次就够:路由器会在一个有界窗口内重试,等基础 bundle 的 `agent-loop` 行让出 factory 槽位。
|
|
33
14
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
上面的安装针对 **发布版** dsh(`npx @deepseek-ai/dsh`),不需要额外操作。若改用**源码**启动 harness(`cd deepseek-harness && pnpm dsh web`),则要多做一步——因为两边会把 harness 的包解析到不同文件:
|
|
37
|
-
|
|
38
|
-
| 一侧 | `@deepseek-ai/dsh-scope` 解析到 |
|
|
39
|
-
|---|---|
|
|
40
|
-
| 源码启动的 harness | `packages/core/scope/src/index.ts`(经 tsconfig `paths`) |
|
|
41
|
-
| 安装的插件(包内只有 `lib/`) | `packages/core/scope/lib/index.js` |
|
|
42
|
-
|
|
43
|
-
也就是同一个包被加载成了两个模块实例。`dsh-scope` 用模块私有的 `Symbol('dsh.scope')` 给 context 打标记,一个实例打的标记另一个实例读不到,于是恢复会话时报错:
|
|
44
|
-
|
|
45
|
-
```
|
|
46
|
-
agent-presets: refusing to compose an unscoped context;
|
|
47
|
-
the scope key is what joins an agent to its preset
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
把 profile 的 peer 桥接到 harness 源码,让两边共用同一个实例。把 `HARNESS` 设为 harness checkout 的 **`file://` URL**,在 profile 目录下执行:
|
|
51
|
-
|
|
52
|
-
```sh
|
|
53
|
-
HARNESS=file:///path/to/deepseek-harness # 例如 file:///D:/repos/deepseek-harness
|
|
54
|
-
cd "$DSH_HOME/profiles/web" && mkdir -p shims
|
|
55
|
-
while IFS='|' read -r name rel; do
|
|
56
|
-
mkdir -p "shims/$name"
|
|
57
|
-
printf '{"name":"@deepseek-ai/%s","version":"0.0.0","private":true,"type":"module","main":"index.mjs"}\n' \
|
|
58
|
-
"$name" > "shims/$name/package.json"
|
|
59
|
-
printf "export * from '%s/%s'\nimport * as mod from '%s/%s'\nexport default mod.default\n" \
|
|
60
|
-
"$HARNESS" "$rel" "$HARNESS" "$rel" > "shims/$name/index.mjs"
|
|
61
|
-
done <<EOF
|
|
62
|
-
cordis|vendor/cordis/src/index.ts
|
|
63
|
-
schemastery|vendor/schemastery/src/index.ts
|
|
64
|
-
dsh-agent|packages/core/agent/src/index.ts
|
|
65
|
-
dsh-scope|packages/core/scope/src/index.ts
|
|
66
|
-
dsh-session|packages/core/session/src/index.ts
|
|
67
|
-
dsh-session-persistence|packages/session/session-persistence/src/index.ts
|
|
68
|
-
dsh-settings|packages/settings/settings/src/index.ts
|
|
69
|
-
dsh-subprocess|packages/subprocess/subprocess/src/index.ts
|
|
70
|
-
dsh-timeout|packages/util/timeout/src/index.ts
|
|
71
|
-
dsh-llm|packages/llm/llm/src/index.ts
|
|
72
|
-
dsh-invariants|packages/runtime-diagnostics/invariants/src/index.ts
|
|
73
|
-
dsh-home-paths|packages/util/home-paths/src/index.ts
|
|
74
|
-
EOF
|
|
75
|
-
```
|
|
15
|
+
> 安装会重写 `cordis.patch.yml` 里一小段**不带引擎 id** 的受管理块:它禁用基础 bundle 的 `agent-loop` 行,好让插件自己的路由器占据进程内唯一的 agent factory 槽位;文件里你写的其它部分逐字节保留。从更早版本升级**不需要手工改文件**——点名了那一个钉住引擎的旧块会在下一次启动时被改写成这种形式,并把那个引擎作为设置页默认值的初始 seed。
|
|
76
16
|
|
|
77
|
-
|
|
17
|
+
> **pnpm 用户:** pnpm 10+ 默认拦截依赖的 build script,安装可能以 `ERR_PNPM_IGNORED_BUILDS` 失败,并列出 `esbuild`、`@google/genai`、`protobufjs`(都经引擎 SDK 传递而来)。放行后重试即可——`pnpm approve-builds`,或在 `pnpm-workspace.yaml` 里用 `allowBuilds`。只有安装方能授予该权限,插件无法预先放行自己的依赖。
|
|
78
18
|
|
|
79
|
-
|
|
80
|
-
node -e 'const f="package.json",j=require("./"+f),d=j.dependencies??={}
|
|
81
|
-
for(const n of ["cordis","schemastery","dsh-agent","dsh-scope","dsh-session","dsh-session-persistence","dsh-settings","dsh-subprocess","dsh-timeout","dsh-llm","dsh-invariants","dsh-home-paths"])
|
|
82
|
-
d["@deepseek-ai/"+n]="file:./shims/"+n
|
|
83
|
-
require("fs").writeFileSync(f,JSON.stringify(j,null,2)+"\n")'
|
|
84
|
-
pnpm install
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
重启 `dsh web`。若有代码加载 `@deepseek-ai/dsh-scope/invariant` 子路径,再给该 shim 补一个 `invariant.mjs`(`export * from '$HARNESS/packages/core/scope/src/invariant.ts'`),并在它的 `exports` 里加上 `"./invariant": "./invariant.mjs"`。
|
|
88
|
-
|
|
89
|
-
> 用本地 **`link:`** 方式安装插件可以完全绕开这一步:checkout 与 harness 仓库相邻时,它会继承 harness 自己的 `tsconfig.json`,从而共用同一份 `paths` 映射。这个分裂只在**打包版**插件(npm 或 tarball)遇到**源码版** harness 时出现。
|
|
90
|
-
|
|
91
|
-
### 环境要求
|
|
19
|
+
### 源码启动 harness 时的额外步骤
|
|
92
20
|
|
|
93
|
-
-
|
|
94
|
-
- 使用 Codex 引擎时需要完成认证:本机执行过 `codex login`,或配置 `CODEX_API_KEY` 环境变量。
|
|
95
|
-
- 使用 Pi 引擎时需要以 `pi` 要求的方式完成认证(其自身的 `~/.pi/agent/auth.json`,或提供方的 API-key 环境变量,如 `ANTHROPIC_API_KEY`)。
|
|
96
|
-
- 使用 Kimi Code 引擎时需要本机已安装并登录 `kimi` CLI(例如 `kimi login`),且在 `PATH` 上(或在组合条目里用 `kimiBin` 固定为绝对路径)。
|
|
21
|
+
**发布版** dsh 不需要额外操作。若改用**源码**启动 harness(`cd deepseek-harness && pnpm dsh web`),则要多做一步:用 `file:` shim 把 profile 的 harness peer 包桥接到 checkout 源码,让两边共用同一个模块实例(一个实例打的 scope 标记另一个实例读不到,恢复会话时会报 `agent-presets: refusing to compose an unscoped context`)。清单里包含 `@deepseek-ai/dsh-agent-loop`。可跑的 shim 步骤在 [docs/source-checkout.md](docs/source-checkout.md);每个 `@deepseek-ai/*` 包必须保持单实例的原因见 [docs/architecture.md](docs/architecture.md)。用本地 **`link:`** 方式安装插件可以完全绕开这一步。
|
|
97
22
|
|
|
98
23
|
## 版本兼容
|
|
99
24
|
|
|
100
|
-
`dsh-loop-engine` 与它针对的 harness
|
|
25
|
+
`dsh-loop-engine` 与它针对的 harness **同版本对齐**:`<harness version>-rcN`。`0.1.5-rc3` 针对 harness `0.1.5-rc.2`;`0.1.5-rc1`/`0.1.5-rc2` 针对 `0.1.5-rc.1`;`1.0.0-rc8` … `1.0.0-rc15` 针对 `0.1.2-rc.1`;`1.0.0-rc7` 及更早针对 `0.1.1-rc.2`。它消费的每个 harness 包都在 `peerDependencies` 里精确钉住,两者必须匹配——不匹配会在启动或会话恢复时响亮地失败。要在更老的 harness 上使用本插件,请安装与之匹配的版本(每个 GitHub Release 正文会写明它针对的 harness 版本)。
|
|
101
26
|
|
|
102
|
-
|
|
103
|
-
|---|---|
|
|
104
|
-
| 0.1.5-rc3 | **0.1.5-rc.2** |
|
|
105
|
-
| 0.1.5-rc1、0.1.5-rc2 | 0.1.5-rc.1 |
|
|
106
|
-
| 1.0.0-rc8 … 1.0.0-rc15 | 0.1.2-rc.1 |
|
|
107
|
-
| 1.0.0-rc7 及更早 | 0.1.1-rc.2 |
|
|
27
|
+
### 环境要求
|
|
108
28
|
|
|
109
|
-
-
|
|
110
|
-
-
|
|
111
|
-
-
|
|
112
|
-
-
|
|
29
|
+
- **Claude Code**:本机已安装并登录 Claude Code CLI。
|
|
30
|
+
- **Codex**:本机执行过 `codex login`,或配置 `CODEX_API_KEY` 环境变量。
|
|
31
|
+
- **Pi**:以 `pi` 要求的方式完成认证(其自身的 `~/.pi/agent/auth.json`,或提供方的 API-key 环境变量,如 `ANTHROPIC_API_KEY`)。
|
|
32
|
+
- **Kimi Code**:本机已安装并登录 `kimi` CLI(例如 `kimi login`),且在 `PATH` 上(或在组合条目里用 `kimiBin` 固定为绝对路径)。
|
|
33
|
+
- 用源码 checkout 跑 harness 时还要补上那份 `dsh-agent-loop` 的 `file:` shim。
|
|
113
34
|
|
|
114
35
|
## 使用方法
|
|
115
36
|
|
|
116
|
-
|
|
117
|
-
2. 要切回默认,选 **In-process** 再重启即可。
|
|
118
|
-
3. 卸载插件:`dsh plugin --profile web remove dsh-loop-engine`,然后重启 `dsh web`。
|
|
37
|
+
**引擎是按会话选的。** 在**新会话页**的 preset 选择器(工作区选择器旁的 preset chip)上选 `Claude Code`、`Codex`、`Pi` 或 `Kimi Code`;保留部署自己的默认预设(通常是 `standard`)则走内置 in-process 循环。不同会话可以同时跑不同引擎,子 agent 沿用拉起它的那个 agent 的引擎。
|
|
119
38
|
|
|
120
|
-
|
|
39
|
+
- **Settings → Loop engine** 只决定**新会话**的默认引擎——改完立即生效,**不需要重启,也不需要刷新页面**,已经在跑的会话不受影响。选 **In-process** 会把插件接手前的默认值恢复回去。
|
|
40
|
+
- **改动你当前这条会话**用对话页 composer 的引擎选择器(勾选 *在对话页显示引擎选择器*),它调用本插件自己的 `remote.loopEngine.select`。只要会话处于打开且空闲状态就能换——包括已经跑过很多轮的会话。
|
|
41
|
+
- **两个托管引擎之间是原地换手**:会话保持打开,只替换 agent——选中即生效,不弹任何确认。
|
|
42
|
+
- **只要有一边是 `in-process` 就无法原地接管**(harness 的 loop 既不接受不是它创建的会话,也不把活会话交出去),所以 composer 会**先弹确认框**说清这次切换要重新载入页面、这一页的滚动位置与还没发出的草稿会跟着丢(会话记录不受影响)。确认后宿主**释放这条会话的 agent** 并回包 `reload: true`;页面自动重载并回到同一条会话,宿主随即按记录构建它。**`dsh web` 进程不重启。**
|
|
43
|
+
- **正在跑一轮时会话会被拒绝**而不是被打断;由 subagent 派生的**子会话**完全不能换。
|
|
44
|
+
- **模型**:模型菜单里所有托管引擎共用一个 `external` 分组,名下只有一条 `default`——含义是「由引擎决定」。选一条**真实 dsh 模型**会把它连同**端点与凭据**一起交给该引擎(能不能用取决于该引擎——不支持会**报错**,而不是静默退回)。`in-process` 会话照常用 dsh 的模型。
|
|
45
|
+
- **卸载**:`dsh plugin --profile web remove dsh-loop-engine`,然后手工删掉 profile 的 `cordis.patch.yml` 里插件的受管理块——这块比插件本身活得更久,只要它还在,基础 `agent-loop` 行就一直被禁用,profile 会完全没有 agent factory。
|
|
121
46
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
### 引擎说明
|
|
47
|
+
### 托管引擎接管什么
|
|
125
48
|
|
|
126
|
-
-
|
|
127
|
-
-
|
|
128
|
-
-
|
|
129
|
-
|
|
49
|
+
- 它的预设是 `standard` 的副本,去掉了外部引擎会替代掉的 dsh 原生行——dsh 的 `/plan`、`/compact`(与自动压缩)、模型可见的 goal 工具、人类 `/goal` 命令、以及 dsh skill 行(每引擎一份剥离后的 preset,位于 `$DSH_HOME/.agent-presets/loop-engine-<engine>/`)。
|
|
50
|
+
- 引擎自己的斜杠命令与技能目录会注册进**这个 agent 自己的 scope**,因此两个跑不同引擎的会话互相看不到对方的菜单,整份表面随 agent 一起回收。
|
|
51
|
+
- 与引擎无关的 dsh 命令(`/export`、`/feedback`、`/permission`)照常可用、保留在菜单里。
|
|
52
|
+
|
|
53
|
+
## 已知限制
|
|
54
|
+
|
|
55
|
+
- **引擎记录落在插件自己的侧车文件 `$DSH_HOME/.loop-engine/engines.json`,不在会话日志里。** 换机器或换 `DSH_HOME` 会丢掉它,会话会优雅回退到 preset 映射。详见 [docs/per-session-engine.md](docs/per-session-engine.md) §5.5 与 [docs/architecture.md](docs/architecture.md) §3.9。
|
|
56
|
+
- **涉及 `in-process` 的切换要重新载入页面**,因为 harness 的 loop 既交不出活会话、也不接管别人的。详见 [docs/per-session-engine.md](docs/per-session-engine.md) §5.2/§5.4。
|
|
57
|
+
- **引擎在会话创建/空白期确定**;跑过一轮之后再换引擎,会重建这条会话的 agent(只在空闲时才行)。详见 [docs/per-session-engine.md](docs/per-session-engine.md) §5。
|
|
58
|
+
- **日志里记着旧版单 preset id(`loop-engine`)的老会话**显示为「旧版托管引擎」,需要一次重建才由新语义接管。详见 [docs/per-session-engine.md](docs/per-session-engine.md) §7。
|
|
59
|
+
- **托管引擎下 dsh 的模型选择只有在你真的选了真实 dsh 模型时才有意义**——`default` 意思是「交回引擎自己决定」。详见 [docs/per-session-engine.md](docs/per-session-engine.md) §5.2。
|
|
60
|
+
- **同一引擎上的多条会话共享该 CLI 自己的认证目录**,插件没有加任何锁。详见 [docs/per-session-engine.md](docs/per-session-engine.md) §6。
|
|
61
|
+
- **换成 `in-process` 时,那个共享的 `external` provider 分组仍会留在模型菜单里**(目录不按会话过滤)。详见 [docs/architecture.md](docs/architecture.md) §3.6。
|
|
62
|
+
|
|
63
|
+
## 细节在哪
|
|
64
|
+
|
|
65
|
+
- [docs/per-session-engine.md](docs/per-session-engine.md)——按会话选引擎的用户可见行为全解。
|
|
66
|
+
- [docs/source-checkout.md](docs/source-checkout.md)——源码启动 harness 时需要的那套 `file:` shim。
|
|
67
|
+
- [docs/architecture.md](docs/architecture.md)——插件核心:唯一 factory 槽位、受管理块、路由、逐会话引擎事实、provider 路由。
|
|
68
|
+
- [docs/driver-core.md](docs/driver-core.md)——共享驱动基础设施。
|
|
69
|
+
- [docs/engine-claude.md](docs/engine-claude.md)、[docs/engine-codex.md](docs/engine-codex.md)、[docs/engine-kimi.md](docs/engine-kimi.md)、[docs/engine-pi.md](docs/engine-pi.md)——逐引擎内部实现。
|
|
70
|
+
- [docs/optimization-backlog.md](docs/optimization-backlog.md)——已知问题与优化清单。
|
|
71
|
+
- [docs/proposals/](docs/proposals/)——主仓提案:`append-ignorable-events.md`、`harness-agent-handover.md`,以及模型选择相关的两篇(`dsh-model-into-hosted-engines.md`、`per-session-model-for-hosted-engines.md`)。
|
|
130
72
|
|
|
131
73
|
## License
|
|
132
74
|
|