@hraness/ghostget 0.17.2

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 (500) hide show
  1. package/CHANGELOG.md +458 -0
  2. package/LICENSE +21 -0
  3. package/README.md +1359 -0
  4. package/bunfig.toml +2 -0
  5. package/dist/apple-photos-client.js +704 -0
  6. package/dist/beeper-client.js +4602 -0
  7. package/dist/client.js +1605 -0
  8. package/dist/index-26yq8q16.js +31 -0
  9. package/dist/index-9t13b4k7.js +508 -0
  10. package/dist/index-axwm9b3v.js +5 -0
  11. package/dist/index-gwk7rbyj.js +30 -0
  12. package/dist/index.js +701 -0
  13. package/dist/messaging.js +1130 -0
  14. package/dist/omni-client.js +1406 -0
  15. package/dist/whatsapp-client.js +355 -0
  16. package/docs/imessage-direct-provider.md +106 -0
  17. package/docs/rental-listings.md +94 -0
  18. package/package.json +600 -0
  19. package/skills/ghostget/SKILL.md +251 -0
  20. package/skills/ghostget/agents/openai.yaml +4 -0
  21. package/skills/ghostget/references/adapter-contract.md +181 -0
  22. package/skills/ghostget/references/apple-photos.md +115 -0
  23. package/skills/ghostget/references/article-drafts.md +235 -0
  24. package/skills/ghostget/references/code-owned-provider-scaffold.md +134 -0
  25. package/skills/ghostget/references/cross-posting-video.md +139 -0
  26. package/skills/ghostget/references/cross-posting.md +82 -0
  27. package/skills/ghostget/references/derivation.md +390 -0
  28. package/skills/ghostget/references/hraness-social-profile-stats.json +269 -0
  29. package/skills/ghostget/references/install.md +66 -0
  30. package/skills/ghostget/references/linkedin-adapter.md +442 -0
  31. package/skills/ghostget/references/messaging.md +168 -0
  32. package/skills/ghostget/references/meta-comet-contract.md +153 -0
  33. package/skills/ghostget/references/platform-patterns.md +122 -0
  34. package/skills/ghostget/references/provider-plugins.md +496 -0
  35. package/skills/ghostget/references/reddit-flair.md +78 -0
  36. package/skills/ghostget/references/safety-and-state.md +214 -0
  37. package/skills/ghostget/references/settlement-and-duplicate-cleanup.md +79 -0
  38. package/skills/ghostget/references/social-platform-routing.md +66 -0
  39. package/skills/ghostget/references/social-profile-stats.md +195 -0
  40. package/skills/ghostget/references/social-video-platform-routing.md +89 -0
  41. package/skills/ghostget/references/whatsapp-adapter.md +101 -0
  42. package/skills/ghostget/references/x-adapter.md +332 -0
  43. package/skills/ghostget/references/x-ai-disclosure.md +71 -0
  44. package/src/apple-photos-cli.ts +639 -0
  45. package/src/apple-photos-client-types.ts +168 -0
  46. package/src/apple-photos-client.ts +201 -0
  47. package/src/apple-photos-contact-evidence.ts +838 -0
  48. package/src/apple-photos-local-source.ts +1131 -0
  49. package/src/args.ts +2053 -0
  50. package/src/article-draft-document.ts +417 -0
  51. package/src/article-draft-embeds.ts +123 -0
  52. package/src/article-draft-images.ts +214 -0
  53. package/src/assets/adapter-template/wrench-adapter.json +40 -0
  54. package/src/assets/adapters/beeper/wrench-web-adapter.json +1500 -0
  55. package/src/assets/adapters/beeper/wrench-web-adapter.v1.0.0.json +129 -0
  56. package/src/assets/adapters/beeper/wrench-web-adapter.v1.1.0.json +207 -0
  57. package/src/assets/adapters/beeper/wrench-web-adapter.v2.0.0.json +1463 -0
  58. package/src/assets/adapters/beeper/wrench-web-adapter.v2.1.0.json +1463 -0
  59. package/src/assets/adapters/beeper/wrench-web-adapter.v2.2.0.json +1475 -0
  60. package/src/assets/adapters/beeper/wrench-web-adapter.v2.3.0.json +1488 -0
  61. package/src/assets/adapters/bluesky/wrench-web-adapter.json +684 -0
  62. package/src/assets/adapters/bluesky/wrench-web-adapter.v1.0.0.json +581 -0
  63. package/src/assets/adapters/bluesky/wrench-web-adapter.v1.1.0.json +581 -0
  64. package/src/assets/adapters/bluesky/wrench-web-adapter.v1.2.0.json +581 -0
  65. package/src/assets/adapters/bluesky/wrench-web-adapter.v1.3.0.json +615 -0
  66. package/src/assets/adapters/bluesky/wrench-web-adapter.v1.4.0.json +684 -0
  67. package/src/assets/adapters/bluesky/wrench-web-adapter.v1.5.0.json +684 -0
  68. package/src/assets/adapters/bluesky/wrench-web-adapter.v1.6.0.json +684 -0
  69. package/src/assets/adapters/clasificados/wrench-web-adapter.json +54 -0
  70. package/src/assets/adapters/facebook/wrench-web-adapter.json +750 -0
  71. package/src/assets/adapters/facebook/wrench-web-adapter.v1.0.0.json +706 -0
  72. package/src/assets/adapters/facebook/wrench-web-adapter.v1.1.0.json +703 -0
  73. package/src/assets/adapters/facebook/wrench-web-adapter.v1.2.0.json +750 -0
  74. package/src/assets/adapters/facebook-group/wrench-web-adapter.json +682 -0
  75. package/src/assets/adapters/facebook-group/wrench-web-adapter.v1.0.0.json +583 -0
  76. package/src/assets/adapters/facebook-group/wrench-web-adapter.v1.1.0.json +682 -0
  77. package/src/assets/adapters/facebook-marketplace/wrench-web-adapter.json +437 -0
  78. package/src/assets/adapters/facebook-marketplace/wrench-web-adapter.v1.0.0.json +383 -0
  79. package/src/assets/adapters/facebook-marketplace/wrench-web-adapter.v1.1.0.json +437 -0
  80. package/src/assets/adapters/facebook-marketplace/wrench-web-adapter.v1.1.1.json +437 -0
  81. package/src/assets/adapters/facebook-page/wrench-web-adapter.json +880 -0
  82. package/src/assets/adapters/facebook-page/wrench-web-adapter.v1.0.0.json +740 -0
  83. package/src/assets/adapters/facebook-page/wrench-web-adapter.v1.1.0.json +880 -0
  84. package/src/assets/adapters/github/wrench-web-adapter.json +69 -0
  85. package/src/assets/adapters/github/wrench-web-adapter.v1.0.0.json +42 -0
  86. package/src/assets/adapters/github/wrench-web-adapter.v1.1.0.json +69 -0
  87. package/src/assets/adapters/gmail/wrench-adapter.json +161 -0
  88. package/src/assets/adapters/gmail/wrench-adapter.v1.2.0.json +157 -0
  89. package/src/assets/adapters/hacker-news/wrench-web-adapter.json +315 -0
  90. package/src/assets/adapters/hacker-news/wrench-web-adapter.v1.0.0.json +315 -0
  91. package/src/assets/adapters/imessage/wrench-web-adapter.json +236 -0
  92. package/src/assets/adapters/instagram/wrench-web-adapter.json +719 -0
  93. package/src/assets/adapters/instagram/wrench-web-adapter.v1.0.0.json +627 -0
  94. package/src/assets/adapters/instagram/wrench-web-adapter.v1.1.0.json +609 -0
  95. package/src/assets/adapters/instagram/wrench-web-adapter.v1.2.0.json +640 -0
  96. package/src/assets/adapters/instagram/wrench-web-adapter.v1.3.0.json +671 -0
  97. package/src/assets/adapters/instagram/wrench-web-adapter.v1.4.0.json +710 -0
  98. package/src/assets/adapters/instagram/wrench-web-adapter.v1.5.0.json +710 -0
  99. package/src/assets/adapters/instagram/wrench-web-adapter.v1.6.0.json +719 -0
  100. package/src/assets/adapters/instagram/wrench-web-adapter.v1.7.0.json +719 -0
  101. package/src/assets/adapters/linkedin/wrench-adapter.json +442 -0
  102. package/src/assets/adapters/linkedin/wrench-adapter.v0.4.0.json +124 -0
  103. package/src/assets/adapters/linkedin/wrench-adapter.v1.0.0.json +411 -0
  104. package/src/assets/adapters/linkedin/wrench-web-adapter.json +914 -0
  105. package/src/assets/adapters/linkedin/wrench-web-adapter.v1.0.0.json +604 -0
  106. package/src/assets/adapters/linkedin/wrench-web-adapter.v1.1.0.json +604 -0
  107. package/src/assets/adapters/linkedin/wrench-web-adapter.v1.10.0.json +809 -0
  108. package/src/assets/adapters/linkedin/wrench-web-adapter.v1.11.0.json +809 -0
  109. package/src/assets/adapters/linkedin/wrench-web-adapter.v1.12.0.json +820 -0
  110. package/src/assets/adapters/linkedin/wrench-web-adapter.v1.13.0.json +819 -0
  111. package/src/assets/adapters/linkedin/wrench-web-adapter.v1.14.0.json +819 -0
  112. package/src/assets/adapters/linkedin/wrench-web-adapter.v1.15.0.json +819 -0
  113. package/src/assets/adapters/linkedin/wrench-web-adapter.v1.16.0.json +874 -0
  114. package/src/assets/adapters/linkedin/wrench-web-adapter.v1.17.0.json +874 -0
  115. package/src/assets/adapters/linkedin/wrench-web-adapter.v1.18.0.json +874 -0
  116. package/src/assets/adapters/linkedin/wrench-web-adapter.v1.19.0.json +874 -0
  117. package/src/assets/adapters/linkedin/wrench-web-adapter.v1.2.0.json +731 -0
  118. package/src/assets/adapters/linkedin/wrench-web-adapter.v1.3.0.json +768 -0
  119. package/src/assets/adapters/linkedin/wrench-web-adapter.v1.4.0.json +808 -0
  120. package/src/assets/adapters/linkedin/wrench-web-adapter.v1.5.0.json +808 -0
  121. package/src/assets/adapters/linkedin/wrench-web-adapter.v1.6.0.json +808 -0
  122. package/src/assets/adapters/linkedin/wrench-web-adapter.v1.7.0.json +825 -0
  123. package/src/assets/adapters/linkedin/wrench-web-adapter.v1.8.0.json +792 -0
  124. package/src/assets/adapters/linkedin/wrench-web-adapter.v1.9.0.json +809 -0
  125. package/src/assets/adapters/reddit/wrench-web-adapter.json +855 -0
  126. package/src/assets/adapters/reddit/wrench-web-adapter.v1.0.0.json +577 -0
  127. package/src/assets/adapters/reddit/wrench-web-adapter.v1.1.0.json +604 -0
  128. package/src/assets/adapters/reddit/wrench-web-adapter.v1.10.0.json +712 -0
  129. package/src/assets/adapters/reddit/wrench-web-adapter.v1.11.0.json +712 -0
  130. package/src/assets/adapters/reddit/wrench-web-adapter.v1.12.0.json +712 -0
  131. package/src/assets/adapters/reddit/wrench-web-adapter.v1.2.0.json +653 -0
  132. package/src/assets/adapters/reddit/wrench-web-adapter.v1.3.0.json +712 -0
  133. package/src/assets/adapters/reddit/wrench-web-adapter.v1.4.0.json +713 -0
  134. package/src/assets/adapters/reddit/wrench-web-adapter.v1.5.0.json +712 -0
  135. package/src/assets/adapters/reddit/wrench-web-adapter.v1.6.0.json +713 -0
  136. package/src/assets/adapters/reddit/wrench-web-adapter.v1.7.0.json +713 -0
  137. package/src/assets/adapters/reddit/wrench-web-adapter.v1.8.0.json +713 -0
  138. package/src/assets/adapters/reddit/wrench-web-adapter.v1.9.0.json +713 -0
  139. package/src/assets/adapters/substack/wrench-web-adapter.json +936 -0
  140. package/src/assets/adapters/substack/wrench-web-adapter.v1.0.0.json +817 -0
  141. package/src/assets/adapters/substack/wrench-web-adapter.v1.1.0.json +812 -0
  142. package/src/assets/adapters/substack/wrench-web-adapter.v1.2.0.json +812 -0
  143. package/src/assets/adapters/substack/wrench-web-adapter.v1.3.0.json +902 -0
  144. package/src/assets/adapters/substack/wrench-web-adapter.v1.4.0.json +936 -0
  145. package/src/assets/adapters/substack/wrench-web-adapter.v1.5.0.json +936 -0
  146. package/src/assets/adapters/substack/wrench-web-adapter.v1.6.0.json +936 -0
  147. package/src/assets/adapters/threads/wrench-web-adapter.json +692 -0
  148. package/src/assets/adapters/threads/wrench-web-adapter.v1.0.0.json +629 -0
  149. package/src/assets/adapters/threads/wrench-web-adapter.v1.1.0.json +623 -0
  150. package/src/assets/adapters/threads/wrench-web-adapter.v1.2.0.json +622 -0
  151. package/src/assets/adapters/threads/wrench-web-adapter.v1.3.0.json +622 -0
  152. package/src/assets/adapters/threads/wrench-web-adapter.v1.4.0.json +622 -0
  153. package/src/assets/adapters/threads/wrench-web-adapter.v1.5.0.json +699 -0
  154. package/src/assets/adapters/threads/wrench-web-adapter.v1.6.0.json +693 -0
  155. package/src/assets/adapters/threads/wrench-web-adapter.v1.7.0.json +692 -0
  156. package/src/assets/adapters/tiktok/wrench-web-adapter.json +762 -0
  157. package/src/assets/adapters/tiktok/wrench-web-adapter.v1.0.0.json +681 -0
  158. package/src/assets/adapters/tiktok/wrench-web-adapter.v1.1.0.json +708 -0
  159. package/src/assets/adapters/tiktok/wrench-web-adapter.v1.2.0.json +762 -0
  160. package/src/assets/adapters/tiktok/wrench-web-adapter.v1.3.0.json +762 -0
  161. package/src/assets/adapters/twitch/wrench-web-adapter.json +42 -0
  162. package/src/assets/adapters/twitch/wrench-web-adapter.v1.0.0.json +42 -0
  163. package/src/assets/adapters/whatsapp/wrench-web-adapter.json +400 -0
  164. package/src/assets/adapters/whatsapp/wrench-web-adapter.v1.0.0.json +355 -0
  165. package/src/assets/adapters/whatsapp/wrench-web-adapter.v1.1.0.json +386 -0
  166. package/src/assets/adapters/whatsapp/wrench-web-adapter.v1.2.0.json +386 -0
  167. package/src/assets/adapters/whatsapp/wrench-web-adapter.v1.3.0.json +400 -0
  168. package/src/assets/adapters/x/wrench-adapter.json +749 -0
  169. package/src/assets/adapters/x/wrench-adapter.v1.0.0.json +700 -0
  170. package/src/assets/adapters/x/wrench-adapter.v1.1.0.json +707 -0
  171. package/src/assets/adapters/x/wrench-web-adapter.json +712 -0
  172. package/src/assets/adapters/x/wrench-web-adapter.v1.1.0.json +579 -0
  173. package/src/assets/adapters/x/wrench-web-adapter.v1.10.0.json +685 -0
  174. package/src/assets/adapters/x/wrench-web-adapter.v1.11.0.json +712 -0
  175. package/src/assets/adapters/x/wrench-web-adapter.v1.12.0.json +712 -0
  176. package/src/assets/adapters/x/wrench-web-adapter.v1.13.0.json +712 -0
  177. package/src/assets/adapters/x/wrench-web-adapter.v1.2.0.json +578 -0
  178. package/src/assets/adapters/x/wrench-web-adapter.v1.3.0.json +610 -0
  179. package/src/assets/adapters/x/wrench-web-adapter.v1.4.0.json +617 -0
  180. package/src/assets/adapters/x/wrench-web-adapter.v1.5.0.json +634 -0
  181. package/src/assets/adapters/x/wrench-web-adapter.v1.6.0.json +632 -0
  182. package/src/assets/adapters/x/wrench-web-adapter.v1.7.0.json +649 -0
  183. package/src/assets/adapters/x/wrench-web-adapter.v1.8.0.json +649 -0
  184. package/src/assets/adapters/x/wrench-web-adapter.v1.9.0.json +683 -0
  185. package/src/assets/adapters/youtube/wrench-web-adapter.json +561 -0
  186. package/src/assets/adapters/youtube/wrench-web-adapter.v1.0.0.json +465 -0
  187. package/src/assets/adapters/youtube/wrench-web-adapter.v1.1.0.json +519 -0
  188. package/src/assets/adapters/youtube/wrench-web-adapter.v1.2.0.json +561 -0
  189. package/src/assets/adapters/youtube/wrench-web-adapter.v1.3.0.json +561 -0
  190. package/src/assets/code-owned-provider-template/plugin.test.ts.template +54 -0
  191. package/src/assets/code-owned-provider-template/plugin.ts.template +102 -0
  192. package/src/assets/code-owned-provider-template/runtime.internal.test.ts.template +44 -0
  193. package/src/assets/code-owned-provider-template/runtime.ts.template +59 -0
  194. package/src/assets/code-owned-provider-template/wrench-adapter.json.template +33 -0
  195. package/src/assets/fonts/nebula-sans/LICENSE.txt +96 -0
  196. package/src/assets/fonts/nebula-sans/NebulaSans-Book.woff2 +0 -0
  197. package/src/assets/fonts/nebula-sans/PROVENANCE.md +14 -0
  198. package/src/assets.d.ts +4 -0
  199. package/src/auth.ts +1157 -0
  200. package/src/beeper-client-types.ts +169 -0
  201. package/src/beeper-client.ts +250 -0
  202. package/src/beeper-contact-interactions-cli.ts +180 -0
  203. package/src/beeper-contact-interactions.ts +1612 -0
  204. package/src/beeper-message-bundle-v1.ts +257 -0
  205. package/src/beeper-message-like-me-cli.ts +84 -0
  206. package/src/beeper-message-like-me-export.ts +2274 -0
  207. package/src/beeper-message-like-me-recovery.ts +931 -0
  208. package/src/beeper-message-like-me-source.ts +4034 -0
  209. package/src/browser-admission.ts +928 -0
  210. package/src/browser-snapshots.ts +260 -0
  211. package/src/browser.ts +3873 -0
  212. package/src/canonical-json.ts +60 -0
  213. package/src/catalog-cli.ts +638 -0
  214. package/src/cli.ts +208 -0
  215. package/src/client-types.ts +301 -0
  216. package/src/client.ts +2726 -0
  217. package/src/confirmed-write-failure.ts +44 -0
  218. package/src/confirmed-write-model.ts +72 -0
  219. package/src/confirmed-write-platform.ts +1055 -0
  220. package/src/confirmed-write-program.ts +224 -0
  221. package/src/confirmed-write-runtime.ts +16 -0
  222. package/src/cursor-token.ts +228 -0
  223. package/src/derivation-dnr-readiness.ts +367 -0
  224. package/src/derivation-file-chooser.ts +384 -0
  225. package/src/derivation-network-boundary.ts +835 -0
  226. package/src/derivation-network-guard.ts +782 -0
  227. package/src/derivation-network-proxy-helper.ts +440 -0
  228. package/src/derivation-network-proxy.ts +385 -0
  229. package/src/derive-command-helper.ts +693 -0
  230. package/src/derive-fixtures.ts +343 -0
  231. package/src/derive-review.ts +980 -0
  232. package/src/derive.ts +4754 -0
  233. package/src/fixtures/beeper-message-like-me-v1/accounts.ndjson +2 -0
  234. package/src/fixtures/beeper-message-like-me-v1/conversations.ndjson +1 -0
  235. package/src/fixtures/beeper-message-like-me-v1/manifest.json +1 -0
  236. package/src/fixtures/beeper-message-like-me-v1/messages.ndjson +2 -0
  237. package/src/fixtures/beeper-message-like-me-v1/participants.ndjson +3 -0
  238. package/src/fixtures/beeper-message-like-me-v1/reactions.ndjson +1 -0
  239. package/src/fixtures/beeper-message-like-me-v1/tombstones.ndjson +1 -0
  240. package/src/ghostget.ts +3672 -0
  241. package/src/gmail-capture.ts +1244 -0
  242. package/src/har-internal.ts +1242 -0
  243. package/src/har.ts +743 -0
  244. package/src/index.ts +218 -0
  245. package/src/invocation-read-platform.ts +55 -0
  246. package/src/invocation-read-program.ts +93 -0
  247. package/src/invocation-read-runtime.ts +11 -0
  248. package/src/linked-device-lifecycle-admission.ts +450 -0
  249. package/src/linked-device-lifecycle-journal.ts +1560 -0
  250. package/src/linked-device-lifecycle-runtime.ts +1602 -0
  251. package/src/local-cli-admission.ts +135 -0
  252. package/src/local-cli-contracts.ts +271 -0
  253. package/src/local-cli-execution.ts +98 -0
  254. package/src/local-cli-surface-contract.ts +1510 -0
  255. package/src/local-cli-tool-identity.ts +336 -0
  256. package/src/media/archive.ts +2325 -0
  257. package/src/media/args.ts +303 -0
  258. package/src/media/cli.ts +287 -0
  259. package/src/media/doctor.ts +475 -0
  260. package/src/media/ffmpeg.ts +1330 -0
  261. package/src/media/http-capture.ts +711 -0
  262. package/src/media/http-probe.ts +593 -0
  263. package/src/media/http.ts +387 -0
  264. package/src/media/index.ts +10 -0
  265. package/src/media/local-transcription.ts +613 -0
  266. package/src/media/lock.ts +321 -0
  267. package/src/media/manifest.ts +1712 -0
  268. package/src/media/metadata.ts +906 -0
  269. package/src/media/process.ts +723 -0
  270. package/src/media/revision.ts +166 -0
  271. package/src/media/runtime-closure.ts +1063 -0
  272. package/src/media/source-router.ts +187 -0
  273. package/src/media/transcriber-config.ts +1122 -0
  274. package/src/media/transcript-persistence-model.ts +15 -0
  275. package/src/media/transcript-persistence-platform.ts +89 -0
  276. package/src/media/transcript-persistence-program.ts +39 -0
  277. package/src/media/transcript-persistence-runtime.ts +24 -0
  278. package/src/media/transcript.ts +616 -0
  279. package/src/media/utf8-order.ts +30 -0
  280. package/src/media/whisper-cpp.ts +464 -0
  281. package/src/media/whisper-language.ts +58 -0
  282. package/src/media/yt-dlp.ts +439 -0
  283. package/src/message-like-me-agentic-messaging.ts +882 -0
  284. package/src/messaging-action-store.ts +793 -0
  285. package/src/messaging-runtime.ts +2136 -0
  286. package/src/messaging-store.ts +539 -0
  287. package/src/messaging-types.ts +1663 -0
  288. package/src/messaging.ts +334 -0
  289. package/src/model.ts +2671 -0
  290. package/src/oauth-google.ts +888 -0
  291. package/src/omni-client-types.ts +306 -0
  292. package/src/omni-client.ts +2225 -0
  293. package/src/omni-limits.ts +34 -0
  294. package/src/omni-model.ts +1614 -0
  295. package/src/omni-request.ts +379 -0
  296. package/src/omni-runtime.ts +1632 -0
  297. package/src/operation-deadline.ts +189 -0
  298. package/src/path-helper.ts +1656 -0
  299. package/src/pinned-https.ts +194 -0
  300. package/src/plan-assets.ts +832 -0
  301. package/src/platform-catalog.ts +1832 -0
  302. package/src/plugins/beeper-linked-device/plugin.ts +310 -0
  303. package/src/plugins/bluesky-web/plugin.ts +178 -0
  304. package/src/plugins/clasificados-web/plugin.ts +66 -0
  305. package/src/plugins/github-web/plugin.ts +80 -0
  306. package/src/plugins/gmail-official/plugin.ts +110 -0
  307. package/src/plugins/hacker-news-web/plugin.ts +52 -0
  308. package/src/plugins/imessage-direct/plugin.ts +195 -0
  309. package/src/plugins/imessage-direct/vendor/0001-fix-keep-AppleScript-send-payloads-out-of-child-argv.patch +1430 -0
  310. package/src/plugins/imessage-direct/vendor/0002-feat-rpc-add-exact-chat-lookup.patch +225 -0
  311. package/src/plugins/imessage-direct/vendor/provenance.json +80 -0
  312. package/src/plugins/linkedin-official/plugin.ts +84 -0
  313. package/src/plugins/linkedin-web/plugin.ts +412 -0
  314. package/src/plugins/meta-web/plugin.ts +317 -0
  315. package/src/plugins/reddit-web/flair.ts +492 -0
  316. package/src/plugins/reddit-web/plugin.ts +477 -0
  317. package/src/plugins/substack-web/plugin.ts +124 -0
  318. package/src/plugins/tiktok-web/plugin.ts +67 -0
  319. package/src/plugins/twitch-web/plugin.ts +54 -0
  320. package/src/plugins/whatsapp-linked-device/plugin.ts +105 -0
  321. package/src/plugins/x-official/plugin.ts +64 -0
  322. package/src/plugins/x-web/plugin.ts +488 -0
  323. package/src/plugins/youtube-web/plugin.ts +101 -0
  324. package/src/portable-run-recovery.ts +466 -0
  325. package/src/process-identity.ts +501 -0
  326. package/src/profile-clone-helper.ts +104 -0
  327. package/src/provider-catalog-views.ts +51 -0
  328. package/src/provider-context.ts +69 -0
  329. package/src/provider-contract-definitions-x.ts +45 -0
  330. package/src/provider-contract-definitions.ts +948 -0
  331. package/src/provider-contract-input-gmail.ts +176 -0
  332. package/src/provider-contract-input-linkedin.ts +77 -0
  333. package/src/provider-contract-input-x.ts +304 -0
  334. package/src/provider-contract-planning.ts +23 -0
  335. package/src/provider-contract-semantic-identity.ts +54 -0
  336. package/src/provider-contracts.ts +251 -0
  337. package/src/provider-http.ts +723 -0
  338. package/src/provider-plugin-auth.ts +36 -0
  339. package/src/provider-plugin-builtins.ts +229 -0
  340. package/src/provider-plugin-cleanup-barrier.ts +229 -0
  341. package/src/provider-plugin-cleanup-execution.ts +79 -0
  342. package/src/provider-plugin-cleanup-resource.ts +470 -0
  343. package/src/provider-plugin-contract-identity.ts +707 -0
  344. package/src/provider-plugin-host.ts +1972 -0
  345. package/src/provider-plugin-identifiers.ts +60 -0
  346. package/src/provider-plugin-invocation-lease.ts +889 -0
  347. package/src/provider-plugin-lifecycle-kernel.ts +566 -0
  348. package/src/provider-plugin-lifecycle.ts +1455 -0
  349. package/src/provider-plugin-module-analysis.ts +336 -0
  350. package/src/provider-plugin-package.ts +2530 -0
  351. package/src/provider-plugin-portable-authority.ts +106 -0
  352. package/src/provider-plugin-portable-catalog.ts +94 -0
  353. package/src/provider-plugin-portable-identity.ts +315 -0
  354. package/src/provider-plugin-portable-runtime.ts +2057 -0
  355. package/src/provider-plugin-protocol.ts +1625 -0
  356. package/src/provider-plugin-registry.ts +3342 -0
  357. package/src/provider-plugin-store.ts +2155 -0
  358. package/src/provider-plugin-test-fixture.ts +5 -0
  359. package/src/provider-plugin.ts +4907 -0
  360. package/src/provider-plugins.generated.ts +43 -0
  361. package/src/provider-plugins.ts +8 -0
  362. package/src/provider-subject.ts +41 -0
  363. package/src/provider.ts +315 -0
  364. package/src/providers/beeper-local-runtime.ts +5426 -0
  365. package/src/providers/beeper-local.ts +2739 -0
  366. package/src/providers/beeper-messaging.ts +462 -0
  367. package/src/providers/beeper-omni.ts +1159 -0
  368. package/src/providers/bluesky-web-runtime.ts +2895 -0
  369. package/src/providers/bluesky-web.ts +1614 -0
  370. package/src/providers/clasificados-web-runtime.ts +255 -0
  371. package/src/providers/clasificados-web.ts +503 -0
  372. package/src/providers/contact-projection.ts +301 -0
  373. package/src/providers/github-read-model.ts +94 -0
  374. package/src/providers/github-read-platform.ts +165 -0
  375. package/src/providers/github-read-program.ts +97 -0
  376. package/src/providers/github-web-runtime.ts +320 -0
  377. package/src/providers/github-web.ts +403 -0
  378. package/src/providers/gmail-api.ts +2834 -0
  379. package/src/providers/gmail-omni.ts +830 -0
  380. package/src/providers/gmail.ts +1130 -0
  381. package/src/providers/hacker-news-web-runtime.ts +220 -0
  382. package/src/providers/hacker-news-web.ts +809 -0
  383. package/src/providers/imessage-direct-install.ts +354 -0
  384. package/src/providers/imessage-direct-messaging.ts +532 -0
  385. package/src/providers/imessage-direct-omni.ts +400 -0
  386. package/src/providers/imessage-direct-runtime.ts +1498 -0
  387. package/src/providers/imessage-direct.ts +351 -0
  388. package/src/providers/instagram-video-foundations.ts +913 -0
  389. package/src/providers/instagram-web-profile-browser.ts +600 -0
  390. package/src/providers/iso-bmff.ts +338 -0
  391. package/src/providers/linkedin-company-platform.ts +42 -0
  392. package/src/providers/linkedin-company-program.ts +88 -0
  393. package/src/providers/linkedin-contact-failure.ts +84 -0
  394. package/src/providers/linkedin-contact-platform.ts +45 -0
  395. package/src/providers/linkedin-contact-program.ts +91 -0
  396. package/src/providers/linkedin-profile-activity-failure.ts +76 -0
  397. package/src/providers/linkedin-profile-activity-platform.ts +41 -0
  398. package/src/providers/linkedin-profile-activity-program.ts +67 -0
  399. package/src/providers/linkedin-read-failure.ts +52 -0
  400. package/src/providers/linkedin-self-platform.ts +53 -0
  401. package/src/providers/linkedin-self-program.ts +122 -0
  402. package/src/providers/linkedin-web-article-browser.ts +794 -0
  403. package/src/providers/linkedin-web-bootstrap.ts +146 -0
  404. package/src/providers/linkedin-web-contact.ts +830 -0
  405. package/src/providers/linkedin-web-feed-browser.ts +832 -0
  406. package/src/providers/linkedin-web-feed.ts +864 -0
  407. package/src/providers/linkedin-web-post-browser.ts +653 -0
  408. package/src/providers/linkedin-web-profile-browser.ts +683 -0
  409. package/src/providers/linkedin-web-runtime.ts +2388 -0
  410. package/src/providers/linkedin-web.ts +3305 -0
  411. package/src/providers/linkedin.ts +1885 -0
  412. package/src/providers/meta-bootstrap.ts +1043 -0
  413. package/src/providers/meta-facebook-group.ts +690 -0
  414. package/src/providers/meta-marketplace-relay.ts +747 -0
  415. package/src/providers/meta-omni.ts +373 -0
  416. package/src/providers/meta-relay-bundle.ts +1049 -0
  417. package/src/providers/meta-web-descriptors.ts +1740 -0
  418. package/src/providers/meta-web-runtime.ts +3388 -0
  419. package/src/providers/meta-web.ts +2931 -0
  420. package/src/providers/read-failure.ts +139 -0
  421. package/src/providers/reddit-omni.ts +462 -0
  422. package/src/providers/reddit-web-runtime.ts +1929 -0
  423. package/src/providers/reddit-web.ts +1611 -0
  424. package/src/providers/rental-listings.ts +460 -0
  425. package/src/providers/substack-omni.ts +406 -0
  426. package/src/providers/substack-video-mp4.ts +33 -0
  427. package/src/providers/substack-web-runtime.ts +2812 -0
  428. package/src/providers/substack-web.ts +1502 -0
  429. package/src/providers/tiktok-video-mp4.ts +33 -0
  430. package/src/providers/tiktok-web-runtime.ts +770 -0
  431. package/src/providers/tiktok-web.ts +2786 -0
  432. package/src/providers/twitch-web-runtime.ts +226 -0
  433. package/src/providers/twitch-web.ts +274 -0
  434. package/src/providers/whatsapp-account-identity.ts +55 -0
  435. package/src/providers/whatsapp-contact-projection-helper.ts +874 -0
  436. package/src/providers/whatsapp-contact-projection-protocol.ts +403 -0
  437. package/src/providers/whatsapp-interaction-projection-helper.ts +1452 -0
  438. package/src/providers/whatsapp-interaction-projection-protocol.ts +462 -0
  439. package/src/providers/whatsapp-message-export-projection-protocol.ts +532 -0
  440. package/src/providers/whatsapp-messaging.ts +132 -0
  441. package/src/providers/whatsapp-omni.ts +586 -0
  442. package/src/providers/whatsapp-web-runtime.ts +3108 -0
  443. package/src/providers/whatsapp-web.ts +1037 -0
  444. package/src/providers/x-image-provenance.ts +523 -0
  445. package/src/providers/x-made-with-ai.ts +106 -0
  446. package/src/providers/x-transaction-id.ts +413 -0
  447. package/src/providers/x-web-runtime.ts +3485 -0
  448. package/src/providers/x-web.ts +2332 -0
  449. package/src/providers/x.ts +1889 -0
  450. package/src/providers/youtube-web-runtime.ts +1400 -0
  451. package/src/providers/youtube-web.ts +1221 -0
  452. package/src/read-admission-runtime.ts +91 -0
  453. package/src/read-client.ts +521 -0
  454. package/src/read-effect-platform.ts +7 -0
  455. package/src/read-effect-runtime.ts +14 -0
  456. package/src/read-effect.ts +52 -0
  457. package/src/read-projection-admission.ts +866 -0
  458. package/src/read-projections.ts +3151 -0
  459. package/src/recovery.ts +1492 -0
  460. package/src/reviewed-template.ts +146 -0
  461. package/src/run-journal.ts +1272 -0
  462. package/src/runtime.ts +5116 -0
  463. package/src/scripts/generate-provider-plugin-catalog.ts +121 -0
  464. package/src/scripts/install-whatsapp-protocol.sh +232 -0
  465. package/src/scripts/recover-install-stages.ts +1491 -0
  466. package/src/scripts/resolve-state-home.ts +30 -0
  467. package/src/scripts/scaffold-web-provider.ts +973 -0
  468. package/src/scripts/sync-bundled-adapters.ts +695 -0
  469. package/src/session-secrets.ts +1183 -0
  470. package/src/state-helper.bunfig.toml +2 -0
  471. package/src/state-helper.ts +2914 -0
  472. package/src/storage.ts +3842 -0
  473. package/src/transport-policy.ts +2 -0
  474. package/src/usage.ts +182 -0
  475. package/src/version.ts +2 -0
  476. package/src/web-session-authentication-policy.ts +136 -0
  477. package/src/web-session-cleanup-admission.ts +2344 -0
  478. package/src/web-session-cleanup-join.ts +99 -0
  479. package/src/web-session-client.ts +996 -0
  480. package/src/web-session-contract-definitions.ts +820 -0
  481. package/src/web-session-contract-planning.ts +42 -0
  482. package/src/web-session-contracts.ts +228 -0
  483. package/src/web-session-cookies.ts +500 -0
  484. package/src/web-session-execution.ts +385 -0
  485. package/src/web-session-http.ts +373 -0
  486. package/src/web-session-read-errors.ts +19 -0
  487. package/src/web-session-read-runtime.ts +124 -0
  488. package/src/web-session-recovery.ts +818 -0
  489. package/src/web-session-sites.ts +58 -0
  490. package/src/web-session-template.ts +994 -0
  491. package/src/web-session.ts +93 -0
  492. package/src/whatsapp-client-binding.ts +21 -0
  493. package/src/whatsapp-client-types.ts +75 -0
  494. package/src/whatsapp-client.ts +395 -0
  495. package/src/whatsapp-export-coordinate.ts +17 -0
  496. package/src/whatsapp-message-bundle-v2.ts +195 -0
  497. package/src/whatsapp-message-like-me-cli.ts +195 -0
  498. package/src/whatsapp-message-like-me-export.ts +1072 -0
  499. package/src/whatsapp-message-like-me-source.ts +1012 -0
  500. package/tsconfig.json +67 -0
