diff --git a/skills/app-install-manager/SKILL.md b/skills/app-install-manager/SKILL.md index f63bced..63036e4 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.0" +version: "1.5.1" requiredClientVersion: "10.0.170" type: procedural risk_level: high @@ -12,7 +12,7 @@ provides: tags: [installation, registry, app-management] metadata: author: desirecore - updated_at: "2026-09-18" + updated_at: "2026-09-19" --- # 应用安装管理 @@ -34,6 +34,6 @@ Agent 理解环境并执行;已有工具提供能力;Agent Service 只可靠 3. 复用 Read、HttpRequest、Bash/PowerShell 等当前已授权工具。`provides.tools` 仅提示按需披露,不授予权限;没有可靠授权通道时停止,不能读取管理 token 或改用本机文件绕过。 4. 可以依据环境适配命令;版本、来源、设备和影响范围变化需要重新确认。固定制品先校验摘要;凭据不进入聊天、指南、日志或核验记录。 5. 超时、断线和结果不明时先观察,不重放有副作用的命令;同一安装同时存在其他修改任务时不并发执行。核验只说明实际检查过的能力。 -6. 结果明确后提交一次带时间的观察,不预写 installing/reinstalling/uninstalling,不拼装 pending 状态或完整收据,不直接编辑 installed-entries.json。版本必须与本次固定资料一致;发现不同版本应先确认正确资料,不能伪报核验成功。 +6. 结果明确后提交一次带时间的观察,material 必须原样复用成功 resolve 的 data.material(包括 action 与 snapshot);首装结束也不能改成 manage。不预写 installing/reinstalling/uninstalling,不拼装 pending 状态或完整收据,不直接编辑 installed-entries.json。版本必须与本次固定资料一致;发现不同版本应先确认正确资料,不能伪报核验成功。 7. 记账失败只重读并处理记录,不重复安装/卸载。记录修订冲突要重新观察,不能只换 expectedRevision。异常时按需读取 [恢复原则](references/recovery.md)。 8. 最后说明软件的实际结果与记录是否提交成功;二者可能不同。进度留在当前会话/既有任务;不得承诺无人执行的后续修复或“所有失败都已回滚”。 diff --git a/skills/app-install-manager/references/recording-api.md b/skills/app-install-manager/references/recording-api.md index 485c03e..2e08315 100644 --- a/skills/app-install-manager/references/recording-api.md +++ b/skills/app-install-manager/references/recording-api.md @@ -6,21 +6,18 @@ API base 使用当前会话已确认的活动 Agent Service;不要猜固定端口。`deviceId=local` 指该 Agent Service 主机,不一定是界面所在电脑。远程设备使用已授权的现有设备执行工具;接口接受设备定位不代表它验证了远程证据。身份/位置不清楚就停止。 -通过已有 HttpRequest 调用 `POST /api/registry/acquisitions/resolve`,body 为 `ApplicationManagement` 原对象: +通过已有 HttpRequest 调用 `POST /api/registry/acquisitions/resolve`,body 为当前请求的 `ApplicationManagement` 原对象。不要用通用示例覆盖本次 action。 -```json -{ - "protocol": "application-observation-v1", - "sourceId": "registry:official", - "entryId": "desirecore-control", - "deviceId": "local", - "action": "manage" -} -``` +| 用户操作 | 读取资料的 action | snapshot 来源 | +| --- | --- | --- | +| 首装或升级到指定版本 | install / update | 该次市场请求的完整固定 snapshot | +| 管理或卸载已登记应用 | manage / uninstall | 不传,使用历史安装记录 | + +首次安装尚未登记,不能调用 manage 来代替 install。 `action=install|update` 必须加上由精确目录条目取得的 `snapshot`:schemaVersion、catalogSourceId、完整 catalogCommit、catalogPath、releaseVersion,及存在时的 contentRef/contentSha256。只允许明确固定版本,不填写 latest,不自行拼下载地址。`action=uninstall|manage` 不传 snapshot,服务端读取原安装资料。 -成功 `data` 包含 `material`、`expectedRevision`、`manifest`、`installGuide` 和 `record`。读取不写账本、不执行软件、不代替审批。复用返回的 material,不复制整份指南进提交请求。服务端内部复核不需要模型重复读全部目录。 +成功 `data` 包含 `material`、`expectedRevision`、`manifest`、`installGuide` 和 `record`。保留成功响应供最终提交使用,不在安装后重新构造 material。读取不写账本、不执行软件、不代替审批。复用返回的 material,不复制整份指南进提交请求。服务端内部复核不需要模型重复读全部目录。 目录的无关条目更新不替换选定版本;资料必须在受信分支可达,当前目标条目仍允许获取。下架/撤回可以阻止新装,原安装的 manage/uninstall 使用历史指南,不依赖目录在线。历史安装缺乏维护资料时明确报告,不能用同名最新指南猜卸载命令。 @@ -32,25 +29,21 @@ API base 使用当前会话已确认的活动 Agent Service;不要猜固定端 ## 提交观察 -`PATCH /api/installed-entries//`,路径段应正常 URL 编码,body: +`PATCH /api/installed-entries//`,路径段应正常 URL 编码。以下为请求对象的构造关系,不是要求运行一份新脚本;将实际值交给 HttpRequest 的 body: -```json -{ - "material": { "protocol": "application-observation-v1", "sourceId": "registry:official", "entryId": "desirecore-control", "deviceId": "local", "action": "manage" }, - "expectedRevision": "使用此次读取返回的修订值", - "requestId": "为本次提交生成一个 UUID,重试保持不变", - "deviceName": "本机", - "observation": { - "result": "present", - "observedAt": "实际核验时取得的 ISO 时间", - "location": "实际核验的安装目录或部署位置", - "evidenceRefs": ["当前工具执行结果或已有会话日志的可追溯引用"], - "notes": "可选的简短维护信息,不含秘密或自动执行指令" - } +```javascript +const body = { + material: resolution.data.material, + expectedRevision: resolution.data.expectedRevision, + requestId, // 本次提交 UUID;响应未知时保留原 ID 和完整正文。 + deviceName, + observation: { result, observedAt, location, evidenceRefs, notes } } ``` -示例中的占位文字不能直接提交。首次安装前没有精确记录时,expectedRevision 为服务端返回的 `none`。observedAt 由实际观察决定;recordedAt 由服务端记录,不要把任务开始时间当核验时间。版本来自固定资料,只有实际匹配时才提交。 +`resolution` 必须是这次成功的资料读取响应。**material 整体原样复用,包括 action 与完整 snapshot;首装完成后仍是 install,不能改为 manage 或删掉 snapshot。** action 表示资料来源,不是把“安装阶段”改成“管理阶段”的状态字段。 + +其余变量使用实际值:result 仅 present/absent,observedAt 为核验时取得的 ISO 时间,location 为实际安装/部署位置,evidenceRefs 为已有工具或会话证据引用数组,notes 是可选简短维护说明。不含秘密,不复制安装指南。首次安装前没有精确记录时,expectedRevision 为服务端返回的 `none`。observedAt 由实际观察决定;recordedAt 由服务端记录,不要把任务开始时间当核验时间。版本来自固定资料,只有实际匹配时才提交。 限制:location 最多 2048 字符、不能含换行/NUL;evidenceRefs 1–8 个,每项最多 512 字符;notes 最多 2000 字符。不填 runtimeStatus、operationId、pending 字段或 catalogReceipt。证据引用由 Agent 报告,服务端校验结构和记录一致性,不声称验证引用内容或软件真实安全。 diff --git a/skills/app-install-manager/references/recovery.md b/skills/app-install-manager/references/recovery.md index a3ed97a..5259a7c 100644 --- a/skills/app-install-manager/references/recovery.md +++ b/skills/app-install-manager/references/recovery.md @@ -8,6 +8,7 @@ | 下载摘要不符 | 不安装,不换来源绕过;报告并等待明确选择 | | 应用已安装,PATCH 失败/响应丢失 | 重读精确记录,必要时原 requestId+原 body 重试记账;不重新安装 | | 卸载完成但 PATCH 失败 | 先确认已移除,再补记;不再次清理用户目录 | +| 首装误用 manage,明确返回 historical-material-unavailable | 重读确认 record=null 且 revision 未变;保留软件,改用原成功 resolve 的完整 data.material,以新提交 ID 补记。只有确认原请求被拒且未落盘才允许修正正文;响应未知不能如此处理 | | application_revision_conflict | 重新读记录和软件现状;不拿旧证据只替换 expectedRevision | | application_request_id_conflict | 同 ID 参数不一致,停止;不能自动换 ID 掩盖错误 | | application_legacy_source_unresolved | 旧记录来源不明确,不能静默认领为官方应用 | 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 f5fa954..9ecab87 100644 --- a/skills/app-install-manager/tests/registry-catalog-acquisition.test.mjs +++ b/skills/app-install-manager/tests/registry-catalog-acquisition.test.mjs @@ -654,6 +654,18 @@ test('Skill-first 正文短而完整、按需提供既有 HTTP 工具、不加 } }) +test('首装观察示例复用 resolver material,不将 install 改为 manage', async () => { + const api = await readFile(join(here, '..', 'references', 'recording-api.md'), 'utf8') + const skill = await readFile(skillPath, 'utf8') + assert.match(api, /material: resolution\.data\.material/) + assert.match(api, /expectedRevision: resolution\.data\.expectedRevision/) + assert.match(api, /首装完成后仍是 install/) + assert.doesNotMatch(api, /"action":\s*"manage"/) + assert.match(skill, /首装结束也不能改成 manage/) + const recovery = await readFile(join(here, '..', 'references', 'recovery.md'), 'utf8') + assert.match(recovery, /响应未知不能如此处理/) +}) + 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/)