@absol-labs/agent 0.10.0 → 0.11.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 (141) hide show
  1. package/README.md +107 -23
  2. package/dist/capability/invocation-capability.d.ts +18 -6
  3. package/dist/capability/invocation-capability.d.ts.map +1 -1
  4. package/dist/capability/invocation-capability.js.map +1 -1
  5. package/dist/cli/commands/claim.d.ts.map +1 -1
  6. package/dist/cli/commands/claim.js +20 -4
  7. package/dist/cli/commands/claim.js.map +1 -1
  8. package/dist/cli/commands/close.d.ts.map +1 -1
  9. package/dist/cli/commands/close.js +16 -4
  10. package/dist/cli/commands/close.js.map +1 -1
  11. package/dist/cli/commands/invoke.d.ts.map +1 -1
  12. package/dist/cli/commands/invoke.js +1 -0
  13. package/dist/cli/commands/invoke.js.map +1 -1
  14. package/dist/cli/commands/open.d.ts.map +1 -1
  15. package/dist/cli/commands/open.js +63 -11
  16. package/dist/cli/commands/open.js.map +1 -1
  17. package/dist/cli/commands/reclaim-unverified.d.ts.map +1 -1
  18. package/dist/cli/commands/reclaim-unverified.js +12 -4
  19. package/dist/cli/commands/reclaim-unverified.js.map +1 -1
  20. package/dist/cli/commands/reclaim.d.ts.map +1 -1
  21. package/dist/cli/commands/reclaim.js +16 -5
  22. package/dist/cli/commands/reclaim.js.map +1 -1
  23. package/dist/cli/commands/streams.js +1 -1
  24. package/dist/cli/commands/streams.js.map +1 -1
  25. package/dist/cli/deps.d.ts +53 -7
  26. package/dist/cli/deps.d.ts.map +1 -1
  27. package/dist/cli/deps.js +65 -15
  28. package/dist/cli/deps.js.map +1 -1
  29. package/dist/cli/privy.d.ts +83 -0
  30. package/dist/cli/privy.d.ts.map +1 -0
  31. package/dist/cli/privy.js +230 -0
  32. package/dist/cli/privy.js.map +1 -0
  33. package/dist/cli/spend-policy.d.ts +60 -0
  34. package/dist/cli/spend-policy.d.ts.map +1 -0
  35. package/dist/cli/spend-policy.js +65 -0
  36. package/dist/cli/spend-policy.js.map +1 -0
  37. package/dist/cli/wallet.d.ts +108 -39
  38. package/dist/cli/wallet.d.ts.map +1 -1
  39. package/dist/cli/wallet.js +108 -43
  40. package/dist/cli/wallet.js.map +1 -1
  41. package/dist/gateway/caller-auth-gateway.d.ts +38 -3
  42. package/dist/gateway/caller-auth-gateway.d.ts.map +1 -1
  43. package/dist/gateway/caller-auth-gateway.js +25 -1
  44. package/dist/gateway/caller-auth-gateway.js.map +1 -1
  45. package/dist/gateway/http-server.d.ts +53 -0
  46. package/dist/gateway/http-server.d.ts.map +1 -1
  47. package/dist/gateway/http-server.js +189 -4
  48. package/dist/gateway/http-server.js.map +1 -1
  49. package/dist/gateway/x402-mode.d.ts +123 -0
  50. package/dist/gateway/x402-mode.d.ts.map +1 -0
  51. package/dist/gateway/x402-mode.js +212 -0
  52. package/dist/gateway/x402-mode.js.map +1 -0
  53. package/dist/index.d.ts +6 -4
  54. package/dist/index.d.ts.map +1 -1
  55. package/dist/index.js +8 -3
  56. package/dist/index.js.map +1 -1
  57. package/dist/mandates/env.d.ts +3 -2
  58. package/dist/mandates/env.d.ts.map +1 -1
  59. package/dist/mandates/env.js +3 -2
  60. package/dist/mandates/env.js.map +1 -1
  61. package/dist/mcp/server.d.ts +49 -0
  62. package/dist/mcp/server.d.ts.map +1 -1
  63. package/dist/mcp/server.js +513 -24
  64. package/dist/mcp/server.js.map +1 -1
  65. package/dist/mcp/service-invoker.d.ts +97 -0
  66. package/dist/mcp/service-invoker.d.ts.map +1 -0
  67. package/dist/mcp/service-invoker.js +167 -0
  68. package/dist/mcp/service-invoker.js.map +1 -0
  69. package/dist/sdk/invoke.d.ts +19 -4
  70. package/dist/sdk/invoke.d.ts.map +1 -1
  71. package/dist/sdk/invoke.js +69 -5
  72. package/dist/sdk/invoke.js.map +1 -1
  73. package/dist/x402/delivery-oracle.d.ts +108 -0
  74. package/dist/x402/delivery-oracle.d.ts.map +1 -0
  75. package/dist/x402/delivery-oracle.js +174 -0
  76. package/dist/x402/delivery-oracle.js.map +1 -0
  77. package/dist/x402/facilitator-server-entry.d.ts +3 -0
  78. package/dist/x402/facilitator-server-entry.d.ts.map +1 -0
  79. package/dist/x402/facilitator-server-entry.js +27 -0
  80. package/dist/x402/facilitator-server-entry.js.map +1 -0
  81. package/dist/x402/facilitator-server.d.ts +73 -0
  82. package/dist/x402/facilitator-server.d.ts.map +1 -0
  83. package/dist/x402/facilitator-server.js +305 -0
  84. package/dist/x402/facilitator-server.js.map +1 -0
  85. package/dist/x402/facilitator.d.ts +69 -17
  86. package/dist/x402/facilitator.d.ts.map +1 -1
  87. package/dist/x402/facilitator.js +76 -33
  88. package/dist/x402/facilitator.js.map +1 -1
  89. package/dist/x402/payment-requirements.d.ts +106 -0
  90. package/dist/x402/payment-requirements.d.ts.map +1 -0
  91. package/dist/x402/payment-requirements.js +138 -0
  92. package/dist/x402/payment-requirements.js.map +1 -0
  93. package/dist/x402/relayer.d.ts +41 -0
  94. package/dist/x402/relayer.d.ts.map +1 -0
  95. package/dist/x402/relayer.js +64 -0
  96. package/dist/x402/relayer.js.map +1 -0
  97. package/dist/x402/resource-index.d.ts +75 -0
  98. package/dist/x402/resource-index.d.ts.map +1 -0
  99. package/dist/x402/resource-index.js +126 -0
  100. package/dist/x402/resource-index.js.map +1 -0
  101. package/dist/x402/verified-delivery-facilitator.d.ts +285 -0
  102. package/dist/x402/verified-delivery-facilitator.d.ts.map +1 -0
  103. package/dist/x402/verified-delivery-facilitator.js +423 -0
  104. package/dist/x402/verified-delivery-facilitator.js.map +1 -0
  105. package/dist/zktls/t2-delivery-proof.d.ts +6 -6
  106. package/docs/agent-layer.md +12 -8
  107. package/docs/cli.md +158 -22
  108. package/docs/mcp-hosted.md +4 -3
  109. package/docs/mcp-reference.md +175 -0
  110. package/docs/quickstart.md +18 -4
  111. package/docs/threat-model.md +4 -3
  112. package/docs/x402.md +648 -0
  113. package/package.json +18 -7
  114. package/src/capability/invocation-capability.ts +17 -1
  115. package/src/cli/commands/claim.ts +30 -4
  116. package/src/cli/commands/close.ts +20 -4
  117. package/src/cli/commands/invoke.ts +1 -0
  118. package/src/cli/commands/open.ts +76 -10
  119. package/src/cli/commands/reclaim-unverified.ts +16 -4
  120. package/src/cli/commands/reclaim.ts +22 -5
  121. package/src/cli/commands/streams.ts +1 -1
  122. package/src/cli/deps.ts +115 -24
  123. package/src/cli/privy.ts +396 -0
  124. package/src/cli/spend-policy.ts +139 -0
  125. package/src/cli/wallet.ts +188 -57
  126. package/src/gateway/caller-auth-gateway.ts +77 -6
  127. package/src/gateway/http-server.ts +280 -6
  128. package/src/gateway/x402-mode.ts +350 -0
  129. package/src/index.ts +40 -5
  130. package/src/mandates/env.ts +3 -2
  131. package/src/mcp/server.ts +681 -29
  132. package/src/mcp/service-invoker.ts +285 -0
  133. package/src/sdk/invoke.ts +90 -12
  134. package/src/x402/delivery-oracle.ts +285 -0
  135. package/src/x402/facilitator-server-entry.ts +35 -0
  136. package/src/x402/facilitator-server.ts +445 -0
  137. package/src/x402/facilitator.ts +125 -46
  138. package/src/x402/payment-requirements.ts +231 -0
  139. package/src/x402/relayer.ts +122 -0
  140. package/src/x402/resource-index.ts +228 -0
  141. package/src/x402/verified-delivery-facilitator.ts +744 -0
