dsh-punky-swarm 0.4.0 → 0.4.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 CHANGED
@@ -1,3 +1,29 @@
1
+ ## 0.4.1(2026-09-05)
2
+
3
+ ### 治理预设规则包
4
+
5
+ - 出厂护栏规则预设随包发布(presets/hook-rules):l1-sensitive(L1 敏感数据防护 12 条)、l2-resource(L2 资源上限 6 条)、compose(L1+L2 全量 18 条),wrapper 结构(`_meta` 元数据 + `rules` 数组),规则字段与引擎 Rule 类型逐字段对齐、零扩展字段。
6
+ - preset 装载与引用:装载器剥离 `_meta` 取 rules 并做受控资产早失败校验;`governance.hook.preset` 支持注册 id / id 数组引用(如 `"preset": "compose"` 或 `["l1-sensitive","l2-resource"]`),跨 preset 规则 id 全局唯一性校验拒绝重复。
7
+
8
+ ### Web UI 治理配置页 + runtime.json 写通道
9
+
10
+ - 治理配置设置页(Web UI 设置区):护栏开关、规则预设、违规自动升级(触发次数 / 窗口)可视化配置;页面保存即时生效、无需重启。
11
+ - runtime.json 热写通道:保存请求经 config-trust 校验(顶层白名单 / 值域 / preset 与内联规则冲突守卫)后落盘 runtime.json,400 校验拒绝不回写;窗口秒输入后端毫秒归一化(windowSeconds → windowMs,线协议键不落盘)。
12
+ - 随包双语主题文档:docs/webui-governance-config(.en).md。
13
+
14
+ ### lane_longrun 超时无进展探针
15
+
16
+ - watch 长跑档(默认开启):running lane 运行超时且长期无 checkpoint / 活动进展 → 产候选并广播给 Manager(探针只产候选,不改成员状态),重派裁决归 Manager / Leader。
17
+ - 与心跳 stalled 档并列扫描;事件留痕可审计。
18
+
19
+ ### Web UI 修复
20
+
21
+ - 治理配置页 UI 修复:重命名、preset 多选、放大字号、移除全组合提示。
22
+
23
+ ### 发布整理
24
+
25
+ - 版本 0.4.0 → 0.4.1;根 README(GitHub 面)精简:170 → ≈100 行,中文 / 英文 1:1 同构重写,去除过期版本与测试数(实测刷新 816);删除 README.market.md(人话版内容并入精简后根 README 机制表与能力段)。
26
+
1
27
  ## 0.4.0(2026-09-03)
2
28
 
3
29
  ### 工具调用级治理护栏
package/cordis.patch.yml CHANGED
@@ -31,8 +31,17 @@
31
31
  # intervalsMinutes 退避档位(分钟,默认 [10,20,30],冷场越久追问间隔越长);maxMissed 硬停拍数
32
32
  # (默认 3,连续 N 拍无活动 → appendEvent('lane.stalled') 停止追问,只标记不自动处置);
33
33
  # scanIntervalMinutes watchdog 扫描间隔(默认 1);probeTemplate 可选覆写追问模板({lane}/{batchId}/{missed} 占位)。
34
+ # longrun 子键(长跑档探针,出厂默认开——用户定案修订,非设计默认关):running lane 持续超
35
+ # maxDurationMs(默认 1200000=20min)且近 noProgressWindowMs(默认 300000=5min)无新 checkpoint
36
+ # 且无活动(严格 AND)→ 同 tick 产 lane.longrun.candidate 事件 + mailbox broadcast 候选通知
37
+ # Manager 裁决(探针只标记不改 lane 状态;lane_longrun 工具并列注册,enabled=false 不注册、
38
+ # tick 不判、零事件零消息零行为变化)。
34
39
  watch:
35
40
  enabled: true
41
+ longrun:
42
+ enabled: true # 出厂默认开(定案修订:显式 false 才关)
43
+ maxDurationMs: 1200000 # 长跑超时阈值(默认 20min;正整数 ms,非法回退默认)
44
+ noProgressWindowMs: 300000 # 无进展窗(默认 5min;正整数 ms,非法回退默认)
36
45
  # worktree 物理隔离(lane-tools):enabled=true 时注册 lane_worktree_create / lane_worktree_merge / lane_checkpoint
