@absol-labs/agent 0.5.0 → 0.7.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.
Files changed (76) hide show
  1. package/README.md +49 -10
  2. package/dist/discovery/registry.d.ts +260 -15
  3. package/dist/discovery/registry.d.ts.map +1 -1
  4. package/dist/discovery/registry.js +174 -31
  5. package/dist/discovery/registry.js.map +1 -1
  6. package/dist/frameworks/agentkit.js +1 -1
  7. package/dist/frameworks/agentkit.js.map +1 -1
  8. package/dist/frameworks/crewai.js +1 -1
  9. package/dist/frameworks/crewai.js.map +1 -1
  10. package/dist/gateway/caller-auth-gateway.d.ts +3 -1
  11. package/dist/gateway/caller-auth-gateway.d.ts.map +1 -1
  12. package/dist/gateway/caller-auth-gateway.js +9 -7
  13. package/dist/gateway/caller-auth-gateway.js.map +1 -1
  14. package/dist/gateway/http-server.d.ts +3 -1
  15. package/dist/gateway/http-server.d.ts.map +1 -1
  16. package/dist/gateway/http-server.js +15 -1
  17. package/dist/gateway/http-server.js.map +1 -1
  18. package/dist/index.d.ts +8 -3
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +8 -3
  21. package/dist/index.js.map +1 -1
  22. package/dist/mcp/server.js +2 -0
  23. package/dist/mcp/server.js.map +1 -1
  24. package/dist/sdk/invoke.d.ts +16 -2
  25. package/dist/sdk/invoke.d.ts.map +1 -1
  26. package/dist/sdk/invoke.js +117 -1
  27. package/dist/sdk/invoke.js.map +1 -1
  28. package/dist/wallet/autonomous-wallet-broker.d.ts +97 -0
  29. package/dist/wallet/autonomous-wallet-broker.d.ts.map +1 -0
  30. package/dist/wallet/autonomous-wallet-broker.js +468 -0
  31. package/dist/wallet/autonomous-wallet-broker.js.map +1 -0
  32. package/dist/wallet/autonomous-wallet-protocol.d.ts +56 -0
  33. package/dist/wallet/autonomous-wallet-protocol.d.ts.map +1 -0
  34. package/dist/wallet/autonomous-wallet-protocol.js +15 -0
  35. package/dist/wallet/autonomous-wallet-protocol.js.map +1 -0
  36. package/dist/wallet/autonomous-wallet.d.ts +106 -0
  37. package/dist/wallet/autonomous-wallet.d.ts.map +1 -0
  38. package/dist/wallet/autonomous-wallet.js +494 -0
  39. package/dist/wallet/autonomous-wallet.js.map +1 -0
  40. package/dist/wallet/privy-broker-server-entry.d.ts +2 -0
  41. package/dist/wallet/privy-broker-server-entry.d.ts.map +1 -0
  42. package/dist/wallet/privy-broker-server-entry.js +8 -0
  43. package/dist/wallet/privy-broker-server-entry.js.map +1 -0
  44. package/dist/wallet/privy-broker-server.d.ts +29 -0
  45. package/dist/wallet/privy-broker-server.d.ts.map +1 -0
  46. package/dist/wallet/privy-broker-server.js +480 -0
  47. package/dist/wallet/privy-broker-server.js.map +1 -0
  48. package/dist/wallet/privy-session-broker.d.ts +109 -0
  49. package/dist/wallet/privy-session-broker.d.ts.map +1 -0
  50. package/dist/wallet/privy-session-broker.js +372 -0
  51. package/dist/wallet/privy-session-broker.js.map +1 -0
  52. package/dist/wallet/privy-session-provider.d.ts +21 -0
  53. package/dist/wallet/privy-session-provider.d.ts.map +1 -0
  54. package/dist/wallet/privy-session-provider.js +94 -0
  55. package/dist/wallet/privy-session-provider.js.map +1 -0
  56. package/dist/wallet/provider.d.ts +51 -2
  57. package/dist/wallet/provider.d.ts.map +1 -1
  58. package/dist/wallet/provider.js +138 -1
  59. package/dist/wallet/provider.js.map +1 -1
  60. package/package.json +4 -2
  61. package/src/discovery/registry.ts +213 -35
  62. package/src/frameworks/agentkit.ts +1 -1
  63. package/src/frameworks/crewai.ts +1 -1
  64. package/src/gateway/caller-auth-gateway.ts +13 -9
  65. package/src/gateway/http-server.ts +18 -1
  66. package/src/index.ts +78 -0
  67. package/src/mcp/server.ts +2 -0
  68. package/src/sdk/invoke.ts +186 -4
  69. package/src/wallet/autonomous-wallet-broker.ts +764 -0
  70. package/src/wallet/autonomous-wallet-protocol.ts +71 -0
  71. package/src/wallet/autonomous-wallet.ts +779 -0
  72. package/src/wallet/privy-broker-server-entry.ts +9 -0
  73. package/src/wallet/privy-broker-server.ts +573 -0
  74. package/src/wallet/privy-session-broker.ts +634 -0
  75. package/src/wallet/privy-session-provider.ts +129 -0
  76. package/src/wallet/provider.ts +260 -3
