@absol-labs/agent 0.9.1 → 0.10.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 (199) hide show
  1. package/README.md +77 -3
  2. package/dist/cli/amounts.d.ts +26 -0
  3. package/dist/cli/amounts.d.ts.map +1 -0
  4. package/dist/cli/amounts.js +71 -0
  5. package/dist/cli/amounts.js.map +1 -0
  6. package/dist/cli/args.d.ts +32 -0
  7. package/dist/cli/args.d.ts.map +1 -0
  8. package/dist/cli/args.js +119 -0
  9. package/dist/cli/args.js.map +1 -0
  10. package/dist/cli/attesters.d.ts +51 -0
  11. package/dist/cli/attesters.d.ts.map +1 -0
  12. package/dist/cli/attesters.js +58 -0
  13. package/dist/cli/attesters.js.map +1 -0
  14. package/dist/cli/bin.d.ts +3 -0
  15. package/dist/cli/bin.d.ts.map +1 -0
  16. package/dist/cli/bin.js +5 -0
  17. package/dist/cli/bin.js.map +1 -0
  18. package/dist/cli/commands/claim.d.ts +13 -0
  19. package/dist/cli/commands/claim.d.ts.map +1 -0
  20. package/dist/cli/commands/claim.js +86 -0
  21. package/dist/cli/commands/claim.js.map +1 -0
  22. package/dist/cli/commands/close.d.ts +3 -0
  23. package/dist/cli/commands/close.d.ts.map +1 -0
  24. package/dist/cli/commands/close.js +43 -0
  25. package/dist/cli/commands/close.js.map +1 -0
  26. package/dist/cli/commands/discover.d.ts +3 -0
  27. package/dist/cli/commands/discover.d.ts.map +1 -0
  28. package/dist/cli/commands/discover.js +183 -0
  29. package/dist/cli/commands/discover.js.map +1 -0
  30. package/dist/cli/commands/invoke.d.ts +3 -0
  31. package/dist/cli/commands/invoke.d.ts.map +1 -0
  32. package/dist/cli/commands/invoke.js +141 -0
  33. package/dist/cli/commands/invoke.js.map +1 -0
  34. package/dist/cli/commands/mcp.d.ts +3 -0
  35. package/dist/cli/commands/mcp.d.ts.map +1 -0
  36. package/dist/cli/commands/mcp.js +113 -0
  37. package/dist/cli/commands/mcp.js.map +1 -0
  38. package/dist/cli/commands/open.d.ts +3 -0
  39. package/dist/cli/commands/open.d.ts.map +1 -0
  40. package/dist/cli/commands/open.js +279 -0
  41. package/dist/cli/commands/open.js.map +1 -0
  42. package/dist/cli/commands/reclaim-unverified.d.ts +3 -0
  43. package/dist/cli/commands/reclaim-unverified.d.ts.map +1 -0
  44. package/dist/cli/commands/reclaim-unverified.js +164 -0
  45. package/dist/cli/commands/reclaim-unverified.js.map +1 -0
  46. package/dist/cli/commands/reclaim.d.ts +33 -0
  47. package/dist/cli/commands/reclaim.d.ts.map +1 -0
  48. package/dist/cli/commands/reclaim.js +297 -0
  49. package/dist/cli/commands/reclaim.js.map +1 -0
  50. package/dist/cli/commands/status.d.ts +3 -0
  51. package/dist/cli/commands/status.d.ts.map +1 -0
  52. package/dist/cli/commands/status.js +43 -0
  53. package/dist/cli/commands/status.js.map +1 -0
  54. package/dist/cli/commands/streams.d.ts +3 -0
  55. package/dist/cli/commands/streams.d.ts.map +1 -0
  56. package/dist/cli/commands/streams.js +257 -0
  57. package/dist/cli/commands/streams.js.map +1 -0
  58. package/dist/cli/commands/watch.d.ts +3 -0
  59. package/dist/cli/commands/watch.d.ts.map +1 -0
  60. package/dist/cli/commands/watch.js +55 -0
  61. package/dist/cli/commands/watch.js.map +1 -0
  62. package/dist/cli/config.d.ts +61 -0
  63. package/dist/cli/config.d.ts.map +1 -0
  64. package/dist/cli/config.js +140 -0
  65. package/dist/cli/config.js.map +1 -0
  66. package/dist/cli/context.d.ts +29 -0
  67. package/dist/cli/context.d.ts.map +1 -0
  68. package/dist/cli/context.js +2 -0
  69. package/dist/cli/context.js.map +1 -0
  70. package/dist/cli/deps.d.ts +145 -0
  71. package/dist/cli/deps.d.ts.map +1 -0
  72. package/dist/cli/deps.js +227 -0
  73. package/dist/cli/deps.js.map +1 -0
  74. package/dist/cli/descriptor.d.ts +61 -0
  75. package/dist/cli/descriptor.d.ts.map +1 -0
  76. package/dist/cli/descriptor.js +78 -0
  77. package/dist/cli/descriptor.js.map +1 -0
  78. package/dist/cli/errors.d.ts +48 -0
  79. package/dist/cli/errors.d.ts.map +1 -0
  80. package/dist/cli/errors.js +129 -0
  81. package/dist/cli/errors.js.map +1 -0
  82. package/dist/cli/escape-hatch.d.ts +50 -0
  83. package/dist/cli/escape-hatch.d.ts.map +1 -0
  84. package/dist/cli/escape-hatch.js +45 -0
  85. package/dist/cli/escape-hatch.js.map +1 -0
  86. package/dist/cli/index.d.ts +8 -0
  87. package/dist/cli/index.d.ts.map +1 -0
  88. package/dist/cli/index.js +7 -0
  89. package/dist/cli/index.js.map +1 -0
  90. package/dist/cli/listings.d.ts +15 -0
  91. package/dist/cli/listings.d.ts.map +1 -0
  92. package/dist/cli/listings.js +56 -0
  93. package/dist/cli/listings.js.map +1 -0
  94. package/dist/cli/mandate.d.ts +66 -0
  95. package/dist/cli/mandate.d.ts.map +1 -0
  96. package/dist/cli/mandate.js +61 -0
  97. package/dist/cli/mandate.js.map +1 -0
  98. package/dist/cli/output.d.ts +33 -0
  99. package/dist/cli/output.d.ts.map +1 -0
  100. package/dist/cli/output.js +34 -0
  101. package/dist/cli/output.js.map +1 -0
  102. package/dist/cli/preflight.d.ts +38 -0
  103. package/dist/cli/preflight.d.ts.map +1 -0
  104. package/dist/cli/preflight.js +72 -0
  105. package/dist/cli/preflight.js.map +1 -0
  106. package/dist/cli/progress.d.ts +36 -0
  107. package/dist/cli/progress.d.ts.map +1 -0
  108. package/dist/cli/progress.js +40 -0
  109. package/dist/cli/progress.js.map +1 -0
  110. package/dist/cli/run.d.ts +25 -0
  111. package/dist/cli/run.d.ts.map +1 -0
  112. package/dist/cli/run.js +173 -0
  113. package/dist/cli/run.js.map +1 -0
  114. package/dist/cli/scan-range.d.ts +55 -0
  115. package/dist/cli/scan-range.d.ts.map +1 -0
  116. package/dist/cli/scan-range.js +57 -0
  117. package/dist/cli/scan-range.js.map +1 -0
  118. package/dist/cli/status.d.ts +90 -0
  119. package/dist/cli/status.d.ts.map +1 -0
  120. package/dist/cli/status.js +252 -0
  121. package/dist/cli/status.js.map +1 -0
  122. package/dist/cli/timeout.d.ts +30 -0
  123. package/dist/cli/timeout.d.ts.map +1 -0
  124. package/dist/cli/timeout.js +55 -0
  125. package/dist/cli/timeout.js.map +1 -0
  126. package/dist/cli/version.d.ts +12 -0
  127. package/dist/cli/version.d.ts.map +1 -0
  128. package/dist/cli/version.js +22 -0
  129. package/dist/cli/version.js.map +1 -0
  130. package/dist/cli/wallet.d.ts +95 -0
  131. package/dist/cli/wallet.d.ts.map +1 -0
  132. package/dist/cli/wallet.js +169 -0
  133. package/dist/cli/wallet.js.map +1 -0
  134. package/dist/frameworks/crewai.d.ts +10 -0
  135. package/dist/frameworks/crewai.d.ts.map +1 -1
  136. package/dist/frameworks/crewai.js +12 -2
  137. package/dist/frameworks/crewai.js.map +1 -1
  138. package/dist/gateway/server-entry.d.ts +1 -0
  139. package/dist/gateway/server-entry.js +1 -0
  140. package/dist/gateway/server-entry.js.map +1 -1
  141. package/dist/mcp/http-server.d.ts +1 -0
  142. package/dist/mcp/http-server.js +1 -0
  143. package/dist/mcp/http-server.js.map +1 -1
  144. package/dist/mcp/server.d.ts.map +1 -1
  145. package/dist/mcp/server.js +20 -1
  146. package/dist/mcp/server.js.map +1 -1
  147. package/dist/mcp/stdio.d.ts +1 -0
  148. package/dist/mcp/stdio.js +1 -0
  149. package/dist/mcp/stdio.js.map +1 -1
  150. package/dist/sdk/client.d.ts +25 -1
  151. package/dist/sdk/client.d.ts.map +1 -1
  152. package/dist/sdk/client.js +20 -1
  153. package/dist/sdk/client.js.map +1 -1
  154. package/docs/agent-layer.md +2 -1
  155. package/docs/cli.md +569 -0
  156. package/docs/crewai.md +6 -2
  157. package/docs/eliza.md +6 -1
  158. package/docs/mcp-hosted.md +3 -2
  159. package/docs/quickstart.md +18 -5
  160. package/package.json +17 -5
  161. package/src/cli/amounts.ts +102 -0
  162. package/src/cli/args.ts +150 -0
  163. package/src/cli/attesters.ts +86 -0
  164. package/src/cli/bin.ts +5 -0
  165. package/src/cli/commands/claim.ts +100 -0
  166. package/src/cli/commands/close.ts +53 -0
  167. package/src/cli/commands/discover.ts +228 -0
  168. package/src/cli/commands/invoke.ts +177 -0
  169. package/src/cli/commands/mcp.ts +135 -0
  170. package/src/cli/commands/open.ts +331 -0
  171. package/src/cli/commands/reclaim-unverified.ts +206 -0
  172. package/src/cli/commands/reclaim.ts +360 -0
  173. package/src/cli/commands/status.ts +56 -0
  174. package/src/cli/commands/streams.ts +344 -0
  175. package/src/cli/commands/watch.ts +70 -0
  176. package/src/cli/config.ts +208 -0
  177. package/src/cli/context.ts +30 -0
  178. package/src/cli/deps.ts +437 -0
  179. package/src/cli/descriptor.ts +133 -0
  180. package/src/cli/errors.ts +162 -0
  181. package/src/cli/escape-hatch.ts +92 -0
  182. package/src/cli/index.ts +27 -0
  183. package/src/cli/listings.ts +77 -0
  184. package/src/cli/mandate.ts +126 -0
  185. package/src/cli/output.ts +63 -0
  186. package/src/cli/preflight.ts +88 -0
  187. package/src/cli/progress.ts +75 -0
  188. package/src/cli/run.ts +209 -0
  189. package/src/cli/scan-range.ts +76 -0
  190. package/src/cli/status.ts +381 -0
  191. package/src/cli/timeout.ts +64 -0
  192. package/src/cli/version.ts +21 -0
  193. package/src/cli/wallet.ts +241 -0
  194. package/src/frameworks/crewai.ts +13 -2
  195. package/src/gateway/server-entry.ts +1 -0
  196. package/src/mcp/http-server.ts +1 -0
  197. package/src/mcp/server.ts +25 -1
  198. package/src/mcp/stdio.ts +1 -0
  199. package/src/sdk/client.ts +33 -1