37
46
  # (git worktree 隔离 + checkpoint 提交;与 lane_claim 逻辑锁互补——lane_claim 管状态层「写谁的」,
38
47
  # 本组工具管工作区层「写到哪」;全能力默认开 → 工具总数 18(14+heartbeat+worktree 三件+log_export),回归已覆盖)。
@@ -70,6 +79,14 @@
70
79
  hook:
71
80
  enabled: true # 默认开启;空规则表 → decide 恒 ALLOW → 零行为变化
72
81
  rules: [] # 规则表(空=零拦截);Rule 结构见 lib/governance/types.ts
82
+ # M5-b preset 装载键(preset-build):引用随包预设(presets/hook-rules/ 三 JSON,注册 id 枚举:
83
+ # l1-sensitive / l2-resource / compose——不接受任意路径),保序展开拼接至 rules(inline rules 在后)。
84
+ # preset: 'l1-sensitive' 或 preset: ['l1-sensitive', 'l2-resource'](= compose 逐条等价,二选一互斥引用——
85
+ # 同批引用 compose + l1-sensitive 会产生重复 id → 装载失败回退空表 + warn,宁空勿半)。
86
+ # runtime.json 热更示例:{"governance":{"hook":{"preset":["l1-sensitive","l2-resource"]}}}(⑤ 通道即时生效)。
87
+ # 出厂不默认启用任何 preset(本键缺省 = 空表零拦截不变,与 rules:[] 出厂安全默认严格一致)——
88
+ # 需启用时经 runtime.json 下发或装配 config 注入;preset 文件内容随包版本发布生效(boot 装载一次)。
89
+ # preset: [] # ← 示例注释(默认不启用,勿取消注释即默认值)
73
90
  # M5-a:护栏违规计数升级(默认关——出厂零行为变化;开启后:归属批次的规则拒绝
74
91
  # (DENY/NARROW)在 windowMs 内达 threshold → 经棘轮校验批 paused(reason=governance-escalate);
75
92
  # 与 hook enabled:true 内核就位零拦截正交——出厂 rules:[] 本就零收据,关保持「改规则不意外武装暂停」保守面)