@@ -0,0 +1,129 @@
1
+ import { getAddress, type Address, type Hex } from "viem";
2
+ import { z } from "zod";
3
+
4
+ import type { PrivyEip1193Provider } from "./provider.js";
5
+
6
+ const jsonRpcResponseSchema = z.object({
7
+ jsonrpc: z.literal("2.0"),
8
+ id: z.union([z.string(), z.number(), z.null()]),
9
+ result: z.unknown().optional(),
10
+ error: z
11
+ .object({
12
+ code: z.number(),
13
+ message: z.string(),
14
+ data: z.unknown().optional(),
15
+ })
16
+ .optional(),
17
+ });
18
+
19
+ export interface PrivySessionProviderConfig {
20
+ readonly brokerUrl: string;
21
+ readonly sessionToken: string;
22
+ readonly address: Address;
23
+ readonly rpcUrl?: string;
24
+ readonly fetchImpl?: typeof fetch;
25
+ }
26
+
27
+ export class PrivySessionProviderError extends Error {
28
+ constructor(message: string, options?: { readonly cause?: unknown }) {
29
+ super(message, options);
30
+ this.name = "PrivySessionProviderError";
31
+ }
32
+ }
33
+
34
+ /**
35
+ * EIP-1193 provider for a policy-bound Metrik/Privy headless session.
36
+ * Signing and writes go only to the broker; public read RPCs go directly to
37
+ * Base Sepolia and never receive the session token.
38
+ */
39
+ export function createPrivySessionProvider(
40
+ config: PrivySessionProviderConfig,
41
+ ): PrivyEip1193Provider {
42
+ const fetchImpl = config.fetchImpl ?? fetch;
43
+ const address = getAddress(config.address);
44
+ const brokerUrl = new URL("/v1/rpc", requireHttpsBase(config.brokerUrl));
45
+ const rpcUrl = config.rpcUrl ?? "https://base-sepolia-rpc.publicnode.com";
46
+ let rpcId = 0;
47
+
48
+ return {
49
+ async request({ method, params = [] }) {
50
+ if (method === "eth_chainId") return "0x14a34";
51
+ if (method === "eth_accounts" || method === "eth_requestAccounts") {
52
+ return [address];
53
+ }
54
+ if (
55
+ method === "eth_sendTransaction" ||
56
+ method === "eth_signTypedData_v4"
57
+ ) {
58
+ const response = await fetchImpl(brokerUrl, {
59
+ method: "POST",
60
+ headers: {
61
+ authorization: `Bearer ${config.sessionToken}`,
62
+ "content-type": "application/json",
63
+ },
64
+ body: JSON.stringify({ method, params }, bigintJson),
65
+ }).catch((error: unknown) => {
66
+ throw new PrivySessionProviderError("session broker is unreachable", {
67
+ cause: error,
68
+ });
69
+ });
70
+ const body = (await response.json().catch(() => null)) as {
71
+ result?: Hex;
72
+ error?: string;
73
+ } | null;
74
+ if (!response.ok || body?.result === undefined) {
75
+ throw new PrivySessionProviderError(
76
+ body?.error ??
77
+ `session broker rejected ${method} (${response.status})`,
78
+ );
79
+ }
80
+ return body.result;
81
+ }
82
+ if (
83
+ method === "personal_sign" ||
84
+ method === "eth_sign" ||
85
+ method === "eth_signTransaction" ||
86
+ method === "wallet_sendCalls"
87
+ ) {
88
+ throw new PrivySessionProviderError(
89
+ `${method} is outside the Metrik agent-session policy`,
90
+ );
91
+ }
92
+
93
+ const id = ++rpcId;
94
+ const response = await fetchImpl(rpcUrl, {
95
+ method: "POST",
96
+ headers: { "content-type": "application/json" },
97
+ body: JSON.stringify(
98
+ { jsonrpc: "2.0", id, method, params },
99
+ bigintJson,
100
+ ),
101
+ }).catch((error: unknown) => {
102
+ throw new PrivySessionProviderError("Base Sepolia RPC is unreachable", {
103
+ cause: error,
104
+ });
105
+ });
106
+ const body = jsonRpcResponseSchema.parse(await response.json());
107
+ if (!response.ok || body.error !== undefined) {
108
+ throw new PrivySessionProviderError(
109
+ body.error?.message ?? `Base Sepolia RPC rejected ${method}`,
110
+ );
111
+ }
112
+ return body.result;
113
+ },
114
+ };
115
+ }
116
+
117
+ function requireHttpsBase(value: string): URL {
118
+ const url = new URL(value);
119
+ if (url.protocol !== "https:" && url.hostname !== "localhost") {
120
+ throw new PrivySessionProviderError(
121
+ "session broker URL must use HTTPS (except localhost development)",
122
+ );
123
+ }
124
+ return url;
125
+ }
126
+
127
+ function bigintJson(_key: string, value: unknown) {
128
+ return typeof value === "bigint" ? `0x${value.toString(16)}` : value;
129
+ }
@@ -4,8 +4,19 @@ import {
4
4
  type EvmServerAccount,
5
5
  type EvmSmartAccount,
6
6
  } from "@coinbase/cdp-sdk";