@@ -0,0 +1,241 @@
1
+ import { privateKeyToAccount } from "viem/accounts";
2
+ import type { Account, LocalAccount } from "viem";
3
+
4
+ import { resolveAgentWallet } from "../wallet/provider.js";
5
+ import { configError } from "./errors.js";
6
+
7
+ /**
8
+ * The ONLY place the CLI resolves a signing wallet.
9
+ *
10
+ * There is deliberately NO `--private-key` flag, and no flag for any other
11
+ * credential. A secret passed on the command line lands in shell history, in
12
+ * `ps` output, and in any CI log that echoes the command; an environment
13
+ * variable does none of those. Secrets are also never echoed back: commands
14
+ * print the derived ADDRESS only.
15
+ *
16
+ * ## Resolution order (first match wins, checked in this order)
17
+ *
18
+ * 1. `METRIK_AGENT_PRIVATE_KEY` — a raw Base Sepolia test key.
19
+ * 2. The `CDP_*` / `METRIK_AGENT_CDP_*` quartet — a Coinbase CDP Server Wallet
20
+ * v2 account, resolved through this package's own `resolveAgentWallet()`.
21
+ *
22
+ * The order matches `parseAgentWalletEnv()` (the MCP server's resolution), so
23
+ * one environment configures every surface the same way. It is checked in a
24
+ * FIXED order rather than "whichever is configured", because an environment
25
+ * carrying both is ambiguous about which address funds the stream — and that
26
+ * ambiguity is a fund-moving one. When both are present the private key wins
27
+ * and `resolveCliWallet` says so on stderr rather than silently picking.
28
+ *
29
+ * ## Why CDP is wired and Privy is not
30
+ *
31
+ * CDP is the only wallet path in this package with live CI evidence
32
+ * (`scripts/e2e-cdp.ts` drives the full journey against the real Base Sepolia
33
+ * escrow), and its account signs LOCALLY — `toAccount(cdpServerAccount)` is a
34
+ * viem local account, so it drops straight into the CLI's existing
35
+ * `http(rpcUrl)` transport with no other change.
36
+ *
37
+ * Privy does not. Both Privy adapters in `src/wallet/` produce a JSON-RPC
38
+ * account with no `signTransaction`: they sign and broadcast by sending
39
+ * `eth_sendTransaction` / `eth_signTypedData_v4` to a provider, so they need the
40
+ * wallet client's TRANSPORT to be that provider rather than a plain RPC. Wiring
41
+ * that into the CLI is not a matter of reading three more variables — it
42
+ * changes the transport for every fund-moving command, and `metrik invoke`
43
+ * additionally requires a local signer (`InvokeOptions.buyer: LocalAccount`).
44
+ * `assertLocalSigner` below therefore REFUSES a non-local account with that
45
+ * fact, instead of accepting one and failing later inside viem. See
46
+ * `docs/cli.md` § Wallet.
47
+ */
48
+ export const PRIVATE_KEY_ENV = "METRIK_AGENT_PRIVATE_KEY";
49
+
50
+ /** Every environment variable the CDP path reads. Named in errors, never printed. */
51
+ export const CDP_ENV_VARS = [
52
+ "CDP_API_KEY_ID",
53
+ "CDP_API_KEY_SECRET",
54
+ "CDP_WALLET_SECRET",
55
+ "METRIK_AGENT_CDP_OWNER_NAME",
56
+ ] as const;
57
+
58
+ /**
59
+ * What the key is for, in the words of the command that needs it. `invoke`
60
+ * signs an EIP-712 invocation capability and broadcasts NOTHING; telling its
61
+ * caller "this command signs a transaction" was simply false.
62
+ */
63
+ export type KeyPurpose = "transaction" | "capability";
64
+
65
+ const PURPOSE_TEXT: Record<KeyPurpose, string> = {
66
+ transaction: "signs a transaction",
67
+ capability:
68
+ "signs an off-chain EIP-712 invocation capability (no transaction is broadcast)",
69
+ };
70
+
71
+ /** Which environment configured the signer, for output and for error text. */
72
+ export type CliWalletSource = "private-key" | "cdp";
73
+
74
+ export interface CliWallet {
75
+ readonly source: CliWalletSource;
76
+ /**
77
+ * A LOCAL signer. Every CLI path either broadcasts through `http(rpcUrl)`
78
+ * (which cannot sign) or signs EIP-712 off-chain, so a signer that delegates
79
+ * to a remote provider is refused at resolution rather than mid-command.
80
+ */
81
+ readonly account: LocalAccount;
82
+ /** One line naming the signer's origin. Safe to print: no secret in it. */
83
+ readonly description: string;
84
+ }
85
+
86
+ const WALLET_HINT =
87
+ `export ${PRIVATE_KEY_ENV}=0x… (a funded Base Sepolia test key), or configure a Coinbase CDP wallet with ` +
88
+ `${CDP_ENV_VARS.join(", ")}. Both are read from the environment only — never pass a credential as a command-line flag.`;
89
+
90
+ /**
91
+ * Resolves the signing wallet, or throws an actionable configuration error.
92
+ *
93
+ * `note` receives one line when the environment is ambiguous (both wallet
94
+ * sources configured). It is optional so `resolveCliWallet` stays usable from a
95
+ * context with no printer; commands always pass `out.note`.
96
+ */
97
+ export async function resolveCliWallet(
98
+ env: NodeJS.ProcessEnv,
99
+ options: {
100
+ readonly purpose?: KeyPurpose;
101
+ readonly note?: (line: string) => void;
102
+ } = {},
103
+ ): Promise<CliWallet> {
104
+ const wallet = await optionalCliWallet(env, options);
105
+ if (wallet === null) {
106
+ throw configError(
107
+ `this command ${PURPOSE_TEXT[options.purpose ?? "transaction"]} and needs a wallet: neither ${PRIVATE_KEY_ENV} nor the CDP variables are set`,
108
+ WALLET_HINT,
109
+ );
110
+ }
111
+ return wallet;
112
+ }
113
+
114
+ /**
115
+ * The same resolution, but "nothing configured" is not an error. Used by
116
+ * look-before-you-leap paths (`open --dry-run`, `streams --address`) that
117
+ * broadcast nothing and must therefore work for someone who does not yet have a
118
+ * funded wallet — which is exactly who a dry run is for.
119
+ *
120
+ * A wallet that is PARTIALLY configured is still an error here: "you set three
121
+ * of the four CDP variables" and "you configured no wallet" are different
122
+ * facts, and rendering the first as the second sends the reader looking in the
123
+ * wrong place.
124
+ */
125
+ export async function optionalCliWallet(
126
+ env: NodeJS.ProcessEnv,
127
+ options: { readonly note?: (line: string) => void } = {},
128
+ ): Promise<CliWallet | null> {
129
+ const privateKeyAccount = optionalCliAccount(env);
130
+ const cdpConfigured = CDP_ENV_VARS.some((name) => isSet(env[name]));
131
+
132
+ if (privateKeyAccount !== null) {
133
+ if (cdpConfigured) {
134
+ options.note?.(
135
+ `warning: both ${PRIVATE_KEY_ENV} and CDP wallet variables are set; using ${PRIVATE_KEY_ENV} (${privateKeyAccount.address}). Unset it to sign with the CDP wallet.`,
136
+ );
137
+ }
138
+ return {
139
+ source: "private-key",
140
+ account: privateKeyAccount,
141
+ description: `${privateKeyAccount.address} (from ${PRIVATE_KEY_ENV})`,
142
+ };
143
+ }
144
+
145
+ if (!cdpConfigured) return null;
146
+
147
+ const missing = CDP_ENV_VARS.filter((name) => !isSet(env[name]));
148
+ if (missing.length > 0) {
149
+ throw configError(
150
+ `the CDP wallet is partially configured: ${missing.join(", ")} ${missing.length === 1 ? "is" : "are"} not set`,
151
+ `a CDP wallet needs all of ${CDP_ENV_VARS.join(", ")}. Set the missing one${missing.length === 1 ? "" : "s"}, or unset the others and use ${PRIVATE_KEY_ENV} instead.`,
152
+ );
153
+ }
154
+
155
+ // A CDP SMART account is an ERC-4337 contract account at a DIFFERENT address
156
+ // from its owner EOA, and it is reached through a bundler — which this CLI
157
+ // does not route to: every command broadcasts through `http(--rpc-url)`. So a
158
+ // smart-account environment would silently sign and spend from the OWNER,
159
+ // while the user funded the smart account. Refuse instead.
160
+ if (
161
+ isSet(env.METRIK_AGENT_CDP_SMART_ACCOUNT_NAME) ||
162
+ isTruthy(env.METRIK_AGENT_CDP_CREATE_SMART_ACCOUNT)
163
+ ) {
164
+ throw configError(
165
+ "the CLI cannot sign from a CDP smart account: METRIK_AGENT_CDP_CREATE_SMART_ACCOUNT / METRIK_AGENT_CDP_SMART_ACCOUNT_NAME is set",
166
+ "a CDP smart account is an ERC-4337 account at a different address from its owner, reached through a bundler; this CLI broadcasts through --rpc-url and would spend from the OWNER address instead. Unset those two variables to use the owner account directly, or use the SDK's account-abstraction path.",
167
+ );
168
+ }
169
+
170
+ const ownerName = env.METRIK_AGENT_CDP_OWNER_NAME as string;
171
+ let account: Account;
172
+ try {
173
+ const resolved = await resolveAgentWallet({
174
+ cdp: {
175
+ apiKeyId: env.CDP_API_KEY_ID as string,
176
+ apiKeySecret: env.CDP_API_KEY_SECRET as string,
177
+ walletSecret: env.CDP_WALLET_SECRET as string,
178
+ ownerName,
179
+ },
180
+ });
181
+ account = resolved.account;
182
+ } catch (error) {
183
+ // Reaching CDP is a network call against an authenticated API, so this is
184
+ // where a wrong key, a revoked key or an outage surfaces. Say which wallet
185
+ // failed; the underlying message carries the rest.
186
+ throw configError(
187
+ `could not resolve the CDP wallet "${ownerName}"`,
188
+ `check ${CDP_ENV_VARS.join(", ")}, and that '@coinbase/cdp-sdk' (an optional peer dependency) is installed. Underlying error: ${error instanceof Error ? error.message : String(error)}`,
189
+ );
190
+ }
191
+
192
+ return {
193
+ source: "cdp",
194
+ account: assertLocalSigner(account, "cdp"),
195
+ description: `${account.address} (CDP account "${ownerName}")`,
196
+ };
197
+ }
198
+
199
+ /**
200
+ * The raw `METRIK_AGENT_PRIVATE_KEY` read, kept separate so the wallet-source
201
+ * precedence above reads as one list rather than as nested conditionals.
202
+ */
203
+ export function optionalCliAccount(
204
+ env: NodeJS.ProcessEnv,
205
+ ): LocalAccount | null {
206
+ const raw = env[PRIVATE_KEY_ENV];
207
+ if (raw === undefined || raw.length === 0) return null;
208
+ if (!/^0x[0-9a-fA-F]{64}$/.test(raw)) {
209
+ throw configError(
210
+ `${PRIVATE_KEY_ENV} must be a 0x-prefixed 32-byte hex private key`,
211
+ );
212
+ }
213
+ return privateKeyToAccount(raw as `0x${string}`);
214
+ }
215
+
216
+ /**
217
+ * Every CLI signing path needs a LOCAL signer: fund-moving commands broadcast
218
+ * through `http(--rpc-url)`, which cannot sign, and `invoke` needs a
219
+ * `LocalAccount` to sign the EIP-712 capability. A JSON-RPC account (Privy's
220
+ * adapters) would type-check as an `Account` and then fail somewhere inside
221
+ * viem with a message about a missing method, so it is rejected here with the
222
+ * actual reason.
223
+ */
224
+ function assertLocalSigner(
225
+ account: Account,
226
+ source: CliWalletSource,
227
+ ): LocalAccount {
228
+ if (account.type === "local") return account;
229
+ throw configError(
230
+ `the ${source} wallet returned a ${account.type} account, which cannot sign locally`,
231
+ "the CLI broadcasts through --rpc-url and signs EIP-712 capabilities in-process, so it needs a local signer.",
232
+ );
233
+ }
234
+
235
+ function isSet(value: string | undefined): boolean {
236
+ return value !== undefined && value.length > 0;
237
+ }
238
+
239
+ function isTruthy(value: string | undefined): boolean {
240
+ return value === "true" || value === "1";
241
+ }
@@ -23,13 +23,24 @@ export interface CrewAiVerifiedStreamMcpConfigOptions {
23
23
  readonly cacheToolsList?: boolean;
24
24
  }