@@ -0,0 +1,1663 @@
1
+ /**
2
+ * Side-effect-free provider-neutral messaging DTOs.
3
+ *
4
+ * Raw route, context, reply, and message references are local capabilities.
5
+ * They belong only in caller-owned memory, stdin, or checked private files.
6
+ */
7
+
8
+ import { types as nodeTypes } from "node:util";
9
+
10
+ import { canonicalJson, sha256 } from "./canonical-json";
11
+ import {
12
+ GHOSTGET_MESSAGING_CONTEXT_BINDING_V1_CONTRACT_DESCRIPTOR,
13
+ GHOSTGET_MESSAGING_CONTEXT_BINDING_V1_CONTRACT_HASH,
14
+ GHOSTGET_MESSAGING_CONTEXT_BINDING_V1_CONTRACT_ID,
15
+ GHOSTGET_MESSAGING_CONTEXT_BINDING_V2_CONTRACT_DESCRIPTOR,
16
+ GHOSTGET_MESSAGING_CONTEXT_BINDING_V2_CONTRACT_HASH,
17
+ GHOSTGET_MESSAGING_CONTEXT_BINDING_V2_CONTRACT_ID,
18
+ GHOSTGET_MESSAGING_RECEIPT_BINDING_V1_CONTRACT_DESCRIPTOR,
19
+ GHOSTGET_MESSAGING_RECEIPT_BINDING_V1_CONTRACT_HASH,
20
+ GHOSTGET_MESSAGING_RECEIPT_BINDING_V1_CONTRACT_ID,
21
+ GHOSTGET_MESSAGING_RECEIPT_BINDING_V2_CONTRACT_DESCRIPTOR,
22
+ GHOSTGET_MESSAGING_RECEIPT_BINDING_V2_CONTRACT_HASH,
23
+ GHOSTGET_MESSAGING_RECEIPT_BINDING_V2_CONTRACT_ID,
24
+ parseGhostgetMessagingContextBindingV1,
25
+ parseGhostgetMessagingContextBindingV2,
26
+ type MessageLikeMeSourceConversationCoordinateBindingV1,
27
+ type GhostgetMessagingContextBindingV1,
28
+ type GhostgetMessagingContextBindingV2,
29
+ type GhostgetMessagingReceiptBindingV1,
30
+ type GhostgetMessagingReceiptBindingV2,
31
+ } from "./message-like-me-agentic-messaging";
32
+
33
+ export const MESSAGING_CONTEXT_BINDING_V1_CONTRACT_ID =
34
+ GHOSTGET_MESSAGING_CONTEXT_BINDING_V1_CONTRACT_ID;
35
+ export const MESSAGING_CONTEXT_BINDING_V1_CONTRACT_DESCRIPTOR =
36
+ GHOSTGET_MESSAGING_CONTEXT_BINDING_V1_CONTRACT_DESCRIPTOR;
37
+ export const MESSAGING_CONTEXT_BINDING_V1_CONTRACT_HASH =
38
+ GHOSTGET_MESSAGING_CONTEXT_BINDING_V1_CONTRACT_HASH;
39
+
40
+ export const MESSAGING_CONTEXT_BINDING_V2_CONTRACT_ID =
41
+ GHOSTGET_MESSAGING_CONTEXT_BINDING_V2_CONTRACT_ID;
42
+ export const MESSAGING_CONTEXT_BINDING_V2_CONTRACT_DESCRIPTOR =
43
+ GHOSTGET_MESSAGING_CONTEXT_BINDING_V2_CONTRACT_DESCRIPTOR;
44
+ export const MESSAGING_CONTEXT_BINDING_V2_CONTRACT_HASH =
45
+ GHOSTGET_MESSAGING_CONTEXT_BINDING_V2_CONTRACT_HASH;
46
+
47
+ export const MESSAGING_CONTEXT_BINDING_CONTRACT_ID =
48
+ MESSAGING_CONTEXT_BINDING_V2_CONTRACT_ID;
49
+
50
+ export const MESSAGING_CONTEXT_BINDING_CONTRACT_DESCRIPTOR =
51
+ MESSAGING_CONTEXT_BINDING_V2_CONTRACT_DESCRIPTOR;
52
+
53
+ /** SHA-256 of the canonical descriptor above. */
54
+ export const MESSAGING_CONTEXT_BINDING_CONTRACT_HASH =
55
+ MESSAGING_CONTEXT_BINDING_V2_CONTRACT_HASH;
56
+
57
+ export const MESSAGING_RECEIPT_BINDING_V1_CONTRACT_ID =
58
+ GHOSTGET_MESSAGING_RECEIPT_BINDING_V1_CONTRACT_ID;
59
+ export const MESSAGING_RECEIPT_BINDING_V1_CONTRACT_DESCRIPTOR =
60
+ GHOSTGET_MESSAGING_RECEIPT_BINDING_V1_CONTRACT_DESCRIPTOR;
61
+ export const MESSAGING_RECEIPT_BINDING_V1_CONTRACT_HASH =
62
+ GHOSTGET_MESSAGING_RECEIPT_BINDING_V1_CONTRACT_HASH;
63
+
64
+ export const MESSAGING_RECEIPT_BINDING_V2_CONTRACT_ID =
65
+ GHOSTGET_MESSAGING_RECEIPT_BINDING_V2_CONTRACT_ID;
66
+ export const MESSAGING_RECEIPT_BINDING_V2_CONTRACT_DESCRIPTOR =
67
+ GHOSTGET_MESSAGING_RECEIPT_BINDING_V2_CONTRACT_DESCRIPTOR;
68
+ export const MESSAGING_RECEIPT_BINDING_V2_CONTRACT_HASH =
69
+ GHOSTGET_MESSAGING_RECEIPT_BINDING_V2_CONTRACT_HASH;
70
+
71
+ export const MESSAGING_RECEIPT_BINDING_CONTRACT_ID =
72
+ MESSAGING_RECEIPT_BINDING_V2_CONTRACT_ID;
73
+
74
+ export const MESSAGING_RECEIPT_BINDING_CONTRACT_DESCRIPTOR =
75
+ MESSAGING_RECEIPT_BINDING_V2_CONTRACT_DESCRIPTOR;
76
+
77
+ export const MESSAGING_RECEIPT_BINDING_CONTRACT_HASH =
78
+ MESSAGING_RECEIPT_BINDING_V2_CONTRACT_HASH;
79
+
80
+ export type MessagingSourceConversationCoordinateBindingV1 =
81
+ MessageLikeMeSourceConversationCoordinateBindingV1;
82
+
83
+ export type MessagingRoutesRequestSourceV1 = {
84
+ readonly adapterId: string;
85
+ readonly authId: string;
86
+ readonly listInput: Readonly<Record<string, unknown>>;
87
+ };
88
+
89
+ export type MessagingRoutesRequestV1 = {
90
+ readonly schemaVersion: 1;
91
+ readonly format: "wrench.messaging-routes-request";
92
+ /** One exact adapter, auth realm, and provider list coordinate. */
93
+ readonly source: MessagingRoutesRequestSourceV1;
94
+ };
95
+
96
+ export type MessagingRouteCoordinateV1 =
97
+ | {
98
+ readonly kind: "beeperConversation";
99
+ readonly network: string;
100
+ readonly conversationId: string;
101
+ }
102
+ | {
103
+ readonly kind: "imessageChat";
104
+ readonly chatGuid: string;
105
+ readonly service: string | null;
106
+ readonly observedChatRowId: number | null;
107
+ }
108
+ | {
109
+ readonly kind: "whatsappJid";
110
+ readonly jid: string;
111
+ };
112
+
113
+ export type MessagingRouteResolveRequestV1 = {
114
+ readonly schemaVersion: 1;
115
+ readonly format: "wrench.messaging-route-resolve-request";
116
+ readonly source: MessagingRoutesRequestSourceV1;
117
+ /** Closed exact provider coordinate, never a name, handle, or participant. */
118
+ readonly candidate: {
119
+ readonly coordinate: MessagingRouteCoordinateV1;
120
+ };
121
+ };
122
+
123
+ export type MessagingRouteResolveRequestV2 = {
124
+ readonly schemaVersion: 2;
125
+ readonly format: "wrench.messaging-route-resolve-request";
126
+ /** Opaque reference to one checked list candidate in Ghostget private state. */
127
+ readonly routeRef: string;
128
+ };
129
+
130
+ export type MessagingRouteV1 = {
131
+ readonly schemaVersion: 1;
132
+ readonly format: "wrench.messaging-route";
133
+ readonly routeRef: string;
134
+ readonly network: string;
135
+ readonly conversation: {
136
+ readonly kind: "single" | "group" | "unknown";
137
+ readonly title: string | null;
138
+ readonly participantCount: number;
139
+ };
140
+ readonly readiness: {
141
+ readonly context: "ready" | "historical-readable";
142
+ readonly turn: "ready" | "unavailable";
143
+ readonly reply: "supported" | "unsupported";
144
+ readonly reason: string | null;
145
+ };
146
+ readonly completeness: {
147
+ readonly kind:
148
+ | "complete"
149
+ | "page"
150
+ | "unknown"
151
+ | "first-page-only"
152
+ | "bounded-local"
153
+ | "search-window"
154
+ | "truncated";
155
+ readonly reason: string | null;
156
+ };
157
+ readonly expiresAt: string;
158
+ };
159
+
160
+ export type MessagingRouteV2 = {
161
+ readonly schemaVersion: 2;
162
+ readonly format: "wrench.messaging-route";
163
+ readonly routeRef: string;
164
+ readonly network: string;
165
+ readonly conversation: MessagingRouteV1["conversation"];
166
+ readonly readiness: {
167
+ readonly context:
168
+ | "ready"
169
+ | "historical-readable"
170
+ | "resolution-required"
171
+ | "unavailable";
172
+ readonly turn: "ready" | "unavailable";
173
+ readonly reply: "supported" | "unsupported";
174
+ readonly reason: string | null;
175
+ };
176
+ readonly completeness: MessagingRouteV1["completeness"];
177
+ readonly expiresAt: string;
178
+ };
179
+
180
+ export type MessagingRoutesV1 = {
181
+ readonly schemaVersion: 1;
182
+ readonly format: "wrench.messaging-routes";
183
+ readonly generatedAt: string;
184
+ readonly completeness: MessagingRouteV1["completeness"];
185
+ readonly continuation: {
186
+ readonly direction: "forward" | "backward" | "none";
187
+ readonly request: string | null;
188
+ readonly nextInput: Readonly<Record<string, unknown>> | null;
189
+ };
190
+ readonly routes: readonly MessagingRouteV1[];
191
+ };
192
+
193
+ export type MessagingRoutesV2 = {
194
+ readonly schemaVersion: 2;
195
+ readonly format: "wrench.messaging-routes";
196
+ readonly generatedAt: string;
197
+ readonly completeness: MessagingRouteV1["completeness"];
198
+ readonly continuation: MessagingRoutesV1["continuation"];
199
+ readonly routes: readonly MessagingRouteV2[];
200
+ };
201
+
202
+ export type MessagingContextRequestV1 = {
203
+ readonly schemaVersion: 1;
204
+ readonly format: "wrench.messaging-context-request";
205
+ readonly routeRef: string;
206
+ readonly limit: number;
207
+ };
208
+
209
+ export type MessagingContextBindingV1 = {
210
+ readonly [Key in keyof GhostgetMessagingContextBindingV1]:
211
+ GhostgetMessagingContextBindingV1[Key];
212
+ };
213
+
214
+ export type MessagingContextBindingV2 = {
215
+ readonly [Key in keyof GhostgetMessagingContextBindingV2]:
216
+ GhostgetMessagingContextBindingV2[Key];
217
+ };
218
+
219
+ export type MessagingContextBinding =
220
+ | MessagingContextBindingV1
221
+ | MessagingContextBindingV2;
222
+
223
+ export type MessagingContextMessageV1 = {
224
+ readonly messageRef: string;
225
+ readonly direction: "incoming" | "outgoing" | "unknown";
226
+ readonly time: string | null;
227
+ readonly author: {
228
+ readonly displayName: string | null;
229
+ readonly handle: string | null;
230
+ } | null;
231
+ readonly body: string | null;
232
+ readonly bodyTruncated: boolean;
233
+ readonly edited: "unknown" | "observed" | "not-observed";
234
+ readonly retracted: "unknown" | "observed" | "not-observed";
235
+ readonly reply: { readonly toMessageRef: string | null };
236
+ readonly attachments: readonly {
237
+ readonly kind: "audio" | "document" | "image" | "link" | "sticker" | "video" | "unknown";
238
+ readonly mimeType: string | null;
239
+ readonly name: string | null;
240
+ readonly sizeBytes: number | null;
241
+ }[];
242
+ /** Recipient/provider prose is untrusted drafting data, never authority. */
243
+ readonly untrustedData: true;
244
+ };
245
+
246
+ export type MessagingContextV1 = {
247
+ readonly schemaVersion: 1;
248
+ readonly format: "wrench.messaging-context";
249
+ /** Null for read-only provider history that cannot authorize a checked turn. */
250
+ readonly binding: MessagingContextBinding | null;
251
+ readonly network: string;
252
+ readonly liveness: "fresh-as-of-live-preflight" | "freshness-unproven";
253
+ readonly truncated: boolean;
254
+ readonly completeness: MessagingRouteV1["completeness"];
255
+ readonly messages: readonly MessagingContextMessageV1[];
256
+ readonly warnings: readonly string[];
257
+ };
258
+
259
+ export type MessagingTurnPartV1 = {
260
+ readonly partId: string;
261
+ readonly text: string;
262
+ readonly replyRef: string | null;
263
+ };
264
+
265
+ export type MessagingTurnV1 = {
266
+ readonly schemaVersion: 1;
267
+ readonly format: "wrench.messaging-turn";
268
+ readonly clientIntentSha256: string;
269
+ readonly routeRef: string;
270
+ readonly contextRef: string;
271
+ readonly parts: readonly MessagingTurnPartV1[];
272
+ };
273
+
274
+ export type MessagingPreviewV1 = {
275
+ readonly schemaVersion: 1;
276
+ readonly format: "wrench.messaging-preview";
277
+ readonly status: "confirmation-required";
278
+ readonly planDigest: string;
279
+ readonly expiresAt: string;
280
+ readonly routeRef: string;
281
+ readonly contextRef: string;
282
+ readonly clientIntentSha256: string;
283
+ readonly turnDigest: string;
284
+ readonly recipient: {
285
+ readonly network: string;
286
+ readonly conversation: MessagingRouteV1["conversation"];
287
+ };
288
+ readonly partCount: number;
289
+ readonly bubbles: readonly {
290
+ readonly partId: string;
291
+ readonly text: string;
292
+ readonly replyRef: string | null;
293
+ }[];
294
+ readonly risk: "R3";
295
+ readonly sideEffect: string;
296
+ };
297
+
298
+ export type MessagingReceiptBindingV1 = {
299
+ readonly [Key in keyof GhostgetMessagingReceiptBindingV1]:
300
+ GhostgetMessagingReceiptBindingV1[Key];
301
+ };
302
+
303
+ export type MessagingReceiptBindingV2 = {
304
+ readonly [Key in keyof GhostgetMessagingReceiptBindingV2]:
305
+ GhostgetMessagingReceiptBindingV2[Key];
306
+ };
307
+
308
+ export type MessagingReceiptBinding =
309
+ | MessagingReceiptBindingV1
310
+ | MessagingReceiptBindingV2;
311
+
312
+ export type MessagingPartJournalStateV1 =
313
+ | "unattempted"
314
+ | "claimed"
315
+ | "dispatching"
316
+ | "accepted"
317
+ | "failed-before-dispatch"
318
+ | "failed-permanent"
319
+ | "indeterminate";
320
+
321
+ /** Provider-defined diagnostic retained only inside the encrypted run. */
322
+ export type MessagingPrivateProviderOutcomeV1 = {
323
+ readonly schemaVersion: 1;
324
+ readonly messagingContractId: string;
325
+ readonly code: string;
326
+ };
327
+
328
+ export type MessagingRunV1 = {
329
+ readonly schemaVersion: 1;
330
+ readonly format: "wrench.messaging-run";
331
+ readonly runId: string;
332
+ readonly planDigest: string;
333
+ readonly routeRef: string;
334
+ readonly contextRef: string;
335
+ readonly clientIntentSha256: string;
336
+ readonly contextBindingSha256: string | null;
337
+ readonly sourceConversationCoordinateSha256: string | null;
338
+ readonly turnDigest: string;
339
+ readonly previewDigest: string;
340
+ readonly state: "pending" | "submitted" | "failed" | "partial" | "indeterminate";
341
+ readonly partCount: number;
342
+ readonly provenPartCount: number;
343
+ /** Durable high-water mark for accepted own messages proven in live context. */
344
+ readonly observedAcceptedPrefixCount: number;
345
+ readonly possibleSubmittedPartIndex: number | null;
346
+ readonly privateProviderOutcome: MessagingPrivateProviderOutcomeV1 | null;
347
+ readonly terminalReason:
348
+ | null
349
+ | "context-drift"
350
+ | "prefix-freshness-unproven"
351
+ | "provider-failed-before-dispatch"
352
+ | "provider-result-indeterminate"
353
+ | "journal-recovery-required";
354
+ readonly parts: readonly {
355
+ readonly partId: string;
356
+ readonly text: string;
357
+ readonly replyRef: string | null;
358
+ readonly replyToProviderId: string | null;
359
+ readonly direction: "outgoing";
360
+ readonly bodySha256: string;
361
+ readonly state: MessagingPartJournalStateV1;
362
+ readonly providerMessageId: string | null;
363
+ readonly providerRevision: string | null;
364
+ readonly delivery: "unknown" | "delivered" | "failed";
365
+ readonly read: "unknown" | "read";
366
+ }[];
367
+ readonly startedAt: string;
368
+ readonly recordedAt: string;
369
+ };
370
+
371
+ export type MessagingRunReceiptV1 = {
372
+ readonly schemaVersion: 1;
373
+ readonly format: "wrench.messaging-run-receipt";
374
+ readonly planDigest: string;
375
+ readonly runId: string;
376
+ readonly state: "submitted" | "failed" | "partial" | "indeterminate";
377
+ readonly partCount: number;
378
+ readonly provenPartCount: number;
379
+ readonly clientIntentSha256: string;
380
+ readonly routeRefSha256: string;
381
+ readonly contextRefSha256: string;
382
+ readonly turnDigest: string;
383
+ readonly previewDigest: string;
384
+ readonly receiptBindingSha256: string;
385
+ readonly recordedAt: string;
386
+ };
387
+
388
+ export type MessagingRunReceiptV2 = {
389
+ readonly schemaVersion: 2;
390
+ readonly format: "wrench.messaging-run-receipt";
391
+ readonly planDigest: string;
392
+ readonly runId: string;
393
+ readonly state: "submitted" | "failed" | "partial" | "indeterminate";
394
+ readonly partCount: number;
395
+ readonly provenPartCount: number;
396
+ readonly clientIntentSha256: string;
397
+ readonly contextBindingSha256: string;
398
+ readonly sourceConversationCoordinateSha256: string;
399
+ readonly routeRefSha256: string;
400
+ readonly contextRefSha256: string;
401
+ readonly turnDigest: string;
402
+ readonly previewDigest: string;
403
+ readonly receiptBindingSha256: string;
404
+ readonly recordedAt: string;
405
+ };
406
+
407
+ export type MessagingRunReceipt = MessagingRunReceiptV1 | MessagingRunReceiptV2;
408
+
409
+ export type MessagingClientEnvironment = Readonly<
410
+ Record<string, string | undefined>
411
+ >;
412
+
413
+ export type MessagingClientOptions = {
414
+ readonly environment?: MessagingClientEnvironment;
415
+ readonly signal?: AbortSignal;
416
+ };
417
+
418
+ export type MessagingPrivateOutputReceiptV1 = {
419
+ readonly schemaVersion: 1;
420
+ readonly format: "wrench.messaging-private-output-receipt";
421
+ readonly artifactFormat:
422
+ | "wrench.messaging-route"
423
+ | "wrench.messaging-routes"
424
+ | "wrench.messaging-context"
425
+ | "wrench.messaging-preview"
426
+ | "wrench.messaging-run"
427
+ | "wrench.messaging-receipt-binding";
428
+ readonly artifactSha256: string;
429
+ readonly itemCount: number;
430
+ readonly generatedAt: string;
431
+ readonly expiresAt: string | null;
432
+ };
433
+
434
+ type JsonRecord = Record<string, unknown>;
435
+
436
+ function fail(label: string, detail: string): never {
437
+ throw new Error(`${label} ${detail}`);
438
+ }
439
+
440
+ function record(value: unknown, label: string): JsonRecord {
441
+ if (
442
+ nodeTypes.isProxy(value)
443
+ || typeof value !== "object"
444
+ || value === null
445
+ || Array.isArray(value)
446
+ || (
447
+ Object.getPrototypeOf(value) !== Object.prototype
448
+ && Object.getPrototypeOf(value) !== null
449
+ )
450
+ ) return fail(label, "must be a plain non-proxy object");
451
+ const descriptors = Object.getOwnPropertyDescriptors(value);
452
+ if (Reflect.ownKeys(descriptors).some((key) => typeof key !== "string")) {
453
+ return fail(label, "must not contain symbol properties");
454
+ }
455
+ const result: JsonRecord = {};
456
+ for (const [key, descriptor] of Object.entries(descriptors)) {
457
+ if (!descriptor.enumerable || !("value" in descriptor)) {
458
+ return fail(`${label}.${key}`, "must be an enumerable data property");
459
+ }
460
+ result[key] = descriptor.value;
461
+ }
462
+ return result;
463
+ }
464
+
465
+ function exactKeys(
466
+ value: JsonRecord,
467
+ required: readonly string[],
468
+ optional: readonly string[],
469
+ label: string,
470
+ ): void {
471
+ const allowed = new Set([...required, ...optional]);
472
+ if (
473
+ required.some((key) => !Object.hasOwn(value, key))
474
+ || Object.keys(value).some((key) => !allowed.has(key))
475
+ ) return fail(label, "has unsupported or missing fields");
476
+ }
477
+
478
+ function denseArray(value: unknown, label: string, maximum: number): readonly unknown[] {
479
+ if (
480
+ nodeTypes.isProxy(value)
481
+ || !Array.isArray(value)
482
+ || Object.getPrototypeOf(value) !== Array.prototype
483
+ || value.length > maximum
484
+ ) return fail(label, `must be a dense array of at most ${maximum} items`);
485
+ for (let index = 0; index < value.length; index += 1) {
486
+ if (!Object.hasOwn(value, index)) return fail(label, "must not be sparse");
487
+ const descriptor = Object.getOwnPropertyDescriptor(value, index);
488
+ if (descriptor === undefined || !("value" in descriptor) || !descriptor.enumerable) {
489
+ return fail(label, "must contain only enumerable data items");
490
+ }
491
+ }
492
+ const expected = new Set<PropertyKey>([
493
+ "length",
494
+ ...Array.from({ length: value.length }, (_unused, index) => String(index)),
495
+ ]);
496
+ if (Reflect.ownKeys(value).some((key) => !expected.has(key))) {
497
+ return fail(label, "must not contain named or symbol properties");
498
+ }
499
+ return value;
500
+ }
501
+
502
+ function hasUnpairedSurrogate(value: string): boolean {
503
+ for (let index = 0; index < value.length; index += 1) {
504
+ const code = value.charCodeAt(index);
505
+ if (code >= 0xd800 && code <= 0xdbff) {
506
+ const next = value.charCodeAt(index + 1);
507
+ if (next < 0xdc00 || next > 0xdfff) return true;
508
+ index += 1;
509
+ } else if (code >= 0xdc00 && code <= 0xdfff) return true;
510
+ }
511
+ return false;
512
+ }
513
+
514
+ function text(
515
+ value: unknown,
516
+ label: string,
517
+ maximumBytes: number,
518
+ options: { readonly allowEmpty?: boolean; readonly allowNewlines?: boolean } = {},
519
+ ): string {
520
+ if (
521
+ typeof value !== "string"
522
+ || (!options.allowEmpty && value.length === 0)
523
+ || Buffer.byteLength(value, "utf8") > maximumBytes
524
+ || hasUnpairedSurrogate(value)
525
+ || /[\0\u007f-\u009f\u061c\u200e\u200f\u2028-\u202e\u2066-\u2069]/u.test(value)
526
+ || (!options.allowNewlines && /[\r\n]/u.test(value))
527
+ ) return fail(label, "must be bounded well-formed inert text");
528
+ return value;
529
+ }
530
+
531
+ function id(value: unknown, label: string, maximum = 256): string {
532
+ const result = text(value, label, maximum);
533
+ if (!/^[A-Za-z0-9][A-Za-z0-9._~-]*$/u.test(result)) {
534
+ return fail(label, "must be an exact opaque identifier");
535
+ }
536
+ return result;
537
+ }
538
+
539
+ function routeRef(value: unknown, label: string): string {
540
+ const result = id(value, label, 128);
541
+ if (!/^wmroute_[A-Za-z0-9_-]{22}$/u.test(result)) {
542
+ return fail(label, "must be a Ghostget messaging route reference");
543
+ }
544
+ return result;
545
+ }
546
+
547
+ function contextRef(value: unknown, label: string): string {
548
+ const result = id(value, label, 128);
549
+ if (!/^wmcontext_[A-Za-z0-9_-]{22}$/u.test(result)) {
550
+ return fail(label, "must be a Ghostget messaging context reference");
551
+ }
552
+ return result;
553
+ }
554
+
555
+ function replyRef(value: unknown, label: string): string {
556
+ const result = id(value, label, 128);
557
+ if (!/^wmreply_[A-Za-z0-9_-]{22}$/u.test(result)) {
558
+ return fail(label, "must be a Ghostget messaging reply reference");
559
+ }
560
+ return result;
561
+ }
562
+
563
+ function integer(value: unknown, label: string, minimum: number, maximum: number): number {
564
+ if (
565
+ typeof value !== "number"
566
+ || !Number.isSafeInteger(value)
567
+ || value < minimum
568
+ || value > maximum
569
+ ) return fail(label, `must be an integer from ${minimum} through ${maximum}`);
570
+ return value;
571
+ }
572
+
573
+ function cloneJsonRecord(value: unknown, label: string): Readonly<Record<string, unknown>> {
574
+ let nodes = 0;
575
+ const ancestors = new WeakSet<object>();
576
+ const visit = (candidate: unknown, path: string, depth: number): unknown => {
577
+ nodes += 1;
578
+ if (nodes > 10_000 || depth > 32) return fail(label, "exceeds its structural bound");
579
+ if (candidate === null || typeof candidate === "boolean" || typeof candidate === "string") {
580
+ if (typeof candidate === "string") text(candidate, path, 256 * 1024, { allowEmpty: true, allowNewlines: true });
581
+ return candidate;
582
+ }
583
+ if (typeof candidate === "number") {
584
+ if (!Number.isFinite(candidate)) return fail(path, "must contain finite JSON numbers");
585
+ return candidate;
586
+ }
587
+ if (typeof candidate !== "object") return fail(path, "must contain only JSON data");
588
+ if (ancestors.has(candidate)) return fail(label, "must not contain cycles");
589
+ ancestors.add(candidate);
590
+ try {
591
+ if (Array.isArray(candidate)) {
592
+ return Object.freeze(denseArray(candidate, path, 1_000)
593
+ .map((item, index) => visit(item, `${path}[${index}]`, depth + 1)));
594
+ }
595
+ const source = record(candidate, path);
596
+ const entries = Object.entries(source).map(([key, item]) => {
597
+ text(key, `${path} key`, 256);
598
+ return [key, visit(item, `${path}.${key}`, depth + 1)] as const;
599
+ });
600
+ return Object.freeze(Object.fromEntries(entries));
601
+ } finally {
602
+ ancestors.delete(candidate);
603
+ }
604
+ };
605
+ return record(visit(value, label, 0), label);
606
+ }
607
+
608
+ function parseRoutesSource(
609
+ value: unknown,
610
+ label: string,
611
+ ): MessagingRoutesRequestSourceV1 {
612
+ const item = record(value, label);
613
+ exactKeys(item, ["adapterId", "authId", "listInput"], [], label);
614
+ const exactSource = Object.freeze({
615
+ adapterId: id(item.adapterId, `${label}.adapterId`, 64),
616
+ authId: id(item.authId, `${label}.authId`, 48),
617
+ listInput: cloneJsonRecord(item.listInput, `${label}.listInput`),
618
+ });
619
+ return exactSource;
620
+ }
621
+
622
+ export function parseMessagingRoutesRequestV1(value: unknown): MessagingRoutesRequestV1 {
623
+ const source = record(value, "messaging routes request");
624
+ exactKeys(source, ["schemaVersion", "format", "source"], [], "messaging routes request");
625
+ if (source.schemaVersion !== 1 || source.format !== "wrench.messaging-routes-request") {
626
+ return fail("messaging routes request", "has an unsupported contract");
627
+ }
628
+ return Object.freeze({
629
+ schemaVersion: 1,
630
+ format: "wrench.messaging-routes-request",
631
+ source: parseRoutesSource(source.source, "messaging routes request.source"),
632
+ });
633
+ }
634
+
635
+ export function parseMessagingRouteResolveRequestV1(
636
+ value: unknown,
637
+ ): MessagingRouteResolveRequestV1 {
638
+ const source = record(value, "messaging route resolve request");
639
+ exactKeys(
640
+ source,
641
+ ["schemaVersion", "format", "source", "candidate"],
642
+ [],
643
+ "messaging route resolve request",
644
+ );
645
+ if (
646
+ source.schemaVersion !== 1
647
+ || source.format !== "wrench.messaging-route-resolve-request"
648
+ ) return fail("messaging route resolve request", "has an unsupported contract");
649
+ const candidate = record(source.candidate, "messaging route resolve request.candidate");
650
+ exactKeys(
651
+ candidate,
652
+ ["coordinate"],
653
+ [],
654
+ "messaging route resolve request.candidate",
655
+ );
656
+ const coordinateSource = record(
657
+ candidate.coordinate,
658
+ "messaging route resolve request.candidate.coordinate",
659
+ );
660
+ let coordinate: MessagingRouteCoordinateV1;
661
+ if (coordinateSource.kind === "beeperConversation") {
662
+ exactKeys(
663
+ coordinateSource,
664
+ ["kind", "network", "conversationId"],
665
+ [],
666
+ "messaging route resolve request.candidate.coordinate",
667
+ );
668
+ coordinate = Object.freeze({
669
+ kind: "beeperConversation",
670
+ network: id(
671
+ coordinateSource.network,
672
+ "messaging route resolve request.candidate.coordinate.network",
673
+ 64,
674
+ ),
675
+ conversationId: text(
676
+ coordinateSource.conversationId,
677
+ "messaging route resolve request.candidate.coordinate.conversationId",
678
+ 2_048,
679
+ ),
680
+ });
681
+ } else if (coordinateSource.kind === "imessageChat") {
682
+ exactKeys(
683
+ coordinateSource,
684
+ ["kind", "chatGuid", "service", "observedChatRowId"],
685
+ [],
686
+ "messaging route resolve request.candidate.coordinate",
687
+ );
688
+ coordinate = Object.freeze({
689
+ kind: "imessageChat",
690
+ chatGuid: text(
691
+ coordinateSource.chatGuid,
692
+ "messaging route resolve request.candidate.coordinate.chatGuid",
693
+ 2_048,
694
+ ),
695
+ service: coordinateSource.service === null
696
+ ? null
697
+ : id(
698
+ coordinateSource.service,
699
+ "messaging route resolve request.candidate.coordinate.service",
700
+ 64,
701
+ ),
702
+ observedChatRowId: coordinateSource.observedChatRowId === null
703
+ ? null
704
+ : integer(
705
+ coordinateSource.observedChatRowId,
706
+ "messaging route resolve request.candidate.coordinate.observedChatRowId",
707
+ 1,
708
+ Number.MAX_SAFE_INTEGER,
709
+ ),
710
+ });
711
+ } else if (coordinateSource.kind === "whatsappJid") {
712
+ exactKeys(
713
+ coordinateSource,
714
+ ["kind", "jid"],
715
+ [],
716
+ "messaging route resolve request.candidate.coordinate",
717
+ );
718
+ coordinate = Object.freeze({
719
+ kind: "whatsappJid",
720
+ jid: text(
721
+ coordinateSource.jid,
722
+ "messaging route resolve request.candidate.coordinate.jid",
723
+ 512,
724
+ ),
725
+ });
726
+ } else {
727
+ return fail(
728
+ "messaging route resolve request.candidate.coordinate.kind",
729
+ "is unsupported",
730
+ );
731
+ }
732
+ return Object.freeze({
733
+ schemaVersion: 1,
734
+ format: "wrench.messaging-route-resolve-request",
735
+ source: parseRoutesSource(source.source, "messaging route resolve request.source"),
736
+ candidate: Object.freeze({ coordinate }),
737
+ });
738
+ }
739
+
740
+ export function parseMessagingRouteResolveRequestV2(
741
+ value: unknown,
742
+ ): MessagingRouteResolveRequestV2 {
743
+ const source = record(value, "messaging route resolve request V2");
744
+ exactKeys(
745
+ source,
746
+ ["schemaVersion", "format", "routeRef"],
747
+ [],
748
+ "messaging route resolve request V2",
749
+ );
750
+ if (
751
+ source.schemaVersion !== 2
752
+ || source.format !== "wrench.messaging-route-resolve-request"
753
+ ) return fail("messaging route resolve request V2", "has an unsupported contract");
754
+ return Object.freeze({
755
+ schemaVersion: 2,
756
+ format: "wrench.messaging-route-resolve-request",
757
+ routeRef: routeRef(
758
+ source.routeRef,
759
+ "messaging route resolve request V2.routeRef",
760
+ ),
761
+ });
762
+ }
763
+
764
+ export function parseMessagingContextRequestV1(value: unknown): MessagingContextRequestV1 {
765
+ const source = record(value, "messaging context request");
766
+ exactKeys(source, ["schemaVersion", "format", "routeRef", "limit"], [], "messaging context request");
767
+ if (source.schemaVersion !== 1 || source.format !== "wrench.messaging-context-request") {
768
+ return fail("messaging context request", "has an unsupported contract");
769
+ }
770
+ return Object.freeze({
771
+ schemaVersion: 1,
772
+ format: "wrench.messaging-context-request",
773
+ routeRef: routeRef(source.routeRef, "messaging context request.routeRef"),
774
+ limit: integer(source.limit, "messaging context request.limit", 1, 200),
775
+ });
776
+ }
777
+
778
+ export function parseMessagingTurnV1(value: unknown): MessagingTurnV1 {
779
+ const source = record(value, "messaging turn");
780
+ exactKeys(source, [
781
+ "schemaVersion",
782
+ "format",
783
+ "clientIntentSha256",
784
+ "routeRef",
785
+ "contextRef",
786
+ "parts",
787
+ ], [], "messaging turn");
788
+ if (source.schemaVersion !== 1 || source.format !== "wrench.messaging-turn") {
789
+ return fail("messaging turn", "has an unsupported contract");
790
+ }
791
+ const seen = new Set<string>();
792
+ const parts = denseArray(source.parts, "messaging turn.parts", 8)
793
+ .map((candidate, index) => {
794
+ const item = record(candidate, `messaging turn.parts[${index}]`);
795
+ exactKeys(item, ["partId", "text", "replyRef"], [], `messaging turn.parts[${index}]`);
796
+ const partId = id(item.partId, `messaging turn.parts[${index}].partId`, 64);
797
+ if (seen.has(partId)) return fail("messaging turn.parts", "must not repeat part IDs");
798
+ seen.add(partId);
799
+ return Object.freeze({
800
+ partId,
801
+ text: text(item.text, `messaging turn.parts[${index}].text`, 65_536, { allowNewlines: true }),
802
+ replyRef: item.replyRef === null
803
+ ? null
804
+ : replyRef(item.replyRef, `messaging turn.parts[${index}].replyRef`),
805
+ });
806
+ });
807
+ if (parts.length === 0) return fail("messaging turn.parts", "must not be empty");
808
+ return Object.freeze({
809
+ schemaVersion: 1,
810
+ format: "wrench.messaging-turn",
811
+ clientIntentSha256: sha256Digest(
812
+ source.clientIntentSha256,
813
+ "messaging turn.clientIntentSha256",
814
+ ),
815
+ routeRef: routeRef(source.routeRef, "messaging turn.routeRef"),
816
+ contextRef: contextRef(source.contextRef, "messaging turn.contextRef"),
817
+ parts: Object.freeze(parts),
818
+ });
819
+ }
820
+
821
+ export function messagingTurnDigest(turn: MessagingTurnV1): string {
822
+ const parsed = parseMessagingTurnV1(turn);
823
+ return sha256(canonicalJson({
824
+ schemaVersion: 1,
825
+ format: "wrench.messaging-turn",
826
+ clientIntentSha256: parsed.clientIntentSha256,
827
+ routeRef: parsed.routeRef,
828
+ contextRef: parsed.contextRef,
829
+ parts: parsed.parts.map((part) => ({
830
+ partId: part.partId,
831
+ text: part.text,
832
+ replyRef: part.replyRef,
833
+ })),
834
+ }));
835
+ }
836
+
837
+ export function parseMessagingContextBindingV1(value: unknown): MessagingContextBindingV1 {
838
+ const parsed = parseGhostgetMessagingContextBindingV1(value);
839
+ routeRef(parsed.routeRef, "messaging context binding.routeRef");
840
+ contextRef(parsed.contextRef, "messaging context binding.contextRef");
841
+ return parsed;
842
+ }
843
+
844
+ export function parseMessagingContextBindingV2(value: unknown): MessagingContextBindingV2 {
845
+ const parsed = parseGhostgetMessagingContextBindingV2(value);
846
+ routeRef(parsed.routeRef, "messaging context binding.routeRef");
847
+ contextRef(parsed.contextRef, "messaging context binding.contextRef");
848
+ return parsed;
849
+ }
850
+
851
+ export function parseMessagingContextBinding(value: unknown): MessagingContextBinding {
852
+ const source = record(value, "messaging context binding");
853
+ return source.contractId === MESSAGING_CONTEXT_BINDING_V1_CONTRACT_ID
854
+ ? parseMessagingContextBindingV1(source)
855
+ : parseMessagingContextBindingV2(source);
856
+ }
857
+
858
+ function sha256Digest(candidate: unknown, label: string): string {
859
+ const result = text(candidate, label, 64);
860
+ if (!/^[a-f0-9]{64}$/u.test(result)) return fail(label, "must be a SHA-256 digest");
861
+ return result;
862
+ }
863
+
864
+ function utcTimestamp(candidate: unknown, label: string): string {
865
+ const result = text(candidate, label, 64);
866
+ if (
867
+ !/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,9})?Z$/u.test(result)
868
+ || !Number.isFinite(Date.parse(result))
869
+ ) return fail(label, "must be an RFC3339 UTC timestamp");
870
+ return result;
871
+ }
872
+
873
+ function nullableBoundedText(
874
+ candidate: unknown,
875
+ label: string,
876
+ maximumBytes: number,
877
+ allowNewlines = false,
878
+ ): string | null {
879
+ return candidate === null
880
+ ? null
881
+ : text(candidate, label, maximumBytes, {
882
+ allowEmpty: true,
883
+ allowNewlines,
884
+ });
885
+ }
886
+
887
+ function bool(candidate: unknown, label: string): boolean {
888
+ if (typeof candidate !== "boolean") return fail(label, "must be boolean");
889
+ return candidate;
890
+ }
891
+
892
+ function completeness(
893
+ value: unknown,
894
+ label: string,
895
+ ): MessagingRouteV1["completeness"] {
896
+ const source = record(value, label);
897
+ exactKeys(source, ["kind", "reason"], [], label);
898
+ const kinds = new Set<MessagingRouteV1["completeness"]["kind"]>([
899
+ "complete",
900
+ "page",
901
+ "unknown",
902
+ "first-page-only",
903
+ "bounded-local",
904
+ "search-window",
905
+ "truncated",
906
+ ]);
907
+ if (!kinds.has(source.kind as MessagingRouteV1["completeness"]["kind"])) {
908
+ return fail(`${label}.kind`, "is unsupported");
909
+ }
910
+ return Object.freeze({
911
+ kind: source.kind as MessagingRouteV1["completeness"]["kind"],
912
+ reason: nullableBoundedText(source.reason, `${label}.reason`, 2_048),
913
+ });
914
+ }
915
+
916
+ function conversation(
917
+ value: unknown,
918
+ label: string,
919
+ ): MessagingRouteV1["conversation"] {
920
+ const source = record(value, label);
921
+ exactKeys(source, ["kind", "title", "participantCount"], [], label);
922
+ if (
923
+ source.kind !== "single"
924
+ && source.kind !== "group"
925
+ && source.kind !== "unknown"
926
+ ) return fail(`${label}.kind`, "is unsupported");
927
+ return Object.freeze({
928
+ kind: source.kind,
929
+ title: nullableBoundedText(source.title, `${label}.title`, 4_096, true),
930
+ participantCount: integer(
931
+ source.participantCount,
932
+ `${label}.participantCount`,
933
+ 0,
934
+ 10_000,
935
+ ),
936
+ });
937
+ }
938
+
939
+ export function parseMessagingRouteV1(value: unknown): MessagingRouteV1 {
940
+ const source = record(value, "messaging route");
941
+ exactKeys(source, [
942
+ "schemaVersion",
943
+ "format",
944
+ "routeRef",
945
+ "network",
946
+ "conversation",
947
+ "readiness",
948
+ "completeness",
949
+ "expiresAt",
950
+ ], [], "messaging route");
951
+ if (source.schemaVersion !== 1 || source.format !== "wrench.messaging-route") {
952
+ return fail("messaging route", "has an unsupported contract");
953
+ }
954
+ const readiness = record(source.readiness, "messaging route.readiness");
955
+ exactKeys(
956
+ readiness,
957
+ ["context", "turn", "reply", "reason"],
958
+ [],
959
+ "messaging route.readiness",
960
+ );
961
+ if (
962
+ readiness.context !== "ready"
963
+ && readiness.context !== "historical-readable"
964
+ ) return fail("messaging route.readiness.context", "is unsupported");
965
+ if (readiness.turn !== "ready" && readiness.turn !== "unavailable") {
966
+ return fail("messaging route.readiness.turn", "is unsupported");
967
+ }
968
+ if (readiness.reply !== "supported" && readiness.reply !== "unsupported") {
969
+ return fail("messaging route.readiness.reply", "is unsupported");
970
+ }
971
+ if (
972
+ readiness.context === "historical-readable"
973
+ && (readiness.turn !== "unavailable" || readiness.reply !== "unsupported")
974
+ ) return fail("messaging route.readiness", "must block actions when freshness is unproven");
975
+ return Object.freeze({
976
+ schemaVersion: 1,
977
+ format: "wrench.messaging-route",
978
+ routeRef: routeRef(source.routeRef, "messaging route.routeRef"),
979
+ network: id(source.network, "messaging route.network", 64),
980
+ conversation: conversation(source.conversation, "messaging route.conversation"),
981
+ readiness: Object.freeze({
982
+ context: readiness.context,
983
+ turn: readiness.turn,
984
+ reply: readiness.reply,
985
+ reason: nullableBoundedText(
986
+ readiness.reason,
987
+ "messaging route.readiness.reason",
988
+ 1_000,
989
+ ),
990
+ }),
991
+ completeness: completeness(source.completeness, "messaging route.completeness"),
992
+ expiresAt: utcTimestamp(source.expiresAt, "messaging route.expiresAt"),
993
+ });
994
+ }
995
+
996
+ export function parseMessagingRouteV2(value: unknown): MessagingRouteV2 {
997
+ const source = record(value, "messaging route V2");
998
+ exactKeys(source, [
999
+ "schemaVersion",
1000
+ "format",
1001
+ "routeRef",
1002
+ "network",
1003
+ "conversation",
1004
+ "readiness",
1005
+ "completeness",
1006
+ "expiresAt",
1007
+ ], [], "messaging route V2");
1008
+ if (source.schemaVersion !== 2 || source.format !== "wrench.messaging-route") {
1009
+ return fail("messaging route V2", "has an unsupported contract");
1010
+ }
1011
+ const readiness = record(source.readiness, "messaging route V2.readiness");
1012
+ exactKeys(
1013
+ readiness,
1014
+ ["context", "turn", "reply", "reason"],
1015
+ [],
1016
+ "messaging route V2.readiness",
1017
+ );
1018
+ if (
1019
+ readiness.context !== "ready"
1020
+ && readiness.context !== "historical-readable"
1021
+ && readiness.context !== "resolution-required"
1022
+ && readiness.context !== "unavailable"
1023
+ ) return fail("messaging route V2.readiness.context", "is unsupported");
1024
+ if (readiness.turn !== "ready" && readiness.turn !== "unavailable") {
1025
+ return fail("messaging route V2.readiness.turn", "is unsupported");
1026
+ }
1027
+ if (readiness.reply !== "supported" && readiness.reply !== "unsupported") {
1028
+ return fail("messaging route V2.readiness.reply", "is unsupported");
1029
+ }
1030
+ if (
1031
+ readiness.context !== "ready"
1032
+ && (readiness.turn !== "unavailable" || readiness.reply !== "unsupported")
1033
+ ) return fail("messaging route V2.readiness", "must block actions when freshness is unproven");
1034
+ if (readiness.context !== "ready" && readiness.reason === null) {
1035
+ return fail("messaging route V2.readiness.reason", "is required when context is not actionable");
1036
+ }
1037
+ return Object.freeze({
1038
+ schemaVersion: 2,
1039
+ format: "wrench.messaging-route",
1040
+ routeRef: routeRef(source.routeRef, "messaging route V2.routeRef"),
1041
+ network: id(source.network, "messaging route V2.network", 64),
1042
+ conversation: conversation(source.conversation, "messaging route V2.conversation"),
1043
+ readiness: Object.freeze({
1044
+ context: readiness.context,
1045
+ turn: readiness.turn,
1046
+ reply: readiness.reply,
1047
+ reason: nullableBoundedText(
1048
+ readiness.reason,
1049
+ "messaging route V2.readiness.reason",
1050
+ 1_000,
1051
+ ),
1052
+ }),
1053
+ completeness: completeness(source.completeness, "messaging route V2.completeness"),
1054
+ expiresAt: utcTimestamp(source.expiresAt, "messaging route V2.expiresAt"),
1055
+ });
1056
+ }
1057
+
1058
+ export function parseMessagingRoutesV1(value: unknown): MessagingRoutesV1 {
1059
+ const source = record(value, "messaging routes");
1060
+ exactKeys(source, [
1061
+ "schemaVersion",
1062
+ "format",
1063
+ "generatedAt",
1064
+ "completeness",
1065
+ "continuation",
1066
+ "routes",
1067
+ ], [], "messaging routes");
1068
+ if (source.schemaVersion !== 1 || source.format !== "wrench.messaging-routes") {
1069
+ return fail("messaging routes", "has an unsupported contract");
1070
+ }
1071
+ const continuation = record(source.continuation, "messaging routes.continuation");
1072
+ exactKeys(
1073
+ continuation,
1074
+ ["direction", "request", "nextInput"],
1075
+ [],
1076
+ "messaging routes.continuation",
1077
+ );
1078
+ if (
1079
+ continuation.direction !== "forward"
1080
+ && continuation.direction !== "backward"
1081
+ && continuation.direction !== "none"
1082
+ ) return fail("messaging routes.continuation.direction", "is unsupported");
1083
+ const request = nullableBoundedText(
1084
+ continuation.request,
1085
+ "messaging routes.continuation.request",
1086
+ 8_192,
1087
+ );
1088
+ const nextInput = continuation.nextInput === null
1089
+ ? null
1090
+ : cloneJsonRecord(
1091
+ continuation.nextInput,
1092
+ "messaging routes.continuation.nextInput",
1093
+ );
1094
+ if (
1095
+ continuation.direction === "none"
1096
+ && (request !== null || nextInput !== null)
1097
+ ) return fail("messaging routes.continuation", "must be empty when direction is none");
1098
+ const routes = denseArray(source.routes, "messaging routes.routes", 1_000)
1099
+ .map(parseMessagingRouteV1);
1100
+ return Object.freeze({
1101
+ schemaVersion: 1,
1102
+ format: "wrench.messaging-routes",
1103
+ generatedAt: utcTimestamp(source.generatedAt, "messaging routes.generatedAt"),
1104
+ completeness: completeness(source.completeness, "messaging routes.completeness"),
1105
+ continuation: Object.freeze({
1106
+ direction: continuation.direction,
1107
+ request,
1108
+ nextInput,
1109
+ }),
1110
+ routes: Object.freeze(routes),
1111
+ });
1112
+ }
1113
+
1114
+ export function parseMessagingRoutesV2(value: unknown): MessagingRoutesV2 {
1115
+ const source = record(value, "messaging routes V2");
1116
+ exactKeys(source, [
1117
+ "schemaVersion",
1118
+ "format",
1119
+ "generatedAt",
1120
+ "completeness",
1121
+ "continuation",
1122
+ "routes",
1123
+ ], [], "messaging routes V2");
1124
+ if (source.schemaVersion !== 2 || source.format !== "wrench.messaging-routes") {
1125
+ return fail("messaging routes V2", "has an unsupported contract");
1126
+ }
1127
+ const continuation = record(source.continuation, "messaging routes V2.continuation");
1128
+ exactKeys(
1129
+ continuation,
1130
+ ["direction", "request", "nextInput"],
1131
+ [],
1132
+ "messaging routes V2.continuation",
1133
+ );
1134
+ if (
1135
+ continuation.direction !== "forward"
1136
+ && continuation.direction !== "backward"
1137
+ && continuation.direction !== "none"
1138
+ ) return fail("messaging routes V2.continuation.direction", "is unsupported");
1139
+ const request = nullableBoundedText(
1140
+ continuation.request,
1141
+ "messaging routes V2.continuation.request",
1142
+ 8_192,
1143
+ );
1144
+ const nextInput = continuation.nextInput === null
1145
+ ? null
1146
+ : cloneJsonRecord(
1147
+ continuation.nextInput,
1148
+ "messaging routes V2.continuation.nextInput",
1149
+ );
1150
+ if (
1151
+ continuation.direction === "none"
1152
+ && (request !== null || nextInput !== null)
1153
+ ) return fail("messaging routes V2.continuation", "must be empty when direction is none");
1154
+ const routes = denseArray(source.routes, "messaging routes V2.routes", 1_000)
1155
+ .map(parseMessagingRouteV2);
1156
+ return Object.freeze({
1157
+ schemaVersion: 2,
1158
+ format: "wrench.messaging-routes",
1159
+ generatedAt: utcTimestamp(source.generatedAt, "messaging routes V2.generatedAt"),
1160
+ completeness: completeness(source.completeness, "messaging routes V2.completeness"),
1161
+ continuation: Object.freeze({
1162
+ direction: continuation.direction,
1163
+ request,
1164
+ nextInput,
1165
+ }),
1166
+ routes: Object.freeze(routes),
1167
+ });
1168
+ }
1169
+
1170
+ export function parseMessagingContextV1(value: unknown): MessagingContextV1 {
1171
+ const source = record(value, "messaging context");
1172
+ exactKeys(source, [
1173
+ "schemaVersion",
1174
+ "format",
1175
+ "binding",
1176
+ "network",
1177
+ "liveness",
1178
+ "truncated",
1179
+ "completeness",
1180
+ "messages",
1181
+ "warnings",
1182
+ ], [], "messaging context");
1183
+ if (source.schemaVersion !== 1 || source.format !== "wrench.messaging-context") {
1184
+ return fail("messaging context", "has an unsupported contract");
1185
+ }
1186
+ if (
1187
+ source.liveness !== "fresh-as-of-live-preflight"
1188
+ && source.liveness !== "freshness-unproven"
1189
+ ) return fail("messaging context.liveness", "is unsupported");
1190
+ const messages = denseArray(source.messages, "messaging context.messages", 200)
1191
+ .map((value_, index): MessagingContextMessageV1 => {
1192
+ const label = `messaging context.messages[${index}]`;
1193
+ const message = record(value_, label);
1194
+ exactKeys(message, [
1195
+ "messageRef",
1196
+ "direction",
1197
+ "time",
1198
+ "author",
1199
+ "body",
1200
+ "bodyTruncated",
1201
+ "edited",
1202
+ "retracted",
1203
+ "reply",
1204
+ "attachments",
1205
+ "untrustedData",
1206
+ ], [], label);
1207
+ if (
1208
+ message.direction !== "incoming"
1209
+ && message.direction !== "outgoing"
1210
+ && message.direction !== "unknown"
1211
+ ) return fail(`${label}.direction`, "is unsupported");
1212
+ if (
1213
+ message.edited !== "unknown"
1214
+ && message.edited !== "observed"
1215
+ && message.edited !== "not-observed"
1216
+ ) return fail(`${label}.edited`, "is unsupported");
1217
+ if (
1218
+ message.retracted !== "unknown"
1219
+ && message.retracted !== "observed"
1220
+ && message.retracted !== "not-observed"
1221
+ ) return fail(`${label}.retracted`, "is unsupported");
1222
+ if (message.untrustedData !== true) {
1223
+ return fail(`${label}.untrustedData`, "must be true");
1224
+ }
1225
+ const authorSource = message.author === null
1226
+ ? null
1227
+ : record(message.author, `${label}.author`);
1228
+ if (authorSource !== null) {
1229
+ exactKeys(authorSource, ["displayName", "handle"], [], `${label}.author`);
1230
+ }
1231
+ const reply = record(message.reply, `${label}.reply`);
1232
+ exactKeys(reply, ["toMessageRef"], [], `${label}.reply`);
1233
+ const attachments = denseArray(
1234
+ message.attachments,
1235
+ `${label}.attachments`,
1236
+ 64,
1237
+ ).map((value__, attachmentIndex) => {
1238
+ const attachmentLabel = `${label}.attachments[${attachmentIndex}]`;
1239
+ const attachment = record(value__, attachmentLabel);
1240
+ exactKeys(
1241
+ attachment,
1242
+ ["kind", "mimeType", "name", "sizeBytes"],
1243
+ [],
1244
+ attachmentLabel,
1245
+ );
1246
+ const kinds = new Set<MessagingContextMessageV1["attachments"][number]["kind"]>([
1247
+ "audio", "document", "image", "link", "sticker", "video", "unknown",
1248
+ ]);
1249
+ if (!kinds.has(attachment.kind as MessagingContextMessageV1["attachments"][number]["kind"])) {
1250
+ return fail(`${attachmentLabel}.kind`, "is unsupported");
1251
+ }
1252
+ return Object.freeze({
1253
+ kind: attachment.kind as MessagingContextMessageV1["attachments"][number]["kind"],
1254
+ mimeType: nullableBoundedText(
1255
+ attachment.mimeType,
1256
+ `${attachmentLabel}.mimeType`,
1257
+ 256,
1258
+ ),
1259
+ name: nullableBoundedText(
1260
+ attachment.name,
1261
+ `${attachmentLabel}.name`,
1262
+ 1_024,
1263
+ true,
1264
+ ),
1265
+ sizeBytes: attachment.sizeBytes === null
1266
+ ? null
1267
+ : integer(
1268
+ attachment.sizeBytes,
1269
+ `${attachmentLabel}.sizeBytes`,
1270
+ 0,
1271
+ Number.MAX_SAFE_INTEGER,
1272
+ ),
1273
+ });
1274
+ });
1275
+ return Object.freeze({
1276
+ messageRef: replyRef(message.messageRef, `${label}.messageRef`),
1277
+ direction: message.direction,
1278
+ time: message.time === null
1279
+ ? null
1280
+ : utcTimestamp(message.time, `${label}.time`),
1281
+ author: authorSource === null
1282
+ ? null
1283
+ : Object.freeze({
1284
+ displayName: nullableBoundedText(
1285
+ authorSource.displayName,
1286
+ `${label}.author.displayName`,
1287
+ 2_048,
1288
+ true,
1289
+ ),
1290
+ handle: nullableBoundedText(
1291
+ authorSource.handle,
1292
+ `${label}.author.handle`,
1293
+ 512,
1294
+ ),
1295
+ }),
1296
+ body: nullableBoundedText(message.body, `${label}.body`, 256 * 1024, true),
1297
+ bodyTruncated: bool(message.bodyTruncated, `${label}.bodyTruncated`),
1298
+ edited: message.edited,
1299
+ retracted: message.retracted,
1300
+ reply: Object.freeze({
1301
+ toMessageRef: reply.toMessageRef === null
1302
+ ? null
1303
+ : replyRef(reply.toMessageRef, `${label}.reply.toMessageRef`),
1304
+ }),
1305
+ attachments: Object.freeze(attachments),
1306
+ untrustedData: true,
1307
+ });
1308
+ });
1309
+ const warnings = denseArray(source.warnings, "messaging context.warnings", 32)
1310
+ .map((value_, index) => text(
1311
+ value_,
1312
+ `messaging context.warnings[${index}]`,
1313
+ 512,
1314
+ ));
1315
+ return Object.freeze({
1316
+ schemaVersion: 1,
1317
+ format: "wrench.messaging-context",
1318
+ binding: source.binding === null
1319
+ ? null
1320
+ : parseMessagingContextBinding(source.binding),
1321
+ network: id(source.network, "messaging context.network", 64),
1322
+ liveness: source.liveness,
1323
+ truncated: bool(source.truncated, "messaging context.truncated"),
1324
+ completeness: completeness(source.completeness, "messaging context.completeness"),
1325
+ messages: Object.freeze(messages),
1326
+ warnings: Object.freeze(warnings),
1327
+ });
1328
+ }
1329
+
1330
+ export function parseMessagingPreviewV1(value: unknown): MessagingPreviewV1 {
1331
+ const source = record(value, "messaging preview");
1332
+ exactKeys(source, [
1333
+ "schemaVersion",
1334
+ "format",
1335
+ "status",
1336
+ "planDigest",
1337
+ "expiresAt",
1338
+ "routeRef",
1339
+ "contextRef",
1340
+ "clientIntentSha256",
1341
+ "turnDigest",
1342
+ "recipient",
1343
+ "partCount",
1344
+ "bubbles",
1345
+ "risk",
1346
+ "sideEffect",
1347
+ ], [], "messaging preview");
1348
+ if (
1349
+ source.schemaVersion !== 1
1350
+ || source.format !== "wrench.messaging-preview"
1351
+ || source.status !== "confirmation-required"
1352
+ || source.risk !== "R3"
1353
+ ) return fail("messaging preview", "has an unsupported contract");
1354
+ const recipient = record(source.recipient, "messaging preview.recipient");
1355
+ exactKeys(recipient, ["network", "conversation"], [], "messaging preview.recipient");
1356
+ const bubbles = denseArray(source.bubbles, "messaging preview.bubbles", 8)
1357
+ .map((value_, index) => {
1358
+ const label = `messaging preview.bubbles[${index}]`;
1359
+ const bubble = record(value_, label);
1360
+ exactKeys(bubble, ["partId", "text", "replyRef"], [], label);
1361
+ return Object.freeze({
1362
+ partId: id(bubble.partId, `${label}.partId`, 64),
1363
+ text: text(bubble.text, `${label}.text`, 65_536, { allowNewlines: true }),
1364
+ replyRef: bubble.replyRef === null
1365
+ ? null
1366
+ : replyRef(bubble.replyRef, `${label}.replyRef`),
1367
+ });
1368
+ });
1369
+ const partCount = integer(source.partCount, "messaging preview.partCount", 1, 8);
1370
+ if (bubbles.length !== partCount) {
1371
+ return fail("messaging preview.bubbles", "must match partCount");
1372
+ }
1373
+ return Object.freeze({
1374
+ schemaVersion: 1,
1375
+ format: "wrench.messaging-preview",
1376
+ status: "confirmation-required",
1377
+ planDigest: sha256Digest(source.planDigest, "messaging preview.planDigest"),
1378
+ expiresAt: utcTimestamp(source.expiresAt, "messaging preview.expiresAt"),
1379
+ routeRef: routeRef(source.routeRef, "messaging preview.routeRef"),
1380
+ contextRef: contextRef(source.contextRef, "messaging preview.contextRef"),
1381
+ clientIntentSha256: sha256Digest(
1382
+ source.clientIntentSha256,
1383
+ "messaging preview.clientIntentSha256",
1384
+ ),
1385
+ turnDigest: sha256Digest(source.turnDigest, "messaging preview.turnDigest"),
1386
+ recipient: Object.freeze({
1387
+ network: id(recipient.network, "messaging preview.recipient.network", 64),
1388
+ conversation: conversation(
1389
+ recipient.conversation,
1390
+ "messaging preview.recipient.conversation",
1391
+ ),
1392
+ }),
1393
+ partCount,
1394
+ bubbles: Object.freeze(bubbles),
1395
+ risk: "R3",
1396
+ sideEffect: text(source.sideEffect, "messaging preview.sideEffect", 1_024),
1397
+ });
1398
+ }
1399
+
1400
+ export function parseMessagingPrivateOutputReceiptV1(
1401
+ value: unknown,
1402
+ ): MessagingPrivateOutputReceiptV1 {
1403
+ const source = record(value, "messaging private output receipt");
1404
+ exactKeys(source, [
1405
+ "schemaVersion",
1406
+ "format",
1407
+ "artifactFormat",
1408
+ "artifactSha256",
1409
+ "itemCount",
1410
+ "generatedAt",
1411
+ "expiresAt",
1412
+ ], [], "messaging private output receipt");
1413
+ const formats = new Set<MessagingPrivateOutputReceiptV1["artifactFormat"]>([
1414
+ "wrench.messaging-route",
1415
+ "wrench.messaging-routes",
1416
+ "wrench.messaging-context",
1417
+ "wrench.messaging-preview",
1418
+ "wrench.messaging-run",
1419
+ "wrench.messaging-receipt-binding",
1420
+ ]);
1421
+ if (
1422
+ source.schemaVersion !== 1
1423
+ || source.format !== "wrench.messaging-private-output-receipt"
1424
+ || !formats.has(source.artifactFormat as MessagingPrivateOutputReceiptV1["artifactFormat"])
1425
+ ) return fail("messaging private output receipt", "has an unsupported contract");
1426
+ return Object.freeze({
1427
+ schemaVersion: 1,
1428
+ format: "wrench.messaging-private-output-receipt",
1429
+ artifactFormat: source.artifactFormat as MessagingPrivateOutputReceiptV1["artifactFormat"],
1430
+ artifactSha256: sha256Digest(
1431
+ source.artifactSha256,
1432
+ "messaging private output receipt.artifactSha256",
1433
+ ),
1434
+ itemCount: integer(
1435
+ source.itemCount,
1436
+ "messaging private output receipt.itemCount",
1437
+ 0,
1438
+ 1_000,
1439
+ ),
1440
+ generatedAt: utcTimestamp(
1441
+ source.generatedAt,
1442
+ "messaging private output receipt.generatedAt",
1443
+ ),
1444
+ expiresAt: source.expiresAt === null
1445
+ ? null
1446
+ : utcTimestamp(
1447
+ source.expiresAt,
1448
+ "messaging private output receipt.expiresAt",
1449
+ ),
1450
+ });
1451
+ }
1452
+
1453
+ export function parseMessagingReceiptBindingV1(
1454
+ value: unknown,
1455
+ ): MessagingReceiptBindingV1 {
1456
+ const source = record(value, "messaging receipt binding V1");
1457
+ exactKeys(source, [
1458
+ "schemaVersion",
1459
+ "format",
1460
+ "contractId",
1461
+ "contractHash",
1462
+ "clientIntentSha256",
1463
+ "routeRefSha256",
1464
+ "contextRefSha256",
1465
+ "turnDigest",
1466
+ "previewDigest",
1467
+ "runId",
1468
+ "state",
1469
+ "partCount",
1470
+ "provenPartCount",
1471
+ "receiptSha256",
1472
+ "recordedAt",
1473
+ ], [], "messaging receipt binding V1");
1474
+ if (
1475
+ source.schemaVersion !== 1
1476
+ || source.format !== "wrench.messaging-receipt-binding"
1477
+ || source.contractId !== MESSAGING_RECEIPT_BINDING_V1_CONTRACT_ID
1478
+ || source.contractHash !== MESSAGING_RECEIPT_BINDING_V1_CONTRACT_HASH
1479
+ || source.state !== "submitted"
1480
+ && source.state !== "failed"
1481
+ && source.state !== "partial"
1482
+ && source.state !== "indeterminate"
1483
+ ) return fail("messaging receipt binding V1", "has an unsupported contract");
1484
+ const partCount = integer(source.partCount, "messaging receipt binding V1.partCount", 1, 8);
1485
+ const provenPartCount = integer(
1486
+ source.provenPartCount,
1487
+ "messaging receipt binding V1.provenPartCount",
1488
+ 0,
1489
+ partCount,
1490
+ );
1491
+ const validPrefix = source.state === "submitted"
1492
+ ? provenPartCount === partCount
1493
+ : source.state === "failed"
1494
+ ? provenPartCount === 0
1495
+ : source.state === "partial"
1496
+ ? provenPartCount >= 1 && provenPartCount < partCount
1497
+ : provenPartCount < partCount;
1498
+ if (!validPrefix) {
1499
+ return fail("messaging receipt binding V1", "violates the proven-prefix state law");
1500
+ }
1501
+ const normalizedWithoutReceipt = Object.freeze({
1502
+ schemaVersion: 1 as const,
1503
+ format: "wrench.messaging-receipt-binding" as const,
1504
+ contractId: MESSAGING_RECEIPT_BINDING_V1_CONTRACT_ID,
1505
+ contractHash: MESSAGING_RECEIPT_BINDING_V1_CONTRACT_HASH,
1506
+ clientIntentSha256: sha256Digest(
1507
+ source.clientIntentSha256,
1508
+ "messaging receipt binding V1.clientIntentSha256",
1509
+ ),
1510
+ routeRefSha256: sha256Digest(
1511
+ source.routeRefSha256,
1512
+ "messaging receipt binding V1.routeRefSha256",
1513
+ ),
1514
+ contextRefSha256: sha256Digest(
1515
+ source.contextRefSha256,
1516
+ "messaging receipt binding V1.contextRefSha256",
1517
+ ),
1518
+ turnDigest: sha256Digest(source.turnDigest, "messaging receipt binding V1.turnDigest"),
1519
+ previewDigest: sha256Digest(
1520
+ source.previewDigest,
1521
+ "messaging receipt binding V1.previewDigest",
1522
+ ),
1523
+ runId: id(source.runId, "messaging receipt binding V1.runId", 256),
1524
+ state: source.state,
1525
+ partCount,
1526
+ provenPartCount,
1527
+ recordedAt: utcTimestamp(
1528
+ source.recordedAt,
1529
+ "messaging receipt binding V1.recordedAt",
1530
+ ),
1531
+ });
1532
+ const receiptSha256 = sha256Digest(
1533
+ source.receiptSha256,
1534
+ "messaging receipt binding V1.receiptSha256",
1535
+ );
1536
+ if (sha256(canonicalJson(normalizedWithoutReceipt)) !== receiptSha256) {
1537
+ return fail(
1538
+ "messaging receipt binding V1.receiptSha256",
1539
+ "does not bind the canonical receipt",
1540
+ );
1541
+ }
1542
+ return Object.freeze({ ...normalizedWithoutReceipt, receiptSha256 });
1543
+ }
1544
+
1545
+ export function parseMessagingReceiptBindingV2(
1546
+ value: unknown,
1547
+ ): MessagingReceiptBindingV2 {
1548
+ const source = record(value, "messaging receipt binding");
1549
+ exactKeys(source, [
1550
+ "schemaVersion",
1551
+ "format",
1552
+ "contractId",
1553
+ "contractHash",
1554
+ "clientIntentSha256",
1555
+ "contextBindingSha256",
1556
+ "sourceConversationCoordinateSha256",
1557
+ "routeRefSha256",
1558
+ "contextRefSha256",
1559
+ "turnDigest",
1560
+ "previewDigest",
1561
+ "runId",
1562
+ "state",
1563
+ "partCount",
1564
+ "provenPartCount",
1565
+ "receiptSha256",
1566
+ "recordedAt",
1567
+ ], [], "messaging receipt binding");
1568
+ if (
1569
+ source.schemaVersion !== 2
1570
+ || source.format !== "wrench.messaging-receipt-binding"
1571
+ || source.contractId !== MESSAGING_RECEIPT_BINDING_CONTRACT_ID
1572
+ || source.contractHash !== MESSAGING_RECEIPT_BINDING_CONTRACT_HASH
1573
+ || source.state !== "submitted"
1574
+ && source.state !== "failed"
1575
+ && source.state !== "partial"
1576
+ && source.state !== "indeterminate"
1577
+ ) return fail("messaging receipt binding", "has an unsupported contract");
1578
+ const partCount = integer(source.partCount, "messaging receipt binding.partCount", 1, 8);
1579
+ const provenPartCount = integer(
1580
+ source.provenPartCount,
1581
+ "messaging receipt binding.provenPartCount",
1582
+ 0,
1583
+ partCount,
1584
+ );
1585
+ const validPrefix = source.state === "submitted"
1586
+ ? provenPartCount === partCount
1587
+ : source.state === "failed"
1588
+ ? provenPartCount === 0
1589
+ : source.state === "partial"
1590
+ ? provenPartCount >= 1 && provenPartCount < partCount
1591
+ : provenPartCount < partCount;
1592
+ if (!validPrefix) {
1593
+ return fail("messaging receipt binding", "violates the proven-prefix state law");
1594
+ }
1595
+ const runId = id(source.runId, "messaging receipt binding.runId", 256);
1596
+ const normalizedWithoutReceipt = Object.freeze({
1597
+ schemaVersion: 2,
1598
+ format: "wrench.messaging-receipt-binding",
1599
+ contractId: MESSAGING_RECEIPT_BINDING_CONTRACT_ID,
1600
+ contractHash: MESSAGING_RECEIPT_BINDING_CONTRACT_HASH,
1601
+ clientIntentSha256: sha256Digest(
1602
+ source.clientIntentSha256,
1603
+ "messaging receipt binding.clientIntentSha256",
1604
+ ),
1605
+ contextBindingSha256: sha256Digest(
1606
+ source.contextBindingSha256,
1607
+ "messaging receipt binding.contextBindingSha256",
1608
+ ),
1609
+ sourceConversationCoordinateSha256: sha256Digest(
1610
+ source.sourceConversationCoordinateSha256,
1611
+ "messaging receipt binding.sourceConversationCoordinateSha256",
1612
+ ),
1613
+ routeRefSha256: sha256Digest(source.routeRefSha256, "messaging receipt binding.routeRefSha256"),
1614
+ contextRefSha256: sha256Digest(source.contextRefSha256, "messaging receipt binding.contextRefSha256"),
1615
+ turnDigest: sha256Digest(source.turnDigest, "messaging receipt binding.turnDigest"),
1616
+ previewDigest: sha256Digest(source.previewDigest, "messaging receipt binding.previewDigest"),
1617
+ runId,
1618
+ state: source.state,
1619
+ partCount,
1620
+ provenPartCount,
1621
+ recordedAt: utcTimestamp(source.recordedAt, "messaging receipt binding.recordedAt"),
1622
+ });
1623
+ const receiptSha256 = sha256Digest(
1624
+ source.receiptSha256,
1625
+ "messaging receipt binding.receiptSha256",
1626
+ );
1627
+ if (sha256(canonicalJson(normalizedWithoutReceipt)) !== receiptSha256) {
1628
+ return fail("messaging receipt binding.receiptSha256", "does not bind the canonical receipt");
1629
+ }
1630
+ return Object.freeze({
1631
+ ...normalizedWithoutReceipt,
1632
+ receiptSha256,
1633
+ });
1634
+ }
1635
+
1636
+ export function parseMessagingReceiptBinding(
1637
+ value: unknown,
1638
+ ): MessagingReceiptBinding {
1639
+ const source = record(value, "messaging receipt binding");
1640
+ return source.contractId === MESSAGING_RECEIPT_BINDING_V1_CONTRACT_ID
1641
+ ? parseMessagingReceiptBindingV1(source)
1642
+ : parseMessagingReceiptBindingV2(source);
1643
+ }
1644
+
1645
+ export declare function discoverMessagingRoutes(
1646
+ request: MessagingRoutesRequestV1,
1647
+ options?: MessagingClientOptions,
1648
+ ): Promise<MessagingRoutesV2>;
1649
+
1650
+ export declare function resolveMessagingRoute(
1651
+ request: MessagingRouteResolveRequestV2,
1652
+ options?: MessagingClientOptions,
1653
+ ): Promise<MessagingRouteV2>;
1654
+
1655
+ export declare function readMessagingContext(
1656
+ request: MessagingContextRequestV1,
1657
+ options?: MessagingClientOptions,
1658
+ ): Promise<MessagingContextV1>;
1659
+
1660
+ export declare function previewMessagingTurn(
1661
+ request: MessagingTurnV1,
1662
+ options?: MessagingClientOptions,
1663
+ ): Promise<MessagingPreviewV1>;