@tallyforagents/sdk 0.1.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -66,6 +66,22 @@ function makeError(payload, status) {
66
66
  }
67
67
 
68
68
  // src/client.ts
69
+ var warnedRotations = /* @__PURE__ */ new Set();
70
+ function warnRotationOnce(key, until) {
71
+ const id = `${key.slice(0, 12)}:${until}`;
72
+ if (warnedRotations.has(id)) return;
73
+ warnedRotations.add(id);
74
+ const remaining = (() => {
75
+ const ms = new Date(until).getTime() - Date.now();
76
+ if (ms <= 0) return "shortly";
77
+ const h = Math.floor(ms / 36e5);
78
+ const m = Math.floor(ms % 36e5 / 6e4);
79
+ return h > 0 ? `${h}h ${m}m` : `${m}m`;
80
+ })();
81
+ console.warn(
82
+ `[tally] API key is in its rotation grace window (ends in ~${remaining}, at ${until}). Switch to the rotated key before then to avoid 401s.`
83
+ );
84
+ }
69
85
  var TallyClient = class {
70
86
  apiKey;
71
87
  baseUrl;
@@ -100,6 +116,8 @@ var TallyClient = class {
100
116
  },
101
117
  body: body === void 0 ? void 0 : JSON.stringify(body)
102
118
  });
119
+ const graceUntil = res.headers.get("tally-rotation-grace-until");
120
+ if (graceUntil) warnRotationOnce(this.apiKey, graceUntil);
103
121
  if (!res.ok) {
104
122
  let payload = {};
105
123
  try {
@@ -151,6 +169,88 @@ var AgentsResource = class {
151
169
  );
152
170
  return agent;
153
171
  }
172
+ /**
173
+ * Soft-deletes an agent. The row stays in the DB so historical
174
+ * transactions keep their attribution; future list/get responses
175
+ * hide it.
176
+ *
177
+ * Throws `ConflictError` (HTTP 409, `code: "has_active_grants"`) if
178
+ * the agent has any active permissions — revoke them first.
179
+ *
180
+ * Re-creating an agent with the same `id` via `upsert()` is the
181
+ * supported way to bring a deleted agent back.
182
+ */
183
+ async delete(id) {
184
+ await this.client.request(
185
+ "DELETE",
186
+ `/v1/agents/${encodeURIComponent(id)}`
187
+ );
188
+ }
189
+ };
190
+
191
+ // src/pagination.ts
192
+ var AsyncResourcePage = class {
193
+ constructor(fetchPage) {
194
+ this.fetchPage = fetchPage;
195
+ }
196
+ fetchPage;
197
+ /**
198
+ * Async-iterates every item across all pages. Stops when the server
199
+ * returns `next_cursor: null`.
200
+ *
201
+ * ```ts
202
+ * for await (const p of tally.payments.list({ status: "confirmed" })) {
203
+ * console.log(p.id);
204
+ * }
205
+ * ```
206
+ */
207
+ async *[Symbol.asyncIterator]() {
208
+ let cursor = null;
209
+ while (true) {
210
+ const page = await this.fetchPage(cursor);
211
+ for (const item of page.data) yield item;
212
+ if (!page.next_cursor) break;
213
+ cursor = page.next_cursor;
214
+ }
215
+ }
216
+ /**
217
+ * Collects items into an array, optionally bounded. `toArray()` with
218
+ * no argument pulls every page; `toArray(20)` stops after the first
219
+ * 20 items (across whatever page boundaries that crosses).
220
+ *
221
+ * ```ts
222
+ * const recent = await tally.payments.list().toArray(20);
223
+ * ```
224
+ */
225
+ async toArray(maxItems) {
226
+ const out = [];
227
+ for await (const item of this) {
228
+ out.push(item);
229
+ if (maxItems !== void 0 && out.length >= maxItems) break;
230
+ }
231
+ return out;
232
+ }
233
+ /**
234
+ * Returns just the first page (no auto-pagination). Use when the
235
+ * cursor itself matters — e.g., persisting it across runs to resume
236
+ * iteration later.
237
+ *
238
+ * ```ts
239
+ * const { data, next_cursor } = await tally.payments.list().firstPage();
240
+ * // … store next_cursor; next run: tally.payments.list().pageAfter(saved_cursor)
241
+ * ```
242
+ */
243
+ async firstPage() {
244
+ return this.fetchPage(null);
245
+ }
246
+ /**
247
+ * Returns the page that follows the given cursor. Useful for resuming
248
+ * iteration from a previously-persisted cursor, or for manual paging
249
+ * when the auto-iterator's eager fetching isn't a fit.
250
+ */
251
+ async pageAfter(cursor) {
252
+ return this.fetchPage(cursor);
253
+ }
154
254
  };
155
255
 
156
256
  // src/resources/payments.ts
@@ -194,10 +294,126 @@ var PaymentsResource = class {
194
294
  );
195
295
  return payment;
196
296
  }
297
+ /**
298
+ * Lists payments in the API key's account + mode, auto-paginated.
299
+ * Returns an `AsyncResourcePage` you can `for await` over to iterate
300
+ * every payment, or call `.toArray(n)` for a bounded read.
301
+ *
302
+ * Pending outbound rows are lazily refreshed from the chain on read,
303
+ * matching the dashboard's behavior — polling `list({ status: "pending" })`
304
+ * is a valid way to wait for confirmation.
305
+ *
306
+ * ```ts
307
+ * for await (const p of tally.payments.list({ status: "confirmed" })) {
308
+ * console.log(p.id, p.amount_usdc, p.to);
309
+ * }
310
+ *
311
+ * // Or bounded:
312
+ * const last20 = await tally.payments.list({ direction: "outbound" }).toArray(20);
313
+ * ```
314
+ */
315
+ list(filters = {}) {
316
+ const { limit, status, direction, agent_id, wallet, q } = filters;
317
+ return new AsyncResourcePage(async (cursor) => {
318
+ const qs = new URLSearchParams();
319
+ if (cursor) qs.set("cursor", cursor);
320
+ if (limit !== void 0) qs.set("limit", String(limit));
321
+ if (status) qs.set("status", status);
322
+ if (direction) qs.set("direction", direction);
323
+ if (agent_id) qs.set("agent_id", agent_id);
324
+ if (wallet) qs.set("wallet", wallet);
325
+ if (q) qs.set("q", q);
326
+ const query = qs.toString();
327
+ const path = query ? `/v1/payments?${query}` : "/v1/payments";
328
+ return this.client.request(
329
+ "GET",
330
+ path
331
+ );
332
+ });
333
+ }
334
+ };
335
+
336
+ // src/resources/permissions.ts
337
+ var PermissionsResource = class {
338
+ constructor(client) {
339
+ this.client = client;
340
+ }
341
+ client;
342
+ /**
343
+ * Lists active grants in the API key's account + mode, auto-paginated.
344
+ * Returns an `AsyncResourcePage` you can `for await` over to iterate
345
+ * every permission, or call `.toArray(n)` for a bounded read.
346
+ *
347
+ * Each row includes the granted caps, today's usage, and the rolling
348
+ * remaining-daily-allowance, so agent code can preflight a payment
349
+ * without round-tripping the policy bounds.
350
+ *
351
+ * ```ts
352
+ * for await (const p of tally.permissions.list({ agent_id: "research-bot" })) {
353
+ * console.log(`${p.wallet_display_name}: $${p.remaining_today_usdc} left`);
354
+ * }
355
+ * ```
356
+ */
357
+ list(filters = {}) {
358
+ const { limit, agent_id } = filters;
359
+ return new AsyncResourcePage(async (cursor) => {
360
+ const qs = new URLSearchParams();
361
+ if (cursor) qs.set("cursor", cursor);
362
+ if (limit !== void 0) qs.set("limit", String(limit));
363
+ if (agent_id) qs.set("agent_id", agent_id);
364
+ const query = qs.toString();
365
+ const path = query ? `/v1/permissions?${query}` : "/v1/permissions";
366
+ return this.client.request("GET", path);
367
+ });
368
+ }
369
+ };
370
+
371
+ // src/resources/wallets.ts
372
+ var WalletsResource = class {
373
+ constructor(client) {
374
+ this.client = client;
375
+ }
376
+ client;
377
+ /**
378
+ * Lists every wallet in the API key's account + mode. Wallets are
379
+ * returned oldest-first (so the "Main Wallet" auto-provisioned at
380
+ * sign-in shows up first).
381
+ */
382
+ async list() {
383
+ const { wallets } = await this.client.request(
384
+ "GET",
385
+ "/v1/wallets"
386
+ );
387
+ return wallets;
388
+ }
389
+ /**
390
+ * Provisions a new Privy server wallet in the API key's account + mode.
391
+ * The wallet is owned (in Privy) by the oldest `owner`-role member of
392
+ * the account, so SDK-created wallets behave identically to dashboard-
393
+ * created ones: the same passkey approves any future signer changes.
394
+ */
395
+ async create(input) {
396
+ const { wallet } = await this.client.request(
397
+ "POST",
398
+ "/v1/wallets",
399
+ input
400
+ );
401
+ return wallet;
402
+ }
197
403
  };
