From b29360ed8daa9894f54f58d3399da53ff670d60c Mon Sep 17 00:00:00 2001 From: Yige Date: Sat, 19 Sep 2026 12:59:01 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E9=99=90=E5=AE=9A=E5=8D=B8=E8=BD=BD?= =?UTF-8?q?=E8=8C=83=E5=9B=B4=E5=B9=B6=E8=A6=81=E6=B1=82=E7=9C=9F=E5=AE=9E?= =?UTF-8?q?=E6=A0=B8=E9=AA=8C=E8=AF=81=E6=8D=AE=E5=BC=95=E7=94=A8=20(#14)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- skills/app-install-manager/SKILL.md | 6 ++++-- skills/app-install-manager/references/recording-api.md | 4 +++- .../tests/registry-catalog-acquisition.test.mjs | 10 ++++++++++ 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/skills/app-install-manager/SKILL.md b/skills/app-install-manager/SKILL.md index 63036e4..68efe02 100644 --- a/skills/app-install-manager/SKILL.md +++ b/skills/app-install-manager/SKILL.md @@ -1,7 +1,7 @@ --- name: 应用安装管理 description: 安装、升级、卸载或检查应用;读取固定资料,复用执行工具核验后记录结果。处理 ApplicationManagement 请求;旧服务 RegistryCatalogAcquisition 请求按需读取兼容指南。 -version: "1.5.1" +version: "1.5.2" requiredClientVersion: "10.0.170" type: procedural risk_level: high @@ -36,4 +36,6 @@ Agent 理解环境并执行;已有工具提供能力;Agent Service 只可靠 5. 超时、断线和结果不明时先观察,不重放有副作用的命令;同一安装同时存在其他修改任务时不并发执行。核验只说明实际检查过的能力。 6. 结果明确后提交一次带时间的观察,material 必须原样复用成功 resolve 的 data.material(包括 action 与 snapshot);首装结束也不能改成 manage。不预写 installing/reinstalling/uninstalling,不拼装 pending 状态或完整收据,不直接编辑 installed-entries.json。版本必须与本次固定资料一致;发现不同版本应先确认正确资料,不能伪报核验成功。 7. 记账失败只重读并处理记录,不重复安装/卸载。记录修订冲突要重新观察,不能只换 expectedRevision。异常时按需读取 [恢复原则](references/recovery.md)。 -8. 最后说明软件的实际结果与记录是否提交成功;二者可能不同。进度留在当前会话/既有任务;不得承诺无人执行的后续修复或“所有失败都已回滚”。 +8. 卸载仅移除已核验的应用包/安装目录;不能因为位于同一前缀就递归清理父目录。未知文件、同级数据、凭据和日志默认保留;额外删除必须逐项说明并另获确认。清理空父目录只用非递归删除,非空就保留。 +9. 证据引用使用工具实际返回的调用 ID、真实会话日志位置或本次已保存的核验报告路径,不编造 session:…:step:… 标识;没有可追溯引用就先保存不含秘密的报告。 +10. 最后说明软件的实际结果与记录是否提交成功;二者可能不同。进度留在当前会话/既有任务;不得承诺无人执行的后续修复或“所有失败都已回滚”。 diff --git a/skills/app-install-manager/references/recording-api.md b/skills/app-install-manager/references/recording-api.md index 2e08315..1b68723 100644 --- a/skills/app-install-manager/references/recording-api.md +++ b/skills/app-install-manager/references/recording-api.md @@ -47,6 +47,8 @@ const body = { 限制:location 最多 2048 字符、不能含换行/NUL;evidenceRefs 1–8 个,每项最多 512 字符;notes 最多 2000 字符。不填 runtimeStatus、operationId、pending 字段或 catalogReceipt。证据引用由 Agent 报告,服务端校验结构和记录一致性,不声称验证引用内容或软件真实安全。 +evidenceRefs 不是自由命名的步骤标题。只填实际存在的工具调用 ID、会话日志位置,或已用 Write/执行工具保存的核验报告路径;不能自行编造 `session:…:step:…`。报告保存包版本、实际操作结果和时间,不含凭据;保存失败则如实报告记账前提未满足。 + 成功返回新的 revision、replayed 和当前记录摘要。相同 requestId+相同 body 的当前结果可重复读取,且不重写时间;同 ID 不同 body 拒绝。旧提交已被后续修订取代时,不换 ID/修订强行重提,也不承诺任意历史请求的全局去重。 ## 重读和失败 @@ -55,4 +57,4 @@ const body = { 提交响应丢失:先重读;当前 requestId 相同可用原正文重试。修订变化:观察实际软件再决定是否发起新的核验,禁止只替换 expectedRevision。写入失败:说明软件可能已修改但记录未完成,不重执行安装。具体恢复见 recovery.md。 -应用正常停止后无需改观察记录。卸载默认保留用户数据和凭据;删除这些材料需要独立确认。注册内部服务也属于单独任务,应用安装不派生新的内部 MCP/HTTP 服务。 +应用正常停止后无需改观察记录。卸载默认保留用户数据和凭据;删除这些材料需要独立确认。先检查实际安装位置及其父目录边界,使用包管理器卸载或只删除已确认归属的应用目录;不得把整个自定义前缀当作可递归删除对象。只检查默认用户数据路径不能证明前缀内没有数据。未知/后来加入的文件一律保留;空父目录可用非递归删除,非空就停止清理。注册内部服务也属于单独任务,应用安装不派生新的内部 MCP/HTTP 服务。 diff --git a/skills/app-install-manager/tests/registry-catalog-acquisition.test.mjs b/skills/app-install-manager/tests/registry-catalog-acquisition.test.mjs index 9ecab87..b9bed98 100644 --- a/skills/app-install-manager/tests/registry-catalog-acquisition.test.mjs +++ b/skills/app-install-manager/tests/registry-catalog-acquisition.test.mjs @@ -666,6 +666,16 @@ test('首装观察示例复用 resolver material,不将 install 改为 manage' assert.match(recovery, /响应未知不能如此处理/) }) +test('卸载不扩大到未知父目录,证据必须实际存在', async () => { + const skill = await readFile(skillPath, 'utf8') + const api = await readFile(join(here, '..', 'references', 'recording-api.md'), 'utf8') + assert.match(skill, /不能因为位于同一前缀就递归清理父目录/) + assert.match(skill, /非递归删除,非空就保留/) + assert.match(api, /只检查默认用户数据路径不能证明前缀内没有数据/) + assert.match(api, /不能自行编造/) + assert.match(api, /已用 Write\/执行工具保存的核验报告路径/) +}) + test('Skill contract has no fixed official/local Registry fallback and resolves before execution', async () => { const skill = await readFile(join(here, '..', 'references', 'legacy-services.md'), 'utf8') assert.doesNotMatch(skill, /registry\/official\/entries/)