fix(agent): solve_challenge authorization covers its child's coordinate steps

With only a browser.solve_challenge grant, the vision child run was
denied browser.click_at, so the loop stopped after the first screenshot
(REAL-2 task 4). Steps inside a run spawned by solve_challenge are now
allowed; ancestors' allowlist and step budget still apply. Needs image
rebuild.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
rootandClaude Opus 5.5 committed 2026-09-29 03:06:05 +08:00
1 parent bd0e011748
commit 7dc7b1c5fa
3 files changed
+38 -1

No files matched your search

+27
View File
@@ -226,4 +226,31 @@ describe.skipIf(!hasPg)("单次审批(真实 PG)", () => {
expect(denied.rows[0]!.n).toBeGreaterThanOrEqual(2);
});
});
it("视觉子流程里的坐标点击随 solve_challenge 放行;普通会话仍要授权", async () => {
const spec = { ...ORGANIZE_SPEC, id: "browser.click_at", scope: "admin" } as CapabilitySpec;
const run = async (spawn_reason: string | null) =>
(
await h.db
.insertInto("agent_run")
.values({ trigger: "user", goal: "g", status: "running", mode: "execute", spawn_reason } as never)
.returning("id")
.executeTakeFirstOrThrow()
).id;
const check = async (agentRunId: string) =>
await createAuthorizer({ db: h.db, settings: h.ctx.settings })({
descriptor: spec,
input: { sessionId: "s", x: 1, y: 1 },
ctx: {
actorId: "user:admin",
taskId: "t",
scopeIds: [],
signal: AbortSignal.timeout(10_000),
source: "agent",
agentRunId,
},
});
expect(await check(await run("browser.solve_challenge"))).toEqual({ allowed: true });
expect(await check(await run(null))).toMatchObject({ allowed: false, reason: /browser.click_at/ });
});
});