198
404
  var SIGNATURE_VERSION = "v1";
199
405
  var DEFAULT_TOLERANCE_SECONDS = 300;
200
406
  var WebhooksResource = class {
407
+ /**
408
+ * The TallyClient is optional so legacy zero-arg construction
409
+ * (`new WebhooksResource()`) still works for non-class consumers who
410
+ * only use `verifySignature`. List / create / revoke require a
411
+ * client; calling them on a clientless instance throws.
412
+ */
413
+ constructor(client) {
414
+ this.client = client;
415
+ }
416
+ client;
201
417
  /**
202
418
  * Verify a `tally-signature` header against the request body. Use in
203
419
  * your webhook handler before processing the payload.
@@ -218,6 +434,58 @@ var WebhooksResource = class {
218
434
  verifySignature(input) {
219
435
  return verifySignature(input);
220
436
  }
437
+ requireClient() {
438
+ if (!this.client) {
439
+ throw new Error(
440
+ "WebhooksResource was constructed without a TallyClient. Use `new Tally({ apiKey })` to enable list/create/revoke."
441
+ );
442
+ }
443
+ return this.client;
444
+ }
445
+ /**
446
+ * Lists every webhook endpoint in the API key's account + mode.
447
+ * Revoked endpoints are included (with `revoked_at` set) for audit.
448
+ *
449
+ * ```ts
450
+ * for (const w of await tally.webhooks.list()) {
451
+ * console.log(`${w.url} → ${w.events.join(",")}`);
452
+ * }
453
+ * ```
454
+ */
455
+ async list() {
456
+ const { webhooks } = await this.requireClient().request("GET", "/v1/webhooks");
457
+ return webhooks;
458
+ }
459
+ /**
460
+ * Creates a new webhook endpoint. The signing secret is returned in
461
+ * `webhook.secret` **exactly once** — store it now; it's not
462
+ * recoverable later. If you lose it, revoke and recreate.
463
+ *
464
+ * ```ts
465
+ * const webhook = await tally.webhooks.create({
466
+ * url: "https://example.com/tally/events",
467
+ * events: ["payment.confirmed", "payment.failed"],
468
+ * });
469
+ * console.log(webhook.secret); // shown once
470
+ * ```
471
+ */
472
+ async create(input) {
473
+ const { webhook } = await this.requireClient().request("POST", "/v1/webhooks", input);
474
+ return webhook;
475
+ }
476
+ /**
477
+ * Revokes a webhook endpoint by id. No further deliveries will be
478
+ * enqueued; deliveries already in-flight are not cancelled.
479
+ * Idempotent: revoking an already-revoked endpoint is a no-op.
480
+ *
481
+ * ```ts
482
+ * await tally.webhooks.revoke("whk_01HXYZ...");
483
+ * ```
484
+ */
485
+ async revoke(id) {
486
+ const { webhook } = await this.requireClient().request("POST", `/v1/webhooks/${encodeURIComponent(id)}/revoke`);
487
+ return webhook;
488
+ }
221
489
  };
