pliuz

Build

SDKs — Python & TypeScript

Zero-config wrappers with first-class types, client-side redaction, deterministic idempotency, and server long-poll. Same wire contract, feature parity.

Error taxonomy

Always handle the three terminal outcomes — they are distinct on purpose.

Python
from pliuz import (
    PliuzRejectedError,            # human said no (e.reason)
    PliuzApprovalExpiredError,     # server SLA hit before a decision
    PliuzApprovalTimeoutError,     # your polling gave up; may resolve later
    PliuzEditNotApplicableError,   # human edited args you can't auto-apply
)

TypeScript mirrors these as typed subclasses (PliuzRejectedError, etc.) with correct instanceof across ESM/CJS.

Idempotency — read before relying on it

The key is hashed over pre-redaction args, so two calls differing only in a redacted field get different keys. The backend dedupes within a 24h window, per agent, and rejects the same key with a different payload (409). For legitimately repeating calls, set a per-run session_id.

Python
# Repeating identical calls (crons, queues)? Scope per run so each
# run gets its own approval (the dedupe window is per (tool,args,session)).
@gated(policy="refund", session_id=run_id)
def refund(order_id: str): ...

Decision latency (long-poll)

The wait is delivered by server long-poll: each call holds the connection up to ~25s and returns the instant the decision lands. Near-zero delivery latency and ~12x fewer requests than fixed-interval polling — no configuration needed.

Human edits

If an approver chooses Edit & Approve, the SDK executes the edited args, never the original call. With a custom toolArgs mapper (TS), provide applyFinalArgs or the SDK throws rather than run unapproved args.

Framework adapters

LangChain redaction caveat
The LangChain adapter currently binds the raw tool signature, which can nest args where top-level redactpaths don't match. Until the adapter rework ships, prefer @gated directly on your functions when redaction matters.
LangChain
from langchain_core.tools import tool
from pliuz.adapters.langchain import gated_tool

@gated_tool(policy="refund", redact=["customer.ssn"])
@tool
def issue_refund(customer_id: str, amount_cents: int) -> str:
    """Issue a refund to a customer."""
    return stripe.refunds.create(...).id

TypeScript ships a Vercel AI SDK adapter (gatedTool()).

Source & reference