@@ -0,0 +1,61 @@
1
+ # Governance Configuration
2
+
3
+ > This guide covers the Governance page under Settings in the dsh Web UI: which guardrail options you can adjust, how changes take effect immediately after saving, and what this page deliberately does not do.
4
+ > 中文: [webui-governance-config.md](webui-governance-config.md)
5
+
6
+ ## What this page does
7
+
8
+ Governance is a dedicated page in the Settings area of the dsh Web UI. It manages a set of guardrail options for dsh running on this machine: the guardrails inspect the tool calls an Agent makes, detect out-of-bounds calls, and then allow, deny, or escalate them according to the rules you have chosen. Changes made on this page take effect **immediately after saving — no dsh restart needed**.
9
+
10
+ At the top of the page you can see the current state: whether the guardrails are live, and how many rules are currently in effect.
11
+
12
+ ## Configurable options
13
+
14
+ ### Guardrail switch
15
+
16
+ - **On**: the guardrails take part in checking, judging out-of-bounds calls against the rule set below.
17
+ - **Off**: the guardrails take no part at all and calls are not held back by this feature.
18
+ - **Factory default: on**. However, the factory rule set is empty, so nothing is actually intercepted (see "Behavior boundaries").
19
+
20
+ ### Rule preset
21
+
22
+ Pick a ready-made rule set to quickly enable a group of out-of-bounds protections:
23
+
24
+ | Option | Number of rules | Purpose |
25
+ |---|---|---|
26
+ | Factory default (no interception) | 0 | No rules enabled; guardrails on but nothing is held back |
27
+ | Sensitive-data guard | 12 | For out-of-bounds calls involving sensitive data such as credentials and private keys |
28
+ | Resource limits | 6 | For calls that exceed resource ceilings such as timeout and concurrency |
29
+ | Combination (L1 + L2) | 18 | The full combination of the two rule sets above |
30
+
31
+ Once you select a preset, the page shows the **count and a purpose summary** for that rule set. Switching presets does not change the running state by itself — it takes effect when you click Save.
32
+
33
+ ### Auto-escalation
34
+
35
+ Off by default. When enabled, once the rules are refused a set number of times within a time window for the same batch of tasks, the affected batch is escalated automatically (the related work is paused and a record is kept, waiting for you to review and continue).
36
+
37
+ - **Refusals within window**: how many rule refusals within one time window trigger escalation (minimum 1).
38
+ - **Window (ms)**: the length of the counting window (minimum 1000).
39
+ - **Counted verdicts**: "Deny" and "Narrowed allowance" are counted by default; "Defer" and "Pause" can be added as needed; verdicts that require human approval are not on this list.
40
+
41
+ ### Narrowed allowance
42
+
43
+ Off by default. When enabled, calls that go beyond the allowed bounds are no longer denied outright; instead the guardrails give narrowing guidance so the caller can retry with narrowed parameters. When disabled, such calls are denied outright.
44
+
45
+ ## Saving and activation
46
+
47
+ - **Save**: after clicking Save, the settings are written to the local configuration file and then applied to the running dsh immediately — no restart at any point. The page first shows "Saved, confirming…" and turns to "Live" once confirmed.
48
+ - **Reset**: discards your unsaved changes and returns to the state most recently loaded into the page.
49
+ - **Save rejected**: the page shows why the save was rejected; the common reasons are listed in "Behavior boundaries" below.
50
+
51
+ ## Behavior boundaries
52
+
53
+ - **A controlled form — no free-form rule editing**: this page offers only the switches, presets, and numeric options above; there is no entry point for editing rules one by one. Teams that need fully custom rules should maintain the rule list in the configuration file (see the technical manual linked below).
54
+ - **Factory default zero interception is unchanged**: a fresh install ships with the factory default — guardrails on but no rules loaded, so no call is intercepted and existing behavior is unaffected; interception only begins after you select a preset or configure rules manually.
55
+ - **Manual rules must be handled first when they conflict with presets**: if a hand-maintained rule list already exists in the configuration, switching presets is rejected (to avoid overwriting manual rules); remove those rules manually first, or keep the preset unchanged, then save.
56
+ - **Saving is accepted only from this machine (or trusted sources)**: this page only accepts save requests from a browser on this machine (a local address); if dsh is served through a remote address, the deployment must add the visiting source to the trust list (the dsh host and this plugin must stay consistent), otherwise saves are rejected.
57
+
58
+ ## Further reading
59
+
60
+ - [Call-level guardrails technical manual](guardrails-hook.en.md): guardrail mechanics, rule authoring, and runtime details (for maintainers and developers).
61
+ - [Batch governance technical manual](governance-technical.en.md): batches, gates, and state machine (for operators).
@@ -0,0 +1,61 @@
1
+ # 治理配置
2
+
3
+ > 本文介绍 dsh Web UI「设置 → 治理配置」页面:可以调整哪些护栏选项、保存后如何即时生效,以及本页面刻意不做的事。
4
+ > English: [webui-governance-config.en.md](webui-governance-config.en.md)
5
+
6
+ ## 这个页面是什么
7
+
8
+ 「治理配置」是 dsh Web UI 设置区的一个独立页面。它管理 dsh 在本机运行时的一组护栏选项:护栏会检查 Agent 的工具调用是否越界,再按你所选规则决定放行、拒绝或升级处理。页面上的改动保存后**立即生效,无需重启 dsh**。
9
+
10
+ 页面顶部显示当前状态:护栏是否已生效,以及当前生效的规则数量。
11
+
12
+ ## 可配置项
13
+
14
+ ### 护栏开关
15
+
16
+ - **打开**:护栏参与检查,依据下方规则集判断越界调用。
17
+ - **关闭**:护栏整体不参与,调用不被本功能拦阻。
18
+ - **出厂默认:打开**。但出厂规则为空,因此实际不拦截任何调用(见「行为边界」)。
19
+
20
+ ### 规则预设
21
+
22
+ 选择一个现成的规则集,快速启用一组越界防护:
23
+
24
+ | 选项 | 规则数量 | 用途 |
25
+ |---|---|---|
26
+ | 出厂空表(零拦截) | 0 | 不启用任何规则;护栏开启但不拦阻 |
27
+ | 敏感数据防护 | 12 | 针对凭据、私钥等敏感数据的越界调用 |
28
+ | 资源上限 | 6 | 针对超时、并发等资源使用上限的调用 |
29
+ | 组合(L1 + L2) | 18 | 以上两套规则集的完整组合 |
30
+
31
+ 选择某个预设后,页面显示该套规则的**数量与用途摘要**。切换预设本身不会立即改变运行状态,需要点击「保存」生效。
32
+
33
+ ### 违规自动升级
34
+
35
+ 出厂关闭。开启后,当同一批任务在设定时间窗口内的规则拒绝达到指定次数时,自动升级处置相关批次(相关工作会被自动暂停并留下记录,等待你检查后继续)。
36
+
37
+ - **窗口内触发次数**:一个时间窗口内触发多少次规则拒绝即升级(最小为 1)。
38
+ - **窗口(毫秒)**:计数的窗口长度(最小为 1000)。
39
+ - **计入的处理结果**:默认计入「拒绝」与「收窄放行」,可按需增选「延后」「暂停」;需要人工审批的处理结果不在此列。
40
+
41
+ ### 窄化放行
42
+
43
+ 出厂关闭。打开后,对超出允许范围的调用不再直接拒绝,而是给出收窄指引,让调用方按收窄后的参数重试放行;关闭时此类调用直接拒绝。
44
+
45
+ ## 保存与生效
46
+
47
+ - **保存**:点击后,设置先写入本机配置文件,再即时应用到运行中的 dsh,全程无需重启。页面先显示「已保存,生效确认中」,确认后转为「已生效」。
48
+ - **重置**:放弃本次未保存的修改,回到最近一次载入页面的状态。
49
+ - **保存被拒**:页面会显示被拒原因;常见原因见下节「行为边界」。
50
+
51
+ ## 行为边界
52
+
53
+ - **受控表单,不开放任意规则编辑**:本页面只提供上述开关、预设与数值选项,没有逐条编辑规则的入口。需要逐条定制规则的团队,请在配置文件中维护规则清单(写法见延伸阅读的技术手册)。
54
+ - **出厂默认零拦截不变**:全新安装即出厂空表——护栏开启但不加载任何规则、不拦截任何调用,既有行为不受影响;只有选择预设或手工配置规则后才会开始拦截。
55
+ - **预设与手工规则冲突时先手工处置**:若配置中已存在手工维护的规则清单,切换预设会被拒绝(避免覆盖手工规则);请先手工移除这些规则,或保持预设不变,再保存。
56
+ - **仅本机(或受信任来源)可保存**:本页面只接受来自本机浏览器(本地地址)的保存请求;若 dsh 通过远程地址对外提供服务,部署方须把访问来源加入信任名单(dsh 宿主与本插件保持一致),否则保存会被拒绝。
57
+
58
+ ## 延伸阅读
59
+
60
+ - [调用级护栏技术手册](guardrails-hook.md):护栏机制、规则写法与运行细节(面向维护者与开发者)。
61
+ - [批级治理技术手册](governance-technical.md):批次、门禁与状态机(面向 Leader 与运维)。
package/lib/api.js CHANGED
@@ -22,6 +22,9 @@ import * as mailbox from './comms/mailbox.js';
22
22
  import { createStreamHub } from './panel/stream.js';
