@aiwayds/dsh-dcp 0.8.0 → 0.9.1
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.en.md +9 -0
- package/README.md +4 -0
- package/lib/index.js +13 -2
- package/lib/skill.js +98 -0
- package/package.json +6 -1
- package/skills/dsh-dcp-config/SKILL.md +91 -0
package/README.en.md
CHANGED
|
@@ -176,6 +176,15 @@ All optional, defaults work out of the box:
|
|
|
176
176
|
|
|
177
177
|
> **Upgrade note (0.5.0)**: the `roundInterval` counter switched from completed turns to assistant messages — the same value now triggers more often (a single turn usually contains several assistant messages).
|
|
178
178
|
|
|
179
|
+
## Bundled skill
|
|
180
|
+
|
|
181
|
+
The package registers a `dsh-dcp-config` skill (`skills/dsh-dcp-config/SKILL.md`, served via
|
|
182
|
+
`ctx.skills.registerProvider`): ask the agent to tune compaction, configure dcp,
|
|
183
|
+
or debug compaction behavior and the guide loads automatically — it carries an
|
|
184
|
+
interactive `ask_user_question` tuning wizard (collect the expectation first,
|
|
185
|
+
then map it to keys) and the persistent recipe for the `config:` section of the
|
|
186
|
+
cordis.patch.yml mount block.
|
|
187
|
+
|
|
179
188
|
## Design reference
|
|
180
189
|
|
|
181
190
|
- [Opencode-DCP/opencode-dynamic-context-pruning](https://github.com/Opencode-DCP/opencode-dynamic-context-pruning)
|
package/README.md
CHANGED
|
@@ -148,6 +148,10 @@ npx dsh-dcp-setup --remove /path/to/cordis.patch.yml
|
|
|
148
148
|
|
|
149
149
|
> **升级提示(0.5.0)**:`roundInterval` 的计数单位由 completed turn 改为 assistant message——同值下触发会更频繁(一个 turn 内往往有多条 assistant message)。
|
|
150
150
|
|
|
151
|
+
## 内置技能 / Bundled skill
|
|
152
|
+
|
|
153
|
+
插件随包注册了 `dsh-dcp-config` skill(`skills/dsh-dcp-config/SKILL.md`,经 `ctx.skills.registerProvider`):在会话里让 agent 调压缩、配置 dcp 或排查压缩行为时,指南自动加载——内含 ask_user_question 交互式调参向导(先问期望再映射到具体键)与 cordis.patch.yml 挂载块 `config:` 段的持久化写法,无需翻文档。
|
|
154
|
+
|
|
151
155
|
## 设计参考
|
|
152
156
|
|
|
153
157
|
- [Opencode-DCP/opencode-dynamic-context-pruning](https://github.com/Opencode-DCP/opencode-dynamic-context-pruning)
|
package/lib/index.js
CHANGED
|
@@ -35,6 +35,7 @@ import { ManualCompactionError } from '@deepseek-ai/dsh-compaction'
|
|
|
35
35
|
import { splitConfig, resolveDcpConfig } from './config.js'
|
|
36
36
|
import { summarizeDeterministically, noticeText } from './summarizer.js'
|
|
37
37
|
import { registerDcpCommand } from './command.js'
|
|
38
|
+
import { skillProvider } from './skill.js'
|
|
38
39
|
|
|
39
40
|
const require = createRequire(import.meta.url)
|
|
40
41
|
const { version: VERSION } = require('../package.json')
|
|
@@ -68,7 +69,8 @@ const kAppendNotice = Symbol('dsh-dcp.appendNotice')
|
|
|
68
69
|
|
|
69
70
|
/**
|
|
70
71
|
* Deterministic compaction engine: `summarize()` overridden, everything else
|
|
71
|
-
* inherited. Registers the `/dcp` command beside the inherited `/compact
|
|
72
|
+
* inherited. Registers the `/dcp` command beside the inherited `/compact`,
|
|
73
|
+
* and serves the bundled usage/config guide through the host skill registry.
|
|
72
74
|
*/
|
|
73
75
|
/** Element schema mirroring compaction-basic's model-policy override shape. */
|
|
74
76
|
const modelPolicy = z.object({
|
|
@@ -85,7 +87,7 @@ const modelPolicy = z.object({
|
|
|
85
87
|
})
|
|
86
88
|
|
|
87
89
|
export class DcpEngine extends BasicCompactionEngine {
|
|
88
|
-
static inject = ['llm', 'tokenMeter', 'sessions', 'commands']
|
|
90
|
+
static inject = ['llm', 'tokenMeter', 'sessions', 'commands', 'skills']
|
|
89
91
|
|
|
90
92
|
static Config = z.object({
|
|
91
93
|
// compaction-basic policy keys (forwarded verbatim)
|
|
@@ -140,6 +142,15 @@ export class DcpEngine extends BasicCompactionEngine {
|
|
|
140
142
|
this.dcpStats = { compactions: 0, shadowedTokens: 0, lastAt: null }
|
|
141
143
|
this.pluginPath = fileURLToPath(import.meta.url)
|
|
142
144
|
const engine = this
|
|
145
|
+
// Bundled usage/config guide. Soft-guarded: engines constructed in exotic
|
|
146
|
+
// hosts without the skill registry must still boot; losing the bundled
|
|
147
|
+
// guide is non-fatal. On the real host, `skills` in `static inject`
|
|
148
|
+
// guarantees the service is present (verified by scripts/smoke-boot.mjs).
|
|
149
|
+
try {
|
|
150
|
+
ctx?.skills?.registerProvider?.(() => skillProvider)
|
|
151
|
+
} catch {
|
|
152
|
+
// non-fatal — see comment above
|
|
153
|
+
}
|
|
143
154
|
ctx.effect(function* () {
|
|
144
155
|
yield registerDcpCommand(ctx, engine, VERSION)
|
|
145
156
|
}, 'dsh-dcp /dcp command lifecycle')
|
package/lib/skill.js
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bundled usage/configuration skill for dsh-dcp.
|
|
3
|
+
*
|
|
4
|
+
* Ships `skills/dsh-dcp-config/SKILL.md` through `ctx.skills.registerProvider`
|
|
5
|
+
* (same mechanism as dsh-llm-proxy and dsh-vault): an agent asked to tune
|
|
6
|
+
* compaction, run `/dcp`, or persist a config change loads the guide
|
|
7
|
+
* automatically instead of guessing at key names and mount shapes.
|
|
8
|
+
*
|
|
9
|
+
* @module dsh-dcp/skill
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { readFile } from 'node:fs/promises'
|
|
13
|
+
import { fileURLToPath } from 'node:url'
|
|
14
|
+
|
|
15
|
+
// The runtime import of '@deepseek-ai/dsh-skill' is deliberately avoided:
|
|
16
|
+
// the registry-published dsh-skill lib imports host-closure siblings
|
|
17
|
+
// (@deepseek-ai/dsh-scope, dsh-llm — peers of it, but absent from a plugin
|
|
18
|
+
// repo's own dependency graph), which dies under pnpm's isolated layout.
|
|
19
|
+
// The host injects the real service at runtime; this module only hands it a
|
|
20
|
+
// plain provider object.
|
|
21
|
+
|
|
22
|
+
/** Mirrors dsh-skill's bundled-skill rank (a non-load-bearing ordering hint;
|
|
23
|
+
* the constant is hardcoded there too). Local copy — see the note above
|
|
24
|
+
* for why dsh-skill is not loaded at runtime here. */
|
|
25
|
+
const BUNDLED_SKILL_RANK = 600
|
|
26
|
+
|
|
27
|
+
/** Provider name under `ctx.skills`; doubles as the skill name. */
|
|
28
|
+
export const SKILL_PROVIDER_NAME = 'dsh-dcp-config'
|
|
29
|
+
|
|
30
|
+
/** Packaged skill body; `../skills/` resolves to the package root from lib/. */
|
|
31
|
+
const SKILL_BODY_URL = new URL('../skills/dsh-dcp-config/SKILL.md', import.meta.url)
|
|
32
|
+
|
|
33
|
+
/** Resource base served with the skill so its relative links resolve. */
|
|
34
|
+
const SKILL_RESOURCE_BASE = {
|
|
35
|
+
kind: 'directory',
|
|
36
|
+
path: fileURLToPath(new URL('../skills/dsh-dcp-config/', import.meta.url)),
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const SKILL_INVOCATION = { modelInvocable: true, userInvocable: true }
|
|
40
|
+
|
|
41
|
+
/** Routing description; must stay identical to the SKILL.md frontmatter (asserted in tests). */
|
|
42
|
+
export const SKILL_DESCRIPTION = 'dsh 压缩引擎插件(@aiwayds/dsh-dcp)使用与配置指南。凡涉及上下文压缩、/dcp 命令、压缩调参(阈值/密度/语言/轮数触发),或要配置 dcp 时先读本指南:/dcp 状态与 /dcp set 十个可调键、持久化到 cordis.patch.yml 挂载块 config: 段(dsh-dcp-setup 管理)、ask_user_question 调参向导、四类触发(压力/溢出/轮数/手动)、subagent 会话独立计数生效。触发词:dcp、压缩、compaction、上下文超限、摘要、thresholdRatio、roundInterval。'
|
|
43
|
+
|
|
44
|
+
const SKILL_CANDIDATE = {
|
|
45
|
+
name: SKILL_PROVIDER_NAME,
|
|
46
|
+
description: SKILL_DESCRIPTION,
|
|
47
|
+
invocation: SKILL_INVOCATION,
|
|
48
|
+
provider: SKILL_PROVIDER_NAME,
|
|
49
|
+
source: 'bundled',
|
|
50
|
+
resourceBase: SKILL_RESOURCE_BASE,
|
|
51
|
+
rank: BUNDLED_SKILL_RANK,
|
|
52
|
+
locator: SKILL_BODY_URL,
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** The bundled-skill catalog entry, served through the host skill registry. */
|
|
56
|
+
export const skillProvider = {
|
|
57
|
+
name: SKILL_PROVIDER_NAME,
|
|
58
|
+
list(_options) {
|
|
59
|
+
return Promise.resolve([SKILL_CANDIDATE])
|
|
60
|
+
},
|
|
61
|
+
async get(_candidate, _options) {
|
|
62
|
+
return {
|
|
63
|
+
name: SKILL_CANDIDATE.name,
|
|
64
|
+
description: SKILL_CANDIDATE.description,
|
|
65
|
+
invocation: SKILL_CANDIDATE.invocation,
|
|
66
|
+
provider: SKILL_CANDIDATE.provider,
|
|
67
|
+
source: SKILL_CANDIDATE.source,
|
|
68
|
+
resourceBase: SKILL_RESOURCE_BASE,
|
|
69
|
+
content: stripFrontmatter(await readFile(SKILL_BODY_URL, 'utf8')),
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Strip a leading YAML frontmatter block (`---` / body / `---`) from a skill
|
|
76
|
+
* markdown file. `SkillDefinition.content` must be the instruction body after
|
|
77
|
+
* metadata removal — the same shape the filesystem provider serves — so the
|
|
78
|
+
* bundled SKILL.md, which keeps its frontmatter for the GitHub/manual install
|
|
79
|
+
* paths, has the block removed when served through `skillProvider.get`.
|
|
80
|
+
* Tolerant by design: input that does not open with a `---` line, or whose
|
|
81
|
+
* frontmatter block is never closed, is returned unchanged. Mirrors the
|
|
82
|
+
* delimiter semantics of the upstream skill-filesystem provider.
|
|
83
|
+
*/
|
|
84
|
+
export function stripFrontmatter(raw) {
|
|
85
|
+
const firstLineEnd = raw.indexOf('\n')
|
|
86
|
+
if (firstLineEnd < 0 || raw.slice(0, firstLineEnd).replace(/\r$/, '') !== '---') return raw
|
|
87
|
+
let lineStart = firstLineEnd + 1
|
|
88
|
+
while (lineStart <= raw.length) {
|
|
89
|
+
const nextNewline = raw.indexOf('\n', lineStart)
|
|
90
|
+
const lineEnd = nextNewline < 0 ? raw.length : nextNewline
|
|
91
|
+
if (raw.slice(lineStart, lineEnd).replace(/\r$/, '') === '---') {
|
|
92
|
+
return raw.slice(nextNewline < 0 ? raw.length : nextNewline + 1).trim()
|
|
93
|
+
}
|
|
94
|
+
if (nextNewline < 0) return raw
|
|
95
|
+
lineStart = nextNewline + 1
|
|
96
|
+
}
|
|
97
|
+
return raw
|
|
98
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aiwayds/dsh-dcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.1",
|
|
4
4
|
"description": "Deterministic context-pruning compaction backend for dsh (DeepSeek Harness) — zero-LLM summaries, /dcp command, works out of the box. Design references Opencode-DCP/opencode-dynamic-context-pruning.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
"files": [
|
|
18
18
|
"lib",
|
|
19
19
|
"scripts",
|
|
20
|
+
"skills",
|
|
20
21
|
"README.md",
|
|
21
22
|
"README.en.md",
|
|
22
23
|
"LICENSE",
|
|
@@ -61,6 +62,7 @@
|
|
|
61
62
|
"@deepseek-ai/dsh-invariants": ">=0.1.2-rc.1",
|
|
62
63
|
"@deepseek-ai/dsh-llm": ">=0.1.2-rc.1",
|
|
63
64
|
"@deepseek-ai/dsh-session": ">=0.1.2-rc.1",
|
|
65
|
+
"@deepseek-ai/dsh-skill": ">=0.1.2-rc.1",
|
|
64
66
|
"@deepseek-ai/dsh-token-meter": ">=0.1.2-rc.1",
|
|
65
67
|
"@deepseek-ai/schemastery": "^3.18.2"
|
|
66
68
|
},
|
|
@@ -95,6 +97,9 @@
|
|
|
95
97
|
"@deepseek-ai/dsh-session": {
|
|
96
98
|
"optional": true
|
|
97
99
|
},
|
|
100
|
+
"@deepseek-ai/dsh-skill": {
|
|
101
|
+
"optional": true
|
|
102
|
+
},
|
|
98
103
|
"@deepseek-ai/dsh-token-meter": {
|
|
99
104
|
"optional": true
|
|
100
105
|
},
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: dsh-dcp-config
|
|
3
|
+
description: "dsh 压缩引擎插件(@aiwayds/dsh-dcp)使用与配置指南。凡涉及上下文压缩、/dcp 命令、压缩调参(阈值/密度/语言/轮数触发),或要配置 dcp 时先读本指南:/dcp 状态与 /dcp set 十个可调键、持久化到 cordis.patch.yml 挂载块 config: 段(dsh-dcp-setup 管理)、ask_user_question 调参向导、四类触发(压力/溢出/轮数/手动)、subagent 会话独立计数生效。触发词:dcp、压缩、compaction、上下文超限、摘要、thresholdRatio、roundInterval。"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# dsh-dcp 使用指南(确定性上下文压缩)
|
|
7
|
+
|
|
8
|
+
> dsh 插件:替换官方 `compaction-basic` 的确定性压缩引擎——**零 LLM 调用**
|
|
9
|
+
> (去重 / 折错 / 密度控制),中文场景优先(CJK 计价、中文报错与"待办:"识别),
|
|
10
|
+
> 附轮数触发。压力触发、保留尾巴、溢出恢复、tool-pairing 安全机制全部继承官方,只替换"摘要"这一环。
|
|
11
|
+
|
|
12
|
+
## 配置入口(两条路)
|
|
13
|
+
|
|
14
|
+
1. **会话内临时调参**:`/dcp set <键> <值>`,只影响当前会话,重启失效。十个可调键:
|
|
15
|
+
`dedup` `purgeErrors` `maxItems` `maxItemChars` `maxSummaryTokens` `language`
|
|
16
|
+
`tokenEstimate` `thresholdRatio` `roundInterval` `notice`。
|
|
17
|
+
2. **持久化**:cordis.patch.yml 里 dsh-dcp 挂载块的 `config:` 段。用
|
|
18
|
+
`npx dsh-dcp-setup` 写入并维护(带 marker 注释、改动前日期备份、幂等);
|
|
19
|
+
`--remove` 只删 setup 写的块,手工写的块不受影响。bundle 方式
|
|
20
|
+
(`dsh plugin add @aiwayds/dsh-dcp` 或列在 profile `bundles`)自动挂载,无需手写 patch。
|
|
21
|
+
|
|
22
|
+
挂载块形状(`~/.dsh/cordis.patch.yml` 或某个 profile 的 cordis.patch.yml):
|
|
23
|
+
|
|
24
|
+
```yaml
|
|
25
|
+
- id: compaction-basic
|
|
26
|
+
name: '@deepseek-ai/dsh-compaction-basic'
|
|
27
|
+
disabled: true
|
|
28
|
+
- insert:
|
|
29
|
+
- id: dsh-dcp
|
|
30
|
+
name: '@aiwayds/dsh-dcp'
|
|
31
|
+
config:
|
|
32
|
+
thresholdRatio: 0.7 # 每个键都可选
|
|
33
|
+
language: zh
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## 配置键
|
|
37
|
+
|
|
38
|
+
dsh-dcp 自有键(除 `thresholdRatio` 外全部可用 `/dcp set` 调):
|
|
39
|
+
|
|
40
|
+
| 键 | 默认 | 说明 |
|
|
41
|
+
|---|---|---|
|
|
42
|
+
| `dedup` | `true` | 重复工具调用折叠成一条标注 |
|
|
43
|
+
| `purgeErrors` | `true` | 旧报错折叠成一条提示 |
|
|
44
|
+
| `maxItems` / `maxItemChars` | 10 / 200 | 摘要密度(条数 / 单条字符上限) |
|
|
45
|
+
| `maxSummaryTokens` | 2048 | 摘要 token 预算 |
|
|
46
|
+
| `language` | `en`(代码默认;bundle 挂载默认 `zh`) | 摘要语言;`zh` 额外识别中文报错和"待办:" |
|
|
47
|
+
| `tokenEstimate` | `cjk` | CJK(中/日/韩/全角)按 ~2 字符/token 计价;`ascii` 与宿主一致 |
|
|
48
|
+
| `protectedTools` | `['write', 'edit', 'apply_patch']` | 写侧工具(子串匹配)的重复调用不折叠进 dedup 标注 |
|
|
49
|
+
| `roundInterval` | 50 | 每 N 条 assistant message(一次 LLM 往返)触发一次压缩;`0` 关闭 |
|
|
50
|
+
| `notice` | `true` | 压缩后在会话追加一行通知 |
|
|
51
|
+
|
|
52
|
+
转发上游 compaction-basic 的策略键:`thresholdRatio`(上游默认 0.8;**本插件 bundle 挂载默认 0.7**,中文场景建议 0.7)、`retainRatio`、`retainTokens`、`maxTokens`、`summarizationProvider`、`summarizationModel`、`compactionRetries`、`maxOverflowRetries`、`modelPolicies`、`auto`。
|
|
53
|
+
|
|
54
|
+
## 交互式调参向导(ask_user_question)
|
|
55
|
+
|
|
56
|
+
用户抱怨压缩行为时,不要甩配置表让对方自己读——先用 `ask_user_question`
|
|
57
|
+
问清期望,再映射到键:
|
|
58
|
+
|
|
59
|
+
1. **触发时机**:更晚触发 → `thresholdRatio` 调高,或 `roundInterval` 调大
|
|
60
|
+
(完全不想要轮数触发 → `roundInterval: 0`);更早/更频繁 → 反向。
|
|
61
|
+
2. **摘要密度**:更细 → `maxItems` / `maxItemChars` 调大(预算不够再加
|
|
62
|
+
`maxSummaryTokens`);更省 → 调小。
|
|
63
|
+
3. **摘要语言** → `language: en|zh`。
|
|
64
|
+
4. **token 计价** → `tokenEstimate: cjk|ascii`。
|
|
65
|
+
5. **通知行** → `notice: on|off`。
|
|
66
|
+
|
|
67
|
+
流程:先用 `/dcp set <键> <值>` 在会话内试效果,满意后再代写持久 config——
|
|
68
|
+
直接改 cordis.patch.yml 里 dsh-dcp 挂载块的 `config:` 段(setup 写的块可原位改,marker 保留)。
|
|
69
|
+
|
|
70
|
+
## 触发条件(四类)
|
|
71
|
+
|
|
72
|
+
| 触发 | 时机 | 说明 |
|
|
73
|
+
|---|---|---|
|
|
74
|
+
| 压力 | 每步请求前 | token ≥ `thresholdRatio` × 上下文窗口 |
|
|
75
|
+
| 溢出 | 模型报 context 超限 | 继承官方恢复流程 |
|
|
76
|
+
| 轮数 | 每累计 `roundInterval` 条 assistant message | 任何一次压缩(含压力/手动)都重置时钟;`0` 关闭;需保持 `auto: true`(默认开) |
|
|
77
|
+
| 手动 | `/dcp compact`、`/compact` | 随时可用 |
|
|
78
|
+
|
|
79
|
+
- **subagent 同样生效**:进程内子代理(含 continuable 与 one-shot)走同一套事件分发,
|
|
80
|
+
压力/溢出/轮数对每个会话独立计数、独立触发。
|
|
81
|
+
- `notice` 通知行本身也是上下文(每次压缩约 15–25 tokens);`notice: false` 可关。
|
|
82
|
+
|
|
83
|
+
## 排障
|
|
84
|
+
|
|
85
|
+
1. `/dcp`(无参数)看状态:当前配置、压缩次数、省下的 LLM 调用,以及
|
|
86
|
+
per-session 概览(含子代理;已销毁的会话自动消失,列表最多前 10 个,超出显示 `+N more`)。
|
|
87
|
+
2. 会话压不动 → 先确认 `auto` 是否为 `true`(自动触发总开关,默认开),再看
|
|
88
|
+
`thresholdRatio` 是否设得过高、`roundInterval` 是否为 `0`。
|
|
89
|
+
3. `npx dsh-dcp-setup --remove` 后出现 `WARN: a compaction-basic entry remains...` →
|
|
90
|
+
patch 文件里还留着 disable 行,官方 LLM 压缩后端会保持关闭;不是给 dsh-dcp 用的
|
|
91
|
+
就手动删掉那一行。
|