7
- import type { Account } from "viem";
8
- import { privateKeyToAccount, toAccount } from "viem/accounts";
7
+ import {
8
+ custom,
9
+ toHex,
10
+ serializeTypedData,
11
+ type Account,
12
+ type Address,
13
+ type Hex,
14
+ type SignableMessage,
15
+ type TypedData,
16
+ type TypedDataDefinition,
17
+ isAddress,
18
+ } from "viem";
19
+ import { parseAccount, privateKeyToAccount, toAccount } from "viem/accounts";
9
20
  import { z } from "zod";
10
21
 
11
22
  import {
@@ -38,6 +49,51 @@ export class AgentWalletConfigError extends Error {
38
49
  }
39
50
  }
40
51
 
52
+ /** A deliberately small EIP-1193 seam implemented by an authenticated Privy session. */
53
+ export interface PrivyEip1193Provider {
54
+ request(args: {
55
+ readonly method: string;
56
+ readonly params?: readonly unknown[];
57
+ }): Promise<unknown>;
58
+ }
59
+
60
+ /**
61
+ * Configuration for a user-owned Privy embedded EOA.
62
+ *
63
+ * The browser/site owns authentication and obtains the provider from Privy's
64
+ * authenticated wallet. This package does not import Privy's UI SDK, accept a
65
+ * secret, persist a wallet, or create a wallet from an app id alone.
66
+ */
67
+ export interface PrivyEmbeddedWalletConfig {
68
+ /** Public Privy app id; never an app secret. */
69
+ readonly appId: string;
70
+ /** Address returned by the authenticated user wallet. */
71
+ readonly address: Address;
72
+ /** Authenticated Privy wallet provider (EIP-1193). */
73
+ readonly provider: PrivyEip1193Provider;
74
+ /** Metrik currently supports Base Sepolia only. */
75
+ readonly chainId?: number;
76
+ }
77
+
78
+ export class PrivyEmbeddedWalletError extends Error {
79
+ readonly code:
80
+ | "invalid-config"
81
+ | "provider-error"
82
+ | "chain-mismatch"
83
+ | "account-mismatch"
84
+ | "invalid-signature";
85
+
86
+ constructor(
87
+ code: PrivyEmbeddedWalletError["code"],
88
+ message: string,
89
+ options?: { readonly cause?: unknown },
90
+ ) {
91
+ super(message, options);
92
+ this.name = "PrivyEmbeddedWalletError";
93
+ this.code = code;
94
+ }
95
+ }
96
+
41
97
  export interface CdpWalletConfig {
42
98
  readonly apiKeyId?: string;
43
99
  readonly apiKeySecret?: string;
@@ -53,13 +109,20 @@ export type AgentWalletInput =
53
109
  }
54
110
  | {
55
111
  readonly cdp: CdpWalletConfig;
112
+ }
113
+ | {
114
+ readonly privy: PrivyEmbeddedWalletConfig;
56
115
  };
57
116
 
58
117
  export interface ResolvedAgentWallet {
59
- readonly source: "injected" | "cdp";
118
+ readonly source: "injected" | "cdp" | "privy";
60
119
  readonly account: Account;
61
120
  readonly cdpOwner?: EvmServerAccount;
62
121
  readonly smartAccount?: EvmSmartAccount;
122
+ readonly privy?: {
123
+ readonly appId: string;
124
+ readonly chainId: number;
125
+ };
63
126
  }
