Repository navigation
0.4.0 — approval mode
Approval mode. A capability gap ("I can't do this": 2FA, a captcha) and an authority boundary ("I may not do this": submit the payment) were the same call with a different reason. They are now two modes of raiseHand, and an approval needs no takeover: one screenshot, the action in words, yes or no.
const result = await raiseHand(page, {
mode: "approval",
reason: "I may not move money without a human",
action: "Submit EUR 12,430 vendor payment to Acme GmbH",
})
// result.outcome: "approved" | "denied" | "timeout" | "disconnected"Deny is one tap on the phone; approve takes the 700 ms hold, because in this mode the answer that cannot be undone is yes. The relay enforces the mode: an approval relay routes only approve and deny, opens no CDP session, injects nothing. The webhook body now carries mode and action.
Fixed, all pre-existing in takeover mode too: the phone's Clear key did nothing; a second holder of the link could overturn a queued answer during an agent reconnect; a lost ended could leave the last frame replayable; an answer given while the phone socket was closing was dropped; a finished tab retried a dead relay forever.
Breaking for TypeScript consumers (compile-time only): HandoffOutcome has two new members, HandoffEvent.mode is new and required, RaiseHandOptions is now a union. Details and migration in the CHANGELOG.
Verified against the real Solari API: takeover, approve and deny e2e, 185 tests, two independent reviews. ADR: docs/adr/0006-approval-mode.md.