25
25
 
26
+ /**
27
+ * The default launcher: the `metrik-mcp` bin this package ships.
28
+ *
29
+ * It used to be `pnpm mcp:stdio`, which only resolves INSIDE this repository —
30
+ * a CrewAI user who installed `@absol-labs/agent` from npm got a config that
31
+ * could never start. `metrik-mcp` is on `PATH` wherever the package is
32
+ * installed (npm/pnpm link it into `node_modules/.bin`), and `npx` resolves it
33
+ * with no install at all.
34
+ */
35
+ export const CREWAI_DEFAULT_MCP_COMMAND = "metrik-mcp";
36
+
26
37
  export function createCrewAiVerifiedStreamMcpConfig(
27
38
  options: CrewAiVerifiedStreamMcpConfigOptions = {},
28
39
  ): CrewAiVerifiedStreamMcpConfig {
29
40
  return {
30
41
  transport: "stdio",
31
- command: options.command ?? "pnpm",
32
- args: options.args ?? ["mcp:stdio"],
42
+ command: options.command ?? CREWAI_DEFAULT_MCP_COMMAND,
43
+ args: options.args ?? [],
33
44
  env: options.env ?? {},
34
45
  allowedToolNames:
35
46
  options.allowedToolNames ?? CREWAI_VERIFIED_STREAM_TOOL_NAMES,
@@ -1,3 +1,4 @@
1
+ #!/usr/bin/env node
1
2
  import { startCallerAuthGatewayServerFromEnv } from "./http-server.js";
2
3
 
3
4
  /**
@@ -1,3 +1,4 @@
1
+ #!/usr/bin/env node
1
2
  import { startHostedMcpHttpServerFromEnv } from "./http.js";
2
3
 
3
4
  /**
package/src/mcp/server.ts CHANGED
@@ -799,10 +799,34 @@ export function createVerifiedStreamMcpServer(
799
799
  };
800
800
  }
801
801
 
802
+ /** One readable line per invalid or missing environment variable. */
803
+ function describeEnvIssues(error: z.ZodError): string {
804
+ const seen = new Set<string>();
805
+ for (const issue of error.issues) {
806
+ const name = issue.path.join(".");
807
+ seen.add(
808
+ issue.code === "invalid_type" && issue.received === "undefined"
809
+ ? `${name} is not set`
810
+ : `${name} is invalid (${issue.message})`,
811
+ );
812
+ }
813
+ return [...seen].join("; ");
814
+ }
815
+
802
816
  export function parseMetrikAgentEnv(
803
817
  env: NodeJS.ProcessEnv = process.env,
804
818
  ): ParsedMetrikAgentEnv {
805
- const parsed = metrikAgentEnvSchema.parse(env);
819
+ const result = metrikAgentEnvSchema.safeParse(env);
820
+ if (!result.success) {
821
+ // `.parse()` threw a ZodError whose message is a JSON array of issue
822
+ // objects. `metrik-mcp` is started by an MCP host and fails closed on stdio,
823
+ // so that array was the ONLY thing an operator ever saw — a correct failure
824
+ // rendered as noise. Name the variables instead.
825
+ throw new Error(
826
+ `metrik-mcp is not configured: ${describeEnvIssues(result.error)}. Set METRIK_AGENT_RPC_URL (an http(s) URL), METRIK_AGENT_ESCROW and METRIK_AGENT_USDC (EVM addresses), plus a wallet (METRIK_AGENT_PRIVATE_KEY, or the CDP/Privy variables). METRIK_AGENT_CHAIN_ID defaults to Base Sepolia (${baseSepolia.id}).`,
827
+ );
828
+ }
829
+ const parsed = result.data;
806
830
  const chainId = Number(parsed.METRIK_AGENT_CHAIN_ID ?? baseSepolia.id);
807
831
  if (!Number.isInteger(chainId) || chainId <= 0) {
808
832
  throw new Error("METRIK_AGENT_CHAIN_ID must be a positive integer");
package/src/mcp/stdio.ts CHANGED
@@ -1,3 +1,4 @@
1
+ #!/usr/bin/env node
1
2
  import { startVerifiedStreamMcpServerStdio } from "./server.js";
2
3
 
3
4
  startVerifiedStreamMcpServerStdio().catch((error) => {
package/src/sdk/client.ts CHANGED
@@ -89,6 +89,16 @@ export interface AgentSdkClient {
89
89
  claim(streamId: `0x${string}`): Promise<StreamProofTransactionResult>;
90
90
  close(streamId: `0x${string}`): Promise<StreamProofTransactionResult>;
91
91
  reclaim(streamId: `0x${string}`): Promise<StreamProofTransactionResult>;
92
+ /**
93
+ * The BUYER's no-proof escape hatch. Every other buyer path to the money
94
+ * (`reclaim` -> `reclaimWithProof`) needs a Merkle proof fetched from the
95
+ * oracle, so it is only as available as the oracle is. `reclaimUnverified`
96
+ * takes no proof, no quorum and no guardian — it is the path that exists
97
+ * precisely for the case where that dependency has failed.
98
+ */
99
+ reclaimUnverified(
100
+ streamId: `0x${string}`,
101
+ ): Promise<StreamProofTransactionResult>;
92
102
  }
93
103
 
94
104
  export type AgentSdkClientFactory = (
@@ -201,7 +211,29 @@ export class VerifiedStreamAgentClient implements VerifiedStreamAgentOpener {
201
211
  }
202
212
 
203
213
  /**
204
- * Authorizes a BUYER-RECOVERY action (close / reclaim). These only return
214
+ * Buyer-only escape hatch: reclaim WITHOUT a checkpoint proof, available only
215
+ * once `block.timestamp >= expiresAt + escapeGraceSeconds` on the escrow.
216
+ *
217
+ * Authorized exactly like `close` / `reclaim` — on the mandate owner's
218
+ * signature alone (see {@link authorizeBuyerRecovery}) — because it only
219
+ * returns the buyer's own unspent escrow. It is a SEPARATE method rather than
220
+ * an option on {@link reclaimStream} because it is not an alternative route to
221
+ * the same outcome: `reclaimUnverified` pays the buyer
222
+ * `deposit - claimedCumulative` and permanently forfeits every operator
223
+ * voucher that was earned but not yet redeemed on-chain.
224
+ *
225
+ * Takes no `closeFirst`: the escrow gates this path on `expiresAt`, never on
226
+ * `closedAt`, so closing first would buy nothing but a second gas fee.
227
+ */
228
+ async reclaimUnverifiedStream(
229
+ input: MandateAuthorizedStreamActionInput,
230
+ ): Promise<StreamProofTransactionResult> {
231
+ await this.authorizeBuyerRecovery(input);
232
+ return await this.sdkClient.reclaimUnverified(input.streamId);
233
+ }
234
+
235
+ /**
236
+ * Authorizes a BUYER-RECOVERY action (close / reclaim / reclaimUnverified). These only return
205
237
  * unspent funds to the buyer or stop payment, so they are authorized on
206
238
  * mandate-owner signature ALONE — never gated by expiry, revocation, or caps,
207
239
  * which would otherwise let a lapsed mandate strand the buyer's own funds.