feat(contracts,db): agent.plan / agent.propose_options, interaction events, proposeFirst replaces run mode

- agent_run_event gains plan_updated, options_proposed/resolved, approval_requested/resolved (migration 0040)
- AgentRunStartInput.proposeFirst; mode deprecated (plan_only maps to proposeFirst); mode dropped from AgentRun API
- POST /agent/runs/{id}/choice; agent.optionsTimeoutSec/optionsTimeoutAction/approvalTimeoutSec settings

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
rootandClaude Opus 5.5 committed 2026-09-28 21:07:01 +08:00
1 parent d931e75310
commit 74c2ed45aa
12 files changed
+379 -12

No files matched your search

+53 -1
View File
@@ -40,10 +40,57 @@ const samples: C.AgentRunEventBody[] = [
summary: "{}",
},
{ type: "subagent_completed", childRunId: RUN, status: "succeeded", summary: "ok", usage, toolCalls: 1 },
{
type: "plan_updated",
steps: [
{ id: "s1", title: "查库存", status: "done", note: null },
{ id: "s2", title: "搜资源", status: "running", note: "只查已开启站点" },
],
revision: 2,
},
{
type: "options_proposed",
invocationId: "inv-1",
decisionId: RUN,
question: "收哪一个版本?",
options: [
{ id: "a", title: "REMUX", summary: "画质最好", risks: ["体积大"], estimatedCost: "78 GB" },
{ id: "b", title: "WEB-DL", summary: "体积小", risks: [], estimatedCost: null },
],
recommendedId: "a",
stepId: "s2",
expiresAt: AT,
timeoutAction: "recommended",
},
{
type: "options_resolved",
invocationId: "inv-1",
decisionId: RUN,
outcome: "chosen",
choice: "a",
reason: null,
by: "user",
},
{
type: "approval_requested",
approvalId: RUN,
decisionId: null,
invocationId: "inv-2",
capabilityId: "download.submit",
planType: "acquisition_plan",
planId: RUN,
payloadHash: "h",
expectedRevision: 1,
riskLevel: "high",
summary: "下载《沙丘2》",
stepId: "s3",
expiresAt: AT,
},
{ type: "approval_resolved", approvalId: RUN, status: "rejected", reason: "太大", decidedBy: "user:1" },
];
describe("agent 运行事件", () => {
it("样例覆盖全部 12 种事件类型", () => {
it("样例覆盖全部 17 种事件类型", () => {
expect(samples.map((s) => s.type).sort()).toEqual([...C.AgentRunEventType.options].sort());
});
@@ -81,6 +128,11 @@ describe("agent 运行事件", () => {
"agent.tool_call:started",
"agent.tool_call:started",
"agent.tool_call:succeeded",
"agent.tool_call:succeeded",
"agent.tool_call:started",
"agent.tool_call:succeeded",
"agent.tool_call:started",
"agent.tool_call:denied",
]);
for (const b of bus) if (b) expect(C.parseEventPayload(b.type, b.payload), b.type).not.toBeNull();
});
+3 -2
View File
@@ -39,8 +39,9 @@ type Obj = z.ZodObject;
const ROWS: Record<string, { row: Obj; dbOnly?: string[]; apiOnly?: string[] }> = {
AcquisitionPlan: { row: C.AcquisitionPlan },
AcquisitionSlot: { row: C.AcquisitionSlot, apiOnly: ["currentAsset"] },
// upstream_id 只在服务端使用;await_wake 是挂起等待子会话时的唤醒信号(C1-B),都不进 AgentRun 视图
AgentRun: { row: C.AgentRun, dbOnly: ["upstreamId", "awaitWake"] },
// upstream_id 只在服务端使用;await_wake 是挂起等待子会话 / 用户选择 / 审批时的唤醒信号(C1-B);
// mode 是内部能力上限(AG-2 取消运行模式后不再出 API):都不进 AgentRun 视图
AgentRun: { row: C.AgentRun, dbOnly: ["upstreamId", "awaitWake", "mode"] },
AgentRunEvent: { row: C.AgentRunEventRow },
AgentRunMessage: { row: C.AgentRunMessage },
CapabilityInvocation: { row: C.CapabilityInvocation },
+5
View File
@@ -103,6 +103,11 @@ const OVERRIDES: Record<string, (v: Record<string, unknown>) => Record<string, u
ReleaseIdentityAssign: (v) => ({ ...v, external: undefined, candidateIndex: undefined }),
CacheSettings: (v) => ({ ...v, hardThresholdPercent: 90, resumePercent: 85, cleanupTargetPercent: 80 }),
GlobalSettings: () => C.DEFAULT_SETTINGS,
// 方案至少 2 个(id 不同)
AgentProposeOptionsInput: (v) => {
const o = (v.options as Record<string, unknown>[])[0]!;
return { ...v, options: [o, { ...o, id: "b" }] };
},
EngineStats: (v) => ({ ...v, byStatus: { pending: 1 } }),
TypedEventEnvelope: () => ({
seq: 1,