Skip to main content
Both SDKs are MIT, deliberately thin, and dependency-free. Both assert byte-identical serialization against every fixture in spec/fixtures/ and reproduce the signature vectors in spec/signing.md, including the negative cases. Both prove, by killing a real subprocess mid-poll, that a client can die at any point without losing an answer.
Installation state, checked while writing this page — neither SDK is on a public registry yet. pip install handoff-human and npm install @handoffproto/sdk do not resolve today; both installs below work from a checkout of github.com/OmegaAgent/handoff. When the registry releases land, the package names will be the ones shown here.

Python: handoff-human

Standard library only. Version 0.2.0 in the repository.
The durable-wait pattern (raise_request, resume, receive) is covered in the quickstart. Two details that matter in production:
  • receive() acks when its block completes. If the block raises, nothing is acked and the signal stays queued. Acking first and applying second would turn at-least-once delivery into at-most-once application, which is the exact bug the protocol exists to make impossible.
  • To record that a decision arrived and could not be acted on, call received.unable("the refund API was down") inside the block. That is not an error; it is a fact worth keeping.
Callback verification is standard library too:
Pass the raw bytes as received; passing a str is refused rather than silently encoded. Receipt-chain verification (verify_receipt_chain, verify_chain) needs nothing outside the standard library; only the optional detached Ed25519 layer needs the cryptography package. If you are coming from the 0.1.x hackathon package: the module is now handoff, and import human still works in 0.2.x with a deprecation warning.

TypeScript: @handoffproto/sdk

Zero runtime dependencies, and no Node built-ins: hashing, HMAC and randomness go through WebCrypto, so the same source runs on Node, Deno, Bun, and Workers. The package ships TypeScript source, consumed directly by any runtime that strips types (Node 22.18 or newer, Deno, Bun) or by any bundler.
The durable wait mirrors Python:
Because hashing rides WebCrypto, verifyCallback, verifyChain, and digest all return promises.

Shared error model

Every error carries a stable code and raises or throws a class that mirrors it: AlreadyAnswered (carrying the receipt id), RequesterMayNotAnswer, InsufficientAuthority, AuthorizationSpent, AnswerValidationFailed (with per-field detail), and the rest of the spec’s §13. A code the SDK version does not recognize surfaces as HandoffProtocolError with the code intact. Never branch on .message; it is written for people and may change at any time.