# Wave 7:最小化 Root-Orchestrator 架构计划(冻结 D1-D13 落地) # Wave 8:Agent Library + Settings V2(supersede §0 的 stage-profiles / agentIds / mcpServers 决策) > **Skill Runtime V4 supersede**:Worker 的 Skill 运行时已迁移到 DeepSeek Harness 原生 > Skill 体系 —— Agent Library 冻结 grant → child-scoped frozen SkillProvider → 官方 > `@deepseek-ai/dsh-tool-skill` 的 durable skill-catalog + `skill({name})` tool + > `` 结果 + 官方 Skill Row UI。`pentester_skill_search` / > `pentester_skill_load` 不再注册(仅历史会话回放兼容)。权威入口见 > [`docs/skill-runtime-v4.md`](skill-runtime-v4.md)。 > > **Skill Runtime V2(历史,已被 V4 取代)**:`*.skills` 升级为一等 Bundle(可整体分配); > Worker 曾使用 `pentester_skill_search` / `pentester_skill_load`。见 > [`docs/skill-runtime.md`](skill-runtime.md)。 Wave 7(D1-D13)其余决策保持不变;本节只记录被取代的决策与新增子系统。 - **Agent Library(新增子系统 `src/agent-library/`)**:Agent(`agents//profile.yml`)与 Skill(`*.skill` bundle)统一注册表。Builtin(随 npm 包发布,只读;Skill 加载 resilient —— 单个 invalid/oversized/duplicate Skill 只产生 diagnostic,绝不导致插件启动失败;只有 builtin agents 根整体不可读 / internal invariant 破坏才 fatal) + Custom(`$DSH_HOME/dsh-pentester/{agents,skills}`,可读写,任何失败只产生 diagnostic + last-known-good)。Agent ID 全局唯一(builtin 优先),Agent Name 也全局唯一;Skill 的 canonical name 全局唯一(builtin 优先,custom 不得重名)。Profile 单继承 (Custom → Builtin/Custom;cycle → `agent_profile_inheritance_cycle`;缺失 base → `agent_profile_base_missing`);引用的 skill 必须存在(`profile_skill_missing` → 仅该 profile 不可用,不影响启动)。Custom 目录 `fs.watch` recursive + 200ms debounce + 全量 rescan(Phase 10)。 - **Skill bundle 契约(Phase 5-7 + Wave 10 ecosystem 兼容)**:canonical Skill identity = `SKILL.md` frontmatter `name`(`SkillDefinition.id === name`);标准第三方格式 `name/description` 无需修改即可加载,extra metadata(license/metadata/allowed-tools…) 忽略。`id:` 仅 legacy:`id === name` → 加载 + `deprecated_frontmatter_id` warning; `id !== name` → invalid(`skill_id_name_conflict`)。`*.skills/` 只是 collection/UI 分组 (可任意嵌套,普通目录亦可作容器);`*.skill/` 是真实 bundle,loader 遇 `*.skill` STOP 不深入。bundle 内 symlink → 拒绝(`skill_bundle_contains_symlink`);SKILL.md ≤ 256 KiB、 bundle ≤ 32 MiB(builtin)/ 10 MiB(custom)(`skill_bundle_too_large`,带 actualBytes/limitBytes)。bundle 目录名 ≠ name → 加载 + warning (`skill_bundle_name_mismatch`)。duplicate canonical name → 冲突双方都不进 registry (`skill_duplicate_name`)。bundle path ≠ identity。`pnpm validate:skills` 用同一 parser 做发布前严格校验(接入 `pnpm check`)。 - **Skill frontmatter = 标准 YAML(Wave 10.1)**:`SKILL.md` frontmatter 用 `yaml` package 解析(非手写行解析),支持 quoted string、literal/folded block scalar、nested metadata、 comments、anchors/aliases、UTF-8。只解释 `name`/`description`/legacy `id`(必须为 string, 否则视为缺失);其余 metadata 忽略且不产生 warning。duplicate YAML key → `skill_frontmatter_invalid`(deterministic,非 silent last-wins)。malformed frontmatter → diagnostic + skip,绝不导致 plugin 启动失败。frontmatter 边界提取只认无缩进的 `---` closing(块标量内缩进的 `---` 不提前结束)。`pnpm package:size` 报告 npm 包体积 (skill bundles 数 / 磁盘大小 / packed / unpacked / files;仅可观测,不设失败阈值)。 - **Settings V2(supersede §0)**:`settings.json` schemaVersion 2,新增 `stageAgents: Record`;V1 自动迁移(补 `DEFAULT_STAGE_AGENTS`)并落盘。 `StageDefinition.agentIds` 删除;默认编排 = `DEFAULT_STAGE_AGENTS`(stages.ts),行为与旧 `agentIds` 完全一致。Pre-engagement 恒为空(不创建 Worker)。分配中的 dangling agent id 保留原样、resolve 时过滤、UI 警告(Phase 17)。 - **stage-profiles.json 机制删除(supersede §0)**:`writeStageProfilesManifest` / `STAGE_PROFILES_FILE` 移除;`publishPentesterPreset` 清理历史残留文件。Root 的 Available AgentProfiles 改为 **PentesterService.listAgentsForStage(stage)** 同步内存读 (SettingsStore + AgentLibrary 组合,run-state.mjs 每次 assembly 求值),条目含 `id/name/skills/source`(Phase 19-20)。 - **Delegation 快照(Phase 23-27 + Wave 10 + Host-private isolation + V4 manifest)**:dispatch 时把 profile 引用的 skill bundle 复制到 **Host-private frozen snapshot**(`pentesterHome/runtime/ skill-snapshots///-/`,在 targetRoot 之外,不进 `/workspace`),目录名始终规范化,复制时忽略 `.git/node_modules/build` 等无关目录, 保留 references/scripts/assets;同目录写 `manifest.json`(V4 frozen grant manifest: name/description/revision/relativeBundle,不含 Host 路径); 写 `input/agent-profile.json`(schemaVersion 3: id/name/source/extendsChain/revision/skills[{id,revision}],只含 metadata)。 完整 Skill Body 唯一入口 = 官方 `skill` tool(child-scoped frozen SkillProvider on-demand 读取);Worker 不能 `cat SKILL.md`,persona 不注入 Skill Catalog / Skill Body (catalog 由官方 tool-skill 从 `ctx.skills` 产生)。 - **Agent Library RPC + UI(Phase 28-39)**:`pentesterAgentLibrary/command` 并入统一 `PENTESTER_HOST_MANIFEST`;Settings → Pentester → Agent Library(Agents / Skills / Stage Assignment),CRUD 全部 atomic 写(tmp+rename),generation 轮询(1.5s)。 - **Worker prompt**:persona = WORKER_BASELINE + 工具 prompt + Resolved systemPrompt + Toolbox catalog(顺序见 delegations.ts `buildWorkerPersona`)。Skill guidance 由官方 tool-skill 的 durable skill-catalog 承担(persona 不再注入任何 Skill 文案)。 **本 wave 明确不做**(可后续建立在 AgentLibrary 上):MCP、远程 Skill Marketplace、 skill extends、多继承、Agent/Skill ID rename、references GUI 文件管理器、运行中 Worker 热切换 Profile/Skill。 ## 0. 定稿决策 - Root Agent 是唯一 Orchestrator,"信息是否足够"由 Root 的 system prompt 判断,TypeScript 不做 criteria engine。 - 6 概念模型:Project / PentestRun / Stage / AgentProfile / Delegation / Deliverable(File)。 - Delegation 取代 Task + AgentRun:一次委托 = 一个 DSH child session;`task_prompt` 为不可变 snapshot。 - Deliverable = 文件(`workspace/stages/-/delegations/D-00N/` 目录),删除全部 store/journal 实体。 - PTAP 整套删除;Worker 禁止 delegate,编排树永远只有 Root → Workers 一层。 - One Workspace = One Primary Target = One PentestRun = One Git 仓库 = One active timeline = One Toolbox 容器 = Many Workers。 **Root 工具面(4 个)**:`pentester_delegate` / `pentester_cancel_delegation` / `pentester_advance_stage` / `pentester_rollback_stage`。Root **不能使用** Worker 执行工具(Wave 14 起唯一名字是 `pentester_exec`;`pentester_container_exec` 是已删除的历史名字):per-Root-agent `tools.restrict` deny 只隐藏这一条 (deny 必须落在 Root 自己的 layer:官方 subagent child 会 join 父 preset, mount 级 deny 会把 worker exec 也挡给 Worker;且 tools.restrict 对未知名字 整体拒绝,不能把别的 preset 才注册的工具名加进来)。通用 subagent/job/bash/fs 工具由**组合排除**:本 deployment 把它们作为 per-preset 行(standard 等 preset 自带),pentester preset 组合不带 → Root 天然看不到。**工具注册在 preset scope**: 宿主不向 global registry 注册 pentester 工具(standard Session 完全看不到), preset 行 run-state.mjs 经宿主 `pentester` service 注册。 **Worker 工具面(1 个执行工具)**:`pentester_exec`(Wave 14 前叫 `pentester_container_exec`)。不增加 fs/complete/submit/status/sync/git 工具。 **Bootstrap 位置(定稿)**:**Pre-engagement 不创建 Worker** —— 由 User + Root + `ask_user_question` 完成(targets / scope / RoE / language)。 **首次初始化在 `pentester_advance_stage`**:无 run.json 时, `pentester_advance_stage({run, summary})` 承担 bootstrap —— 创建 workspace 骨架、 run.json、target 文件,git init + 初始 commit,然后同一调用完成 pre-engagement checkpoint 并激活 intelligence-gathering。**`pentester_delegate` 不 bootstrap**: 无 Run 时明确报错(先完成 pre-engagement 并调用 advance_stage 初始化)。 **Root 每次推理前注入的 compact state 包含 Available AgentProfiles**:当前 stage 的合法 AgentProfile 列表由 Settings V2 `stageAgents[currentStage]` ∩ AgentLibrary 生成(AgentLibraryManager 组合根;run-state.mjs 经 `ctx.pentester.listAgentsForStage()` 同步内存读取,不写 preset 目录文件)。Root 只从该列表选 agent id。 **全 Workspace git 跟踪(Owner 要求完整回溯)**:不忽略 `work/`、`traffic/`、 `*.pcap`、`*.mitm`;`.gitignore` 只保留 `tmp/ cache/ *.log`。 **远程阶段边界(§24)**:advance 时先 remote → local 同步 Worker 数据(本地 bind 下 no-op),再 promotion / summary / transition / git checkpoint / local → remote。 **Worker 执行 = DeepSeek Harness 官方 Subagent seam(定稿)**: 不再维护自定义 `ctx.agents.create()` / `agent.inject()` / `agent.followup()` / `agent.whenIdle()` Worker lifecycle。Worker 走: `ctx.subagents`(Service,dsh-base 已挂载)→ provider `spawn` (`@deepseek-ai/dsh-subagent-spawn-in-process`,dsh-base 已注册)→ durable continuable child(`startContinuable` / `followup` / `interrupt`;跨 Activation 持续,FIFO inbox,官方管理 cold resume)。无 `ctx.jobs`、无 one-shot `SubagentRun.result/dispose`。基础设施缺失(`ctx.subagents` / provider 不存在) **fail loud**,禁止 fallback 回自定义 lifecycle。Root 不获得通用 subagent/job/bash/fs 工具(组合排除 + per-Root deny,见 §0),只通过 `pentester_*` 领域工具编排。Worker prompt 拆分:`persona` = WORKER_BASELINE + AgentProfile.systemPrompt(官方 persona 模板);`prompt` = standalone task context(Delegation ID / Stage / Target / RoE / 目录 / 完成契约 / task_prompt)—— spawn child 不继承 parent transcript,所以 task_prompt 必须自包含。 worker toolFilter = `{ deny: [4 个 pentester Root 工具 + ask_user_question] }`。Delegation ↔ childSessionId 绑定:startContinuable 发布后宿主立即把 `childId` 写入 `Delegation.sessionId`(`pentester_container_exec` 据此解析 worker work 目录并校验调用者身份)。 **Pre-engagement 固定问卷(定稿)**:Pre-engagement 由 User + Root + 原生 `ask_user_question` 完成,使用固定结构问卷(五问一次调用): `target_confirm` / `scope_confirm` / `exclusions_confirm` / `roe_confirm` / `language_confirm`。不封装 `pentester_ask_user`,不临时加问。 ## 1. 目标架构 ``` User ⇄ DSH Root Session(pentester persona preset) │ Root 工具面 4 个(见上) ▼ Delegation(并行、可多个)── 每个对应一个 DSH child session │ Worker prompt = baseline + AgentProfile.systemPrompt │ + host 注入 trusted metadata + task_prompt ▼ pentester_exec(ExecutionService → FrozenRuntime → Docker/SSH;Root 不可见) ▼ workspace/stages/-/delegations/D-00N/ ← Deliverable 就是文件 ▼ Worker 是 durable continuable child:写/更新 result.md 作为当前可交付结果, 可接收 Root 的 send_message 后续;Root 自行判断再派 / advance / rollback ``` Root 每次 reasoning 前,宿主从 `run.json` 注入 **compact state** (target / branch / run 状态 / current stage / stage 状态 / delegations 摘要), 经 preset `systemPrompt.context`(text 函数每次 assembly 求值)实现;不注入 完整 run.json,不提供 status 工具。 ## 1b. Workspace + Git Checkpoint - 交付树 = `workspace/`(= 容器 `/workspace`)。固定骨架每次 run 一致: `.git/` `.dsh-pentester/`(run.json/workspace.json/events.jsonl) `target/`(target.json scope.md roe.md inputs/)`stages/-/ {summary.md, delegations/D-00N/{input,work,artifacts,evidence,result.md}}` `findings/ report/ traffic/`。 - **run.json 位于 workspace git 树内**(`workspace/.dsh-pentester/run.json`): 阶段 checkpoint 的 commit 记录 transition 后的机器状态,rollback checkout 后 状态随 git 还原。这是"commit 里 current completed / next active"(T16)与 rollback 恢复(§31)的前提。 - Workspace 挂载:本地 Docker → host `workspace/` **bind mount**(无复制版); 远程 Docker → 共享 named volume,阶段边界才 full sync。 - 同步(仅远程)为 **mirror 语义**:create/update/delete 都反映;pull 排除 `.git/` 与 `.dsh-pentester/`(Host 控制态,防止 stale remote 覆盖宿主 run.json)。 - Stage 生命周期 `pending → active → reviewing → completed`。 - advance = mechanical validation(无 running/starting delegation)→ **Stage Advance Policy gate**(`stageAdvancePolicy` 为 manual/custom 时,先验证 Root Session 内存在针对当前 Stage 的 fresh 人工推进确认 —— ask_user_question id `stage_advance_confirm:`、选项 推进/暂不推进(reporting 为 确认完成/暂不完成)、结果事件 time 晚于该 Stage 当前 entry;缺失/拒绝/过期 → 稳定错误 `stage_advance_confirmation_required`;见 src/pentest/stage-advance-approval.ts)→ 远程先 remote→local sync(§24)→ promotion → 写 summary.md(Root 提供)→ run transition → saveRun → git checkpoint(`ptes(-): complete` + tag)→ push。首次 advance(无 run.json)额外承担 bootstrap(见 §0)。 - **result.md = 当前正式可交付结果**:Worker 写/更新 result.md 作为当前产出 (不是 Session 终止标志)。continuable child 跨轮持续;宿主不自动 settle/完成 检测,不自动生成。 - delivery receipt(result.md + artifacts/evidence 文件清单)宿主可按需读取, 只存在于运行时响应,不持久化 INDEX/registry。 - 不需要:STATUS.md、INDEX.*、manifest.json、logs/、Deliverable Registry、 全局 assets.json。 - git 仓库宿主侧管理(`workspace/.git`),Worker 容器内无 .git;输出语言: 宿主生成文件用 run.language,Worker 文件跟随 task_prompt 语言,工具原始输出 保持原样。 ## 2. 源码布局 ``` src/ ├── index.ts # cordis 入口:apply/Config/inject;preset 发布;ctx.provide('pentester') 发布工具实现 + Settings RPC ├── model.ts # 6 概念类型 + Delegation status + StageDefinition + StageStatus ├── stages.ts # 7 个 PTES StageDefinition ├── agent-library/ # AgentLibrary:profiles+skills 加载/继承解析/库/watcher/manager/RPC │ │ # (types/paths/profile-loader/profile-resolver/skill-loader/ │ │ # library/watcher/manager/rpc/invocations) ├── store.ts # run.json 读写(workspace/.dsh-pentester/run.json;原子写、schemaVersion) ├── workspace.ts # 工作区骨架/target 落盘/events.jsonl/summary/promotion ├── git.ts # GitCheckpointService(init/checkpoint/rollback timeline,injectable runner) ├── dsh.ts # DSH seam:spawn child、cancel、完成捕获、followup 唤醒、trusted caller ├── delegations.ts # Delegation 生命周期 + task_prompt snapshot + 目录骨架 + result.md 契约 + receipt + childSessionId 绑定 ├── tools.ts # 4 个 Root 工具(注册经 pentester service,preset scope)+ caller=root 校验 + advance + rollback ├── worker-tools.ts # container_exec(注册经 pentester service,preset scope)+ caller=worker-only 校验 + 默认 cwd=work/ ├── docker/ │ ├── host.ts # docker host 解析 + 连接探测 + 引擎构造 │ ├── connection.ts # Settings 测试连接(TLS/SSH) │ ├── images.ts # Toolbox 白名单 │ └── runtime.ts # 硬化、容器/卷生命周期、exec、bind/volume、mirror sync、recovery ├── rpc.ts / settings-store.ts # Settings UI 的 Typert RPC 与全局设置 └── ui/client/ # 仅 Settings(Docker Engine Tab),无数据面板 presets/pentester/ # persona + ask_user + run-state.mjs(preset scope 工具注册 + per-Root deny + 动态状态注入) ``` ## 3. 关键设计 - **Delegation 生命周期**:starting → active → cancelling → closed(或 interrupted/failed);resultSummary 由 Root 在 cancel 时提供(reason)。 cancel 携带经验证的 Harness interrupt authority(live Root agent 为 ancestor,或 registry 记录的 Root session id 为 direct parent); UNAUTHORIZED/interrupt 失败 fail loud,绝不伪装 closed。interrupt 准许后 先进入 cancelling 并排空该 Delegation 已准入的 in-flight command,再落 closed(§18 quiescence)。 task_prompt/objective 持久化为不可变 snapshot;后续 steer/followup 不改写。 - **完成信号**:continuable 模式下无 host 自动 settle —— Worker 是 durable child,写/更新 result.md 作为当前可交付结果;Root 自行判断 advance / cancel / 再派;Worker 经 report 工具主动上报重要发现(Worker lifecycle 属 DSH, 宿主只管 PTES 领域状态)。 - **并行**:delegate 批量 assignments 走三阶段批处理 —— ① 纯 preflight (agent/stage/objective/task_prompt/路径泄漏/MCP gate/skill pack gate 全部 通过 → 生成 D-ID 顺序分配的 immutable DelegationPlan;任一失败整批拒绝, 0 side effects)→ ② reservation(目录 + run.json + event)→ ③ dispatch (逐个 spawn;单个 spawn 失败只把该 Delegation 落到 failed,其余继续, 绝不留下长期 starting)。 - **目录**:每 Delegation 独立 `workspace/stages/-/delegations/D-00N/` (input/work/artifacts/evidence),prompt 告知"只写自己目录、可读全 workspace/"。 - **dockerode 层**:窄接口 seam 供测试注入 fake。安全不变量:镜像白名单、 禁 privileged/host net/pid/ipc、capAdd 白名单、禁 docker.sock、 HostConfig 的 Binds **与 Mounts** 双语法校验、1 PentestRun = 1 容器。 - **exec(F09/F10)**:multiplexed stream 解码用官方 docker-modem `demuxStream`;结果为 discriminated status (completed / timed_out / cancelled / runtime_error),未知退出状态绝不 报成功。绝对 deadline 覆盖 guard 安装 → create → start → 流消费 → inspect → 终止序列;timeout/AbortSignal 经 host-installed supervision guard(独立进程组 + PID 文件)真正终止容器内命令(TERM → grace → KILL)。 stdout/stderr 保留量按流有界(BoundedOutputSink + truncated 标记)。 - **Worker 执行准入(F01)**:pentester_exec 在任何 Runtime 副作用 前校验 run.status === active 且 delegation 处于 active/starting;exec 开始前重读 authoritative run.json(同 run.id / instanceId / 状态)作 race fence。stop/restart 取消链先排空 in-flight command 再做 destructive cleanup。 - **Worker fs**:跨 Delegation 写隔离当前为 prompt 级协作约束(只写自己的 delegation 目录);Host 控制态(`.git/`、`.dsh-pentester/`)除外 —— 它在 本地与远程模式下都物理不可见/不可写(见 workspace.md 的 PathPolicy 与 本地挂载布局)。跨 Delegation 隔离是有意接受的设计(owner 决策): - 不实现 UID 隔离 / ACL / filesystem jail / 独立 container / mount namespace; - 不把该约束宣传为 security boundary; - 原因:多个 Worker 属于同一个授权 Pentest Project、同一个信任域,当前架构 优先保证共享 Workspace、多 Agent 协作、实现简单和可维护性。 Worker 默认只写自己的 Delegation 目录,但整个 `/workspace` 全局可读,且技术上 同一共享 Container 内 Worker 仍可能写入其他 Workspace 路径 —— 这是接受的边界。 - **Worker prompt 注入**:delegationId/stage/workspaceDir 由 host 注入 metadata 段; persona 不注入 Skill Catalog / Skill Body(完整 Skill Body 唯一入口 = 官方 `skill` tool,见 Host-private frozen snapshot)。 - **安全让步(显式记录)**:无程序化 approval/scope 检查,RoE/Scope 作为数据 注入 prompt + engagement 开始前 ask_user 预授权门 + 镜像白名单 + 容器硬化。 ## 4. Rollback / Rework Timeline - `pentester_rollback_stage({stage, reason})`:stage 是领域 StageId(如 intelligence-gathering),rollback 到该 stage 的 checkpoint,从**下一 stage** 重新开始(§27)。 - Host 流程:cancel 当前 starting/running delegation → quiescent → (远程)pull worker 数据 → dirty 则建 `backup/rollback-` + WIP commit (`wip: before rollback to `)→ 在 HEAD first-parent reachable 历史中 按 subject `ptes(-): complete` 找 checkpoint → 从 checkpoint 建 `rework/-`(n 自动递增)并 checkout(run.json 随还原)→ (远程)push → 返回 delta。 - D-ID 在 branch timeline 内跨 Stage 全局递增;rollback 后新 branch 允许复用旧 编号(不搞跨 branch global allocator)。 - tag 冲突(T25):main 与 rework 都完成同一 stage 时,`checkpointStage` 遇已 存在 tag 直接跳过(tag 只作人类可读 ref,正确性靠 commit subject)。 - 不重写历史:当前 active branch 原历史不得 reset/rewrite;不自动 merge、 不自动删旧 branch。 ## 5. Recovery - remote volume 还在 → 复用(不 local→remote 覆盖)。 - container 丢失但 volume 存在 → 重建容器复用 volume。 - container 与 volume 都丢失 → 新建 volume 并从本地 mirror 恢复。 - 未完成 Delegation:reconcile 标记 failed/interrupted,不自动重跑; Root 决定是否重新 delegate。 ## 6. 测试计划 - model/stages 校验;Agent Library:profile 继承/cycle/缺失 base、skill bundle 扫描 (symlink/大小)、uniqueness、last-known-good、watcher、settings V1→V2 迁移; store 原子写 round-trip。 - Delegation 生命周期(fake DSH client):批量并行、cancel、continuable start/interrupt、task_prompt 不可变。 - 工具 caller 身份:worker 调 Root 工具被拒、Root 面无 container_exec。 - rollback:cancel workers、backup branch、rework branch、checkpoint 查找、tag 冲突。 - docker 层(fake engine seam):白名单拒绝、HostConfig 硬化(Binds+Mounts)、 bind/volume 选择、mirror sync、cleanup 404 幂等 / 403/500 上抛、stop 后 容器真实消失复核、recovery。 - exec 硬化(fake engine):官方 demux 的全分片 property 测试(每个切点等价)、 结构化 timeout/cancel/runtime_error、inspect 失败 ≠ exitCode=0、输出有界、 supervision TERM→KILL 真实终止、AbortSignal 传播;真实 Docker E2E (opt-in DSH_PENTESTER_E2E_DOCKER=1):命令停止心跳验证、本地挂载布局下 控制态不可见、stop/resume/cleanup 幂等。 - Worker 准入与取消:closed/stopped/completed 在任何 Docker 副作用前拒绝、 pre-exec authoritative recheck(run 被替换即中止)、cancel authority 契约 (child-as-parent 必须失败、UNAUTHORIZED 不吞成 closed)、cancelling 排空。 - run 一致性:并发 cancel 不丢更新、saveRun revision fence 拒绝 stale 覆盖、 updateRun revision 单调、serial queue 无 idle 残留。 - run-state:compact 状态渲染、branch 读取。 - Worker prompt 组装 snapshot。 ## 7. 新增依赖 - `dockerode` + `@types/dockerode`:容器管理/exec 库。`docker-modem`(dockerode 同源 ^5.0.7):官方 multiplexed stream demuxStream(exec 解码,不自研 framing parser)。`tar-stream`:volume 同步。`zod`:Settings RPC strict codec。其余零新增。 ## 8. 本 wave 明确不做 approval 流 / scope 参数级检查;数据面板 UI;HTML/PDF 报告(report.md 即终稿); 多 PentestRun 并发;stage 跳跃 override;Worker 二级委派;workspace_fs / UID / ACL 文件隔离;Git LFS。 --- # Wave 9:AgentProfile MCP Assignment + 官方 DSH MCP Runtime(Settings V3) > supersede:§0(Wave 7)中 "mcpServers 决策延后" 与 Wave 8 §0/§77 的 > "本 wave 明确不做:MCP"。Wave 7/8 的其余决策不变。完整架构与行为见 > `docs/mcp.md`(本 wave 的详细设计文档)。 ## 0. 决策 - MCP 接入**只**走官方 `@deepseek-ai/dsh-mcp-client`(0.1.0-rc.8,与 仓库 DSH rc.8 family 对齐):不实现 MCP 协议 / JSON-RPC / stdio spawn / HTTP transport / reconnect —— 全部官方拥有。 - 一个 MCP Server = 一个官方 plugin fiber(`ctx.plugin(dsh-mcp-client)`, Host 加载,连接生命周期 = Host;不是 per-Worker 连接)。 - **Connection lifecycle = Host;visibility/assignment = AgentProfile/ Delegation**:Worker 的 MCP 可见性由 `startContinuable` 的 `toolFilter.allow`(base Worker tools + assigned MCP tool names)决定。 - AgentProfile 新增 `mcps` / `mcpsRemove`(继承语义与 skills 同构); ResolvedAgentProfile 增加 `mcpIds` / `inheritedMcpIds`。 - **Root 工具收敛从 deny-list 改为正向 allow-list(ROOT_ALLOW)**: deny-list 无法防住未来新增的 global MCP 工具自动进入 Root —— 这是从 deny-list 迁移的核心理由(见 docs/mcp.md「Root 工具隔离」)。 - **Worker toolFilter 从 deny-list 改为 allow-list**:每个 Delegation 的 精确工具名集合;frozen allow-list 不随服务器 tool list 变化扩大 (权限不扩大不变量)。 - Mandatory Gate:dispatch 前校验 profile.mcpIds 全部 available/enabled/loaded/有工具,否则 Delegation fail loud (mcp_* 确定性错误码),child 不创建。 - MCP secrets:`env`/`headers` 值支持 `${ENV_NAME}` 模板,mount 前在 Host 内存展开;resolved 值绝不落盘 / 不回传 UI / 不进日志。 - settings.json V2 → V3:新增 `mcpServers`(自动迁移,原子写); agent-profile.json V3 → V4:新增 `mcpAssignment`(mcpIds + toolNames, 无 secret 值)。 - 禁止:`mcp_lookup` / `pentester_mcp_search` / `pentester_mcp_load` / MCP Tool Bundle / CapabilityManager 式抽象 / MCP 放进 Kali 容器 / 模型控制 command/args/env/url/headers。 ## 1. 落点(文件) ```text src/mcp-runtime.ts 新增:definition 模型 + 校验 + env 模板 + Host runtime src/ui/client/mcp-section.tsx 新增:Agent Library 的 MCP tab src/ui/client/mcp-section.module.css src/agent-library/types.ts mcps/mcpsRemove + mcpIds/inheritedMcpIds src/agent-library/profile-loader.ts 解析 mcps/mcpsRemove src/agent-library/profile-resolver.ts 继承解析 + ResolvedAgentProfile src/agent-library/library.ts applyOverride/serialize/AgentView/NewAgentInput src/agent-library/invocations.ts MCP DTO schema + RPC input(mcp.* commands) src/agent-library/rpc.ts mcp.create/update/delete/reconnect + reconcile src/settings-store.ts V3(mcpServers + 校验 + 迁移) src/delegations.ts Mandatory Gate + toolFilter allow-list + V4 snapshot src/dsh.ts workerToolFilter()(allow-list) src/index.ts McpRuntime 挂载 + reconcile presets/pentester/run-state.mjs ROOT_ALLOW(正向 allow-list)+ MCP in Available AgentProfiles ``` ## 2. 测试(新增于 Wave 9) - test/mcp-runtime.test.ts:真实官方集成(真实 stdio fixture server: tools/list → ctx.tools.register → ctx.tools.execute → tools/call)、 env 模板端到端、失败路径、reconcile 生命周期、Mandatory Gate - test/agent-yaml-roundtrip.test.ts:mcps/mcpsRemove roundtrip + 继承 + override + 手写 YAML - test/agent-library.test.ts:mcp.* RPC CRUD + snapshot DTO + secret 契约 - test/delegations.test.ts:MCP assignment → toolFilter allow-list + V4 snapshot + Mandatory Gate fail loud - test/agent-library-client.test.ts:MCP tab / Overview MCP 卡 UI regression - test/run-state.test.ts + test/tools.test.ts:ROOT_ALLOW allow-list 契约 ## 3. 本 wave 明确不做 MCP Resources/Prompts(官方也只桥接 Tools);per-Worker 动态 MCP grant; scope 参数级检查 / approval 流(安全边界不变,见 docs/mcp.md「安全边界」); MCP sidecar 容器;程序化 reconnect 策略(用官方自带 reconnect + UI 显式 Reconnect)。 # Wave 11:Builtin Agent Topology V3(19-Agent Specialist Architecture + Settings V4) > supersede Wave 8 的 9-Agent 默认拓扑与 Settings V3 的 stageAgents 默认值语义。 > 详细分配见 docs/agent-skill-allocation.md(权威文档)。 ## 0. 决策 - **19 Builtin Agents**:保留 9 个既有 ID(recon/web/vulnerability/validation/ impact/threat-model/reporting/active-directory/cloud),新增 10 个 (network/api-auth/src-hunter/source-code/mobile/reverse/ai-security/ windows/forensics/ctf)。ctf 为 Library-only(不出现在任何 DEFAULT_STAGE_AGENTS,但 Settings/Library 可见、可手工加入 Stage、 Custom Profile 可 extends)。 - **Skill curation 原则**:不追求机械 100% 授权。PTES_CORE/PTES_SPECIALIST/ SRC_BUG_BOUNTY/LAB_CTF 必须有 primaryOwner;防御性 Skill(SOC/检测工程/ 合规/加固部署/IR 恢复/治理)intentionally unassigned。全 961 Skill 完成分类。 - **Bundle 策略**:只有高一致性 bundle 整体授权 —— reverse→reverse-skills、 ctf→ctf-skills。anthropic-cybersecurity(817 leaves)/hack-skills(102)/ pentest-skills / strix 跨多领域,全部 leaf 级 grant,禁止整体授权。 - **web 拆分**:Web & API Agent(119 skills)拆为 web(70,Web Application specialist)+ api-auth(55,API & Identity);LLM/MCP→ai-security; 白盒源码→source-code;Bug Bounty workflow→src-hunter(48)。 - **每 Agent resolved allowedSkillIds ≤ 80**(Native Catalog 硬上限; audit script 校验)。Overlap:1 owner 最佳 / 2 正常 / 3 需理由 / >3 禁止。 - **Settings V4**:新增 `stageAgentsDefaultsVersion`(当前 = 2)。 V3→V4 迁移按 Stage 判断:配置仍精确等于旧默认(LEGACY_STAGE_AGENTS_V1, 顺序敏感 deepEqual)的 Stage 升级到新默认;用户改过(含改过顺序)的 Stage 原样保留。dockerHost/toolboxImages/mcpServers 逐字保留。 - **Root 状态注入瘦身**:Available AgentProfiles 注入 id/name/description + MCP 行(不再注入每个 Agent 的 Skill 列表——几十条 skill name 会稀释 Root 注意力;Worker 自身的 native catalog 才是 skill 路由面)。 - **Root persona**:新增最小派发原则(smallest set of specialists)与 specialist routing guide;Worker handoff 语义(Worker 不能 delegate, 推荐→Root 决策)。 ## 1. 落点(文件) - `agents/<10 new>/profile.yml`:新 profile(systemPrompt 遵循 §79 通用约束)。 - `src/stages.ts`:DEFAULT_STAGE_AGENTS(19-Agent 拓扑)+ CURRENT_STAGE_AGENT_DEFAULTS_VERSION + LEGACY_STAGE_AGENTS_V1(迁移基线)。 - `src/settings-store.ts`:Settings V4(schemaVersion 4 + stageAgentsDefaultsVersion + upgradeStageAgentsDefaults per-stage 迁移)。 - `src/tools.ts` + `presets/pentester/run-state.mjs`:listAgentsForStage 摘要含 description/mcps;run-state 渲染 description 而非 skill 列表。 - `presets/pentester/agent.cordis.yml`:MINIMAL DISPATCH + SPECIALIST ROUTING + WORKER HANDOFF 段。 - `scripts/audit-agent-skill-allocation.mjs`:只读审计(19 agents / ≤80 / stage coverage / overlap / 无未解析引用),exit 0 = 通过。 - `test/agent-topology.test.ts`:结构断言(19 IDs / catalog size / bundle 语义 / stage matrix / ctf gate / V4 迁移矩阵)。 ## 2. 本 wave 明确不做 Worker 二级委派(Worker 仍只推荐、Root 决策);skill 自动推荐引擎变化 (Search V2.3 保留现状);MCP/Docker Runtime 语义变化;Custom Agent / user override / existing Delegation frozen grant 任何行为变化。 # Wave 12:Builtin Skill Intake(src-6k-hunting + Skill Pack v1.0.2) - 用户提供的 Grok「换机身份包」审计后取其核心 Skill:中文 SRC 生态挖洞工作流 (种子队列资产扩张 + 打穿短表指纹路由 + 49 知识模块 + 最小伤害红线 + 中文 报告格式),规范化为 standalone builtin skill `src-6k-hunting`(剥离 Grok 宿主配置/MCP/换机 junk;~/.grok 路径归一为 bundle 相对路径)。 - Owner:primary src-hunter + secondary web(COMPLEMENTARY:与 payload 库驱动 的 src-hunter skill 正交,方法论/中文化/红线体系为增量价值)。 - Curation:S0962 card + family security-test-routing-and-orchestration (secondary attack-surface-discovery)+ 5 条高置信 relations。 - Skill Pack v1.0.2(962 leaves / 6 bundles / 6 standalone);embedded manifest 同步;pre-existing 1.0.1 未发布不一致按「不复用版本号」规则处理(1.0.1 曾 代表的内容在 manifest 历史,新内容走 1.0.2)。 - Default Revision(同日):用户 UI 调整固化 —— recon +dark-web-monitoring /external-network-pentest(28→30)、web +src-hunter skill(71→72); Settings 破坏性操作(Reset/Delete)改为两步行内确认,移除全部 window.confirm。 # Wave 13:PTES Shared Worker Capabilities(Settings V5) > additive 扩展:AgentProfile 语义不变、Root allow-list 不变、Native Skill / > MCP runtime 不变。权威实现细节见 docs/architecture.md「Worker Capability > Composition」、docs/skill-runtime-v4.md §3.4、docs/mcp.md「MCP Assignment > Sources」。 ## 0. 决策 - **三层 additive capability**:Effective Worker Capability = Agent intrinsic (ResolvedAgentProfile)∪ PTES Global Worker grant ∪ 当前 Stage grant。 只有 ADD/GRANT(无 Remove/Deny);不该共享的能力继续放 AgentProfile。 Agent 的 skillsRemove/mcpsRemove 只作用于 Agent inheritance,绝不减除 Shared policy。 - **Settings V5(additive)**:新增 `ptesCapabilities: { global, stages }` (`PtesCapabilityGrant = { skills: SkillGrantRef[], mcps: string[] }`)。 `stageAgents` 不重构(Manager/RPC/Overview/UI/tests 均围绕它稳定运行, additive schema 风险最低)。V4 → V5:ptesCapabilities 默认全空,其余字段 逐字保留,升级后运行行为零变化。pre-engagement(Root-only)恒空,RPC 写 路径拒绝(`ptes_capability_root_only_stage`)。 - **effective 冻结**:preflight 一次 resolve(纯函数 `resolveEffectiveCapabilities`,Bundle 展开复用 expandGrantRef/ resolveBundleLeaves)→ 冻结进 `DelegationPlan.capabilities`;dispatch 只 消费 plan,不重新读 Settings/Profile(TOCTOU 收敛)。Effective Skills 进 同一个 frozen native catalog(manifest schema 不变);Effective MCP 进 同一个 frozen toolFilter。已创建 Delegation 不变;新 Delegation 用新配置。 - **gate 作用在 effective 层**:MCP Mandatory Gate 校验 effective mcpIds (Shared MCP = required capability,不可用 → preflight fail,0 side effects);Skill availability gate(pack missing → builtin_skill_pack_required / 悬空 → ptes_skill_not_available)。agent-profile.json → V5(skillGrant/ mcpAssignment 记录 agent/global/stage 三层来源 + effective 冻结值,无 secret)。 - **Root 不继承 Shared**:ROOT_ALLOW 不变;Root 状态注入新增 Shared Worker Capabilities 轻量摘要(Skill 只 count、MCP 给 id、全空省略),Available AgentProfiles 仍只含 Agent 自身 assignment(不逐 agent 重复 Shared)。 - **RPC**:`ptes.global.set` / `ptes.stage.set` / `ptes.capabilities.reset` 与 stage.set/stage.reset 分离(两个 Reset 语义互不牵连)。写路径强校验 (ref 存在、MCP 已配置、pre-engagement 拒绝);启动加载只做形状校验。 - **引用 guard**:mcp.delete 被 AgentProfile / Global / Stage 引用 → `mcp_in_use`;custom skill.delete 被 Shared(skill 级或所属 bundle)引用 → `skill_in_use`。MCP disable 仍允许(临时关闭 → gate fail loud 兜底)。 - **effective catalog 治理**:Agent ∪ Global ∪ Stage ≤ 80 (MAX_EFFECTIVE_SKILL_CATALOG,与 builtin 拓扑同一上限)。save-time 校验 覆盖 ptes.global.set(全部 Stage)/ ptes.stage.set(该 Stage)/ stage.set (Agent 加入 Stage)。 - **UI**:Agent Library 第 4 个内层 Tab 更名 PTES Assignment(tab id 'stages' 不变):顶部 Global Worker Capabilities 卡 + 每 Stage Agents grid + Shared Skills/MCP 管理(picker 复用 Manage Skills Modal 结构:search/Bundle tri-state/Standalone/Custom-first,但只有 Selected/Unselected;提交 = computeGrantDiff(inherited=∅),全勾 Bundle 存 bundle ref、partial 落 leaf) + pre-engagement Root-only(无 Manage 按钮)。Overview 只加轻量 counts (无 Skill DTO / MCP id)。 ## 1. 落点(文件) - `src/ptes-capabilities.ts`(新增):类型 + 解析 + resolveEffectiveCapabilities + MAX_EFFECTIVE_SKILL_CATALOG。 - `src/settings-store.ts`:V5 + 迁移 + setPtesGlobal/Stage/resetPtesCapabilities。 - `src/agent-library/manager.ts`:resolveDelegationCapabilities + sharedCapabilitiesForStage + snapshot.ptesCapabilities。 - `src/delegations.ts`:DelegationPlan.capabilities + preflight effective gates + dispatch 消费 plan + agent-profile.json V5。 - `src/agent-library/invocations.ts` + `rpc.ts`:ptes.* mutations + 写路径校验 + mcp.delete/skill.delete 引用 guard + effective catalog 治理。 - `src/tools.ts` + `src/index.ts` + `presets/pentester/run-state.mjs`: sharedCapabilitiesForStage + Root 状态摘要节。 - `src/ui/client/agent-library-section.tsx`:PTES Assignment Tab + Shared picker modals;`src/ui/client/pentester-overview.tsx` + `src/overview/`: 轻量 counts。 - `test/ptes-capabilities.test.ts`(新增)+ delegations/agent-library-client/ overview/run-state/agent-topology/settings 测试更新。 # Wave 14:Runtime 架构(Docker + SSH 统一执行面) > 本 wave supersede 前面所有把「Docker = 唯一执行环境」当作既定事实的描述: > Worker 的执行环境从此是**可配置的 Runtime Profile**(Docker 或 SSH),在 > PentestRun 创建时冻结,Run 内所有 Delegation 继承同一个 Runtime。 ## 0. 决策 - **Runtime = Worker 实际运行 `pentester_exec` 的执行环境**。用户可以长期配置 多个 Runtime Profile(`Local Docker` / `Remote Docker` / `Lab Kali` …),每个 Run 在 intake 时选一个并**冻结**(`PentestRun.runtime: FrozenRuntime`)。 Worker 没有 runtime 参数,Delegation / Stage 都没有 override —— 无法中途切换。 - **统一执行面 `pentester_exec`**:`pentester_container_exec` 不再注册 (breaking change)。历史 session / ToolRow / event 回放只读兼容旧名字。 输入是 argv 语义(绝不拼 shell 字符串)+ **逻辑** `cwd`(相对当前 Delegation `work/`,默认 `.`),解析 + 路径穿越防护在 Host 侧。 - **统一抽象 `ExecutionRuntime`**(prepare / healthCheck / exec / pushWorkspace / pullWorkspace / dispose)。Docker 与 SSH 是它的两个实现;上层 (Root 工具 / ExecutionService / RPC / Overview)只认识这个接口,不认识 dockerode / ssh2 / container / SFTP。 - **Runtime 冻结**:`RuntimeManager.freeze(profileId)` 在**创建 Run 之前**执行, 且必须先通过 healthCheck(Ready)——失败则不创建 Run,并返回稳定错误码 (runtime_unreachable / runtime_auth_failed / runtime_misconfigured / runtime_workspace_unwritable / runtime_image_missing / runtime_disabled / runtime_required / runtime_unavailable)。 - **secret 只以 `credentialRef` 形式出现在 profile / run.json**:private key、 password、TLS 材料存进 `/credentials.json`(0600),绝不进 日志 / RPC 普通返回 / UI snapshot。 - **动态 Intake(8 fields)**:runtime / target / scope / exclusions / rules_of_engagement / language / stage_advance_policy / final_confirmation。 首条用户 prompt 是信息源:Root 对已明确字段直接采用(带**用户原话 evidence**), 只对缺失/模糊字段调用 ask_user_question(一次一个批次,canonical 顺序)。 Host 验证「结果」:每个字段要么有确实出现在用户消息里的 evidence,要么有 已确认的回答;**不再**验证问了几个问题 / 是否一次问满七问。 - **Settings 的 Docker Engine 页升级为 Runtimes**:多 profile 列表 + Add Runtime(Docker / SSH 两种类型)+ Edit / Test connection / Enable-Disable / Delete。RPC 命名空间 `pentesterRuntime`;旧 `pentesterDocker` 只作为迁移期 兼容面(明确 deprecated,写路径同时落到默认 Docker profile)。 - **Overview 不增加 Runtime Tab**:Current Run 的 Runtime 信息(Runtime card) 与 Execution 指标 / Current Work execution / Recent Executions(最多 3 条) 展示在 Overview;完整执行历史在 Trace。 ## 1. 落点(文件) - SSH Runtime **不做 Toolbox / 镜像语义**:远端主机自己就是工具箱;远端布局 `//workspace`(默认 `workspaceRoot = /tmp/dsh-pentester`)。 - `src/runtime/`(新增):`types.ts`(ExecutionRuntime / RuntimeProfile / FrozenRuntime / RuntimeView)、`errors.ts`(稳定错误码)、`profile.ts` (校验 + revision + freeze + 迁移)、`credentials.ts`(CredentialStore)、 `manager.ts`(RuntimeManager)、`exec-guard.ts`(固定监督 wrapper)、 `exec-result.ts`(统一结果投影 + safeEnv)、`output.ts`(有界输出)、 `logical-path.ts`(逻辑 cwd + 控制态边界)、`workspace-mirror.ts`、 `workspace-limits.ts`、`rpc.ts` + `invocations.ts`(pentesterRuntime)、 `docker/`(原 `src/docker/` 迁入,实现 ExecutionRuntime)、`ssh/` (connection seam + SSHRuntime:SFTP workspace 同步、进程组终止、health)。 - `src/execution/`(新增):`service.ts`(ExecutionService:准入 / resolve / exec 跟踪 / 审计 / 结果投影)、`schema.ts`(pentester_exec schema + 结果)、 `audit.ts`(内存 ring + durable `executions.jsonl` + ExecutionView)。 - `src/tools.ts`:`pentester_run` 增加 `runtime` + `intake`;start/restart 在 创建 Run 前 healthCheck + freeze;`run.runtime` 持久化;rootState 增加 runtime 摘要。 - `src/pentest/intake.ts`(新增):8 fields 解析 + evidence 核对 + 顺序校验; `pre-engagement.ts` 收敛为 question catalog + session 读取原语。 - `src/ui-view/`:snapshot 增加 `runtime` / `stats.executions` / `recentExecutions` / `DelegationView.currentExecution`。 - `src/ui/client/`:`runtimes-section.tsx`(Settings → Runtimes)+ Overview Runtime card / Executions / Recent Executions; `docker-engine-section.tsx` + `docker-host-card.tsx` 删除(能力迁入)。 - `presets/pentester/agent.cordis.yml` + `run-state.mjs`:动态 Intake 契约、 `pentester_exec`、Available Runtimes / frozen Runtime 注入。 - 迁移:首次启动由 legacy `settings.dockerHost` + `toolboxImages.kali` 生成 `Local Docker` / `Configured Docker` profile(幂等;文件存在即不覆盖)。 ## 2. 测试(新增) `test/runtime-manager.test.ts`、`test/ssh-runtime.test.ts`(fake ssh2 seam)、 `test/execution-service.test.ts`、`test/intake.test.ts`、 `test/runtime-freeze.test.ts`、`test/runtimes-ui.test.ts`、`test/ssh-testkit.ts`、 `test/runtime-testkit.ts`;Docker 既有套件与新 `pentester_exec` 路由共存。 ## 3. 本 wave 明确不做 程序化 approval 流、scope 参数级强制、多 Runtime 并行(一个 Run 一个 Runtime)、 SSH 上的 toolbox 镜像语义(远端主机自带工具,不实现)、端口转发 / 跳板机(除现有 Docker over SSH 连接能力)。