feat(agent): W4-E2 in-grant auto recovery for decisions with verified outcome

- settings.agent.autoInvestigateMode investigate|recover, investigateMaxAttempts, recoverVerifySec
- new capability download.recheck (execute, replay never): qB force recheck / resume, no source swap/delete
- autoInvestigate moves to agent/investigate.ts: recover mode runs execute with a recovery allowlist
  only when an active grant (published policy if bound) covers the recovery actions; otherwise degrades
  to read-only investigation and leaves suggestion + recheck option on the decision
- runAgent verifies recovery against business facts (download_task/task/source_release) before finalize;
  verified -> decision resolved by system:auto-recover, run summary '已恢复(系统核实)'; otherwise stays
  awaiting_user; per-root-cause attempt cap
- e2e E6: missingFiles -> download_failed -> recheck -> verified; no grant degrades; swap rejected;
  dedupe; attempts exhausted; budget exceeded keeps attention

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
rootandClaude Opus 5.5 committed 2026-09-27 22:35:03 +08:00
1 parent 23309042dc
commit 86af723b2a
13 files changed
+1072 -15

No files matched your search

@@ -7,7 +7,7 @@ import {
type IdempotencyKeyGenerator,
stableStringify,
} from "@umt/agent";
import type { ActorType, CapabilityKind, CapabilitySpec } from "@umt/contracts";
import { type ActorType, CAPABILITY_IDS, type CapabilityKind, type CapabilitySpec } from "@umt/contracts";
import { type DB, type Kysely, sql } from "@umt/db";
import type { Logger } from "pino";
import type { z } from "zod";
@@ -20,7 +20,7 @@ import { createAuthorizer } from "./authorize.ts";
const SIDE_EFFECT: readonly CapabilityKind[] = ["propose", "approve", "execute"];
/** 首批能力总数:启动时断言注册数与此一致(漏挂处理器即启动失败) */
export const EXPECTED_CAPABILITY_COUNT = 14;
export const EXPECTED_CAPABILITY_COUNT = CAPABILITY_IDS.length;
/**
* 幂等键:能力 ID + 版本 + 所属运行(agent run / 任务)+ 规范化输入的 sha256。
@@ -187,7 +187,7 @@ export function createCapabilityRegistry(deps: CapabilityRegistryDeps): Capabili
});
}
/** 启动断言:首批 14 个能力全部挂上处理器 */
/** 启动断言:首批能力全部挂上处理器 */
export function assertCapabilityCount(registry: CapabilityRegistry): void {
const n = registry.list().length;
if (n !== EXPECTED_CAPABILITY_COUNT) {