64
127
 
65
128
  export interface CdpClientLike {
@@ -98,6 +161,16 @@ export async function resolveAgentWallet(
98
161
  };
99
162
  }
100
163
 
164
+ if ("privy" in input) {
165
+ const chainId = input.privy.chainId ?? BASE_SEPOLIA_CHAIN_ID;
166
+ const account = await createPrivyEmbeddedAccount(input.privy);
167
+ return {
168
+ source: "privy",
169
+ account,
170
+ privy: { appId: input.privy.appId, chainId },
171
+ };
172
+ }
173
+
101
174
  const cdpClient = (options.createCdpClient ?? defaultCdpClientFactory)(
102
175
  input.cdp,
103
176
  );
@@ -119,6 +192,62 @@ export async function resolveAgentWallet(
119
192
  };
120
193
  }
121
194
 
195
+ /**
196
+ * Adapts an authenticated Privy EIP-1193 wallet to a viem JSON-RPC Account.
197
+ * It deliberately remains a JSON-RPC account so viem sends transactions via
198
+ * Privy's documented `eth_sendTransaction` boundary instead of assuming that
199
+ * the embedded provider exposes raw transaction signing. Every signing
200
+ * operation rechecks chain and account identity so a
201
+ * browser account/network switch cannot silently redirect a spend.
202
+ *
203
+ * This is an EOA adapter only. It does not implement EIP-1271, smart accounts,
204
+ * session-key issuance, or gas sponsorship; those require provider/site or
205
+ * backend integration and must be wired explicitly by the caller.
206
+ */
207
+ export async function createPrivyEmbeddedAccount(
208
+ config: PrivyEmbeddedWalletConfig,
209
+ ): Promise<Account> {
210
+ validatePrivyConfig(config);
211
+ const chainId = config.chainId ?? BASE_SEPOLIA_CHAIN_ID;
212
+ await assertPrivySession(config, chainId);
213
+
214
+ const account = {
215
+ ...parseAccount(config.address),
216
+ signMessage: async ({ message }: { message: SignableMessage }) => {
217
+ await assertPrivySession(config, chainId);
218
+ const raw =
219
+ typeof message === "string"
220
+ ? toHex(message)
221
+ : message.raw instanceof Uint8Array
222
+ ? toHex(message.raw)
223
+ : message.raw;
224
+ return requireSignature(
225
+ await requestPrivy(config.provider, "personal_sign", [
226
+ raw,
227
+ config.address,
228
+ ]),
229
+ );
230
+ },
231
+ signTypedData: async <
232
+ const typedData extends TypedData | Record<string, unknown>,
233
+ primaryType extends keyof typedData | "EIP712Domain" = keyof typedData,
234
+ >(
235
+ parameters: TypedDataDefinition<typedData, primaryType>,
236
+ ) => {
237
+ await assertPrivySession(config, chainId);
238
+ const serialized = serializeTypedData(parameters);
239
+ return requireSignature(
240
+ await requestPrivy(config.provider, "eth_signTypedData_v4", [
241
+ config.address,
242
+ serialized,
243
+ ]),
244
+ );
245
+ },
246
+ } as Account;
247
+
248
+ return account;
249
+ }
250
+
122
251
  export async function createWalletBackedAgentClient(
123
252
  sdkConfig: StreamProofClientConfig,
124
253
  walletInput: AgentWalletInput,
@@ -129,6 +258,9 @@ export async function createWalletBackedAgentClient(
129
258
  {
130
259
  ...sdkConfig,
131
260
  account: wallet.account,
261
+ ...("privy" in walletInput
262
+ ? { transport: custom(createCheckedPrivyProvider(walletInput.privy)) }
263
+ : {}),
132
264
  },
133
265
  {
134
266
  ...(options.createSdkClient === undefined
@@ -195,3 +327,128 @@ function defaultCdpClientFactory(config: CdpWalletConfig): CdpClientLike {
195
327
  : { walletSecret: config.walletSecret }),
196
328
  });
197
329
  }
330
+
331
+ const BASE_SEPOLIA_CHAIN_ID = 84532;
332
+
333
+ function validatePrivyConfig(config: PrivyEmbeddedWalletConfig): void {
334
+ if (config.appId.trim() === "") {
335
+ throw new PrivyEmbeddedWalletError(
336
+ "invalid-config",
337
+ "Privy public app id is required; an app secret is never accepted by this adapter",
338
+ );
339
+ }
340
+ if (!isAddress(config.address)) {
341
+ throw new PrivyEmbeddedWalletError(
342
+ "invalid-config",
343
+ "Privy wallet address is invalid",
344
+ );
345
+ }
346
+ if (
347
+ config.chainId !== undefined &&
348
+ config.chainId !== BASE_SEPOLIA_CHAIN_ID
349
+ ) {
350
+ throw new PrivyEmbeddedWalletError(
351
+ "invalid-config",
352
+ `Metrik Privy wallets support Base Sepolia chain ID ${BASE_SEPOLIA_CHAIN_ID} only`,
353
+ );
354
+ }
355
+ }
356
+
357
+ async function assertPrivySession(
358
+ config: PrivyEmbeddedWalletConfig,
359
+ expectedChainId: number,
360
+ ): Promise<void> {
361
+ let chainResult: unknown;
362
+ let accountsResult: unknown;
363
+ try {
364
+ [chainResult, accountsResult] = await Promise.all([
365
+ config.provider.request({ method: "eth_chainId" }),
366
+ config.provider.request({ method: "eth_accounts" }),
367
+ ]);
368
+ } catch (error) {
369
+ throw new PrivyEmbeddedWalletError(
370
+ "provider-error",
371
+ "Privy wallet provider rejected the session check",
372
+ { cause: error },
373
+ );
374
+ }
375
+
376
+ const chainId = parseChainId(chainResult);
377
+ if (chainId !== expectedChainId) {
378
+ throw new PrivyEmbeddedWalletError(
379
+ "chain-mismatch",
380
+ `Privy wallet chain ID ${chainId} is not Base Sepolia (${expectedChainId})`,
381
+ );
382
+ }
383
+ if (
384
+ !Array.isArray(accountsResult) ||
385
+ accountsResult.length === 0 ||
386
+ typeof accountsResult[0] !== "string" ||
387
+ accountsResult[0].toLowerCase() !== config.address.toLowerCase()
388
+ ) {
389
+ throw new PrivyEmbeddedWalletError(
390
+ "account-mismatch",
391
+ "Privy provider account does not match configured address",
392
+ );
393
+ }
394
+ }
395
+
396
+ async function requestPrivy(
397
+ provider: PrivyEip1193Provider,
398
+ method: string,
399
+ params: readonly unknown[],
400
+ ): Promise<unknown> {
401
+ try {
402
+ return await provider.request({ method, params });
403
+ } catch (error) {
404
+ throw new PrivyEmbeddedWalletError(
405
+ "provider-error",
406
+ `Privy wallet provider rejected ${method}`,
407
+ { cause: error },
408
+ );
409
+ }
410
+ }
411
+
412
+ function createCheckedPrivyProvider(
413
+ config: PrivyEmbeddedWalletConfig,
414
+ ): PrivyEip1193Provider {
415
+ const expectedChainId = config.chainId ?? BASE_SEPOLIA_CHAIN_ID;
416
+ return {
417
+ request: async (args) => {
418
+ if (args.method === "eth_sendTransaction") {
419
+ await assertPrivySession(config, expectedChainId);
420
+ }
421
+ try {
422
+ return await config.provider.request(args);
423
+ } catch (error) {
424
+ throw new PrivyEmbeddedWalletError(
425
+ "provider-error",
426
+ `Privy wallet provider rejected ${args.method}`,
427
+ { cause: error },
428
+ );
429
+ }
430
+ },
431
+ };
432
+ }
433
+
434
+ function parseChainId(value: unknown): number {
435
+ if (typeof value === "number" && Number.isSafeInteger(value) && value > 0)
436
+ return value;
437
+ if (typeof value === "string" && /^0x[0-9a-fA-F]+$/.test(value)) {
438
+ const parsed = Number.parseInt(value.slice(2), 16);
439
+ if (Number.isSafeInteger(parsed) && parsed > 0) return parsed;
440
+ }
441
+ throw new PrivyEmbeddedWalletError(
442
+ "provider-error",
443
+ "Privy wallet provider returned an invalid chain ID",
444
+ );
445
+ }
446
+
447
+ function requireSignature(value: unknown): Hex {
448
+ if (typeof value === "string" && /^0x[0-9a-fA-F]+$/.test(value))
449
+ return value as Hex;
450
+ throw new PrivyEmbeddedWalletError(
451
+ "invalid-signature",
452
+ "Privy wallet provider returned a non-hex signature",
453
+ );
454
+ }