23
23
  // P1 批 R-07 排除项(承接归 panel 批):事件读端字面量收敛——EVT 常量源 lib/state/event-types.js(P2-07 单点)
24
24
  import * as EVT from './state/event-types.js';
25
+ // WebUI 治理配置写通道(webui-config-build-20260903):/config 端点 trusted 判定(自复刻宿主 /api
26
+ // 护栏语义,conn:184-198 未导出故复刻——插件 exact 路由不经宿主护栏,写端点须自理,见 config-trust.js)
27
+ import { isTrustedConfigRequest } from './webui/config-trust.js';
25
28
 
26
29
  function sendJson(res, status, data) {
27
30
  const body = JSON.stringify(data);
@@ -217,6 +220,68 @@ export function createApi(ctx, deps) {
217
220
  });
218
221
  }
219
222
 
223
+ // WebUI 治理配置写通道(webui-config-build-20260903,设计 §1.1/§1.2,落点 = discover 段与 stream 段之间):
224
+ // GET + POST /api/dsh-punky-swarm/config —— 配置页页载取数 / 受控字段集保存(写 <root>/config/runtime.json)。
225
+ // 条件注册仿 discovery/agentCatalog(上方 :162-190 注入形态):deps.configEndpoints.runtimeConfig
226
+ // 注入时注册;未注入不注册 → 既有 7 路由/9 路由计数测试零回归(不改既有注册面,disposer 统一回收)。
227
+ // trusted 判定(GET/POST 共用,§1.3):Host loopback/trustedHosts + sec-fetch-site≠cross-site + Origin 同源
228
+ // (isTrustedConfigRequest,lib/webui/config-trust.js;trustedHosts 出厂 [] → loopback-only)。
229
+ // 写逻辑全在 service(lib/webui/runtime-config.js):白名单预检 400 → 读-改-写 → validateOverlay
230
+ // 兜底 500 → tmp+rename 原子写;GET 取数 overlay(磁盘原样)/applied(装配侧解析快照)/presets(注册目录)。
231
+ if (deps.configEndpoints?.runtimeConfig) {
232
+ const cfgEp = deps.configEndpoints;
233
+ const trustedHosts = Array.isArray(cfgEp.trustedHosts) ? cfgEp.trustedHosts : [];
234
+ register({
235
+ kind: 'exact',
236
+ path: '/api/dsh-punky-swarm/config',
237
+ handler(req, res) {
238
+ // trusted 护栏前置(GET/POST 共用;护栏语义非鉴权层、防 DNS-rebinding/跨站——§1.2)
239
+ if (!isTrustedConfigRequest(req, trustedHosts)) {
240
+ const body = req.method === 'POST' ? { ok: false, error: 'forbidden' } : { error: 'forbidden' };
241
+ return sendJson(res, 403, body);
242
+ }
243
+ if (req.method === 'GET') {
244
+ try {
245
+ const overlay = cfgEp.runtimeConfig.readOverlay();
246
+ const gov = overlay && typeof overlay === 'object' && !Array.isArray(overlay)
247
+ && overlay.governance && typeof overlay.governance === 'object' && !Array.isArray(overlay.governance)
248
+ ? overlay.governance : null;
249
+ const applied = typeof cfgEp.applied === 'function' ? cfgEp.applied() : null;
250
+ const presets = typeof cfgEp.presets === 'function' ? (cfgEp.presets() ?? []) : [];
251
+ // overlay = 磁盘 runtime.json governance 段原样(表单未保存改动基准;无 = null);
252
+ // applied = 装配侧已解析快照(默认补齐 + preset 展开 rules);presets = 注册目录元数据 [{id,count}]
253
+ sendJson(res, 200, { overlay: gov, applied: { hook: applied }, presets });
254
+ } catch (e) { sendJson(res, 500, { error: String(e?.message ?? e) }); }
255
+ return;
256
+ }
257
+ if (req.method === 'POST') {
258
+ let bodyPromise;
259
+ try {
260
+ bodyPromise = readJsonBody(req); // req.body 为 string 时 JSON.parse 同步抛 → 先包住归 400
261
+ } catch (e) {
262
+ return sendJson(res, 400, { ok: false, error: 'invalid-json: ' + String(e?.message ?? e) });
263
+ }
264
+ return bodyPromise.then((payload) => {
265
+ try {
266
+ const out = cfgEp.runtimeConfig.writeGovernance(payload);
267
+ if (!out.ok) {
268
+ if (out.status === 500 || !Array.isArray(out.errors)) {
269
+ return sendJson(res, out.status || 500, { ok: false, error: out.error ?? 'write-rejected' });
270
+ }
271
+ return sendJson(res, 400, { ok: false, errors: out.errors });
272
+ }
273
+ return sendJson(res, 200, { ok: true, written: out.written, ts: new Date().toISOString() });
274
+ } catch (e) {
275
+ // 读-改-写 IO 异常(坏 base JSON / rename 失败等)→ 500 不回写(设计 §1.5「不应发生」面)
276
+ return sendJson(res, 500, { ok: false, error: String(e?.message ?? e) });
277
+ }
278
+ }).catch((e) => sendJson(res, 400, { ok: false, error: 'invalid-json: ' + String(e?.message ?? e) }));
279
+ }
280
+ return sendJson(res, 405, { ok: false, error: 'method-not-allowed' });
281
+ },
282
+ });
283
+ }
284
+
220
285
  // R3 SSE 端点(设计 §3.3.3,纯新增路由——既有 /api 路由一字不动):
221
286
  // GET /api/dsh-punky-swarm/stream?session=<sid>[&batchId=<bid>]
222
287
  // SSE 帧协议:event: batch|mailbox|heartbeat + data:<JSON>;注释心跳帧每 10s(hub 内维护)。