@teneo-protocol/sdk 2.0.0 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (329) hide show
  1. package/.eslintrc.json +11 -2
  2. package/.github/CODEOWNERS +2 -0
  3. package/.github/ISSUE_TEMPLATE/01-bug.yml +85 -0
  4. package/.github/ISSUE_TEMPLATE/config.yml +8 -0
  5. package/.github/ISSUE_TEMPLATE/feature_request.md +22 -0
  6. package/.github/workflows/claude-code-review.yml +8 -3
  7. package/.github/workflows/claude-reviewer.yml +6 -4
  8. package/.github/workflows/publish-npm.yml +1 -0
  9. package/.github/workflows/push-to-main.yml +1 -1
  10. package/.github/workflows/top-issue.yml +102 -0
  11. package/CHANGELOG.md +69 -0
  12. package/CONCEPTS.md +747 -0
  13. package/README.md +178 -8
  14. package/dist/constants.js +8 -8
  15. package/dist/constants.js.map +1 -1
  16. package/dist/core/websocket-client.d.ts +15 -3
  17. package/dist/core/websocket-client.d.ts.map +1 -1
  18. package/dist/core/websocket-client.js +52 -13
  19. package/dist/core/websocket-client.js.map +1 -1
  20. package/dist/formatters/response-formatter.js +4 -0
  21. package/dist/formatters/response-formatter.js.map +1 -1
  22. package/dist/handlers/message-handler-registry.js +2 -1
  23. package/dist/handlers/message-handler-registry.js.map +1 -1
  24. package/dist/handlers/message-handlers/agent-details-response-handler.d.ts +1666 -0
  25. package/dist/handlers/message-handlers/agent-details-response-handler.d.ts.map +1 -0
  26. package/dist/handlers/message-handlers/agent-details-response-handler.js +38 -0
  27. package/dist/handlers/message-handlers/agent-details-response-handler.js.map +1 -0
  28. package/dist/handlers/message-handlers/agent-room-operation-response-handler.d.ts.map +1 -1
  29. package/dist/handlers/message-handlers/agent-room-operation-response-handler.js +2 -5
  30. package/dist/handlers/message-handlers/agent-room-operation-response-handler.js.map +1 -1
  31. package/dist/handlers/message-handlers/agent-selected-handler.js +2 -5
  32. package/dist/handlers/message-handlers/agent-selected-handler.js.map +1 -1
  33. package/dist/handlers/message-handlers/agent-status-update-handler.d.ts +783 -0
  34. package/dist/handlers/message-handlers/agent-status-update-handler.d.ts.map +1 -1
  35. package/dist/handlers/message-handlers/agent-status-update-handler.js +2 -5
  36. package/dist/handlers/message-handlers/agent-status-update-handler.js.map +1 -1
  37. package/dist/handlers/message-handlers/agents-list-handler.js +2 -5
  38. package/dist/handlers/message-handlers/agents-list-handler.js.map +1 -1
  39. package/dist/handlers/message-handlers/all-agents-response-handler.d.ts +439 -0
  40. package/dist/handlers/message-handlers/all-agents-response-handler.d.ts.map +1 -0
  41. package/dist/handlers/message-handlers/all-agents-response-handler.js +36 -0
  42. package/dist/handlers/message-handlers/all-agents-response-handler.js.map +1 -0
  43. package/dist/handlers/message-handlers/auth-error-handler.js +2 -5
  44. package/dist/handlers/message-handlers/auth-error-handler.js.map +1 -1
  45. package/dist/handlers/message-handlers/auth-message-handler.d.ts.map +1 -1
  46. package/dist/handlers/message-handlers/auth-message-handler.js +6 -5
  47. package/dist/handlers/message-handlers/auth-message-handler.js.map +1 -1
  48. package/dist/handlers/message-handlers/auth-required-handler.js +2 -5
  49. package/dist/handlers/message-handlers/auth-required-handler.js.map +1 -1
  50. package/dist/handlers/message-handlers/auth-success-handler.d.ts.map +1 -1
  51. package/dist/handlers/message-handlers/auth-success-handler.js +6 -5
  52. package/dist/handlers/message-handlers/auth-success-handler.js.map +1 -1
  53. package/dist/handlers/message-handlers/base-handler.d.ts +4 -4
  54. package/dist/handlers/message-handlers/base-handler.d.ts.map +1 -1
  55. package/dist/handlers/message-handlers/base-handler.js +3 -1
  56. package/dist/handlers/message-handlers/base-handler.js.map +1 -1
  57. package/dist/handlers/message-handlers/challenge-handler.js +3 -2
  58. package/dist/handlers/message-handlers/challenge-handler.js.map +1 -1
  59. package/dist/handlers/message-handlers/error-message-handler.js +2 -5
  60. package/dist/handlers/message-handlers/error-message-handler.js.map +1 -1
  61. package/dist/handlers/message-handlers/index.d.ts +6 -0
  62. package/dist/handlers/message-handlers/index.d.ts.map +1 -1
  63. package/dist/handlers/message-handlers/index.js +33 -1
  64. package/dist/handlers/message-handlers/index.js.map +1 -1
  65. package/dist/handlers/message-handlers/list-available-agents-handler.d.ts +783 -0
  66. package/dist/handlers/message-handlers/list-available-agents-handler.d.ts.map +1 -1
  67. package/dist/handlers/message-handlers/list-available-agents-handler.js +2 -5
  68. package/dist/handlers/message-handlers/list-available-agents-handler.js.map +1 -1
  69. package/dist/handlers/message-handlers/list-room-agents-handler.d.ts +783 -0
  70. package/dist/handlers/message-handlers/list-room-agents-handler.d.ts.map +1 -1
  71. package/dist/handlers/message-handlers/list-room-agents-handler.js +2 -5
  72. package/dist/handlers/message-handlers/list-room-agents-handler.js.map +1 -1
  73. package/dist/handlers/message-handlers/list-rooms-response-handler.d.ts +2 -199
  74. package/dist/handlers/message-handlers/list-rooms-response-handler.d.ts.map +1 -1
  75. package/dist/handlers/message-handlers/list-rooms-response-handler.js +4 -6
  76. package/dist/handlers/message-handlers/list-rooms-response-handler.js.map +1 -1
  77. package/dist/handlers/message-handlers/ping-pong-handler.js +4 -10
  78. package/dist/handlers/message-handlers/ping-pong-handler.js.map +1 -1
  79. package/dist/handlers/message-handlers/rate-limit-notification-handler.d.ts +94 -0
  80. package/dist/handlers/message-handlers/rate-limit-notification-handler.d.ts.map +1 -0
  81. package/dist/handlers/message-handlers/rate-limit-notification-handler.js +35 -0
  82. package/dist/handlers/message-handlers/rate-limit-notification-handler.js.map +1 -0
  83. package/dist/handlers/message-handlers/regular-message-handler.d.ts.map +1 -1
  84. package/dist/handlers/message-handlers/regular-message-handler.js +4 -6
  85. package/dist/handlers/message-handlers/regular-message-handler.js.map +1 -1
  86. package/dist/handlers/message-handlers/room-operation-response-handler.d.ts.map +1 -1
  87. package/dist/handlers/message-handlers/room-operation-response-handler.js +2 -5
  88. package/dist/handlers/message-handlers/room-operation-response-handler.js.map +1 -1
  89. package/dist/handlers/message-handlers/subscribe-response-handler.d.ts.map +1 -1
  90. package/dist/handlers/message-handlers/subscribe-response-handler.js +4 -6
  91. package/dist/handlers/message-handlers/subscribe-response-handler.js.map +1 -1
  92. package/dist/handlers/message-handlers/task-quote-handler.d.ts +14 -0
  93. package/dist/handlers/message-handlers/task-quote-handler.d.ts.map +1 -0
  94. package/dist/handlers/message-handlers/task-quote-handler.js +29 -0
  95. package/dist/handlers/message-handlers/task-quote-handler.js.map +1 -0
  96. package/dist/handlers/message-handlers/task-response-handler.js +2 -5
  97. package/dist/handlers/message-handlers/task-response-handler.js.map +1 -1
  98. package/dist/handlers/message-handlers/types.d.ts +21 -9
  99. package/dist/handlers/message-handlers/types.d.ts.map +1 -1
  100. package/dist/handlers/message-handlers/unsubscribe-response-handler.d.ts.map +1 -1
  101. package/dist/handlers/message-handlers/unsubscribe-response-handler.js +4 -6
  102. package/dist/handlers/message-handlers/unsubscribe-response-handler.js.map +1 -1
  103. package/dist/handlers/message-handlers/user-authenticated-handler.d.ts +40 -0
  104. package/dist/handlers/message-handlers/user-authenticated-handler.d.ts.map +1 -0
  105. package/dist/handlers/message-handlers/user-authenticated-handler.js +28 -0
  106. package/dist/handlers/message-handlers/user-authenticated-handler.js.map +1 -0
  107. package/dist/handlers/message-handlers/user-count-handler.d.ts +49 -0
  108. package/dist/handlers/message-handlers/user-count-handler.d.ts.map +1 -0
  109. package/dist/handlers/message-handlers/user-count-handler.js +31 -0
  110. package/dist/handlers/message-handlers/user-count-handler.js.map +1 -0
  111. package/dist/handlers/webhook-handler.d.ts +1 -1
  112. package/dist/handlers/webhook-handler.d.ts.map +1 -1
  113. package/dist/handlers/webhook-handler.js +14 -5
  114. package/dist/handlers/webhook-handler.js.map +1 -1
  115. package/dist/index.d.ts +6 -1
  116. package/dist/index.d.ts.map +1 -1
  117. package/dist/index.js +19 -2
  118. package/dist/index.js.map +1 -1
  119. package/dist/managers/admin-manager.d.ts +116 -0
  120. package/dist/managers/admin-manager.d.ts.map +1 -0
  121. package/dist/managers/admin-manager.js +169 -0
  122. package/dist/managers/admin-manager.js.map +1 -0
  123. package/dist/managers/agent-registry.d.ts +52 -1
  124. package/dist/managers/agent-registry.d.ts.map +1 -1
  125. package/dist/managers/agent-registry.js +145 -6
  126. package/dist/managers/agent-registry.js.map +1 -1
  127. package/dist/managers/agent-room-manager.d.ts +1 -7
  128. package/dist/managers/agent-room-manager.d.ts.map +1 -1
  129. package/dist/managers/agent-room-manager.js +83 -36
  130. package/dist/managers/agent-room-manager.js.map +1 -1
  131. package/dist/managers/connection-manager.js +2 -0
  132. package/dist/managers/connection-manager.js.map +1 -1
  133. package/dist/managers/index.d.ts +2 -1
  134. package/dist/managers/index.d.ts.map +1 -1
  135. package/dist/managers/index.js +3 -1
  136. package/dist/managers/index.js.map +1 -1
  137. package/dist/managers/message-router.d.ts +56 -5
  138. package/dist/managers/message-router.d.ts.map +1 -1
  139. package/dist/managers/message-router.js +155 -8
  140. package/dist/managers/message-router.js.map +1 -1
  141. package/dist/managers/room-management-manager.d.ts.map +1 -1
  142. package/dist/managers/room-management-manager.js +9 -7
  143. package/dist/managers/room-management-manager.js.map +1 -1
  144. package/dist/managers/room-manager.d.ts +7 -5
  145. package/dist/managers/room-manager.d.ts.map +1 -1
  146. package/dist/managers/room-manager.js +14 -10
  147. package/dist/managers/room-manager.js.map +1 -1
  148. package/dist/payments/index.d.ts +5 -0
  149. package/dist/payments/index.d.ts.map +1 -0
  150. package/dist/payments/index.js +21 -0
  151. package/dist/payments/index.js.map +1 -0
  152. package/dist/payments/payment-client.d.ts +74 -0
  153. package/dist/payments/payment-client.d.ts.map +1 -0
  154. package/dist/payments/payment-client.js +207 -0
  155. package/dist/payments/payment-client.js.map +1 -0
  156. package/dist/teneo-sdk.d.ts +135 -21
  157. package/dist/teneo-sdk.d.ts.map +1 -1
  158. package/dist/teneo-sdk.js +268 -26
  159. package/dist/teneo-sdk.js.map +1 -1
  160. package/dist/types/categories.d.ts +22 -0
  161. package/dist/types/categories.d.ts.map +1 -0
  162. package/dist/types/categories.js +40 -0
  163. package/dist/types/categories.js.map +1 -0
  164. package/dist/types/config.d.ts +79 -8
  165. package/dist/types/config.d.ts.map +1 -1
  166. package/dist/types/config.js +62 -5
  167. package/dist/types/config.js.map +1 -1
  168. package/dist/types/error-codes.d.ts +8 -0
  169. package/dist/types/error-codes.d.ts.map +1 -1
  170. package/dist/types/error-codes.js +9 -0
  171. package/dist/types/error-codes.js.map +1 -1
  172. package/dist/types/events.d.ts +44 -0
  173. package/dist/types/events.d.ts.map +1 -1
  174. package/dist/types/events.js +19 -1
  175. package/dist/types/events.js.map +1 -1
  176. package/dist/types/index.d.ts +3 -2
  177. package/dist/types/index.d.ts.map +1 -1
  178. package/dist/types/index.js +33 -3
  179. package/dist/types/index.js.map +1 -1
  180. package/dist/types/messages.d.ts +11219 -423
  181. package/dist/types/messages.d.ts.map +1 -1
  182. package/dist/types/messages.js +249 -7
  183. package/dist/types/messages.js.map +1 -1
  184. package/dist/utils/bounded-queue.d.ts.map +1 -1
  185. package/dist/utils/bounded-queue.js +5 -2
  186. package/dist/utils/bounded-queue.js.map +1 -1
  187. package/dist/utils/circuit-breaker.js +11 -4
  188. package/dist/utils/circuit-breaker.js.map +1 -1
  189. package/dist/utils/deduplication-cache.js +3 -1
  190. package/dist/utils/deduplication-cache.js.map +1 -1
  191. package/dist/utils/event-waiter.d.ts +3 -3
  192. package/dist/utils/event-waiter.d.ts.map +1 -1
  193. package/dist/utils/index.d.ts +1 -0
  194. package/dist/utils/index.d.ts.map +1 -1
  195. package/dist/utils/index.js +4 -1
  196. package/dist/utils/index.js.map +1 -1
  197. package/dist/utils/logger.js.map +1 -1
  198. package/dist/utils/pricing-resolver.d.ts +26 -0
  199. package/dist/utils/pricing-resolver.d.ts.map +1 -0
  200. package/dist/utils/pricing-resolver.js +85 -0
  201. package/dist/utils/pricing-resolver.js.map +1 -0
  202. package/dist/utils/rate-limiter.d.ts.map +1 -1
  203. package/dist/utils/rate-limiter.js +6 -0
  204. package/dist/utils/rate-limiter.js.map +1 -1
  205. package/dist/utils/retry-policy.js +1 -0
  206. package/dist/utils/retry-policy.js.map +1 -1
  207. package/dist/utils/secure-private-key.js +3 -1
  208. package/dist/utils/secure-private-key.js.map +1 -1
  209. package/dist/utils/signature-verifier.d.ts.map +1 -1
  210. package/dist/utils/signature-verifier.js +3 -1
  211. package/dist/utils/signature-verifier.js.map +1 -1
  212. package/examples/.env.example +1 -1
  213. package/examples/agent-room-management-example.ts +10 -9
  214. package/examples/basic-usage.ts +3 -4
  215. package/examples/claude-agent-x-follower/.env.example +1 -1
  216. package/examples/claude-agent-x-follower/QUICKSTART.md +2 -2
  217. package/examples/claude-agent-x-follower/README.md +2 -2
  218. package/examples/claude-agent-x-follower/index.ts +120 -96
  219. package/examples/n8n-teneo/.env.example +1 -1
  220. package/examples/n8n-teneo/README.md +1 -1
  221. package/examples/n8n-teneo/index.ts +54 -44
  222. package/examples/nestjs-dashboard/.env.example +11 -0
  223. package/examples/nestjs-dashboard/README.md +297 -0
  224. package/examples/nestjs-dashboard/nest-cli.json +10 -0
  225. package/examples/nestjs-dashboard/package.json +44 -0
  226. package/examples/nestjs-dashboard/pnpm-lock.yaml +3079 -0
  227. package/examples/nestjs-dashboard/src/app.controller.ts +24 -0
  228. package/examples/nestjs-dashboard/src/app.module.ts +15 -0
  229. package/examples/nestjs-dashboard/src/main.ts +32 -0
  230. package/examples/nestjs-dashboard/src/public/dashboard.html +1144 -0
  231. package/examples/nestjs-dashboard/src/teneo/agents.controller.ts +54 -0
  232. package/examples/nestjs-dashboard/src/teneo/events.controller.ts +65 -0
  233. package/examples/nestjs-dashboard/src/teneo/messages.controller.ts +47 -0
  234. package/examples/nestjs-dashboard/src/teneo/rooms.controller.ts +258 -0
  235. package/examples/nestjs-dashboard/src/teneo/teneo.module.ts +13 -0
  236. package/examples/nestjs-dashboard/src/teneo/teneo.service.ts +484 -0
  237. package/examples/nestjs-dashboard/tsconfig.json +22 -0
  238. package/examples/openai-teneo/.env.example +1 -1
  239. package/examples/openai-teneo/README.md +2 -2
  240. package/examples/openai-teneo/index.ts +82 -71
  241. package/examples/production-dashboard/.env.example +1 -1
  242. package/examples/production-dashboard/README.md +1 -1
  243. package/examples/production-dashboard/server.ts +2 -2
  244. package/examples/room-management-example.ts +5 -8
  245. package/examples/usage/.env.example +1 -1
  246. package/examples/usage/01-connect.ts +3 -4
  247. package/examples/usage/02-list-agents.ts +2 -3
  248. package/examples/usage/03-pick-agent.ts +2 -3
  249. package/examples/usage/04-find-by-capability.ts +2 -3
  250. package/examples/usage/05-webhook-example.ts +2 -3
  251. package/examples/usage/06-simple-api-server.ts +2 -3
  252. package/examples/usage/07-event-listener.ts +2 -3
  253. package/examples/webhook-integration.ts +1 -1
  254. package/examples/x-influencer-battle-server.ts +2 -2
  255. package/package.json +4 -1
  256. package/src/core/websocket-client.test.ts +8 -3
  257. package/src/core/websocket-client.ts +36 -6
  258. package/src/formatters/response-formatter.test.ts +2 -0
  259. package/src/formatters/response-formatter.ts +3 -3
  260. package/src/handlers/message-handlers/agent-details-response-handler.ts +42 -0
  261. package/src/handlers/message-handlers/agent-room-operation-response-handler.ts +2 -8
  262. package/src/handlers/message-handlers/agent-status-update-handler.ts +3 -9
  263. package/src/handlers/message-handlers/all-agents-response-handler.ts +39 -0
  264. package/src/handlers/message-handlers/auth-message-handler.ts +5 -0
  265. package/src/handlers/message-handlers/auth-success-handler.ts +6 -1
  266. package/src/handlers/message-handlers/base-handler.ts +20 -7
  267. package/src/handlers/message-handlers/index.ts +34 -0
  268. package/src/handlers/message-handlers/list-room-agents-handler.ts +2 -5
  269. package/src/handlers/message-handlers/list-rooms-response-handler.ts +4 -2
  270. package/src/handlers/message-handlers/rate-limit-notification-handler.ts +45 -0
  271. package/src/handlers/message-handlers/regular-message-handler.ts +3 -2
  272. package/src/handlers/message-handlers/room-operation-response-handler.ts +3 -6
  273. package/src/handlers/message-handlers/subscribe-response-handler.ts +12 -2
  274. package/src/handlers/message-handlers/task-quote-handler.ts +31 -0
  275. package/src/handlers/message-handlers/types.ts +37 -9
  276. package/src/handlers/message-handlers/unsubscribe-response-handler.ts +12 -2
  277. package/src/handlers/message-handlers/user-authenticated-handler.ts +31 -0
  278. package/src/handlers/message-handlers/user-count-handler.ts +34 -0
  279. package/src/handlers/webhook-handler.test.ts +3 -2
  280. package/src/handlers/webhook-handler.ts +13 -7
  281. package/src/index.ts +21 -0
  282. package/src/managers/admin-manager.ts +249 -0
  283. package/src/managers/agent-registry.test.ts +2 -1
  284. package/src/managers/agent-registry.ts +170 -2
  285. package/src/managers/agent-room-manager.ts +98 -42
  286. package/src/managers/index.ts +13 -1
  287. package/src/managers/message-router.ts +215 -17
  288. package/src/managers/room-management-manager.ts +4 -7
  289. package/src/managers/room-manager.ts +11 -15
  290. package/src/payments/index.ts +22 -0
  291. package/src/payments/payment-client.ts +240 -0
  292. package/src/teneo-sdk.ts +302 -27
  293. package/src/types/categories.ts +45 -0
  294. package/src/types/config.ts +70 -2
  295. package/src/types/error-codes.ts +10 -0
  296. package/src/types/events.test.ts +1 -0
  297. package/src/types/events.ts +43 -0
  298. package/src/types/index.ts +56 -0
  299. package/src/types/messages.test.ts +2 -1
  300. package/src/types/messages.ts +307 -5
  301. package/src/utils/bounded-queue.test.ts +1 -1
  302. package/src/utils/bounded-queue.ts +2 -1
  303. package/src/utils/circuit-breaker.test.ts +1 -1
  304. package/src/utils/deduplication-cache.test.ts +1 -1
  305. package/src/utils/event-waiter.test.ts +1 -1
  306. package/src/utils/event-waiter.ts +3 -3
  307. package/src/utils/index.ts +7 -0
  308. package/src/utils/logger.ts +8 -8
  309. package/src/utils/pricing-resolver.ts +128 -0
  310. package/src/utils/rate-limiter.test.ts +1 -1
  311. package/src/utils/rate-limiter.ts +1 -0
  312. package/src/utils/signature-verifier.test.ts +2 -2
  313. package/src/utils/signature-verifier.ts +3 -2
  314. package/tests/.env.example +7 -0
  315. package/tests/direct-agent-test.ts +151 -0
  316. package/tests/integration/real-server.test.ts +2 -0
  317. package/tests/integration/room-management.test.ts +10 -8
  318. package/tests/integration/websocket.test.ts +4 -1
  319. package/tests/payment-flow-test.ts +147 -0
  320. package/tests/unit/handlers/agent-room-operation-response-handler.test.ts +17 -29
  321. package/tests/unit/handlers/agent-status-update-handler.test.ts +2 -6
  322. package/tests/unit/handlers/auth-success-handler-rooms.test.ts +1 -3
  323. package/tests/unit/handlers/list-available-agents-handler.test.ts +4 -12
  324. package/tests/unit/handlers/list-room-agents-handler.test.ts +2 -6
  325. package/tests/unit/handlers/room-operation-response-handler.test.ts +9 -36
  326. package/tests/unit/managers/agent-room-manager.test.ts +9 -16
  327. package/tests/unit/managers/room-management-manager.test.ts +21 -39
  328. package/tsconfig.json +2 -2
  329. package/vitest.config.ts +1 -0
