feat(agent): 选定方案即批准,服务端绑定计划(AGUI-3)
- agent.propose_options 的方案可带 action(capabilityId + planId)。提出方案时服务端按计划当前的 payload hash + revision 绑定,并用计划本身生成动作要点(对象、大小 / 文件数)发给界面,不用模型的说法。 - 控制台管理员会话选定绑定了计划的方案时,建一份 scope.via=option 的已批准单次审批(审计 approval.approved); 执行时授权器 optionApproval 核对能力、计划类型 + ID、payload hash、revision、有效期后放行,用后 consumed, 不再出第二张审批卡。API Key 作答、超时自动选、计划已变化都不算批准;移动 / 替换文件的整理与发布策略 (删除、覆盖类)选定后仍单独确认。会话停止时撤销没用掉的这类审批。 - contracts:CAPABILITY_ACTIVITY 给每个能力加 verb / noun(会话进度行的人话计数);agent.plan / agent.propose_options 标为 hidden。 - e12 补测试:管理员选定 → 无 approval_requested、审批绑定同一计划并 consumed、换 hash 不放行; API Key 选定 → 仍出审批卡。 Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
c9c6505d71
commit
4e0e6712da
8 files changed
+507
-33
No files matched your search
@@ -220,15 +220,25 @@ export function registerAgentRoutes(
|
||||
runToApi(await appendMessage(ctx, params.id, body.text, actorId).catch(mapErr)),
|
||||
);
|
||||
|
||||
route(app, Api.agentRuns.choose, async ({ params, body, actorId }) => {
|
||||
route(app, Api.agentRuns.choose, async ({ params, body, actorId, c }) => {
|
||||
const run = await db().selectFrom("agent_run").selectAll().where("id", "=", params.id).executeTakeFirst();
|
||||
if (!run) throw new ApiException("not_found", "智能体运行不存在");
|
||||
if (body.optionId === null && !body.reason?.trim())
|
||||
throw new ApiException("validation_failed", "都不要时写一句原因,智能体按它调整方案");
|
||||
const r = await answerOptions(ctx, run.id, body, actorId);
|
||||
if (r === "not_found") throw new ApiException("not_found", "这个会话没有这项方案选择");
|
||||
if (r === "bad_option") throw new ApiException("validation_failed", "没有这个方案");
|
||||
if (r === "closed") throw new ApiException("conflict", "这项方案选择已处理");
|
||||
// 选定即批准只认控制台管理员会话(与 PATCH /approvals 同一口径);API Key 作答只选方案
|
||||
const r = await answerOptions(ctx, run.id, body, actorId, {
|
||||
approver: c.get("principal")?.kind === "admin_session",
|
||||
});
|
||||
if (r.status === "not_found") throw new ApiException("not_found", "这个会话没有这项方案选择");
|
||||
if (r.status === "bad_option") throw new ApiException("validation_failed", "没有这个方案");
|
||||
if (r.status === "closed") throw new ApiException("conflict", "这项方案选择已处理");
|
||||
if (r.status === "ok" && r.approvalId)
|
||||
await audit(ctx, actorId, "approval.approved", r.approvalId, {
|
||||
via: "option",
|
||||
runId: run.id,
|
||||
decisionId: body.decisionId,
|
||||
optionId: body.optionId,
|
||||
});
|
||||
const fresh = await db().selectFrom("agent_run").selectAll().where("id", "=", run.id).executeTakeFirstOrThrow();
|
||||
return runToApi(fresh);
|
||||
});
|
||||
|
||||
Reference in new issue
Block a user