@twinsearth/dsh-master 0.0.0-stage → 3.2.8
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/LICENSE +661 -0
- package/README.md +89 -2
- package/client.js +263 -0
- package/cordis.patch.yml +71 -0
- package/dist-host/contract/client.js +82 -0
- package/dist-host/contract/constants.js +70 -0
- package/dist-host/contract/envelope.js +84 -0
- package/dist-host/contract/index.js +17 -0
- package/dist-host/contract/negotiate.js +87 -0
- package/dist-host/contract/server.js +71 -0
- package/dist-host/contract/transports.js +360 -0
- package/dist-host/index.js +260 -0
- package/dist-host/kernel.js +186 -0
- package/dist-host/plugins/cost-optimizer.js +147 -0
- package/dist-host/plugins/dual-manager.js +251 -0
- package/dist-host/plugins/superagent-router.js +499 -0
- package/dist-host/plugins/superagent-worker.js +73 -0
- package/dist-host/superagent/bridge-tokens.js +236 -0
- package/dist-host/superagent/dispatch.js +198 -0
- package/dist-host/superagent/feishu.js +115 -0
- package/dist-host/superagent/github.js +119 -0
- package/dist-host/superagent/pr-delivery.js +351 -0
- package/dist-host/superagent/router.js +219 -0
- package/dist-host/superagent/stream-adapter.js +238 -0
- package/dist-host/superagent/stream-tokens.js +141 -0
- package/dist-host/superagent/ticket-store.js +108 -0
- package/dist-host/superagent/trust.js +145 -0
- package/dist-host/superagent/types.js +144 -0
- package/dist-host/superagent/worker.js +95 -0
- package/dist-host/superagent/writeback.js +162 -0
- package/dist-worker/agent-loop.js +493 -0
- package/dist-worker/errors.js +41 -0
- package/dist-worker/github-pr.js +44 -0
- package/dist-worker/index.js +323 -0
- package/dist-worker/llm-http.js +351 -0
- package/dist-worker/main.js +45 -0
- package/dist-worker/prompt.js +35 -0
- package/dist-worker/sink-http.js +135 -0
- package/dist-worker/tools.js +542 -0
- package/dist-worker/types.js +14 -0
- package/docs/ARCHITECTURE.md +114 -0
- package/docs/CHANGELOG.md +377 -0
- package/docs/INTEROP.md +60 -0
- package/docs/REVIEW-v3.2.7.md +121 -0
- package/docs/ROADMAP.md +106 -0
- package/docs/SECURITY.md +153 -0
- package/docs/SMOKE-3.2.5.md +77 -0
- package/docs/SMOKE-3.2.7.md +73 -0
- package/docs/SUPERAGENT.md +141 -0
- package/docs/WORKER-AGENT-LOOP.md +139 -0
- package/icon.svg +9 -0
- package/locale/en.json +6 -0
- package/locale/zh.json +6 -0
- package/package.json +79 -4
- package/src/contract/client.js +86 -0
- package/src/contract/constants.js +79 -0
- package/src/contract/envelope.js +83 -0
- package/src/contract/index.js +19 -0
- package/src/contract/negotiate.js +106 -0
- package/src/contract/server.js +77 -0
- package/src/contract/transports.js +319 -0
- package/src/index.ts +265 -0
- package/src/kernel.ts +190 -0
- package/src/plugins/cost-optimizer.ts +155 -0
- package/src/plugins/dual-manager.ts +252 -0
- package/src/plugins/superagent-router.ts +465 -0
- package/src/plugins/superagent-worker.ts +85 -0
- package/src/superagent/bridge-tokens.ts +236 -0
- package/src/superagent/dispatch.ts +197 -0
- package/src/superagent/feishu.ts +104 -0
- package/src/superagent/github.ts +111 -0
- package/src/superagent/pr-delivery.ts +308 -0
- package/src/superagent/router.ts +201 -0
- package/src/superagent/stream-adapter.ts +184 -0
- package/src/superagent/stream-tokens.ts +142 -0
- package/src/superagent/ticket-store.ts +104 -0
- package/src/superagent/trust.ts +139 -0
- package/src/superagent/types.ts +135 -0
- package/src/superagent/worker.ts +100 -0
- package/src/superagent/writeback.ts +154 -0
- package/tsconfig.host.build.json +12 -0
- package/tsconfig.host.json +21 -0
- package/tsconfig.worker.build.json +13 -0
- package/tsconfig.worker.json +17 -0
- package/types/superagent.d.ts +99 -0
- package/worker/agent-loop.ts +524 -0
- package/worker/errors.ts +47 -0
- package/worker/github-pr.ts +65 -0
- package/worker/index.ts +334 -0
- package/worker/llm-http.ts +359 -0
- package/worker/main.ts +45 -0
- package/worker/prompt.ts +39 -0
- package/worker/sink-http.ts +159 -0
- package/worker/tools.ts +531 -0
- package/worker/types.ts +247 -0
package/docs/ROADMAP.md
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# 路线图:3.2.0 → 3.4.0
|
|
2
|
+
|
|
3
|
+
> 版本线:`3.2.0 – 3.2.9`(1 个大版本 + 9 个小版本),随后 3.3.0、3.4.0。
|
|
4
|
+
> 本文保留版本演进记录;当前基线为 `3.2.8`。
|
|
5
|
+
> 最新测试/实机状态与尚未完成的范围见 `REVIEW-v3.2.7.md`,下列旧版本待办不得再理解为当前状态。
|
|
6
|
+
|
|
7
|
+
## 三条主线
|
|
8
|
+
|
|
9
|
+
| 版本 | 主题 | 核心组件 | 定位 |
|
|
10
|
+
|---|---|---|---|
|
|
11
|
+
| **3.2.0–3.2.9** | 主动性:从被动响应到主动巡航 | SuperAgent(Router/Worker) | 原身控制面 + 分身执行体的双层结构 |
|
|
12
|
+
| **3.3.0** | 经济性:车间成本控制器 | HarnessMM | 分身的 Token 效率优化器;EGD/GDR/CCS |
|
|
13
|
+
| **3.4.0** | 组织性:人机协同制度化 | PM + PJM | 母体/元主内的元 Agent,调度分身集群 |
|
|
14
|
+
|
|
15
|
+
依赖顺序不可颠倒:先有可靠的自动化执行(3.2.x),再降成本(3.3.0),最后引入管理者(3.4.0)。
|
|
16
|
+
|
|
17
|
+
## 3.2.0(已完成架构骨架)
|
|
18
|
+
|
|
19
|
+
- [x] 新仓库 `TwinsEarth/dsh-Master`,独立 bundle / 版本线;
|
|
20
|
+
- [x] 主插件 dsh-Master 与瓮城 dsh-Fang 并行(patch 只 insert 自己的行);
|
|
21
|
+
- [x] 子插件骨架 + 角色矩阵 + 与瓮城服务契约;
|
|
22
|
+
- [x] 只读状态路由 + 侧栏「房主」抽屉 + 测试/文档。
|
|
23
|
+
|
|
24
|
+
## 3.2.3(本次交付:Worker preset 构建 + 派发桥 + 真实 streaming/回写)
|
|
25
|
+
|
|
26
|
+
- [x] `tsc` 构建 Worker preset(`dist-worker/`),package export + bundle 行 `master-superagent-loop`;
|
|
27
|
+
- [x] fork 有 Ticket 自动跑 Agent Loop;角色门禁(母体/元主拒绝)、autrun 开关;
|
|
28
|
+
- [x] 跨进程桥:原身 Router → 母体 `/dsh-master/api/bridge/spawn`(Bearer bridgeToken);
|
|
29
|
+
- [x] Worker → Router 实时事件通道(per-ticket token、批量 POST、done 落库/状态推进);
|
|
30
|
+
- [x] 回写:webhook / GitHub Issue 评论(默认关闭,失败只告警);
|
|
31
|
+
- [x] 安全审计 P0/P1 加固:Shell 环境脱敏、进程树 kill、解释器黑名单、敏感路径拒绝、
|
|
32
|
+
Mandate 一致性、secret 脱敏、token 估算兜底、limits 绝对上限;
|
|
33
|
+
- [x] 测试 95/95;tsc strict/build 双通过;
|
|
34
|
+
- [ ] 前端 SSE/WS 实时 UI(Worker 事件 → 浏览器)——3.2.4;
|
|
35
|
+
- [ ] bridge token/stream token 自动轮换与审计——3.2.4;
|
|
36
|
+
- [ ] Worker 资源配额/并发看护(宿主层)——3.2.4;
|
|
37
|
+
- [ ] Ticket → 瓮城 session 仓(3_Sessions)映射——3.2.4。
|
|
38
|
+
|
|
39
|
+
## 3.2.2(已完成 Worker Agent Loop + 安全工具集)
|
|
40
|
+
|
|
41
|
+
- [x] Worker Agent Loop(TypeScript):初始化→thinking→tool_call/tool_result→reporting→done 状态机;
|
|
42
|
+
- [x] 多轮工具调用循环:工具结果回灌模型、继续迭代直到模型不再调用工具;
|
|
43
|
+
- [x] 硬熔断:maxSteps / maxTokens / maxWallClockMs / maxContextChars / 连续同调用死循环检测;
|
|
44
|
+
- [x] LLM 调用超时与分类错误(llm-timeout / llm-call-failed / invalid-response);
|
|
45
|
+
- [x] 安全工具集:fs_read_file / fs_list_dir / fs_write_file(Mandate.scope jail)/ exec_shell_in_vm(allowShell+黑名单)/ create_pr(宿主注入,默认拒绝);
|
|
46
|
+
- [x] 路径 jail + realpath 防符号链接逃逸;工具错误转为 `执行报错` 回灌模型,不打断循环;
|
|
47
|
+
- [x] WorkerStep 审计 + snapshot() 崩溃恢复现场;
|
|
48
|
+
- [x] WorkerSink 流式日志(phase/step/stream/log)与 Cordis 事件转发;
|
|
49
|
+
- [x] OpenAI 兼容 HTTP LLM 适配器(key 不落日志);
|
|
50
|
+
- [x] CLI:`node worker/main.ts`(env 读 Ticket/工作区/LLM 配置);
|
|
51
|
+
- [x] tsc `strict + noEmit` 类型检查通过;Node 22.6+ 类型擦除直接运行;
|
|
52
|
+
- [x] 测试:79 项全绿(含 21 项 worker TS 测试);
|
|
53
|
+
- [ ] 把 Worker preset 打包成已发布 bundle(tsc 构建产物):3.2.3;
|
|
54
|
+
- [ ] 跨进程 Router→DerivedManager 桥:3.2.3;
|
|
55
|
+
- [ ] 真实 streaming(SSE/WS)转发:3.2.3;
|
|
56
|
+
- [ ] PR/评论/飞书回写:3.2.3;
|
|
57
|
+
- [ ] Worker 资源配额/并发/超时看护(宿主层):3.2.4;
|
|
58
|
+
- [ ] Ticket → 瓮城 session 仓(3_Sessions):3.2.4。
|
|
59
|
+
|
|
60
|
+
## 3.2.1(已完成 SuperAgent 双层落地)
|
|
61
|
+
|
|
62
|
+
- [x] **Router / Worker 拆分**:Router essence-only 控制面,Worker fork-only 执行体;
|
|
63
|
+
- [x] SourceEvent 归一化与类型(含 TypeScript 声明 `types/superagent.d.ts`);
|
|
64
|
+
- [x] GitHub Webhook(HMAC 验签、issue_comment / pull_request_review)+ 飞书(token/challenge/text);
|
|
65
|
+
- [x] Ticket + 不可变 Mandate(scope/strength/policyVersion);
|
|
66
|
+
- [x] 渐进式信任 off / observe / auto(默认 observe);确定性 Gate(actor/仓库/mention/bot/去重);
|
|
67
|
+
- [x] `fangForker.spawn({env})` 派发契约;缺服务/配置 fail-closed,不本地执行;
|
|
68
|
+
- [x] Ticket 持久化(可选 dataDir,JSON 原子写);
|
|
69
|
+
- [x] Worker 侧 Ticket 恢复、Mandate scope 守卫、Worker prompt、>5s 命令后台化契约;
|
|
70
|
+
- [x] 单元测试全绿(58 项);
|
|
71
|
+
- [ ] **跨进程 DerivedManager 桥**(essence Router → mother forker):3.2.2;
|
|
72
|
+
- [ ] Worker preset 的 LLM 工具循环与结果回写:3.2.2;
|
|
73
|
+
- [ ] 飞书 Encrypt Key 解密、GitHub 多仓库 secret:3.2.2;
|
|
74
|
+
- [ ] Worker 资源配额/并发/超时:3.2.3;
|
|
75
|
+
- [ ] Ticket → 瓮城 session 仓(3_Sessions)映射:3.2.4;
|
|
76
|
+
- [ ] 观察面板(近期事件/Ticket/拒绝原因):3.2.5;
|
|
77
|
+
- [ ] 审计全链路与回放:3.2.6;
|
|
78
|
+
- [ ] 灰度工具(按仓库/actor 切 auto):3.2.7;
|
|
79
|
+
- [ ] 安全回归(确认母体/元主无 SuperAgent 行):3.2.8;
|
|
80
|
+
- [ ] 3.2.9:发布候选、文档/演示、升级说明。
|
|
81
|
+
|
|
82
|
+
## 3.3.0:HarnessMM 成本优化
|
|
83
|
+
|
|
84
|
+
- 观察层(3.2.x 已占位):token 估算、实测记录、节省汇总;
|
|
85
|
+
- 接入 EGD(可执行决策图):复用工具调用决策;
|
|
86
|
+
- GDR(受保护决策复用)/ CCS(认证回调替换):仅当回执符合预期时替换模型推理;
|
|
87
|
+
- 按分身/任务启用(Overlay 合并由瓮城提供);
|
|
88
|
+
- 收益通过瓮城“产物回流”通道回传原身,供后续派生复用;
|
|
89
|
+
- 成本接入瓮城控制面板“分身信息栏”:派生前预估、归档后实测。
|
|
90
|
+
|
|
91
|
+
## 3.4.0:PM + PJM 双主管
|
|
92
|
+
|
|
93
|
+
- PM:需求入口,自然语言 → PRD;通过决策中心澄清;
|
|
94
|
+
- PJM:读 PRD → `spawn_dev_environment` 申请资源 → `execute_project_step` 调度与健康监控;
|
|
95
|
+
- 与 SuperAgent 闭环:事件 → PM 分析 → PRD → PJM 调度分身 → 结果回写事件源;
|
|
96
|
+
- 元 Agent 只运行在母体/元主,不进入分身执行层。
|
|
97
|
+
|
|
98
|
+
## 版本对应
|
|
99
|
+
|
|
100
|
+
| 瓮城 | 房主 | 说明 |
|
|
101
|
+
|---|---|---|
|
|
102
|
+
| dsh-Fang 3.1.x | — | 环境/决策基础设施;服务契约基线 3.1.14 |
|
|
103
|
+
| dsh-Fang 3.2.0 | dsh-Master 3.2.0 | 两插件并行;房主骨架 |
|
|
104
|
+
| dsh-Fang 3.2.x | dsh-Master 3.2.1–3.2.9 | SuperAgent 双层落地 + 灰度 |
|
|
105
|
+
| dsh-Fang 3.3.x | dsh-Master 3.3.0 | HarnessMM 接入 |
|
|
106
|
+
| dsh-Fang 3.4.x | dsh-Master 3.4.0 | PM/PJM 调度 |
|
package/docs/SECURITY.md
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
# 安全边界(3.2.8)
|
|
2
|
+
|
|
3
|
+
> 最新变化:worker-events 读取需 UI/匹配 worker token;旧 token 轮换 fail-closed;契约与 PR 网络调用全部有超时;DSH 同路径路由合并分流;飞书加密事件支持 AES-256-CBC。实机证据见 `REVIEW-v3.2.7.md`。
|
|
4
|
+
|
|
5
|
+
## 安全回归测试清单(v3.2.6)
|
|
6
|
+
|
|
7
|
+
审计建议的 8 类用例均已固化,任一防线回退都会让 CI 变红:
|
|
8
|
+
|
|
9
|
+
| 编号 | 风险 | 用例(测试名) | 文件 |
|
|
10
|
+
|---|---|---|---|
|
|
11
|
+
| P0-1+ | spawn 丢掉 env 白名单(结构性回退) | `tools: structural guard — spawn must pass sanitizeShellEnv(options.env ?? process.env)`(源码级断言:spawn options 必须出现 `sanitizeShellEnv`,且不得出现裸 `process.env`) | worker-tools |
|
|
12
|
+
| P0-1 | Shell 继承 Worker 环境泄密 | `shell child process cannot see worker secrets (real env dump)`;`shell env is sanitized and interpreter-eval commands are rejected` | worker-tools |
|
|
13
|
+
| P0-2 | Shell 读写 workspace/Mandate 外 | `shell cannot read or write outside the workspace / mandate jail`(8 种逃逸:绝对路径、相对穿越、重定向写、建目录、cd、git -C push、sh -c、powershell) | worker-tools |
|
|
14
|
+
| P0-2 | 未证明 VM 隔离时 Shell 可用 | `shell is denied without Mandate.allowShell and blocks dangerous commands`(含 fail-closed 断言) | worker-tools |
|
|
15
|
+
| P0-2 | 解释器绕过黑名单 | `node -e` / `python -c` 在同一用例内被拒 | worker-tools |
|
|
16
|
+
| P0-3 | 超时后进程树存活 | `shell timeout kills the process tree (no late side effect)` | worker-tools |
|
|
17
|
+
| P1-1 | 构造 Mandate 与 Ticket 不一致 | `ticket mandate mismatch with worker mandate is rejected`;`mandate can narrow allowShell but never widen` | worker-agent-loop |
|
|
18
|
+
| P1-2 | 无 usage 预算失效 / 超大单条响应 | `missing provider usage falls back to an estimated token budget`;`oversized single response is rejected before it enters history` | worker-agent-loop |
|
|
19
|
+
| P1-3 | 工具输出/步骤未脱敏 | `tool results are redacted before steps/history`;`error steps are redacted too`;`sink.step and host log never receive raw secrets` | worker-agent-loop |
|
|
20
|
+
| P2-1 | .git/hooks 等敏感路径可写 | `sensitive .git writes are denied`;`every .env variant is denied for writes` | worker-tools |
|
|
21
|
+
| P2-2 | limits 被调用方放大 | `ABSOLUTE_LIMIT_CEILING` + `mergeLimits` 硬顶(代码级) | worker-agent-loop |
|
|
22
|
+
| P2-3 | A/B 交替死循环 | `alternating A/B calls are also detected as a dead loop`;`total tool-call budget stops a runaway loop` | worker-agent-loop |
|
|
23
|
+
| P2-4 | create_pr 参数 | `create_pr rejects protected branches and illegal base`;`create_pr refuses without backend and uses injected PR client` | worker-tools |
|
|
24
|
+
|
|
25
|
+
## P2 控制项(v3.2.5)
|
|
26
|
+
|
|
27
|
+
| 项目 | 现状 | 说明 |
|
|
28
|
+
|---|---|---|
|
|
29
|
+
| 敏感路径写入 | `.git`、`.dsh`、`.env*`(全变体)、`.ssh`、`node_modules/.cache` 默认拒绝 | 可通过 `denyWritePaths` 配置扩展;**不是**替代 VM 写范围 |
|
|
30
|
+
| limits 放大 | `ABSOLUTE_LIMIT_CEILING` 冻结硬顶(steps 200 / tokens 10M / wall 24h / ctx 5M / toolCalls 32) | 配置只能收紧不能放大 |
|
|
31
|
+
| 死循环检测 | 滑动窗口 40 次内同签名 > 阈值 → `dead-loop-detected` | 覆盖 A/B 交替;另设工具总调用硬顶 |
|
|
32
|
+
| PR 参数 | `branch` 必填 + 白名单校验;受保护分支不可作为 head;`base` 必须是主干 | Worker 不持 push 凭据,PR 由宿主 token 创建 |
|
|
33
|
+
| TOCTOU | 写前 realpath 校验与写入之间仍存在窗口 | P0-2 之后 Shell 默认不可用(fail-closed)已大幅缓解;**根治靠 VM overlay/OS 账户**,见 §VM 前提 |
|
|
34
|
+
|
|
35
|
+
## P1 控制项(v3.2.5)
|
|
36
|
+
|
|
37
|
+
| 项目 | 实现 | 判定 | 测试 |
|
|
38
|
+
|---|---|---|---|
|
|
39
|
+
| **Mandate 一致性** | `agent-loop.ts` 构造时保存 `baseMandate`;执行前比对 `ticket.mandate.scope` | 不一致 → `mandate-mismatch` 失败(不执行);`allowShell` 取交集 | `ticket mandate mismatch…`、`mandate can narrow allowShell but never widen` |
|
|
40
|
+
| **预算兜底** | provider 无 `usage` 时 `ceil(len(history)/4)` 估算并累加 | 估算后立即判 `maxTokens`;另受 `maxContextChars` / 单条响应长度约束 | `token budget circuit breaker`、`missing provider usage falls back…` |
|
|
41
|
+
| **脱敏** | `redactSecrets()` 正则 + `secretValuesOf()` 具体值替换 | 覆盖 sk-/ghp_/github_pat_/AKIA/Bearer/私钥;凭据形态环境变量全收(长度≥8) | `tool results are redacted…`、`every credential-shaped env value…` |
|
|
42
|
+
|
|
43
|
+
> 脱敏是**纵深防御**,不是唯一防线:`exec_shell_in_vm` 仍使用白名单环境变量
|
|
44
|
+
> (KEY/TOKEN/SECRET/`DSH_*` 一律不传给子进程),两者叠加。
|
|
45
|
+
|
|
46
|
+
## token 生命周期(v3.2.5)
|
|
47
|
+
|
|
48
|
+
| token | 用途 | 有效期 | 轮换 | 审计 |
|
|
49
|
+
|---|---|---|---|---|
|
|
50
|
+
| `bridgeToken`(静态) | 母体↔原身派发桥 | 长期(部署决定) | 作为 fallback 长期有效 | 每次校验动作 |
|
|
51
|
+
| 桥轮换 token | 同上 | `bridgeTokenRotateMs`(默认 30m) | 自动 + 手动 | `token-audit.jsonl` |
|
|
52
|
+
| 桥 token 宽限期 | 同上 | `bridgeTokenGraceMs`(默认 5m) | — | 记录 `source=grace` |
|
|
53
|
+
| Worker stream token | Worker→Router 事件回传 | `streamTokenTtlMs`(默认 6h) | done 后立即撤销 | 发起/校验/撤销 |
|
|
54
|
+
| UI stream token | 浏览器订阅 SSE | `uiTokenTtlMs`(默认 10m) | 前端每 4 分钟轮换 | 发起/轮换/校验 |
|
|
55
|
+
|
|
56
|
+
落盘只存 **sha256 摘要**,审计行不含明文;文件权限 0600。
|
|
57
|
+
|
|
58
|
+
## 1. 角色强制(代码级)
|
|
59
|
+
|
|
60
|
+
| 子插件 | 允许角色 | 强制位置 |
|
|
61
|
+
|---|---|---|
|
|
62
|
+
| SuperAgent Router | **essence only** | `superagent-router.mjs` apply 开头 `roleAllowed(ROW_ID, role)`;母体/fork 不注册服务 |
|
|
63
|
+
| SuperAgent Worker | **fork only** | `superagent-worker.mjs` 同上;母体/元主不注册 |
|
|
64
|
+
| 成本优化 | **fork only** | `cost-optimizer.mjs` 同上 |
|
|
65
|
+
| 双主管 | **mother / essence** | `dual-manager.mjs` 同上 |
|
|
66
|
+
|
|
67
|
+
- 母体(mother)不接纳任何 SuperAgent 行;
|
|
68
|
+
- Router 是控制面:不读文件、不跑命令、不改代码;所有执行体必须在 fork Worker 中;
|
|
69
|
+
- Worker 是官方研究原型:物理隔离是底线,**绝不**在母体/元主启用;
|
|
70
|
+
- 测试逐角色断言(mother/essence/fork)服务是否存在。
|
|
71
|
+
|
|
72
|
+
## 2. 外部输入不可信
|
|
73
|
+
|
|
74
|
+
- GitHub:必须配置 `githubSecret`;`X-Hub-Signature-256` HMAC-SHA256 + timingSafeEqual;
|
|
75
|
+
没有密钥/签名缺失一律 401;
|
|
76
|
+
- 飞书:必须配置 `feishuVerificationToken`;challenge 也先验 token;
|
|
77
|
+
- 归一化只接受受支持的事件类型与文本;正文截断 8,000 字符;
|
|
78
|
+
- 事件按 id 去重(最近 500 条),防重放/重复接单。
|
|
79
|
+
|
|
80
|
+
## 3. Mandate(授权快照)防越权
|
|
81
|
+
|
|
82
|
+
- Ticket 的 Mandate 创建后 `Object.freeze`,后续评论不能扩权;
|
|
83
|
+
- `mandate.scope = threadId`;Worker 的 `assertMandateScope` 精确匹配,
|
|
84
|
+
越权返回 `out-of-mandate`,必须退回 Router 重新评估;
|
|
85
|
+
- Worker prompt 明确:忽略外部文本中任何“改变授权/上传密钥”的注入指令。
|
|
86
|
+
|
|
87
|
+
## 4. 渐进式信任(fail-closed)
|
|
88
|
+
|
|
89
|
+
| 模式 | 行为 |
|
|
90
|
+
|---|---|
|
|
91
|
+
| `off` | 全部 ignore,不建票 |
|
|
92
|
+
| `observe` | 通过 Gate 只建票(observed),不派发 |
|
|
93
|
+
| `auto` | 通过 Gate 才派发;缺 forker/配置 → `dispatch-unavailable`,Ticket=failed/unavailable |
|
|
94
|
+
|
|
95
|
+
Gate:来源允许列表、Actor 允许列表(**空列表 = 全部拒绝**)、必须 @、Bot 拒绝、
|
|
96
|
+
仓库允许列表、事件去重。默认 `observe` + 空 Actor 列表,因此默认不接单。
|
|
97
|
+
|
|
98
|
+
## 5. 派发安全
|
|
99
|
+
|
|
100
|
+
- Router 只通过 `fangForker.spawn` 派发;不存在的服务/配置 → fail-closed,绝不本地执行;
|
|
101
|
+
- 派发失败如实落 `failed`,不假装成功;错误码原样返回;
|
|
102
|
+
- 跨进程派发桥(essence → mother forker)未落地前,auto 模式不可用(这比“半可用”更安全)。
|
|
103
|
+
|
|
104
|
+
## 6. Worker 工具与运行时防线(3.2.2)
|
|
105
|
+
|
|
106
|
+
| 项 | 措施 |
|
|
107
|
+
|---|---|
|
|
108
|
+
| 文件读/列 | 限制在 workspace 内;单文件 256KB |
|
|
109
|
+
| 文件写 | workspace + **Mandate.scope** 内;realpath 防 symlink 逃逸;1MB 上限 |
|
|
110
|
+
| Shell | 必须 `mandate.allowShell`;危险命令黑名单;cwd=workspace;超时 120s;输出 16k 截断 |
|
|
111
|
+
| PR | 默认拒绝;只接受宿主注入的 PR 客户端 |
|
|
112
|
+
| 工具异常 | 转 `执行报错` 文本回灌模型,不击穿循环;不可恢复错误按预算/墙钟熔断 |
|
|
113
|
+
| 循环 | maxSteps 15 / maxTokens 50k / maxWallClock 15min / context 400k 字符 / 连续同调用 3 次 |
|
|
114
|
+
| 审计 | 每步 WorkerStep + snapshot(),崩溃后由 Router/会话仓恢复现场 |
|
|
115
|
+
| LLM | 请求超时 120s;错误分类;key 不落日志/stdout |
|
|
116
|
+
|
|
117
|
+
## 7. v3.2.3 新增边界(bridge / streaming / preset)
|
|
118
|
+
|
|
119
|
+
| 面 | 措施 |
|
|
120
|
+
|---|---|
|
|
121
|
+
| 母体派发桥 | `POST /dsh-master/api/bridge/spawn` 必须 Bearer `bridgeToken`(常量时间比较);只接受 ticket + `DSH_MASTER_*` env;只调用 `fangForker.spawn`,无本地降级 |
|
|
122
|
+
| Worker preset 角色 | 编译行 `master-superagent-loop` 只 fork;母体/元主(`DSH_FANG_ESSENCE=1`)直接拒绝执行 |
|
|
123
|
+
| Worker 实时通道 | Router 生成 per-ticket stream token;Worker 只能提交自己 ticket 的事件(token→ticketId 绑定),done 后消费 token |
|
|
124
|
+
| 回写 | 默认关闭;webhook/GitHub token 只在配置内,状态接口只返回模式;失败只告警 |
|
|
125
|
+
| Shell 环境 | 白名单 env,剥掉 `KEY/TOKEN/SECRET/DSH_*`;超时 kill 进程树;解释器 `-e/-c` 黑名单 |
|
|
126
|
+
| 文件写 | workspace + Mandate.scope + 敏感路径(`.git/.dsh/.env/.ssh`)拒绝;realpath 防 symlink |
|
|
127
|
+
| Mandate | Ticket 与构造 Mandate 必须一致(`mandate-mismatch`);权限取交集;工具从 `context.mandate` 取 |
|
|
128
|
+
| 日志/步骤 | secret 正则 + 具体值脱敏;provider 无 usage 时按字符估算 Token 预算 |
|
|
129
|
+
|
|
130
|
+
> 仍坚持:Shell 的高危语义无法用黑名单穷尽,**VM/容器隔离是主防线**;未证明隔离时不要开启 `allowShell`。
|
|
131
|
+
|
|
132
|
+
## 8. 长任务与资源
|
|
133
|
+
|
|
134
|
+
- `exec_shell_in_vm` 超过 5 秒返回后台 job id,`vm_job_result` 查询真实终态与输出;后台结果 10 分钟过期,最多 50 个活动/已完成 job;
|
|
135
|
+
- Worker 的资源配额/网络限制由瓮城分身 VM 提供(OS 账户、CPU/内存/网络出站);
|
|
136
|
+
- Worker 不持有母体凭据、SSH 私钥;GitHub 写回使用专用机器人(3.2.2)。
|
|
137
|
+
|
|
138
|
+
## 9. 当前明确边界
|
|
139
|
+
|
|
140
|
+
- 出站 webhook/GitHub/飞书回写与宿主侧 PR 交付**由配置启用,默认关闭**;全部有超时与结构化错误;
|
|
141
|
+
- 不明文持久化 secret;状态接口不返回 secret;审计只存摘要/元数据;
|
|
142
|
+
- VM 隔离仍依赖显式 `DSH_MASTER_VM_ISOLATION=attested` 声明,未实现自动内核 attestation;
|
|
143
|
+
- 后续修复不自动安装到用户主 profile、不发布 npm/Release。
|
|
144
|
+
|
|
145
|
+
## 10. 后续安全项
|
|
146
|
+
|
|
147
|
+
- [ ] GitHub webhook secret 轮换与多仓库密钥;
|
|
148
|
+
- [x] 飞书 Encrypt Key AES-256-CBC 解密与 verification token 校验;
|
|
149
|
+
- [x] Ticket 审计全链路(评估→派发→完成→回写);
|
|
150
|
+
- [x] Worker 配额、并发上限、超时与失控回收;
|
|
151
|
+
- [x] `GET /superagent/session?ticketId=` 映射;
|
|
152
|
+
- [x] 在母体/元主启动时做“误装 SuperAgent 行”回归测试;
|
|
153
|
+
- [ ] 对 VM attestation 做可验证证明,而不是部署方声明。
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# 真实 Host 集成 smoke(v3.2.5)
|
|
2
|
+
|
|
3
|
+
> 目的:在**真实 DSH Host + 临时 profile** 上验证 Fang↔Master 契约、真实 fork 派生、
|
|
4
|
+
> Worker Agent Loop 与 LLM 调用(LLM 使用本地 OpenAI 兼容 stub,其余均为真实进程)。
|
|
5
|
+
|
|
6
|
+
## 环境
|
|
7
|
+
|
|
8
|
+
```text
|
|
9
|
+
临时 DSH_HOME: E:\GPT\.tmp\dsh-smoke-home
|
|
10
|
+
临时 profile: smoke(from-default-profile web)
|
|
11
|
+
安装包: @local/dsh-fang 3.1.15(tgz)+ @twinsearth/dsh-master 3.2.5(tgz)
|
|
12
|
+
host: dsh --profile smoke --patch dsh-smoke-patch.yml --port 18188 --no-open
|
|
13
|
+
配置:
|
|
14
|
+
Fang contract.token = smoke-contract-token(HTTP 契约端点)
|
|
15
|
+
Fang 安装级 x-fang-auth token = <临时 profile fang-auth.json>
|
|
16
|
+
Master core bridgeToken = smoke-bridge-token
|
|
17
|
+
Worker LLM: 本地 OpenAI 兼容 stub @ 127.0.0.1:18200/v1(返回固定 final answer + usage)
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## 结果
|
|
21
|
+
|
|
22
|
+
| 检查 | 结果 |
|
|
23
|
+
|---|---|
|
|
24
|
+
| 真实 Host 启动 / Fang 3.1.15 | ✅ `/fang/api/status` 200,version 3.1.15,role mother |
|
|
25
|
+
| 契约 HTTP 端点(双 token) | ✅ `POST /fang/api/contract/hello` 200,contractVersion 1.0.0,9 methods |
|
|
26
|
+
| Master 内嵌 SDK HTTP 客户端 | ✅ hello/health 经 `transport=http`;`fang.fork.list` 200 |
|
|
27
|
+
| 契约派发真实 fork | ✅ `fang.fork.spawn` 返回 `forkId=fork_mwbrkf4e2o91`、pid、port 18001、forkSeed 全部成功 |
|
|
28
|
+
| Worker preset 在真实 fork 中运行 | ✅ fork 进程启动,继承 `DSH_MASTER_TICKET`;Worker 自动跑 Agent Loop |
|
|
29
|
+
| LLM 调用(真实 HTTP) | ✅ stub 收到 1 次 `/v1/chat/completions`,请求含 ticket system/user prompt + 5 个安全工具 schema |
|
|
30
|
+
| 超时/清理 | ✅ smoke host/fork/stub 全部停止,无残留监听端口 |
|
|
31
|
+
|
|
32
|
+
## 已知/未覆盖
|
|
33
|
+
|
|
34
|
+
- Router 的 GitHub/飞书 Webhook → 契约 HTTP → 母体 fork 的**双层 Host** 链路未在本 smoke 中跑通
|
|
35
|
+
(需要第二个 essence 角色 Host;本次以直接用契约派发覆盖了核心跨进程路径);
|
|
36
|
+
- UI 实时 SSE、token 轮换/审计、并发配额已由单元/集成测试覆盖,未在浏览器中人工点验;
|
|
37
|
+
- Worker 的 PR/评论回写、真实 LLM(非 stub)未在本 smoke 中验证;
|
|
38
|
+
- 临时 DSH_HOME/profile 与 smoke 包已保留在 `.tmp` 以便复核,可按需清理。
|
|
39
|
+
|
|
40
|
+
## 追加:双 Host 跨进程派发桥 smoke(2026-11-11)
|
|
41
|
+
|
|
42
|
+
```text
|
|
43
|
+
母体 Host: DSH_HOME=smoke2-mother-home / profile=mothersmoke / port=18188
|
|
44
|
+
Fang 3.1.15 contract.fileDir=...\smoke2-shared\contract-outbox
|
|
45
|
+
原身 Host: DSH_HOME=smoke2-essence-home / profile=essencesmoke / port=18189
|
|
46
|
+
env DSH_FANG_ROLE=fork, DSH_FANG_ESSENCE=1
|
|
47
|
+
Master Router: trustMode=auto, dispatch.contractFileDir=同一 outbox
|
|
48
|
+
workerEnv: DSH_MASTER_LLM_* -> stub LLM(18200)
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
结果:
|
|
52
|
+
|
|
53
|
+
| 检查 | 结果 |
|
|
54
|
+
|---|---|
|
|
55
|
+
| Router status | dispatch.mode=contract,contract 路由 file available=true |
|
|
56
|
+
| 签名 GitHub Webhook | 202 decision=accept,Ticket dispatched |
|
|
57
|
+
| 跨进程 file bridge | 原身 Router 写 outbox → 母体 Fang 契约服务读取 → 真实 fork fork_mwbrznrzcalj pid 18128 port 18001 |
|
|
58
|
+
| Worker Agent Loop | fork 继承 DSH_MASTER_TICKET,stub LLM 收到 ticket prompt + tools |
|
|
59
|
+
| Worker→Router SSE | token audit:issue 1 / verify-ok 3 / revoke(ticket-done) 1 |
|
|
60
|
+
| 发现的竞态 | Worker done 可能先于 dispatch() 返回,旧代码把 done 覆盖成 dispatched;已修复并加回归测试 superagent-status-race.test.mjs |
|
|
61
|
+
|
|
62
|
+
> 结论:跨进程派发桥已落地并通过真实双 Host smoke(file 传输;HTTP/legacy 同源实现)。
|
|
63
|
+
> 部署前提:两侧配置同一 contract.fileDir(或 Router 通过 DSH_FANG_MOTHER 自动推导 + Fang 后续版本注入该 env);HTTP 还需 Fang 安装级 x-fang-auth。
|
|
64
|
+
|
|
65
|
+
## 追加:Worker preset bundle 行真实 Host 验证(2026-11-11)
|
|
66
|
+
|
|
67
|
+
临时 profile `loopverify`(DSH_HOME=`E:\GPT\.tmp\loopverify-home`,主 profile 未动),安装打包后的 tgz:
|
|
68
|
+
|
|
69
|
+
| 检查 | 结果 |
|
|
70
|
+
|---|---|
|
|
71
|
+
| tarball 内容 | 87 文件,含 `dist-worker/`(10)与 `dist-host/`(23) |
|
|
72
|
+
| fork Host 状态 | `role=fork`;`superagent-loop` **active=true / registered=true**,capabilities 全量 |
|
|
73
|
+
| 角色门禁 | router/dual 显示 `role-not-allowed:fork`;worker/loop/cost 激活 |
|
|
74
|
+
| 真实 LLM 调用 | stub 收到 `/v1/chat/completions`:model=loopverify-model、messages=2、tools=5(来自安装后的 `dist-worker`) |
|
|
75
|
+
| 修复项 | preset 原先缺少 `name/inject`、未注册子插件、BOM 导致 `ticket-invalid`;本次全部修复并回归验证 |
|
|
76
|
+
|
|
77
|
+
> 结论:TS Worker preset 经 tsc 产物 + bundle 行接入后,可在真实 Host 中按行加载、按角色门禁、自动跑循环并调用 LLM。
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# SMOKE v3.2.7 后续修复:真实双 Host / fork 验收
|
|
2
|
+
|
|
3
|
+
日期:2026-11-12。范围:`E:\GPT\dsh-master` 工作树构建产物;未发布、未安装用户主 profile。
|
|
4
|
+
|
|
5
|
+
## 环境
|
|
6
|
+
|
|
7
|
+
- 真实 DSH mother Host(隔离测试 home,契约 file bridge);
|
|
8
|
+
- 真实 DSH essence Host(加载本工作树 `npm pack` 的 tarball);
|
|
9
|
+
- 由 mother 实际派生 fork,加载同一构建的 Worker preset;
|
|
10
|
+
- 本地脚本化 OpenAI 兼容 SSE provider:真实工具序列 `fs_write_file → fs_read_file → 最终文本`;
|
|
11
|
+
- 本地 HTTP 回写接收端;浏览器为真实 headless Edge + CDP,不是 DOM 模拟。
|
|
12
|
+
|
|
13
|
+
最终票据:`ticket-mwd44ggz-1`(生产默认 `DSH_MASTER_WARMUP_MS=1200`)。
|
|
14
|
+
|
|
15
|
+
## 全部通过的断言
|
|
16
|
+
|
|
17
|
+
| # | 断言 | 实测 |
|
|
18
|
+
|---|---|---|
|
|
19
|
+
| 1 | 回环 UI token 签发 | HTTP 200,随机 token |
|
|
20
|
+
| 2 | UI token 订阅 SSE | HTTP 200 |
|
|
21
|
+
| 3 | GitHub 签名 webhook 异步接单 | HTTP 202 + `pending:true` |
|
|
22
|
+
| 4 | 真实 fork 到达终态 | `done` |
|
|
23
|
+
| 5 | `GET /superagent/session` | 映射状态 `done` |
|
|
24
|
+
| 6 | 真实工具写入 | `e2e-artifact.txt` 内容逐字等于 `dsh-master real deployment 中文断言 ✅` |
|
|
25
|
+
| 7 | 模型/工具序列 | 写文件 → 回读 → 最终回答,真实 LLM 请求历史含 tool result |
|
|
26
|
+
| 8 | HTTP 结果回写 | 接收端取得本 ticket 的成功结果 |
|
|
27
|
+
| 9 | 落库 | `superagent-tickets.json` 中本 ticket=`done` 且 result.ok=true |
|
|
28
|
+
| 10 | 凭据脱敏 | 落盘文件不含 LLM key |
|
|
29
|
+
| 11 | 匿名读取事件 | HTTP 401 |
|
|
30
|
+
| 12 | UI token 读取事件 | HTTP 200,25 个事件,包含真实工具步骤 |
|
|
31
|
+
| 13 | SSE 流式正文 | `REAL-E2E-WRITE-READ-DONE` |
|
|
32
|
+
| 14 | SSE 分批 | `at` 间隔 **188ms / 187ms**,不是一次性聚合 |
|
|
33
|
+
| 15 | 重复 webhook | `duplicate-event`,不重复派生 |
|
|
34
|
+
| 16 | 配额释放 | active=0,remaining=1 |
|
|
35
|
+
| 17 | 文件桥零残留 | spawn 结算后 requests=[]、responses=[] |
|
|
36
|
+
|
|
37
|
+
## 浏览器点验
|
|
38
|
+
|
|
39
|
+
- 「房主」抽屉正常打开,实时事件流显示 `SSE 1`、`live`、`done`;
|
|
40
|
+
- 卡片正文为 `REAL-E2E-WRITE-READ-DONE`,终态后不再错误显示「工具中」;
|
|
41
|
+
- token 轮换:旧 token 换新 token HTTP 200;再用旧 token HTTP 401;
|
|
42
|
+
- 截图:测试证据目录 `ui-final.png`。
|
|
43
|
+
|
|
44
|
+
## 计时(最终票)
|
|
45
|
+
|
|
46
|
+
| 阶段 | 实测 |
|
|
47
|
+
|---|---:|
|
|
48
|
+
| Webhook 202 | 9ms |
|
|
49
|
+
| fork 预置至首个 Worker 事件 | 5,228ms |
|
|
50
|
+
| Worker Agent Loop | 1,963ms |
|
|
51
|
+
| Worker 完成(ticket 创建至 done) | 7,191ms |
|
|
52
|
+
| 验收脚本业务断言完成 | 8,109ms |
|
|
53
|
+
| spawn 完整结算 / 文件桥清理完成 | 60,508ms |
|
|
54
|
+
|
|
55
|
+
> **Worker done ≠ spawn 已返回。** 本环境 Fang 的 spawn 响应可能约 60s 才返回;文件桥零残留必须在传输结算后判定,不能把进行中的请求误报为泄漏。
|
|
56
|
+
|
|
57
|
+
## 三次连续派生性能(保留波动,不 cherry-pick)
|
|
58
|
+
|
|
59
|
+
| Run | fork 预置 | Worker Loop | Worker 完成 |
|
|
60
|
+
|---|---:|---:|---:|
|
|
61
|
+
| 1 | 3,304ms | 2,165ms | 5,469ms |
|
|
62
|
+
| 2 | 59,274ms | 2,496ms | 61,770ms |
|
|
63
|
+
| 3 | 57,168ms | 3,576ms | 60,744ms |
|
|
64
|
+
|
|
65
|
+
Webhook 中位数 4ms;Worker Loop 中位数 2,496ms。后续票出现约 57–59s 的派生等待,说明瓶颈在 Fang/Host 派生与调度,而非 Agent Loop;该波动作为剩余性能问题保留,不宣称「每票 7 秒」。
|
|
66
|
+
|
|
67
|
+
## 清理
|
|
68
|
+
|
|
69
|
+
- 停掉测试 mother/essence/LLM/writeback/browser 进程;
|
|
70
|
+
- 检查测试端口不再监听;
|
|
71
|
+
- 删除本工作流创建的大型 fork seed 副本和失败 profile;
|
|
72
|
+
- 保留小型证据 JSON、截图与本报告;
|
|
73
|
+
- `test:clean` 后测试临时目录残留为 `[]`。
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# SuperAgent(v3.2.8)
|
|
2
|
+
|
|
3
|
+
> 把“用户从多窗口注意力调度中解放出来”的架构跃迁:**Router(常驻控制面)** 与
|
|
4
|
+
> **Worker(隔离执行体)** 分离,Ticket/Mandate 约束授权,渐进式信任控制自动接单。
|
|
5
|
+
> 3.2.8 为审计修复发布线。历史限制与实机证据见 `REVIEW-v3.2.7.md`。
|
|
6
|
+
|
|
7
|
+
## 1. 双层结构
|
|
8
|
+
|
|
9
|
+
```text
|
|
10
|
+
外部事件(GitHub Webhook / 飞书事件)
|
|
11
|
+
│ 验签/归一化(外部输入不可信)
|
|
12
|
+
▼
|
|
13
|
+
┌─────────────────────────────┐
|
|
14
|
+
│ Router(essence / 原身常驻) │ 评估 → Ticket + Mandate → 派发
|
|
15
|
+
│ * 不执行代码、不跑命令 │ * 持久化 Ticket(dataDir 可选)
|
|
16
|
+
│ * trustMode: off/observe/auto
|
|
17
|
+
└─────────────┬───────────────┘
|
|
18
|
+
│ fangForker.spawn(env = ticket)
|
|
19
|
+
▼
|
|
20
|
+
┌─────────────────────────────┐
|
|
21
|
+
│ Worker(fork / 分身 VM) │ 每个 Ticket 一个隔离生产环境
|
|
22
|
+
│ * 只恢复 Ticket、守 Mandate │ * 研究原型:绝不进入母体/元主
|
|
23
|
+
│ * >5s Shell 命令异步后台化 │ * 不持有母体凭据
|
|
24
|
+
└─────────────────────────────┘
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
| 层 | 瓮城物理层 | 本仓库实现 | 角色 |
|
|
28
|
+
|---|---|---|---|
|
|
29
|
+
| 事件源 | 外部环境 | `src/superagent/github.mjs`、`feishu.mjs` | 被动入口 |
|
|
30
|
+
| Router | 原身(安全盘) | `src/plugins/superagent-router.mjs` + `src/superagent/router.mjs` | **essence only** |
|
|
31
|
+
| Worker | 分身(隔离 VM) | `src/plugins/superagent-worker.mjs` + `worker.mjs` | **fork only** |
|
|
32
|
+
| 持久化 | 会话仓 | `ticket-store.mjs`(`<dataDir>/superagent-tickets.json`) | Router 侧 |
|
|
33
|
+
| 派发 | DerivedManager | `dispatch.mjs` → `fangForker.spawn({env})` | Router 侧 |
|
|
34
|
+
|
|
35
|
+
## 2. 核心原语
|
|
36
|
+
|
|
37
|
+
### SourceEvent(外部不可信)
|
|
38
|
+
|
|
39
|
+
`{ eventId, sourceId, eventType, actor, isMention, content, threadId, actorIsBot, repo }`
|
|
40
|
+
|
|
41
|
+
- `sourceId`:`github:owner/repo` / `feishu:<chat_id>`;
|
|
42
|
+
- `eventType`:`issue_comment` / `pull_request_review` / `feishu_message`;
|
|
43
|
+
- 入口先做签名/Token 校验,再进入 Router;文本截断 8,000 字符。
|
|
44
|
+
|
|
45
|
+
### Ticket + Mandate(不可变授权快照)
|
|
46
|
+
|
|
47
|
+
```js
|
|
48
|
+
{
|
|
49
|
+
id: 'ticket-…',
|
|
50
|
+
sourceId, requester, payload, eventType, createdAt,
|
|
51
|
+
mandate: {
|
|
52
|
+
scope: '<threadId>', // Worker 只能在此范围作业
|
|
53
|
+
policyVersion: 'v1.0',
|
|
54
|
+
strength: 'explicit' | 'inferred', // 是否 @ 了 Agent
|
|
55
|
+
allowedSources: ['github'],
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
- Ticket/Mandate 均 `Object.freeze`;
|
|
61
|
+
- Worker 的 `assertMandateScope(ticket, scope)` 精确匹配,越权返回 `out-of-mandate`,必须退回 Router;
|
|
62
|
+
- Mandate 创建后不随 Issue 后续评论修改(防“顺便升级下依赖”式扩权)。
|
|
63
|
+
|
|
64
|
+
### 渐进式信任
|
|
65
|
+
|
|
66
|
+
| 模式 | 行为 | 默认 |
|
|
67
|
+
|---|---|---|
|
|
68
|
+
| `off` | 全部忽略,不建票 | |
|
|
69
|
+
| `observe` | 通过 Gate 的请求**只建票记录**(status=observed),不派发、不改代码 | ✅ 默认 |
|
|
70
|
+
| `auto` | 通过 Gate → 建票 pending → `fangForker.spawn` → dispatched | |
|
|
71
|
+
|
|
72
|
+
Gate(确定性,不调模型):来源允许列表、Actor 允许列表(空列表 fail-closed)、
|
|
73
|
+
必须 @(可关)、Bot 拒绝、仓库允许列表、事件去重。
|
|
74
|
+
|
|
75
|
+
## 3. 事件源
|
|
76
|
+
|
|
77
|
+
### GitHub
|
|
78
|
+
|
|
79
|
+
- `POST /dsh-master/api/superagent/github`
|
|
80
|
+
- 必须配置 `githubSecret`;校验 `X-Hub-Signature-256`(HMAC-SHA256,timingSafeEqual);
|
|
81
|
+
- 支持 `issue_comment(created)` 与 `pull_request_review(submitted)`;
|
|
82
|
+
- `x-github-delivery` 作为事件去重 id。
|
|
83
|
+
|
|
84
|
+
### 飞书
|
|
85
|
+
|
|
86
|
+
- `POST /dsh-master/api/superagent/feishu`
|
|
87
|
+
- 必须配置 `feishuVerificationToken`;支持 `url_verification` challenge 回显;
|
|
88
|
+
- 处理 `im.message.receive_v1` 文本消息;`feishuEncryptKey` 配置后支持 AES-256-CBC 加密事件,解密失败/错误 token 均 fail-closed。
|
|
89
|
+
|
|
90
|
+
## 4. 派发契约(与瓮城)
|
|
91
|
+
|
|
92
|
+
```js
|
|
93
|
+
fangForker.spawn({
|
|
94
|
+
homeDir, cliPath, profile,
|
|
95
|
+
taskTitle: `SuperAgent ${ticket.id}`,
|
|
96
|
+
env: {
|
|
97
|
+
DSH_MASTER_TICKET: JSON.stringify(ticket),
|
|
98
|
+
DSH_MASTER_TICKET_ID: ticket.id,
|
|
99
|
+
DSH_MASTER_TRUST_MODE: 'auto',
|
|
100
|
+
DSH_MASTER_MANDATE_SCOPE: ticket.mandate.scope,
|
|
101
|
+
},
|
|
102
|
+
})
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
- Router 只通过该契约派发,**绝不本地执行**;`fangForker.spawn` 不在、或
|
|
106
|
+
`dispatch.homeDir/cliPath` 未配置时返回 `dispatch-unavailable`。
|
|
107
|
+
- 当前 `dsh-fang >= 3.1.14` 的 `fangForker` 只在母体激活;Router 在元宝运行态
|
|
108
|
+
(essence)内使用 `fangForker` 需要瓮城提供跨进程 DerivedManager 桥(3.2.2)。
|
|
109
|
+
在桥落地前,Router 的 `dispatch.available=false`,auto 模式统一返回 unavailable——
|
|
110
|
+
这是 fail-closed,不会降级成任何本地执行。
|
|
111
|
+
- Worker 从环境变量恢复 Ticket(`ticketFromEnv`);普通分身没有 Ticket 时
|
|
112
|
+
`ready=false, reason=no-ticket`,不产生副作用。
|
|
113
|
+
|
|
114
|
+
## 5. 工程防线
|
|
115
|
+
|
|
116
|
+
| 防线 | 实现 |
|
|
117
|
+
|---|---|
|
|
118
|
+
| 外部输入不可信 | 验签/Token → 归一化 → Gate;payload 永远当不可信文本 |
|
|
119
|
+
| Mandate 防越权 | 不可变 Ticket;Worker 精确 scope 校验,越权退回 Router |
|
|
120
|
+
| 物理隔离 | Worker 只在 fork;Router essence-only;母体不接纳任何 SuperAgent 行 |
|
|
121
|
+
| 长任务不阻塞 | `exec_shell_in_vm` 超过 5s 返回后台 job id,`vm_job_result` 查询真实终态与输出 |
|
|
122
|
+
| 失败不假装成功 | dispatch 失败 → Ticket= failed、decision=unavailable,错误码原样返回 |
|
|
123
|
+
| 去重 | 事件 id 最近 500 条 FIFO(重复事件不刷新顺序);契约幂等键随 envelope 传递 |
|
|
124
|
+
| 持久化 | 可选 JSON 原子写(tmp+rename);重启可恢复 Ticket |
|
|
125
|
+
|
|
126
|
+
## 6. 灰度上线(首发不 auto)
|
|
127
|
+
|
|
128
|
+
1. Router 以 `trustMode=observe` + 空允许列表部署(此时所有事件 ignore,先接入不接单);
|
|
129
|
+
2. 配置 `allowedActors`(可信开发者)和仓库允许列表,继续 observe;
|
|
130
|
+
3. 人工审阅 observed Ticket 的误判率与重复率;
|
|
131
|
+
4. 仅对验证过的项目/actor 切 `auto`;
|
|
132
|
+
5. Worker 完成并通过测试后,按配置启用 webhook/GitHub 评论/飞书回写与宿主侧 PR 交付;默认关闭。
|
|
133
|
+
|
|
134
|
+
## 7. 当前限制(3.2.7 后续审计)
|
|
135
|
+
|
|
136
|
+
- Worker preset、跨进程契约桥、SSE、回写与宿主侧 PR 交付均已实现并有实机证据;
|
|
137
|
+
- `trustMode` 修改仍仅内存,不接 configEditor;
|
|
138
|
+
- HarnessMM 目前是 observe:记录真实 token 与成功率,不虚报节省;EGD/GDR/CCS 属 3.3.0;
|
|
139
|
+
- PM 仍以显式字段创建 PRD,自然语言理解与完整 PJM 健康闭环属 3.4.0;
|
|
140
|
+
- VM 隔离证明目前由 `DSH_MASTER_VM_ISOLATION=attested` 显式声明,不是自动内核 attestation;
|
|
141
|
+
- 后续审计修复尚未发布,未安装到用户主 profile。
|