Merge pull request 'Propose the G1 extension package: EXT-13..16 (shared retrieval, commands-as-tools, shared project list, exec backend)' (#52) from feature/g1-ext-b1-b3-proposal into main

This commit was merged in pull request #52.
This commit is contained in:
2026-10-09 14:50:07 +00:00
@@ -0,0 +1,124 @@
# EXT 申请包 · G1:回合内共享检索、命令即工具、项目列表共享与执行后端接口(B-1/B-2/B-3 + B-5②)
状态:**proposed(2026-10-09)**;owner:weichen;需求依据 = RDM 评审与实施方案 V1.1
(`Agent-System-RDM docs/proposals/2026-10-09-V22-review-and-implementation-plan.md` §3.3/§3.4/§5.3,
owner 2026-10-09 Q1–Q8 批复;Q4=方案 A,Q2=暂时禁用+隔离档)。
撰写日期:2026-10-09。配套:[扩展面兼容与演进](2026-10-01-extension-compat-policy.md)、
[EXT 申请包 C1–C7](2026-10-08-ext-c1-c7-rdm-shared-contracts.md)(EXT-6…EXT-12)。
## 0. 总则
- 本包四条申请按既有序列顺延 **EXT-13…EXT-16**;最终编号以受理评审为准。
- 分期(对齐 RDM 方案 §5.2):B-1(EXT-13)与 B-3(EXT-15)为 **G1 序 4 的硬前置**
(成员智能体看不到项目知识 = R04 路径不通);B-2(EXT-14)为 G2/V0.1 端到端的硬前置
(对话入口无法触达 `rdm.*`);B-5②(EXT-16)为 G2-8 原型的前置。
- SurfaceVersion 影响:EXT-14 触及 `pkg/extension` 导出面(命令规格新字段)→ 需升
SurfaceVersion 并同步 surface.txt;EXT-13/15/16 为 internal 行为扩展,不改导出面
(EXT-16 若只加 `Options` 字段则属加法,见 §4)。
## 1. EXT-13(←B-1)回合内共享检索:智能体的检索遵循项目共享授权
- **场景**:RDM R04/R05——成员 B 的智能体在**回合内**(ReAct 循环的 `knowledge_search`
工具与自动知识注入)必须能检索到成员 A 已发布的项目知识;现状只能看到本人 owner 的
知识。HTTP 面(`/api/…/search`)已走 `RetrieveShared`(EXT-8 余项,Dev #269/#273),
但回合内两条路径仍是 owner-only:
- `internal/runmgr/runmgr.go:726-736` `runCtx.Retrieve` → `retriever.Retrieve(ctx, c.run.Owner, q)`(owner-only);
- `internal/app/hostcaps.go:64-66` `hostcaps.search`(进程内 `Host.KnowledgeSearch` 底座)→ `pipe.Retrieve(owner)`(owner-only)。
结果:同一个成员,页面搜索能看到共享知识,**智能体对话里看不到**——「所有用户的智能体
应当能够看到项目级的共享信息」(用户补充材料 §2树1图)走不通。
- **希望公开面**:组合根把 EXT-8 适配器(`Server.ProjectAuthz` 同款判定函数)注入
runmgr 与 hostcaps:两条检索路径在本人 owner 两路(全文+向量)如常之外,追加
「经适配器放行的共享两路」——与 HTTP 面 `rag.Pipeline.RetrieveShared` 完全同一实现与
同一授权判定(复用 `sharedVisible`/`SearchKnowledgeShared`,不另造第二套语义)。
进程内 `Host.KnowledgeSearch` 同步走共享版。
- **为何组合不出**:`RetrieveShared` 已存在,但 runmgr/hostcaps 没有适配器的引用——
适配器槽在 `internal/app/hostcaps.go` 的 `projectAuthz`,由组合根在装载扩展后注入
Server;runmgr 的构造早于扩展装载,且 `Manager` 无该依赖。下游无法经任何公开面把
「按 owner 判定共享」的回调递进回合执行路径。
- **兼容方式**:适配器未注册=现状(本人两路);注册后共享命中追加、RRF 融合。
不动 `pkg/extension` 导出面,SurfaceVersion 不变。
- **验收**:同项目两人+另一项目一人(RDM 成员表桩):成员 B 发起对话,模型经
`knowledge_search` 命中 A 已发布的知识切片(共享命中出现在工具结果);未授权者与
跨项目者为零命中;`Host.KnowledgeSearch`(hostapi 与进程内两形态)行为一致;
未注册适配器时与现状逐字节一致(回归)。
## 2. EXT-14(←B-2)插件命令即工具:`exposeAsTool`(Q4 方案 A)
- **场景**:RDM V0.1 端到端(C-11)要求「从空项目创建到生成项目建议书,全对话完成」;
RDM 的全部能力(两树一网读写、上传、起草、追溯查询)都登记为 `rdm.*` 命令,而智能体
回合的工具清单里没有任何插件命令——模型既不能检索笔记树,也不能发起 `rdm.draft.compose`。
U0 §3.3/方案 §3.3 裁定走**方案 A**:命令经命令总线暴露为工具,四入口同源
(button/card/slash/chat)自然延伸出第五入口「智能体」。
- **希望公开面**:命令规格(`agentcommand.CommandSpec` 经 `Registry.AddCommandHandler`
登记的 JSON)新增可选字段 `"exposeAsTool": {"risk": "low|medium|high"}`(缺省不暴露)。
runmgr 装配工具清单时,把带该字段的命令投影为工具描述(名称=命令 id,参数=命令
paramsSchema);模型调用该工具时,**不分发为工具执行,而是构造 `cmdbus.Request` 经
命令总线分发**(Entrance="agent",Actor=会话 owner,ConversationID=当前会话)——
同一审计信封、同一 HITL 内联确认卡(`risk=high` 或命令规格要求确认时照常弹卡)、
同一危险分级。
- **为何组合不出**:`Registry` 只有 AddSkill/AddCommand/AddCommandHandler/Note/
RegisterResourceAuthorizer;工具注册表(`tools.Registry`)与命令总线之间没有任何
桥接;MCP 通道(方案 B)绕过命令总线与确认卡,与 SR-E6/HITL 门禁冲突,已被 Q4 否决。
- **兼容方式**:`pkg/extension` 命令规格加可选字段=导出面加法,SurfaceVersion 升
(建议 2.6.0-experimental)+ surface.txt 同步。缺省(不写 exposeAsTool)=现状零差异。
- **验收**:带 `exposeAsTool` 的 `/rdm.ping` 出现在模型工具清单且调用返回回执,
审计信封 Entrance="agent" 与按钮入口同源;`risk=high` 的暴露命令在对话内触发内联
确认卡;不带字段的命令不出现;RDM 侧 `rdm.trace`/`rdm.baseline` 等只读命令可在
回合内被模型调用(成员 B 场景结合 EXT-13)。
## 3. EXT-15(←B-3)项目列表与建会话的共享放行(SR-G10)
- **场景**:RDM R03——成员 B 被加入项目后「界面自动出现该项目,可直接开始工作(新建
对话)」。基座 `projects` 表按 owner 硬隔离(`store/projects.go:22-34`):
- `GET /api/projects`(`server.go:438-441` `listProjects`)只返回本人 owner 的项目——成员看不到共享项目;
- `POST /api/projects/{id}/conversations`(`store/projects.go:124-130`)校验「项目属于请求者」——成员在共享项目下建会话被 404。
EXT-8 已放行的入口(工件三入口、SSE 项目流、检索)不含这两处。
- **希望公开面**:这两处入口在 owner 自有判定未命中时咨询同一 EXT-8 适配器:
`listProjects` 追加经适配器放行的项目(去重、标注 `shared: true` 字段供前端区分
展示);`CreateConversation` 的归属校验放宽为「本人自有 **或** 适配器放行」。会话
本身仍归属创建者(owner 不变),只是创建权扩展到成员。
- **为何组合不出**:`ListProjects(owner)` 的 SQL 按 owner 过滤,无回调;建会话校验在
store 层硬编码。与 C4(EXT-8)同因:纯下游无法改变基座 owner 过滤。
- **兼容方式**:未注册适配器=现状;放行结果带审计。internal 行为扩展,SurfaceVersion
不变。
- **验收**:成员 B 的 `GET /api/projects` 含共享项目(shared 标注)且不含未授权项目;
B 在共享项目下 `POST conversations` 成功、会话 owner=B;撤销成员后两者立即拒绝;
项目拥有者视角无任何变化。
## 4. EXT-16(←B-5②)执行后端接口:隔离执行档(Q2 阶段二,G2-8 原型)
- **场景**:Q2 裁定「暂时禁用,但后续执行任务时肯定还是要代码执行能力」。v0.2.0 已
交付启动级硬关闭(`Options.DisableExecTools`,Backend #110);隔离档是恢复执行能力的
路径:执行的是**模型生成的代码**,须按不可信代码对待(方案 §3.4):只读系统根、仅挂
会话工作目录与技能目录、默认无网、cgroup 资源限额、硬超时、按项目 venv 只读挂载;
本机已实测 `bwrap --unshare-all` 可用(无特权 userns 被 AppArmor 限制,bwrap 专门
策略放行)。
- **希望公开面**:`internal/tools/exec_interpreter.go` 的进程启动点抽象为「执行后端」
接口(`ExecBackend`:`Run(spec) → result`),启动参数选择后端:
`ExecBackend = direct | sandbox`(缺省 direct=现状)。sandbox 后端以 bwrap 实现
(资源限额经 systemd-run/cgroup v2)。`extension.Options` 增加
`ExecBackend string`(加法;空=direct)。`DisableExecTools` 保留为最终兜底,优先级
高于后端选择。
- **为何组合不出**:现实现直接 `exec.Command`(`runInterpreter`),无后端抽象;沙箱
参数组装(bind 清单、网络命名空间、限额)属平台面,下游插件无法注入。
- **兼容方式**:缺省 direct=逐字节现状;`Options` 加字段=SurfaceVersion 升。RDM 在
G3-7 才切换 sandbox;G1–G2 期间维持 `DisableExecTools=true`。
- **验收**:sandbox 后端下逃逸负例全部被拦——读 `~/.secrets`、读其他会话目录、访问
外网、fork 炸弹、超时不可杀均为拒绝/杀灭;会话工作目录内 docx 生成任务成功且产物
可登记;direct 后端与现状一致(回归)。
## 5. 不在本申请包(显式排除)
- EXT-6/7/10/12 完整形态:仍在 C1–C7 包(EXT-6…12)受理轨道,节奏不变。
- 提示词随执行可用性切换(`base.deliverables` 在禁用期的降级文案):基座自有任务,
不占 EXT 编号。
- UI 仓 nightly runner 修复:与代码无关(rolldown 原生绑定缺失),另立任务。
## 6. 受理建议
| 申请 | 内容 | 优先级 | 兼容 |
|---|---|---|---|
| EXT-13 回合内共享检索 | G1 序 4 硬前置(R04/R05) | **P0 最先** | 不变(internal) |
| EXT-15 项目列表/建会话共享 | G1 序 4(R03/SR-G10) | P0 | 不变(internal) |
| EXT-14 命令即工具 | G2/V0.1 端到端硬前置(Q4 方案 A) | P1 | 升(加字段) |
| EXT-16 执行后端接口 | G2-8 原型(Q2 阶段二) | P1 | 升(加字段) |