@@ -1,6 +1,7 @@
1
1
  /**
2
2
  * MessageRouter - Manages message sending and routing
3
3
  * Handles user messages, direct commands, and message-response patterns
4
+ * Supports quote-approve payment flow (v2.2.0)
4
5
  */
5
6
 
6
7
  import { EventEmitter } from "eventemitter3";
@@ -11,11 +12,15 @@ import { ResponseFormatter, FormattedResponse } from "../formatters/response-for
11
12
  import {
12
13
  UserMessage,
13
14
  createUserMessage,
15
+ createRequestTask,
16
+ createConfirmTask,
14
17
  Logger,
15
18
  ResponseFormat,
16
- TaskResponseMessage
19
+ TaskResponseMessage,
20
+ TaskQuoteMessage,
21
+ PricingInfo
17
22
  } from "../types";
18
- import { SDKEvents, SDKError, ValidationError, AgentResponse } from "../types/events";
23
+ import { SDKEvents, SDKError, ValidationError, AgentResponse, PaymentError } from "../types/events";
19
24
  import { ErrorCode } from "../types/error-codes";
20
25
  import { TIMEOUTS } from "../constants";
21
26
  import {
@@ -24,6 +29,8 @@ import {
24
29
  AgentCommandContentSchema
25
30
  } from "../types/validation";
26
31
  import { waitForEvent } from "../utils/event-waiter";
32
+ import { PaymentClient, buildX402ResourceUrl } from "../payments/payment-client";
33
+ import type { SecurePrivateKey } from "../utils/secure-private-key";
27
34
 
28
35
  export interface SendMessageOptions {
29
36
  room: string;
@@ -39,6 +46,30 @@ export interface AgentCommand {
39
46
  room: string;
40
47
  }
41
48
 
49
+ /**
50
+ * Result of a quote request, containing pricing and agent details.
51
+ */
52
+ export interface QuoteResult {
53
+ taskId: string;
54
+ agentId: string;
55
+ agentName: string;
56
+ agentWallet: string;
57
+ command: string;
58
+ pricing: PricingInfo;
59
+ expiresAt: Date;
60
+ }
61
+
62
+ export interface MessageRouterConfig {
63
+ messageTimeout?: number;
64
+ responseFormat?: ResponseFormat;
65
+ autoApproveQuotes?: boolean;
66
+ maxPricePerRequest?: number;
67
+ quoteTimeout?: number;
68
+ wsUrl?: string;
69
+ paymentNetwork?: string;
70
+ paymentAsset?: string;
71
+ }
72
+
42
73
  export class MessageRouter extends EventEmitter<SDKEvents> {
43
74
  private readonly wsClient: WebSocketClient;
44
75
  private readonly webhookHandler: WebhookHandler;
@@ -47,15 +78,22 @@ export class MessageRouter extends EventEmitter<SDKEvents> {
47
78
  private readonly messageTimeout: number;
48
79
  private readonly responseFormat: ResponseFormat;
49
80
 
81
+ // Quote-approve flow (v2.2.0)
82
+ private paymentClient: PaymentClient | null = null;
83
+ private readonly pendingQuotes: Map<string, QuoteResult> = new Map();
84
+ private readonly autoApproveQuotes: boolean;
85
+ private readonly maxPricePerRequest?: number;
86
+ private readonly quoteTimeout: number;
87
+ private readonly wsUrl: string;
88
+ private readonly paymentNetwork: string;
89
+ private readonly paymentAsset: string;
90
+
50
91
  constructor(
51
92
  wsClient: WebSocketClient,
52
93
  webhookHandler: WebhookHandler,
53
94
  responseFormatter: ResponseFormatter,
54
95
  logger: Logger,
55
- config: {
56
- messageTimeout?: number;
57
- responseFormat?: ResponseFormat;
58
- }
96
+ config: MessageRouterConfig
59
97
  ) {
60
98
  super();
61
99
  this.wsClient = wsClient;
@@ -65,9 +103,29 @@ export class MessageRouter extends EventEmitter<SDKEvents> {
65
103
  this.messageTimeout = config.messageTimeout ?? TIMEOUTS.DEFAULT_MESSAGE_TIMEOUT;
66
104
  this.responseFormat = config.responseFormat ?? "humanized";
67
105
 
106
+ // Quote-approve config (v2.2.0)
107
+ this.autoApproveQuotes = config.autoApproveQuotes ?? true;
108
+ this.maxPricePerRequest = config.maxPricePerRequest;
109
+ this.quoteTimeout = config.quoteTimeout ?? 30000;
110
+ this.wsUrl = config.wsUrl ?? "";
111
+ this.paymentNetwork = config.paymentNetwork ?? "eip155:3338";
112
+ // USDC contract address on PEAQ network
113
+ this.paymentAsset = config.paymentAsset ?? "0xbbA60da06c2c5424f03f7434542280FCAd453d10";
114
+
68
115
  this.setupEventForwarding();
69
116
  }
70
117
 
118
+ /**
119
+ * Sets up the payment client for quote-approve flow.
120
+ * Must be called before using requestQuote/confirmQuote with paid tasks.
121
+ */
122
+ public setPaymentClient(secureKey: SecurePrivateKey, walletAddress: string): void {
123
+ this.paymentClient = new PaymentClient(secureKey, walletAddress, {
124
+ network: this.paymentNetwork,
125
+ asset: this.paymentAsset
126
+ });
127
+ }
128
+
71
129
  /**
72
130
  * Sends a message to agents via the coordinator.
73
131
  * The coordinator intelligently selects the most appropriate agent.
@@ -107,21 +165,31 @@ export class MessageRouter extends EventEmitter<SDKEvents> {
107
165
  throw new SDKError("Not connected to Teneo network", ErrorCode.NOT_CONNECTED);
108
166
  }
109
167
 
110
- // Validate content
111
168
  const validatedContent = MessageContentSchema.parse(content);
112
-
113
169
  const room = options.room;
114
170
  if (!room) {
115
171
  throw new ValidationError("Room parameter is required");
116
172
  }
117
173
 
118
- // Use custom 'from' address if provided, otherwise use wallet address from auth state
174
+ // Use quote-approve flow with auto-approval (v2.2.0)
175
+ if (this.autoApproveQuotes) {
176
+ this.logger.debug("MessageRouter: Using quote-approve flow", {
177
+ content: validatedContent,
178
+ room
179
+ });
180
+ const quote = await this.requestQuote(validatedContent, room);
181
+ return await this.confirmQuote(quote.taskId, {
182
+ waitForResponse: options.waitForResponse,
183
+ timeout: options.timeout ?? this.messageTimeout
184
+ });
185
+ }
186
+
187
+ // Legacy flow (auto-approval disabled - user must manually confirm quotes)
119
188
  const authState = this.wsClient.getAuthState();
120
189
  const fromAddress = options.from ?? authState.walletAddress;
121
-
122
190
  const message = createUserMessage(validatedContent, room, fromAddress);
123
191
 
124
- this.logger.debug("MessageRouter: Sending message", {
192
+ this.logger.debug("MessageRouter: Sending message (legacy)", {
125
193
  content: validatedContent,
126
194
  room,
127
195
  from: fromAddress
@@ -164,7 +232,6 @@ export class MessageRouter extends EventEmitter<SDKEvents> {
164
232
  throw new SDKError("Not connected to Teneo network", ErrorCode.NOT_CONNECTED);
165
233
  }
166
234
 
167
- // Validate command
168
235
  const validatedAgent = AgentIdSchema.parse(command.agent);
169
236
  const validatedCommand = AgentCommandContentSchema.parse(command.command);
170
237
 
@@ -173,15 +240,24 @@ export class MessageRouter extends EventEmitter<SDKEvents> {
173
240
  throw new ValidationError("Room parameter is required");
174
241
  }
175
242
 
176
- // Get wallet address from auth state
243
+ const content = `@${validatedAgent} ${validatedCommand}`;
244
+
245
+ // Use quote-approve flow with auto-approval (v2.2.0)
246
+ if (this.autoApproveQuotes) {
247
+ this.logger.debug("MessageRouter: Using quote-approve flow", { content, room });
248
+ const quote = await this.requestQuote(content, room);
249
+ return await this.confirmQuote(quote.taskId, {
250
+ waitForResponse,
251
+ timeout: this.messageTimeout
252
+ });
253
+ }
254
+
255
+ // Legacy flow (auto-approval disabled - user must manually confirm quotes)
177
256
  const authState = this.wsClient.getAuthState();
178
257
  const walletAddress = authState.walletAddress;
179
-
180
- // Format as direct command
181
- const content = `@${validatedAgent} ${validatedCommand}`;
182
258
  const message = createUserMessage(content, room, walletAddress);
183
259
 
184
- this.logger.debug("MessageRouter: Sending direct command", {
260
+ this.logger.debug("MessageRouter: Sending direct command (legacy)", {
185
261
  agent: validatedAgent,
186
262
  command: validatedCommand,
187
263
  room,
@@ -204,6 +280,128 @@ export class MessageRouter extends EventEmitter<SDKEvents> {
204
280
  }
205
281
  }
206
282
 
283
+ /**
284
+ * Requests a quote for a task without auto-approval.
285
+ * Returns the quote data for manual confirmation.
286
+ */
287
+ public async requestQuote(content: string, room: string): Promise<QuoteResult> {
288
+ if (!this.wsClient.isConnected) {
289
+ throw new SDKError("Not connected to Teneo network", ErrorCode.NOT_CONNECTED);
290
+ }
291
+
292
+ const message = createRequestTask(content, room);
293
+ this.logger.debug("MessageRouter: Requesting quote", { content, room });
294
+
295
+ await this.wsClient.sendMessage(message);
296
+
297
+ const quote = await waitForEvent<TaskQuoteMessage>(this.wsClient, "quote:received", {
298
+ timeout: this.quoteTimeout,
299
+ timeoutMessage: `Quote request timed out after ${this.quoteTimeout}ms`
300
+ });
301
+
302
+ const result: QuoteResult = {
303
+ taskId: quote.data.task_id,
304
+ agentId: quote.data.agent_id,
305
+ agentName: quote.data.agent_name,
306
+ agentWallet: quote.data.agent_wallet,
307
+ command: quote.data.command,
308
+ pricing: quote.data.pricing,
309
+ expiresAt: new Date(quote.data.expires_at)
310
+ };
311
+
312
+ this.pendingQuotes.set(result.taskId, result);
313
+ return result;
314
+ }
315
+
316
+ /**
317
+ * Confirms a quote and executes the task with payment.
318
+ * Can optionally wait for the task response.
319
+ */
320
+ public async confirmQuote(
321
+ taskId: string,
322
+ options?: { waitForResponse?: boolean; timeout?: number }
323
+ ): Promise<FormattedResponse | void> {
324
+ if (!this.wsClient.isConnected) {
325
+ throw new SDKError("Not connected to Teneo network", ErrorCode.NOT_CONNECTED);
326
+ }
327
+
328
+ const quote = this.pendingQuotes.get(taskId);
329
+ if (!quote) {
330
+ throw new ValidationError(`No pending quote found for task ${taskId}`);
331
+ }
332
+
333
+ if (new Date() > quote.expiresAt) {
334
+ this.pendingQuotes.delete(taskId);
335
+ throw new SDKError("Quote has expired", ErrorCode.QUOTE_EXPIRED);
336
+ }
337
+
338
+ // Check price limit
339
+ if (this.maxPricePerRequest !== undefined) {
340
+ const priceInUnits = quote.pricing.pricePerUnit * 1_000_000;
341
+ if (priceInUnits > this.maxPricePerRequest) {
342
+ this.emit("payment:blocked", {
343
+ agentId: quote.agentId,
344
+ agentPrice: priceInUnits,
345
+ maxPrice: this.maxPricePerRequest
346
+ });
347
+ throw new PaymentError(
348
+ `Quote price exceeds limit: ${priceInUnits} > ${this.maxPricePerRequest}`,
349
+ ErrorCode.PRICE_LIMIT_EXCEEDED,
350
+ { agentId: quote.agentId, agentPrice: priceInUnits, maxPrice: this.maxPricePerRequest }
351
+ );
352
+ }
353
+ }
354
+
355
+ let paymentHeader: string | undefined;
356
+
357
+ // Create payment header if payment client is configured and price > 0
358
+ if (this.paymentClient && quote.pricing.pricePerUnit > 0) {
359
+ try {
360
+ paymentHeader = await this.paymentClient.createPaymentHeader(
361
+ quote.pricing.pricePerUnit * 1_000_000,
362
+ quote.agentWallet,
363
+ buildX402ResourceUrl(this.wsUrl)
364
+ );
365
+ this.emit("payment:attached", {
366
+ agentId: quote.agentId,
367
+ amount: quote.pricing.pricePerUnit * 1_000_000,
368
+ command: quote.command
369
+ });
370
+ } catch (error) {
371
+ this.logger.error("Failed to create payment header for quote confirmation", error);
372
+ throw new PaymentError("Failed to create payment", ErrorCode.PAYMENT_FAILED, {
373
+ agentId: quote.agentId
374
+ });
375
+ }
376
+ }
377
+
378
+ const confirmMessage = createConfirmTask(taskId, paymentHeader);
379
+
380
+ this.logger.debug("MessageRouter: Confirming quote", { taskId });
381
+
382
+ await this.wsClient.sendMessage(confirmMessage);
383
+
384
+ // Delete quote only after successful send (enables retry on network failure)
385
+ this.pendingQuotes.delete(taskId);
386
+
387
+ if (options?.waitForResponse) {
388
+ const timeout = options.timeout ?? this.messageTimeout;
389
+ const response = await waitForEvent<AgentResponse>(this.wsClient, "agent:response", {
390
+ timeout,
391
+ filter: (r) => r.taskId === taskId,
392
+ timeoutMessage: `Task response timed out after ${timeout}ms (taskId: ${taskId})`
393
+ });
394
+ return response as FormattedResponse;
395
+ }
396
+ }
397
+
398
+ /**
399
+ * Gets a pending quote by task ID.
400
+ */
401
+ public getPendingQuote(taskId: string): QuoteResult | undefined {
402
+ return this.pendingQuotes.get(taskId);
403
+ }
404
+
207
405
  /**
208
406
  * Send message and wait for agent response
209
407
  * Uses event-waiter utility for clean Promise-based waiting with automatic cleanup
@@ -58,7 +58,7 @@ export class RoomManagementManager extends EventEmitter<SDKEvents> {
58
58
  */
59
59
  public async createRoom(options: CreateRoomOptions): Promise<RoomInfo> {
60
60
  if (!this.wsClient.isConnected) {
61
- throw new SDKError("Not connected to Teneo network", ErrorCode.NOT_CONNECTED);
61
+ throw new SDKError("Not connected to Teneo Protocol", ErrorCode.NOT_CONNECTED);
62
62
  }
63
63
 
64
64
  // Validate inputs
@@ -135,7 +135,7 @@ export class RoomManagementManager extends EventEmitter<SDKEvents> {
135
135
  */
136
136
  public async updateRoom(roomId: string, updates: UpdateRoomOptions): Promise<RoomInfo> {
137
137
  if (!this.wsClient.isConnected) {
138
- throw new SDKError("Not connected to Teneo network", ErrorCode.NOT_CONNECTED);
138
+ throw new SDKError("Not connected to Teneo Protocol", ErrorCode.NOT_CONNECTED);
139
139
  }
140
140
 
141
141
  // Verify user owns room
@@ -224,7 +224,7 @@ export class RoomManagementManager extends EventEmitter<SDKEvents> {
224
224
  */
225
225
  public async deleteRoom(roomId: string): Promise<void> {
226
226
  if (!this.wsClient.isConnected) {
227
- throw new SDKError("Not connected to Teneo network", ErrorCode.NOT_CONNECTED);
227
+ throw new SDKError("Not connected to Teneo Protocol", ErrorCode.NOT_CONNECTED);
228
228
  }
229
229
 
230
230
  // Verify user owns room
@@ -505,10 +505,7 @@ export class RoomManagementManager extends EventEmitter<SDKEvents> {
505
505
  throw new SDKError("Room name cannot be empty", ErrorCode.VALIDATION_ERROR);
506
506
  }
507
507
  if (name.length > 100) {
508
- throw new SDKError(
509
- "Room name too long (max 100 characters)",
510
- ErrorCode.VALIDATION_ERROR
511
- );
508
+ throw new SDKError("Room name too long (max 100 characters)", ErrorCode.VALIDATION_ERROR);
512
509
  }
513
510
  }
514
511
 
@@ -5,13 +5,7 @@
5
5
 
6
6
  import { EventEmitter } from "eventemitter3";
7
7
  import { WebSocketClient } from "../core/websocket-client";
8
- import {
9
- createSubscribe,
10
- createUnsubscribe,
11
- createListRooms,
12
- Logger,
13
- RoomInfo
14
- } from "../types";
8
+ import { createSubscribe, createUnsubscribe, createListRooms, Logger, RoomInfo } from "../types";
15
9
  import { SDKEvents, SDKError } from "../types/events";
16
10
  import { ErrorCode } from "../types/error-codes";
17
11
  import { RoomIdSchema } from "../types/validation";
@@ -29,7 +23,7 @@ export class RoomManager extends EventEmitter<SDKEvents> {
29
23
  }
30
24
 
31
25
  /**
32
- * Subscribes to a public room in the Teneo network.
26
+ * Subscribes to a public room in the Teneo Protocol.
33
27
  * Validates room ID and sends subscribe message to the server.
34
28
  * The actual subscription state is updated when the server confirms via room:subscribed event.
35
29
  *
@@ -46,7 +40,7 @@ export class RoomManager extends EventEmitter<SDKEvents> {
46
40
  */
47
41
  public async subscribeToRoom(roomId: string): Promise<void> {
48
42
  if (!this.wsClient.isConnected) {
49
- throw new SDKError("Not connected to Teneo network", ErrorCode.NOT_CONNECTED);
43
+ throw new SDKError("Not connected to Teneo Protocol", ErrorCode.NOT_CONNECTED);
50
44
  }
51
45
 
52
46
  // Validate room ID
@@ -59,24 +53,26 @@ export class RoomManager extends EventEmitter<SDKEvents> {
59
53
  }
60
54
 
61
55
  /**
62
- * Unsubscribes from a room in the Teneo network.
56
+ * Unsubscribes from a public room in the Teneo Protocol.
63
57
  * Validates room ID and sends unsubscribe message to the server.
64
58
  * The actual subscription state is updated when the server confirms via room:unsubscribed event.
65
59
  *
66
- * @param roomId - The ID of the room to unsubscribe from
60
+ * Note: This only applies to public rooms. Private rooms cannot be unsubscribed from.
61
+ *
62
+ * @param roomId - The ID of the public room to unsubscribe from
67
63
  * @returns Promise that resolves when unsubscribed
68
64
  * @throws {SDKError} If not connected to the network
69
65
  * @throws {ValidationError} If roomId is empty or invalid
70
66
  *
71
67
  * @example
72
68
  * ```typescript
73
- * await roomManager.unsubscribeFromRoom('general');
74
- * console.log('Unsubscription request sent for general room');
69
+ * await roomManager.unsubscribeFromRoom('public-announcements');
70
+ * console.log('Unsubscription request sent for public room');
75
71
  * ```
76
72
  */
77
73
  public async unsubscribeFromRoom(roomId: string): Promise<void> {
78
74
  if (!this.wsClient.isConnected) {
79
- throw new SDKError("Not connected to Teneo network", ErrorCode.NOT_CONNECTED);
75
+ throw new SDKError("Not connected to Teneo Protocol", ErrorCode.NOT_CONNECTED);
80
76
  }
81
77
 
82
78
  // Validate room ID
@@ -185,7 +181,7 @@ export class RoomManager extends EventEmitter<SDKEvents> {
185
181
  */
186
182
  public async listRooms(): Promise<RoomInfo[]> {
187
183
  if (!this.wsClient.isConnected) {
188
- throw new SDKError("Not connected to Teneo network", ErrorCode.NOT_CONNECTED);
184
+ throw new SDKError("Not connected to Teneo Protocol", ErrorCode.NOT_CONNECTED);
189
185
  }
190
186
 
191
187
  this.logger.info("RoomManager: Listing rooms");
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Payment module exports for quote-approve flow (v2.2.0)
3
+ */
4
+
5
+ export {
6
+ PaymentClient,
7
+ type PaymentClientConfig,
8
+ // Constants
9
+ USDC_CONTRACT,
10
+ PEAQ_CHAIN_ID,
11
+ USDC_DECIMALS,
12
+ X402_VERSION,
13
+ DEFAULT_PAYMENT_TIMEOUT_SECONDS,
14
+ DEFAULT_PAY_TO_ADDRESS,
15
+ DEFAULT_RPC_URL,
16
+ // Utilities
17
+ buildX402ResourceUrl,
18
+ usdcToUnits,
19
+ unitsToUsdc,
20
+ // Types
21
+ type SupportedChain
22
+ } from "./payment-client";
@@ -0,0 +1,240 @@
1
+ /**
2
+ * PaymentClient - Handles x402 payment header generation for quote-approve flow
3
+ *
4
+ * Creates x402 V2 payment headers for USDC payments on PEAQ network.
5
+ * Implements ERC-3009 TransferWithAuthorization signing using viem's EIP-712.
6
+ */
7
+
8
+ import { defineChain, type Hex, toHex } from "viem";
9
+ import { privateKeyToAccount, signTypedData } from "viem/accounts";
10
+ import type { SecurePrivateKey } from "../utils/secure-private-key";
11
+
12
+ // PEAQ chain definition
13
+ const peaq = defineChain({
14
+ id: 3338,
15
+ name: "peaq",
16
+ network: "peaq",
17
+ nativeCurrency: {
18
+ decimals: 18,
19
+ name: "PEAQ",
20
+ symbol: "PEAQ"
21
+ },
22
+ rpcUrls: {
23
+ default: {
24
+ http: ["https://peaq.api.onfinality.io/public"]
25
+ },
26
+ public: {
27
+ http: ["https://peaq.api.onfinality.io/public"]
28
+ }
29
+ }
30
+ });
31
+
32
+ // USDC contract on PEAQ
33
+ const USDC_CONTRACT = "0xbbA60da06c2c5424f03f7434542280FCAd453d10";
34
+ const PEAQ_NETWORK_CAIP2 = "eip155:3338";
35
+
36
+ // ERC-3009 TransferWithAuthorization EIP-712 types
37
+ const ERC3009_TYPES = {
38
+ TransferWithAuthorization: [
39
+ { name: "from", type: "address" },
40
+ { name: "to", type: "address" },
41
+ { name: "value", type: "uint256" },
42
+ { name: "validAfter", type: "uint256" },
43
+ { name: "validBefore", type: "uint256" },
44
+ { name: "nonce", type: "bytes32" }
45
+ ]
46
+ } as const;
47
+
48
+ export interface PaymentClientConfig {
49
+ network?: string; // CAIP-2 format, default: "eip155:3338"
50
+ asset?: string; // Asset contract, default: USDC on PEAQ
51
+ resourceUrl?: string; // x402 resource URL
52
+ }
53
+
54
+ // x402 constants
55
+ export const USDC_DECIMALS = 6;
56
+ export const X402_VERSION = 2;
57
+ export const DEFAULT_PAYMENT_TIMEOUT_SECONDS = 60;
58
+ export const DEFAULT_PAY_TO_ADDRESS = "0x0000000000000000000000000000000000000000";
59
+ export const DEFAULT_RPC_URL = "https://peaq.api.onfinality.io/public";
60
+ export type SupportedChain = "peaq";
61
+
62
+ // Re-export constants for external use
63
+ export { USDC_CONTRACT, PEAQ_NETWORK_CAIP2 as PEAQ_CHAIN_ID };
64
+
65
+ /**
66
+ * Converts a WebSocket URL to an HTTP(S) URL for x402 resource specification.
67
+ * The x402 protocol requires HTTP URLs, not WebSocket URLs.
68
+ *
69
+ * @param wsUrl - WebSocket URL (wss:// or ws://)
70
+ * @returns HTTP URL (https:// or http://)
71
+ *
72
+ * @example
73
+ * buildX402ResourceUrl("wss://api.teneo.com/ws") // "https://api.teneo.com/x402"
74
+ */
75
+ export function buildX402ResourceUrl(wsUrl: string): string {
76
+ const httpUrl = wsUrl.replace(/^wss:\/\//, "https://").replace(/^ws:\/\//, "http://");
77
+
78
+ // Replace /ws endpoint with /x402, or append /x402 if URL doesn't end with /ws
79
+ return /\/ws\/?$/.test(httpUrl)
80
+ ? httpUrl.replace(/\/ws\/?$/, "/x402")
81
+ : httpUrl.replace(/\/?$/, "/x402");
82
+ }
83
+
84
+ /**
85
+ * Converts USDC human-readable amount to micro-units (6 decimals).
86
+ * @param usdc - Amount in USDC (e.g., 1.5)
87
+ * @returns Amount in micro-units (e.g., 1500000)
88
+ */
89
+ export function usdcToUnits(usdc: number): number {
90
+ return Math.round(usdc * 10 ** USDC_DECIMALS);
91
+ }
92
+
93
+ /**
94
+ * Converts micro-units to USDC human-readable amount.
95
+ * @param units - Amount in micro-units (e.g., 1500000)
96
+ * @returns Amount in USDC (e.g., 1.5)
97
+ */
98
+ export function unitsToUsdc(units: number): number {
99
+ return units / 10 ** USDC_DECIMALS;
100
+ }
101
+
102
+ /**
103
+ * Generate a random 32-byte nonce as hex string
104
+ */
105
+ function generateNonce(): Hex {
106
+ const randomBytes = new Uint8Array(32);
107
+ crypto.getRandomValues(randomBytes);
108
+ return toHex(randomBytes);
109
+ }
110
+
111
+ /**
112
+ * PaymentClient handles creation of x402 V2 payment headers
113
+ * for the quote-approve payment flow.
114
+ */
115
+ export class PaymentClient {
116
+ private readonly secureKey: SecurePrivateKey;
117
+ private readonly walletAddress: string;
118
+ private readonly network: string;
119
+ private readonly asset: string;
120
+ private readonly resourceUrl: string;
121
+
122
+ constructor(
123
+ secureKey: SecurePrivateKey,
124
+ walletAddress: string,
125
+ config: PaymentClientConfig = {}
126
+ ) {
127
+ this.secureKey = secureKey;
128
+ this.walletAddress = walletAddress;
129
+ this.network = config.network ?? PEAQ_NETWORK_CAIP2;
130
+ this.asset = config.asset ?? USDC_CONTRACT;
131
+ this.resourceUrl = config.resourceUrl ?? "";
132
+ }
133
+
134
+ /**
135
+ * Creates an x402 V2 payment header for a specific amount and recipient.
136
+ *
137
+ * @param amountMicroUnits - Amount in micro-units (e.g., 1000 = 0.001 USDC)
138
+ * @param recipientAddress - Wallet address of the payment recipient (agent)
139
+ * @param resourceUrl - Optional override for x402 resource URL
140
+ * @returns Base64 encoded x402 V2 payment header
141
+ */
142
+ async createPaymentHeader(
143
+ amountMicroUnits: number,
144
+ recipientAddress: string,
145
+ resourceUrl?: string
146
+ ): Promise<string> {
147
+ const resource = resourceUrl || this.resourceUrl || this.getDefaultResourceUrl();
148
+ const amountStr = Math.round(amountMicroUnits).toString();
149
+
150
+ // Create payment header using the secure key
151
+ const header = await this.secureKey.use(async (privateKey) => {
152
+ const account = privateKeyToAccount(privateKey as `0x${string}`);
153
+
154
+ // Time bounds for the authorization (valid for 60 seconds)
155
+ const now = Math.floor(Date.now() / 1000);
156
+ const validAfter = now - 60; // Valid from 60 seconds ago
157
+ const validBefore = now + 60; // Valid until 60 seconds from now
158
+ const nonce = generateNonce();
159
+
160
+ // EIP-712 domain for USDC on PEAQ
161
+ const domain = {
162
+ name: "USDC",
163
+ version: "2",
164
+ chainId: BigInt(peaq.id),
165
+ verifyingContract: this.asset as `0x${string}`
166
+ };
167
+
168
+ // ERC-3009 TransferWithAuthorization message
169
+ const message = {
170
+ from: account.address,
171
+ to: recipientAddress as `0x${string}`,
172
+ value: BigInt(amountStr),
173
+ validAfter: BigInt(validAfter),
174
+ validBefore: BigInt(validBefore),
175
+ nonce: nonce
176
+ };
177
+
178
+ // Sign the EIP-712 typed data
179
+ const signature = await signTypedData({
180
+ privateKey: privateKey as `0x${string}`,
181
+ domain,
182
+ types: ERC3009_TYPES,
183
+ primaryType: "TransferWithAuthorization",
184
+ message
185
+ });
186
+
187
+ // Build V1 payload (authorization data)
188
+ const v1Payload = {
189
+ authorization: {
190
+ from: account.address,
191
+ to: recipientAddress,
192
+ value: amountStr,
193
+ validAfter: validAfter.toString(),
194
+ validBefore: validBefore.toString(),
195
+ nonce: nonce
196
+ },
197
+ signature: signature
198
+ };
199
+
200
+ // Build V2 payload (what the backend expects)
201
+ const v2Payload = {
202
+ x402Version: 2,
203
+ resource: {
204
+ url: resource,
205
+ description: "Teneo SDK payment",
206
+ mimeType: "application/json"
207
+ },
208
+ accepted: {
209
+ scheme: "exact",
210
+ network: this.network,
211
+ amount: amountStr,
212
+ asset: this.asset,
213
+ payTo: recipientAddress,
214
+ maxTimeoutSeconds: 60,
215
+ extra: { name: "USDC", version: "2" }
216
+ },
217
+ payload: v1Payload
218
+ };
219
+
220
+ return Buffer.from(JSON.stringify(v2Payload)).toString("base64");
221
+ });
222
+
223
+ return header;
224
+ }
225
+
226
+ /**
227
+ * Get default x402 resource URL from WebSocket URL
228
+ */
229
+ private getDefaultResourceUrl(): string {
230
+ // Default fallback
231
+ return "https://dev-rooms-websocket-ai-core-o9fmb.ondigitalocean.app/x402";
232
+ }
233
+
234
+ /**
235
+ * Gets the wallet address associated with this payment client.
236
+ */
237
+ get address(): string {
238
+ return this.walletAddress;
239
+ }
240
+ }