dsh-ops 0.2.3 → 0.2.5-beta.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/CHANGELOG.md +18 -0
- package/PROVENANCE.md +24 -0
- package/README.en.md +51 -9
- package/README.md +36 -11
- package/README.zh.md +36 -11
- package/cordis.patch.yml +27 -155
- package/docs/auto-compact.md +102 -0
- package/docs/manual-validation.md +10 -5
- package/docs/schema-current.json +20 -13
- package/docs/schema-measurement.md +29 -9
- package/dsh-plugin.json +40 -7
- package/lib/components/background/icon.png +0 -0
- package/lib/components/background/index.js +3 -0
- package/lib/components/background/locale/en.json +6 -0
- package/lib/components/background/locale/zh.json +6 -0
- package/lib/components/background/package.json +6 -0
- package/lib/components/file/icon.png +0 -0
- package/lib/components/file/index.js +3 -0
- package/lib/components/file/locale/en.json +6 -0
- package/lib/components/file/locale/zh.json +6 -0
- package/lib/components/file/package.json +6 -0
- package/lib/components/shell/icon.png +0 -0
- package/lib/components/shell/index.js +3 -0
- package/lib/components/shell/locale/en.json +6 -0
- package/lib/components/shell/locale/zh.json +6 -0
- package/lib/components/shell/package.json +6 -0
- package/lib/index.js +46 -32
- package/lib/policy.js +16 -8
- package/lib/profile-install.js +1 -1
- package/lib/runtime-pool.js +42 -0
- package/lib/session-pwsh.js +31 -0
- package/lib/tools.js +64 -16
- package/node_modules/@dsh-ops/auto-compact/LICENSE +30 -0
- package/node_modules/@dsh-ops/auto-compact/README.md +23 -0
- package/node_modules/@dsh-ops/auto-compact/lib/client.js +79 -0
- package/node_modules/@dsh-ops/auto-compact/lib/config.js +60 -0
- package/node_modules/@dsh-ops/auto-compact/lib/index.js +132 -0
- package/node_modules/@dsh-ops/auto-compact/locale/en.json +6 -0
- package/node_modules/@dsh-ops/auto-compact/locale/zh.json +6 -0
- package/node_modules/@dsh-ops/auto-compact/package.json +31 -0
- package/package.json +27 -4
- package/packages/README.md +87 -0
- package/packages/auto-compact/LICENSE +30 -0
- package/packages/auto-compact/README.md +23 -0
- package/packages/auto-compact/lib/client.js +79 -0
- package/packages/auto-compact/lib/config.js +60 -0
- package/packages/auto-compact/lib/index.js +132 -0
- package/packages/auto-compact/locale/en.json +6 -0
- package/packages/auto-compact/locale/zh.json +6 -0
- package/packages/auto-compact/package.json +31 -0
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# 上下文自动压缩 / Automatic context compaction
|
|
2
|
+
|
|
3
|
+
## 界面位置
|
|
4
|
+
|
|
5
|
+
本地开发版在 **插件列表 → dsh-ops → 包含的组件** 中新增第四项「上下文自动压缩」
|
|
6
|
+
(`dsh-ops-auto-compact` / `@dsh-ops/auto-compact`)。使用它旁边的配置入口进入设置,
|
|
7
|
+
不是全局设置中的独立页面。新组件默认启用;原有 background 仍默认停用。
|
|
8
|
+
`0.2.5-beta.1` 是待发布测试版候选;正式版 npm 0.2.4 不含此功能。是否发布以 registry 为准。
|
|
9
|
+
|
|
10
|
+
| 配置 | 默认 | 范围 |
|
|
11
|
+
| --- | --- | --- |
|
|
12
|
+
| `thresholdPercent` | 50 | 1–99,整数 |
|
|
13
|
+
| `cooldownSeconds` | 120 | 30–3600,整数 |
|
|
14
|
+
| `timeoutSeconds` | 120 | 10–600,整数 |
|
|
15
|
+
|
|
16
|
+
设置调用现有宿主表单的 revision-fenced mutate,保存到当前 profile 的
|
|
17
|
+
`cordis.patch.yml`,可恢复默认,不使用 localStorage。启用组件且当前客户端具有
|
|
18
|
+
宿主设置权限时才可保存;停用组件不伪装成可配置。修改即时影响下一次判定,
|
|
19
|
+
不重挂载或中断正在运行的压缩;压缩已经开始时沿用当次捕获的截止时间。
|
|
20
|
+
|
|
21
|
+
## 触发与安全
|
|
22
|
+
|
|
23
|
+
- 使用与 Web 一致的 `contextPressure` 投影:`projectedTokens ?? pressureTokens`,
|
|
24
|
+
除以 `contextWindow`,四舍五入并上限 100。显示值 **严格高于** 阈值才满足条件;
|
|
25
|
+
等于 50% 不触发。无样本或容量、异常值时跳过,不猜模型容量。
|
|
26
|
+
- 不按计时器轮询。正常 turn 进入 idle 时判定,热启用时检查已加载的 idle agent。
|
|
27
|
+
用户取消的 turn 不自动启动维护。长时间持续运行的 turn 不会在中途压缩;
|
|
28
|
+
原有宿主 pressure/overflow 自动策略保持不动。
|
|
29
|
+
- 调用宿主 `/compact` 同一个 `compaction.compactNow(agent, signal)` 服务,不向
|
|
30
|
+
模型发送 `/compact` 文本,不增加模型可调用工具或额外提示词。
|
|
31
|
+
- idle 状态本身不构成互斥:由宿主 runMaintenance admission 和 durable compaction
|
|
32
|
+
marker 执行真实串行化;busy 是正常竞态,跳过而非循环重试。
|
|
33
|
+
- 一个组件实例同时只运行一个压缩,跨会话共享冷却期;不干扰宿主自己的压缩器。
|
|
34
|
+
同一 surface 在没有新内容/阈值改变时不重复尝试,自己的摘要替换也不会形成循环。
|
|
35
|
+
无可压缩历史、失败都受冷却和 surface 门禁控制;无定时后台重试。
|
|
36
|
+
- commit/persistence 故障会暂停该会话的本组件自动压缩,直到重新加载组件。
|
|
37
|
+
应先检查会话状态,不直接重试。其它故障只记录固定脱敏消息。
|
|
38
|
+
- deadline 是 AbortSignal 取消,不是强制销毁:依赖宿主/模型适配器响应 abort。
|
|
39
|
+
即使适配器延迟退出,本组件也不开放第二个任务;关闭组件会 abort 自己的压缩并
|
|
40
|
+
await allSettled,不调用 agent.cancel,不丢用户 inbox,不杀后台任务。
|
|
41
|
+
- 摘要使用宿主配置的模型,可能产生调用费用;摘要会改变 model-visible surface,
|
|
42
|
+
原始历史/标记与持久化由宿主负责。压缩不是无损保证,不宣称百分比是未来 provider
|
|
43
|
+
请求的精确计费。文档与截图不能替代真实 Desktop 验收。
|
|
44
|
+
|
|
45
|
+
## English
|
|
46
|
+
|
|
47
|
+
The fourth component is in **Plugins → dsh-ops → Included components**. Its row
|
|
48
|
+
configuration page uses the existing host forms, revision-fenced profile writes,
|
|
49
|
+
and restore-default operations. The new component defaults to ON; background stays OFF.
|
|
50
|
+
Only integer settings in the table above are accepted. This feature belongs to the
|
|
51
|
+
0.2.5-beta.1 prerelease candidate, not npm 0.2.4. Check the registry for publication status.
|
|
52
|
+
|
|
53
|
+
The policy matches the rounded Web context meter and requires a value strictly
|
|
54
|
+
above the threshold. It calls the same public idle-maintenance service as `/compact`
|
|
55
|
+
after a non-cancelled turn, never during active tools. Missing samples skip work.
|
|
56
|
+
No model-callable tool, polling, invented model capacity, host policy mutation, or
|
|
57
|
+
shared registry rewrite is involved. One owned operation, global cooldown,
|
|
58
|
+
unchanged-surface suppression, abort/drain teardown, and commit/save-failure session
|
|
59
|
+
suspension bound work. New user messages are queued by the host's maintenance seam.
|
|
60
|
+
A timeout requests cooperative cancellation, not forced quiescence. Summarization
|
|
61
|
+
may incur model cost and is not guaranteed lossless.
|
|
62
|
+
|
|
63
|
+
## 内部分发
|
|
64
|
+
|
|
65
|
+
宿主 Web module discovery 只接受 package-root row,不接受 `dsh-ops/*` 子入口。
|
|
66
|
+
因此此组件是 `packages/auto-compact/` 内的伴随包,声明 file dependency 并通过
|
|
67
|
+
`bundleDependencies` 随主包携带,无单独 npm 发布或安装要求。旧三入口不变,列表
|
|
68
|
+
仍为四组件。宿主从所选 bundle 的 dependency closure 解析这个嵌套依赖。
|
|
69
|
+
`npm pack` 后在隔离目录离线安装,已从主包锚点确认宿主入口/浏览器入口可解析;
|
|
70
|
+
从 consumer 根直接 import 嵌套伴随包失败是预期的 npm 非提升布局,不假称根可解析。
|
|
71
|
+
|
|
72
|
+
## 验证记录
|
|
73
|
+
|
|
74
|
+
Target: DSH 0.2.0-rc.2. Runtime packages and vendored Rust are unchanged.
|
|
75
|
+
|
|
76
|
+
```console
|
|
77
|
+
node test/run.mjs auto-compact
|
|
78
|
+
node --check packages/auto-compact/lib/client.js
|
|
79
|
+
node --check packages/auto-compact/lib/index.js
|
|
80
|
+
git diff --check
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Targeted regressions cover schema rehydration with the real host library, real
|
|
84
|
+
Cordis mounting/live update/unloading, strict bounds and occupancy, cancellation,
|
|
85
|
+
serialization, no-op/self-replacement suppression, deadline/drain, errors and row
|
|
86
|
+
registration. The compaction backend and browser rendering are test seams, not a
|
|
87
|
+
real provider-backed Desktop E2E. Manual acceptance still required:
|
|
88
|
+
|
|
89
|
+
1. Install a local development artifact in an isolated profile, reload the app,
|
|
90
|
+
confirm four components and this row's configuration control.
|
|
91
|
+
2. Save 60%, reload page/app, confirm profile persistence; restore 50%.
|
|
92
|
+
3. Grow a conversation past the threshold, confirm one compaction after idle;
|
|
93
|
+
inspect its durable marker and summary; queue input during compaction.
|
|
94
|
+
4. Cancel a turn: no fresh automatic compaction. Disable during a summary: owned
|
|
95
|
+
abort/drain, no user/tool cancellation. Existing tools and background remain live.
|
|
96
|
+
5. Missing model capacity, busy/manual compaction, provider failure and persistence
|
|
97
|
+
failure: no spin/no duplicate summary; inspect warnings and original session log.
|
|
98
|
+
|
|
99
|
+
Full historical `npm run verify` is currently blocked by a pre-existing SVG-only
|
|
100
|
+
icon expectation while the package intentionally ships PNG. Running the old suites
|
|
101
|
+
also reveals earlier ladder/tool/metadata expectations incompatible with 0.2.4;
|
|
102
|
+
no unrelated legacy migration is included in this change.
|
|
@@ -4,13 +4,17 @@
|
|
|
4
4
|
|
|
5
5
|
## 工具发布与提示词
|
|
6
6
|
|
|
7
|
-
-
|
|
8
|
-
- `
|
|
9
|
-
- `enableShellTools
|
|
7
|
+
- 组件列表恰为三个:shell/file 默认开启,background 默认关闭。分别核验组件名称、中文介绍、图标,以及实际版本。
|
|
8
|
+
- `workspace-write`、`read-only`:只有开启的四个 ops 文件工具;没有 ops_bash/后台工具,提示词不广告不存在的命令。
|
|
9
|
+
- `danger-full-access`:默认四个文件工具 + ops_bash;仅后台组件开且 enableShellTools=true 时增加四个后台工具。任何组合都不发布 ops_run。
|
|
10
|
+
- 文件与后台共享一个 FastCtx;关文件仍能管理后台,关后台仍能读搜改,重开不积累注册。
|
|
11
|
+
- `enableShellTools: false`、无 sandboxPolicy 服务、无会话:后台组不出现。
|
|
10
12
|
- 同时开受限会话与完全权限会话:两者目录互不污染。受限子 agent 不继承祖先的命令组。
|
|
11
13
|
- 同一会话切换受限 → 完全权限 → 受限:每次下一请求目录/提示词跟随变化,不重连 FastCtx;旧命令句柄不能继续调用。
|
|
12
14
|
- 受限会话不发布 `ops_bash`;完全权限 + publishBashTool=true + bash 可解析 + subprocess 可用时出现,通用命令优先走它。FastCtx 缺失或 enableShellTools=false 时 bash 层仍独立可用。
|
|
13
|
-
-
|
|
15
|
+
- 提示词按组件分段 `dsh-ops:repository-tooling:{file,shell,background}`:开关只移除对应段;bash/pwsh 路由仅 shell 开时出现,后台关闭不出现后台指导。不写 AGENTS.md,没有旧 host-shell 段或 mcp:fastctx。
|
|
16
|
+
- shell 开时宿主 pwsh-sandbox 采用随包 pwsh7,关时恢复最新宿主原配置;profile 文件不得被改动。开关期间用户更改执行器原配置后也应恢复新值。
|
|
17
|
+
- 极简预设 complete persona 可排除附加提示;PTC 通过 SDK 使用同一工具层,权限/组件开关仍应生效。
|
|
14
18
|
- publishBashTool=false 隐去 bash 这一层;bash 权限动态切换、受限子 agent 继承过滤和卸载清理一起验证。
|
|
15
19
|
- `promptPolicy: false`:不发布路由提示词。
|
|
16
20
|
- `deny-host-shell`:有无 ops 命令组都应拒绝配置的宿主 shell;若工具来自继承层,目录也应遮蔽。无可用执行器时不推荐不可调用的 ops 命令。
|
|
@@ -35,6 +39,7 @@
|
|
|
35
39
|
- 完成/kill 后仍能读取自己已有 ID 的日志。
|
|
36
40
|
- job_list status/limit/offset 是自己的分页,不暴露全局总数/offset;空列表明确为空。
|
|
37
41
|
- 大全局 job 存储扫描不完整时必须报告 Partial,不冒充完整;原工具结果的 Complete/Partial 续页提示不能丢。
|
|
42
|
+
- 关闭后台组件/卸载:等待已发调用,再尝试终止当前组件已知的自有 ID;不能杀别的会话或组件任务。清理失败有告警。断线后已丢归属的任务不保证终止。
|
|
38
43
|
- 重连后不认领旧 ID;操作者可通过上游工具清理持久 job。权限降级不自动杀已运行任务,须操作者管理。
|
|
39
44
|
|
|
40
45
|
## 生命周期与边界
|
|
@@ -42,7 +47,7 @@
|
|
|
42
47
|
- 服务端断线立即撤下工具与对应提示词项,恢复后重新出现。
|
|
43
48
|
- 卸载后工具/提示词/限制全部消失;共享注册表方法未改写,外来同名工具仍可按自己的 scope 注册。
|
|
44
49
|
- 未知配置键报错点名;required=true 的缺失/不可用二进制拒绝激活。
|
|
45
|
-
- 取消/传输超时只表明停止等待,不把它误读为命令已停止。
|
|
50
|
+
- 取消/传输超时只表明停止等待,不把它误读为命令已停止。ops_bash 的宿主执行器截止时间独立于 MCP 等待超时。
|
|
46
51
|
|
|
47
52
|
## 数字复核
|
|
48
53
|
|
package/docs/schema-current.json
CHANGED
|
@@ -38,11 +38,6 @@
|
|
|
38
38
|
"bytes": 1511,
|
|
39
39
|
"approximateTokens": 378
|
|
40
40
|
},
|
|
41
|
-
{
|
|
42
|
-
"name": "ops_run",
|
|
43
|
-
"bytes": 751,
|
|
44
|
-
"approximateTokens": 188
|
|
45
|
-
},
|
|
46
41
|
{
|
|
47
42
|
"name": "ops_run_background",
|
|
48
43
|
"bytes": 635,
|
|
@@ -50,28 +45,40 @@
|
|
|
50
45
|
}
|
|
51
46
|
],
|
|
52
47
|
"total": {
|
|
53
|
-
"bytes":
|
|
54
|
-
"approximateTokens":
|
|
48
|
+
"bytes": 8988,
|
|
49
|
+
"approximateTokens": 2247
|
|
55
50
|
},
|
|
56
51
|
"fileOnly": {
|
|
57
52
|
"bytes": 6724,
|
|
58
53
|
"approximateTokens": 1681
|
|
59
54
|
},
|
|
60
55
|
"fileOnlyPrompt": {
|
|
61
|
-
"bytes":
|
|
62
|
-
"approximateTokens":
|
|
56
|
+
"bytes": 498,
|
|
57
|
+
"approximateTokens": 125
|
|
63
58
|
},
|
|
64
59
|
"bashSchema": {
|
|
65
60
|
"bytes": 961,
|
|
66
61
|
"approximateTokens": 241
|
|
67
62
|
},
|
|
63
|
+
"defaultWithBash": {
|
|
64
|
+
"bytes": 7685,
|
|
65
|
+
"approximateTokens": 1922
|
|
66
|
+
},
|
|
68
67
|
"fullWithBash": {
|
|
69
|
-
"bytes":
|
|
70
|
-
"approximateTokens":
|
|
68
|
+
"bytes": 9949,
|
|
69
|
+
"approximateTokens": 2488
|
|
70
|
+
},
|
|
71
|
+
"shellPrompt": {
|
|
72
|
+
"bytes": 492,
|
|
73
|
+
"approximateTokens": 123
|
|
74
|
+
},
|
|
75
|
+
"backgroundPrompt": {
|
|
76
|
+
"bytes": 269,
|
|
77
|
+
"approximateTokens": 68
|
|
71
78
|
},
|
|
72
79
|
"toolingPrompt": {
|
|
73
|
-
"bytes":
|
|
74
|
-
"approximateTokens":
|
|
80
|
+
"bytes": 878,
|
|
81
|
+
"approximateTokens": 220
|
|
75
82
|
},
|
|
76
83
|
"publishedServerInstructions": {
|
|
77
84
|
"bytes": 0,
|
|
@@ -1,17 +1,37 @@
|
|
|
1
1
|
# Schema measurement
|
|
2
2
|
|
|
3
|
-
Manual measurement only; not
|
|
3
|
+
Manual measurement only; not a tokenizer, CI, or proof of whole-task savings.
|
|
4
|
+
Metric: compact UTF-8 JSON `{name,description,parameters}` bytes, tokens ≈ ceil(bytes/4).
|
|
5
|
+
Runtime is FastCtx 0.2.6; only plugin publication/presentation changes.
|
|
4
6
|
|
|
5
|
-
|
|
7
|
+
## 0.2.4 component surface
|
|
6
8
|
|
|
7
|
-
|
|
|
9
|
+
| Enabled components | Tools | Schema bytes | Approximate tokens |
|
|
8
10
|
| --- | ---: | ---: | ---: |
|
|
9
|
-
|
|
|
10
|
-
|
|
|
11
|
-
|
|
|
11
|
+
| File only | 4 | 6,724 | 1,681 |
|
|
12
|
+
| Shell + file (default, full access) | 5 | 7,685 | 1,922 |
|
|
13
|
+
| Shell + file + background (full access) | 9 | 9,949 | 2,488 |
|
|
14
|
+
| 0.2.3 full surface (historical) | 10 | 10,700 | 2,675 |
|
|
12
15
|
|
|
13
|
-
|
|
16
|
+
Default reduces plugin schema bytes by 28.2% versus 0.2.3. Background contributes
|
|
17
|
+
2,264 bytes only when enabled; duplicate ops_run (751 bytes) is no longer published.
|
|
18
|
+
The host tool declarations remain additive and are not included in this table.
|
|
19
|
+
Restricted sessions have no ops_bash or background command tools.
|
|
14
20
|
|
|
15
|
-
|
|
21
|
+
Prompt sections are component-owned and independently removed: file 498 bytes,
|
|
22
|
+
shell 492 bytes, background 269 bytes. These sum when multiple components are on;
|
|
23
|
+
`toolingPrompt` in the snapshot is the legacy combined CLI renderer, not the actual
|
|
24
|
+
sum of separately registered sections. Minimal's complete persona can exclude them.
|
|
25
|
+
No raw server instructions or old host-shell policy segment is published.
|
|
16
26
|
|
|
17
|
-
|
|
27
|
+
## Historical comparison
|
|
28
|
+
|
|
29
|
+
The unchanged baseline is dee3c57 before description compression: nine FastCtx
|
|
30
|
+
schemas 18,889 bytes; four file schemas 12,807. File descriptions remain 6,724 bytes
|
|
31
|
+
(47.5% reduction). ops_grep: 3,766 → 2,019; inspect: 4,375 → 1,968. These are plugin
|
|
32
|
+
old/new comparisons, NOT official-host versus plugin comparisons.
|
|
33
|
+
|
|
34
|
+
Reproduce with `node scripts/measure-schemas.mjs docs/schema-current.json`.
|
|
35
|
+
It initializes and lists schemas without tool execution. Preserve baseline JSON.
|
|
36
|
+
Actual request count, cache pricing, tool output and repeated history can outweigh
|
|
37
|
+
fixed schema savings; no percentage here predicts task-total provider tokens.
|
package/dsh-plugin.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"manifestVersion": "0.15",
|
|
4
4
|
"id": "dsh-ops.fastctx",
|
|
5
5
|
"name": "dsh-ops",
|
|
6
|
-
"version": "0.2.
|
|
6
|
+
"version": "0.2.5-beta.1",
|
|
7
7
|
"license": "MIT AND Apache-2.0",
|
|
8
8
|
"facets": {
|
|
9
9
|
"host": {
|
|
@@ -50,9 +50,19 @@
|
|
|
50
50
|
],
|
|
51
51
|
"x-prompt-sections": [
|
|
52
52
|
{
|
|
53
|
-
"name": "dsh-ops:repository-tooling",
|
|
53
|
+
"name": "dsh-ops:repository-tooling:file",
|
|
54
54
|
"order": 3250,
|
|
55
|
-
"summary": "
|
|
55
|
+
"summary": "File operations only; follows the file component visibility."
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
"name": "dsh-ops:repository-tooling:shell",
|
|
59
|
+
"order": 3251,
|
|
60
|
+
"summary": "Prefer bash, use bundled PowerShell 7 for Windows-native functionality; removed when shell component is off."
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
"name": "dsh-ops:repository-tooling:background",
|
|
64
|
+
"order": 3252,
|
|
65
|
+
"summary": "Owned background tasks only; absent by default."
|
|
56
66
|
}
|
|
57
67
|
],
|
|
58
68
|
"x-tool-namespaces": [
|
|
@@ -66,21 +76,44 @@
|
|
|
66
76
|
"grep",
|
|
67
77
|
"glob",
|
|
68
78
|
"replace",
|
|
69
|
-
"run",
|
|
70
79
|
"run_background",
|
|
71
80
|
"job_output",
|
|
72
81
|
"job_list",
|
|
73
82
|
"job_kill"
|
|
74
83
|
],
|
|
75
|
-
"shellPermission": "enableShellTools AND per-session sandboxPolicy danger-full-access"
|
|
84
|
+
"shellPermission": "background component enabled AND enableShellTools AND per-session sandboxPolicy danger-full-access"
|
|
76
85
|
},
|
|
77
86
|
{
|
|
78
87
|
"serverName": "dsh-ops",
|
|
79
88
|
"transport": "in-process",
|
|
80
89
|
"source": "the plugin's bash layer via host subprocess",
|
|
81
90
|
"publicPrefix": "ops_",
|
|
82
|
-
"tools": [
|
|
83
|
-
|
|
91
|
+
"tools": [
|
|
92
|
+
"bash"
|
|
93
|
+
],
|
|
94
|
+
"shellPermission": "shell component enabled AND publishBashTool AND per-session sandboxPolicy danger-full-access"
|
|
95
|
+
}
|
|
96
|
+
],
|
|
97
|
+
"x-components": [
|
|
98
|
+
{
|
|
99
|
+
"id": "dsh-ops-shell",
|
|
100
|
+
"entry": "dsh-ops/shell",
|
|
101
|
+
"defaultEnabled": true
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
"id": "dsh-ops-file",
|
|
105
|
+
"entry": "dsh-ops/file",
|
|
106
|
+
"defaultEnabled": true
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
"id": "dsh-ops-background",
|
|
110
|
+
"entry": "dsh-ops/background",
|
|
111
|
+
"defaultEnabled": false
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
"id": "dsh-ops-auto-compact",
|
|
115
|
+
"entry": "@dsh-ops/auto-compact",
|
|
116
|
+
"defaultEnabled": true
|
|
84
117
|
}
|
|
85
118
|
]
|
|
86
119
|
},
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/lib/index.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
/** dsh-ops host half. Own registrations only; no @deepseek-ai value imports. */
|
|
2
2
|
import { PACKAGE_ROOT, probeBinary, resolveBinary } from './binary.js'
|
|
3
3
|
import { resolveConfig } from './config.js'
|
|
4
|
-
import { SECTION_ORDERS, TOOLING_SECTION, hostShellRefusal, renderToolingPolicy } from './policy.js'
|
|
4
|
+
import { SECTION_ORDERS, TOOLING_SECTION, BACKGROUND_TOOLS, FILE_TOOLS, BASH_TOOL, publicToolName, hostShellRefusal, renderToolingPolicy } from './policy.js'
|
|
5
5
|
import { resolveShells } from './shells.js'
|
|
6
6
|
import { mountSessionBash } from './session-shells.js'
|
|
7
|
+
import { mountBundledPwsh } from './session-pwsh.js'
|
|
7
8
|
import { FastCtxTools } from './tools.js'
|
|
8
9
|
|
|
9
10
|
export const name = 'dsh-ops'
|
|
@@ -54,30 +55,34 @@ function installHostShellDenial(ctx, config) {
|
|
|
54
55
|
}
|
|
55
56
|
}
|
|
56
57
|
|
|
57
|
-
async function mountServer(ctx, config, runtime) {
|
|
58
|
+
async function mountServer(ctx, config, runtime, component) {
|
|
58
59
|
if (!runtime) return
|
|
59
|
-
const
|
|
60
|
+
const background = component === 'background'
|
|
61
|
+
const surface = new FastCtxTools({ ctx, config, runtime,
|
|
62
|
+
fileTools: !background, commandTools: background ? BACKGROUND_TOOLS : [], shared: true,
|
|
63
|
+
report: (message, level) => report(ctx, message, level) })
|
|
60
64
|
ctx.effect(() => () => surface.stop(), 'dsh-ops: fastctx server')
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
surface.refreshAgents()
|
|
72
|
-
authorityCtx.effect(() => () => {
|
|
73
|
-
surface.authorityAvailable = false
|
|
65
|
+
if (background) {
|
|
66
|
+
ctx.on('agent/created', ({ agent }) => surface.attachAgent(agent))
|
|
67
|
+
ctx.on('agent/disposed', ({ agent }) => surface.detachAgent(agent))
|
|
68
|
+
ctx.on('session/event', (_session, event) => {
|
|
69
|
+
if (event.type === 'sandbox/mode') surface.refreshAgents()
|
|
70
|
+
})
|
|
71
|
+
// Background command layers follow authoritative per-session permission.
|
|
72
|
+
surface.authorityAvailable = false
|
|
73
|
+
ctx.inject(['sandboxPolicy'], authorityCtx => {
|
|
74
|
+
surface.authorityAvailable = true
|
|
74
75
|
surface.refreshAgents()
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
76
|
+
authorityCtx.effect(() => () => {
|
|
77
|
+
surface.authorityAvailable = false
|
|
78
|
+
surface.refreshAgents()
|
|
79
|
+
}, 'dsh-ops: permission authority')
|
|
80
|
+
})
|
|
81
|
+
for (const agent of ctx.get('agents')?.list?.() ?? []) surface.attachAgent(agent)
|
|
82
|
+
}
|
|
78
83
|
try {
|
|
79
84
|
const tools = await surface.start()
|
|
80
|
-
report(ctx, `dsh-ops: FastCtx ${runtime.version} ready; ${tools.length}
|
|
85
|
+
report(ctx, `dsh-ops: FastCtx ${runtime.version} ready; ${component} component, ${tools.length} global tools.`, 'info')
|
|
81
86
|
} catch (error) {
|
|
82
87
|
report(ctx, `dsh-ops: FastCtx unavailable: ${error.message}`)
|
|
83
88
|
if (config.required) throw error
|
|
@@ -85,24 +90,33 @@ async function mountServer(ctx, config, runtime) {
|
|
|
85
90
|
}
|
|
86
91
|
}
|
|
87
92
|
|
|
88
|
-
export function
|
|
93
|
+
export function applyComponent(root, rawConfig, component) {
|
|
89
94
|
const config = resolveConfig(rawConfig)
|
|
90
|
-
const runtime = prepareRuntime(root, config)
|
|
91
|
-
const shells = resolveShells(config)
|
|
95
|
+
const runtime = component === 'shell' ? undefined : prepareRuntime(root, config)
|
|
96
|
+
const shells = component === 'shell' ? resolveShells(config) : undefined
|
|
97
|
+
const owned = component === 'shell' ? [BASH_TOOL, 'pwsh']
|
|
98
|
+
: component === 'file' ? FILE_TOOLS.map(publicToolName) : BACKGROUND_TOOLS.map(publicToolName)
|
|
99
|
+
if (component === 'file') owned.push('read_image')
|
|
92
100
|
if (config.promptPolicy) root.inject(['systemPrompt'], ctx => {
|
|
93
101
|
ctx.effect(() => ctx.systemPrompt.section({
|
|
94
|
-
name: TOOLING_SECTION
|
|
95
|
-
order: SECTION_ORDERS[TOOLING_SECTION],
|
|
102
|
+
name: `${TOOLING_SECTION}:${component}`,
|
|
103
|
+
order: SECTION_ORDERS[TOOLING_SECTION] + (component === 'shell' ? 1 : component === 'background' ? 2 : 0),
|
|
96
104
|
interpolate: false,
|
|
97
105
|
text: context => renderToolingPolicy({
|
|
98
|
-
published: visibleTools(ctx, context.agent)
|
|
106
|
+
published: visibleTools(ctx, context.agent).filter(name => owned.includes(name)),
|
|
107
|
+
shellComponent: component === 'shell', pwsh7: shells?.pwsh.available ?? false,
|
|
99
108
|
extraGuidance: config.extraGuidance,
|
|
100
109
|
}),
|
|
101
|
-
}),
|
|
102
|
-
})
|
|
103
|
-
root.inject(['tools'], ctx => {
|
|
104
|
-
mountSessionBash(ctx, config, shells)
|
|
105
|
-
if (config.shellPolicy === 'deny-host-shell') installHostShellDenial(ctx, config)
|
|
106
|
-
return mountServer(ctx, config, runtime)
|
|
110
|
+
}), `dsh-ops: ${component} tooling`)
|
|
107
111
|
})
|
|
112
|
+
if (component === 'shell') {
|
|
113
|
+
mountBundledPwsh(root, shells)
|
|
114
|
+
root.inject(['tools'], ctx => {
|
|
115
|
+
mountSessionBash(ctx, config, shells)
|
|
116
|
+
if (config.shellPolicy === 'deny-host-shell') installHostShellDenial(ctx, config)
|
|
117
|
+
})
|
|
118
|
+
} else root.inject(['tools'], ctx => mountServer(ctx, config, runtime, component))
|
|
108
119
|
}
|
|
120
|
+
|
|
121
|
+
// Backward-compatible module entry. The bundle uses independently toggled rows.
|
|
122
|
+
export function apply(root, rawConfig) { return applyComponent(root, rawConfig, 'file') }
|
package/lib/policy.js
CHANGED
|
@@ -5,7 +5,8 @@ export const HOST_SHELL_SECTION = 'dsh-ops:host-shell-policy'
|
|
|
5
5
|
export const SECTION_ORDERS = Object.freeze({ [TOOLING_SECTION]: 3250 })
|
|
6
6
|
export const TOOL_PREFIX = 'ops_'
|
|
7
7
|
export const FILE_TOOLS = Object.freeze(['inspect_local_file', 'grep', 'glob', 'replace'])
|
|
8
|
-
export const
|
|
8
|
+
export const BACKGROUND_TOOLS = Object.freeze(['run_background', 'job_output', 'job_list', 'job_kill'])
|
|
9
|
+
export const SHELL_TOOLS = Object.freeze(['run', ...BACKGROUND_TOOLS])
|
|
9
10
|
// The plugin's second layer executes POSIX commands; pwsh7 uses the host tool.
|
|
10
11
|
export const IN_PROCESS_TOOLS = Object.freeze(['bash'])
|
|
11
12
|
export function publicToolName(rawName) { return `${TOOL_PREFIX}${rawName}` }
|
|
@@ -23,17 +24,24 @@ export function ladderLevels({ published, pwsh = false }) {
|
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
/** A single compact table; absent tools never get advertised. */
|
|
26
|
-
export function renderToolingPolicy({ published, enableShellTools, mounted, levels, pwsh7 = false, extraGuidance = '' }) {
|
|
27
|
+
export function renderToolingPolicy({ published, enableShellTools, mounted, levels, pwsh7 = false, shellComponent = true, extraGuidance = '' }) {
|
|
27
28
|
const names = new Set(published ?? (mounted || levels?.fastctx
|
|
28
29
|
? [...FILE_TOOLS, ...(enableShellTools ? SHELL_TOOLS : []), ...(levels?.bash ? IN_PROCESS_TOOLS : [])].map(publicToolName) : []))
|
|
29
30
|
if (levels?.pwsh) names.add('pwsh')
|
|
30
|
-
if (!
|
|
31
|
-
const
|
|
31
|
+
if (!shellComponent) names.delete('pwsh')
|
|
32
|
+
const hasFiles = FILE_TOOLS.some(name => names.has(publicToolName(name)))
|
|
33
|
+
const hasBash = names.has(BASH_TOOL)
|
|
34
|
+
const hasBackground = names.has(publicToolName('run_background'))
|
|
35
|
+
const hasPwsh = shellComponent && names.has('pwsh')
|
|
36
|
+
if (!hasFiles && !hasBash && !hasBackground && !hasPwsh) return ''
|
|
37
|
+
const heading = hasBash ? '# Shell routing: bash → PowerShell 7'
|
|
38
|
+
: hasFiles ? '# Repository file tools' : hasBackground ? '# Background task tools' : '# Windows-native shell'
|
|
39
|
+
const lines = [heading, '', '| Task | Tool |', '| --- | --- |']
|
|
32
40
|
const row = (raw, task) => {
|
|
33
41
|
if (names.has(publicToolName(raw))) lines.push(`| ${task} | ${publicToolName(raw)} |`)
|
|
34
42
|
}
|
|
35
43
|
row('inspect_local_file', 'Text / multiple ranges / encoding / PDF text / hex')
|
|
36
|
-
lines.push('| Images | read_image (host) |')
|
|
44
|
+
if (hasFiles && names.has('read_image')) lines.push('| Images | read_image (host) |')
|
|
37
45
|
row('grep', 'Search contents')
|
|
38
46
|
row('glob', 'Find paths')
|
|
39
47
|
row('replace', 'Batch replacement; use host edit for precise edits')
|
|
@@ -41,10 +49,10 @@ export function renderToolingPolicy({ published, enableShellTools, mounted, leve
|
|
|
41
49
|
if (names.has('pwsh')) lines.push(`| Windows-native cmdlets / registry / services only | pwsh (${pwsh7 || levels?.pwsh ? 'bundled PowerShell 7 via host' : 'host PowerShell'}) |`)
|
|
42
50
|
row('run', 'Bounded bash command result (full access only)')
|
|
43
51
|
if (names.has(publicToolName('run_background'))) {
|
|
44
|
-
lines.push(`| Background commands / owned jobs | ${
|
|
52
|
+
lines.push(`| Background commands / owned jobs | ${BACKGROUND_TOOLS.map(publicToolName).filter(name => names.has(name)).join(', ')} |`)
|
|
45
53
|
}
|
|
46
|
-
lines.push('', '- ops_* arguments are strict: pass only declared fields.'
|
|
47
|
-
|
|
54
|
+
lines.push('', '- ops_* arguments are strict: pass only declared fields.')
|
|
55
|
+
if (hasFiles) lines.push('- Use file tools for file operations; use count for totals and batch independent requests.',
|
|
48
56
|
'- On a tool error, correct the arguments and retry; do not switch to a shell workaround.')
|
|
49
57
|
if (names.has(BASH_TOOL)) lines.push('- Prefer bash for command execution. Use PowerShell only when Windows-native functionality is necessary; fix bash errors in bash, do not switch shells or mix their syntax.')
|
|
50
58
|
if (names.has(publicToolName('run_background'))) lines.push('- Only operate on job IDs you started in this session.')
|
package/lib/profile-install.js
CHANGED
|
@@ -50,7 +50,7 @@ export function profileCommand(operation, { profile, dshCli, dryRun = false }) {
|
|
|
50
50
|
if (/\.cmd$/i.test(command)) {
|
|
51
51
|
// cmd wrappers are Windows-owned launchers; reject shell metacharacters in caller-supplied paths.
|
|
52
52
|
if (/["\r\n&|<>^%!]/.test(command)) throw new Error('Unsafe CLI path; use a path without cmd metacharacters.')
|
|
53
|
-
result = spawnSync(process.env.ComSpec ?? 'cmd.exe', ['/d', '/s', '/c', `""${command}" ${args.join(' ')}"`], { stdio: 'inherit', windowsHide: true })
|
|
53
|
+
result = spawnSync(process.env.ComSpec ?? 'cmd.exe', ['/d', '/s', '/c', `""${command}" ${args.join(' ')}"`], { stdio: 'inherit', windowsHide: true, windowsVerbatimArguments: true })
|
|
54
54
|
} else result = spawnSync(command, [...prefix, ...args], { stdio: 'inherit', windowsHide: true })
|
|
55
55
|
if (result.error) throw result.error
|
|
56
56
|
if (result.status !== 0) return result.status ?? 1
|