@@ -0,0 +1,228 @@
1
+ import {
2
+ discoverServices,
3
+ type DiscoverServicesOptions,
4
+ type ServiceListing,
5
+ } from "../discovery/registry.js";
6
+ import type { SignedListingPayee } from "./payment-requirements.js";
7
+
8
+ /**
9
+ * Maps an x402 `PaymentRequirements.resource` URL onto the Metrik listing that
10
+ * serves it — the step that decides whether Metrik has any business having an
11
+ * opinion about a payment at all.
12
+ *
13
+ * ## Derived, never supplied
14
+ *
15
+ * The only thing taken from the request is the `resource` URL itself, because
16
+ * the x402 wire format gives the facilitator nothing else to key on. Everything
17
+ * the verdict is actually computed from — which listing that URL belongs to, the
18
+ * operator address on it, and the delivery record for it — is resolved HERE,
19
+ * from Metrik's own verified view of the registry. In particular the
20
+ * `extra.metrikServiceRef` hint that `buildMetrikPaymentRequirements` publishes
21
+ * is deliberately ignored: it rides inside a caller-supplied body, and reading
22
+ * it would let a caller choose which service's delivery record gets checked.
23
+ *
24
+ * ## Origin matching, and why not path matching
25
+ *
26
+ * A listing owns an ORIGIN (scheme + host + port). Matching on origin means a
27
+ * seller does not have to re-register every route, and it cannot over-reach: two
28
+ * listings never share an origin, because the origin is what the oracle probes
29
+ * and what `serviceRef` is derived from. A resource whose origin matches no
30
+ * listing is simply not ours.
31
+ */
32
+
33
+ export interface ResolvedMetrikResource extends SignedListingPayee {
34
+ /** The listing, as verified by `discoverServices` (EIP-712 signer recovered). */
35
+ readonly listing: ServiceListing;
36
+ /** Which of the listing's signed origins the resource matched. */
37
+ readonly matchedOrigin: string;
38
+ }
39
+
40
+ export type ResourceIndexStatus =
41
+ | "resolved"
42
+ | "not-listed"
43
+ /**
44
+ * Two or more signed listings claim this origin, so there is no single right
45
+ * answer about whose delivery record applies.
46
+ *
47
+ * Picking one — which "first claimant wins" did — is the origin-squatting
48
+ * primitive: a second listing claiming a victim's origin could take over the
49
+ * victim's gate and permanently refuse their payments. Not exploitable while
50
+ * listing insertion requires an operator-held key, but "unresolvable" costs
51
+ * nothing and removes the primitive before registration ever opens.
52
+ */
53
+ | "ambiguous"
54
+ | "registry-unavailable";
55
+
56
+ export interface ResourceLookup {
57
+ readonly status: ResourceIndexStatus;
58
+ readonly resource?: ResolvedMetrikResource;
59
+ }
60
+
61
+ export interface MetrikResourceIndex {
62
+ /** Never throws: an unreachable registry is a status, not an exception. */
63
+ lookup(resourceUrl: string): Promise<ResourceLookup>;
64
+ }
65
+
66
+ /** How long a registry snapshot is reused before a refresh is attempted. */
67
+ export const DEFAULT_RESOURCE_INDEX_REFRESH_MS = 60_000;
68
+
69
+ /**
70
+ * How long a stale snapshot keeps being served after refreshes start failing.
71
+ * Serving a stale listing is safe in a way that serving a stale DELIVERY verdict
72
+ * is not: listings change on a human timescale (a new signed descriptor), and
73
+ * the fields used from them — `operator`, `serviceRef` — are the stable ones. The
74
+ * live verdict is always fetched fresh by the delivery oracle.
75
+ */
76
+ export const DEFAULT_RESOURCE_INDEX_MAX_STALE_MS = 15 * 60_000;
77
+
78
+ export interface RegistryResourceIndexOptions {
79
+ readonly discover?: () => Promise<readonly ServiceListing[]>;
80
+ readonly discoverOptions?: DiscoverServicesOptions;
81
+ readonly refreshMs?: number;
82
+ readonly maxStaleMs?: number;
83
+ readonly now?: () => number;
84
+ }
85
+
86
+ export function createRegistryResourceIndex(
87
+ options: RegistryResourceIndexOptions = {},
88
+ ): MetrikResourceIndex {
89
+ const refreshMs = options.refreshMs ?? DEFAULT_RESOURCE_INDEX_REFRESH_MS;
90
+ const maxStaleMs = options.maxStaleMs ?? DEFAULT_RESOURCE_INDEX_MAX_STALE_MS;
91
+ const now = options.now ?? (() => Date.now());
92
+ const discover =
93
+ options.discover ??
94
+ (() =>
95
+ discoverServices(options.discoverOptions ?? {}) as Promise<
96
+ readonly ServiceListing[]
97
+ >);
98
+
99
+ interface Snapshot {
100
+ readonly loadedAtMs: number;
101
+ readonly byOrigin: ReadonlyMap<string, ResolvedMetrikResource>;
102
+ readonly ambiguous: ReadonlySet<string>;
103
+ }
104
+ let snapshot: Snapshot | undefined;
105
+ let inFlight: Promise<Snapshot | undefined> | undefined;
106
+
107
+ async function refresh(): Promise<Snapshot | undefined> {
108
+ try {
109
+ const listings = await discover();
110
+ const byOrigin = new Map<string, ResolvedMetrikResource>();
111
+ const ambiguous = new Set<string>();
112
+ for (const listing of listings) {
113
+ for (const origin of signedOriginsOf(listing)) {
114
+ const claimed = byOrigin.get(origin);
115
+ if (claimed === undefined) {
116
+ byOrigin.set(origin, {
117
+ serviceRef: listing.serviceRef,
118
+ operator: listing.operator,
119
+ listing,
120
+ matchedOrigin: origin,
121
+ });
122
+ continue;
123
+ }
124
+ // A DIFFERENT service claiming an origin another already claims makes
125
+ // the origin unresolvable, not first-come-first-served. See the
126
+ // `ambiguous` doc on `ResourceIndexStatus`. A listing re-claiming its
127
+ // OWN origin (e.g. publicUrl and baseUrl agreeing) is not a conflict.
128
+ if (
129
+ claimed.serviceRef.toLowerCase() !==
130
+ listing.serviceRef.toLowerCase()
131
+ ) {
132
+ ambiguous.add(origin);
133
+ }
134
+ }
135
+ }
136
+ for (const origin of ambiguous) {
137
+ byOrigin.delete(origin);
138
+ }
139
+ return { loadedAtMs: now(), byOrigin, ambiguous };
140
+ } catch {
141
+ return undefined;
142
+ }
143
+ }
144
+
145
+ async function currentSnapshot(): Promise<Snapshot | undefined> {
146
+ const cached = snapshot;
147
+ if (cached !== undefined && now() - cached.loadedAtMs < refreshMs) {
148
+ return cached;
149
+ }
150
+ inFlight ??= refresh().finally(() => {
151
+ inFlight = undefined;
152
+ });
153
+ const loaded = await inFlight;
154
+ if (loaded !== undefined) {
155
+ snapshot = loaded;
156
+ return loaded;
157
+ }
158
+ if (cached !== undefined && now() - cached.loadedAtMs <= maxStaleMs) {
159
+ return cached;
160
+ }
161
+ return undefined;
162
+ }
163
+
164
+ return {
165
+ async lookup(resourceUrl) {
166
+ const origin = originOf(resourceUrl);
167
+ if (origin === undefined) {
168
+ return { status: "not-listed" };
169
+ }
170
+ const loaded = await currentSnapshot();
171
+ if (loaded === undefined) {
172
+ return { status: "registry-unavailable" };
173
+ }
174
+ if (loaded.ambiguous.has(origin)) {
175
+ return { status: "ambiguous" };
176
+ }
177
+ const resource = loaded.byOrigin.get(origin);
178
+ return resource === undefined
179
+ ? { status: "not-listed" }
180
+ : { status: "resolved", resource };
181
+ },
182
+ };
183
+ }
184
+
185
+ /** Every origin the SIGNED record claims for a listing. */
186
+ export function signedOriginsOf(listing: ServiceListing): readonly string[] {
187
+ const candidates: (string | undefined)[] = [
188
+ listing.publicUrl,
189
+ listing.accessUrl,
190
+ ];
191
+ const descriptor = (
192
+ listing.signed as {
193
+ descriptor?: {
194
+ interface?: { baseUrl?: string };
195
+ callerAuth?: { accessUrl?: string };
196
+ publicUrl?: string;
197
+ };
198
+ }
199
+ ).descriptor;
200
+ if (descriptor !== undefined) {
201
+ candidates.push(
202
+ descriptor.publicUrl,
203
+ descriptor.interface?.baseUrl,
204
+ descriptor.callerAuth?.accessUrl,
205
+ );
206
+ }
207
+ const origins = new Set<string>();
208
+ for (const candidate of candidates) {
209
+ const origin = candidate === undefined ? undefined : originOf(candidate);
210
+ if (origin !== undefined) {
211
+ origins.add(origin);
212
+ }
213
+ }
214
+ return [...origins];
215
+ }
216
+
217
+ /** Normalized `scheme://host[:port]`, or `undefined` if not a usable URL. */
218
+ export function originOf(url: string): string | undefined {
219
+ try {
220
+ const parsed = new URL(url);
221
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
222
+ return undefined;
223
+ }
224
+ return `${parsed.protocol}//${parsed.host.toLowerCase()}`;
225
+ } catch {
226
+ return undefined;
227
+ }
228
+ }