@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,820 @@
1
+ import blueskyWebManifest from "./assets/adapters/bluesky/wrench-web-adapter.json";
2
+ import beeperWebManifest from "./assets/adapters/beeper/wrench-web-adapter.json";
3
+ import facebookGroupWebManifest from "./assets/adapters/facebook-group/wrench-web-adapter.json";
4
+ import facebookMarketplaceWebManifest from "./assets/adapters/facebook-marketplace/wrench-web-adapter.json";
5
+ import facebookPageWebManifest from "./assets/adapters/facebook-page/wrench-web-adapter.json";
6
+ import facebookWebManifest from "./assets/adapters/facebook/wrench-web-adapter.json";
7
+ import githubWebManifest from "./assets/adapters/github/wrench-web-adapter.json";
8
+ import hackerNewsWebManifest from "./assets/adapters/hacker-news/wrench-web-adapter.json";
9
+ import instagramWebManifest from "./assets/adapters/instagram/wrench-web-adapter.json";
10
+ import linkedinWebManifest from "./assets/adapters/linkedin/wrench-web-adapter.json";
11
+ import redditWebManifest from "./assets/adapters/reddit/wrench-web-adapter.json";
12
+ import substackWebManifest from "./assets/adapters/substack/wrench-web-adapter.json";
13
+ import tiktokWebManifest from "./assets/adapters/tiktok/wrench-web-adapter.json";
14
+ import threadsWebManifest from "./assets/adapters/threads/wrench-web-adapter.json";
15
+ import twitchWebManifest from "./assets/adapters/twitch/wrench-web-adapter.json";
16
+ import xWebManifest from "./assets/adapters/x/wrench-web-adapter.json";
17
+ import youtubeWebManifest from "./assets/adapters/youtube/wrench-web-adapter.json";
18
+ import whatsappWebManifest from "./assets/adapters/whatsapp/wrench-web-adapter.json";
19
+ import {
20
+ type IdempotencyKind,
21
+ type InputField,
22
+ type InputSchema,
23
+ type OperationRisk,
24
+ type WebSessionSiteId,
25
+ } from "./model";
26
+ import type { SemanticOperationName } from "./platform-catalog";
27
+ import type { ProviderPluginOperationName } from "./provider-plugin-identifiers";
28
+ import { redditFlairContracts } from "./plugins/reddit-web/flair";
29
+
30
+ export type WebSessionContractState = "observed" | "capture-required";
31
+
32
+ export type WebSessionContract = {
33
+ readonly site: WebSessionSiteId;
34
+ readonly operation: ProviderPluginOperationName;
35
+ readonly contractVersion: number;
36
+ readonly risk: OperationRisk;
37
+ /** Exact code-owned schema; an installed manifest cannot widen or rename inputs. */
38
+ readonly input: InputSchema;
39
+ /** Exact confirmation copy and replay policy bound into invocation plans. */
40
+ readonly sideEffect: string;
41
+ readonly idempotency: IdempotencyKind;
42
+ readonly dedupeWindowMs: number;
43
+ readonly state: WebSessionContractState;
44
+ readonly dispatch: "none" | "single" | "thread-items" | "bounded-items";
45
+ readonly implementation: string;
46
+ };
47
+
48
+ const bundledManifests: Readonly<Partial<Record<WebSessionSiteId, unknown>>> = {
49
+ beeper: beeperWebManifest,
50
+ bluesky: blueskyWebManifest,
51
+ facebook: facebookWebManifest,
52
+ "facebook-group": facebookGroupWebManifest,
53
+ "facebook-marketplace": facebookMarketplaceWebManifest,
54
+ "facebook-page": facebookPageWebManifest,
55
+ github: githubWebManifest,
56
+ "hacker-news": hackerNewsWebManifest,
57
+ instagram: instagramWebManifest,
58
+ linkedin: linkedinWebManifest,
59
+ reddit: redditWebManifest,
60
+ substack: substackWebManifest,
61
+ tiktok: tiktokWebManifest,
62
+ threads: threadsWebManifest,
63
+ twitch: twitchWebManifest,
64
+ x: xWebManifest,
65
+ youtube: youtubeWebManifest,
66
+ whatsapp: whatsappWebManifest,
67
+ };
68
+
69
+ function isRecord(value: unknown): value is Record<string, unknown> {
70
+ return typeof value === "object" && value !== null && !Array.isArray(value);
71
+ }
72
+
73
+ function isUnknownArray(value: unknown): value is readonly unknown[] {
74
+ return Array.isArray(value);
75
+ }
76
+
77
+ function requireExactKeys(value: Readonly<Record<string, unknown>>, allowed: readonly string[], path: string): void {
78
+ const allowedKeys = new Set(allowed);
79
+ const unexpected = Object.keys(value).filter((key) => !allowedKeys.has(key));
80
+ if (unexpected.length > 0) throw new Error(`${path} contains unsupported keys: ${unexpected.join(", ")}`);
81
+ }
82
+
83
+ function requiredString(value: unknown, path: string): string {
84
+ if (typeof value !== "string" || value.length < 1) throw new Error(`${path} must be a non-empty string`);
85
+ return value;
86
+ }
87
+
88
+ function optionalSafeNumber(
89
+ value: Readonly<Record<string, unknown>>,
90
+ key: string,
91
+ path: string,
92
+ ): number | undefined {
93
+ const candidate = value[key];
94
+ if (candidate === undefined) return undefined;
95
+ if (typeof candidate !== "number" || !Number.isSafeInteger(candidate)) {
96
+ throw new Error(`${path}.${key} must be a safe integer`);
97
+ }
98
+ return candidate;
99
+ }
100
+
101
+ function optionalStringList(
102
+ value: Readonly<Record<string, unknown>>,
103
+ key: string,
104
+ path: string,
105
+ ): readonly string[] | undefined {
106
+ const candidate = value[key];
107
+ if (candidate === undefined) return undefined;
108
+ if (!isUnknownArray(candidate)) {
109
+ throw new Error(`${path}.${key} must be a string array`);
110
+ }
111
+ const result: string[] = [];
112
+ for (const item of candidate) {
113
+ if (typeof item !== "string") throw new Error(`${path}.${key} must be a string array`);
114
+ result.push(item);
115
+ }
116
+ return Object.freeze(result);
117
+ }
118
+
119
+ function parseOwnedInputField(value: unknown, path: string): InputField {
120
+ if (!isRecord(value)) throw new Error(`${path} must be an object`);
121
+ const type = value.type;
122
+ const description = requiredString(value.description, `${path}.description`);
123
+ if (type === "file") {
124
+ requireExactKeys(value, ["type", "description", "maxBytes", "mediaTypes"], path);
125
+ const maxBytes = optionalSafeNumber(value, "maxBytes", path);
126
+ if (maxBytes === undefined || maxBytes < 1) throw new Error(`${path}.maxBytes must be a positive safe integer`);
127
+ const mediaTypes = optionalStringList(value, "mediaTypes", path);
128
+ return Object.freeze({
129
+ type: "file",
130
+ description,
131
+ maxBytes,
132
+ ...(mediaTypes === undefined ? {} : { mediaTypes }),
133
+ });
134
+ }
135
+ if (type === "array") {
136
+ requireExactKeys(value, ["type", "description", "items", "minItems", "maxItems"], path);
137
+ const minItems = optionalSafeNumber(value, "minItems", path);
138
+ const maxItems = optionalSafeNumber(value, "maxItems", path);
139
+ if (minItems === undefined || maxItems === undefined || minItems < 0 || maxItems < minItems) {
140
+ throw new Error(`${path} must declare a valid minItems/maxItems range`);
141
+ }
142
+ const items = parseOwnedInputField(value.items, `${path}.items`);
143
+ if (items.type === "array") throw new Error(`${path}.items cannot be a nested array`);
144
+ return Object.freeze({ type: "array", description, items, minItems, maxItems });
145
+ }
146
+ if (type !== "string" && type !== "boolean" && type !== "number") {
147
+ throw new Error(`${path}.type must be string, boolean, number, file, or array`);
148
+ }
149
+ requireExactKeys(
150
+ value,
151
+ ["type", "description", "minLength", "maxLength", "minimum", "maximum", "enum", "format", "urlPathPrefixes"],
152
+ path,
153
+ );
154
+ const minLength = optionalSafeNumber(value, "minLength", path);
155
+ const maxLength = optionalSafeNumber(value, "maxLength", path);
156
+ const minimum = optionalSafeNumber(value, "minimum", path);
157
+ const maximum = optionalSafeNumber(value, "maximum", path);
158
+ const rawEnum = value.enum;
159
+ let enumValues: readonly (string | number | boolean)[] | undefined;
160
+ if (rawEnum !== undefined) {
161
+ if (!isUnknownArray(rawEnum) || rawEnum.length < 1) {
162
+ throw new Error(`${path}.enum must be a non-empty scalar array`);
163
+ }
164
+ const values: (string | number | boolean)[] = [];
165
+ for (const item of rawEnum) {
166
+ if (typeof item !== "string" && typeof item !== "number" && typeof item !== "boolean") {
167
+ throw new Error(`${path}.enum must be a non-empty scalar array`);
168
+ }
169
+ values.push(item);
170
+ }
171
+ enumValues = Object.freeze(values);
172
+ }
173
+ const format = value.format;
174
+ if (format !== undefined && format !== "url" && format !== "path-segment") {
175
+ throw new Error(`${path}.format must be url or path-segment`);
176
+ }
177
+ const urlPathPrefixes = optionalStringList(value, "urlPathPrefixes", path);
178
+ return Object.freeze({
179
+ type,
180
+ description,
181
+ ...(minLength === undefined ? {} : { minLength }),
182
+ ...(maxLength === undefined ? {} : { maxLength }),
183
+ ...(minimum === undefined ? {} : { minimum }),
184
+ ...(maximum === undefined ? {} : { maximum }),
185
+ ...(enumValues === undefined ? {} : { enum: enumValues }),
186
+ ...(format === undefined ? {} : { format }),
187
+ ...(urlPathPrefixes === undefined ? {} : { urlPathPrefixes }),
188
+ });
189
+ }
190
+
191
+ function parseOwnedInputSchema(value: unknown, path: string): InputSchema {
192
+ if (!isRecord(value)) throw new Error(`${path} must be an object`);
193
+ requireExactKeys(value, ["properties", "required"], path);
194
+ if (!isRecord(value.properties)) throw new Error(`${path}.properties must be an object`);
195
+ const requiredValue = value.required;
196
+ if (!isUnknownArray(requiredValue)) {
197
+ throw new Error(`${path}.required must be a string array`);
198
+ }
199
+ const required: string[] = [];
200
+ for (const item of requiredValue) {
201
+ if (typeof item !== "string") throw new Error(`${path}.required must be a string array`);
202
+ required.push(item);
203
+ }
204
+ const properties: Record<string, InputField> = {};
205
+ for (const [name, field] of Object.entries(value.properties)) {
206
+ properties[name] = parseOwnedInputField(field, `${path}.properties.${name}`);
207
+ }
208
+ for (const name of required) {
209
+ if (!(name in properties)) throw new Error(`${path}.required references undeclared property ${name}`);
210
+ }
211
+ return Object.freeze({
212
+ properties: Object.freeze(properties),
213
+ required: Object.freeze(required),
214
+ });
215
+ }
216
+
217
+ function manifestOperationSemantics(
218
+ manifest: unknown,
219
+ site: WebSessionSiteId,
220
+ operation: SemanticOperationName,
221
+ expectedRisk: OperationRisk,
222
+ ): Pick<WebSessionContract, "input" | "sideEffect" | "idempotency" | "dedupeWindowMs"> {
223
+ if (!isRecord(manifest) || !isRecord(manifest.operations)) {
224
+ throw new Error(`bundled authenticated web manifest ${site} is malformed`);
225
+ }
226
+ const definition = manifest.operations[operation];
227
+ if (!isRecord(definition)) throw new Error(`bundled authenticated web operation ${site}/${operation} is missing`);
228
+ if (definition.risk !== expectedRisk) {
229
+ throw new Error(`bundled authenticated web operation ${site}/${operation} has unexpected risk`);
230
+ }
231
+ const sideEffect = requiredString(definition.sideEffect, `${site}/${operation}.sideEffect`);
232
+ const idempotency = definition.idempotency;
233
+ if (idempotency !== "none" && idempotency !== "local-at-most-once") {
234
+ throw new Error(`bundled authenticated web operation ${site}/${operation}.idempotency is invalid`);
235
+ }
236
+ const dedupeWindowMs = definition.dedupeWindowMs;
237
+ if (typeof dedupeWindowMs !== "number" || !Number.isSafeInteger(dedupeWindowMs) || dedupeWindowMs < 0) {
238
+ throw new Error(`bundled authenticated web operation ${site}/${operation}.dedupeWindowMs is invalid`);
239
+ }
240
+ return Object.freeze({
241
+ input: parseOwnedInputSchema(definition.input, `${site}/${operation}.input`),
242
+ sideEffect,
243
+ idempotency,
244
+ dedupeWindowMs,
245
+ });
246
+ }
247
+
248
+ function bundledOperationSemantics(
249
+ site: WebSessionSiteId,
250
+ operation: SemanticOperationName,
251
+ expectedRisk: OperationRisk,
252
+ ): Pick<WebSessionContract, "input" | "sideEffect" | "idempotency" | "dedupeWindowMs"> {
253
+ return manifestOperationSemantics(
254
+ bundledManifests[site],
255
+ site,
256
+ operation,
257
+ expectedRisk,
258
+ );
259
+ }
260
+
261
+ /**
262
+ * Rehydrate one exact immutable bundled adapter contract for durable recovery.
263
+ * The returned route keeps its historical schema and confirmation text; it is
264
+ * never an alias to a semantically different active contract.
265
+ */
266
+ export function reviewedArchivedWebSessionContract(
267
+ manifestValue: unknown,
268
+ expected: {
269
+ readonly adapterId: string;
270
+ readonly adapterVersion: string;
271
+ readonly site: WebSessionSiteId;
272
+ readonly operation: SemanticOperationName;
273
+ readonly contractVersion: number;
274
+ readonly risk: OperationRisk;
275
+ readonly state: WebSessionContractState;
276
+ readonly implementation: string;
277
+ },
278
+ ): WebSessionContract {
279
+ if (!isRecord(manifestValue)) {
280
+ throw new Error(`archived authenticated web manifest ${expected.adapterId} is malformed`);
281
+ }
282
+ if (
283
+ manifestValue.schemaVersion !== 4
284
+ || manifestValue.id !== expected.adapterId
285
+ || manifestValue.version !== expected.adapterVersion
286
+ || !isRecord(manifestValue.operations)
287
+ ) throw new Error(`archived authenticated web manifest ${expected.adapterId}@${expected.adapterVersion} changed identity`);
288
+ const definition = manifestValue.operations[expected.operation];
289
+ if (!isRecord(definition) || !isRecord(definition.webSession)) {
290
+ throw new Error(`archived authenticated web operation ${expected.site}/${expected.operation} is missing`);
291
+ }
292
+ if (
293
+ definition.webSession.site !== expected.site
294
+ || definition.webSession.action !== expected.operation
295
+ || definition.webSession.contractVersion !== expected.contractVersion
296
+ ) throw new Error(`archived authenticated web operation ${expected.site}/${expected.operation} changed route identity`);
297
+ return Object.freeze({
298
+ site: expected.site,
299
+ operation: expected.operation,
300
+ contractVersion: expected.contractVersion,
301
+ risk: expected.risk,
302
+ ...manifestOperationSemantics(
303
+ manifestValue,
304
+ expected.site,
305
+ expected.operation,
306
+ expected.risk,
307
+ ),
308
+ state: expected.state,
309
+ dispatch: expected.risk === "R1"
310
+ ? "none"
311
+ : expected.operation === "articles.draft.save"
312
+ ? "bounded-items"
313
+ : expected.operation === "threads.publish"
314
+ ? "thread-items"
315
+ : "single",
316
+ implementation: expected.implementation,
317
+ });
318
+ }
319
+
320
+ type WebOperationPolicy = {
321
+ readonly risk: OperationRisk;
322
+ readonly state: WebSessionContractState;
323
+ readonly reason: string;
324
+ readonly contractVersion: number;
325
+ };
326
+
327
+ function operationPolicies(
328
+ site: WebSessionSiteId,
329
+ observedOperations: readonly SemanticOperationName[],
330
+ versionOverrides: Readonly<Partial<Record<SemanticOperationName, number>>> = {},
331
+ ): Readonly<Record<SemanticOperationName, WebOperationPolicy>> {
332
+ const manifest = bundledManifests[site];
333
+ if (!isRecord(manifest) || !isRecord(manifest.operations)) {
334
+ throw new Error(`bundled authenticated web manifest ${site} is malformed`);
335
+ }
336
+ const observed = new Set<SemanticOperationName>(observedOperations);
337
+ const policies: Partial<Record<SemanticOperationName, WebOperationPolicy>> = {};
338
+ for (const [operationValue, definition] of Object.entries(manifest.operations)) {
339
+ if (!isRecord(definition)) {
340
+ throw new Error(`bundled authenticated web operation ${site}/${operationValue} is malformed`);
341
+ }
342
+ const risk = definition.risk;
343
+ if (risk !== "R1" && risk !== "R2" && risk !== "R3" && risk !== "R4") {
344
+ throw new Error(`bundled authenticated web operation ${site}/${operationValue} has invalid risk`);
345
+ }
346
+ const operation = operationValue as SemanticOperationName;
347
+ const state = observed.has(operation) ? "observed" : "capture-required";
348
+ policies[operation] = Object.freeze({
349
+ risk,
350
+ state,
351
+ reason: state === "observed"
352
+ ? `reviewed ${site} ${operation} authenticated first-party contract`
353
+ : `${site} ${operation} requires a fresh reviewed authenticated first-party contract before execution`,
354
+ contractVersion: versionOverrides[operation] ?? 1,
355
+ });
356
+ }
357
+ return Object.freeze(policies) as Readonly<
358
+ Record<SemanticOperationName, WebOperationPolicy>
359
+ >;
360
+ }
361
+
362
+ const BLUESKY_WEB_OPERATIONS = operationPolicies("bluesky", [
363
+ "comments.read",
364
+ "content.delete",
365
+ "feeds.read",
366
+ "media.publish",
367
+ "media.read",
368
+ "posts.publish",
369
+ "posts.read",
370
+ "profiles.read",
371
+ ], {
372
+ "media.publish": 2,
373
+ "profiles.read": 2,
374
+ "posts.publish": 3,
375
+ });
376
+ const LINKEDIN_WEB_OPERATIONS = operationPolicies("linkedin", [
377
+ "articles.draft.save",
378
+ "contacts.read",
379
+ "feeds.read",
380
+ "organizations.read",
381
+ "posts.publish",
382
+ "profiles.read",
383
+ ], {
384
+ "articles.draft.save": 7,
385
+ "feeds.read": 2,
386
+ "posts.publish": 3,
387
+ });
388
+ const GITHUB_WEB_OPERATIONS = operationPolicies("github", [
389
+ "organizations.read",
390
+ "profiles.read",
391
+ ]);
392
+ const HACKER_NEWS_WEB_OPERATIONS = operationPolicies("hacker-news", [
393
+ "comments.read",
394
+ "feeds.read",
395
+ "posts.read",
396
+ ]);
397
+ const REDDIT_WEB_OPERATIONS = operationPolicies("reddit", [
398
+ "comments.read",
399
+ "content.delete",
400
+ "feeds.read",
401
+ "media.read",
402
+ "media.publish",
403
+ "messaging.list",
404
+ "messaging.read",
405
+ "posts.read",
406
+ "profiles.read",
407
+ ], {
408
+ "media.read": 2,
409
+ "media.publish": 8,
410
+ });
411
+ const BEEPER_LOCAL_OPERATIONS = operationPolicies("beeper", [
412
+ "contacts.list",
413
+ "contacts.search",
414
+ "messaging.list",
415
+ "messaging.search",
416
+ "messaging.read",
417
+ ], {
418
+ "contacts.list": 3,
419
+ "messaging.search": 2,
420
+ "messaging.read": 3,
421
+ });
422
+ const SUBSTACK_WEB_OPERATIONS = operationPolicies("substack", [
423
+ "articles.read",
424
+ "comments.read",
425
+ "content.delete",
426
+ "feeds.read",
427
+ "media.read",
428
+ "messaging.list",
429
+ "organizations.read",
430
+ "posts.publish",
431
+ "posts.read",
432
+ "profiles.read",
433
+ ], {
434
+ "posts.publish": 3,
435
+ });
436
+ const TIKTOK_WEB_OPERATIONS = operationPolicies("tiktok", [
437
+ "comments.read",
438
+ "feeds.read",
439
+ "profiles.read",
440
+ ], {
441
+ "media.publish": 2,
442
+ });
443
+ const TWITCH_WEB_OPERATIONS = operationPolicies("twitch", [
444
+ "profiles.read",
445
+ ]);
446
+ const WHATSAPP_WEB_OPERATIONS = operationPolicies("whatsapp", [
447
+ "contacts.list",
448
+ "media.read",
449
+ "messaging.list",
450
+ "messaging.read",
451
+ ]);
452
+
453
+ type MetaWebSite =
454
+ | "instagram"
455
+ | "threads"
456
+ | "facebook"
457
+ | "facebook-page"
458
+ | "facebook-group"
459
+ | "facebook-marketplace";
460
+
461
+ const META_WEB_OPERATIONS = Object.freeze({
462
+ instagram: operationPolicies("instagram", [
463
+ "comments.read",
464
+ "content.delete",
465
+ "contacts.list",
466
+ "feeds.read",
467
+ "media.read",
468
+ "messaging.list",
469
+ "posts.read",
470
+ "profiles.read",
471
+ ], {
472
+ "comments.read": 2,
473
+ "content.delete": 2,
474
+ "feeds.read": 2,
475
+ "media.publish": 3,
476
+ "messaging.list": 2,
477
+ }),
478
+ threads: operationPolicies("threads", ["feeds.read", "media.publish", "posts.publish", "profiles.read"], {
479
+ "feeds.read": 2,
480
+ "media.publish": 1,
481
+ "posts.publish": 5,
482
+ }),
483
+ facebook: operationPolicies("facebook", ["feeds.read"], {
484
+ "feeds.read": 2,
485
+ "messaging.list": 2,
486
+ }),
487
+ "facebook-page": operationPolicies("facebook-page", []),
488
+ "facebook-group": operationPolicies("facebook-group", ["feeds.read"], {
489
+ "feeds.read": 2,
490
+ }),
491
+ "facebook-marketplace": operationPolicies(
492
+ "facebook-marketplace",
493
+ ["feeds.read", "listings.read"],
494
+ {
495
+ "feeds.read": 2,
496
+ "listings.read": 2,
497
+ },
498
+ ),
499
+ });
500
+
501
+ const contract = (
502
+ site: WebSessionSiteId,
503
+ operation: SemanticOperationName,
504
+ risk: OperationRisk,
505
+ state: WebSessionContractState,
506
+ implementation: string,
507
+ contractVersion = 1,
508
+ ): WebSessionContract => Object.freeze({
509
+ site,
510
+ operation,
511
+ contractVersion,
512
+ risk,
513
+ ...bundledOperationSemantics(site, operation, risk),
514
+ state,
515
+ dispatch: risk === "R1"
516
+ ? "none"
517
+ : operation === "articles.draft.save"
518
+ ? "bounded-items"
519
+ : operation === "threads.publish" ? "thread-items" : "single",
520
+ implementation,
521
+ });
522
+
523
+ function metaRegistry(
524
+ site: MetaWebSite,
525
+ ): Readonly<Partial<Record<SemanticOperationName, WebSessionContract>>> {
526
+ const registry: Partial<Record<SemanticOperationName, WebSessionContract>> = {};
527
+ for (const [operationValue, selected] of Object.entries(META_WEB_OPERATIONS[site])) {
528
+ const operation = operationValue as SemanticOperationName;
529
+ if (selected === undefined) {
530
+ throw new Error(`Meta authenticated web contract ${site}/${operation} is missing`);
531
+ }
532
+ registry[operation] = contract(
533
+ site,
534
+ operation,
535
+ selected.risk,
536
+ selected.state,
537
+ selected.reason,
538
+ selected.contractVersion,
539
+ );
540
+ }
541
+ return Object.freeze(registry);
542
+ }
543
+
544
+ const instagram = metaRegistry("instagram");
545
+ const threads = metaRegistry("threads");
546
+ const facebook = metaRegistry("facebook");
547
+ const facebookPage = metaRegistry("facebook-page");
548
+ const facebookGroup = metaRegistry("facebook-group");
549
+ const facebookMarketplace = metaRegistry("facebook-marketplace");
550
+
551
+ const bluesky = {
552
+ "content.delete": contract("bluesky", "content.delete", BLUESKY_WEB_OPERATIONS["content.delete"].risk, BLUESKY_WEB_OPERATIONS["content.delete"].state, BLUESKY_WEB_OPERATIONS["content.delete"].reason),
553
+ "comments.read": contract("bluesky", "comments.read", BLUESKY_WEB_OPERATIONS["comments.read"].risk, BLUESKY_WEB_OPERATIONS["comments.read"].state, BLUESKY_WEB_OPERATIONS["comments.read"].reason),
554
+ "content.save": contract("bluesky", "content.save", BLUESKY_WEB_OPERATIONS["content.save"].risk, BLUESKY_WEB_OPERATIONS["content.save"].state, BLUESKY_WEB_OPERATIONS["content.save"].reason),
555
+ "content.share": contract("bluesky", "content.share", BLUESKY_WEB_OPERATIONS["content.share"].risk, BLUESKY_WEB_OPERATIONS["content.share"].state, BLUESKY_WEB_OPERATIONS["content.share"].reason),
556
+ "feeds.read": contract("bluesky", "feeds.read", BLUESKY_WEB_OPERATIONS["feeds.read"].risk, BLUESKY_WEB_OPERATIONS["feeds.read"].state, BLUESKY_WEB_OPERATIONS["feeds.read"].reason),
557
+ "likes.set": contract("bluesky", "likes.set", BLUESKY_WEB_OPERATIONS["likes.set"].risk, BLUESKY_WEB_OPERATIONS["likes.set"].state, BLUESKY_WEB_OPERATIONS["likes.set"].reason),
558
+ "media.publish": contract("bluesky", "media.publish", BLUESKY_WEB_OPERATIONS["media.publish"].risk, BLUESKY_WEB_OPERATIONS["media.publish"].state, BLUESKY_WEB_OPERATIONS["media.publish"].reason, BLUESKY_WEB_OPERATIONS["media.publish"].contractVersion),
559
+ "media.read": contract("bluesky", "media.read", BLUESKY_WEB_OPERATIONS["media.read"].risk, BLUESKY_WEB_OPERATIONS["media.read"].state, BLUESKY_WEB_OPERATIONS["media.read"].reason),
560
+ "messaging.list": contract("bluesky", "messaging.list", BLUESKY_WEB_OPERATIONS["messaging.list"].risk, BLUESKY_WEB_OPERATIONS["messaging.list"].state, BLUESKY_WEB_OPERATIONS["messaging.list"].reason),
561
+ "messaging.read": contract("bluesky", "messaging.read", BLUESKY_WEB_OPERATIONS["messaging.read"].risk, BLUESKY_WEB_OPERATIONS["messaging.read"].state, BLUESKY_WEB_OPERATIONS["messaging.read"].reason),
562
+ "messaging.send": contract("bluesky", "messaging.send", BLUESKY_WEB_OPERATIONS["messaging.send"].risk, BLUESKY_WEB_OPERATIONS["messaging.send"].state, BLUESKY_WEB_OPERATIONS["messaging.send"].reason),
563
+ "posts.publish": contract("bluesky", "posts.publish", BLUESKY_WEB_OPERATIONS["posts.publish"].risk, BLUESKY_WEB_OPERATIONS["posts.publish"].state, BLUESKY_WEB_OPERATIONS["posts.publish"].reason, BLUESKY_WEB_OPERATIONS["posts.publish"].contractVersion),
564
+ "posts.quote": contract("bluesky", "posts.quote", BLUESKY_WEB_OPERATIONS["posts.quote"].risk, BLUESKY_WEB_OPERATIONS["posts.quote"].state, BLUESKY_WEB_OPERATIONS["posts.quote"].reason),
565
+ "posts.read": contract("bluesky", "posts.read", BLUESKY_WEB_OPERATIONS["posts.read"].risk, BLUESKY_WEB_OPERATIONS["posts.read"].state, BLUESKY_WEB_OPERATIONS["posts.read"].reason),
566
+ "posts.repost": contract("bluesky", "posts.repost", BLUESKY_WEB_OPERATIONS["posts.repost"].risk, BLUESKY_WEB_OPERATIONS["posts.repost"].state, BLUESKY_WEB_OPERATIONS["posts.repost"].reason),
567
+ "profiles.read": contract("bluesky", "profiles.read", BLUESKY_WEB_OPERATIONS["profiles.read"].risk, BLUESKY_WEB_OPERATIONS["profiles.read"].state, BLUESKY_WEB_OPERATIONS["profiles.read"].reason, BLUESKY_WEB_OPERATIONS["profiles.read"].contractVersion),
568
+ "relationships.follow.set": contract("bluesky", "relationships.follow.set", BLUESKY_WEB_OPERATIONS["relationships.follow.set"].risk, BLUESKY_WEB_OPERATIONS["relationships.follow.set"].state, BLUESKY_WEB_OPERATIONS["relationships.follow.set"].reason),
569
+ "replies.create": contract("bluesky", "replies.create", BLUESKY_WEB_OPERATIONS["replies.create"].risk, BLUESKY_WEB_OPERATIONS["replies.create"].state, BLUESKY_WEB_OPERATIONS["replies.create"].reason),
570
+ "threads.publish": contract("bluesky", "threads.publish", BLUESKY_WEB_OPERATIONS["threads.publish"].risk, BLUESKY_WEB_OPERATIONS["threads.publish"].state, BLUESKY_WEB_OPERATIONS["threads.publish"].reason),
571
+ } as const satisfies Readonly<Partial<Record<SemanticOperationName, WebSessionContract>>>;
572
+
573
+ const linkedin = {
574
+ "contacts.list": contract("linkedin", "contacts.list", "R1", "capture-required", "consumer-web contact statistics require a fresh viewer-bound messaging-participant collection with real conversation and message pagination, group attribution, completeness, and acknowledgement-free behavior"),
575
+ "contacts.read": contract(
576
+ "linkedin",
577
+ "contacts.read",
578
+ LINKEDIN_WEB_OPERATIONS["contacts.read"].risk,
579
+ LINKEDIN_WEB_OPERATIONS["contacts.read"].state,
580
+ "reviewed contained-Chrome 1st-degree Contact-info read: bind the signed-in viewer, require a 1st-degree relationship on the requested vanity from classic Profile embeds or Como rehydration, then project page-embedded Contact-info fields or GET voyagerIdentityDashProfileContactInfo and project email plus any shown profile link, connected-since date, phones, websites, or birthday",
581
+ LINKEDIN_WEB_OPERATIONS["contacts.read"].contractVersion,
582
+ ),
583
+ "feeds.read": contract(
584
+ "linkedin",
585
+ "feeds.read",
586
+ LINKEDIN_WEB_OPERATIONS["feeds.read"].risk,
587
+ LINKEDIN_WEB_OPERATIONS["feeds.read"].state,
588
+ "reviewed contained-Chrome profile-activity page: resolve one live voyagerFeedDashProfileUpdates query, bind the signed-in viewer, and project one recent-activity page for the requested vanity. feed=home remains capture-required and is refused at runtime",
589
+ LINKEDIN_WEB_OPERATIONS["feeds.read"].contractVersion,
590
+ ),
591
+ "profiles.read": contract("linkedin", "profiles.read", LINKEDIN_WEB_OPERATIONS["profiles.read"].risk, LINKEDIN_WEB_OPERATIONS["profiles.read"].state, LINKEDIN_WEB_OPERATIONS["profiles.read"].reason),
592
+ "organizations.read": contract("linkedin", "organizations.read", LINKEDIN_WEB_OPERATIONS["organizations.read"].risk, LINKEDIN_WEB_OPERATIONS["organizations.read"].state, LINKEDIN_WEB_OPERATIONS["organizations.read"].reason),
593
+ "relationships.recommendations.read": contract("linkedin", "relationships.recommendations.read", "R1", "capture-required", "recommended-connection collection variables, paging, and viewer binding require a reviewed capture"),
594
+ "messaging.list": contract("linkedin", "messaging.list", "R1", "capture-required", "the prior mailbox projection drifted; exact current normalized identity-to-mailbox binding, registered query, paging, completeness, and acknowledgement-free behavior require a new reviewed capture"),
595
+ "messaging.read": contract("linkedin", "messaging.read", "R1", "capture-required", "message query revision is known, but exact current variables and acknowledgement-free response handling need a fresh reviewed capture"),
596
+ "articles.read": contract("linkedin", "articles.read", "R1", "capture-required", "exact response author, authenticated identity, and paging bindings require a fresh reviewed capture"),
597
+ "articles.draft.save": contract(
598
+ "linkedin",
599
+ "articles.draft.save",
600
+ LINKEDIN_WEB_OPERATIONS["articles.draft.save"].risk,
601
+ LINKEDIN_WEB_OPERATIONS["articles.draft.save"].state,
602
+ "reviewed current-member-bound native Article title/content autosave with PARAGRAPH, HEADING_1, HEADING_2, and QUOTE blocks plus distinct bounded cover and inline-image single-upload registrations, signed transfers, asset-URN projections, and exact unpublished server-response readback",
603
+ LINKEDIN_WEB_OPERATIONS["articles.draft.save"].contractVersion,
604
+ ),
605
+ "posts.read": contract("linkedin", "posts.read", "R1", "capture-required", "exact consumer-web post read requires a reviewed capture"),
606
+ "comments.read": contract("linkedin", "comments.read", "R1", "capture-required", "exact comment collection requires a reviewed capture"),
607
+ "messaging.send": contract("linkedin", "messaging.send", "R3", "capture-required", "createMessage mutation requires a reviewed capture and response binding"),
608
+ "media.publish": contract("linkedin", "media.publish", "R3", "capture-required", "member video upload registration, contiguous signed transfer, processing/finalization, audience and actor binding, created-share response, and independent exact-share readback require an authorized fixture"),
609
+ "posts.publish": contract(
610
+ "linkedin",
611
+ "posts.publish",
612
+ LINKEDIN_WEB_OPERATIONS["posts.publish"].risk,
613
+ LINKEDIN_WEB_OPERATIONS["posts.publish"].state,
614
+ "reviewed member-bound bounded page-staged IMAGE_SHARING registration/upload, registered post-create mutation, durable accepted-share targeting, and independent exact-share readback",
615
+ LINKEDIN_WEB_OPERATIONS["posts.publish"].contractVersion,
616
+ ),
617
+ "posts.repost": contract("linkedin", "posts.repost", "R3", "capture-required", "repost requires an exact reviewed mutation"),
618
+ "posts.quote": contract("linkedin", "posts.quote", "R3", "capture-required", "quote repost requires an exact reviewed mutation"),
619
+ "comments.create": contract("linkedin", "comments.create", "R3", "capture-required", "createComment requires exact actor/root/parent response binding"),
620
+ "replies.create": contract("linkedin", "replies.create", "R3", "capture-required", "reply creation requires exact actor/root/parent response binding"),
621
+ "reactions.set": contract("linkedin", "reactions.set", "R2", "capture-required", "desired-state reaction requires reviewed create/delete contracts"),
622
+ "relationships.connect": contract("linkedin", "relationships.connect", "R3", "capture-required", "connection invitation requires exact viewer, target, optional note, response, and duplicate-state bindings"),
623
+ "articles.publish": contract("linkedin", "articles.publish", "R3", "capture-required", "native Article publication remains a distinct unobserved operation"),
624
+ } as const satisfies Readonly<Partial<Record<SemanticOperationName, WebSessionContract>>>;
625
+
626
+ const hackerNews = {
627
+ "comments.create": contract("hacker-news", "comments.create", HACKER_NEWS_WEB_OPERATIONS["comments.create"].risk, HACKER_NEWS_WEB_OPERATIONS["comments.create"].state, HACKER_NEWS_WEB_OPERATIONS["comments.create"].reason),
628
+ "comments.read": contract("hacker-news", "comments.read", HACKER_NEWS_WEB_OPERATIONS["comments.read"].risk, HACKER_NEWS_WEB_OPERATIONS["comments.read"].state, HACKER_NEWS_WEB_OPERATIONS["comments.read"].reason),
629
+ "content.edit": contract("hacker-news", "content.edit", HACKER_NEWS_WEB_OPERATIONS["content.edit"].risk, HACKER_NEWS_WEB_OPERATIONS["content.edit"].state, HACKER_NEWS_WEB_OPERATIONS["content.edit"].reason),
630
+ "content.save": contract("hacker-news", "content.save", HACKER_NEWS_WEB_OPERATIONS["content.save"].risk, HACKER_NEWS_WEB_OPERATIONS["content.save"].state, HACKER_NEWS_WEB_OPERATIONS["content.save"].reason),
631
+ "feeds.read": contract("hacker-news", "feeds.read", HACKER_NEWS_WEB_OPERATIONS["feeds.read"].risk, HACKER_NEWS_WEB_OPERATIONS["feeds.read"].state, HACKER_NEWS_WEB_OPERATIONS["feeds.read"].reason),
632
+ "posts.publish": contract("hacker-news", "posts.publish", HACKER_NEWS_WEB_OPERATIONS["posts.publish"].risk, HACKER_NEWS_WEB_OPERATIONS["posts.publish"].state, HACKER_NEWS_WEB_OPERATIONS["posts.publish"].reason),
633
+ "posts.read": contract("hacker-news", "posts.read", HACKER_NEWS_WEB_OPERATIONS["posts.read"].risk, HACKER_NEWS_WEB_OPERATIONS["posts.read"].state, HACKER_NEWS_WEB_OPERATIONS["posts.read"].reason),
634
+ "reactions.set": contract("hacker-news", "reactions.set", HACKER_NEWS_WEB_OPERATIONS["reactions.set"].risk, HACKER_NEWS_WEB_OPERATIONS["reactions.set"].state, HACKER_NEWS_WEB_OPERATIONS["reactions.set"].reason),
635
+ "replies.create": contract("hacker-news", "replies.create", HACKER_NEWS_WEB_OPERATIONS["replies.create"].risk, HACKER_NEWS_WEB_OPERATIONS["replies.create"].state, HACKER_NEWS_WEB_OPERATIONS["replies.create"].reason),
636
+ } as const satisfies Readonly<Partial<Record<SemanticOperationName, WebSessionContract>>>;
637
+
638
+ const github = {
639
+ "organizations.read": contract(
640
+ "github",
641
+ "organizations.read",
642
+ GITHUB_WEB_OPERATIONS["organizations.read"].risk,
643
+ GITHUB_WEB_OPERATIONS["organizations.read"].state,
644
+ GITHUB_WEB_OPERATIONS["organizations.read"].reason,
645
+ ),
646
+ "profiles.read": contract(
647
+ "github",
648
+ "profiles.read",
649
+ GITHUB_WEB_OPERATIONS["profiles.read"].risk,
650
+ GITHUB_WEB_OPERATIONS["profiles.read"].state,
651
+ GITHUB_WEB_OPERATIONS["profiles.read"].reason,
652
+ ),
653
+ } as const satisfies Readonly<Partial<Record<SemanticOperationName, WebSessionContract>>>;
654
+
655
+ const x = {
656
+ "feeds.read": contract("x", "feeds.read", "R1", "observed", "current first-party GraphQL timeline/list/search/bookmark query"),
657
+ "profiles.read": contract("x", "profiles.read", "R1", "observed", "current target-bound UserByScreenName first-party GraphQL query with exact follower and following counts"),
658
+ "posts.read": contract("x", "posts.read", "R1", "observed", "current TweetDetail/UserTweets first-party GraphQL query"),
659
+ "comments.read": contract("x", "comments.read", "R1", "observed", "current TweetDetail conversation entries"),
660
+ "messaging.list": contract("x", "messaging.list", "R1", "capture-required", "current X Chat inbox events are encrypted and require the reviewed key-recovery runtime before plaintext listing"),
661
+ "messaging.read": contract("x", "messaging.read", "R1", "capture-required", "current X Chat conversation events are encrypted and require verified key recovery before plaintext projection"),
662
+ "articles.read": contract("x", "articles.read", "R1", "observed", "current-viewer-owned private Draft read through the exact ArticleEntityResultByRestId query with bounded title and rich-content projection; published Articles remain outside this contract", 2),
663
+ "articles.draft.save": contract("x", "articles.draft.save", "R2", "observed", "current bounded media INIT/APPEND/FINALIZE plus Article entity create/title/content mutations save one response-bound private rich-text-and-image draft and never call ArticleEntityPublish", 2),
664
+ "messaging.send": contract("x", "messaging.send", "R3", "capture-required", "DM send requires exact current mutation and target binding"),
665
+ "posts.publish": contract("x", "posts.publish", "R3", "observed", "current optional single-PNG or MP4 upload with pixels-only provenance scrub, CreateTweet tweet_text up to the reviewed 25000-unit long-post bound, strict CreateTweet response including note_tweet, durable accepted-target evidence, and fail-closed TweetResultByRestId readback for Made with AI labels", 5),
666
+ "threads.publish": contract("x", "threads.publish", "R3", "capture-required", "ordered CreateTweet root/self-reply dispatch needs an authorized live fixture and reviewed transaction-header behavior"),
667
+ "replies.create": contract("x", "replies.create", "R3", "capture-required", "CreateTweet reply needs an authorized live fixture and reviewed transaction-header behavior"),
668
+ "posts.repost": contract("x", "posts.repost", "R3", "capture-required", "repost desired-state mutation needs an authorized live fixture and reviewed transaction-header behavior"),
669
+ "posts.quote": contract("x", "posts.quote", "R3", "capture-required", "CreateTweet quote needs an authorized live fixture and reviewed transaction-header behavior; quote text uses the same reviewed 25000-unit CreateTweet bound as posts.publish", 2),
670
+ "likes.set": contract("x", "likes.set", "R2", "observed", "current FavoriteTweet/UnfavoriteTweet desired-state mutations with ephemeral transaction header and independent TweetResultByRestId readback", 2),
671
+ "content.save": contract("x", "content.save", "R2", "observed", "current CreateBookmark/DeleteBookmark desired-state mutations with ephemeral transaction header and independent TweetResultByRestId readback"),
672
+ "content.delete": contract("x", "content.delete", "R3", "capture-required", "current DeleteTweet request, accepted response, author binding, and exact not-found readback require an authorized live fixture; expected_text uses the same reviewed 25000-unit CreateTweet bound as posts.publish", 2),
673
+ "articles.publish": contract("x", "articles.publish", "R3", "capture-required", "ArticleEntityPublish and public readback remain outside the private draft contract", 4),
674
+ } as const satisfies Readonly<Partial<Record<SemanticOperationName, WebSessionContract>>>;
675
+
676
+ const reddit = {
677
+ ...Object.fromEntries(redditFlairContracts.map((definition) => [definition.operation, definition])),
678
+ "comments.create": contract("reddit", "comments.create", REDDIT_WEB_OPERATIONS["comments.create"].risk, REDDIT_WEB_OPERATIONS["comments.create"].state, REDDIT_WEB_OPERATIONS["comments.create"].reason),
679
+ "comments.read": contract("reddit", "comments.read", REDDIT_WEB_OPERATIONS["comments.read"].risk, REDDIT_WEB_OPERATIONS["comments.read"].state, REDDIT_WEB_OPERATIONS["comments.read"].reason),
680
+ "communities.membership.set": contract("reddit", "communities.membership.set", REDDIT_WEB_OPERATIONS["communities.membership.set"].risk, REDDIT_WEB_OPERATIONS["communities.membership.set"].state, REDDIT_WEB_OPERATIONS["communities.membership.set"].reason),
681
+ "content.delete": contract("reddit", "content.delete", REDDIT_WEB_OPERATIONS["content.delete"].risk, REDDIT_WEB_OPERATIONS["content.delete"].state, REDDIT_WEB_OPERATIONS["content.delete"].reason),
682
+ "content.edit": contract("reddit", "content.edit", REDDIT_WEB_OPERATIONS["content.edit"].risk, REDDIT_WEB_OPERATIONS["content.edit"].state, REDDIT_WEB_OPERATIONS["content.edit"].reason),
683
+ "content.save": contract("reddit", "content.save", REDDIT_WEB_OPERATIONS["content.save"].risk, REDDIT_WEB_OPERATIONS["content.save"].state, REDDIT_WEB_OPERATIONS["content.save"].reason),
684
+ "feeds.read": contract("reddit", "feeds.read", REDDIT_WEB_OPERATIONS["feeds.read"].risk, REDDIT_WEB_OPERATIONS["feeds.read"].state, REDDIT_WEB_OPERATIONS["feeds.read"].reason),
685
+ "media.read": contract("reddit", "media.read", REDDIT_WEB_OPERATIONS["media.read"].risk, REDDIT_WEB_OPERATIONS["media.read"].state, REDDIT_WEB_OPERATIONS["media.read"].reason, 2),
686
+ "media.publish": contract("reddit", "media.publish", REDDIT_WEB_OPERATIONS["media.publish"].risk, REDDIT_WEB_OPERATIONS["media.publish"].state, REDDIT_WEB_OPERATIONS["media.publish"].reason, 9),
687
+ "messaging.list": contract("reddit", "messaging.list", REDDIT_WEB_OPERATIONS["messaging.list"].risk, REDDIT_WEB_OPERATIONS["messaging.list"].state, REDDIT_WEB_OPERATIONS["messaging.list"].reason),
688
+ "messaging.read": contract("reddit", "messaging.read", REDDIT_WEB_OPERATIONS["messaging.read"].risk, REDDIT_WEB_OPERATIONS["messaging.read"].state, REDDIT_WEB_OPERATIONS["messaging.read"].reason),
689
+ "messaging.send": contract("reddit", "messaging.send", REDDIT_WEB_OPERATIONS["messaging.send"].risk, REDDIT_WEB_OPERATIONS["messaging.send"].state, REDDIT_WEB_OPERATIONS["messaging.send"].reason),
690
+ "posts.publish": contract("reddit", "posts.publish", REDDIT_WEB_OPERATIONS["posts.publish"].risk, REDDIT_WEB_OPERATIONS["posts.publish"].state, REDDIT_WEB_OPERATIONS["posts.publish"].reason),
691
+ "posts.read": contract("reddit", "posts.read", REDDIT_WEB_OPERATIONS["posts.read"].risk, REDDIT_WEB_OPERATIONS["posts.read"].state, REDDIT_WEB_OPERATIONS["posts.read"].reason),
692
+ "posts.repost": contract("reddit", "posts.repost", REDDIT_WEB_OPERATIONS["posts.repost"].risk, REDDIT_WEB_OPERATIONS["posts.repost"].state, REDDIT_WEB_OPERATIONS["posts.repost"].reason),
693
+ "profiles.read": contract("reddit", "profiles.read", REDDIT_WEB_OPERATIONS["profiles.read"].risk, REDDIT_WEB_OPERATIONS["profiles.read"].state, REDDIT_WEB_OPERATIONS["profiles.read"].reason),
694
+ "reactions.set": contract("reddit", "reactions.set", REDDIT_WEB_OPERATIONS["reactions.set"].risk, REDDIT_WEB_OPERATIONS["reactions.set"].state, REDDIT_WEB_OPERATIONS["reactions.set"].reason),
695
+ "relationships.follow.set": contract("reddit", "relationships.follow.set", REDDIT_WEB_OPERATIONS["relationships.follow.set"].risk, REDDIT_WEB_OPERATIONS["relationships.follow.set"].state, REDDIT_WEB_OPERATIONS["relationships.follow.set"].reason),
696
+ "replies.create": contract("reddit", "replies.create", REDDIT_WEB_OPERATIONS["replies.create"].risk, REDDIT_WEB_OPERATIONS["replies.create"].state, REDDIT_WEB_OPERATIONS["replies.create"].reason),
697
+ } as const satisfies Readonly<Partial<Record<SemanticOperationName, WebSessionContract>>>;
698
+
699
+ const whatsapp = {
700
+ "contacts.list": contract("whatsapp", "contacts.list", WHATSAPP_WEB_OPERATIONS["contacts.list"].risk, WHATSAPP_WEB_OPERATIONS["contacts.list"].state, WHATSAPP_WEB_OPERATIONS["contacts.list"].reason, 2),
701
+ "content.edit": contract("whatsapp", "content.edit", WHATSAPP_WEB_OPERATIONS["content.edit"].risk, WHATSAPP_WEB_OPERATIONS["content.edit"].state, WHATSAPP_WEB_OPERATIONS["content.edit"].reason),
702
+ "content.save": contract("whatsapp", "content.save", WHATSAPP_WEB_OPERATIONS["content.save"].risk, WHATSAPP_WEB_OPERATIONS["content.save"].state, WHATSAPP_WEB_OPERATIONS["content.save"].reason),
703
+ "content.share": contract("whatsapp", "content.share", WHATSAPP_WEB_OPERATIONS["content.share"].risk, WHATSAPP_WEB_OPERATIONS["content.share"].state, WHATSAPP_WEB_OPERATIONS["content.share"].reason),
704
+ "media.read": contract("whatsapp", "media.read", WHATSAPP_WEB_OPERATIONS["media.read"].risk, WHATSAPP_WEB_OPERATIONS["media.read"].state, WHATSAPP_WEB_OPERATIONS["media.read"].reason),
705
+ "messaging.list": contract("whatsapp", "messaging.list", WHATSAPP_WEB_OPERATIONS["messaging.list"].risk, WHATSAPP_WEB_OPERATIONS["messaging.list"].state, WHATSAPP_WEB_OPERATIONS["messaging.list"].reason),
706
+ "messaging.read": contract("whatsapp", "messaging.read", WHATSAPP_WEB_OPERATIONS["messaging.read"].risk, WHATSAPP_WEB_OPERATIONS["messaging.read"].state, WHATSAPP_WEB_OPERATIONS["messaging.read"].reason),
707
+ "messaging.send": contract("whatsapp", "messaging.send", WHATSAPP_WEB_OPERATIONS["messaging.send"].risk, WHATSAPP_WEB_OPERATIONS["messaging.send"].state, WHATSAPP_WEB_OPERATIONS["messaging.send"].reason),
708
+ "reactions.set": contract("whatsapp", "reactions.set", WHATSAPP_WEB_OPERATIONS["reactions.set"].risk, WHATSAPP_WEB_OPERATIONS["reactions.set"].state, WHATSAPP_WEB_OPERATIONS["reactions.set"].reason),
709
+ } as const satisfies Readonly<Partial<Record<SemanticOperationName, WebSessionContract>>>;
710
+
711
+ const beeper = {
712
+ "contacts.list": contract("beeper", "contacts.list", BEEPER_LOCAL_OPERATIONS["contacts.list"].risk, BEEPER_LOCAL_OPERATIONS["contacts.list"].state, BEEPER_LOCAL_OPERATIONS["contacts.list"].reason, BEEPER_LOCAL_OPERATIONS["contacts.list"].contractVersion),
713
+ "contacts.search": contract("beeper", "contacts.search", BEEPER_LOCAL_OPERATIONS["contacts.search"].risk, BEEPER_LOCAL_OPERATIONS["contacts.search"].state, BEEPER_LOCAL_OPERATIONS["contacts.search"].reason, BEEPER_LOCAL_OPERATIONS["contacts.search"].contractVersion),
714
+ "messaging.list": contract("beeper", "messaging.list", BEEPER_LOCAL_OPERATIONS["messaging.list"].risk, BEEPER_LOCAL_OPERATIONS["messaging.list"].state, BEEPER_LOCAL_OPERATIONS["messaging.list"].reason, BEEPER_LOCAL_OPERATIONS["messaging.list"].contractVersion),
715
+ "messaging.search": contract("beeper", "messaging.search", BEEPER_LOCAL_OPERATIONS["messaging.search"].risk, BEEPER_LOCAL_OPERATIONS["messaging.search"].state, BEEPER_LOCAL_OPERATIONS["messaging.search"].reason, BEEPER_LOCAL_OPERATIONS["messaging.search"].contractVersion),
716
+ "messaging.read": contract("beeper", "messaging.read", BEEPER_LOCAL_OPERATIONS["messaging.read"].risk, BEEPER_LOCAL_OPERATIONS["messaging.read"].state, BEEPER_LOCAL_OPERATIONS["messaging.read"].reason, BEEPER_LOCAL_OPERATIONS["messaging.read"].contractVersion),
717
+ } as const satisfies Readonly<Partial<Record<SemanticOperationName, WebSessionContract>>>;
718
+
719
+ const substack = {
720
+ "articles.publish": contract("substack", "articles.publish", SUBSTACK_WEB_OPERATIONS["articles.publish"].risk, SUBSTACK_WEB_OPERATIONS["articles.publish"].state, SUBSTACK_WEB_OPERATIONS["articles.publish"].reason),
721
+ "articles.read": contract("substack", "articles.read", SUBSTACK_WEB_OPERATIONS["articles.read"].risk, SUBSTACK_WEB_OPERATIONS["articles.read"].state, SUBSTACK_WEB_OPERATIONS["articles.read"].reason),
722
+ "comments.create": contract("substack", "comments.create", SUBSTACK_WEB_OPERATIONS["comments.create"].risk, SUBSTACK_WEB_OPERATIONS["comments.create"].state, SUBSTACK_WEB_OPERATIONS["comments.create"].reason),
723
+ "comments.read": contract("substack", "comments.read", SUBSTACK_WEB_OPERATIONS["comments.read"].risk, SUBSTACK_WEB_OPERATIONS["comments.read"].state, SUBSTACK_WEB_OPERATIONS["comments.read"].reason),
724
+ "content.delete": contract("substack", "content.delete", SUBSTACK_WEB_OPERATIONS["content.delete"].risk, SUBSTACK_WEB_OPERATIONS["content.delete"].state, SUBSTACK_WEB_OPERATIONS["content.delete"].reason),
725
+ "content.edit": contract("substack", "content.edit", SUBSTACK_WEB_OPERATIONS["content.edit"].risk, SUBSTACK_WEB_OPERATIONS["content.edit"].state, SUBSTACK_WEB_OPERATIONS["content.edit"].reason),
726
+ "content.save": contract("substack", "content.save", SUBSTACK_WEB_OPERATIONS["content.save"].risk, SUBSTACK_WEB_OPERATIONS["content.save"].state, SUBSTACK_WEB_OPERATIONS["content.save"].reason),
727
+ "content.schedule": contract("substack", "content.schedule", SUBSTACK_WEB_OPERATIONS["content.schedule"].risk, SUBSTACK_WEB_OPERATIONS["content.schedule"].state, SUBSTACK_WEB_OPERATIONS["content.schedule"].reason),
728
+ "content.share": contract("substack", "content.share", SUBSTACK_WEB_OPERATIONS["content.share"].risk, SUBSTACK_WEB_OPERATIONS["content.share"].state, SUBSTACK_WEB_OPERATIONS["content.share"].reason),
729
+ "feeds.read": contract("substack", "feeds.read", SUBSTACK_WEB_OPERATIONS["feeds.read"].risk, SUBSTACK_WEB_OPERATIONS["feeds.read"].state, SUBSTACK_WEB_OPERATIONS["feeds.read"].reason),
730
+ "likes.set": contract("substack", "likes.set", SUBSTACK_WEB_OPERATIONS["likes.set"].risk, SUBSTACK_WEB_OPERATIONS["likes.set"].state, SUBSTACK_WEB_OPERATIONS["likes.set"].reason),
731
+ "media.publish": contract("substack", "media.publish", SUBSTACK_WEB_OPERATIONS["media.publish"].risk, SUBSTACK_WEB_OPERATIONS["media.publish"].state, SUBSTACK_WEB_OPERATIONS["media.publish"].reason),
732
+ "media.read": contract("substack", "media.read", SUBSTACK_WEB_OPERATIONS["media.read"].risk, SUBSTACK_WEB_OPERATIONS["media.read"].state, SUBSTACK_WEB_OPERATIONS["media.read"].reason),
733
+ "messaging.list": contract("substack", "messaging.list", SUBSTACK_WEB_OPERATIONS["messaging.list"].risk, SUBSTACK_WEB_OPERATIONS["messaging.list"].state, SUBSTACK_WEB_OPERATIONS["messaging.list"].reason),
734
+ "messaging.read": contract("substack", "messaging.read", SUBSTACK_WEB_OPERATIONS["messaging.read"].risk, SUBSTACK_WEB_OPERATIONS["messaging.read"].state, SUBSTACK_WEB_OPERATIONS["messaging.read"].reason),
735
+ "messaging.send": contract("substack", "messaging.send", SUBSTACK_WEB_OPERATIONS["messaging.send"].risk, SUBSTACK_WEB_OPERATIONS["messaging.send"].state, SUBSTACK_WEB_OPERATIONS["messaging.send"].reason),
736
+ "organizations.read": contract("substack", "organizations.read", SUBSTACK_WEB_OPERATIONS["organizations.read"].risk, SUBSTACK_WEB_OPERATIONS["organizations.read"].state, SUBSTACK_WEB_OPERATIONS["organizations.read"].reason),
737
+ "posts.publish": contract("substack", "posts.publish", SUBSTACK_WEB_OPERATIONS["posts.publish"].risk, SUBSTACK_WEB_OPERATIONS["posts.publish"].state, SUBSTACK_WEB_OPERATIONS["posts.publish"].reason, SUBSTACK_WEB_OPERATIONS["posts.publish"].contractVersion),
738
+ "posts.quote": contract("substack", "posts.quote", SUBSTACK_WEB_OPERATIONS["posts.quote"].risk, SUBSTACK_WEB_OPERATIONS["posts.quote"].state, SUBSTACK_WEB_OPERATIONS["posts.quote"].reason),
739
+ "posts.read": contract("substack", "posts.read", SUBSTACK_WEB_OPERATIONS["posts.read"].risk, SUBSTACK_WEB_OPERATIONS["posts.read"].state, SUBSTACK_WEB_OPERATIONS["posts.read"].reason),
740
+ "posts.repost": contract("substack", "posts.repost", SUBSTACK_WEB_OPERATIONS["posts.repost"].risk, SUBSTACK_WEB_OPERATIONS["posts.repost"].state, SUBSTACK_WEB_OPERATIONS["posts.repost"].reason),
741
+ "profiles.read": contract("substack", "profiles.read", SUBSTACK_WEB_OPERATIONS["profiles.read"].risk, SUBSTACK_WEB_OPERATIONS["profiles.read"].state, SUBSTACK_WEB_OPERATIONS["profiles.read"].reason),
742
+ "relationships.follow.set": contract("substack", "relationships.follow.set", SUBSTACK_WEB_OPERATIONS["relationships.follow.set"].risk, SUBSTACK_WEB_OPERATIONS["relationships.follow.set"].state, SUBSTACK_WEB_OPERATIONS["relationships.follow.set"].reason),
743
+ "replies.create": contract("substack", "replies.create", SUBSTACK_WEB_OPERATIONS["replies.create"].risk, SUBSTACK_WEB_OPERATIONS["replies.create"].state, SUBSTACK_WEB_OPERATIONS["replies.create"].reason),
744
+ } as const satisfies Readonly<Partial<Record<SemanticOperationName, WebSessionContract>>>;
745
+
746
+ const tiktok = {
747
+ "comments.create": contract("tiktok", "comments.create", TIKTOK_WEB_OPERATIONS["comments.create"].risk, TIKTOK_WEB_OPERATIONS["comments.create"].state, TIKTOK_WEB_OPERATIONS["comments.create"].reason),
748
+ "comments.read": contract("tiktok", "comments.read", TIKTOK_WEB_OPERATIONS["comments.read"].risk, TIKTOK_WEB_OPERATIONS["comments.read"].state, TIKTOK_WEB_OPERATIONS["comments.read"].reason),
749
+ "content.delete": contract("tiktok", "content.delete", TIKTOK_WEB_OPERATIONS["content.delete"].risk, TIKTOK_WEB_OPERATIONS["content.delete"].state, TIKTOK_WEB_OPERATIONS["content.delete"].reason),
750
+ "content.save": contract("tiktok", "content.save", TIKTOK_WEB_OPERATIONS["content.save"].risk, TIKTOK_WEB_OPERATIONS["content.save"].state, TIKTOK_WEB_OPERATIONS["content.save"].reason),
751
+ "content.schedule": contract("tiktok", "content.schedule", TIKTOK_WEB_OPERATIONS["content.schedule"].risk, TIKTOK_WEB_OPERATIONS["content.schedule"].state, TIKTOK_WEB_OPERATIONS["content.schedule"].reason),
752
+ "content.share": contract("tiktok", "content.share", TIKTOK_WEB_OPERATIONS["content.share"].risk, TIKTOK_WEB_OPERATIONS["content.share"].state, TIKTOK_WEB_OPERATIONS["content.share"].reason),
753
+ "feeds.read": contract("tiktok", "feeds.read", TIKTOK_WEB_OPERATIONS["feeds.read"].risk, TIKTOK_WEB_OPERATIONS["feeds.read"].state, TIKTOK_WEB_OPERATIONS["feeds.read"].reason),
754
+ "likes.set": contract("tiktok", "likes.set", TIKTOK_WEB_OPERATIONS["likes.set"].risk, TIKTOK_WEB_OPERATIONS["likes.set"].state, TIKTOK_WEB_OPERATIONS["likes.set"].reason),
755
+ "media.publish": contract("tiktok", "media.publish", TIKTOK_WEB_OPERATIONS["media.publish"].risk, TIKTOK_WEB_OPERATIONS["media.publish"].state, TIKTOK_WEB_OPERATIONS["media.publish"].reason, TIKTOK_WEB_OPERATIONS["media.publish"].contractVersion),
756
+ "media.read": contract("tiktok", "media.read", TIKTOK_WEB_OPERATIONS["media.read"].risk, TIKTOK_WEB_OPERATIONS["media.read"].state, TIKTOK_WEB_OPERATIONS["media.read"].reason),
757
+ "messaging.list": contract("tiktok", "messaging.list", TIKTOK_WEB_OPERATIONS["messaging.list"].risk, TIKTOK_WEB_OPERATIONS["messaging.list"].state, TIKTOK_WEB_OPERATIONS["messaging.list"].reason),
758
+ "messaging.read": contract("tiktok", "messaging.read", TIKTOK_WEB_OPERATIONS["messaging.read"].risk, TIKTOK_WEB_OPERATIONS["messaging.read"].state, TIKTOK_WEB_OPERATIONS["messaging.read"].reason),
759
+ "messaging.send": contract("tiktok", "messaging.send", TIKTOK_WEB_OPERATIONS["messaging.send"].risk, TIKTOK_WEB_OPERATIONS["messaging.send"].state, TIKTOK_WEB_OPERATIONS["messaging.send"].reason),
760
+ "posts.publish": contract("tiktok", "posts.publish", TIKTOK_WEB_OPERATIONS["posts.publish"].risk, TIKTOK_WEB_OPERATIONS["posts.publish"].state, TIKTOK_WEB_OPERATIONS["posts.publish"].reason),
761
+ "posts.read": contract("tiktok", "posts.read", TIKTOK_WEB_OPERATIONS["posts.read"].risk, TIKTOK_WEB_OPERATIONS["posts.read"].state, TIKTOK_WEB_OPERATIONS["posts.read"].reason),
762
+ "posts.repost": contract("tiktok", "posts.repost", TIKTOK_WEB_OPERATIONS["posts.repost"].risk, TIKTOK_WEB_OPERATIONS["posts.repost"].state, TIKTOK_WEB_OPERATIONS["posts.repost"].reason),
763
+ "profiles.read": contract("tiktok", "profiles.read", TIKTOK_WEB_OPERATIONS["profiles.read"].risk, TIKTOK_WEB_OPERATIONS["profiles.read"].state, TIKTOK_WEB_OPERATIONS["profiles.read"].reason),
764
+ "relationships.follow.set": contract("tiktok", "relationships.follow.set", TIKTOK_WEB_OPERATIONS["relationships.follow.set"].risk, TIKTOK_WEB_OPERATIONS["relationships.follow.set"].state, TIKTOK_WEB_OPERATIONS["relationships.follow.set"].reason),
765
+ "replies.create": contract("tiktok", "replies.create", TIKTOK_WEB_OPERATIONS["replies.create"].risk, TIKTOK_WEB_OPERATIONS["replies.create"].state, TIKTOK_WEB_OPERATIONS["replies.create"].reason),
766
+ } as const satisfies Readonly<Partial<Record<SemanticOperationName, WebSessionContract>>>;
767
+
768
+ const twitch = {
769
+ "profiles.read": contract(
770
+ "twitch",
771
+ "profiles.read",
772
+ TWITCH_WEB_OPERATIONS["profiles.read"].risk,
773
+ TWITCH_WEB_OPERATIONS["profiles.read"].state,
774
+ TWITCH_WEB_OPERATIONS["profiles.read"].reason,
775
+ ),
776
+ } as const satisfies Readonly<Partial<Record<SemanticOperationName, WebSessionContract>>>;
777
+
778
+ const youtube = {
779
+ "comments.create": contract("youtube", "comments.create", "R3", "capture-required", "current comment mutation, actor/target response binding, and an authorized live fixture remain required"),
780
+ "comments.read": contract("youtube", "comments.read", "R1", "observed", "current acknowledgement-free Innertube next/continuation requests with exact video binding"),
781
+ "content.delete": contract("youtube", "content.delete", "R3", "capture-required", "cleanup only discarded the stalled incomplete Studio draft; no uploaded-video authored pre-read, accepted video/delete response, or exact-target absence readback was observed"),
782
+ "content.edit": contract("youtube", "content.edit", "R3", "capture-required", "video, Community-post, and comment edit mutations require separate reviewed contracts"),
783
+ "content.save": contract("youtube", "content.save", "R2", "capture-required", "the current Watch Later playlist edit implementation and target-bound readback are deterministic-test proven but still require an authorized low-stakes live fixture"),
784
+ "content.schedule": contract("youtube", "content.schedule", "R3", "capture-required", "Studio scheduling requires current multi-origin visibility, timezone, audience, and processing contracts"),
785
+ "feeds.read": contract("youtube", "feeds.read", "R1", "observed", "current signed-in fixed Innertube browse feeds"),
786
+ "likes.set": contract("youtube", "likes.set", "R2", "capture-required", "the current target-bound like implementation and independent readback are deterministic-test proven but still require an authorized low-stakes live fixture"),
787
+ "media.publish": contract("youtube", "media.publish", "R3", "capture-required", "the signed-in Studio capture reached metadata JSON responses, but the selected MP4 remained at 0%; resumable initiation, byte-transfer acceptance, finalization, processing, and exact current-account readback remain unproved", 2),
788
+ "media.read": contract("youtube", "media.read", "R1", "observed", "current fixed Innertube player metadata request with playback credentials omitted"),
789
+ "posts.publish": contract("youtube", "posts.publish", "R3", "capture-required", "Community text/media publication requires current actor binding and an authorized fixture"),
790
+ "posts.read": contract("youtube", "posts.read", "R1", "observed", "current resolve_url plus exact Community-post browse request"),
791
+ "profiles.read": contract("youtube", "profiles.read", "R1", "observed", "current target-bound first-party channel response with exact subscriber, video, and lifetime-view counts"),
792
+ "relationships.follow.set": contract("youtube", "relationships.follow.set", "R2", "capture-required", "the current target-bound subscription implementation and independent browse readback are deterministic-test proven but still require an authorized low-stakes live fixture"),
793
+ "replies.create": contract("youtube", "replies.create", "R3", "capture-required", "current reply mutation, parent binding, and an authorized live fixture remain required"),
794
+ } as const satisfies Readonly<Partial<Record<SemanticOperationName, WebSessionContract>>>;
795
+
796
+ export const webSessionContractDefinitions = {
797
+ beeper,
798
+ bluesky,
799
+ facebook,
800
+ "facebook-group": facebookGroup,
801
+ "facebook-marketplace": facebookMarketplace,
802
+ "facebook-page": facebookPage,
803
+ github,
804
+ "hacker-news": hackerNews,
805
+ instagram,
806
+ linkedin,
807
+ reddit,
808
+ substack,
809
+ tiktok,
810
+ threads,
811
+ twitch,
812
+ x,
813
+ youtube,
814
+ whatsapp,
815
+ } as const satisfies Readonly<Partial<Record<
816
+ WebSessionSiteId,
817
+ Readonly<Partial<Record<SemanticOperationName, WebSessionContract>>>
818
+ >>>;
819
+
820
+ export { planWebSessionContractDispatches } from "./web-session-contract-planning";