@datalayer/agent-runtimes 1.3.114 → 1.5.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 (192) hide show
  1. package/dist-embed/chunks/ChatView-CaBuPTyG.js +32 -0
  2. package/dist-embed/chunks/{DeckComponent-CuRWiARr.js → DeckComponent-cRJJMGgt.js} +1 -1
  3. package/dist-embed/chunks/{DocumentView-BYU2HREd.js → DocumentView-CiqN9ze6.js} +1 -1
  4. package/dist-embed/chunks/{EphemeralDocument-BjFSuQKH.js → EphemeralDocument-BjJfn7J-.js} +1 -1
  5. package/dist-embed/chunks/{ExcalidrawComponent-CFLnMPnf.js → ExcalidrawComponent-BtNXWUN8.js} +1 -1
  6. package/dist-embed/chunks/{ExcalidrawModal-CB9d5Vpk.js → ExcalidrawModal-C6SEKFlD.js} +1 -1
  7. package/dist-embed/chunks/{JupyterLexicalExtension-CYnZrelh.js → JupyterLexicalExtension-BuRLImy5.js} +5 -5
  8. package/dist-embed/chunks/{NotebookToolSurfaces-D62q-lbb.js → NotebookToolSurfaces-ktcGRm7d.js} +1 -1
  9. package/dist-embed/chunks/{Output-2KxoKu5F.js → Output-BscOPLqE.js} +10 -10
  10. package/dist-embed/chunks/{YouTubePlugin-CjODyxZ2.js → YouTubePlugin-xBjNr2Iu.js} +1 -1
  11. package/dist-embed/chunks/{ar-SA-G6X2FPQ2-C-zAYgA-.js → ar-SA-G6X2FPQ2-BB82iKLz.js} +1 -1
  12. package/dist-embed/chunks/{az-AZ-76LH7QW2-BHq5js6U.js → az-AZ-76LH7QW2-DsMRKXlw.js} +1 -1
  13. package/dist-embed/chunks/{bg-BG-XCXSNQG7-BTcNNcTc.js → bg-BG-XCXSNQG7-43Y1kPxa.js} +1 -1
  14. package/dist-embed/chunks/{bn-BD-2XOGV67Q-DXRk27u1.js → bn-BD-2XOGV67Q-CLqPJBll.js} +1 -1
  15. package/dist-embed/chunks/{ca-ES-6MX7JW3Y-D-0J9rqp.js → ca-ES-6MX7JW3Y-CDFGSEh3.js} +1 -1
  16. package/dist-embed/chunks/{cs-CZ-2BRQDIVT-CgN3I-57.js → cs-CZ-2BRQDIVT-ByNB8eZd.js} +1 -1
  17. package/dist-embed/chunks/{da-DK-5WZEPLOC-CHQloSoX.js → da-DK-5WZEPLOC-B7MrVwy8.js} +1 -1
  18. package/dist-embed/chunks/{de-DE-XR44H4JA-C-Hq84pe.js → de-DE-XR44H4JA-CN1cdk94.js} +1 -1
  19. package/dist-embed/chunks/{el-GR-BZB4AONW-MTzGcSsP.js → el-GR-BZB4AONW-DnaGhWD1.js} +1 -1
  20. package/dist-embed/chunks/{es-ES-U4NZUMDT-Thj-4Rwn.js → es-ES-U4NZUMDT-DE1vZzDE.js} +1 -1
  21. package/dist-embed/chunks/{eu-ES-A7QVB2H4-BEEyNQo-.js → eu-ES-A7QVB2H4-DmHB0zpm.js} +1 -1
  22. package/dist-embed/chunks/{fa-IR-HGAKTJCU-CtUGmNBZ.js → fa-IR-HGAKTJCU-CNzE8tJj.js} +1 -1
  23. package/dist-embed/chunks/{fi-FI-Z5N7JZ37-6GMgpkSu.js → fi-FI-Z5N7JZ37-Ca2vTFQC.js} +1 -1
  24. package/dist-embed/chunks/{fr-FR-RHASNOE6-BakNCbxj.js → fr-FR-RHASNOE6-B0yWFX3_.js} +1 -1
  25. package/dist-embed/chunks/{gl-ES-HMX3MZ6V-Cwkb9OPk.js → gl-ES-HMX3MZ6V-2_3FMVJq.js} +1 -1
  26. package/dist-embed/chunks/{he-IL-6SHJWFNN-BaMmvCkr.js → he-IL-6SHJWFNN-BW1lby00.js} +1 -1
  27. package/dist-embed/chunks/{hi-IN-IWLTKZ5I-1aWewhep.js → hi-IN-IWLTKZ5I-DTixfSwB.js} +1 -1
  28. package/dist-embed/chunks/{hu-HU-A5ZG7DT2-BIHI23BQ.js → hu-HU-A5ZG7DT2-DOfYwLWP.js} +1 -1
  29. package/dist-embed/chunks/{id-ID-SAP4L64H-CYpgmx8X.js → id-ID-SAP4L64H-Cwtdtkvg.js} +1 -1
  30. package/dist-embed/chunks/{index-BhSMzuKq.js → index--qMT5k1q.js} +1 -1
  31. package/dist-embed/chunks/{index-b1yzCiTq.js → index-B-UP9DlM.js} +1 -1
  32. package/dist-embed/chunks/{index-DMKsCn-y.js → index-BAwsCLGT.js} +1 -1
  33. package/dist-embed/chunks/{index-BUhqZVtg.js → index-BITOMOWN.js} +1 -1
  34. package/dist-embed/chunks/{index-1wTrAXI1.js → index-B_9UD0bt.js} +2 -2
  35. package/dist-embed/chunks/{index-DtlUttQj.js → index-BgFbP5P0.js} +1 -1
  36. package/dist-embed/chunks/{index-CC-Ht79i.js → index-CMUTS_sA.js} +1 -1
  37. package/dist-embed/chunks/{index-B7nmHsEo.js → index-Cgf7tlW3.js} +1 -1
  38. package/dist-embed/chunks/{index-CsZ2HycN.js → index-CrNfUmIU.js} +1 -1
  39. package/dist-embed/chunks/{index-DIOcQUrz.js → index-CzuLxFBK.js} +1 -1
  40. package/dist-embed/chunks/{index-EmZm7_bH.js → index-DEn5R18M.js} +1 -1
  41. package/dist-embed/chunks/{index-DeVZ-nmF.js → index-DFEBhySA.js} +1 -1
  42. package/dist-embed/chunks/{index-CMYyiALV.js → index-DYkwi0aX.js} +1 -1
  43. package/dist-embed/chunks/{index-Fzz2KjFM.js → index-DvpdAlVH.js} +1 -1
  44. package/dist-embed/chunks/{index-W7TVoWP5.js → index-Dyh5xopp.js} +1 -1
  45. package/dist-embed/chunks/{index-en9Yf1Yz.js → index-Jt4bWLoz.js} +1 -1
  46. package/dist-embed/chunks/{index-BbucLMre.js → index-S-sjhoD3.js} +1 -1
  47. package/dist-embed/chunks/{index-D18sf241.js → index-TRLGqZaF.js} +1 -1
  48. package/dist-embed/chunks/{index-ZZnjmFwm.js → index-YcqCoWJl.js} +1 -1
  49. package/dist-embed/chunks/{index-BFLtEBuP.js → index-sQy5cmWo.js} +1 -1
  50. package/dist-embed/chunks/{it-IT-JPQ66NNP-CLcqplyX.js → it-IT-JPQ66NNP-C3xZ61-6.js} +1 -1
  51. package/dist-embed/chunks/{ja-JP-DBVTYXUO-u8Qe6tR7.js → ja-JP-DBVTYXUO-CeTmglEb.js} +1 -1
  52. package/dist-embed/chunks/{kaa-6HZHGXH3-BG2kx50I.js → kaa-6HZHGXH3-D6Jr6B6Y.js} +1 -1
  53. package/dist-embed/chunks/{kab-KAB-ZGHBKWFO-CSYUpRxd.js → kab-KAB-ZGHBKWFO-pEXBqeuG.js} +1 -1
  54. package/dist-embed/chunks/{kk-KZ-P5N5QNE5-CpBMgHAz.js → kk-KZ-P5N5QNE5-BRJunmHm.js} +1 -1
  55. package/dist-embed/chunks/{km-KH-HSX4SM5Z-CoMUUQMb.js → km-KH-HSX4SM5Z--GJgT9rN.js} +1 -1
  56. package/dist-embed/chunks/{ko-KR-MTYHY66A-DwxY8CGI.js → ko-KR-MTYHY66A-Bjnez4qR.js} +1 -1
  57. package/dist-embed/chunks/{ku-TR-6OUDTVRD-UoNYUfm0.js → ku-TR-6OUDTVRD-DgOdYJrS.js} +1 -1
  58. package/dist-embed/chunks/{lt-LT-XHIRWOB4-Dr4uXaEj.js → lt-LT-XHIRWOB4-BMwhrtjM.js} +1 -1
  59. package/dist-embed/chunks/{lv-LV-5QDEKY6T-CNC56J-_.js → lv-LV-5QDEKY6T-0pxW7YE0.js} +1 -1
  60. package/dist-embed/chunks/{mr-IN-CRQNXWMA-BKWa9nzZ.js → mr-IN-CRQNXWMA-tndp8BXC.js} +1 -1
  61. package/dist-embed/chunks/{my-MM-5M5IBNSE-bwAzOWhV.js → my-MM-5M5IBNSE-sispDhO4.js} +1 -1
  62. package/dist-embed/chunks/{nb-NO-T6EIAALU-CkpsChor.js → nb-NO-T6EIAALU-CjBurOuS.js} +1 -1
  63. package/dist-embed/chunks/{nl-NL-IS3SIHDZ-COVk4rlb.js → nl-NL-IS3SIHDZ-B58EVmr3.js} +1 -1
  64. package/dist-embed/chunks/{nn-NO-6E72VCQL-DlyFF-C1.js → nn-NO-6E72VCQL-D1p7LZSq.js} +1 -1
  65. package/dist-embed/chunks/{oc-FR-POXYY2M6-CFDosV2C.js → oc-FR-POXYY2M6-C1M8sddP.js} +1 -1
  66. package/dist-embed/chunks/{pa-IN-N4M65BXN-BKRB3O0U.js → pa-IN-N4M65BXN-D8ESzASl.js} +1 -1
  67. package/dist-embed/chunks/{percentages-BXMCSKIN-D7pnqUsZ.js → percentages-BXMCSKIN-BihMTsFR.js} +3 -3
  68. package/dist-embed/chunks/{pl-PL-T2D74RX3-C5r2Tds7.js → pl-PL-T2D74RX3-DQGe6LHC.js} +1 -1
  69. package/dist-embed/chunks/{pt-BR-5N22H2LF-CDnT98bW.js → pt-BR-5N22H2LF-CvXBgZQs.js} +1 -1
  70. package/dist-embed/chunks/{pt-PT-UZXXM6DQ-C6c4SPk1.js → pt-PT-UZXXM6DQ-BMnnlIUJ.js} +1 -1
  71. package/dist-embed/chunks/{ro-RO-JPDTUUEW-CW6pbzEZ.js → ro-RO-JPDTUUEW-DZ-9UUH7.js} +1 -1
  72. package/dist-embed/chunks/{ru-RU-B4JR7IUQ-CbaAJG0w.js → ru-RU-B4JR7IUQ-JlRBwjob.js} +1 -1
  73. package/dist-embed/chunks/{si-LK-N5RQ5JYF-BZkUjpXF.js → si-LK-N5RQ5JYF-Bx8kHnkd.js} +1 -1
  74. package/dist-embed/chunks/{sk-SK-C5VTKIMK-DsfWgpPU.js → sk-SK-C5VTKIMK-xsfEfbZb.js} +1 -1
  75. package/dist-embed/chunks/{sl-SI-NN7IZMDC-Kx0FqxKU.js → sl-SI-NN7IZMDC-SQYUECnr.js} +1 -1
  76. package/dist-embed/chunks/{subset-shared.chunk-DM3l9OuH.js → subset-shared.chunk-Ch6j8nKC.js} +1 -1
  77. package/dist-embed/chunks/{subset-worker.chunk-4w4yibm3.js → subset-worker.chunk-pJjck5pR.js} +1 -1
  78. package/dist-embed/chunks/{sv-SE-XGPEYMSR-DY0ryO-L.js → sv-SE-XGPEYMSR-BxcjSHE2.js} +1 -1
  79. package/dist-embed/chunks/{ta-IN-2NMHFXQM-CWpIs0i0.js → ta-IN-2NMHFXQM-rItSkDUS.js} +1 -1
  80. package/dist-embed/chunks/{th-TH-HPSO5L25-Cwkcfb3u.js → th-TH-HPSO5L25-B7UzESFj.js} +1 -1
  81. package/dist-embed/chunks/{tr-TR-DEFEU3FU-BqJZZl-Y.js → tr-TR-DEFEU3FU-Ci6oNfEX.js} +1 -1
  82. package/dist-embed/chunks/{uk-UA-QMV73CPH-Dg213mG4.js → uk-UA-QMV73CPH-C9csqvO6.js} +1 -1
  83. package/dist-embed/chunks/{vi-VN-M7AON7JQ-DY94NKxF.js → vi-VN-M7AON7JQ-DRAj2kZj.js} +1 -1
  84. package/dist-embed/chunks/{zh-CN-LNUGB5OW-B6I9A7sA.js → zh-CN-LNUGB5OW-DLOyW8hH.js} +1 -1
  85. package/dist-embed/chunks/{zh-HK-E62DVLB3-D3oqgDOt.js → zh-HK-E62DVLB3-DwC6_2Ws.js} +1 -1
  86. package/dist-embed/chunks/{zh-TW-RAJ6MFWO-CYwgY0Jo.js → zh-TW-RAJ6MFWO-DEMugIwa.js} +1 -1
  87. package/dist-embed/datalayer-app-main.js +156 -156
  88. package/lib/apps/apps/appspec.d.ts +13 -0
  89. package/lib/apps/apps/appspec.js +93 -0
  90. package/lib/apps/apps/checks.d.ts +1 -1
  91. package/lib/apps/apps/checks.js +2 -2
  92. package/lib/apps/apps/customComponents.js +1 -1
  93. package/lib/apps/core/canvasBlocks.d.ts +3 -3
  94. package/lib/apps/core/canvasParts.d.ts +6 -4
  95. package/lib/apps/core/index.d.ts +8 -0
  96. package/lib/apps/embed/AppEmbed.js +13 -20
  97. package/lib/apps/embed/embedSession.d.ts +8 -4
  98. package/lib/apps/embed/embedSession.js +16 -9
  99. package/lib/apps/embed/hostBridge.d.ts +47 -13
  100. package/lib/apps/embed/hostBridge.js +135 -18
  101. package/lib/apps/index.d.ts +1 -1
  102. package/lib/apps/index.js +1 -1
  103. package/lib/apps/plugins/a2ui-surface/AnswerShows.d.ts +21 -0
  104. package/lib/apps/plugins/a2ui-surface/AnswerShows.js +29 -0
  105. package/lib/apps/plugins/a2ui-surface/SurfaceToolResult.js +4 -7
  106. package/lib/apps/plugins/a2ui-surface/index.d.ts +2 -1
  107. package/lib/apps/plugins/a2ui-surface/index.js +10 -1
  108. package/lib/apps/plugins/a2ui-surface/toolResult.d.ts +20 -11
  109. package/lib/apps/plugins/a2ui-surface/toolResult.js +27 -11
  110. package/lib/apps/plugins/app-components/index.d.ts +1 -1
  111. package/lib/apps/plugins/app-components/index.js +2 -2
  112. package/lib/apps/plugins/app-elements/AppElements.d.ts +3 -3
  113. package/lib/apps/plugins/app-elements/AppElements.js +5 -20
  114. package/lib/apps/plugins/app-elements/QuestionView.d.ts +30 -0
  115. package/lib/apps/plugins/app-elements/QuestionView.js +70 -0
  116. package/lib/apps/plugins/app-elements/index.d.ts +7 -4
  117. package/lib/apps/plugins/app-elements/index.js +14 -5
  118. package/lib/apps/plugins/app-page/appPageModel.d.ts +6 -8
  119. package/lib/apps/plugins/app-page/appPageModel.js +0 -2
  120. package/lib/apps/plugins/app-page/pageRun.d.ts +2 -2
  121. package/lib/apps/plugins/app-page/pageRun.js +3 -3
  122. package/lib/apps/plugins/canvas-blocks/index.d.ts +13 -13
  123. package/lib/apps/plugins/canvas-blocks/index.js +24 -24
  124. package/lib/apps/plugins/canvas-parts/index.d.ts +7 -1
  125. package/lib/apps/plugins/canvas-parts/index.js +36 -0
  126. package/lib/apps/plugins/chat/ChatView.js +29 -3
  127. package/lib/chat/base/ChatBase.js +264 -43
  128. package/lib/chat/base/loopElement.d.ts +16 -17
  129. package/lib/chat/base/loopElement.js +11 -27
  130. package/lib/chat/base/loopStep.d.ts +15 -15
  131. package/lib/chat/base/loopStep.js +10 -13
  132. package/lib/chat/base/loopWindow.d.ts +15 -20
  133. package/lib/chat/base/loopWindow.js +26 -27
  134. package/lib/chat/base/uiFiles.d.ts +29 -0
  135. package/lib/chat/base/uiFiles.js +69 -0
  136. package/lib/chat/base/uiOps.d.ts +96 -0
  137. package/lib/chat/base/uiOps.js +251 -0
  138. package/lib/chat/messages/ChatMessageList.d.ts +13 -1
  139. package/lib/chat/messages/ChatMessageList.js +4 -4
  140. package/lib/components/a2ui/datalayer/Chart.d.ts +1 -1
  141. package/lib/components/a2ui/datalayer/schema.js +1 -1
  142. package/lib/components/teams/AnswerSurfaces.d.ts +3 -2
  143. package/lib/components/teams/AnswerSurfaces.js +7 -7
  144. package/lib/components/teams/useA2ATeam.d.ts +1 -1
  145. package/lib/config/AgentConfiguration.d.ts +1 -1
  146. package/lib/examples/ProtocolSignedRequestsExample.d.ts +10 -0
  147. package/lib/examples/ProtocolSignedRequestsExample.js +41 -0
  148. package/lib/examples/ProtocolUcpCheckoutExample.d.ts +10 -0
  149. package/lib/examples/ProtocolUcpCheckoutExample.js +59 -0
  150. package/lib/examples/example-selector.js +2 -0
  151. package/lib/examples/exampleGroups.d.ts +1 -1
  152. package/lib/examples/exampleGroups.js +4 -0
  153. package/lib/examples/index.d.ts +2 -0
  154. package/lib/examples/index.js +2 -0
  155. package/lib/examples/protocols/signedRequestsDemo.d.ts +27 -0
  156. package/lib/examples/protocols/signedRequestsDemo.js +59 -0
  157. package/lib/examples/protocols/ucpCheckoutDemo.d.ts +36 -0
  158. package/lib/examples/protocols/ucpCheckoutDemo.js +68 -0
  159. package/lib/protocols/AGUIAdapter.d.ts +7 -0
  160. package/lib/protocols/AGUIAdapter.js +43 -3
  161. package/lib/protocols/a2a/index.js +1 -0
  162. package/lib/protocols/agentic-commerce/index.js +1 -0
  163. package/lib/protocols/core.d.ts +6 -0
  164. package/lib/protocols/mcp/index.js +1 -0
  165. package/lib/protocols/pact/index.js +1 -0
  166. package/lib/protocols/ucp/index.js +1 -0
  167. package/lib/protocols/ucp/merchant.d.ts +15 -0
  168. package/lib/protocols/ucp/merchant.js +257 -0
  169. package/lib/protocols/web-bot-auth/index.js +1 -0
  170. package/lib/specs/agents/agents.js +338 -169
  171. package/lib/specs/apps.js +134 -6
  172. package/lib/specs/appspecSchema.js +59 -0
  173. package/lib/specs/cogs.js +6 -3
  174. package/lib/specs/index.d.ts +2 -1
  175. package/lib/specs/index.js +2 -1
  176. package/lib/specs/protocols.d.ts +24 -0
  177. package/lib/specs/protocols.js +197 -0
  178. package/lib/specs/ui.d.ts +21 -0
  179. package/lib/specs/{uiPlugins.js → ui.js} +14 -14
  180. package/lib/types/agentspecs.d.ts +69 -5
  181. package/lib/types/messages.d.ts +7 -0
  182. package/lib/types/protocol.d.ts +15 -0
  183. package/package.json +2 -2
  184. package/scripts/codegen/generate_agents.py +43 -8
  185. package/scripts/codegen/generate_protocols.py +207 -0
  186. package/scripts/codegen/generate_sandbox_agents.py +1 -1
  187. package/scripts/codegen/{generate_ui_plugins.py → generate_ui.py} +40 -40
  188. package/scripts/gen_workers.py +2 -2
  189. package/dist-embed/chunks/ChatView-D41Nx5rC.js +0 -32
  190. package/lib/chat/base/loopMessage.d.ts +0 -37
  191. package/lib/chat/base/loopMessage.js +0 -37
  192. package/lib/specs/uiPlugins.d.ts +0 -21
