dsh-punky-swarm 0.4.2 → 0.4.3
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 +28 -12
- package/cordis.patch.yml +15 -15
- package/docs/guardrails-hook.en.md +18 -3
- package/docs/guardrails-hook.md +18 -3
- package/lib/acps/certs.js +1 -1
- package/lib/acps/server.js +1 -1
- package/lib/aip/agent-descriptor.js +1 -1
- package/lib/aip/identity.js +1 -1
- package/lib/api.js +22 -22
- package/lib/artifact-types.js +1 -1
- package/lib/assembly/schema.js +5 -5
- package/lib/assembly.js +1 -1
- package/lib/bridge/dispatch-register.js +16 -16
- package/lib/bridge/trajectory.js +6 -6
- package/lib/client.js +9 -9
- package/lib/comms/aip-format.js +3 -3
- package/lib/comms/budget.js +3 -3
- package/lib/comms/topic-runtime.js +6 -6
- package/lib/comms/topic.js +5 -5
- package/lib/governance/classify.js +10 -10
- package/lib/governance/classify.ts +15 -15
- package/lib/governance/config.js +14 -14
- package/lib/governance/config.ts +20 -20
- package/lib/governance/decisions.js +1 -1
- package/lib/governance/decisions.ts +7 -7
- package/lib/governance/escalation.js +13 -13
- package/lib/governance/hash-utils.js +6 -6
- package/lib/governance/index.js +3 -3
- package/lib/governance/index.ts +3 -3
- package/lib/governance/kernel.js +8 -8
- package/lib/governance/kernel.ts +12 -12
- package/lib/governance/narrow.js +1 -1
- package/lib/governance/narrow.ts +3 -3
- package/lib/governance/preset-loader.js +3 -3
- package/lib/governance/preset-loader.ts +3 -3
- package/lib/governance/receipt-store.js +26 -26
- package/lib/governance/state-store.js +5 -5
- package/lib/governance/types.ts +27 -28
- package/lib/governance/wiring.js +165 -64
- package/lib/hot/config-watch.js +13 -13
- package/lib/index.js +98 -100
- package/lib/panel/gov-config.js +4 -4
- package/lib/panel/main.js +5 -5
- package/lib/panel/stream.js +11 -11
- package/lib/schema.js +3 -3
- package/lib/schema.ts +4 -4
- package/lib/state/archive.js +2 -2
- package/lib/state/command-exec.js +3 -3
- package/lib/state/constants.js +2 -2
- package/lib/state/corrupt-registry.js +2 -2
- package/lib/state/event-types.js +22 -23
- package/lib/state/gates.js +22 -22
- package/lib/state/gates.ts +22 -22
- package/lib/state/machine-rules.js +1 -1
- package/lib/state/machine-rules.ts +1 -1
- package/lib/state/machine.js +1 -1
- package/lib/state/resume.js +11 -11
- package/lib/state/schema-v3.ts +2 -2
- package/lib/state/store.js +46 -46
- package/lib/state/task-utils.js +1 -1
- package/lib/tools/core.js +7 -7
- package/lib/tools/git-utils.js +3 -3
- package/lib/tools/lane-tools.js +9 -9
- package/lib/tools/log-tools.js +6 -6
- package/lib/tools/mailbox-tools.js +4 -4
- package/lib/tools/register.js +5 -5
- package/lib/tools/shared.js +1 -1
- package/lib/types/contracts.d.ts +2 -2
- package/lib/types/contracts.ts +16 -18
- package/lib/verify/evidence.js +4 -4
- package/lib/verify/gate.js +3 -3
- package/lib/verify/mount.js +1 -1
- package/lib/verify/selector.js +2 -2
- package/lib/watch/lane-heartbeat.js +14 -14
- package/lib/wave-plan.js +14 -14
- package/lib/wave-plan.ts +14 -14
- package/lib/webui/config-trust.js +6 -6
- package/lib/webui/runtime-config.js +29 -30
- package/package.json +1 -1
- package/presets/hook-rules/README.md +32 -0
- package/presets/jiufeng/NOTICE +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,19 @@
|
|
|
1
|
+
## 0.4.3(2026-09-07)
|
|
2
|
+
|
|
3
|
+
### 调用级护栏拒绝可见性
|
|
4
|
+
|
|
5
|
+
- 护栏拦截明示:工具调用送入人工审批后被拒绝(含审批服务不可达、无可用 agent 等降级情形)时,Agent 收到的拒绝不再只是「用户拒绝了该工具」的泛化提示,而会携带护栏标注、命中规则、违规说明与查阅路径——Agent 与用户均可识别该拒绝源于护栏对疑似敏感数据调用的拦截,并可按违规说明修正参数后重发合规调用;非拦截场景行为保持不变。
|
|
6
|
+
- 拒绝文本携带命中规则:拒绝原因末尾追加命中规则及其预设归属(如 L1-A10 · preset l1-sensitive),人工审批请求与直接拒绝路径均携带,用户在批准/拒绝前即可核对所触发的具体规则。
|
|
7
|
+
- 行为说明同步:拒绝可见性语义(拦截明示与规则引用,含边界说明)随双语主题文档 guardrails-hook 同步更新。
|
|
8
|
+
|
|
9
|
+
### 护栏预设逐条审阅清单
|
|
10
|
+
|
|
11
|
+
- presets/hook-rules/README.md 新增逐条规则审阅清单:敏感数据防护 12 条与资源边界 6 条,按规则编号 / 预设归属 / 类别 / 生效原语 / 触发工具 / 匹配摘要 / 违规说明逐条成表,供用户与 Agent 主动审阅护栏规则全集;配套一致性断言守护清单与规则文件同步。
|
|
12
|
+
|
|
13
|
+
### 发布整理
|
|
14
|
+
|
|
15
|
+
- 版本 0.4.2 → 0.4.3;变更记录整理与发布前内部口径清查。
|
|
16
|
+
|
|
1
17
|
## 0.4.2(2026-09-06)
|
|
2
18
|
|
|
3
19
|
### 治理配置面板:Lane 过期检测与重派探针
|
|
@@ -29,7 +45,7 @@
|
|
|
29
45
|
### Web UI 治理配置页 + runtime.json 写通道
|
|
30
46
|
|
|
31
47
|
- 治理配置设置页(Web UI 设置区):护栏开关、规则预设、违规自动升级(触发次数 / 窗口)可视化配置;页面保存即时生效、无需重启。
|
|
32
|
-
- runtime.json 热写通道:保存请求经 config-trust 校验(顶层白名单 / 值域 / preset 与内联规则冲突守卫)后落盘 runtime.json,400
|
|
48
|
+
- runtime.json 热写通道:保存请求经 config-trust 校验(顶层白名单 / 值域 / preset 与内联规则冲突守卫)后落盘 runtime.json,400 校验拒绝不落盘;窗口秒输入后端毫秒归一化(windowSeconds → windowMs,线协议键不落盘)。
|
|
33
49
|
- 随包双语主题文档:docs/webui-governance-config(.en).md。
|
|
34
50
|
|
|
35
51
|
### lane_longrun 超时无进展探针
|
|
@@ -99,17 +115,17 @@
|
|
|
99
115
|
|
|
100
116
|
## 0.3.3(2026-08-22)
|
|
101
117
|
|
|
102
|
-
### 国标 AIP
|
|
118
|
+
### 国标 AIP 兼容契约对齐
|
|
103
119
|
|
|
104
|
-
- **aip.enabled 默认开启**(readCapability 合并 `{enabled:true}`);智能体描述改为 ACS
|
|
120
|
+
- **aip.enabled 默认开启**(readCapability 合并 `{enabled:true}`);智能体描述改为 ACS 字段集(根对象 20 键 必填 14/可选 6、AgentSkill 8 键,协议 02.01,旧 14+8 属性降级为 toLegacyDescriptor 兼容映射层);消息映射对齐 ACPs AIP(aip-format.js Message/TaskCommand/Session 三函数,mailbox/batch 附 ACPs 投影);身份体系(默认关):AIC 身份码(前缀 1.2.156.3088 + CRC-16/CCITT-FALSE + Base36)+ CAI 身份证书 + 可插拔签名(默认 ECDSA-P256/RSA-2048);发现服务(ADP,默认开):`lib/discovery/` 新域 + `POST /api/dsh-punky-swarm/discover`(type 四类/filter 34 运算符/错误码 40000~40005/50001)+ `GET /.well-known/aip`;工具描述 6 属性保持现状(待正式协议文本校准)。
|
|
105
121
|
|
|
106
|
-
### ACPs
|
|
122
|
+
### ACPs 通讯方式(默认关)
|
|
107
123
|
|
|
108
|
-
-
|
|
124
|
+
- **能力总开关默认关**:`acps.enabled` 与 `acps.endpoint.enabled` 均默认 `false`,关闭时零运行时路径;对外 mTLS 服务端点:独立 HTTPS 监听器(node:https/tls 原生、零新依赖),默认端口 9443/host 127.0.0.1、TLSv1.3 + 双向证书(CERT_REQUIRED)、端点 `POST /acps/rpc`(AIP JSON-RPC)+ `GET /.well-known/acs.json`(ACS 14 必填键 + mutualTLS + JSONRPC)+ `GET /health`,证书 CA 自签(CN=AIC/SAN=acps://AIC,默认 `<root>/acps/certs`);内部桥接(默认关):`acps.bridge`(同进程双向,inbound 默认关需显式 `acps.bridge.inbound=true`;outbound = mailbox→ACPs 投影/投递;`/rpc→bridge 接线` 已通,inbound=false 时协议级 rejected INBOUND_DISABLED);registry 对接(半自动注册,默认关):login→upsertAgent→submitAgent(人工工批不自动跳过)→requestEab→queryAcs,EAB macKey **AES-256-GCM 加密存证**(与参考实现 SM4-CBC 标注差异);discovery 对接(ADP 客户端,默认关):`POST {baseUrl}/discover` 查询外部 Agent(type 四类/34 运算符与本地共享协议常量),scope=local/external/both(默认 local);能力注册表扩至 9 键(aip/identity/discovery/verify/watch/worktree/budget/trajectory/**acps**,acps 与 identity 为默认关能力);未实现项如实标注(工具调用待正式协议文本校准;SM2 签名无参考证据可插拔;mini-ADSP 仅预留签名;与参考实现真实互通待 demo 验证)。
|
|
109
125
|
|
|
110
|
-
### 护栏根治 +
|
|
126
|
+
### 护栏根治 + 文档补建
|
|
111
127
|
|
|
112
|
-
-
|
|
128
|
+
- 护栏 `\r?\n` 处理修复(merge-agent 护栏);`README.en.md` 英文文档补建(22 KB,含中文互链)。
|
|
113
129
|
|
|
114
130
|
## 0.3.2(2026-08-22)
|
|
115
131
|
|
|
@@ -121,15 +137,15 @@
|
|
|
121
137
|
|
|
122
138
|
### 许可合规修正 + npm 发布
|
|
123
139
|
- 许可唯一化:全仓表述统一为 AGPL-3.0 唯一许可(AGPL-3.0-only),移除商业授权字段;其他授权一律「联系作者获得许可」(README.md / README.en.md / CHANGELOG 0.3.0 记载 / docs/OPENSOURCE.md)
|
|
124
|
-
- 品牌残留清零:Swarm 集群品牌词全包改写为 dsh 语义历史沿革(README
|
|
140
|
+
- 品牌残留清零:Swarm 集群品牌词全包改写为 dsh 语义历史沿革(README / SKILL.md / CHANGELOG 历史记载)
|
|
125
141
|
- npm 发布:dsh-punky-swarm@0.3.1 发布至 npm registry(`npm install -g dsh-punky-swarm`),README / docs/OPENSOURCE 安装章节同步更新
|
|
126
|
-
-
|
|
142
|
+
- 发布包与主仓库文本/版本号对齐(排除备份与依赖目录)
|
|
127
143
|
- 审计清理:docs/OPENSOURCE.md checklist LICENSE 项修正为 AGPL-3.0;本地库 package-lock.json root license 修正为 AGPL-3.0-only
|
|
128
144
|
|
|
129
145
|
## 0.3.0(2026-08-21)
|
|
130
146
|
|
|
131
|
-
### 0.3.0 发布:
|
|
132
|
-
-
|
|
147
|
+
### 0.3.0 发布:AGPL-3.0 唯一许可 + 治理能力默认全开
|
|
148
|
+
- 能力升级:引擎修复(目录 consume 判定 / 产物根指引 / 难度门禁豁免)+ lib 四域解耦(43 文件,删 3 单体)+ 国标 AIP 兼容 + 7 能力域(资产/装配/桥接/通信/面板/状态/验证/监控)+ 生命周期 + 恢复机制
|
|
133
149
|
- 测试 93 → 276 全绿(27 测试文件)
|
|
134
150
|
- 许可切换:Apache-2.0 → AGPL-3.0 唯一许可(AGPL-3.0-only;其他授权一律联系作者获得许可,自 0.3.0 起)
|
|
135
151
|
- 治理能力默认全开:wavePlan 三层 DAG + 引擎级门禁 + 状态机 + 锁/mailbox + 会话隔离
|
|
@@ -148,7 +164,7 @@
|
|
|
148
164
|
- 新增 SKILL.md「Manager 角色派发模板」:Manager=代劳指挥(只指挥不执行、不派发子代理),指挥循环 5 步(batch_status 读黑板 → mailbox_send 建议派发 → mailbox_read 收通知 → member_status/settle 结算 → report 批次完成);任务包模板补 worker 双通道回执约定(report→Leader 简短 + mailbox_send outbox→Manager 详细)
|
|
149
165
|
- 新增 persona 纪律 0g Leader 唤醒协议:worker 由 Leader 派发(depth-1 直系)、Manager mailbox 建议、report→send_message 一行唤醒、Leader 不做调度决策(调度循环在 Manager 上下文)
|
|
150
166
|
- manager.md 协作方式 5 要素更新:不派发子代理 / mailbox 建议派发 / 收 worker 通知 / member_settle 结算裁决 / worker 双通道回执
|
|
151
|
-
- references/
|
|
167
|
+
- references/ 残留术语改写:Swarm 集群运行时术语(HITL/HATL/Converge/任务包)统一为 dsh 治理语义(人审门禁/gap-list 对账/lane 任务)。
|
|
152
168
|
- 测试 93/93 全绿;安装链路验证(模块加载 + syncAssets 幂等)通过
|
|
153
169
|
|
|
154
170
|
## 0.2.0(2026-08-19)
|
package/cordis.patch.yml
CHANGED
|
@@ -31,7 +31,7 @@
|
|
|
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
|
|
34
|
+
# longrun 子键(长跑档探针,出厂默认开):running lane 持续超
|
|
35
35
|
# maxDurationMs(默认 1200000=20min)且近 noProgressWindowMs(默认 300000=5min)无新 checkpoint
|
|
36
36
|
# 且无活动(严格 AND)→ 同 tick 产 lane.longrun.candidate 事件 + mailbox broadcast 候选通知
|
|
37
37
|
# Manager 裁决(探针只标记不改 lane 状态;lane_longrun 工具并列注册,enabled=false 不注册、
|
|
@@ -39,12 +39,12 @@
|
|
|
39
39
|
watch:
|
|
40
40
|
enabled: true
|
|
41
41
|
longrun:
|
|
42
|
-
enabled: true #
|
|
42
|
+
enabled: true # 出厂默认开(显式 false 才关)
|
|
43
43
|
maxDurationMs: 1200000 # 长跑超时阈值(默认 20min;正整数 ms,非法回退默认)
|
|
44
44
|
noProgressWindowMs: 300000 # 无进展窗(默认 5min;正整数 ms,非法回退默认)
|
|
45
45
|
# worktree 物理隔离(lane-tools):enabled=true 时注册 lane_worktree_create / lane_worktree_merge / lane_checkpoint
|
|
46
46
|
# (git worktree 隔离 + checkpoint 提交;与 lane_claim 逻辑锁互补——lane_claim 管状态层「写谁的」,
|
|
47
|
-
#
|
|
47
|
+
# 本组工具管工作区层「写到哪」;工具总数:裸配置 20 / patch 全开 21(含 lane_longrun 与 lane_checkpoint_status)/ 显式关(worktree+watch 关)14)。
|
|
48
48
|
worktree:
|
|
49
49
|
enabled: true
|
|
50
50
|
# merge agent 装配键:enabled=true 且宿主注入 mergeAgentSpawner 时,
|
|
@@ -66,28 +66,28 @@
|
|
|
66
66
|
# 落盘到引擎产物根(可审计);显式 enabled:false 可关闭(关闭时工具不注册)。
|
|
67
67
|
logs:
|
|
68
68
|
enabled: true
|
|
69
|
-
# 工具调用级护栏(governance hook
|
|
69
|
+
# 工具调用级护栏(governance hook):订阅宿主 tools/pre-execute + tools/post-execute,
|
|
70
70
|
# 6 原语纯函数内核(ALLOW/DENY/REQUIRE_APPROVAL/DEFER/NARROW/PAUSE);拒绝收据落盘
|
|
71
71
|
# <root>/governance/refusals/<sessionId>/。rules 空表=零拦截(decide 恒 ALLOW,行为不变)。
|
|
72
|
-
# 分层:任务级=ctx.tools.guard
|
|
73
|
-
#
|
|
74
|
-
#
|
|
75
|
-
#
|
|
76
|
-
# rules / flags / defaults
|
|
72
|
+
# 分层:任务级=ctx.tools.guard 难度门禁(派发前治理);调用级=本 hook(执行时治理)。
|
|
73
|
+
# 默认开启(hook.enabled: true),可显式关闭(governance.hook.enabled=false)。
|
|
74
|
+
# 热更新:governance 键已纳入 runtime.json 热更白名单
|
|
75
|
+
# ——governance.hook 任一子键生效变化(enabled 翻转 /
|
|
76
|
+
# rules / flags / defaults)经热更 dispose+重挂即时生效,免重启;
|
|
77
77
|
# <root>/config/runtime.json 覆盖示例见 README(如 {"governance":{"hook":{"enabled":false}}})。
|
|
78
78
|
governance:
|
|
79
79
|
hook:
|
|
80
80
|
enabled: true # 默认开启;空规则表 → decide 恒 ALLOW → 零行为变化
|
|
81
81
|
rules: [] # 规则表(空=零拦截);Rule 结构见 lib/governance/types.ts
|
|
82
|
-
#
|
|
82
|
+
# preset 装载键:引用随包预设(presets/hook-rules/ 三 JSON,注册 id 枚举:
|
|
83
83
|
# l1-sensitive / l2-resource / compose——不接受任意路径),保序展开拼接至 rules(inline rules 在后)。
|
|
84
84
|
# preset: 'l1-sensitive' 或 preset: ['l1-sensitive', 'l2-resource'](= compose 逐条等价,二选一互斥引用——
|
|
85
85
|
# 同批引用 compose + l1-sensitive 会产生重复 id → 装载失败回退空表 + warn,宁空勿半)。
|
|
86
|
-
# runtime.json 热更示例:{"governance":{"hook":{"preset":["l1-sensitive","l2-resource"]}}}
|
|
86
|
+
# runtime.json 热更示例:{"governance":{"hook":{"preset":["l1-sensitive","l2-resource"]}}}(热更即时生效)。
|
|
87
87
|
# 出厂不默认启用任何 preset(本键缺省 = 空表零拦截不变,与 rules:[] 出厂安全默认严格一致)——
|
|
88
88
|
# 需启用时经 runtime.json 下发或装配 config 注入;preset 文件内容随包版本发布生效(boot 装载一次)。
|
|
89
89
|
# preset: [] # ← 示例注释(默认不启用,勿取消注释即默认值)
|
|
90
|
-
#
|
|
90
|
+
# 护栏违规计数升级(默认关——出厂零行为变化;开启后:归属批次的规则拒绝
|
|
91
91
|
# (DENY/NARROW)在 windowMs 内达 threshold → 经棘轮校验批 paused(reason=governance-escalate);
|
|
92
92
|
# 与 hook enabled:true 内核就位零拦截正交——出厂 rules:[] 本就零收据,关保持「改规则不意外武装暂停」保守面)
|
|
93
93
|
escalation:
|
|
@@ -96,9 +96,9 @@
|
|
|
96
96
|
windowMs: 600000 # 滚动计数窗口(毫秒,≥1000;10 分钟)
|
|
97
97
|
primitives: [DENY, NARROW] # 计入原语子集(可扩入 DEFER/PAUSE;REQUIRE_APPROVAL 与状态门收据不可配)
|
|
98
98
|
defaults:
|
|
99
|
-
deny: DENY # fail-closed 兜底(未分类违规 →
|
|
100
|
-
# (如 REQUIRE_APPROVAL);不可为 ALLOW——resolve 校验回退 DENY
|
|
99
|
+
deny: DENY # fail-closed 兜底(未分类违规 → 兜底原语);可配置为其他拒绝类原语
|
|
100
|
+
# (如 REQUIRE_APPROVAL);不可为 ALLOW——resolve 校验回退 DENY(保证配置真实生效)
|
|
101
101
|
flags:
|
|
102
|
-
pause: false # 原语开关(
|
|
102
|
+
pause: false # 原语开关(feature-flag 式;默认关 → 该原语回退 DENY)
|
|
103
103
|
narrow: false
|
|
104
104
|
defer: false
|
|
@@ -15,7 +15,7 @@ Execution order: pre-execute event chain (kernel) → ask resolution → difficu
|
|
|
15
15
|
## 1. Two-Phase Wiring
|
|
16
16
|
|
|
17
17
|
- **pre-execute**: session-state pre-check (session deferred/paused → reject directly, see §2 DEFER/PAUSE) → kernel adjudication → ALLOW passes through (`next()`); non-ALLOW synchronously writes the refusal receipt (failure only warns, observer discipline, does not block adjudication) → returns `{kind:'deny'|'ask'}`;
|
|
18
|
-
- **post-execute**: pass-through observer — always `next()`, never alters the result; only best-effort backfills the ask outcome (lookup + inference, write failure only warns);
|
|
18
|
+
- **post-execute**: pass-through observer — always `next()`, never alters the result; only best-effort backfills the ask outcome (lookup + inference, write failure only warns). **Controlled exception (denial visibility, controlled correction)**: once the host's serviceAsk generic branches (rejected/cancelled/unavailable/no-agent) overwrite ask.reason, post short-circuits with a corrected agent-visible text for matched asks of this plugin — returns `{kind:'accept', content:[corrected text]}` (the host's accept+content replacement swaps only the display content, preserving isError:true and error.message); the short-circuit condition is strictly narrowed (only those 4 branches plus a matched pending ask of this plugin), backfill/correction failures degrade to `next()` (zero behavior regression); every other scenario stays `next()` (see §2);
|
|
19
19
|
- **event order**: pre → execute → post → result; this hook never calls `ctx.emit` to tamper with the event stream; on the pre-rejection short path execute does not run, but the post observer is still invoked (the degraded-deny path of ask is backfilled the same way);
|
|
20
20
|
- **teardown**: `dispose()` unsubscribes pre + post listeners in order (idempotent).
|
|
21
21
|
|
|
@@ -41,8 +41,8 @@ Adjudication order (category priority, first match wins):
|
|
|
41
41
|
7. `soft` with confidence ≥ 0.70 → REQUIRE_APPROVAL;
|
|
42
42
|
8. unclassified violations → fallback primitive (defaults.deny, default DENY; never ALLOW — fail-closed).
|
|
43
43
|
|
|
44
|
-
- **Unified refusal message format**: `[governance:<primitive>] <reason>` (primitive ∈ ALLOW/DENY/REQUIRE_APPROVAL/DEFER/NARROW/PAUSE; aligned with the difficulty gate's `[task-difficulty-gate]` prefix style) — the model side can distinguish "task level unassessed" from "call level out of bounds". DENY/DEFER/NARROW/PAUSE all land as `{kind:'deny'}`; REQUIRE_APPROVAL → `{kind:'ask'}`.
|
|
45
|
-
- **REQUIRE_APPROVAL ask behavior (explicit)**: pre synchronously writes `ask: {channel:'host-serviceAsk', initiated, requestId(=callId)}
|
|
44
|
+
- **Unified refusal message format**: `[governance:<primitive>] <reason>` (primitive ∈ ALLOW/DENY/REQUIRE_APPROVAL/DEFER/NARROW/PAUSE; aligned with the difficulty gate's `[task-difficulty-gate]` prefix style) — the model side can distinguish "task level unassessed" from "call level out of bounds". DENY/DEFER/NARROW/PAUSE all land as `{kind:'deny'}`; REQUIRE_APPROVAL → `{kind:'ask'}`. **Matched-rule reference**: when ruleRefs is non-empty the reason appends「; rule reference: `<ruleId>` (preset `<presetId>`)」— carried by both the DENY short path and ask.reason (if the approval UI renders reason, the matched rules are visible before the user rejects; custom rules without preset attribution list only the rule id).
|
|
45
|
+
- **REQUIRE_APPROVAL ask behavior (explicit)**: pre synchronously writes `ask: {channel:'host-serviceAsk', initiated, requestId(=callId)}` (and caches a decision snapshot — primitive/reason/ruleRefs — for zero-disk-read post correction); post best-effort backfills `outcome` (denied-no-approval / denied-no-agent / denied-rejected / denied-cancelled / unavailable / allowed-once). **Depends on the host approval channel (serviceAsk); no approval service / no agent → degraded deny** (behavior unchanged, record made explicit); allowed-once → allow. **Denial visibility (controlled exception)**: the host's serviceAsk overwrites ask.reason with tool-name-level generic text on 4 generic branches — rejected (`user rejected`) / cancelled (`was cancelled`) / unavailable (`no approval channel is available`) / no-agent (`no agent to route it through`) — e.g. `the user rejected tool "pwsh"` (no guardrail marker, no matched rules); the wiring post observer **short-circuits a controlled correction** for matched asks of this plugin: returns `{kind:'accept', content:[corrected text]}` whose text carries the `[governance:REQUIRE_APPROVAL approval-gate rejection (guardrail interception…)]` marker + matched rules (rule id + preset attribution) + violation message + receipt/checklist lookup paths (<root>/governance/refusals/… and presets/hook-rules/README.md), preserving isError and degrading to `next()` on failure (zero behavior regression); **denied-no-approval (no-approval-service degrade — the host keeps ask.reason, i.e. the guardrail-prefixed text) is not corrected** (no duplicate annotation). The short-circuit condition is strictly narrowed; the boundary of touching the "post always next()" discipline lives in §1/§7.
|
|
46
46
|
- **DEFER/PAUSE file-state state machine (truly effective once flags are on)**: `flags.defer: true` (soft violation) → session suspended and deferred (state file `<root>/governance/state/<sessionId>.json`, window 30s, receipt carries `deferMeta`); `flags.pause: true` (pausable violation) → session paused (window 60s, receipt carries `pauseMeta`). While suspended/paused, same-session calls are uniformly denied with `[governance:DEFER|PAUSE]` (reason includes retry-after / pauseToken / until); **lazy expiry auto-recovers** (cleaned on read; no timer / no resume endpoint); flag-off collapses to DENY with no state side effect (distinguishable from "session deferred/paused").
|
|
47
47
|
|
|
48
48
|
## 3. Configuration Guide and Example Rules
|
|
@@ -142,6 +142,21 @@ The current guardrails provide in-process, single-machine, rule-table-driven cal
|
|
|
142
142
|
- full RFC8785 (numeric normalization / per-character escaping) and true-signature evidence envelope (the sha256-chain simplified version is provided, see §4);
|
|
143
143
|
- immutable storage (write-once class).
|
|
144
144
|
|
|
145
|
+
### Engine boundaries and denial-visibility lookup paths
|
|
146
|
+
|
|
147
|
+
**Engine boundaries (host dependency layer — not modifiable by the plugin)**:
|
|
148
|
+
|
|
149
|
+
- The host's `serviceAsk` retention/concatenation of the denial reason — its rejected/cancelled/unavailable/no-agent branches overwrite `ask.reason` with tool-name-level generic text; the true root fix requires the host upstream to retain/concatenate reason (an npm overlay would be lost — not provided); the plugin compensates via the post controlled correction (see §2);
|
|
150
|
+
- The host approval UI's rendering of reason (whether the approval card shows multi-line guardrail details) — host web implementation; the plugin only guarantees that the reason text (with the rule reference) reaches the UI via `approval.request`;
|
|
151
|
+
- Per-rule view/edit on the WebUI「Governance Configuration」page — the page itself lives in the host's dsh-web; the plugin only exposes presetCatalog metadata; the current review surface is this repo's rule checklist (see ④ below).
|
|
152
|
+
|
|
153
|
+
**Denial-visibility lookup paths** (traceable chain "visible text → audit detail → full rule list"):
|
|
154
|
+
|
|
155
|
+
- ① Agent-reported text after a refusal (controlled-correction text): session-immediately-visible `[governance:REQUIRE_APPROVAL approval-gate rejection…]` + matched rules (rule id + preset attribution) + violation message + receipt path; the Agent can retry a compliant call following the violation message;
|
|
156
|
+
- ② Approval-request reason (rule-reference delivery): reason tail「; rule reference: …」, delivered to the host UI with `approval.request`;
|
|
157
|
+
- ③ Receipt details (manual/audit): `<root>/governance/refusals/<sessionId>/<receiptId>.json` (decision.reason + ruleRefs + attemptedParams + ask.outcome, hash-anchored) + `ledger-<sessionId>.jsonl` + the batch-level event stream (see §4/§5);
|
|
158
|
+
- ④ Full rule list (proactive review): the「Per-rule review checklist」in `presets/hook-rules/README.md` (l1-sensitive 12 + l2-resource 6; rule id/preset/category/primitive/tools/match/message) + the preset JSON files.
|
|
159
|
+
|
|
145
160
|
## 8. Capability Boundaries & Trade-offs
|
|
146
161
|
|
|
147
162
|
The call-level guardrails extend governance beyond the task level with a call-level line of defense while keeping determinism, auditability, zero added dependencies, and zero host modification. The summary below is grouped into **enhancement surface / capability boundaries / technical trade-offs** (implementation details in §1–§7).
|
package/docs/guardrails-hook.md
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
## 1. 双阶段接线
|
|
16
16
|
|
|
17
17
|
- **pre-execute**:状态前置检查(会话延后/暂停中 → 直接拒绝,见 §2 DEFER/PAUSE)→ 内核裁决 → ALLOW 则透传(`next()`);非 ALLOW 同步落盘拒绝收据(失败仅 warn,观察者纪律,不阻断裁决)→ 返回 `{kind:'deny'|'ask'}`;
|
|
18
|
-
- **post-execute**:pass-through 观察者——恒 `next()`,不篡改结果;仅尽力补记 ask outcome(查表推断,写失败仅 warn);
|
|
18
|
+
- **post-execute**:pass-through 观察者——恒 `next()`,不篡改结果;仅尽力补记 ask outcome(查表推断,写失败仅 warn)。**受控例外(拒绝可见性,受控补正)**:宿主 serviceAsk 泛化分支(rejected/cancelled/unavailable/no-agent)覆盖 ask.reason 后,post 对本插件 ask 命中分支**短路补正** Agent 可见文本——返回 `{kind:'accept', content:[补正文本]}`(宿主 accept+content 替换仅改展示 content、保 isError:true 与 error.message);短路条件严格收窄(仅该 4 分支 + 本插件 pendingAsk 命中),补记/补正失败降级恒 `next()`(零行为回归);其余一切场景恒 `next()`(详见 §2);
|
|
19
19
|
- **事件序**:pre → execute → post → result;本 hook 不调用 `ctx.emit` 篡改事件流;pre 拒绝短路径下 execute 不执行,post 观察者仍被调用(ask 降级 deny 路径同样补记);
|
|
20
20
|
- **卸载**:`dispose()` 依次卸载 pre + post listener(幂等)。
|
|
21
21
|
|
|
@@ -41,8 +41,8 @@
|
|
|
41
41
|
7. `soft` 置信 ≥ 0.70 → REQUIRE_APPROVAL;
|
|
42
42
|
8. 未分类违规 → 兜底原语(defaults.deny,缺省 DENY;绝不 ALLOW——fail-closed)。
|
|
43
43
|
|
|
44
|
-
- **统一拒绝消息格式**:`[governance:<primitive>] <reason>`(primitive ∈ ALLOW/DENY/REQUIRE_APPROVAL/DEFER/NARROW/PAUSE;对齐难度门禁 `[task-difficulty-gate]` 前缀风格)——模型侧可区分「任务级未评估」vs「调用级越界」。DENY/DEFER/NARROW/PAUSE 统一以 `{kind:'deny'}` 落地;REQUIRE_APPROVAL → `{kind:'ask'}
|
|
45
|
-
- **REQUIRE_APPROVAL ask 行为(显式化)**:pre 同步落盘 `ask: {channel:'host-serviceAsk', initiated, requestId(=callId)}
|
|
44
|
+
- **统一拒绝消息格式**:`[governance:<primitive>] <reason>`(primitive ∈ ALLOW/DENY/REQUIRE_APPROVAL/DEFER/NARROW/PAUSE;对齐难度门禁 `[task-difficulty-gate]` 前缀风格)——模型侧可区分「任务级未评估」vs「调用级越界」。DENY/DEFER/NARROW/PAUSE 统一以 `{kind:'deny'}` 落地;REQUIRE_APPROVAL → `{kind:'ask'}`。**命中规则引用**:ruleRefs 非空时 reason 尾部追加「;规则引用:`<ruleId>`(preset `<presetId>`)」——DENY 短路径与 ask.reason 均携带(审批 UI 若渲染 reason,用户拒绝前即可见命中规则;无 preset 归属的自定义规则仅列 rule id)。
|
|
45
|
+
- **REQUIRE_APPROVAL ask 行为(显式化)**:pre 同步落盘 `ask: {channel:'host-serviceAsk', initiated, requestId(=callId)}`(登记时顺带缓存 decision 快照:primitive/reason/ruleRefs——post 补正零盘读);post 尽力补记 `outcome`(denied-no-approval / denied-no-agent / denied-rejected / denied-cancelled / unavailable / allowed-once)。**依赖宿主 approval 通道(serviceAsk),无审批服务 / 无 agent = 降级 deny**(行为不变,记录显式化);allowed-once → allow。**拒绝可见性(受控例外)**:宿主 serviceAsk 在 4 个泛化分支——rejected(`user rejected`)/ cancelled(`was cancelled`)/ unavailable(`no approval channel is available`)/ no-agent(`no agent to route it through`)——会把 ask.reason 覆盖为「工具名级」泛化文本(如 `the user rejected tool "pwsh"`,无护栏标识、无命中规则);wiring post 观察者对本插件 ask 命中分支做**受控补正**:短路返回 `{kind:'accept', content:[补正文本]}`——补正文本含 `[governance:REQUIRE_APPROVAL 人工闸拒绝(护栏拦截…)]` 护栏标注 + 命中规则(rule id + preset 归属)+ 违规 message + 收据/清单查阅路径(<root>/governance/refusals/… 与 presets/hook-rules/README.md),保 isError、失败降级恒 `next()`(零行为回归);**denied-no-approval(无审批服务降级,宿主保留 ask.reason 即护栏前缀正文)不补正**(不重复标注)。短路条件严格收窄,触碰「post 恒 next」纪律的边界见 §1/§7。
|
|
46
46
|
- **DEFER/PAUSE 文件态状态机(flag 开启后真实生效)**:`flags.defer: true`(soft 违规)→ 会话挂起延后(状态文件 `<root>/governance/state/<sessionId>.json`,窗口 30s,收据含 `deferMeta`);`flags.pause: true`(pausable 违规)→ 会话暂停(窗口 60s,收据含 `pauseMeta`)。挂起/暂停期间同会话调用统一 `[governance:DEFER|PAUSE]` deny(reason 含 retry-after / pauseToken / until),**惰性过期自动恢复**(读时清理,无定时器 / 无 resume 端点);flag-off 折叠 DENY 无状态副作用(与「会话延后/暂停中」可区分)。
|
|
47
47
|
|
|
48
48
|
## 3. 配置指南与示例规则
|
|
@@ -142,6 +142,21 @@ governance:
|
|
|
142
142
|
- 完整 RFC8785(数字规范化 / 逐字符转义)与真签名证据信封(sha256 链简版已提供,见 §4);
|
|
143
143
|
- 不可变存储(write-once 类)。
|
|
144
144
|
|
|
145
|
+
### 引擎边界与拒绝可见性查阅路径
|
|
146
|
+
|
|
147
|
+
**引擎边界(宿主依赖层,插件不可改)**:
|
|
148
|
+
|
|
149
|
+
- 宿主 `serviceAsk` 对拒绝 reason 的保留/拼接——rejected/cancelled/unavailable/no-agent 四分支把 `ask.reason` 覆盖为「工具名级」泛化文本;真根修需宿主上游保留/拼接 reason(npm 覆盖即丢,不提供),插件侧以 post 受控补正兜底(见 §2);
|
|
150
|
+
- 宿主审批 UI 对 reason 的渲染形态(审批卡片是否展示多行护栏明细)——宿主 Web 实现;插件只保证 reason 文本(含规则引用)随 `approval.request` 送达;
|
|
151
|
+
- WebUI「治理配置」页逐条规则查看/编辑——页面本体在宿主 dsh-web;插件只提供 presetCatalog 元数据,当前审阅面 = 本仓库规则清单(见下 ④)。
|
|
152
|
+
|
|
153
|
+
**拒绝可见性查阅路径**(「可见文本 → 审计明细 → 全量清单」可追溯链):
|
|
154
|
+
|
|
155
|
+
- ① 被拒后 Agent 汇报文本(受控补正文本):会话内即时可见 `[governance:REQUIRE_APPROVAL 人工闸拒绝…]` + 命中规则(rule id + preset 归属)+ 违规 message + 收据路径,Agent 可按违规 message 修正参数后重发合规调用;
|
|
156
|
+
- ② 审批请求 reason(规则引用送达):reason 尾部「;规则引用:…」,随 approval.request 送达宿主 UI;
|
|
157
|
+
- ③ 收据明细(人工/审计):`<root>/governance/refusals/<sessionId>/<receiptId>.json`(decision.reason + ruleRefs + attemptedParams + ask.outcome,哈希锚定)+ `ledger-<sessionId>.jsonl` + 批级事件流(见 §4/§5);
|
|
158
|
+
- ④ 全量规则清单(主动审阅):`presets/hook-rules/README.md`「逐条规则审阅清单」(l1-sensitive 12 + l2-resource 6,rule id/preset/category/原语/tools/match/message)+ 各 preset JSON。
|
|
159
|
+
|
|
145
160
|
## 8. 能力边界与取舍
|
|
146
161
|
|
|
147
162
|
调用级护栏把治理能力从任务级扩展出调用级一道防线,同时保持确定性、可审计、零新增依赖与零宿主改造。以下按**增强面 / 能力边界 / 技术取舍**三组小结(实现细节见 §1–§7)。
|
package/lib/acps/certs.js
CHANGED
|
@@ -15,7 +15,7 @@ You should have received a copy of the GNU Affero General Public License
|
|
|
15
15
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
|
-
// 文件 certs:ACPs mTLS 证书材料(lib/acps
|
|
18
|
+
// 文件 certs:ACPs mTLS 证书材料(lib/acps 域)
|
|
19
19
|
// 契约:CAI 自签(node:crypto 自签 X.509,零新依赖);文件三路径(cert/key/ca 三路径
|
|
20
20
|
// config 可配,默认 acps 数据目录);TLS 语义对齐参考实现
|
|
21
21
|
// registry-server/app/main_mtls.py:14-30(cert/key/ca 三件套 + CERT_REQUIRED + TLSv1_3)。
|
package/lib/acps/server.js
CHANGED
|
@@ -15,7 +15,7 @@ You should have received a copy of the GNU Affero General Public License
|
|
|
15
15
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
|
-
// 文件 server:ACPs 对外 mTLS 服务端点(lib/acps
|
|
18
|
+
// 文件 server:ACPs 对外 mTLS 服务端点(lib/acps 域)
|
|
19
19
|
// 契约:
|
|
20
20
|
// - 独立 HTTPS 监听器(node:https + node:tls,零新依赖),默认端口 9443、绑定 127.0.0.1(config 可配);
|
|
21
21
|
// - TLS:minVersion TLSv1.3 + requestCert + rejectUnauthorized(=CERT_REQUIRED + TLSv1_3 语义,
|
|
@@ -21,7 +21,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
|
21
21
|
// ⚠ 2026-08 校准(覆盖旧 14+8 口径):字段集以 ACS(Agent Capability Specification)原文为准——
|
|
22
22
|
// registry-server/app/agent/acsSchema.json(JSON Schema 全文)为逐字字段来源;
|
|
23
23
|
// 旧「14+8 兼容映射层」(toLegacyDescriptor / toLegacySkill + ALL_TOOLS / LAYER_CAPABILITIES)
|
|
24
|
-
//
|
|
24
|
+
// 已移除:ACPs 兼容路径中无调用——ACPs 描述直接消费 buildAgentDescriptor 的 ACS 格式
|
|
25
25
|
// (server.js:37,136 装配),描述以 ACS 格式为准。
|
|
26
26
|
// 派生值标记:固定值 = 本文件推导;预留 = 后续填充。
|
|
27
27
|
// 仅用内建能力,零新增依赖(红线:不改 node_modules)。
|
package/lib/aip/identity.js
CHANGED
|
@@ -17,7 +17,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
|
17
17
|
|
|
18
18
|
// 文件 identity:国标 P2/P3 身份体系(AIC 身份码 + CAI 身份证书)
|
|
19
19
|
// -----------------------------------------------------------------------------
|
|
20
|
-
//
|
|
20
|
+
// 校准基准(§3.4/§3.5;[参考] = ACPs-community v2.1.0 原文):
|
|
21
21
|
// P2 身份码 AIC:10 级编码(前缀 1.2.156.3088 + 版本/ARSP/供应商/本体/实体 + 校验码),
|
|
22
22
|
// CRC-16/CCITT-FALSE(poly=0x1021, init=0xFFFF, refin/refout=false, xorout=0x0000)+
|
|
23
23
|
// ARSP 盐值(>=2 字节)+ Base36 固定 4 位校验码(ACPs-spec-AIC-v02.01 §4)。
|
package/lib/api.js
CHANGED
|
@@ -20,10 +20,10 @@ import { readFileSync, readdirSync, existsSync } from 'node:fs';
|
|
|
20
20
|
import { join } from 'node:path';
|
|
21
21
|
import * as mailbox from './comms/mailbox.js';
|
|
22
22
|
import { createStreamHub } from './panel/stream.js';
|
|
23
|
-
//
|
|
23
|
+
// 事件读端字面量收敛:EVT 常量单源 lib/state/event-types.js
|
|
24
24
|
import * as EVT from './state/event-types.js';
|
|
25
|
-
// WebUI
|
|
26
|
-
//
|
|
25
|
+
// WebUI 治理配置写通道:/config 端点 trusted 判定(自复刻宿主 /api
|
|
26
|
+
// 护栏语义,宿主未导出故复刻——插件 exact 路由不经宿主护栏,写端点须自理,见 config-trust.js)
|
|
27
27
|
import { isTrustedConfigRequest } from './webui/config-trust.js';
|
|
28
28
|
|
|
29
29
|
function sendJson(res, status, data) {
|
|
@@ -41,9 +41,9 @@ export function createApi(ctx, deps) {
|
|
|
41
41
|
const disposers = [];
|
|
42
42
|
const register = (route) => disposers.push(ctx.webServer.register(route));
|
|
43
43
|
|
|
44
|
-
//
|
|
45
|
-
// 装配点(index.js
|
|
46
|
-
// 缺省自建(fs.watch
|
|
44
|
+
// SSE hub:随 webServer 挂载(无独立配置键;降级回轮询即运行时自适配开关)。
|
|
45
|
+
// 装配点(index.js 层)预留时经 deps.panelStream 注入复用(含 topic 触发源 attachTopic 接线);
|
|
46
|
+
// 缺省自建(fs.watch 单通道先行交付),自建者负责 dispose。
|
|
47
47
|
const panelStream = deps.panelStream || createStreamHub({ root, logger: ctx.logger });
|
|
48
48
|
if (!deps.panelStream) disposers.push(() => { try { panelStream.dispose(); } catch {} });
|
|
49
49
|
|
|
@@ -112,7 +112,7 @@ export function createApi(ctx, deps) {
|
|
|
112
112
|
laneAttempts,
|
|
113
113
|
upgrades,
|
|
114
114
|
lanesGate: Object.fromEntries(Object.keys(b.lanes).map((l) => [l, store.gateStatus(session, batchId, l)])),
|
|
115
|
-
//
|
|
115
|
+
// 装配注入 aipFormat 时附 ACPs Session 投影(纯函数,不改存储;缺省不附 → 既有响应不变)
|
|
116
116
|
...(aipFormat ? { aipSession: aipFormat.toAipSession(b) } : {}),
|
|
117
117
|
});
|
|
118
118
|
} catch (e) { sendJson(res, 500, { error: String(e.message) }); }
|
|
@@ -127,7 +127,7 @@ export function createApi(ctx, deps) {
|
|
|
127
127
|
const { batchId, box, lane, session } = q(req.url);
|
|
128
128
|
if (!batchId || !box) return sendJson(res, 400, { error: 'batchId+box required' });
|
|
129
129
|
if (!session) return sendJson(res, 400, { error: 'session required' });
|
|
130
|
-
//
|
|
130
|
+
// box 枚举校验——非法 box 返回 400(含枚举提示)而非透传进 mailbox.readUnacked 抛错兜成 500
|
|
131
131
|
const BOXES = ['inbox', 'outbox', 'broadcast'];
|
|
132
132
|
if (!BOXES.includes(box)) {
|
|
133
133
|
return sendJson(res, 400, { error: 'invalid box: ' + box + ' (allowed: ' + BOXES.join(' | ') + ')' });
|
|
@@ -136,7 +136,7 @@ export function createApi(ctx, deps) {
|
|
|
136
136
|
if (box === 'outbox' && !lane) return sendJson(res, 400, { error: 'lane required for outbox' });
|
|
137
137
|
const b = box === 'outbox' ? { type: 'outbox', lane } : { type: box };
|
|
138
138
|
const items = mailbox.readUnacked(join(root, 'sessions', session, 'mailbox', batchId), b);
|
|
139
|
-
//
|
|
139
|
+
// 装配注入 aipFormat 时逐条附 ACPs Message 投影(纯函数投影,不改 mailbox 存储与 ack 语义)
|
|
140
140
|
sendJson(res, 200, aipFormat ? { items: items.map((it) => ({ ...it, aip: aipFormat.toAipMessage(it) })) } : { items });
|
|
141
141
|
} catch (e) { sendJson(res, 500, { error: String(e.message) }); }
|
|
142
142
|
},
|
|
@@ -159,7 +159,7 @@ export function createApi(ctx, deps) {
|
|
|
159
159
|
},
|
|
160
160
|
});
|
|
161
161
|
|
|
162
|
-
// 国标 AIP
|
|
162
|
+
// 国标 AIP 工具列表同步(只读 API 端点):catalog 非空(缺省默认开启)时注册 /tools;
|
|
163
163
|
// 仅显式 aip.enabled=false 时 catalog 为 null,不注册该路由(保持既有 6 路由契约)。
|
|
164
164
|
// catalog 由 register.js 经 readCapability 默认合并口径提供——本条件与 enabled 联动,此处零逻辑改动。
|
|
165
165
|
if (catalog) {
|
|
@@ -176,7 +176,7 @@ export function createApi(ctx, deps) {
|
|
|
176
176
|
});
|
|
177
177
|
}
|
|
178
178
|
|
|
179
|
-
//
|
|
179
|
+
// 智能体描述目录:enabled=true 时 agentCatalog 非空,注册 /agents;
|
|
180
180
|
// 端点输出 ACS 字段集(AgentCapabilitySpec,逐字字段见 lib/aip/agent-descriptor.js);只读、无参。
|
|
181
181
|
// aip.enabled=false(显式关闭——aip 出厂默认开,readCapability 缺省合并 {enabled:true})时 agentCatalog 为 null,不注册该路由(既有路由契约不变)。
|
|
182
182
|
if (agentCatalog) {
|
|
@@ -191,7 +191,7 @@ export function createApi(ctx, deps) {
|
|
|
191
191
|
},
|
|
192
192
|
});
|
|
193
193
|
}
|
|
194
|
-
//
|
|
194
|
+
// 发现服务(ADP 语义):discovery 服务实例注入时注册
|
|
195
195
|
// POST /api/dsh-punky-swarm/discover — 统一发现查询(DiscoveryRequest → DiscoveryResponse)
|
|
196
196
|
// GET /.well-known/aip — 发现服务预置信息(地址/协议版本/能力概要)
|
|
197
197
|
if (discovery) {
|
|
@@ -220,11 +220,11 @@ export function createApi(ctx, deps) {
|
|
|
220
220
|
});
|
|
221
221
|
}
|
|
222
222
|
|
|
223
|
-
// WebUI
|
|
223
|
+
// WebUI 治理配置写通道(路由落点 = discover 段与 stream 段之间):
|
|
224
224
|
// GET + POST /api/dsh-punky-swarm/config —— 配置页页载取数 / 受控字段集保存(写 <root>/config/runtime.json)。
|
|
225
|
-
// 条件注册仿 discovery/agentCatalog
|
|
225
|
+
// 条件注册仿 discovery/agentCatalog(上方注入形态):deps.configEndpoints.runtimeConfig
|
|
226
226
|
// 注入时注册;未注入不注册 → 既有 7 路由/9 路由计数测试零回归(不改既有注册面,disposer 统一回收)。
|
|
227
|
-
// trusted 判定(GET/POST
|
|
227
|
+
// trusted 判定(GET/POST 共用):Host loopback/trustedHosts + sec-fetch-site≠cross-site + Origin 同源
|
|
228
228
|
// (isTrustedConfigRequest,lib/webui/config-trust.js;trustedHosts 出厂 [] → loopback-only)。
|
|
229
229
|
// 写逻辑全在 service(lib/webui/runtime-config.js):白名单预检 400 → 读-改-写 → validateOverlay
|
|
230
230
|
// 兜底 500 → tmp+rename 原子写;GET 取数 overlay(磁盘原样)/applied(装配侧解析快照)/presets(注册目录)。
|
|
@@ -235,7 +235,7 @@ export function createApi(ctx, deps) {
|
|
|
235
235
|
kind: 'exact',
|
|
236
236
|
path: '/api/dsh-punky-swarm/config',
|
|
237
237
|
handler(req, res) {
|
|
238
|
-
// trusted 护栏前置(GET/POST 共用;护栏语义非鉴权层、防 DNS-rebinding
|
|
238
|
+
// trusted 护栏前置(GET/POST 共用;护栏语义非鉴权层、防 DNS-rebinding/跨站)
|
|
239
239
|
if (!isTrustedConfigRequest(req, trustedHosts)) {
|
|
240
240
|
const body = req.method === 'POST' ? { ok: false, error: 'forbidden' } : { error: 'forbidden' };
|
|
241
241
|
return sendJson(res, 403, body);
|
|
@@ -246,7 +246,7 @@ export function createApi(ctx, deps) {
|
|
|
246
246
|
const gov = overlay && typeof overlay === 'object' && !Array.isArray(overlay)
|
|
247
247
|
&& overlay.governance && typeof overlay.governance === 'object' && !Array.isArray(overlay.governance)
|
|
248
248
|
? overlay.governance : null;
|
|
249
|
-
// watch
|
|
249
|
+
// watch 段取数:overlayWatch = 磁盘 capabilities.watch 段原样
|
|
250
250
|
// (无 = null);applied.watch = 装配侧解析生效快照(watchInstalledCfg,经 appliedWatch getter——
|
|
251
251
|
// 未注入时省略该键,旧 harness/旧客户端零感知)。既有 overlay=governance 语义不动。
|
|
252
252
|
const caps = overlay && typeof overlay === 'object' && !Array.isArray(overlay)
|
|
@@ -276,8 +276,8 @@ export function createApi(ctx, deps) {
|
|
|
276
276
|
}
|
|
277
277
|
return bodyPromise.then((payload) => {
|
|
278
278
|
try {
|
|
279
|
-
// POST 按 body
|
|
280
|
-
// (单保存合并 governance + capabilities.watch 双段同 body
|
|
279
|
+
// POST 按 body 键存在性分派:含 capabilities 段 → writeWatch
|
|
280
|
+
// (单保存合并 governance + capabilities.watch 双段同 body——writeWatch 内部
|
|
281
281
|
// 同时处理可选 governance 段,分节校验 + 单次原子写);仅 governance(旧客户端/既有测试契约)
|
|
282
282
|
// → writeGovernance 原路径(错误形态与路由零变化)。
|
|
283
283
|
const hasCaps = payload && typeof payload === 'object' && !Array.isArray(payload) && 'capabilities' in payload;
|
|
@@ -292,7 +292,7 @@ export function createApi(ctx, deps) {
|
|
|
292
292
|
}
|
|
293
293
|
return sendJson(res, 200, { ok: true, written: out.written, ts: new Date().toISOString() });
|
|
294
294
|
} catch (e) {
|
|
295
|
-
// 读-改-写 IO 异常(坏 base JSON / rename 失败等)→ 500
|
|
295
|
+
// 读-改-写 IO 异常(坏 base JSON / rename 失败等)→ 500 不落盘(「不应发生」面)
|
|
296
296
|
return sendJson(res, 500, { ok: false, error: String(e?.message ?? e) });
|
|
297
297
|
}
|
|
298
298
|
}).catch((e) => sendJson(res, 400, { ok: false, error: 'invalid-json: ' + String(e?.message ?? e) }));
|
|
@@ -302,10 +302,10 @@ export function createApi(ctx, deps) {
|
|
|
302
302
|
});
|
|
303
303
|
}
|
|
304
304
|
|
|
305
|
-
//
|
|
305
|
+
// SSE 端点(纯新增路由——既有 /api 路由一字不动):
|
|
306
306
|
// GET /api/dsh-punky-swarm/stream?session=<sid>[&batchId=<bid>]
|
|
307
307
|
// SSE 帧协议:event: batch|mailbox|heartbeat + data:<JSON>;注释心跳帧每 10s(hub 内维护)。
|
|
308
|
-
// 推送只发轻量摘要 {sessionId,batchId,eventCount,updatedAt},客户端回拉既有只读 API
|
|
308
|
+
// 推送只发轻量摘要 {sessionId,batchId,eventCount,updatedAt},客户端回拉既有只读 API 取全量。
|
|
309
309
|
register({
|
|
310
310
|
kind: 'exact',
|
|
311
311
|
path: '/api/dsh-punky-swarm/stream',
|
package/lib/artifact-types.js
CHANGED
|
@@ -15,7 +15,7 @@ You should have received a copy of the GNU Affero General Public License
|
|
|
15
15
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
|
-
//
|
|
18
|
+
// 通用产物类型注册表
|
|
19
19
|
// 定位:通用任务治理模式——登记产物类型 → 层/目录前缀的约定,供校验与查询;
|
|
20
20
|
// 不绑定任何团队模板(jiufeng 四件套只是使用者,产物内部格式归模板层)。
|
|
21
21
|
// 三层目录约定:plan/(任务层)、exec/(执行层)、audit/(审计层),与 wave-plan 路径契约一致。
|
package/lib/assembly/schema.js
CHANGED
|
@@ -43,11 +43,11 @@ export const CAPABILITY_REGISTRY = [
|
|
|
43
43
|
{ key: 'worktree', path: ['capabilities', 'worktree'], default: { enabled: true, mergeAgent: { enabled: false, model: null, timeoutMs: 600000 } }, consumers: ['tools/lane-tools.js 三工具注册'] },
|
|
44
44
|
{ key: 'budget', path: ['capabilities', 'budget'], default: { enabled: true, maxChainHops: 4, maxChainRoundTrips: 2 }, consumers: ['comms/budget.js + mailbox-tools.js 发送检查'] },
|
|
45
45
|
{ key: 'trajectory', path: ['capabilities', 'trajectory'], default: TRAJECTORY_DEFAULTS, consumers: ['index.js 桥接订阅'] },
|
|
46
|
-
// logs
|
|
46
|
+
// logs 键:日志导出工具注册开关——默认关(与 cordis.patch.yml 显式 logs.enabled:true 相合);
|
|
47
47
|
// 消费点经 readCapability(config,'logs') 缺省读取,显式 enabled:false 可关。
|
|
48
48
|
{ key: 'logs', path: ['capabilities', 'logs'], default: { enabled: false }, consumers: ['tools/log-tools.js log_export 注册'] },
|
|
49
|
-
// topic
|
|
50
|
-
{ key: 'topic', path: ['capabilities', 'topic'], default: { enabled: false }, consumers: ['comms/topic.js
|
|
49
|
+
// topic 键:comms/topic.js 预留模块开关——默认关;接线(trajectory 桥广播改经本模块等)预留。
|
|
50
|
+
{ key: 'topic', path: ['capabilities', 'topic'], default: { enabled: false }, consumers: ['comms/topic.js(预留)'] },
|
|
51
51
|
];
|
|
52
52
|
|
|
53
53
|
// ── 互斥表(预留空表:当前能力两两可叠加;未来互斥能力登记于此)──
|
|
@@ -64,8 +64,8 @@ export const BLIND_REVIEW_ROLES = ['audit-panelist', 'audit-aggregate', 'audit-c
|
|
|
64
64
|
export const BLIND_REVIEW_TEMPLATE_KEYS = ['bundle', 'panelist', 'aggregate', 'critic', 'checklist', 'config'];
|
|
65
65
|
|
|
66
66
|
// ── 内部工具 ──
|
|
67
|
-
//
|
|
68
|
-
//
|
|
67
|
+
// 热更新(lib/hot/config-watch.js)导出复用本函数做 runtime 覆盖层 deepMerge——
|
|
68
|
+
// 禁止复制散落实现(复制即漂移源);readCapability 缺省合并与本函数同源。
|
|
69
69
|
export function deepMerge(base, override) {
|
|
70
70
|
if (override === undefined || override === null) return base;
|
|
71
71
|
if (typeof base !== 'object' || base === null || typeof override !== 'object' || override === null) return override;
|
package/lib/assembly.js
CHANGED
|
@@ -15,7 +15,7 @@ You should have received a copy of the GNU Affero General Public License
|
|
|
15
15
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
|
-
//
|
|
18
|
+
// 可插拔装配数据:team → layer → role → skills
|
|
19
19
|
// 引擎只认 "role 契约 + skill 前缀" 通用格式,不感知 team;非 jiufeng 团队 = 换装配(外部路径 config.assembly 或自定义 resolveAssembly)
|
|
20
20
|
export const DEFAULT_ASSEMBLY = {
|
|
21
21
|
team: 'jiufeng',
|
|
@@ -15,17 +15,17 @@ You should have received a copy of the GNU Affero General Public License
|
|
|
15
15
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
|
-
// bridge/dispatch-register.js ——
|
|
19
|
-
//
|
|
20
|
-
//
|
|
21
|
-
//
|
|
22
|
-
//
|
|
23
|
-
//
|
|
24
|
-
//
|
|
25
|
-
// 零宿主改造:仅订阅宿主既有 tools/post-execute waterfall(pass-through 恒 next()
|
|
26
|
-
// swarm 自身工具(member_status 等)同样流经该 waterfall
|
|
27
|
-
//
|
|
28
|
-
//
|
|
18
|
+
// bridge/dispatch-register.js —— 派发登记点(dispatch registration):装配层观察派发工具,把
|
|
19
|
+
// 「worker 会话 → { sessionId, batchId, lane }」写为 member.dispatch 批次事件。
|
|
20
|
+
// 背景:归属读侧骨架(dispatchIndex,从批次事件 member.dispatch 幂等重建)依赖写侧登记;
|
|
21
|
+
// 本模块经装配层 post-execute 观察 Manager 派发(member_status(status=running) 意图)
|
|
22
|
+
// → resolveBatchContext 得当前批/lane → store.appendEvent 写 member.dispatch
|
|
23
|
+
// (与读侧骨架对接零改动生效:启动重建 + miss 惰性重建,幂等);
|
|
24
|
+
// 未取到批上下文(非 Manager 派发场景)→ 不登记(无登记静默降级语义)。
|
|
25
|
+
// 零宿主改造:仅订阅宿主既有 tools/post-execute waterfall(pass-through 恒 next());
|
|
26
|
+
// swarm 自身工具(member_status 等)同样流经该 waterfall → 兜底解析 = 观察同会话
|
|
27
|
+
// member_status(status=running) 的派发意图(时序:先置 running 再派发 worker)。
|
|
28
|
+
// 语义红线:漏计不误暂停(安全侧)——解析不出批上下文宁可跳过;不引入任何批级状态迁移。
|
|
29
29
|
import { EVT_MEMBER_DISPATCH } from '../state/event-types.js';
|
|
30
30
|
import { sessionOf } from '../tools/shared.js'; // 会话解析与 swarm 工具同源(args.session ?? exec.agent.session.id)
|
|
31
31
|
|
|
@@ -63,12 +63,12 @@ export function extractWorkerSessionId(exec, result) {
|
|
|
63
63
|
return m ? m[1] : null;
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
-
//
|
|
66
|
+
// 装配层登记点(对齐 mountVerify/installGovernanceHook 模式):
|
|
67
67
|
// 订阅 ctx.on('tools/post-execute')——识别派发类工具 → 提取 workerSessionId → resolveBatchContext(exec)
|
|
68
68
|
// (deps 显式注入优先;缺省 = 同会话 member_status(running) 派发意图兜底)→ 命中批上下文则 appendEvent
|
|
69
|
-
// member.dispatch {lane, workerSessionId}(读侧骨架零改动生效);未命中 →
|
|
69
|
+
// member.dispatch {lane, workerSessionId}(读侧骨架零改动生效);未命中 → 不登记。
|
|
70
70
|
// 幂等守卫:dispatchIndex.has(workerSessionId) 已映射 → 跳过(防 send_message 重复唤醒重复登记)。
|
|
71
|
-
// 观察者纪律:任一失败仅 warn,恒 return next()
|
|
71
|
+
// 观察者纪律:任一失败仅 warn,恒 return next()(不阻断、不抛错)。
|
|
72
72
|
// 返回 { installed, dispose, count, mapping };ctx.on 缺失 → inert 静默降级(宿主能力缺失不炸)。
|
|
73
73
|
export function installDispatchRegistration(ctx, deps = {}) {
|
|
74
74
|
const { store, dispatchIndex, logger, resolveBatchContext, tools = DEFAULT_DISPATCH_TOOLS } = deps;
|
|
@@ -107,12 +107,12 @@ export function installDispatchRegistration(ctx, deps = {}) {
|
|
|
107
107
|
// ② 非派发类工具 → 不登记(透传)
|
|
108
108
|
if (!toolSet.has(exec.name)) return next();
|
|
109
109
|
const workerSessionId = extractWorkerSessionId(exec, result);
|
|
110
|
-
if (!workerSessionId) return next(); // 无持久 worker 会话(background/foreground/失败)→
|
|
110
|
+
if (!workerSessionId) return next(); // 无持久 worker 会话(background/foreground/失败)→ 不登记
|
|
111
111
|
// ③ resolveBatchContext(exec):显式注入优先,缺省 = 同会话派发意图兜底(装配注入 resolveBatchContext 兜底)
|
|
112
112
|
const hit = typeof resolveBatchContext === 'function'
|
|
113
113
|
? resolveBatchContext(exec, { workerSessionId, result })
|
|
114
114
|
: (intentBySession.get(caller) ?? null);
|
|
115
|
-
if (!hit || !hit.sessionId || !hit.batchId || !hit.lane) return next(); // 未取到批上下文 →
|
|
115
|
+
if (!hit || !hit.sessionId || !hit.batchId || !hit.lane) return next(); // 未取到批上下文 → 不登记
|
|
116
116
|
if (register(hit.sessionId, hit.batchId, hit.lane, workerSessionId)) {
|
|
117
117
|
intentBySession.delete(caller); // 消费意图(一次 running → 一次派发登记)
|
|
118
118
|
}
|