@ch4acko3/dsh-turn-fold 0.4.0 → 0.4.2
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 +11 -5
- package/README.zh-CN.md +4 -2
- package/index.cjs +2 -3
- package/inline-source.cjs +14 -7
- package/locales/en.json +2 -0
- package/locales/zh.json +2 -0
- package/package.json +9 -6
- package/patch.cjs +6 -3
- package/settings.cjs +40 -4
package/README.md
CHANGED
|
@@ -2,14 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
English | [简体中文](README.zh-CN.md)
|
|
4
4
|
|
|
5
|
+
[](https://memorax-ai.github.io/dsh-harmony/)
|
|
6
|
+
|
|
5
7
|
A [dsh-harmony](https://github.com/memorax-ai/dsh-harmony) provider that adds
|
|
6
8
|
Codex-Desktop-style turn folding to the DSH WebUI conversation.
|
|
7
9
|
|
|
8
10
|
https://github.com/user-attachments/assets/3c9dfdcf-a454-4750-9edf-76771ed5a9a6
|
|
9
11
|
|
|
10
12
|
During a turn, the summary bar remains visible while native thinking, notes,
|
|
11
|
-
commands, and tool calls keep streaming. Consecutive reasoning
|
|
12
|
-
share one compact group from the second item
|
|
13
|
+
commands, and tool calls keep streaming. Consecutive reasoning, context
|
|
14
|
+
injections, and tool activity share one compact group from the second item
|
|
15
|
+
onward. When a turn settles, known
|
|
13
16
|
Agent activity, including context injection, moves into one disclosure
|
|
14
17
|
immediately before the final answer. Its configurable metrics default to wall
|
|
15
18
|
time, tool-call count, and input/output tokens, and appear only when the loaded
|
|
@@ -31,9 +34,12 @@ short open/close transition that unmounts the activity after it closes.
|
|
|
31
34
|
|
|
32
35
|
## How it works
|
|
33
36
|
|
|
34
|
-
Three
|
|
35
|
-
`@deepseek-ai/dsh-client-ui-conversation` (`lib/client.js`)
|
|
36
|
-
|
|
37
|
+
Three shape-guarded Source Patches run in memory against the compiled browser
|
|
38
|
+
bundle of `@deepseek-ai/dsh-client-ui-conversation` (`lib/client.js`) from
|
|
39
|
+
`0.1.0-rc.8` up to, but not including, the `0.2.0` prerelease line; installed
|
|
40
|
+
DSH files are never modified. Every selector must still match exactly once, so
|
|
41
|
+
an incompatible compiled shape fails closed instead of modifying an uncertain
|
|
42
|
+
target.
|
|
37
43
|
|
|
38
44
|
| Patch | Selector (expect 1) | Effect |
|
|
39
45
|
| --- | --- | --- |
|
package/README.zh-CN.md
CHANGED
|
@@ -2,11 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
[English](README.md) | 简体中文
|
|
4
4
|
|
|
5
|
+
[](https://memorax-ai.github.io/dsh-harmony/)
|
|
6
|
+
|
|
5
7
|
一个基于 [dsh-harmony](https://github.com/memorax-ai/dsh-harmony) 的 Provider,为 DSH WebUI 对话添加 Codex Desktop 风格的回合折叠。
|
|
6
8
|
|
|
7
9
|
https://github.com/user-attachments/assets/3c9dfdcf-a454-4750-9edf-76771ed5a9a6
|
|
8
10
|
|
|
9
|
-
|
|
11
|
+
回合进行期间,摘要栏保持可见,原生思考、说明、命令和工具调用继续流式输出。从第二项开始,连续的推理、上下文注入和工具活动会合并为一个紧凑分组。回合结束后,包括上下文注入在内的已知 Agent 活动会收进最终答复前方的折叠栏。摘要指标可以配置,默认显示耗时、工具调用次数以及输入/输出 token;只有当已加载的回合包含足够数据、无需猜测即可计算时才会展示对应指标。
|
|
10
12
|
|
|
11
13
|
最终答复通过 `turn-tail.closing.finalNode` 定位,不依赖 `finish_reason` 或 DOM 位置。正常完成、已停止和已中断的回合都会折叠,后两者显示不同的状态标签。当 DSH 将结束分支标记为不可用、结束答复之后仍有节点,或者用户的键盘焦点或文本选择位于活动内容中时,回合会保持展开。失败、达到最大 token、缺少结束节点以及仍在进行的回合也会保持展开,避免隐藏错误或未完成的工作。
|
|
12
14
|
|
|
@@ -14,7 +16,7 @@ https://github.com/user-attachments/assets/3c9dfdcf-a454-4750-9edf-76771ed5a9a6
|
|
|
14
16
|
|
|
15
17
|
## 工作原理
|
|
16
18
|
|
|
17
|
-
|
|
19
|
+
三个带形状校验的 Source Patch 会在内存中修改 `@deepseek-ai/dsh-client-ui-conversation` `>=0.1.0-rc.8 <0.2.0-0` 的编译后浏览器包(`lib/client.js`),不会修改已安装的 DSH 文件。每条选择器仍必须恰好命中一次,因此遇到不兼容的编译形状时会停止应用,而不会修改不确定的目标。
|
|
18
20
|
|
|
19
21
|
| Patch | 选择器(预期命中 1 次) | 作用 |
|
|
20
22
|
| --- | --- | --- |
|
package/index.cjs
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
// Host entry for the @ch4acko3/dsh-turn-fold Harmony provider.
|
|
4
|
-
const {
|
|
5
|
-
const { Config, SETTINGS_NAMESPACE } = require('./settings.cjs')
|
|
4
|
+
const { Config, SETTINGS_NAMESPACE, createSettingsSchema } = require('./settings.cjs')
|
|
6
5
|
|
|
7
6
|
exports.Config = Config
|
|
8
7
|
exports.inject = ['harmony']
|
|
9
8
|
exports.apply = (ctx, config) => {
|
|
10
9
|
ctx.inject(['settings'], (settingsCtx) => {
|
|
11
|
-
settingsCtx.settings.register(
|
|
10
|
+
settingsCtx.settings.register(SETTINGS_NAMESPACE, createSettingsSchema(), { base: config })
|
|
12
11
|
})
|
|
13
12
|
}
|
package/inline-source.cjs
CHANGED
|
@@ -242,9 +242,11 @@ function __ch4acko3DshTurnFoldActivityGroup(props) {
|
|
|
242
242
|
var foldKey = props.foldKey;
|
|
243
243
|
var toolCount = 0;
|
|
244
244
|
var reasoningCount = 0;
|
|
245
|
+
var contextCount = 0;
|
|
245
246
|
for (var itemIndex = 0; itemIndex < props.items.length; itemIndex++) {
|
|
246
247
|
if (props.items[itemIndex].kind === "tool") toolCount++;
|
|
247
|
-
else reasoningCount++;
|
|
248
|
+
else if (props.items[itemIndex].kind === "reasoning") reasoningCount++;
|
|
249
|
+
else if (props.items[itemIndex].kind === "context") contextCount++;
|
|
248
250
|
}
|
|
249
251
|
var initialOpen = __ch4acko3DshTurnFoldOpenKeys.has(foldKey);
|
|
250
252
|
var openState = react.useState(initialOpen);
|
|
@@ -265,12 +267,15 @@ function __ch4acko3DshTurnFoldActivityGroup(props) {
|
|
|
265
267
|
};
|
|
266
268
|
}, []);
|
|
267
269
|
var reasoningTitle = reasoningCount === 0 ? null : __ch4acko3DshTurnFoldText("activityGroup.reasoning." + (reasoningCount === 1 ? "one" : "many"), { count: reasoningCount });
|
|
270
|
+
var contextTitle = contextCount === 0 ? null : __ch4acko3DshTurnFoldText("activityGroup.context." + (contextCount === 1 ? "one" : "many"), { count: contextCount });
|
|
268
271
|
var toolTitle = toolCount === 0 ? null : __ch4acko3DshTurnFoldText("activityGroup.tools." + (toolCount === 1 ? "one" : "many"), { count: toolCount });
|
|
269
|
-
var
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
272
|
+
var titleParts = [reasoningTitle, contextTitle, toolTitle].filter(function (part) { return part !== null; });
|
|
273
|
+
var titleChildren = [];
|
|
274
|
+
for (var titleIndex = 0; titleIndex < titleParts.length; titleIndex++) {
|
|
275
|
+
if (titleIndex > 0) titleChildren.push(react_jsx_runtime.jsx("span", { className: "__ch4acko3-dsh-turn-fold-activity__separator", "aria-hidden": true }, "separator-" + String(titleIndex)));
|
|
276
|
+
titleChildren.push(titleParts[titleIndex]);
|
|
277
|
+
}
|
|
278
|
+
var title = titleParts.length === 1 ? titleParts[0] : react_jsx_runtime.jsx(react_jsx_runtime.Fragment, { children: titleChildren });
|
|
274
279
|
var failure = props.failed === 0 ? null : __ch4acko3DshTurnFoldText("activityGroup.failures." + (props.failed === 1 ? "one" : "many"), { count: props.failed });
|
|
275
280
|
var state = props.running ? "running" : props.failed > 0 ? "error" : "ok";
|
|
276
281
|
function toggle() {
|
|
@@ -829,7 +834,7 @@ function __ch4acko3DshTurnFoldHasToolCallBlock(node) {
|
|
|
829
834
|
return Array.isArray(blocks) && blocks.some(function (block) { return block !== void 0 && block.kind === "tool-call"; });
|
|
830
835
|
}
|
|
831
836
|
function __ch4acko3DshTurnFoldIsActivityNode(node) {
|
|
832
|
-
if (node.kind === "tool-call") return true;
|
|
837
|
+
if (node.kind === "tool-call" || node.kind === "context") return true;
|
|
833
838
|
if (node.kind !== "assistant-step" || __ch4acko3DshTurnFoldReasoningTexts(node).length === 0) return false;
|
|
834
839
|
return __ch4acko3DshTurnFoldHasToolCallBlock(node) || !__ch4acko3DshTurnFoldHasVisibleNonReasoning(node);
|
|
835
840
|
}
|
|
@@ -877,6 +882,8 @@ function __ch4acko3DshTurnFoldRenderEntries(entries, nodeStore, renderNode, sess
|
|
|
877
882
|
if (activityEntry.node.kind === "tool-call") {
|
|
878
883
|
toolKeys.push(activityEntry.key);
|
|
879
884
|
items.push({ kind: "tool", key: activityEntry.key });
|
|
885
|
+
} else if (activityEntry.node.kind === "context") {
|
|
886
|
+
items.push({ kind: "context", key: activityEntry.key });
|
|
880
887
|
} else {
|
|
881
888
|
var texts = __ch4acko3DshTurnFoldReasoningTexts(activityEntry.node);
|
|
882
889
|
for (var textIndex = 0; textIndex < texts.length; textIndex++) items.push({ kind: "reasoning", key: String(activityEntry.key) + ":" + String(textIndex), text: texts[textIndex] });
|
package/locales/en.json
CHANGED
|
@@ -17,6 +17,8 @@
|
|
|
17
17
|
"summary.interruptedSuffix": " - Interrupted",
|
|
18
18
|
"activityGroup.reasoning.one": "{count} reasoning step",
|
|
19
19
|
"activityGroup.reasoning.many": "{count} reasoning steps",
|
|
20
|
+
"activityGroup.context.one": "{count} context injection",
|
|
21
|
+
"activityGroup.context.many": "{count} context injections",
|
|
20
22
|
"activityGroup.tools.one": "{count} tool call",
|
|
21
23
|
"activityGroup.tools.many": "{count} tool calls",
|
|
22
24
|
"activityGroup.failures.one": "{count} failed",
|
package/locales/zh.json
CHANGED
|
@@ -17,6 +17,8 @@
|
|
|
17
17
|
"summary.interruptedSuffix": " - 已中断",
|
|
18
18
|
"activityGroup.reasoning.one": "{count} 段思考",
|
|
19
19
|
"activityGroup.reasoning.many": "{count} 段思考",
|
|
20
|
+
"activityGroup.context.one": "{count} 次上下文注入",
|
|
21
|
+
"activityGroup.context.many": "{count} 次上下文注入",
|
|
20
22
|
"activityGroup.tools.one": "{count} 次工具调用",
|
|
21
23
|
"activityGroup.tools.many": "{count} 次工具调用",
|
|
22
24
|
"activityGroup.failures.one": "{count} 个失败",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ch4acko3/dsh-turn-fold",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.2",
|
|
4
4
|
"description": "Codex-style turn folding for the DSH WebUI conversation, implemented as a dsh-harmony provider.",
|
|
5
5
|
"main": "./index.cjs",
|
|
6
6
|
"engines": {
|
|
@@ -30,7 +30,9 @@
|
|
|
30
30
|
"patch": "./harmony.patch.yml"
|
|
31
31
|
},
|
|
32
32
|
"harmony": {
|
|
33
|
-
"patches": [
|
|
33
|
+
"patches": [
|
|
34
|
+
"./patch.cjs"
|
|
35
|
+
]
|
|
34
36
|
},
|
|
35
37
|
"plugin": {
|
|
36
38
|
"schemaVersion": 1,
|
|
@@ -53,17 +55,18 @@
|
|
|
53
55
|
"@deepseek-ai/schemastery": "^3.18.1"
|
|
54
56
|
},
|
|
55
57
|
"peerDependencies": {
|
|
56
|
-
"@deepseek-ai/dsh-client-ui-conversation": "^0.1.0-rc.8",
|
|
57
|
-
"@deepseek-ai/dsh-settings": "^0.1.0-rc.8",
|
|
58
|
-
"dsh-harmony": "^0.7.0"
|
|
58
|
+
"@deepseek-ai/dsh-client-ui-conversation": "^0.1.0-rc.8 || ^0.1.1-0",
|
|
59
|
+
"@deepseek-ai/dsh-settings": "^0.1.0-rc.8 || ^0.1.1-0",
|
|
60
|
+
"dsh-harmony": "^0.7.0 || ^0.8.0"
|
|
59
61
|
},
|
|
60
62
|
"devDependencies": {
|
|
61
63
|
"@deepseek-ai/dsh-client-ui-conversation": "0.1.0-rc.8",
|
|
62
64
|
"@deepseek-ai/dsh-settings": "0.1.0-rc.8",
|
|
63
65
|
"@phenomnomnominal/tsquery": "^6.2.0",
|
|
64
|
-
"dsh-harmony": "^0.
|
|
66
|
+
"dsh-harmony": "^0.8.0",
|
|
65
67
|
"react": "18.3.1",
|
|
66
68
|
"react-test-renderer": "18.3.1",
|
|
69
|
+
"semver": "^7.8.5",
|
|
67
70
|
"typescript": "^6.0.3"
|
|
68
71
|
}
|
|
69
72
|
}
|
package/patch.cjs
CHANGED
|
@@ -13,20 +13,21 @@
|
|
|
13
13
|
// 3. install-turn-fold-services — registers the plugin's native DSH locale
|
|
14
14
|
// namespace and binds its native settings scope during conversation boot.
|
|
15
15
|
//
|
|
16
|
-
// All three selectors
|
|
17
|
-
// a compiled-shape drift fails loudly in `dsh harmony status`.
|
|
16
|
+
// All three selectors use an exact `expect: 1` inside a bounded target-version
|
|
17
|
+
// range, so a compiled-shape drift still fails loudly in `dsh harmony status`.
|
|
18
18
|
|
|
19
19
|
const INLINE = require('./inline-source.cjs')
|
|
20
20
|
|
|
21
21
|
const TARGET = {
|
|
22
22
|
package: '@deepseek-ai/dsh-client-ui-conversation',
|
|
23
|
-
version: '0.1.0-rc.8',
|
|
23
|
+
version: '>=0.1.0-rc.8 <0.2.0-0',
|
|
24
24
|
file: 'lib/client.js',
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
module.exports = [
|
|
28
28
|
{
|
|
29
29
|
id: 'inject-turn-fold-runtime',
|
|
30
|
+
description: 'Provides the Turn Fold rendering, disclosure, metrics, settings, and locale runtime used by ChatView.',
|
|
30
31
|
target: TARGET,
|
|
31
32
|
select: 'FunctionDeclaration[name.name="ChatView"], VariableStatement:has(VariableDeclaration[name.name="ChatView"])',
|
|
32
33
|
expect: 1,
|
|
@@ -36,6 +37,7 @@ module.exports = [
|
|
|
36
37
|
},
|
|
37
38
|
{
|
|
38
39
|
id: 'rewrite-node-render-loop',
|
|
40
|
+
description: 'Routes ChatView node rendering through Turn Fold while preserving the native node renderer.',
|
|
39
41
|
target: TARGET,
|
|
40
42
|
select: 'CallExpression[expression.name.name="map"][expression.expression.name="order"]',
|
|
41
43
|
expect: 1,
|
|
@@ -52,6 +54,7 @@ module.exports = [
|
|
|
52
54
|
},
|
|
53
55
|
{
|
|
54
56
|
id: 'install-turn-fold-services',
|
|
57
|
+
description: 'Registers Turn Fold locales and connects its settings when the conversation UI starts.',
|
|
55
58
|
target: TARGET,
|
|
56
59
|
select: 'VariableStatement:has(VariableDeclaration[name.name="t"][initializer.expression.name.name="bind"])',
|
|
57
60
|
expect: 1,
|
package/settings.cjs
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const z = require('@deepseek-ai/schemastery')
|
|
4
|
-
|
|
5
3
|
const SETTINGS_NAMESPACE = 'dsh-turn-fold'
|
|
6
4
|
|
|
7
5
|
const SUMMARY_FIELDS = [
|
|
@@ -24,12 +22,50 @@ const DEFAULT_SUMMARY_FIELDS = [
|
|
|
24
22
|
'outputTokens',
|
|
25
23
|
]
|
|
26
24
|
|
|
27
|
-
|
|
28
|
-
|
|
25
|
+
function decodeConfig(value) {
|
|
26
|
+
const source = value == null ? {} : value
|
|
27
|
+
if (typeof source !== 'object' || Array.isArray(source)) throw new TypeError('expected config to be an object')
|
|
28
|
+
const summaryFields = source.summaryFields === undefined ? DEFAULT_SUMMARY_FIELDS : source.summaryFields
|
|
29
|
+
if (!Array.isArray(summaryFields)) throw new TypeError('summaryFields must be an array')
|
|
30
|
+
for (const field of summaryFields) {
|
|
31
|
+
if (!SUMMARY_FIELDS.includes(field)) throw new TypeError(`unknown summary field: ${String(field)}`)
|
|
32
|
+
}
|
|
33
|
+
return { ...source, summaryFields: summaryFields.slice() }
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function Config(value) {
|
|
37
|
+
return decodeConfig(value)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
Object.defineProperty(Config, '~standard', {
|
|
41
|
+
value: {
|
|
42
|
+
version: 1,
|
|
43
|
+
vendor: '@ch4acko3/dsh-turn-fold',
|
|
44
|
+
validate(value) {
|
|
45
|
+
try {
|
|
46
|
+
return { value: decodeConfig(value) }
|
|
47
|
+
} catch (error) {
|
|
48
|
+
return { issues: [{ message: error instanceof Error ? error.message : String(error) }] }
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
},
|
|
29
52
|
})
|
|
30
53
|
|
|
54
|
+
let settingsSchema
|
|
55
|
+
function createSettingsSchema() {
|
|
56
|
+
if (settingsSchema !== undefined) return settingsSchema
|
|
57
|
+
// The Host loads CommonJS providers alongside the ESM settings graph. Defer
|
|
58
|
+
// Schemastery until that service is ready to avoid a Node 24 CJS/ESM race.
|
|
59
|
+
const z = require('@deepseek-ai/schemastery')
|
|
60
|
+
settingsSchema = z.object({
|
|
61
|
+
summaryFields: z.array(z.union(SUMMARY_FIELDS)).default(DEFAULT_SUMMARY_FIELDS),
|
|
62
|
+
})
|
|
63
|
+
return settingsSchema
|
|
64
|
+
}
|
|
65
|
+
|
|
31
66
|
module.exports = {
|
|
32
67
|
Config,
|
|
68
|
+
createSettingsSchema,
|
|
33
69
|
DEFAULT_SUMMARY_FIELDS,
|
|
34
70
|
SETTINGS_NAMESPACE,
|
|
35
71
|
SUMMARY_FIELDS,
|