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

+24 -5
View File
@@ -17,7 +17,11 @@ export const AgentRunStatus = z.enum([
"budget_exceeded",
]);
export type AgentRunStatus = z.infer<typeof AgentRunStatus>;
/** execute:授权范围内边执行边汇报;plan_only:"先给方案",只允许 read/preview/propose 类能力 */
/**
* 内部能力上限(agent_run.mode 列,不再出现在 API 与界面上):execute = 按白名单与授权执行;
* plan_only = 只允许 read/preview/propose 类能力(后台只读调查、只读子会话用)。
* 用户会话一律 execute;"先看方案再执行"是会话选项(scope.proposeFirst),见 AgentRunStartInput。
*/
export const AgentRunMode = z.enum(["execute", "plan_only"]);
export type AgentRunMode = z.infer<typeof AgentRunMode>;
@@ -65,7 +69,6 @@ export const AgentRun = EntityBase.extend({
startedAt: Timestamp.nullable(),
finishedAt: Timestamp.nullable(),
createdBy: z.string().nullable(),
mode: AgentRunMode,
/** 后台触发按根因去重(如 investigate:<decisionId>);用户委托为 null */
dedupeKey: z.string().nullable(),
/** 派生深度:用户/后台发起的会话为 0,agent.spawn 派生的子会话 = 父 + 1 */
@@ -321,8 +324,14 @@ export type AutomationGrantUpdate = z.infer<typeof AutomationGrantUpdate>;
/** POST /agent/runs:建 agent_run + 入队持久任务 runAgent(不在 HTTP 请求里跑模型) */
export const AgentRunStartInput = z.object({
goal: z.string().trim().min(1).max(8000),
mode: AgentRunMode.default("execute"),
/** 能力白名单(缺省按模式给全部可用能力);plan_only 下超出 read/preview/propose 的会被拒绝 */
/**
* 先看方案再执行:模型先调查,用 agent.propose_options 给方案,用户选定前服务端拒绝有副作用的执行类能力。
* 缺省关(自动化优先:按步骤清单直接执行,需授权的动作在步骤处审批)。
*/
proposeFirst: z.boolean().default(false),
/** @deprecated 已取消运行模式;plan_only 按 proposeFirst=true 处理,execute 忽略 */
mode: AgentRunMode.optional().describe("已弃用:plan_only 等同 proposeFirst=true"),
/** 能力白名单(缺省全部可用能力) */
capabilityAllowlist: z.array(KnownCapabilityId).min(1).max(50).optional(),
});
export type AgentRunStartInput = z.infer<typeof AgentRunStartInput>;
@@ -330,6 +339,17 @@ export type AgentRunStartInput = z.infer<typeof AgentRunStartInput>;
export const AgentRunMessageInput = z.object({ text: z.string().trim().min(1).max(8000) });
export type AgentRunMessageInput = z.infer<typeof AgentRunMessageInput>;
/**
* 回答 agent.propose_options:选一个方案(optionId),或都不要(optionId=null,reason 写原因)。
* 会话挂起在这个问题上;回答后会话从这里继续。
*/
export const AgentRunChoiceInput = z.object({
decisionId: Id,
optionId: z.string().min(1).max(40).nullable(),
reason: z.string().trim().max(1000).optional(),
});
export type AgentRunChoiceInput = z.infer<typeof AgentRunChoiceInput>;
export const AgentRunAbortInput = z.object({ reason: z.string().max(500).optional() });
export type AgentRunAbortInput = z.infer<typeof AgentRunAbortInput>;
@@ -398,7 +418,6 @@ export const AgentRunNode = z.object({
parentRunId: Id.nullable(),
goal: z.string(),
status: AgentRunStatus,
mode: AgentRunMode,
depth: NonNegInt,
spawnReason: z.string().nullable(),
toolCallCount: NonNegInt,