222
490
  function verifySignature(input) {
223
491
  const { body, header, secret } = input;
@@ -256,6 +524,8 @@ function verifySignature(input) {
256
524
  var Tally = class {
257
525
  agents;
258
526
  payments;
527
+ permissions;
528
+ wallets;
259
529
  webhooks;
260
530
  // Expose the underlying client for advanced use (custom retries, etc.).
261
531
  // Internal callers go through the resource classes instead.
@@ -264,10 +534,13 @@ var Tally = class {
264
534
  this.client = new TallyClient(opts);
265
535
  this.agents = new AgentsResource(this.client);
266
536
  this.payments = new PaymentsResource(this.client);
267
- this.webhooks = new WebhooksResource();
537
+ this.permissions = new PermissionsResource(this.client);
538
+ this.wallets = new WalletsResource(this.client);
539
+ this.webhooks = new WebhooksResource(this.client);
268
540
  }
269
541
  };
270
542
 
543
+ exports.AsyncResourcePage = AsyncResourcePage;
271
544
  exports.AuthenticationError = AuthenticationError;
272
545
  exports.ConflictError = ConflictError;
273
546
  exports.NotFoundError = NotFoundError;
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/errors.ts","../src/client.ts","../src/resources/agents.ts","../src/resources/payments.ts","../src/resources/webhooks.ts","../src/index.ts"],"names":["createHmac","timingSafeEqual"],"mappings":"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","file":"index.cjs","sourcesContent":["// Tally SDK error types. The server returns a consistent shape on every\n// failure — see src/lib/api-errors.ts in the web app — and the SDK turns\n// it into typed exceptions consumers can catch and branch on.\n//\n// Usage in consumer code:\n//\n// try {\n// await tally.agents.upsert({ id: \"research-bot\", display_name: \"...\" });\n// } catch (e) {\n// if (e instanceof AuthenticationError) { /* rotate key */ }\n// if (e instanceof ValidationError) { /* fix input */ }\n// throw e;\n// }\n\nexport type ApiErrorPayload = {\n type: string;\n message: string;\n code?: string;\n details?: unknown;\n};\n\nexport class TallyError extends Error {\n readonly type: string;\n readonly code?: string;\n readonly status: number;\n readonly details?: unknown;\n\n constructor(opts: {\n type: string;\n message: string;\n status: number;\n code?: string;\n details?: unknown;\n }) {\n super(opts.message);\n this.name = \"TallyError\";\n this.type = opts.type;\n this.code = opts.code;\n this.status = opts.status;\n this.details = opts.details;\n }\n}\n\nexport class AuthenticationError extends TallyError {\n constructor(payload: ApiErrorPayload, status: number) {\n super({ ...payload, status });\n this.name = \"AuthenticationError\";\n }\n}\n\nexport class NotFoundError extends TallyError {\n constructor(payload: ApiErrorPayload, status: number) {\n super({ ...payload, status });\n this.name = \"NotFoundError\";\n }\n}\n\nexport class ValidationError extends TallyError {\n constructor(payload: ApiErrorPayload, status: number) {\n super({ ...payload, status });\n this.name = \"ValidationError\";\n }\n}\n\nexport class RateLimitError extends TallyError {\n constructor(payload: ApiErrorPayload, status: number) {\n super({ ...payload, status });\n this.name = \"RateLimitError\";\n }\n}\n\nexport class ConflictError extends TallyError {\n constructor(payload: ApiErrorPayload, status: number) {\n super({ ...payload, status });\n this.name = \"ConflictError\";\n }\n}\n\n// Maps server error.type → typed exception class.\nexport function makeError(payload: ApiErrorPayload, status: number): TallyError {\n switch (payload.type) {\n case \"unauthenticated\":\n case \"forbidden\":\n return new AuthenticationError(payload, status);\n case \"not_found\":\n return new NotFoundError(payload, status);\n case \"validation_failed\":\n return new ValidationError(payload, status);\n case \"rate_limited\":\n return new RateLimitError(payload, status);\n case \"conflict\":\n return new ConflictError(payload, status);\n default:\n return new TallyError({ ...payload, status });\n }\n}\n","import { makeError, TallyError, type ApiErrorPayload } from \"./errors\";\n\n// Thin HTTP client used by every resource. Centralizes:\n// - Authorization header\n// - JSON request/response handling\n// - Error mapping (server error.type → typed exception)\n//\n// Uses Node 18+'s native fetch. Works in browsers too if you ever want to\n// hit the API from one — though the more common pattern is server-to-server\n// from the consumer's agent infrastructure.\n\nexport type ClientOptions = {\n /** Tally API key (begins with `tly_`). */\n apiKey: string;\n /** Base URL of the Tally API. Defaults to https://app.tallyforagents.com. Override for local dev or self-hosting. */\n baseUrl?: string;\n /** Custom fetch implementation. Useful for tests or non-Node runtimes. */\n fetch?: typeof fetch;\n};\n\nexport class TallyClient {\n readonly apiKey: string;\n readonly baseUrl: string;\n readonly #fetch: typeof fetch;\n\n constructor(opts: ClientOptions) {\n if (\n !opts.apiKey ||\n (!opts.apiKey.startsWith(\"tly_test_\") && !opts.apiKey.startsWith(\"tly_live_\"))\n ) {\n throw new TallyError({\n type: \"validation_failed\",\n message:\n \"Invalid API key. Tally keys start with 'tly_test_' (test mode) or 'tly_live_' (live mode).\",\n status: 0,\n });\n }\n this.apiKey = opts.apiKey;\n this.baseUrl = (opts.baseUrl ?? \"https://app.tallyforagents.com\").replace(/\\/$/, \"\");\n this.#fetch = opts.fetch ?? globalThis.fetch;\n if (typeof this.#fetch !== \"function\") {\n throw new TallyError({\n type: \"internal\",\n message: \"fetch is not available in this runtime. Provide one via `fetch` option.\",\n status: 0,\n });\n }\n }\n\n async request<T>(\n method: \"GET\" | \"POST\" | \"DELETE\",\n path: string,\n body?: unknown,\n ): Promise<T> {\n const url = `${this.baseUrl}${path}`;\n const res = await this.#fetch(url, {\n method,\n headers: {\n Authorization: `Bearer ${this.apiKey}`,\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\",\n },\n body: body === undefined ? undefined : JSON.stringify(body),\n });\n\n if (!res.ok) {\n let payload: { error?: ApiErrorPayload } = {};\n try {\n payload = await res.json();\n } catch {\n // Non-JSON error body — fall back to a generic message.\n }\n const err = payload.error ?? {\n type: \"internal\",\n message: `Request failed with status ${res.status}.`,\n };\n throw makeError(err, res.status);\n }\n\n // 204 No Content (e.g., future delete endpoints)\n if (res.status === 204) return undefined as T;\n return (await res.json()) as T;\n }\n}\n","import type { TallyClient } from \"../client\";\n\nexport type AgentWallet = {\n /** Wallet address (lowercased EVM hex) the agent can spend from. */\n address: string;\n /** Friendly name the wallet was given in the dashboard. */\n display_name: string;\n /** Optional role label (e.g., \"main\", \"treasury\"); null if unset. */\n role_label: string | null;\n /** Max per-tx spend in USDC as a decimal string. */\n max_per_tx_usdc: string;\n /** Daily cap in USDC as a decimal string, null if uncapped. */\n daily_cap_usdc: string | null;\n};\n\nexport type Agent = {\n /** Stable user-provided identifier (e.g., \"research-bot\"). */\n id: string;\n /** \"active\" — at least one activated (non-revoked) signer is attached.\n * \"no_permissions\" — no activated signer (may still have pending\n * grants — see pending_signers). */\n status: \"no_permissions\" | \"active\";\n mode: \"test\" | \"live\";\n /** ISO 8601 timestamp. */\n created_at: string;\n /** The account slug this agent belongs to. Surfaced so callers can\n * build dashboard deep-links (e.g., when telling the user to grant\n * a permission). */\n account_slug: string;\n /** Count of non-revoked, activated signers. */\n active_signers: number;\n /** Count of non-revoked, not-yet-activated signers. */\n pending_signers: number;\n /** Wallets this agent has active permission to spend from, with the\n * per-wallet caps from the permission. Empty array if no active\n * grants. Pick one to pass as `wallet` to tally.payments.create(). */\n wallets: AgentWallet[];\n};\n\nexport type AgentUpsertInput = {\n /** User-provided stable string identifier (e.g., \"research-bot\"). */\n id: string;\n};\n\n// agents.upsert / list / get — Phase 2's full surface.\n// Future phases will add: agents.delete, agents.permissions.*, etc.\n\nexport class AgentsResource {\n constructor(private readonly client: TallyClient) {}\n\n /**\n * Creates the agent if it doesn't exist; no-op if it does.\n *\n * Scope: the active API key's account + mode. Same id can exist\n * independently in test and live mode for the same account.\n */\n async upsert(input: AgentUpsertInput): Promise<Agent> {\n const { agent } = await this.client.request<{ agent: Agent }>(\n \"POST\",\n \"/v1/agents\",\n input,\n );\n return agent;\n }\n\n async list(): Promise<Agent[]> {\n const { agents } = await this.client.request<{ agents: Agent[] }>(\n \"GET\",\n \"/v1/agents\",\n );\n return agents;\n }\n\n async get(id: string): Promise<Agent> {\n const { agent } = await this.client.request<{ agent: Agent }>(\n \"GET\",\n `/v1/agents/${encodeURIComponent(id)}`,\n );\n return agent;\n }\n}\n","import type { TallyClient } from \"../client\";\n\nexport type Payment = {\n id: string;\n /** \"pending\" until the on-chain tx confirms; updated by a follow-up\n * GET (planned) or webhook (Phase 6). */\n status: \"pending\" | \"confirmed\" | \"failed\";\n /** Transaction hash on the underlying chain. Set as soon as Privy\n * accepts the signed RPC; the SDK does not wait for receipt. */\n tx_hash: string | null;\n amount_usdc: string;\n to: string;\n from: string;\n memo: string | null;\n idempotency_key: string | null;\n created_at: string;\n};\n\nexport type PaymentCreateInput = {\n /** Agent's user-provided id (the externalId), e.g. \"research-bot\". */\n agent_id: string;\n /** Sender wallet address. Must be in this API key's account+mode and\n * must have an active permission grant for the agent. */\n wallet: string;\n /** Recipient EVM address. */\n to: string;\n /** Decimal USDC amount as a string, e.g. \"10\" or \"2.50\". */\n amount_usdc: string;\n /** Optional memo (max 200 chars), stored alongside the transaction. */\n memo?: string;\n /** Optional idempotency key (max 64 chars). Scoped to (account, mode);\n * retries with the same key return the original Payment without\n * resubmitting on-chain. */\n idempotency_key?: string;\n};\n\n// payments.create — single endpoint for Phase 4.\n// Future phases will add: list, get (with chain status refresh), retry.\n\nexport class PaymentsResource {\n constructor(private readonly client: TallyClient) {}\n\n /**\n * Submits a USDC payment from one of the agent's granted wallets.\n *\n * Enforcement happens in two layers:\n * 1. Tally checks policy (per-tx max, recipient/contract allowlist,\n * expiry, daily cap) and rejects with a structured error before\n * any Privy call.\n * 2. Privy's secure enclave independently checks the per-tx max and\n * recipient/contract conditions encoded in the signer's policy.\n *\n * Returns immediately with `status: \"pending\"` and the on-chain\n * `tx_hash` once Privy accepts the signed RPC. The SDK does not wait\n * for block confirmation; the Transaction row is updated by a\n * separate confirmation flow (forthcoming).\n */\n async create(input: PaymentCreateInput): Promise<Payment> {\n const { payment } = await this.client.request<{ payment: Payment }>(\n \"POST\",\n \"/v1/payments\",\n input,\n );\n return payment;\n }\n\n /**\n * Fetches the current state of a payment by id. If still pending on\n * Tally's side, this call lazily refreshes from the chain — so polling\n * this is the canonical way to wait for `confirmed` / `failed`.\n */\n async get(id: string): Promise<Payment> {\n const { payment } = await this.client.request<{ payment: Payment }>(\n \"GET\",\n `/v1/payments/${encodeURIComponent(id)}`,\n );\n return payment;\n }\n}\n","import { createHmac, timingSafeEqual } from \"node:crypto\";\n\n// Webhook helpers for SDK consumers. Tally signs every delivery with\n// HMAC-SHA256 over `${timestamp}.${body}` and sends the signature in\n// the `tally-signature` header:\n//\n// tally-signature: t=<unix_seconds>,v1=<hex_lowercase>\n//\n// Receivers MUST verify before trusting any payload — otherwise a\n// known URL is forge-able. Pass the RAW request body (not the\n// JSON-parsed object) so the bytes match what Tally signed.\n//\n// The verifier is intentionally duplicated from the server-side\n// implementation (src/lib/webhook-sign.ts) to keep the SDK\n// self-contained — external consumers shouldn't depend on Tally's\n// server code.\n\nconst SIGNATURE_VERSION = \"v1\";\nexport const DEFAULT_TOLERANCE_SECONDS = 300; // 5 minutes\n\nexport type WebhookVerifyResult =\n | { ok: true }\n | {\n ok: false;\n reason:\n | \"missing_header\"\n | \"malformed_header\"\n | \"no_v1_signature\"\n | \"timestamp_out_of_tolerance\"\n | \"signature_mismatch\";\n };\n\nexport type WebhookVerifyInput = {\n /** Raw request body the receiver got — string, exactly the bytes\n * Tally signed. Don't pass a JSON-parsed object. */\n body: string;\n /** Value of the `tally-signature` header on the incoming request. */\n header: string | null | undefined;\n /** The plaintext signing secret you saved at webhook creation\n * (`whsec_<mode>_<base64url>`). */\n secret: string;\n /** Unix seconds. Defaults to now. Pin for tests. */\n now?: number;\n /** How far the header timestamp may drift from `now`. Defaults to\n * 300s (5 min). Tighten or loosen to taste; the wider the window,\n * the longer a stolen signature stays replayable. */\n toleranceSeconds?: number;\n};\n\nexport class WebhooksResource {\n /**\n * Verify a `tally-signature` header against the request body. Use in\n * your webhook handler before processing the payload.\n *\n * @example\n * ```ts\n * const body = await req.text(); // raw, not JSON-parsed\n * const result = tally.webhooks.verifySignature({\n * body,\n * header: req.headers.get(\"tally-signature\"),\n * secret: process.env.TALLY_WEBHOOK_SECRET!,\n * });\n * if (!result.ok) return new Response(\"invalid signature\", { status: 400 });\n * const event = JSON.parse(body);\n * // ... handle event\n * ```\n */\n verifySignature(input: WebhookVerifyInput): WebhookVerifyResult {\n return verifySignature(input);\n }\n}\n\nexport function verifySignature(input: WebhookVerifyInput): WebhookVerifyResult {\n const { body, header, secret } = input;\n const now = input.now ?? Math.floor(Date.now() / 1000);\n const tolerance = input.toleranceSeconds ?? DEFAULT_TOLERANCE_SECONDS;\n\n if (!header) return { ok: false, reason: \"missing_header\" };\n\n let timestamp: number | null = null;\n let v1: string | null = null;\n for (const part of header.split(\",\")) {\n const [k, v] = part.split(\"=\", 2);\n if (k === \"t\") {\n const n = Number(v);\n if (Number.isFinite(n)) timestamp = n;\n } else if (k === SIGNATURE_VERSION) {\n v1 = v;\n }\n }\n\n if (timestamp === null) return { ok: false, reason: \"malformed_header\" };\n if (!v1) return { ok: false, reason: \"no_v1_signature\" };\n\n if (Math.abs(now - timestamp) > tolerance) {\n return { ok: false, reason: \"timestamp_out_of_tolerance\" };\n }\n\n const expected = createHmac(\"sha256\", secret)\n .update(`${timestamp}.${body}`)\n .digest(\"hex\");\n\n if (expected.length !== v1.length) {\n return { ok: false, reason: \"signature_mismatch\" };\n }\n const a = Buffer.from(expected, \"utf8\");\n const b = Buffer.from(v1, \"utf8\");\n if (!timingSafeEqual(a, b)) {\n return { ok: false, reason: \"signature_mismatch\" };\n }\n return { ok: true };\n}\n","// @tallyforagents/sdk — the financial OS for AI agents.\n//\n// Usage:\n// import { Tally } from \"@tallyforagents/sdk\";\n//\n// const tally = new Tally({ apiKey: process.env.TALLY_API_KEY! });\n// await tally.agents.upsert({ id: \"research-bot\", display_name: \"Research Bot\" });\n\nimport { TallyClient, type ClientOptions } from \"./client\";\nimport { AgentsResource } from \"./resources/agents\";\nimport { PaymentsResource } from \"./resources/payments\";\nimport { WebhooksResource } from \"./resources/webhooks\";\n\nexport class Tally {\n readonly agents: AgentsResource;\n readonly payments: PaymentsResource;\n readonly webhooks: WebhooksResource;\n\n // Expose the underlying client for advanced use (custom retries, etc.).\n // Internal callers go through the resource classes instead.\n private readonly client: TallyClient;\n\n constructor(opts: ClientOptions) {\n this.client = new TallyClient(opts);\n this.agents = new AgentsResource(this.client);\n this.payments = new PaymentsResource(this.client);\n this.webhooks = new WebhooksResource();\n }\n}\n\nexport type { ClientOptions } from \"./client\";\nexport type { Agent, AgentUpsertInput, AgentWallet } from \"./resources/agents\";\nexport type { Payment, PaymentCreateInput } from \"./resources/payments\";\nexport type {\n WebhookVerifyInput,\n WebhookVerifyResult,\n} from \"./resources/webhooks\";\nexport { verifySignature } from \"./resources/webhooks\";\nexport {\n TallyError,\n AuthenticationError,\n NotFoundError,\n ValidationError,\n RateLimitError,\n ConflictError,\n} from \"./errors\";\n"]}
1
+ {"version":3,"sources":["../src/errors.ts","../src/client.ts","../src/resources/agents.ts","../src/pagination.ts","../src/resources/payments.ts","../src/resources/permissions.ts","../src/resources/wallets.ts","../src/resources/webhooks.ts","../src/index.ts"],"names":["createHmac","timingSafeEqual"],"mappings":"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","file":"index.cjs","sourcesContent":["// Tally SDK error types. The server returns a consistent shape on every\n// failure — see src/lib/api-errors.ts in the web app — and the SDK turns\n// it into typed exceptions consumers can catch and branch on.\n//\n// Usage in consumer code:\n//\n// try {\n// await tally.agents.upsert({ id: \"research-bot\", display_name: \"...\" });\n// } catch (e) {\n// if (e instanceof AuthenticationError) { /* rotate key */ }\n// if (e instanceof ValidationError) { /* fix input */ }\n// throw e;\n// }\n\nexport type ApiErrorPayload = {\n type: string;\n message: string;\n code?: string;\n details?: unknown;\n};\n\nexport class TallyError extends Error {\n readonly type: string;\n readonly code?: string;\n readonly status: number;\n readonly details?: unknown;\n\n constructor(opts: {\n type: string;\n message: string;\n status: number;\n code?: string;\n details?: unknown;\n }) {\n super(opts.message);\n this.name = \"TallyError\";\n this.type = opts.type;\n this.code = opts.code;\n this.status = opts.status;\n this.details = opts.details;\n }\n}\n\nexport class AuthenticationError extends TallyError {\n constructor(payload: ApiErrorPayload, status: number) {\n super({ ...payload, status });\n this.name = \"AuthenticationError\";\n }\n}\n\nexport class NotFoundError extends TallyError {\n constructor(payload: ApiErrorPayload, status: number) {\n super({ ...payload, status });\n this.name = \"NotFoundError\";\n }\n}\n\nexport class ValidationError extends TallyError {\n constructor(payload: ApiErrorPayload, status: number) {\n super({ ...payload, status });\n this.name = \"ValidationError\";\n }\n}\n\nexport class RateLimitError extends TallyError {\n constructor(payload: ApiErrorPayload, status: number) {\n super({ ...payload, status });\n this.name = \"RateLimitError\";\n }\n}\n\nexport class ConflictError extends TallyError {\n constructor(payload: ApiErrorPayload, status: number) {\n super({ ...payload, status });\n this.name = \"ConflictError\";\n }\n}\n\n// Maps server error.type → typed exception class.\nexport function makeError(payload: ApiErrorPayload, status: number): TallyError {\n switch (payload.type) {\n case \"unauthenticated\":\n case \"forbidden\":\n return new AuthenticationError(payload, status);\n case \"not_found\":\n return new NotFoundError(payload, status);\n case \"validation_failed\":\n return new ValidationError(payload, status);\n case \"rate_limited\":\n return new RateLimitError(payload, status);\n case \"conflict\":\n return new ConflictError(payload, status);\n default:\n return new TallyError({ ...payload, status });\n }\n}\n","import { makeError, TallyError, type ApiErrorPayload } from \"./errors\";\n\n// One-shot warning when the API key being used is in its rotation\n// grace window. The server sets `Tally-Rotation-Grace-Until` on every\n// /v1/* response when grace is active; we surface it on stderr so the\n// developer notices before the key actually expires. Throttled to\n// once per process per (key + grace_until) tuple so high-volume\n// callers don't get spammed.\nconst warnedRotations = new Set<string>();\nfunction warnRotationOnce(key: string, until: string) {\n const id = `${key.slice(0, 12)}:${until}`;\n if (warnedRotations.has(id)) return;\n warnedRotations.add(id);\n const remaining = (() => {\n const ms = new Date(until).getTime() - Date.now();\n if (ms <= 0) return \"shortly\";\n const h = Math.floor(ms / 3_600_000);\n const m = Math.floor((ms % 3_600_000) / 60_000);\n return h > 0 ? `${h}h ${m}m` : `${m}m`;\n })();\n // Use stderr so it doesn't pollute the program's normal output.\n // eslint-disable-next-line no-console\n console.warn(\n `[tally] API key is in its rotation grace window (ends in ~${remaining}, at ${until}). Switch to the rotated key before then to avoid 401s.`,\n );\n}\n\n// Thin HTTP client used by every resource. Centralizes:\n// - Authorization header\n// - JSON request/response handling\n// - Error mapping (server error.type → typed exception)\n//\n// Uses Node 18+'s native fetch. Works in browsers too if you ever want to\n// hit the API from one — though the more common pattern is server-to-server\n// from the consumer's agent infrastructure.\n\nexport type ClientOptions = {\n /** Tally API key (begins with `tly_`). */\n apiKey: string;\n /** Base URL of the Tally API. Defaults to https://app.tallyforagents.com. Override for local dev or self-hosting. */\n baseUrl?: string;\n /** Custom fetch implementation. Useful for tests or non-Node runtimes. */\n fetch?: typeof fetch;\n};\n\nexport class TallyClient {\n readonly apiKey: string;\n readonly baseUrl: string;\n readonly #fetch: typeof fetch;\n\n constructor(opts: ClientOptions) {\n if (\n !opts.apiKey ||\n (!opts.apiKey.startsWith(\"tly_test_\") && !opts.apiKey.startsWith(\"tly_live_\"))\n ) {\n throw new TallyError({\n type: \"validation_failed\",\n message:\n \"Invalid API key. Tally keys start with 'tly_test_' (test mode) or 'tly_live_' (live mode).\",\n status: 0,\n });\n }\n this.apiKey = opts.apiKey;\n this.baseUrl = (opts.baseUrl ?? \"https://app.tallyforagents.com\").replace(/\\/$/, \"\");\n this.#fetch = opts.fetch ?? globalThis.fetch;\n if (typeof this.#fetch !== \"function\") {\n throw new TallyError({\n type: \"internal\",\n message: \"fetch is not available in this runtime. Provide one via `fetch` option.\",\n status: 0,\n });\n }\n }\n\n async request<T>(\n method: \"GET\" | \"POST\" | \"DELETE\",\n path: string,\n body?: unknown,\n ): Promise<T> {\n const url = `${this.baseUrl}${path}`;\n const res = await this.#fetch(url, {\n method,\n headers: {\n Authorization: `Bearer ${this.apiKey}`,\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\",\n },\n body: body === undefined ? undefined : JSON.stringify(body),\n });\n\n // Surface rotation-grace warning on every response (success or\n // error) where the server attached the header. Server sets it on\n // post-auth responses; absent header = no rotation in progress.\n const graceUntil = res.headers.get(\"tally-rotation-grace-until\");\n if (graceUntil) warnRotationOnce(this.apiKey, graceUntil);\n\n if (!res.ok) {\n let payload: { error?: ApiErrorPayload } = {};\n try {\n payload = await res.json();\n } catch {\n // Non-JSON error body — fall back to a generic message.\n }\n const err = payload.error ?? {\n type: \"internal\",\n message: `Request failed with status ${res.status}.`,\n };\n throw makeError(err, res.status);\n }\n\n // 204 No Content (e.g., future delete endpoints)\n if (res.status === 204) return undefined as T;\n return (await res.json()) as T;\n }\n}\n","import type { TallyClient } from \"../client\";\n\nexport type AgentWallet = {\n /** Wallet address (lowercased EVM hex) the agent can spend from. */\n address: string;\n /** Friendly name the wallet was given in the dashboard. */\n display_name: string;\n /** Optional role label (e.g., \"main\", \"treasury\"); null if unset. */\n role_label: string | null;\n /** Max per-tx spend in USDC as a decimal string. */\n max_per_tx_usdc: string;\n /** Daily cap in USDC as a decimal string, null if uncapped. */\n daily_cap_usdc: string | null;\n};\n\nexport type Agent = {\n /** Stable user-provided identifier (e.g., \"research-bot\"). */\n id: string;\n /** \"active\" — at least one activated (non-revoked) signer is attached.\n * \"no_permissions\" — no activated signer (may still have pending\n * grants — see pending_signers). */\n status: \"no_permissions\" | \"active\";\n mode: \"test\" | \"live\";\n /** ISO 8601 timestamp. */\n created_at: string;\n /** The account slug this agent belongs to. Surfaced so callers can\n * build dashboard deep-links (e.g., when telling the user to grant\n * a permission). */\n account_slug: string;\n /** Count of non-revoked, activated signers. */\n active_signers: number;\n /** Count of non-revoked, not-yet-activated signers. */\n pending_signers: number;\n /** Wallets this agent has active permission to spend from, with the\n * per-wallet caps from the permission. Empty array if no active\n * grants. Pick one to pass as `wallet` to tally.payments.create(). */\n wallets: AgentWallet[];\n};\n\nexport type AgentUpsertInput = {\n /** User-provided stable string identifier (e.g., \"research-bot\"). */\n id: string;\n};\n\n// agents.upsert / list / get — Phase 2's full surface.\n// Future phases will add: agents.delete, agents.permissions.*, etc.\n\nexport class AgentsResource {\n constructor(private readonly client: TallyClient) {}\n\n /**\n * Creates the agent if it doesn't exist; no-op if it does.\n *\n * Scope: the active API key's account + mode. Same id can exist\n * independently in test and live mode for the same account.\n */\n async upsert(input: AgentUpsertInput): Promise<Agent> {\n const { agent } = await this.client.request<{ agent: Agent }>(\n \"POST\",\n \"/v1/agents\",\n input,\n );\n return agent;\n }\n\n async list(): Promise<Agent[]> {\n const { agents } = await this.client.request<{ agents: Agent[] }>(\n \"GET\",\n \"/v1/agents\",\n );\n return agents;\n }\n\n async get(id: string): Promise<Agent> {\n const { agent } = await this.client.request<{ agent: Agent }>(\n \"GET\",\n `/v1/agents/${encodeURIComponent(id)}`,\n );\n return agent;\n }\n\n /**\n * Soft-deletes an agent. The row stays in the DB so historical\n * transactions keep their attribution; future list/get responses\n * hide it.\n *\n * Throws `ConflictError` (HTTP 409, `code: \"has_active_grants\"`) if\n * the agent has any active permissions — revoke them first.\n *\n * Re-creating an agent with the same `id` via `upsert()` is the\n * supported way to bring a deleted agent back.\n */\n async delete(id: string): Promise<void> {\n await this.client.request<{ deleted: boolean; id: string }>(\n \"DELETE\",\n `/v1/agents/${encodeURIComponent(id)}`,\n );\n }\n}\n","// Stripe-style auto-paginating list result. Implements `AsyncIterable`\n// so callers can `for await (const item of list) { ... }` and the SDK\n// transparently fetches subsequent pages as iteration consumes items.\n//\n// Server contract: each fetch returns `{ data: T[], next_cursor: string | null }`.\n// When `next_cursor` is null, the iterator stops.\n//\n// Used by:\n// - `tally.payments.list()`\n// - `tally.permissions.list()` (Phase 2)\n// - any future v1 list endpoint that follows the same shape\n\nexport interface PageFetcher<T> {\n (cursor: string | null): Promise<{ data: T[]; next_cursor: string | null }>;\n}\n\nexport class AsyncResourcePage<T> implements AsyncIterable<T> {\n constructor(private readonly fetchPage: PageFetcher<T>) {}\n\n /**\n * Async-iterates every item across all pages. Stops when the server\n * returns `next_cursor: null`.\n *\n * ```ts\n * for await (const p of tally.payments.list({ status: \"confirmed\" })) {\n * console.log(p.id);\n * }\n * ```\n */\n async *[Symbol.asyncIterator](): AsyncGenerator<T> {\n let cursor: string | null = null;\n while (true) {\n const page = await this.fetchPage(cursor);\n for (const item of page.data) yield item;\n if (!page.next_cursor) break;\n cursor = page.next_cursor;\n }\n }\n\n /**\n * Collects items into an array, optionally bounded. `toArray()` with\n * no argument pulls every page; `toArray(20)` stops after the first\n * 20 items (across whatever page boundaries that crosses).\n *\n * ```ts\n * const recent = await tally.payments.list().toArray(20);\n * ```\n */\n async toArray(maxItems?: number): Promise<T[]> {\n const out: T[] = [];\n for await (const item of this) {\n out.push(item);\n if (maxItems !== undefined && out.length >= maxItems) break;\n }\n return out;\n }\n\n /**\n * Returns just the first page (no auto-pagination). Use when the\n * cursor itself matters — e.g., persisting it across runs to resume\n * iteration later.\n *\n * ```ts\n * const { data, next_cursor } = await tally.payments.list().firstPage();\n * // … store next_cursor; next run: tally.payments.list().pageAfter(saved_cursor)\n * ```\n */\n async firstPage(): Promise<{ data: T[]; next_cursor: string | null }> {\n return this.fetchPage(null);\n }\n\n /**\n * Returns the page that follows the given cursor. Useful for resuming\n * iteration from a previously-persisted cursor, or for manual paging\n * when the auto-iterator's eager fetching isn't a fit.\n */\n async pageAfter(\n cursor: string,\n ): Promise<{ data: T[]; next_cursor: string | null }> {\n return this.fetchPage(cursor);\n }\n}\n","import type { TallyClient } from \"../client\";\nimport { AsyncResourcePage } from \"../pagination\";\n\nexport type Payment = {\n id: string;\n /** \"pending\" until the on-chain tx confirms; updated by a follow-up\n * GET or webhook. */\n status: \"pending\" | \"confirmed\" | \"failed\";\n /** Transaction hash on the underlying chain. Set as soon as Privy\n * accepts the signed RPC; the SDK does not wait for receipt. */\n tx_hash: string | null;\n amount_usdc: string;\n /** Destination address. For outbound, this is the external recipient.\n * For inbound, this is one of your wallets. */\n to: string;\n /** Source address. For outbound, this is your wallet. For inbound,\n * this is the external sender. */\n from: string;\n memo: string | null;\n idempotency_key: string | null;\n created_at: string;\n /** \"inbound\" or \"outbound\". Populated on list responses; may be\n * undefined on responses from `create()` or `get()` (which always\n * describe outbound payments). */\n direction?: \"inbound\" | \"outbound\";\n /** Your wallet's address — always one of YOUR wallets regardless of\n * direction. Same as `from` for outbound, `to` for inbound. */\n wallet_address?: string;\n /** Friendly name of `wallet_address`. */\n wallet_display_name?: string;\n /** The agent's externalId for outbound payments; null for inbound. */\n agent_id?: string | null;\n /** Block timestamp from the chain, set after the tx confirms. */\n block_timestamp?: string | null;\n /** Human-readable reason for `status: \"failed\"` rows. */\n error_reason?: string | null;\n};\n\nexport type PaymentListFilters = {\n /** Filter by payment status. */\n status?: \"pending\" | \"confirmed\" | \"failed\";\n /** Filter by direction. */\n direction?: \"inbound\" | \"outbound\";\n /** Filter by agent (external id, the user-provided string). */\n agent_id?: string;\n /** Filter by wallet address (case-insensitive). */\n wallet?: string;\n /** Free-text search over tx hash, addresses, memo. */\n q?: string;\n /** Max items per page. Default 50, max 100. The auto-iterator uses\n * this for each page fetch. */\n limit?: number;\n};\n\nexport type PaymentCreateInput = {\n /** Agent's user-provided id (the externalId), e.g. \"research-bot\". */\n agent_id: string;\n /** Sender wallet address. Must be in this API key's account+mode and\n * must have an active permission grant for the agent. */\n wallet: string;\n /** Recipient EVM address. */\n to: string;\n /** Decimal USDC amount as a string, e.g. \"10\" or \"2.50\". */\n amount_usdc: string;\n /** Optional memo (max 200 chars), stored alongside the transaction. */\n memo?: string;\n /** Optional idempotency key (max 64 chars). Scoped to (account, mode);\n * retries with the same key return the original Payment without\n * resubmitting on-chain. */\n idempotency_key?: string;\n};\n\n// payments.create — single endpoint for Phase 4.\n// Future phases will add: list, get (with chain status refresh), retry.\n\nexport class PaymentsResource {\n constructor(private readonly client: TallyClient) {}\n\n /**\n * Submits a USDC payment from one of the agent's granted wallets.\n *\n * Enforcement happens in two layers:\n * 1. Tally checks policy (per-tx max, recipient/contract allowlist,\n * expiry, daily cap) and rejects with a structured error before\n * any Privy call.\n * 2. Privy's secure enclave independently checks the per-tx max and\n * recipient/contract conditions encoded in the signer's policy.\n *\n * Returns immediately with `status: \"pending\"` and the on-chain\n * `tx_hash` once Privy accepts the signed RPC. The SDK does not wait\n * for block confirmation; the Transaction row is updated by a\n * separate confirmation flow (forthcoming).\n */\n async create(input: PaymentCreateInput): Promise<Payment> {\n const { payment } = await this.client.request<{ payment: Payment }>(\n \"POST\",\n \"/v1/payments\",\n input,\n );\n return payment;\n }\n\n /**\n * Fetches the current state of a payment by id. If still pending on\n * Tally's side, this call lazily refreshes from the chain — so polling\n * this is the canonical way to wait for `confirmed` / `failed`.\n */\n async get(id: string): Promise<Payment> {\n const { payment } = await this.client.request<{ payment: Payment }>(\n \"GET\",\n `/v1/payments/${encodeURIComponent(id)}`,\n );\n return payment;\n }\n\n /**\n * Lists payments in the API key's account + mode, auto-paginated.\n * Returns an `AsyncResourcePage` you can `for await` over to iterate\n * every payment, or call `.toArray(n)` for a bounded read.\n *\n * Pending outbound rows are lazily refreshed from the chain on read,\n * matching the dashboard's behavior — polling `list({ status: \"pending\" })`\n * is a valid way to wait for confirmation.\n *\n * ```ts\n * for await (const p of tally.payments.list({ status: \"confirmed\" })) {\n * console.log(p.id, p.amount_usdc, p.to);\n * }\n *\n * // Or bounded:\n * const last20 = await tally.payments.list({ direction: \"outbound\" }).toArray(20);\n * ```\n */\n list(filters: PaymentListFilters = {}): AsyncResourcePage<Payment> {\n const { limit, status, direction, agent_id, wallet, q } = filters;\n return new AsyncResourcePage<Payment>(async (cursor) => {\n const qs = new URLSearchParams();\n if (cursor) qs.set(\"cursor\", cursor);\n if (limit !== undefined) qs.set(\"limit\", String(limit));\n if (status) qs.set(\"status\", status);\n if (direction) qs.set(\"direction\", direction);\n if (agent_id) qs.set(\"agent_id\", agent_id);\n if (wallet) qs.set(\"wallet\", wallet);\n if (q) qs.set(\"q\", q);\n const query = qs.toString();\n const path = query ? `/v1/payments?${query}` : \"/v1/payments\";\n return this.client.request<{ data: Payment[]; next_cursor: string | null }>(\n \"GET\",\n path,\n );\n });\n }\n}\n","import type { TallyClient } from \"../client\";\nimport { AsyncResourcePage } from \"../pagination\";\n\nexport type Permission = {\n /** Stable cuid for this grant. Use as the reference id if/when revoke\n * or update endpoints land. */\n id: string;\n /** Agent's user-provided externalId (e.g., \"research-bot\"). */\n agent_id: string;\n /** Wallet address this grant is on. */\n wallet_address: string;\n /** Friendly wallet name set at provisioning. */\n wallet_display_name: string;\n /** \"active\" once the wallet owner has authorized the signer on-chain;\n * \"pending\" before that. */\n status: \"active\" | \"pending\";\n max_per_tx_usdc: string;\n daily_cap_usdc: string | null;\n /** USDC already moved by this grant today (UTC). */\n used_today_usdc: string;\n /** daily_cap_usdc minus used_today_usdc, or null if uncapped. Clamped\n * to 0 when over-spent (shouldn't happen — daily-cap enforcement is\n * pre-broadcast — but defensively non-negative). */\n remaining_today_usdc: string | null;\n /** Whitelisted recipient addresses. Empty = unrestricted. */\n recipient_allowlist: string[];\n /** Whitelisted contract addresses. Defaults to the mode's USDC contract. */\n contract_allowlist: string[];\n /** ISO 8601 timestamp; null if no expiry. */\n expires_at: string | null;\n created_at: string;\n activated_at: string | null;\n};\n\nexport type PermissionListFilters = {\n /** Filter by agent (external id). */\n agent_id?: string;\n /** Max items per page. Default 50, max 100. */\n limit?: number;\n};\n\nexport class PermissionsResource {\n constructor(private readonly client: TallyClient) {}\n\n /**\n * Lists active grants in the API key's account + mode, auto-paginated.\n * Returns an `AsyncResourcePage` you can `for await` over to iterate\n * every permission, or call `.toArray(n)` for a bounded read.\n *\n * Each row includes the granted caps, today's usage, and the rolling\n * remaining-daily-allowance, so agent code can preflight a payment\n * without round-tripping the policy bounds.\n *\n * ```ts\n * for await (const p of tally.permissions.list({ agent_id: \"research-bot\" })) {\n * console.log(`${p.wallet_display_name}: $${p.remaining_today_usdc} left`);\n * }\n * ```\n */\n list(filters: PermissionListFilters = {}): AsyncResourcePage<Permission> {\n const { limit, agent_id } = filters;\n return new AsyncResourcePage<Permission>(async (cursor) => {\n const qs = new URLSearchParams();\n if (cursor) qs.set(\"cursor\", cursor);\n if (limit !== undefined) qs.set(\"limit\", String(limit));\n if (agent_id) qs.set(\"agent_id\", agent_id);\n const query = qs.toString();\n const path = query ? `/v1/permissions?${query}` : \"/v1/permissions\";\n return this.client.request<{\n data: Permission[];\n next_cursor: string | null;\n }>(\"GET\", path);\n });\n }\n}\n","import type { TallyClient } from \"../client\";\n\nexport type Wallet = {\n /** Stable Tally cuid for this wallet. Pass as `wallet_id` to permission\n * routes (grant / edit / revoke). */\n id: string;\n /** EVM address. Use as `wallet` in `tally.payments.create({ wallet })`. */\n address: string;\n /** Friendly name set when the wallet was created. */\n display_name: string;\n /** Optional role label (e.g., \"treasury\", \"ops\"); null if unset. */\n role_label: string | null;\n mode: \"test\" | \"live\";\n /** ISO 8601 timestamp. */\n created_at: string;\n};\n\nexport type WalletCreateInput = {\n /** Human-friendly name. 1–60 chars after trimming. */\n display_name: string;\n /** Optional role label (1–40 chars after trimming). */\n role_label?: string;\n};\n\nexport class WalletsResource {\n constructor(private readonly client: TallyClient) {}\n\n /**\n * Lists every wallet in the API key's account + mode. Wallets are\n * returned oldest-first (so the \"Main Wallet\" auto-provisioned at\n * sign-in shows up first).\n */\n async list(): Promise<Wallet[]> {\n const { wallets } = await this.client.request<{ wallets: Wallet[] }>(\n \"GET\",\n \"/v1/wallets\",\n );\n return wallets;\n }\n\n /**\n * Provisions a new Privy server wallet in the API key's account + mode.\n * The wallet is owned (in Privy) by the oldest `owner`-role member of\n * the account, so SDK-created wallets behave identically to dashboard-\n * created ones: the same passkey approves any future signer changes.\n */\n async create(input: WalletCreateInput): Promise<Wallet> {\n const { wallet } = await this.client.request<{ wallet: Wallet }>(\n \"POST\",\n \"/v1/wallets\",\n input,\n );\n return wallet;\n }\n}\n","import { createHmac, timingSafeEqual } from \"node:crypto\";\nimport type { TallyClient } from \"../client\";\n\n// Webhook helpers for SDK consumers. Tally signs every delivery with\n// HMAC-SHA256 over `${timestamp}.${body}` and sends the signature in\n// the `tally-signature` header:\n//\n// tally-signature: t=<unix_seconds>,v1=<hex_lowercase>\n//\n// Receivers MUST verify before trusting any payload — otherwise a\n// known URL is forge-able. Pass the RAW request body (not the\n// JSON-parsed object) so the bytes match what Tally signed.\n//\n// The verifier is intentionally duplicated from the server-side\n// implementation (src/lib/webhook-sign.ts) to keep the SDK\n// self-contained — external consumers shouldn't depend on Tally's\n// server code.\n\nconst SIGNATURE_VERSION = \"v1\";\nexport const DEFAULT_TOLERANCE_SECONDS = 300; // 5 minutes\n\nexport type WebhookVerifyResult =\n | { ok: true }\n | {\n ok: false;\n reason:\n | \"missing_header\"\n | \"malformed_header\"\n | \"no_v1_signature\"\n | \"timestamp_out_of_tolerance\"\n | \"signature_mismatch\";\n };\n\nexport type WebhookVerifyInput = {\n /** Raw request body the receiver got — string, exactly the bytes\n * Tally signed. Don't pass a JSON-parsed object. */\n body: string;\n /** Value of the `tally-signature` header on the incoming request. */\n header: string | null | undefined;\n /** The plaintext signing secret you saved at webhook creation\n * (`whsec_<mode>_<base64url>`). */\n secret: string;\n /** Unix seconds. Defaults to now. Pin for tests. */\n now?: number;\n /** How far the header timestamp may drift from `now`. Defaults to\n * 300s (5 min). Tighten or loosen to taste; the wider the window,\n * the longer a stolen signature stays replayable. */\n toleranceSeconds?: number;\n};\n\nexport type WebhookEventType =\n | \"payment.created\"\n | \"payment.confirmed\"\n | \"payment.failed\"\n | \"inbound.received\"\n | \"permission.granted\"\n | \"permission.updated\"\n | \"permission.revoked\";\n\nexport type Webhook = {\n /** Stable cuid for this endpoint. Pass to `revoke()`. */\n id: string;\n mode: \"test\" | \"live\";\n /** Receiver URL Tally POSTs to. */\n url: string;\n /** Event types this endpoint subscribes to. */\n events: string[];\n /** First few characters of the secret (e.g., `whsec_test_abc…`).\n * Useful for identifying the secret in dashboards without\n * exposing the plaintext. */\n secret_prefix: string;\n created_at: string;\n revoked_at: string | null;\n /** ISO timestamp of the last delivery attempt. null until first send. */\n last_delivery_at: string | null;\n};\n\nexport type CreatedWebhook = Webhook & {\n /** Plaintext signing secret. Returned exactly once at creation.\n * Store it now — it's NOT recoverable later. */\n secret: string;\n};\n\nexport type WebhookCreateInput = {\n /** Receiver URL. Must be https:// (or http://localhost for dev). */\n url: string;\n /** Event types to subscribe to. */\n events: WebhookEventType[];\n};\n\nexport class WebhooksResource {\n /**\n * The TallyClient is optional so legacy zero-arg construction\n * (`new WebhooksResource()`) still works for non-class consumers who\n * only use `verifySignature`. List / create / revoke require a\n * client; calling them on a clientless instance throws.\n */\n constructor(private readonly client?: TallyClient) {}\n\n /**\n * Verify a `tally-signature` header against the request body. Use in\n * your webhook handler before processing the payload.\n *\n * @example\n * ```ts\n * const body = await req.text(); // raw, not JSON-parsed\n * const result = tally.webhooks.verifySignature({\n * body,\n * header: req.headers.get(\"tally-signature\"),\n * secret: process.env.TALLY_WEBHOOK_SECRET!,\n * });\n * if (!result.ok) return new Response(\"invalid signature\", { status: 400 });\n * const event = JSON.parse(body);\n * // ... handle event\n * ```\n */\n verifySignature(input: WebhookVerifyInput): WebhookVerifyResult {\n return verifySignature(input);\n }\n\n private requireClient(): TallyClient {\n if (!this.client) {\n throw new Error(\n \"WebhooksResource was constructed without a TallyClient. Use `new Tally({ apiKey })` to enable list/create/revoke.\",\n );\n }\n return this.client;\n }\n\n /**\n * Lists every webhook endpoint in the API key's account + mode.\n * Revoked endpoints are included (with `revoked_at` set) for audit.\n *\n * ```ts\n * for (const w of await tally.webhooks.list()) {\n * console.log(`${w.url} → ${w.events.join(\",\")}`);\n * }\n * ```\n */\n async list(): Promise<Webhook[]> {\n const { webhooks } = await this.requireClient().request<{\n webhooks: Webhook[];\n }>(\"GET\", \"/v1/webhooks\");\n return webhooks;\n }\n\n /**\n * Creates a new webhook endpoint. The signing secret is returned in\n * `webhook.secret` **exactly once** — store it now; it's not\n * recoverable later. If you lose it, revoke and recreate.\n *\n * ```ts\n * const webhook = await tally.webhooks.create({\n * url: \"https://example.com/tally/events\",\n * events: [\"payment.confirmed\", \"payment.failed\"],\n * });\n * console.log(webhook.secret); // shown once\n * ```\n */\n async create(input: WebhookCreateInput): Promise<CreatedWebhook> {\n const { webhook } = await this.requireClient().request<{\n webhook: CreatedWebhook;\n }>(\"POST\", \"/v1/webhooks\", input);\n return webhook;\n }\n\n /**\n * Revokes a webhook endpoint by id. No further deliveries will be\n * enqueued; deliveries already in-flight are not cancelled.\n * Idempotent: revoking an already-revoked endpoint is a no-op.\n *\n * ```ts\n * await tally.webhooks.revoke(\"whk_01HXYZ...\");\n * ```\n */\n async revoke(id: string): Promise<Webhook> {\n const { webhook } = await this.requireClient().request<{\n webhook: Webhook;\n }>(\"POST\", `/v1/webhooks/${encodeURIComponent(id)}/revoke`);\n return webhook;\n }\n}\n\nexport function verifySignature(input: WebhookVerifyInput): WebhookVerifyResult {\n const { body, header, secret } = input;\n const now = input.now ?? Math.floor(Date.now() / 1000);\n const tolerance = input.toleranceSeconds ?? DEFAULT_TOLERANCE_SECONDS;\n\n if (!header) return { ok: false, reason: \"missing_header\" };\n\n let timestamp: number | null = null;\n let v1: string | null = null;\n for (const part of header.split(\",\")) {\n const [k, v] = part.split(\"=\", 2);\n if (k === \"t\") {\n const n = Number(v);\n if (Number.isFinite(n)) timestamp = n;\n } else if (k === SIGNATURE_VERSION) {\n v1 = v;\n }\n }\n\n if (timestamp === null) return { ok: false, reason: \"malformed_header\" };\n if (!v1) return { ok: false, reason: \"no_v1_signature\" };\n\n if (Math.abs(now - timestamp) > tolerance) {\n return { ok: false, reason: \"timestamp_out_of_tolerance\" };\n }\n\n const expected = createHmac(\"sha256\", secret)\n .update(`${timestamp}.${body}`)\n .digest(\"hex\");\n\n if (expected.length !== v1.length) {\n return { ok: false, reason: \"signature_mismatch\" };\n }\n const a = Buffer.from(expected, \"utf8\");\n const b = Buffer.from(v1, \"utf8\");\n if (!timingSafeEqual(a, b)) {\n return { ok: false, reason: \"signature_mismatch\" };\n }\n return { ok: true };\n}\n","// @tallyforagents/sdk — the financial OS for AI agents.\n//\n// Usage:\n// import { Tally } from \"@tallyforagents/sdk\";\n//\n// const tally = new Tally({ apiKey: process.env.TALLY_API_KEY! });\n// await tally.agents.upsert({ id: \"research-bot\", display_name: \"Research Bot\" });\n\nimport { TallyClient, type ClientOptions } from \"./client\";\nimport { AgentsResource } from \"./resources/agents\";\nimport { PaymentsResource } from \"./resources/payments\";\nimport { PermissionsResource } from \"./resources/permissions\";\nimport { WalletsResource } from \"./resources/wallets\";\nimport { WebhooksResource } from \"./resources/webhooks\";\n\nexport class Tally {\n readonly agents: AgentsResource;\n readonly payments: PaymentsResource;\n readonly permissions: PermissionsResource;\n readonly wallets: WalletsResource;\n readonly webhooks: WebhooksResource;\n\n // Expose the underlying client for advanced use (custom retries, etc.).\n // Internal callers go through the resource classes instead.\n private readonly client: TallyClient;\n\n constructor(opts: ClientOptions) {\n this.client = new TallyClient(opts);\n this.agents = new AgentsResource(this.client);\n this.payments = new PaymentsResource(this.client);\n this.permissions = new PermissionsResource(this.client);\n this.wallets = new WalletsResource(this.client);\n this.webhooks = new WebhooksResource(this.client);\n }\n}\n\nexport type { ClientOptions } from \"./client\";\nexport type { Agent, AgentUpsertInput, AgentWallet } from \"./resources/agents\";\nexport type {\n Payment,\n PaymentCreateInput,\n PaymentListFilters,\n} from \"./resources/payments\";\nexport { AsyncResourcePage } from \"./pagination\";\nexport type { Wallet, WalletCreateInput } from \"./resources/wallets\";\nexport type {\n Permission,\n PermissionListFilters,\n} from \"./resources/permissions\";\nexport type {\n Webhook,\n CreatedWebhook,\n WebhookCreateInput,\n WebhookEventType,\n WebhookVerifyInput,\n WebhookVerifyResult,\n} from \"./resources/webhooks\";\nexport { verifySignature } from \"./resources/webhooks\";\nexport {\n TallyError,\n AuthenticationError,\n NotFoundError,\n ValidationError,\n RateLimitError,\n ConflictError,\n} from \"./errors\";\n"]}