SDK helpers
The repository provides small helpers for the signed agent boundary, in TypeScript and Python. They are deliberately not a complete HTTP API client: your service remains responsible for sending most requests, receiving raw bodies, and storage.
Delivery arrives
raw body
Verify
verify()
Build the reply
text, card...
Sign
sign()
Send
ReplyClient
Platform accepts
202
Everything before "Send" is covered in both languages. Sending is covered only in TypeScript, by ReplyClient; a Python agent signs and posts its own request. Neither helper opens a webhook server, reads /v1/*, or stores anything on your behalf.
What is included
| Package | Implemented surface |
|---|---|
| TypeScript | HMAC sign, verify, and authorization; typed reply-block constructors; ReplyClient for posting a reply (one-shot, streaming partials, or pass); schema-derived wire types |
| Python | HMAC sign, verify, and authorization; reply-block constructors; schema-derived wire types |
The TypeScript package declares an entry point as @anywe/sdk-ts, with separate CommonJS and ESM builds and a ./generated/* subpath for the schema-derived types, which are deliberately not re-exported from the root. Neither package is on a public registry yet, and neither package sends /v1/* requests for you: build from an authorized checkout and treat the documented behavior as a reference until a distribution channel is announced.
Sign and verify
A platform delivery is authenticated over its exact raw bytes. Verify before parsing JSON; sign the exact bytes you will transmit, never a re-serialization. The authorization scheme is HMAC, not Bearer.
import { authorization, sign, verify } from "@anywe/sdk-ts";
const index = verify([currentSecret, previousSecret], inboundSignature, rawBody, Date.now());
const signature = sign(currentSecret, Math.floor(Date.now() / 1000), replyBody);
const authorizationHeader = authorization(agentId, credentialId);from anywe import authorization, sign, verify
index = verify([current_secret, previous_secret], inbound_signature, raw_body)
signature = sign(current_secret, int(time.time()), reply_body)
authorization_header = authorization(agent_id, credential_id)Keep every active secret in the verification list during a credential overlap. The helpers check both future and past replay-window violations, and they do not make a secret's position observable through an early return. Do not base64-decode the printed secret or reserialize the request body.
Reply-block builders
Both packages construct the same twelve reply shapes: text, card, action, image, file, buttons, progress, table, audio, form, taskUpdate (task_update in Python), and the blocks collector. Four builders throw rather than emit an invalid block: a card takes at most 3 actions, buttons takes 1 to 5, a table takes 1 to 8 columns and at most 50 rows, and a form takes 1 to 20 fields.
A helper existing does not establish end-to-end platform support, and a helper missing does not withhold it. Check the interaction reference rather than the builder list: list, location, event, notification, and video are supported end to end with no builder in either package, so construct those as plain objects, while date_picker has no builder and is refused on the agent path regardless.
import { action, blocks, buttons, card, text } from "@anywe/sdk-ts";
const replyBlocks = blocks([
text("Here is the result."),
card({ title: "Result", body: "Details", actions: [action("Open", "open://result")] }),
buttons([action("Continue", "continue")]),
]);from anywe import blocks as b
reply_blocks = b.blocks([
b.text("Here is the result."),
b.card("Result", body="Details", actions=[b.action("Open", "open://result")]),
b.buttons([b.action("Continue", "continue")]),
])Sending a reply (TypeScript only)
ReplyClient posts blocks to /hooks/v1/reply, signing every frame and retrying a 429 up to three times using the wait the platform names in the response. There is no Python equivalent: sign the body with sign above and post it yourself.
import { ReplyClient, text } from "@anywe/sdk-ts";
const client = new ReplyClient({
platformUrl: "https://anywe.dev",
agentId: "agt_01ARZ3NDEKTSV4RRFFQ69G5FAV",
credentialId: "cred_01ARZ3NDEKTSV4RRFFQ69G5FAV",
secret: process.env.ANYWE_AGENT_SECRETS!,
});
// One-shot: the whole answer, no streaming.
await client.sendFinal("dlv_01ARZ3NDEKTSV4RRFFQ69G5FAV", [text("Done.")]);
// Streaming: update() coalesces snapshots to the platform's published rate; final() flushes and ends.
const stream = client.stream("dlv_01ARZ3NDEKTSV4RRFFQ69G5FAV", 1);
stream.update([text("Working on it...")]);
await stream.final([text("Done.")]);
// Explicitly declining to answer this delivery:
await client.pass("dlv_01ARZ3NDEKTSV4RRFFQ69G5FAV", "no action needed");ANYWE_AGENT_SECRETS is the name anywe agent secret exports; CLI v0.2.0 exports the same value as AICONNECT_AGENT_SECRETS, so read that name if your credential came from it.
update takes the complete cumulative snapshot, never a delta, and coalesces to the platform's published rate: calling it faster than that is safe, and only the newest snapshot is ever sent. sendFinal and pass each derive their own idempotency key, so retrying the same call after a network failure replays safely. See replies and streaming for the wire contract this client implements.
Builders and the client enforce local bounds and retries, but they do not replace platform validation. Use the interaction reference for rendered block behavior.