Activation status: Source-ready, with compatible Worker code deployed.
Required production database activation and authenticated approval canaries
remain pending. The live partner-preview baseline already supports durable
task execution, run reads, and SSE; do not depend on public approval events
until activation is confirmed.
The first public approval boundary is intentionally narrow. Praxa projects an
approval only for an immutable browse_session_act action whose exact
instruction or steps, target host, and action digest can be re-derived at every
boundary. Generic tool approvals and candidate turn approvals are not available.
mode:"turn" returns 400 invalid_request, and /v1/chat is not routed.
The golden rule
Render summary exactly as supplied. Do not truncate, paraphrase,
translate, reorder, or reconstruct it. The public read, event, and mutation
paths all enforce the same invariant: render == record == gate-match.
action_digest is the 64-character lowercase SHA-256 digest that binds the
decision to that exact stored action. Copy it from the current
pendingApproval or approval.required projection; never compute it in the
browser.
1. Listen for the durable event
Open GET /v1/runs/{run_id}/events with a key carrying runs:read. Resume a
disconnected stream with its last committed numeric SSE id in
Last-Event-ID.
The same fields can appear in GET /v1/runs/{run_id} as
pendingApproval while status is awaiting_approval. If Praxa cannot prove
the exact presentation, it emits no approval event, omits the pending
projection, and refuses a decision.
2. Render and collect a decision
Show summary verbatim in an expandable or scrollable region. Offer two
explicit controls: Approve and Deny. There is no default and no public
“approve all” path. If expiresAt is present, show it and disable stale
controls; the server remains authoritative.
3. Post the exact digest
Use a key carrying runs:write:
The public request literal is exactly "approve" or "deny". A successful
response is the updated run projection; it means the decision was recorded,
not that the action or run has finished. Continue reading events until
run.completed, run.failed, or run.cancelled.
Late, replayed, unsupported, foreign-tenant, changed-state, expired, and
digest-mismatched decisions fail closed. A stale or mismatched digest returns
409 conflict rather than authorizing whatever is currently parked.
4. Compact React example
This component expects validated durable run events from your SSE reader. It
does not place a secret API key in browser code; call onDecision through your
authenticated backend.
If an approval.required webhook wakes your backend, fetch the current run and
require a matching pendingApproval before rendering. The webhook records a
resolved decision as approved or denied; those webhook payload values are
different from the public mutation literals approve and deny.
Next steps
Last modified on August 14, 2026