@absol-labs/agent 0.8.0 → 0.9.1
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/discovery/registry.d.ts +110 -305
- package/dist/discovery/registry.d.ts.map +1 -1
- package/dist/discovery/registry.js +141 -318
- package/dist/discovery/registry.js.map +1 -1
- package/dist/frameworks/agentkit.d.ts.map +1 -1
- package/dist/frameworks/agentkit.js +31 -10
- package/dist/frameworks/agentkit.js.map +1 -1
- package/dist/frameworks/eliza.d.ts.map +1 -1
- package/dist/frameworks/eliza.js +14 -3
- package/dist/frameworks/eliza.js.map +1 -1
- package/dist/frameworks/langchain.d.ts.map +1 -1
- package/dist/frameworks/langchain.js +14 -3
- package/dist/frameworks/langchain.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +26 -5
- package/dist/mcp/server.js.map +1 -1
- package/dist/sdk/client.d.ts +19 -2
- package/dist/sdk/client.d.ts.map +1 -1
- package/dist/sdk/client.js +18 -2
- package/dist/sdk/client.js.map +1 -1
- package/dist/wallet/cdp-sdk.d.ts +23 -0
- package/dist/wallet/cdp-sdk.d.ts.map +1 -0
- package/dist/wallet/cdp-sdk.js +27 -0
- package/dist/wallet/cdp-sdk.js.map +1 -0
- package/dist/wallet/provider.d.ts +1 -1
- package/dist/wallet/provider.d.ts.map +1 -1
- package/dist/wallet/provider.js +8 -3
- package/dist/wallet/provider.js.map +1 -1
- package/dist/zktls/reclaim-js-sdk.d.ts +24 -0
- package/dist/zktls/reclaim-js-sdk.d.ts.map +1 -0
- package/dist/zktls/reclaim-js-sdk.js +29 -0
- package/dist/zktls/reclaim-js-sdk.js.map +1 -0
- package/dist/zktls/reclaim.d.ts +14 -2
- package/dist/zktls/reclaim.d.ts.map +1 -1
- package/dist/zktls/reclaim.js +29 -6
- package/dist/zktls/reclaim.js.map +1 -1
- package/dist/zktls/t2-delivery-proof.d.ts +8 -1
- package/dist/zktls/t2-delivery-proof.d.ts.map +1 -1
- package/dist/zktls/t2-delivery-proof.js +22 -6
- package/dist/zktls/t2-delivery-proof.js.map +1 -1
- package/docs/agent-layer.md +150 -0
- package/docs/autonomous-privy-wallet.md +133 -0
- package/docs/crewai.md +70 -0
- package/docs/eliza.md +109 -0
- package/docs/langchain.md +63 -0
- package/docs/mcp-hosted.md +137 -0
- package/docs/privy-embedded-wallet.md +102 -0
- package/docs/quickstart.md +370 -0
- package/docs/threat-model.md +160 -0
- package/package.json +19 -6
- package/src/discovery/registry.ts +242 -414
- package/src/frameworks/agentkit.ts +32 -8
- package/src/frameworks/eliza.ts +14 -3
- package/src/frameworks/langchain.ts +14 -3
- package/src/index.ts +7 -0
- package/src/mcp/server.ts +26 -5
- package/src/sdk/client.ts +38 -3
- package/src/wallet/cdp-sdk.ts +33 -0
- package/src/wallet/provider.ts +16 -9
- package/src/zktls/reclaim-js-sdk.ts +50 -0
- package/src/zktls/reclaim.ts +57 -23
- package/src/zktls/t2-delivery-proof.ts +28 -10
|
@@ -14,12 +14,14 @@ import { isAddress, isHex } from "viem";
|
|
|
14
14
|
import { z } from "zod";
|
|
15
15
|
|
|
16
16
|
import {
|
|
17
|
+
RegistryUnavailableError,
|
|
17
18
|
discoverServices as defaultDiscoverServices,
|
|
18
19
|
type DiscoverServicesOptions,
|
|
19
20
|
type ServiceListing,
|
|
20
21
|
} from "../discovery/registry.js";
|
|
21
22
|
import { checkMandate, type SignedSpendMandate } from "../mandates/mandate.js";
|
|
22
23
|
import {
|
|
24
|
+
isCheckpointStream,
|
|
23
25
|
MandateDeniedError,
|
|
24
26
|
VerifiedStreamAgentClient,
|
|
25
27
|
type AgentSdkClientFactory,
|
|
@@ -281,12 +283,30 @@ export class MetrikActionProvider extends ActionProvider<WalletProvider> {
|
|
|
281
283
|
_walletProvider: WalletProvider,
|
|
282
284
|
args: z.infer<typeof discoverServicesSchema>,
|
|
283
285
|
): Promise<string> {
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
286
|
+
let listings: ServiceListing[];
|
|
287
|
+
try {
|
|
288
|
+
listings = await this.#discover({
|
|
289
|
+
...(args.category === undefined ? {} : { category: args.category }),
|
|
290
|
+
...(args.limit === undefined ? {} : { limit: args.limit }),
|
|
291
|
+
});
|
|
292
|
+
} catch (error) {
|
|
293
|
+
// Discovery throws when the registry could not be READ at all. Every other
|
|
294
|
+
// action here returns a string, and more importantly an agent must be able to
|
|
295
|
+
// tell "the marketplace was never reached" from "the marketplace is empty" —
|
|
296
|
+
// collapsing them is how an agent concludes there is nothing to hire and gives
|
|
297
|
+
// up on a live marketplace. So report the failure as a failure, explicitly.
|
|
298
|
+
if (error instanceof RegistryUnavailableError) {
|
|
299
|
+
return (
|
|
300
|
+
"Could not reach the Metrik marketplace registry, so the set of available " +
|
|
301
|
+
"services is UNKNOWN — this is not the same as there being no services. " +
|
|
302
|
+
`Do not conclude nothing is hireable. Reason: ${error.message}`
|
|
303
|
+
);
|
|
304
|
+
}
|
|
305
|
+
throw error;
|
|
306
|
+
}
|
|
288
307
|
if (listings.length === 0) {
|
|
289
|
-
|
|
308
|
+
// Reachable and genuinely empty, or everything filtered out — a real answer.
|
|
309
|
+
return "No verified Metrik services discovered (the registry was reachable and returned no listing that passed verification).";
|
|
290
310
|
}
|
|
291
311
|
const lines = listings.map(
|
|
292
312
|
(listing) =>
|
|
@@ -381,9 +401,13 @@ export class MetrikActionProvider extends ActionProvider<WalletProvider> {
|
|
|
381
401
|
|
|
382
402
|
return (
|
|
383
403
|
`Stream ${streamId} is ${stream.status}${paused ? " (payment halted)" : ""}. ` +
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
404
|
+
(isCheckpointStream(stream)
|
|
405
|
+
? `Verified entitlement: ${formatUsdc(stream.settledCumulative)} USDC of ` +
|
|
406
|
+
`${formatUsdc(stream.deposit)} deposited, of which ` +
|
|
407
|
+
`${formatUsdc(stream.claimedCumulative)} USDC has been claimed. `
|
|
408
|
+
: `Verified accrued: ${formatUsdc(stream.accrued)} USDC of ` +
|
|
409
|
+
`${formatUsdc(stream.deposit)} deposited. `) +
|
|
410
|
+
`Claimable by seller: ${formatUsdc(claimable)} USDC. Reclaimable by buyer: ${formatUsdc(reclaimable)} USDC.`
|
|
387
411
|
);
|
|
388
412
|
}
|
|
389
413
|
|
package/src/frameworks/eliza.ts
CHANGED
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
} from "../discovery/registry.js";
|
|
16
16
|
import { checkMandate, type SignedSpendMandate } from "../mandates/mandate.js";
|
|
17
17
|
import {
|
|
18
|
+
isCheckpointStream,
|
|
18
19
|
type MandateAuthorizedStreamActionInput,
|
|
19
20
|
type ReclaimAuthorizedStreamInput,
|
|
20
21
|
type ReclaimVerifiedStreamResult,
|
|
@@ -733,9 +734,19 @@ function serializeStreamStatus(
|
|
|
733
734
|
...result.stream,
|
|
734
735
|
deposit: result.stream.deposit.toString(),
|
|
735
736
|
ratePerSecond: result.stream.ratePerSecond.toString(),
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
737
|
+
// The two escrow generations report entitlement differently; emit the
|
|
738
|
+
// vocabulary of whichever one answered rather than inventing a shared one.
|
|
739
|
+
...(isCheckpointStream(result.stream)
|
|
740
|
+
? {
|
|
741
|
+
settledCumulative: result.stream.settledCumulative.toString(),
|
|
742
|
+
claimedCumulative: result.stream.claimedCumulative.toString(),
|
|
743
|
+
feesPaid: result.stream.feesPaid.toString(),
|
|
744
|
+
}
|
|
745
|
+
: {
|
|
746
|
+
accrued: result.stream.accrued.toString(),
|
|
747
|
+
claimed: result.stream.claimed.toString(),
|
|
748
|
+
lastSequence: result.stream.lastSequence.toString(),
|
|
749
|
+
}),
|
|
739
750
|
},
|
|
740
751
|
claimable: result.claimable.toString(),
|
|
741
752
|
reclaimable: result.reclaimable.toString(),
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
} from "../discovery/registry.js";
|
|
10
10
|
import { checkMandate, type SignedSpendMandate } from "../mandates/mandate.js";
|
|
11
11
|
import {
|
|
12
|
+
isCheckpointStream,
|
|
12
13
|
type MandateAuthorizedStreamActionInput,
|
|
13
14
|
type ReclaimAuthorizedStreamInput,
|
|
14
15
|
type ReclaimVerifiedStreamResult,
|
|
@@ -332,9 +333,19 @@ function serializeStreamStatus(
|
|
|
332
333
|
...result.stream,
|
|
333
334
|
deposit: result.stream.deposit.toString(),
|
|
334
335
|
ratePerSecond: result.stream.ratePerSecond.toString(),
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
336
|
+
// The two escrow generations report entitlement differently; emit the
|
|
337
|
+
// vocabulary of whichever one answered rather than inventing a shared one.
|
|
338
|
+
...(isCheckpointStream(result.stream)
|
|
339
|
+
? {
|
|
340
|
+
settledCumulative: result.stream.settledCumulative.toString(),
|
|
341
|
+
claimedCumulative: result.stream.claimedCumulative.toString(),
|
|
342
|
+
feesPaid: result.stream.feesPaid.toString(),
|
|
343
|
+
}
|
|
344
|
+
: {
|
|
345
|
+
accrued: result.stream.accrued.toString(),
|
|
346
|
+
claimed: result.stream.claimed.toString(),
|
|
347
|
+
lastSequence: result.stream.lastSequence.toString(),
|
|
348
|
+
}),
|
|
338
349
|
},
|
|
339
350
|
claimable: result.claimable.toString(),
|
|
340
351
|
reclaimable: result.reclaimable.toString(),
|
package/src/index.ts
CHANGED
|
@@ -46,13 +46,18 @@ export {
|
|
|
46
46
|
} from "./x402/facilitator.js";
|
|
47
47
|
|
|
48
48
|
export {
|
|
49
|
+
DEFAULT_METRIK_PUBLIC_REGISTRY_URL,
|
|
49
50
|
DEFAULT_METRIK_REGISTRY_URL,
|
|
50
51
|
DEMO_SEED_LISTING,
|
|
51
52
|
DEMO_SEED_SERVICE_REF,
|
|
52
53
|
LIVE_DATA_SEED_LISTING,
|
|
53
54
|
LIVE_DATA_SEED_SERVICE_REF,
|
|
55
|
+
RegistryUnavailableError,
|
|
54
56
|
discoverServices,
|
|
57
|
+
discoverServicesDetailed,
|
|
58
|
+
type DiscoverResult,
|
|
55
59
|
type DiscoverServicesOptions,
|
|
60
|
+
type DiscoveredService,
|
|
56
61
|
type ServiceListing,
|
|
57
62
|
} from "./discovery/registry.js";
|
|
58
63
|
|
|
@@ -99,6 +104,8 @@ export {
|
|
|
99
104
|
type ReclaimAuthorizedStreamInput,
|
|
100
105
|
type ReclaimVerifiedStreamOptions,
|
|
101
106
|
type ReclaimVerifiedStreamResult,
|
|
107
|
+
isCheckpointStream,
|
|
108
|
+
type StreamStatusStream,
|
|
102
109
|
type StreamStatusView,
|
|
103
110
|
type VerifiedStreamAgentOpener,
|
|
104
111
|
} from "./sdk/client.js";
|
package/src/mcp/server.ts
CHANGED
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
ServerMandateConfigError,
|
|
21
21
|
} from "../mandates/env.js";
|
|
22
22
|
import {
|
|
23
|
+
isCheckpointStream,
|
|
23
24
|
VerifiedStreamAgentClient,
|
|
24
25
|
type ReclaimAuthorizedStreamInput,
|
|
25
26
|
type ReclaimVerifiedStreamResult,
|
|
@@ -859,6 +860,11 @@ export async function createVerifiedStreamMcpServerOptionsFromEnv(
|
|
|
859
860
|
chain: parsed.chain,
|
|
860
861
|
transport: http(parsed.rpcUrl),
|
|
861
862
|
escrow: parsed.escrow,
|
|
863
|
+
// Declare the escrow as V2 as well. Without this the SDK treats the
|
|
864
|
+
// client as V1 and routes claimable()/reclaimable() to the legacy ABI,
|
|
865
|
+
// which reverts against StreamEscrowV2 — the checkpoint escrow replaced
|
|
866
|
+
// those one-argument views with proof-carrying ones.
|
|
867
|
+
escrowV2: parsed.escrow,
|
|
862
868
|
usdc: parsed.usdc,
|
|
863
869
|
settlementTarget: {
|
|
864
870
|
chainId: parsed.chain.id,
|
|
@@ -943,15 +949,30 @@ function serializeStreamStatus(
|
|
|
943
949
|
operator: status.stream.operator,
|
|
944
950
|
serviceRef: status.stream.serviceRef,
|
|
945
951
|
deposit: status.stream.deposit.toString(),
|
|
952
|
+
// Informational on a checkpoint escrow: it stores the rate but computes
|
|
953
|
+
// no accrual. Emit the vocabulary of whichever escrow generation answered
|
|
954
|
+
// instead of flattening two incompatible tuples into one shape.
|
|
946
955
|
ratePerSecond: status.stream.ratePerSecond.toString(),
|
|
947
|
-
accrued: status.stream.accrued.toString(),
|
|
948
|
-
claimed: status.stream.claimed.toString(),
|
|
949
956
|
openedAt: status.stream.openedAt,
|
|
950
957
|
expiresAt: status.stream.expiresAt,
|
|
951
|
-
lastVerifiedAt: status.stream.lastVerifiedAt,
|
|
952
|
-
lastSequence: status.stream.lastSequence.toString(),
|
|
953
|
-
consecutiveFailures: status.stream.consecutiveFailures,
|
|
954
958
|
status: status.stream.status,
|
|
959
|
+
...(isCheckpointStream(status.stream)
|
|
960
|
+
? {
|
|
961
|
+
settlement: "checkpoint" as const,
|
|
962
|
+
settledCumulative: status.stream.settledCumulative.toString(),
|
|
963
|
+
claimedCumulative: status.stream.claimedCumulative.toString(),
|
|
964
|
+
feesPaid: status.stream.feesPaid.toString(),
|
|
965
|
+
closedAt: status.stream.closedAt,
|
|
966
|
+
reclaimed: status.stream.reclaimed,
|
|
967
|
+
}
|
|
968
|
+
: {
|
|
969
|
+
settlement: "per-attestation" as const,
|
|
970
|
+
accrued: status.stream.accrued.toString(),
|
|
971
|
+
claimed: status.stream.claimed.toString(),
|
|
972
|
+
lastVerifiedAt: status.stream.lastVerifiedAt,
|
|
973
|
+
lastSequence: status.stream.lastSequence.toString(),
|
|
974
|
+
consecutiveFailures: status.stream.consecutiveFailures,
|
|
975
|
+
}),
|
|
955
976
|
},
|
|
956
977
|
claimable: status.claimable.toString(),
|
|
957
978
|
reclaimable: status.reclaimable.toString(),
|
package/src/sdk/client.ts
CHANGED
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
type HireComputeResult,
|
|
4
4
|
type StreamProofClientConfig,
|
|
5
5
|
type StreamProofStream,
|
|
6
|
+
type StreamV2,
|
|
6
7
|
} from "@absol-labs/sdk";
|
|
7
8
|
|
|
8
9
|
import {
|
|
@@ -25,8 +26,23 @@ export interface OpenVerifiedStreamInput {
|
|
|
25
26
|
readonly revokedMandateIds?: Iterable<`0x${string}`>;
|
|
26
27
|
}
|
|
27
28
|
|
|
29
|
+
/**
|
|
30
|
+
* A stream as read from the configured escrow. Checkpoint escrows (V2) and the
|
|
31
|
+
* legacy per-attestation escrow (V1) expose incompatible tuples, so the view
|
|
32
|
+
* carries whichever one the client is pointed at. Narrow with
|
|
33
|
+
* {@link isCheckpointStream} before touching version-specific fields.
|
|
34
|
+
*/
|
|
35
|
+
export type StreamStatusStream = StreamProofStream | StreamV2;
|
|
36
|
+
|
|
37
|
+
/** True when the stream came from a checkpoint-settled (V2) escrow. */
|
|
38
|
+
export function isCheckpointStream(
|
|
39
|
+
stream: StreamStatusStream,
|
|
40
|
+
): stream is StreamV2 {
|
|
41
|
+
return "settledCumulative" in stream;
|
|
42
|
+
}
|
|
43
|
+
|
|
28
44
|
export interface StreamStatusView {
|
|
29
|
-
readonly stream:
|
|
45
|
+
readonly stream: StreamStatusStream;
|
|
30
46
|
readonly claimable: bigint;
|
|
31
47
|
readonly reclaimable: bigint;
|
|
32
48
|
}
|
|
@@ -63,7 +79,11 @@ export interface AgentSdkClient {
|
|
|
63
79
|
readonly ratePerSecond: bigint;
|
|
64
80
|
readonly maxDurationSeconds: number;
|
|
65
81
|
}): Promise<HireComputeResult>;
|
|
82
|
+
// Both decoders. The SDK deliberately makes `getStream` throw once a V2
|
|
83
|
+
// escrow is configured (the tuples are incompatible), so the caller must pick
|
|
84
|
+
// based on configuration rather than probing.
|
|
66
85
|
getStream(streamId: `0x${string}`): Promise<StreamProofStream>;
|
|
86
|
+
getStreamV2(streamId: `0x${string}`): Promise<StreamV2>;
|
|
67
87
|
claimable(streamId: `0x${string}`): Promise<bigint>;
|
|
68
88
|
reclaimable(streamId: `0x${string}`): Promise<bigint>;
|
|
69
89
|
claim(streamId: `0x${string}`): Promise<StreamProofTransactionResult>;
|
|
@@ -93,6 +113,12 @@ export class MandateDeniedError extends Error {
|
|
|
93
113
|
|
|
94
114
|
export class VerifiedStreamAgentClient implements VerifiedStreamAgentOpener {
|
|
95
115
|
private readonly sdkClient: AgentSdkClient;
|
|
116
|
+
/**
|
|
117
|
+
* Whether the configured escrow is checkpoint-settled. Set once from config:
|
|
118
|
+
* the SDK throws rather than guessing if the wrong decoder is used, so this
|
|
119
|
+
* has to be decided here instead of discovered by trial.
|
|
120
|
+
*/
|
|
121
|
+
private readonly checkpointSettled: boolean;
|
|
96
122
|
|
|
97
123
|
constructor(
|
|
98
124
|
config: StreamProofClientConfig,
|
|
@@ -101,6 +127,15 @@ export class VerifiedStreamAgentClient implements VerifiedStreamAgentOpener {
|
|
|
101
127
|
this.sdkClient = (options.createSdkClient ?? defaultSdkClientFactory)(
|
|
102
128
|
config,
|
|
103
129
|
);
|
|
130
|
+
this.checkpointSettled = config.escrowV2 !== undefined;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
private async readStream(
|
|
134
|
+
streamId: `0x${string}`,
|
|
135
|
+
): Promise<StreamStatusStream> {
|
|
136
|
+
return this.checkpointSettled
|
|
137
|
+
? await this.sdkClient.getStreamV2(streamId)
|
|
138
|
+
: await this.sdkClient.getStream(streamId);
|
|
104
139
|
}
|
|
105
140
|
|
|
106
141
|
async openVerifiedStream(
|
|
@@ -132,7 +167,7 @@ export class VerifiedStreamAgentClient implements VerifiedStreamAgentOpener {
|
|
|
132
167
|
|
|
133
168
|
async getStreamStatus(streamId: `0x${string}`): Promise<StreamStatusView> {
|
|
134
169
|
const [stream, claimable, reclaimable] = await Promise.all([
|
|
135
|
-
this.
|
|
170
|
+
this.readStream(streamId),
|
|
136
171
|
this.sdkClient.claimable(streamId),
|
|
137
172
|
this.sdkClient.reclaimable(streamId),
|
|
138
173
|
]);
|
|
@@ -183,7 +218,7 @@ export class VerifiedStreamAgentClient implements VerifiedStreamAgentOpener {
|
|
|
183
218
|
private async authorizeExistingStreamAction(
|
|
184
219
|
input: MandateAuthorizedStreamActionInput,
|
|
185
220
|
): Promise<void> {
|
|
186
|
-
const stream = await this.
|
|
221
|
+
const stream = await this.readStream(input.streamId);
|
|
187
222
|
const decision = await checkMandate(
|
|
188
223
|
input.signedMandate,
|
|
189
224
|
{
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@coinbase/cdp-sdk` is an OPTIONAL peer dependency: only consumers that use the
|
|
3
|
+
* Coinbase CDP wallet path need it, and it carries a very large transitive graph
|
|
4
|
+
* (it was the main contributor to the 18 -> ~515 package jump a consumer saw just
|
|
5
|
+
* for hiring and invoking a service). The top-level "@absol-labs/agent" barrel
|
|
6
|
+
* DOES reach `./provider.ts`, so that module must import the SDK's TYPES only
|
|
7
|
+
* (`import type`, erased at compile time) and load the runtime module lazily
|
|
8
|
+
* through this loader at the point of first use.
|
|
9
|
+
*
|
|
10
|
+
* This is the same contract as `../zktls/reclaim-js-sdk.ts`, and it is enforced
|
|
11
|
+
* by the barrel-reachability guard in `test/flat-install.test.ts`: a static value
|
|
12
|
+
* import of an optional peer from any barrel-reachable module fails that test.
|
|
13
|
+
*
|
|
14
|
+
* Deliberately NOT a top-level `await import(...)` (the pattern used by
|
|
15
|
+
* `../frameworks/agentkit.ts`): those modules are not barrel-reachable, this one
|
|
16
|
+
* is, and a top-level await would re-introduce the hard dependency at
|
|
17
|
+
* barrel-import time — precisely the regression that issue #79 was filed for.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
type CdpSdkModule = typeof import("@coinbase/cdp-sdk");
|
|
21
|
+
|
|
22
|
+
let modulePromise: Promise<CdpSdkModule> | undefined;
|
|
23
|
+
|
|
24
|
+
/** Memoized lazy load with an actionable missing-peer error. */
|
|
25
|
+
export async function loadCdpSdk(): Promise<CdpSdkModule> {
|
|
26
|
+
modulePromise ??= import("@coinbase/cdp-sdk").catch((error: unknown) => {
|
|
27
|
+
throw new Error(
|
|
28
|
+
"@absol-labs/agent CDP wallet support requires the optional peer dependency '@coinbase/cdp-sdk'. Install it with `npm install @coinbase/cdp-sdk`, or use a different wallet mode (the autonomous Privy wallet and an injected account both work without it).",
|
|
29
|
+
{ cause: error },
|
|
30
|
+
);
|
|
31
|
+
});
|
|
32
|
+
return await modulePromise;
|
|
33
|
+
}
|
package/src/wallet/provider.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { type StreamProofClientConfig } from "@absol-labs/sdk";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
} from "@coinbase/cdp-sdk";
|
|
2
|
+
// `@coinbase/cdp-sdk` is an OPTIONAL peer dependency: TYPES only here (erased at
|
|
3
|
+
// compile time), the runtime module via `loadCdpSdk()` at the point of first use.
|
|
4
|
+
// This module IS reachable from the package barrel, so a value import here would
|
|
5
|
+
// re-introduce the hard dependency for every consumer. See ./cdp-sdk.ts.
|
|
6
|
+
import type { EvmServerAccount, EvmSmartAccount } from "@coinbase/cdp-sdk";
|
|
7
|
+
import { loadCdpSdk } from "./cdp-sdk.js";
|
|
7
8
|
import {
|
|
8
9
|
custom,
|
|
9
10
|
toHex,
|
|
@@ -173,9 +174,12 @@ export async function resolveAgentWallet(
|
|
|
173
174
|
};
|
|
174
175
|
}
|
|
175
176
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
177
|
+
// An injected factory keeps its synchronous public signature; only the DEFAULT
|
|
178
|
+
// path is async, because it lazily loads the optional @coinbase/cdp-sdk peer.
|
|
179
|
+
const cdpClient =
|
|
180
|
+
options.createCdpClient === undefined
|
|
181
|
+
? await defaultCdpClientFactory(input.cdp)
|
|
182
|
+
: options.createCdpClient(input.cdp);
|
|
179
183
|
const owner = await cdpClient.evm.getOrCreateAccount({
|
|
180
184
|
name: input.cdp.ownerName,
|
|
181
185
|
});
|
|
@@ -338,7 +342,10 @@ export function parseAgentWalletEnv(
|
|
|
338
342
|
};
|
|
339
343
|
}
|
|
340
344
|
|
|
341
|
-
function defaultCdpClientFactory(
|
|
345
|
+
async function defaultCdpClientFactory(
|
|
346
|
+
config: CdpWalletConfig,
|
|
347
|
+
): Promise<CdpClientLike> {
|
|
348
|
+
const { CdpClient } = await loadCdpSdk();
|
|
342
349
|
return new CdpClient({
|
|
343
350
|
...(config.apiKeyId === undefined ? {} : { apiKeyId: config.apiKeyId }),
|
|
344
351
|
...(config.apiKeySecret === undefined
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@reclaimprotocol/js-sdk` is an OPTIONAL peer dependency: only consumers that
|
|
3
|
+
* actually generate/verify zkTLS delivery proofs (T2, issue #24) need it, and it
|
|
4
|
+
* carries a large transitive graph. The top-level "@absol-labs/agent" barrel
|
|
5
|
+
* DOES re-export the zkTLS modules, so those modules must import the SDK's
|
|
6
|
+
* TYPES only (`import type`, erased at compile time) and load the runtime module
|
|
7
|
+
* lazily through this loader at the point of first use.
|
|
8
|
+
*
|
|
9
|
+
* Deliberately NOT a top-level `await import(...)` (the pattern used by
|
|
10
|
+
* `../frameworks/langchain.ts`): that module is not barrel-reachable, this one
|
|
11
|
+
* is, and a top-level await would re-introduce the hard dependency at
|
|
12
|
+
* barrel-import time.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
type ReclaimJsSdkModule = typeof import("@reclaimprotocol/js-sdk");
|
|
16
|
+
|
|
17
|
+
let modulePromise: Promise<ReclaimJsSdkModule> | undefined;
|
|
18
|
+
|
|
19
|
+
/** Memoized lazy load with an actionable missing-peer error. */
|
|
20
|
+
export async function loadReclaimJsSdk(): Promise<ReclaimJsSdkModule> {
|
|
21
|
+
modulePromise ??= import("@reclaimprotocol/js-sdk").catch(
|
|
22
|
+
(error: unknown) => {
|
|
23
|
+
throw new Error(
|
|
24
|
+
"@absol-labs/agent zkTLS delivery-proof support requires the optional peer dependency '@reclaimprotocol/js-sdk'. Install it with `npm install @reclaimprotocol/js-sdk`.",
|
|
25
|
+
{ cause: error },
|
|
26
|
+
);
|
|
27
|
+
},
|
|
28
|
+
);
|
|
29
|
+
return await modulePromise;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* `@reclaimprotocol/zk-fetch` is the companion optional peer used to PRODUCE
|
|
34
|
+
* proofs (the SDK above verifies them). Same contract, same actionable error.
|
|
35
|
+
*/
|
|
36
|
+
type ReclaimZkFetchModule = typeof import("@reclaimprotocol/zk-fetch");
|
|
37
|
+
|
|
38
|
+
let zkFetchModulePromise: Promise<ReclaimZkFetchModule> | undefined;
|
|
39
|
+
|
|
40
|
+
export async function loadReclaimZkFetch(): Promise<ReclaimZkFetchModule> {
|
|
41
|
+
zkFetchModulePromise ??= import("@reclaimprotocol/zk-fetch").catch(
|
|
42
|
+
(error: unknown) => {
|
|
43
|
+
throw new Error(
|
|
44
|
+
"@absol-labs/agent zkTLS delivery-proof support requires the optional peer dependency '@reclaimprotocol/zk-fetch'. Install it with `npm install @reclaimprotocol/zk-fetch`.",
|
|
45
|
+
{ cause: error },
|
|
46
|
+
);
|
|
47
|
+
},
|
|
48
|
+
);
|
|
49
|
+
return await zkFetchModulePromise;
|
|
50
|
+
}
|
package/src/zktls/reclaim.ts
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
// `@reclaimprotocol/js-sdk` is an OPTIONAL peer dependency: TYPES only here
|
|
2
|
+
// (erased at compile time), runtime values via `loadReclaimJsSdk()` at the point
|
|
3
|
+
// of first use. See ./reclaim-js-sdk.ts.
|
|
4
|
+
import type {
|
|
4
5
|
verifyProof,
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
Proof as ReclaimProtocolProof,
|
|
7
|
+
RequestSpec,
|
|
8
|
+
VerifyProofResult,
|
|
8
9
|
} from "@reclaimprotocol/js-sdk";
|
|
9
10
|
import { z } from "zod";
|
|
10
11
|
|
|
12
|
+
import { loadReclaimJsSdk, loadReclaimZkFetch } from "./reclaim-js-sdk.js";
|
|
13
|
+
|
|
11
14
|
const addressSchema = z.string().regex(/^0x[0-9a-fA-F]{40}$/);
|
|
12
15
|
const bytes32Schema = z.string().regex(/^0x[0-9a-fA-F]{64}$/);
|
|
13
16
|
const httpMethodSchema = z.enum(["GET", "POST", "PUT"]);
|
|
@@ -172,7 +175,14 @@ const reclaimProofInputSchema = z.object({
|
|
|
172
175
|
export class ReclaimConsumerProofService implements ConsumerDeliveryProofService {
|
|
173
176
|
private readonly client: ReclaimClientLike;
|
|
174
177
|
private readonly config: ReclaimProofServiceConfig;
|
|
175
|
-
|
|
178
|
+
/**
|
|
179
|
+
* Caller-supplied verifier, if any. NOT defaulted in the constructor: the real
|
|
180
|
+
* `verifyProof` lives in the optional peer `@reclaimprotocol/js-sdk`, which is
|
|
181
|
+
* loaded lazily at first use so that merely constructing this service — or
|
|
182
|
+
* importing the package barrel — never requires the peer to be installed.
|
|
183
|
+
* Resolved in {@link resolveVerifyProof}.
|
|
184
|
+
*/
|
|
185
|
+
private readonly verifyProofOverride: VerifyProofFn | undefined;
|
|
176
186
|
|
|
177
187
|
constructor(
|
|
178
188
|
config: ReclaimProofServiceConfig,
|
|
@@ -194,7 +204,16 @@ export class ReclaimConsumerProofService implements ConsumerDeliveryProofService
|
|
|
194
204
|
this.config.applicationSecret,
|
|
195
205
|
this.config.logs ?? false,
|
|
196
206
|
);
|
|
197
|
-
this.
|
|
207
|
+
this.verifyProofOverride = options.verifyProof;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* The verifier to use: a caller-supplied override, else the optional peer's
|
|
212
|
+
* `verifyProof`, loaded on first use with an actionable missing-peer error.
|
|
213
|
+
*/
|
|
214
|
+
private async resolveVerifyProof(): Promise<VerifyProofFn> {
|
|
215
|
+
if (this.verifyProofOverride !== undefined) return this.verifyProofOverride;
|
|
216
|
+
return (await loadReclaimJsSdk()).verifyProof;
|
|
198
217
|
}
|
|
199
218
|
|
|
200
219
|
async proveConsumedHttpsResponse(
|
|
@@ -244,8 +263,15 @@ export class ReclaimConsumerProofService implements ConsumerDeliveryProofService
|
|
|
244
263
|
);
|
|
245
264
|
}
|
|
246
265
|
|
|
247
|
-
|
|
248
|
-
|
|
266
|
+
// Both the verifier and the provider-hash helper live in the optional peer;
|
|
267
|
+
// load it once here rather than importing it at module scope.
|
|
268
|
+
const [verifyProofFn, reclaimSdk] = await Promise.all([
|
|
269
|
+
this.resolveVerifyProof(),
|
|
270
|
+
loadReclaimJsSdk(),
|
|
271
|
+
]);
|
|
272
|
+
|
|
273
|
+
const verification = await verifyProofFn(proof, {
|
|
274
|
+
...reclaimSdk.getProviderHashRequirementsFromSpec({
|
|
249
275
|
requests: [toRequestSpec(parsed, method)],
|
|
250
276
|
}),
|
|
251
277
|
...(parsed.useTee === true
|
|
@@ -271,25 +297,33 @@ export class ReclaimConsumerProofService implements ConsumerDeliveryProofService
|
|
|
271
297
|
);
|
|
272
298
|
}
|
|
273
299
|
|
|
274
|
-
|
|
300
|
+
// Same optional peer, already memoized by the loader above.
|
|
301
|
+
const request = reclaimSdk.getHttpProviderClaimParamsFromProof(proof);
|
|
302
|
+
type ClaimParams = ReturnType<
|
|
303
|
+
typeof reclaimSdk.getHttpProviderClaimParamsFromProof
|
|
304
|
+
>;
|
|
275
305
|
return {
|
|
276
306
|
proof,
|
|
277
307
|
request: {
|
|
278
308
|
url: request.url,
|
|
279
309
|
method: request.method as "GET" | "POST" | "PUT",
|
|
280
310
|
body: request.body === "" ? null : (request.body ?? null),
|
|
281
|
-
responseMatches: request.responseMatches.map(
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
311
|
+
responseMatches: request.responseMatches.map(
|
|
312
|
+
(match: ClaimParams["responseMatches"][number]) => ({
|
|
313
|
+
value: match.value,
|
|
314
|
+
type: match.type,
|
|
315
|
+
invert: match.invert,
|
|
316
|
+
isOptional: match.isOptional,
|
|
317
|
+
}),
|
|
318
|
+
),
|
|
319
|
+
responseRedactions: request.responseRedactions.map(
|
|
320
|
+
(redaction: ClaimParams["responseRedactions"][number]) => ({
|
|
321
|
+
regex: redaction.regex,
|
|
322
|
+
jsonPath: redaction.jsonPath,
|
|
323
|
+
xPath: redaction.xPath,
|
|
324
|
+
hash: redaction.hash,
|
|
325
|
+
}),
|
|
326
|
+
),
|
|
293
327
|
},
|
|
294
328
|
verification: {
|
|
295
329
|
context: trusted.context,
|
|
@@ -5,11 +5,12 @@ import {
|
|
|
5
5
|
type DeliveryReceipt,
|
|
6
6
|
type DeliveryReceiptDomainInput,
|
|
7
7
|
} from "@absol-labs/shared";
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
} from "@reclaimprotocol/js-sdk";
|
|
8
|
+
// `@reclaimprotocol/js-sdk` is an OPTIONAL peer dependency and this module IS
|
|
9
|
+
// reachable from the package barrel, so it may import TYPES only (erased at
|
|
10
|
+
// compile time); runtime values come from `loadReclaimJsSdk()` at first use.
|
|
11
|
+
// Enforced by the barrel-reachability guard in test/flat-install.test.ts.
|
|
12
|
+
import type { verifyProof } from "@reclaimprotocol/js-sdk";
|
|
13
|
+
import { loadReclaimJsSdk } from "./reclaim-js-sdk.js";
|
|
13
14
|
import {
|
|
14
15
|
keccak256,
|
|
15
16
|
stringToBytes,
|
|
@@ -167,7 +168,12 @@ const reclaimT2UrlSchema = z.string().url();
|
|
|
167
168
|
export class ReclaimDeliveryProofAttestor implements DeliveryProofAttestor {
|
|
168
169
|
private readonly client: ReclaimClientLike;
|
|
169
170
|
private readonly config: ReclaimProofServiceConfig;
|
|
170
|
-
|
|
171
|
+
/**
|
|
172
|
+
* Caller-supplied verifier, if any. NOT defaulted in the constructor: the real
|
|
173
|
+
* `verifyProof` lives in the optional peer, loaded lazily at first use so that
|
|
174
|
+
* constructing this service never requires the peer to be installed.
|
|
175
|
+
*/
|
|
176
|
+
private readonly verifyProofOverride: VerifyProofFn | undefined;
|
|
171
177
|
|
|
172
178
|
constructor(
|
|
173
179
|
config: ReclaimProofServiceConfig,
|
|
@@ -189,7 +195,13 @@ export class ReclaimDeliveryProofAttestor implements DeliveryProofAttestor {
|
|
|
189
195
|
this.config.applicationSecret,
|
|
190
196
|
this.config.logs ?? false,
|
|
191
197
|
);
|
|
192
|
-
this.
|
|
198
|
+
this.verifyProofOverride = options.verifyProof;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/** Override if given, else the optional peer's `verifyProof`, loaded on demand. */
|
|
202
|
+
private async resolveVerifyProof(): Promise<VerifyProofFn> {
|
|
203
|
+
if (this.verifyProofOverride !== undefined) return this.verifyProofOverride;
|
|
204
|
+
return (await loadReclaimJsSdk()).verifyProof;
|
|
193
205
|
}
|
|
194
206
|
|
|
195
207
|
async proveDeliveryResponse(
|
|
@@ -245,8 +257,14 @@ export class ReclaimDeliveryProofAttestor implements DeliveryProofAttestor {
|
|
|
245
257
|
);
|
|
246
258
|
}
|
|
247
259
|
|
|
248
|
-
|
|
249
|
-
|
|
260
|
+
// Verifier and provider-hash helper both live in the optional peer; load once.
|
|
261
|
+
const [verifyProofFn, reclaimSdk] = await Promise.all([
|
|
262
|
+
this.resolveVerifyProof(),
|
|
263
|
+
loadReclaimJsSdk(),
|
|
264
|
+
]);
|
|
265
|
+
|
|
266
|
+
const verification = await verifyProofFn(proof, {
|
|
267
|
+
...reclaimSdk.getProviderHashRequirementsFromSpec({
|
|
250
268
|
requests: [toRequestSpec(parsed, method)],
|
|
251
269
|
}),
|
|
252
270
|
...(parsed.useTee === true
|
|
@@ -266,7 +284,7 @@ export class ReclaimDeliveryProofAttestor implements DeliveryProofAttestor {
|
|
|
266
284
|
);
|
|
267
285
|
}
|
|
268
286
|
|
|
269
|
-
const request = getHttpProviderClaimParamsFromProof(proof);
|
|
287
|
+
const request = reclaimSdk.getHttpProviderClaimParamsFromProof(proof);
|
|
270
288
|
return {
|
|
271
289
|
proof,
|
|
272
290
|
request: {
|