feat(agent): automation-first sessions with step plan, option choice and inline approval

- agent.plan emits plan_updated; system prompt asks for a plan first and updates while executing
- agent.propose_options registers a decision and suspends the run (waitForFact, no worker slot);
  the answer or timeout (recommended/stop) resumes it as the tool result
- console-session runs get inline approvals for plan-bound capabilities: approve replays the call
  with the approval, reject fails it with the reason and records it on the decision
- proposeFirst blocks execute capabilities until an option is chosen; E12 covers all paths

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 74c2ed45aa
commit cf2c4ff0e6
14 files changed
+1360 -26

No files matched your search

@@ -36,14 +36,17 @@ describe.skipIf(!hasPg)("E2 Agent 委托闭环(fake 模型 + 真实能力/授权
}, 60_000);
const createRun = async () => {
const r = await e.api("POST", "/agent/runs", { goal: GOAL, mode: "execute" });
const r = await e.api("POST", "/agent/runs", { goal: GOAL });
expect(r.status).toBe(201);
expect(r.body.data).toMatchObject({ status: "queued", trigger: "user", mode: "execute" });
expect(r.body.data).toMatchObject({ status: "queued", trigger: "user" });
// 运行模式已取消(AG-2):API 不再返回 mode;API Key / 开发密钥发起的会话不出会话内审批卡
expect(r.body.data.mode).toBeUndefined();
expect(r.body.data.scope).not.toHaveProperty("interactive");
return r.body.data.id as string;
};
it("启动断言:25 个能力全部注册,并在能力目录里可见", async () => {
expect(e.booted.ctx.capabilities.list()).toHaveLength(25);
it("启动断言:27 个能力全部注册,并在能力目录里可见", async () => {
expect(e.booted.ctx.capabilities.list()).toHaveLength(27);
const r = await e.api("GET", "/capabilities");
expect(r.body.data.items.map((c: { id: string }) => c.id).sort()).toEqual(
e.booted.ctx.capabilities