Skip to content

0.4.0 — approval mode

Choose a tag to compare

@Sy-D Sy-D released this 02 Sep 13:30
· 16 commits to main since this release

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.