dsh-loop-engine 0.1.5-rc1 → 0.1.5-rc3
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 +112 -19
- package/README.zh.md +85 -5
- package/lib/client.js +158 -0
- package/lib/index.js +1020 -1486
- package/lib/invariant.js +6 -3
- package/lib/types/client/turn-status.d.ts +43 -0
- package/lib/types/driver-core/agents-md-skill-provider.d.ts +72 -0
- package/lib/types/driver-core/hosted-loop-factory.d.ts +119 -0
- package/lib/types/driver-core/ownership.d.ts +6 -6
- package/lib/types/driver-core/permission-knobs.d.ts +1 -1
- package/lib/types/driver-core/prompt.d.ts +1 -1
- package/lib/types/driver-core/skill-inject.d.ts +2 -2
- package/lib/types/engine-claude/agent.d.ts +22 -0
- package/lib/types/engine-claude/loop.d.ts +7 -56
- package/lib/types/engine-claude/mapping.d.ts +28 -3
- package/lib/types/engine-codex/agent.d.ts +46 -3
- package/lib/types/engine-codex/appserver/client.d.ts +16 -0
- package/lib/types/engine-codex/loop.d.ts +7 -56
- package/lib/types/engine-codex/permission.d.ts +100 -6
- package/lib/types/engine-codex/skills.d.ts +7 -8
- package/lib/types/engine-kimi/acp/client.d.ts +33 -3
- package/lib/types/engine-kimi/acp/mapping.d.ts +36 -7
- package/lib/types/engine-kimi/acp/types.d.ts +41 -2
- package/lib/types/engine-kimi/agent.d.ts +65 -8
- package/lib/types/engine-kimi/loop.d.ts +7 -56
- package/lib/types/engine-kimi/skills.d.ts +9 -14
- package/lib/types/engine-pi/agent.d.ts +23 -4
- package/lib/types/engine-pi/loop.d.ts +7 -56
- package/lib/types/engine-pi/permission.d.ts +16 -12
- package/lib/types/engine-pi/skills.d.ts +6 -14
- package/lib/types/patch-manager.d.ts +7 -0
- package/lib/types/provider-route.d.ts +16 -7
- package/lib/types/settings.d.ts +4 -1
- package/lib/types/skills.d.ts +6 -14
- package/package.json +22 -22
package/README.md
CHANGED
|
@@ -20,30 +20,120 @@ Restart `dsh web`, then open **Settings → Loop engine**.
|
|
|
20
20
|
> span changes.
|
|
21
21
|
|
|
22
22
|
> **pnpm users:** pnpm 10+ blocks dependency build scripts by default, so the
|
|
23
|
-
> install may
|
|
24
|
-
>
|
|
25
|
-
>
|
|
26
|
-
>
|
|
27
|
-
>
|
|
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.
|
|
41
|
+
|
|
42
|
+
### Running against a harness source checkout
|
|
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:
|
|
66
|
+
|
|
67
|
+
```sh
|
|
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
|
+
```
|
|
101
|
+
|
|
102
|
+
Restart `dsh web`. If something loads the `@deepseek-ai/dsh-scope/invariant`
|
|
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`.
|
|
106
|
+
|
|
107
|
+
> Installing the plugin as a local **`link:`** checkout sidesteps this entirely:
|
|
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.
|
|
28
111
|
|
|
29
112
|
## Version compatibility
|
|
30
113
|
|
|
31
114
|
dsh-loop-engine is versioned **in lockstep with the harness it targets**: the
|
|
32
|
-
version is the harness version plus a plugin release counter (`0.1.5-rc1`
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
loudly at boot
|
|
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:
|
|
36
120
|
|
|
37
121
|
| dsh-loop-engine | Requires harness |
|
|
38
122
|
|---|---|
|
|
39
|
-
| 0.1.5-
|
|
123
|
+
| 0.1.5-rc3 | **0.1.5-rc.2** |
|
|
124
|
+
| 0.1.5-rc1, 0.1.5-rc2 | 0.1.5-rc.1 |
|
|
40
125
|
| 1.0.0-rc8 … 1.0.0-rc15 | 0.1.2-rc.1 |
|
|
41
126
|
| 1.0.0-rc7 and earlier | 0.1.1-rc.2 |
|
|
42
127
|
|
|
43
|
-
- **0.1.5-
|
|
44
|
-
|
|
45
|
-
`
|
|
46
|
-
|
|
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`.
|
|
47
137
|
- Releases up to `1.0.0-rc15` used the plugin's own version series and target
|
|
48
138
|
harness `0.1.2-rc.1`; they are not compatible with harness `0.1.5-rc.1`.
|
|
49
139
|
- To use the plugin with an older harness, install the release matching it
|
|
@@ -89,11 +179,14 @@ they were created with.
|
|
|
89
179
|
bridged into the web menu (built-ins plus user-level `~/.claude/commands/`)
|
|
90
180
|
and forwarded to the engine, which expands them natively. Project-level
|
|
91
181
|
`.claude/commands/` files stay engine-side and also work typed directly.
|
|
92
|
-
- The Codex driver runs `codex app-server
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
dsh
|
|
96
|
-
|
|
182
|
+
- The Codex driver runs `codex app-server`; the thread starts with the
|
|
183
|
+
session's `sandboxMode` + `approvalPolicy` stance, and the model's runtime
|
|
184
|
+
approval requests (command, file-change, permissions) are answered through
|
|
185
|
+
the dsh approval seam — `request_user_input` questions go to the
|
|
186
|
+
user-questions seam and MCP elicitations are declined, all fail-closed when
|
|
187
|
+
their seam is absent. Its `AGENTS.md` instruction files are surfaced through
|
|
188
|
+
the dsh skill-injection seam across every directory from the session cwd up
|
|
189
|
+
to the git root, plus `~/.codex/AGENTS.md`.
|
|
97
190
|
- The Pi driver runs `pi --mode rpc`; Pi has no permission system, so the whole
|
|
98
191
|
child is sandboxed through the dsh subprocess service (default `read-only`).
|
|
99
192
|
Its context files (`AGENTS.md`/`CLAUDE.md` with `AGENTS.override.md`
|
package/README.zh.md
CHANGED
|
@@ -14,6 +14,80 @@ dsh plugin --profile web add dsh-loop-engine
|
|
|
14
14
|
|
|
15
15
|
> 切换引擎会重写 `cordis.patch.yml` 中一小段受管理的内容,文件里你写的其它部分都会保留,只改动插件自己的区间。
|
|
16
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
|
+
> 且不再识别它们,写在那里会静默失效。
|
|
33
|
+
|
|
34
|
+
### 源码启动 harness 时的额外步骤
|
|
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
|
+
```
|
|
76
|
+
|
|
77
|
+
再把这些写进 profile 的 `package.json` 并重新安装:
|
|
78
|
+
|
|
79
|
+
```sh
|
|
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
|
+
|
|
17
91
|
### 环境要求
|
|
18
92
|
|
|
19
93
|
- 使用 Claude Code 引擎时需要本机已安装并登录 Claude Code CLI。
|
|
@@ -23,15 +97,16 @@ dsh plugin --profile web add dsh-loop-engine
|
|
|
23
97
|
|
|
24
98
|
## 版本兼容
|
|
25
99
|
|
|
26
|
-
`dsh-loop-engine` 与它针对的 harness **同版本对齐**:版本号即 harness 版本加插件发布序号(`0.1.5-rc1` 针对 harness `0.1.5-rc.1`),它消费的每个 harness 包都在 `peerDependencies` 里精确钉住。两者必须匹配——不匹配会在启动或会话恢复时响亮地失败:
|
|
100
|
+
`dsh-loop-engine` 与它针对的 harness **同版本对齐**:版本号即 harness 版本加插件发布序号(`0.1.5-rc1`、`0.1.5-rc2` 针对 harness `0.1.5-rc.1`,`0.1.5-rc3` 针对 harness `0.1.5-rc.2`),它消费的每个 harness 包都在 `peerDependencies` 里精确钉住。两者必须匹配——不匹配会在启动或会话恢复时响亮地失败:
|
|
27
101
|
|
|
28
102
|
| dsh-loop-engine | 需要 harness |
|
|
29
103
|
|---|---|
|
|
30
|
-
| 0.1.5-
|
|
104
|
+
| 0.1.5-rc3 | **0.1.5-rc.2** |
|
|
105
|
+
| 0.1.5-rc1、0.1.5-rc2 | 0.1.5-rc.1 |
|
|
31
106
|
| 1.0.0-rc8 … 1.0.0-rc15 | 0.1.2-rc.1 |
|
|
32
107
|
| 1.0.0-rc7 及更早 | 0.1.1-rc.2 |
|
|
33
108
|
|
|
34
|
-
-
|
|
109
|
+
- **每个 0.1.5-rcN 版本需要它为之构建的那个 0.1.5 补丁。** `0.1.5-rc1`/`0.1.5-rc2` 需要 harness `0.1.5-rc.1`;`0.1.5-rc3` 需要 harness `0.1.5-rc.2`。三者都使用 0.1.5 的 assistant-stream 契约(`assistant/message` 内嵌精确计时的 `stream`,并禁止 `sourceEventSeqs`)、由 driver 自己实现的 `Inbox` 接口、双参数 `AgentSetup`,以及 `SessionPersistence.create` / `open` 句柄 seam。`0.1.5-rc.2` 是一次客户端 UI/文档 backport,没有触碰这些 seam,因此 driver 代码在 `0.1.5-rc.1` 与 `0.1.5-rc.2` 之间完全一致。
|
|
35
110
|
- `1.0.0-rc15` 及以前的版本沿用插件自己的版本序列,针对 harness `0.1.2-rc.1`,与 harness `0.1.5-rc.1` 不兼容。
|
|
36
111
|
- 要在更老的 harness 上使用本插件,请安装与之匹配的版本(例如 harness 0.1.2-rc.1 用 `npm i dsh-loop-engine@1.0.0-rc15`)。
|
|
37
112
|
- 每个 tag 的 GitHub Release 正文会写明它针对的 harness 版本。
|
|
@@ -42,10 +117,15 @@ dsh plugin --profile web add dsh-loop-engine
|
|
|
42
117
|
2. 要切回默认,选 **In-process** 再重启即可。
|
|
43
118
|
3. 卸载插件:`dsh plugin --profile web remove dsh-loop-engine`,然后重启 `dsh web`。
|
|
44
119
|
|
|
120
|
+
### 托管引擎接管什么
|
|
121
|
+
|
|
122
|
+
选中托管引擎后,它接管该会话的命令与技能面:插件禁用 dsh 自己的 `/goal`,并把新会话指向一个受管理的 `loop-engine` agent 预设——它是 `standard` 的副本,去掉了外部引擎无法履行的 dsh 原生 `/compact`、`/plan`、goal 工具与 skill 行——于是斜杠菜单只显示引擎桥接过来的命令与它自己的技能目录。与引擎无关的 dsh 命令(`/export`、`/feedback`、`/permission`)照常可用、保留在菜单里。切回 `in-process` 会恢复之前的预设默认值;已经在跑的会话始终保留它创建时的预设。
|
|
123
|
+
|
|
45
124
|
### 引擎说明
|
|
46
125
|
|
|
47
|
-
-
|
|
48
|
-
-
|
|
126
|
+
- Claude Code 驱动每步跑一次 SDK query;它的斜杠命令桥接进 web 菜单(内置命令加上用户级 `~/.claude/commands/`),再转发给引擎由它原生展开。项目级 `.claude/commands/` 留在引擎侧,直接手敲同样可用。
|
|
127
|
+
- Codex 驱动运行 `codex app-server`;线程以会话的 `sandboxMode` + `approvalPolicy` 姿态启动,模型运行时的工具审批请求(command、file-change、permissions)经 dsh 审批 seam 应答——用户提问走 user-questions seam,MCP elicitation 一律拒绝,seam 缺席时均失败关闭。其 `AGENTS.md` 指令文件经 dsh 技能注入接缝暴露:从会话 cwd 逐级到 git 根,外加 `~/.codex/AGENTS.md`。
|
|
128
|
+
- Pi 驱动运行 `pi --mode rpc`;Pi 没有权限系统,所以整个子进程经 dsh subprocess 服务做沙箱化(默认 `read-only`)。它的上下文文件(`AGENTS.md`/`CLAUDE.md`,优先 `AGENTS.override.md`,外加 pi 配置目录下的用户级文件)与 `skills/` 目录(`~/.pi/agent/skills/` 和 `.pi/skills/`)经 dsh 技能注入接缝暴露。
|
|
49
129
|
- Kimi Code 驱动运行一个常驻的 `kimi acp` 子进程(Agent Client Protocol over stdio),每步一次无状态的 `session/new` + `session/prompt`;durable 会话日志是唯一模型上下文。它把助手文本(`agent_message_chunk`)与**思考**(`agent_thought_chunk`)**增量**写入日志,并把工具调用/流(`tool_call` / `tool_call_update`)映射为 `tool/call` + `tool/result`。ACP 通过 `session/request_permission` 暴露工具审批,驱动根据会话的 dsh 审批旋钮应答(ask 策略拒绝,失败关闭)。子进程经 dsh subprocess seam 拉起——唯一权限边界(默认只读沙箱)。其项目 `AGENTS.md` 链(cwd→git 根)与 `.kimi-code/skills/` 目录(用户与项目)通过 dsh 技能注入接口暴露,其斜杠命令也已桥接(内置命令把原始 `/name` 行转发回引擎展开)。prompt 是 ACP 请求体而非 argv 位置参数,因此**不存在命令行长度上限**。注意 Kimi 剩余的斜杠命令面是纯 TUI(`/login`、`/provider`、`/settings`、`/sessions`…),这些不桥接(ACP prompt 面不扩展它们);`skill:` 命令由技能接口与 kimi 自身的 shorthand 承载。
|
|
50
130
|
|
|
51
131
|
## License
|
package/lib/client.js
CHANGED
|
@@ -565,6 +565,163 @@ var LoopEngineStore = class {
|
|
|
565
565
|
}
|
|
566
566
|
};
|
|
567
567
|
|
|
568
|
+
// src/client/turn-status.ts
|
|
569
|
+
var ENGINE_ATTR = "data-loop-engine";
|
|
570
|
+
var PLUGIN_ID = "dsh-loop-engine";
|
|
571
|
+
var STYLESHEET = `
|
|
572
|
+
[class$="_turnStatus"]::before {
|
|
573
|
+
margin-right: 6px;
|
|
574
|
+
background: none;
|
|
575
|
+
-webkit-background-clip: border-box;
|
|
576
|
+
background-clip: border-box;
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
/*
|
|
580
|
+
* The row's own sweep, re-asserted for hosted engines.
|
|
581
|
+
*
|
|
582
|
+
* ui-chat disables that animation under \`prefers-reduced-motion: reduce\`
|
|
583
|
+
* (ChatView.module.css), and a media query carries no specificity \u2014 so this
|
|
584
|
+
* attribute-gated rule outranks it. That is deliberate here: deployment images
|
|
585
|
+
* ship with Windows' client-area animation off (SPI_GETCLIENTAREAANIMATION
|
|
586
|
+
* false), and with the guard in force EVERY indicator on this row is frozen \u2014
|
|
587
|
+
* the sweep and the glyph alike. Scoped to hosted engines, so in-process
|
|
588
|
+
* sessions keep the stock reduced-motion behaviour; delete this rule and
|
|
589
|
+
* restore the guard at the foot of the sheet to hand the decision back to the OS.
|
|
590
|
+
*/
|
|
591
|
+
html[${ENGINE_ATTR}] [class$="_turnStatus"] {
|
|
592
|
+
background-position: 100% 0;
|
|
593
|
+
background-size: 250% 100%;
|
|
594
|
+
animation: le-shimmer 1.8s linear infinite;
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
@keyframes le-shimmer {
|
|
598
|
+
to { background-position: 0 0; }
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
html[${ENGINE_ATTR}="claude-code"] [class$="_turnStatus"] {
|
|
602
|
+
--dsw-static-deepseek-500: #d97757;
|
|
603
|
+
--dsw-static-deepseek-200: #f5bda6;
|
|
604
|
+
}
|
|
605
|
+
html[${ENGINE_ATTR}="claude-code"] [class$="_turnStatus"]::before {
|
|
606
|
+
content: "\u273B";
|
|
607
|
+
color: #d97757;
|
|
608
|
+
-webkit-text-fill-color: #d97757;
|
|
609
|
+
animation: le-bloom 1.6s ease-in-out infinite;
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
html[${ENGINE_ATTR}="codex"] [class$="_turnStatus"] {
|
|
613
|
+
--dsw-static-deepseek-500: #a9b1c0;
|
|
614
|
+
--dsw-static-deepseek-200: #e6eaf2;
|
|
615
|
+
}
|
|
616
|
+
html[${ENGINE_ATTR}="codex"] [class$="_turnStatus"]::before {
|
|
617
|
+
content: "\u2022";
|
|
618
|
+
color: #a9b1c0;
|
|
619
|
+
-webkit-text-fill-color: #a9b1c0;
|
|
620
|
+
text-shadow: 0 0 6px currentColor;
|
|
621
|
+
animation: le-pulse 1.4s ease-in-out infinite;
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
html[${ENGINE_ATTR}="pi"] [class$="_turnStatus"] {
|
|
625
|
+
--dsw-static-deepseek-500: #8e4ec6;
|
|
626
|
+
--dsw-static-deepseek-200: #d6bff0;
|
|
627
|
+
}
|
|
628
|
+
html[${ENGINE_ATTR}="pi"] [class$="_turnStatus"]::before {
|
|
629
|
+
content: "\u280B";
|
|
630
|
+
color: #8e4ec6;
|
|
631
|
+
-webkit-text-fill-color: #8e4ec6;
|
|
632
|
+
font-size: 1.1em;
|
|
633
|
+
animation: le-braille 1s linear infinite;
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
html[${ENGINE_ATTR}="kimi"] [class$="_turnStatus"] {
|
|
637
|
+
--dsw-static-deepseek-500: #e5484d;
|
|
638
|
+
--dsw-static-deepseek-200: #f5b2b4;
|
|
639
|
+
}
|
|
640
|
+
html[${ENGINE_ATTR}="kimi"] [class$="_turnStatus"]::before {
|
|
641
|
+
content: "\u{1F317}";
|
|
642
|
+
color: #e5484d;
|
|
643
|
+
-webkit-text-fill-color: #e5484d;
|
|
644
|
+
animation: le-moon 2.5s linear infinite;
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
/* Grows from small to large each cycle \u2014 the opposite of a twinkle. The range
|
|
648
|
+
is deliberately wide (3x) because a bare size change on a thin glyph reads
|
|
649
|
+
weakly otherwise, and the large state is held briefly (50%-62%) so it blooms
|
|
650
|
+
rather than throbs. 1.35x extends ~2.5px per side at the 14px glyph, inside
|
|
651
|
+
the 6px ::before margin. */
|
|
652
|
+
@keyframes le-bloom {
|
|
653
|
+
0%, 100% { transform: scale(0.45); opacity: 0.45; }
|
|
654
|
+
50%, 62% { transform: scale(1.35); opacity: 1; }
|
|
655
|
+
}
|
|
656
|
+
/* A terminal braille spinner. The glyph is an ordinary text character, not an
|
|
657
|
+
emoji, so the engine color actually paints \u2014 a colored emoji silently ignores
|
|
658
|
+
color/-webkit-text-fill-color. Stepping content walks the ten dot frames. */
|
|
659
|
+
@keyframes le-braille {
|
|
660
|
+
0% { content: "\u280B"; }
|
|
661
|
+
10% { content: "\u2819"; }
|
|
662
|
+
20% { content: "\u2839"; }
|
|
663
|
+
30% { content: "\u2838"; }
|
|
664
|
+
40% { content: "\u283C"; }
|
|
665
|
+
50% { content: "\u2834"; }
|
|
666
|
+
60% { content: "\u2826"; }
|
|
667
|
+
70% { content: "\u2827"; }
|
|
668
|
+
80% { content: "\u2807"; }
|
|
669
|
+
90% { content: "\u280F"; }
|
|
670
|
+
100% { content: "\u280B"; }
|
|
671
|
+
}
|
|
672
|
+
/* A soft pulse for the codex dot: the glyph breathes between a small, dim
|
|
673
|
+
point and a larger, full-brightness one \u2014 a light dot, not a spinner. */
|
|
674
|
+
@keyframes le-pulse {
|
|
675
|
+
0%, 100% { transform: scale(0.5); opacity: 0.35; }
|
|
676
|
+
50% { transform: scale(1.3); opacity: 1; }
|
|
677
|
+
}
|
|
678
|
+
/* Moon phases rather than a rigid rotation: a spinning moon bitmap can only
|
|
679
|
+
squash and mirror itself, never show a full or a new moon. Stepping the
|
|
680
|
+
glyph through the phase set sweeps the lit edge across the disc AND actually
|
|
681
|
+
reaches \u{1F315} and \u{1F311}. The order runs waning (full \u2192 new), so the lit edge
|
|
682
|
+
retreats right-to-left \u2014 the direction the user picked. */
|
|
683
|
+
@keyframes le-moon {
|
|
684
|
+
0% { content: "\u{1F315}"; }
|
|
685
|
+
12.5% { content: "\u{1F314}"; }
|
|
686
|
+
25% { content: "\u{1F313}"; }
|
|
687
|
+
37.5% { content: "\u{1F312}"; }
|
|
688
|
+
50% { content: "\u{1F311}"; }
|
|
689
|
+
62.5% { content: "\u{1F318}"; }
|
|
690
|
+
75% { content: "\u{1F317}"; }
|
|
691
|
+
87.5% { content: "\u{1F316}"; }
|
|
692
|
+
100% { content: "\u{1F315}"; }
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
`;
|
|
696
|
+
function reflect(engine, settled) {
|
|
697
|
+
const root = document.documentElement;
|
|
698
|
+
if (!settled || engine === "in-process") {
|
|
699
|
+
delete root.dataset.loopEngine;
|
|
700
|
+
return;
|
|
701
|
+
}
|
|
702
|
+
root.dataset.loopEngine = engine;
|
|
703
|
+
}
|
|
704
|
+
function installTurnStatusStyles(ctx, store) {
|
|
705
|
+
if (typeof document === "undefined") return;
|
|
706
|
+
ctx.effect(() => {
|
|
707
|
+
const tag = document.createElement("style");
|
|
708
|
+
tag.dataset.plugin = PLUGIN_ID;
|
|
709
|
+
tag.dataset.pluginCss = `${PLUGIN_ID}/turn-status.css`;
|
|
710
|
+
tag.textContent = STYLESHEET;
|
|
711
|
+
document.head.appendChild(tag);
|
|
712
|
+
return () => {
|
|
713
|
+
tag.remove();
|
|
714
|
+
delete document.documentElement.dataset.loopEngine;
|
|
715
|
+
};
|
|
716
|
+
}, "loop-engine: per-engine turn status styles");
|
|
717
|
+
const sync = () => {
|
|
718
|
+
const { status, engine } = store.getSnapshot();
|
|
719
|
+
reflect(engine, status === "ready");
|
|
720
|
+
};
|
|
721
|
+
ctx.effect(() => store.subscribe(sync), "loop-engine: turn status engine reflection");
|
|
722
|
+
sync();
|
|
723
|
+
}
|
|
724
|
+
|
|
568
725
|
// src/client/locales.ts
|
|
569
726
|
var zh = {
|
|
570
727
|
nav: "\u5FAA\u73AF\u5F15\u64CE",
|
|
@@ -624,6 +781,7 @@ function apply(ctx) {
|
|
|
624
781
|
controller.dispose();
|
|
625
782
|
};
|
|
626
783
|
}, "loop-engine: store lifecycle");
|
|
784
|
+
installTurnStatusStyles(ctx, controller.store);
|
|
627
785
|
const t = ctx.locale.bind(NS);
|
|
628
786
|
const injected = () => ({
|
|
629
787
|
controller,
|