@@ -0,0 +1,257 @@
1
+ /*
2
+ * Copyright (c) 2025-2026 Datalayer, Inc.
3
+ * Distributed under the terms of the Modified BSD License.
4
+ */
5
+ /**
6
+ * *Example Store*, the local UCP test merchant, as a `fetch` (AGENT_PROTOCOLS
7
+ * EX-04, EX-10): the same catalogue, totals, email gate, idempotency and
8
+ * test payment as `agent_runtimes.protocols.ucp.merchant.ExampleStore`, so a
9
+ * page or a test buys from it with no network. A test merchant only: it keeps
10
+ * nothing beyond the page, and refuses any payment but its test handler's.
11
+ *
12
+ * @module protocols/ucp/merchant
13
+ */
14
+ import { CHECKOUT_CAPABILITY, SHOPPING_SERVICE, UCP_VERSION } from './index';
15
+ /** The store's products, as the Python store lists them. */
16
+ export const EXAMPLE_STORE_CATALOG = {
17
+ tea_sencha: { title: 'Sencha green tea, 100 g', price: 1250 },
18
+ tea_assam: { title: 'Assam black tea, 250 g', price: 1800 },
19
+ mug: { title: 'Stoneware mug', price: 2400 },
20
+ };
21
+ /** The one payment handler it takes, and the prefix of its test tokens. */
22
+ export const EXAMPLE_STORE_HANDLER_ID = 'test_pay_1';
23
+ const TEST_TOKEN_PREFIX = 'tok_test_';
24
+ const json = (body, status = 200) => new Response(JSON.stringify(body), {
25
+ status,
26
+ headers: { 'content-type': 'application/json' },
27
+ });
28
+ const refuse = (status, code, content) => json({
29
+ ucp: { version: UCP_VERSION, status: 'error' },
30
+ messages: [{ type: 'error', code, content, severity: 'recoverable' }],
31
+ }, status);
32
+ const meta = () => ({
33
+ version: UCP_VERSION,
34
+ status: 'success',
35
+ capabilities: { [CHECKOUT_CAPABILITY]: [{ version: UCP_VERSION }] },
36
+ payment_handlers: {
37
+ 'com.example.test_pay': [
38
+ {
39
+ id: EXAMPLE_STORE_HANDLER_ID,
40
+ version: UCP_VERSION,
41
+ available_instruments: [{ type: 'card' }],
42
+ config: {},
43
+ },
44
+ ],
45
+ },
46
+ });
47
+ async function fingerprint(path, body) {
48
+ const digest = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(path + body));
49
+ return Array.from(new Uint8Array(digest), b => b.toString(16).padStart(2, '0')).join('');
50
+ }
51
+ /** The store: its `fetch`, and the orders it took. */
52
+ export function exampleStore(base = 'https://store.example') {
53
+ const checkouts = new Map();
54
+ const replies = new Map();
55
+ const orders = [];
56
+ const render = (checkout) => {
57
+ let subtotal = 0;
58
+ const lines = checkout.lines.map((line, index) => {
59
+ const product = EXAMPLE_STORE_CATALOG[line.id];
60
+ const amount = product.price * line.quantity;
61
+ subtotal += amount;
62
+ return {
63
+ id: `li_${index + 1}`,
64
+ item: { id: line.id, title: product.title, price: product.price },
65
+ quantity: line.quantity,
66
+ totals: [
67
+ { type: 'subtotal', amount },
68
+ { type: 'total', amount },
69
+ ],
70
+ };
71
+ });
72
+ const tax = Math.round(subtotal * 0.08);
73
+ const messages = [];
74
+ if (['incomplete', 'ready_for_complete'].includes(checkout.status)) {
75
+ checkout.status = checkout.buyer?.email
76
+ ? 'ready_for_complete'
77
+ : 'incomplete';
78
+ if (!checkout.buyer?.email) {
79
+ messages.push({
80
+ type: 'error',
81
+ code: 'missing',
82
+ path: '$.buyer.email',
83
+ content: 'Buyer email is required',
84
+ severity: 'recoverable',
85
+ });
86
+ }
87
+ }
88
+ return {
89
+ ucp: meta(),
90
+ id: checkout.id,
91
+ status: checkout.status,
92
+ currency: 'USD',
93
+ line_items: lines,
94
+ totals: [
95
+ { type: 'subtotal', amount: subtotal },
96
+ { type: 'tax', amount: tax },
97
+ { type: 'total', amount: subtotal + tax },
98
+ ],
99
+ links: [{ type: 'terms_of_service', url: `${base}/terms` }],
100
+ expires_at: checkout.expiresAt,
101
+ ...(messages.length ? { messages } : {}),
102
+ ...(checkout.buyer?.email ? { buyer: checkout.buyer } : {}),
103
+ ...(checkout.order ? { order: checkout.order } : {}),
104
+ };
105
+ };
106
+ const linesOf = (body) => {
107
+ const lines = [];
108
+ for (const line of body.line_items ?? []) {
109
+ const id = line?.item?.id;
110
+ if (!(id in EXAMPLE_STORE_CATALOG)) {
111
+ return `No product ${id}`;
112
+ }
113
+ lines.push({ id, quantity: Number(line.quantity ?? 1) });
114
+ }
115
+ return lines;
116
+ };
117
+ const handle = (method, path, body) => {
118
+ if (method === 'POST' && path === '/checkout-sessions') {
119
+ const lines = linesOf(body);
120
+ if (typeof lines === 'string') {
121
+ return { status: 404, body: { messages: [{ content: lines }] } };
122
+ }
123
+ const checkout = {
124
+ id: `chk_${crypto.randomUUID().slice(0, 12)}`,
125
+ status: 'incomplete',
126
+ lines,
127
+ buyer: body.buyer ?? {},
128
+ expiresAt: new Date(Date.now() + 60 * 60 * 1000).toISOString(),
129
+ };
130
+ checkouts.set(checkout.id, checkout);
131
+ return { status: 201, body: render(checkout) };
132
+ }
133
+ const [, , id, action] = path.split('/');
134
+ const checkout = checkouts.get(id);
135
+ if (!checkout) {
136
+ return { status: 404, body: { messages: [{ content: 'No checkout' }] } };
137
+ }
138
+ if (method === 'PUT' && !action) {
139
+ if (!['incomplete', 'ready_for_complete'].includes(checkout.status)) {
140
+ return { status: 409, body: render(checkout) };
141
+ }
142
+ if (body.buyer) {
143
+ checkout.buyer = body.buyer;
144
+ }
145
+ if (body.line_items) {
146
+ const lines = linesOf(body);
147
+ if (typeof lines === 'string') {
148
+ return { status: 404, body: { messages: [{ content: lines }] } };
149
+ }
150
+ checkout.lines = lines;
151
+ }
152
+ return { status: 200, body: render(checkout) };
153
+ }
154
+ if (method === 'POST' && action === 'complete') {
155
+ if (checkout.status !== 'ready_for_complete') {
156
+ return { status: 200, body: render(checkout) };
157
+ }
158
+ const [instrument] = body.payment?.instruments ?? [{}];
159
+ const token = String(instrument?.credential?.token ?? '');
160
+ if (instrument?.handler_id !== EXAMPLE_STORE_HANDLER_ID ||
161
+ !token.startsWith(TEST_TOKEN_PREFIX)) {
162
+ return {
163
+ status: 402,
164
+ body: {
165
+ ucp: { version: UCP_VERSION, status: 'error' },
166
+ messages: [
167
+ {
168
+ type: 'error',
169
+ code: 'payment_declined',
170
+ content: 'The payment was declined',
171
+ },
172
+ ],
173
+ },
174
+ };
175
+ }
176
+ const order = {
177
+ id: `ord_${crypto.randomUUID().slice(0, 10)}`,
178
+ permalink_url: `${base}/orders/${checkout.id}`,
179
+ };
180
+ checkout.status = 'completed';
181
+ checkout.order = order;
182
+ orders.push({ order: order.id, checkout: checkout.id });
183
+ return { status: 200, body: render(checkout) };
184
+ }
185
+ if (method === 'POST' && action === 'cancel') {
186
+ if (checkout.status !== 'completed') {
187
+ checkout.status = 'canceled';
188
+ }
189
+ return { status: 200, body: render(checkout) };
190
+ }
191
+ if (method === 'GET' && !action) {
192
+ return { status: 200, body: render(checkout) };
193
+ }
194
+ return { status: 405, body: {} };
195
+ };
196
+ const fetchImpl = (async (input, init) => {
197
+ const url = new URL(String(input));
198
+ if (url.pathname === '/.well-known/ucp') {
199
+ return json({
200
+ ucp: {
201
+ version: UCP_VERSION,
202
+ services: {
203
+ [SHOPPING_SERVICE]: [
204
+ {
205
+ version: UCP_VERSION,
206
+ transport: 'rest',
207
+ endpoint: `${base}/ucp/v1`,
208
+ },
209
+ ],
210
+ },
211
+ capabilities: { [CHECKOUT_CAPABILITY]: [{ version: UCP_VERSION }] },
212
+ payment_handlers: meta().payment_handlers,
213
+ },
214
+ });
215
+ }
216
+ const headers = new Headers(init?.headers);
217
+ const agent = headers.get('UCP-Agent') ?? '';
218
+ if (!/^profile="https:\/\/[^"]+"$/.test(agent)) {
219
+ return refuse(400, 'invalid_ucp_agent', 'UCP-Agent names its profile over https');
220
+ }
221
+ if (!headers.get('Request-Id')) {
222
+ return refuse(400, 'missing_request_id', 'Request-Id is required');
223
+ }
224
+ const method = (init?.method ?? 'GET').toUpperCase();
225
+ const path = url.pathname.replace(/^\/ucp\/v1/, '');
226
+ const raw = typeof init?.body === 'string' ? init.body : '';
227
+ const body = raw ? JSON.parse(raw) : {};
228
+ if (path === '/catalog/search') {
229
+ const query = String(body.query ?? '').toLowerCase();
230
+ return json({
231
+ ucp: { version: UCP_VERSION },
232
+ products: Object.entries(EXAMPLE_STORE_CATALOG)
233
+ .filter(([, product]) => product.title.toLowerCase().includes(query))
234
+ .map(([id, product]) => ({ id, title: product.title })),
235
+ });
236
+ }
237
+ if (method === 'GET') {
238
+ const answer = handle(method, path, body);
239
+ return json(answer.body, answer.status);
240
+ }
241
+ const key = headers.get('Idempotency-Key');
242
+ if (!key) {
243
+ return refuse(400, 'missing_idempotency_key', 'Idempotency-Key is required');
244
+ }
245
+ const seen = await fingerprint(url.pathname, raw);
246
+ const replied = replies.get(key);
247
+ if (replied) {
248
+ return replied.seen === seen
249
+ ? json(replied.body, replied.status)
250
+ : refuse(409, 'idempotency_conflict', 'the key was used for another request');
251
+ }
252
+ const answer = handle(method, path, body);
253
+ replies.set(key, { seen, ...answer });
254
+ return json(answer.body, answer.status);
255
+ });
256
+ return { fetch: fetchImpl, orders };
257
+ }
@@ -324,6 +324,7 @@ export const WebBotAuthPlugin = definePlugin({
324
324
  status: 'implemented',
325
325
  specification: 'https://datatracker.ietf.org/doc/draft-ietf-webbotauth-httpsig-protocol/',
326
326
  plugin: WEB_BOT_AUTH_PLUGIN,
327
+ catalogue: 'web-bot-auth',
327
328
  points: WEB_BOT_AUTH_POINTS.map(point => point.id),
328
329
  }, { id: 'web_bot_auth', order: DEFAULT_ORDER }),
329
330
  contribution(WebBotAuthDirectoryDiscoveries, directoryDiscovery(), {