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:
1 parent
23309042dc
commit
86af723b2a
13 files changed
+1072
-15
No files matched your search
@@ -1,4 +1,5 @@
|
||||
import { join, resolve } from "node:path";
|
||||
import type { DownloaderClient } from "@umt/adapters";
|
||||
import {
|
||||
type AgentRunHandle,
|
||||
type AgentSettings,
|
||||
@@ -21,7 +22,7 @@ import {
|
||||
type GlobalSettings,
|
||||
KnownCapabilityId,
|
||||
} from "@umt/contracts";
|
||||
import { type AgentRunRow, sql } from "@umt/db";
|
||||
import { type AgentRunRow, type DownloaderInstanceRow, sql } from "@umt/db";
|
||||
import type { ServerContext } from "../types.ts";
|
||||
import { DEMO_SCRIPTS, parseFakeScripts } from "./fake-scripts.ts";
|
||||
import { buildGoalReport } from "./report.ts";
|
||||
@@ -36,6 +37,8 @@ export interface AgentModuleOptions {
|
||||
fakeScripts?: FakeScript[];
|
||||
/** 覆盖 settings.agent.goalPollSec(测试用小值) */
|
||||
goalPollSec?: number;
|
||||
/** download.recheck 用的下载器客户端;缺省按实例配置 + secret 现场创建 qB 客户端 */
|
||||
downloader?: (inst: DownloaderInstanceRow) => Promise<DownloaderClient>;
|
||||
}
|
||||
|
||||
export interface AgentEnv {
|
||||
@@ -54,6 +57,14 @@ export const INVESTIGATE_CAPABILITIES = [
|
||||
"task.inspect",
|
||||
"identity.resolve",
|
||||
] as const;
|
||||
/**
|
||||
* 授权内恢复(settings.agent.autoInvestigateMode = recover)的能力白名单:只读调查 + 恢复类
|
||||
* (重试失败任务、强制重新校验 / 恢复暂停)。换源(download.plan / download.submit)、删除 / 清理、
|
||||
* 取消任务、整理执行、策略发布一律不在白名单内。实际能否执行仍由 authorize 按持续授权判定。
|
||||
*/
|
||||
export const RECOVER_CAPABILITIES = [...INVESTIGATE_CAPABILITIES, "task.retry", "download.recheck"] as const;
|
||||
/** 被视为"恢复动作"的能力(结果需系统核实) */
|
||||
export const RECOVERY_ACTIONS = ["task.retry", "download.recheck", "identity.resolve"] as const;
|
||||
|
||||
export interface RunScope {
|
||||
capabilities: string[];
|
||||
@@ -243,7 +254,12 @@ function systemPromptFor(run: AgentRunRow, scope: RunScope): string {
|
||||
];
|
||||
if (run.mode === "plan_only")
|
||||
constraints.push("本次为“先给方案”:只调查与规划,不提交下载、不做文件变更、不发布策略。");
|
||||
if (run.trigger === "event")
|
||||
if (run.trigger === "event" && run.mode === "execute")
|
||||
constraints.push(
|
||||
"本任务由后台事件触发,是有界的授权内恢复:先查证据,再只在授权范围内执行恢复(task.retry / download.recheck / identity.resolve)," +
|
||||
"每种恢复动作最多一次;不换源、不删除、不清理;恢复是否成功由系统按业务状态核实,不要自行宣称已恢复。",
|
||||
);
|
||||
else if (run.trigger === "event")
|
||||
constraints.push("本任务由后台事件触发,是有界的只读调查:收集证据、给出结论与建议,不要反复重试同一操作。");
|
||||
return buildSystemPrompt({
|
||||
role: run.trigger === "event" ? "媒体库异常调查员" : "媒体库自动化助手",
|
||||
|
||||
Reference in new issue
Block a user