@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,1055 @@
1
+ import * as Context from "effect/Context";
2
+ import * as Effect from "effect/Effect";
3
+ import { GENERIC_EXECUTOR_TERMINATION, type BoundedExecution, type LedgerEntry, type LedgerSnapshot, type RunPreparedOptions } from "./confirmed-write-model";
4
+ import type { PreparedInvocation, StoredPlan, InvocationPlan, InvocationDuplicateRiskV1, InvocationResult, RunReceipt, ConfirmationClaimSnapshot, ConfirmationClaimRepairReport, RunJournalRepairReport, confirmInvocation } from "./runtime";
5
+ import { ConfirmedWriteFailure, confirmedWriteAttempt, type ConfirmedWritePhase } from "./confirmed-write-failure";
6
+
7
+ import { redactSensitiveText } from "@hraness/kb/clip/persist";
8
+
9
+ import { executeBrowserRecipe, PreservedBrowserArtifactsError, type BrowserDispatchEvent } from "./browser";
10
+ import { canonicalJson, DOM_ACTION_TRANSPORT_DISABLED_MESSAGE, expandBrowserRecipe, isLocalCliOperation, isProviderOperation, isReviewedTemplateOperation, isWebSessionOperation, manifestHash, sha256, type FileInputValue, type InputValue, type GhostgetManifest } from "./model";
11
+
12
+ import { localCliContractIdentity } from "./local-cli-contracts";
13
+ import type { LocalCliDispatchEvent, LocalCliExecutionOptions } from "./local-cli-execution";
14
+ import { runLocalCliOperationWithDeadline } from "./local-cli-execution";
15
+ import { localCliToolArtifactForCurrentRuntime } from "./local-cli-tool-identity";
16
+
17
+ import {
18
+ getProviderContract,
19
+ providerContractHash,
20
+ } from "./provider-contracts";
21
+ import {
22
+ executeProviderOperation,
23
+ type ProviderDispatchEvent,
24
+ } from "./provider";
25
+ import { runProviderPluginPlanConformance } from "./provider-plugin";
26
+
27
+ import type {
28
+ ProviderPluginOperationResolutionV1,
29
+ ProviderPluginRegistry,
30
+ } from "./provider-plugin-registry";
31
+ import { isProviderPluginOperationName } from "./provider-plugin-identifiers";
32
+ import { type PortableOperationIdentityV1 } from "./provider-plugin-portable-identity";
33
+ import {
34
+ acquirePortableProviderPluginInvocationLease,
35
+ createPortableProviderPluginInvocationLeaseContainmentController,
36
+ releasePortableProviderPluginInvocationLease,
37
+ } from "./provider-plugin-invocation-lease";
38
+ import {
39
+ createPortableProviderPluginCleanupContext,
40
+ } from "./provider-plugin-cleanup-barrier";
41
+
42
+ import { providerPluginRegistry } from "./provider-plugins";
43
+ import {
44
+ getWebSessionContract,
45
+ webSessionContractHash,
46
+ } from "./web-session-contracts";
47
+ import { runWebSessionOperationWithDeadline, WebSessionCleanupUnverifiedError, type WebSessionExecutionOptions, type WebSessionDispatchEvent, type WebSessionProviderAcceptedMutationTargetEvent, type WebSessionProviderBoundMutationTargetEvent } from "./web-session-execution";
48
+ import {
49
+ WebSessionCleanupAdmissionBlockedError,
50
+ acquireWebSessionCleanupAdmission,
51
+ type WebSessionCleanupAdmissionIdentity,
52
+ } from "./web-session-cleanup-admission";
53
+ import { isPublicWebSessionInvocationAuthority, persistedAuthAuthority, type InvocationAuthority } from "./web-session-authentication-policy";
54
+ import { executeReviewedTemplateOperation, planReviewedTemplateDispatches, reviewedTemplateHash, type ReviewedTemplateDispatchEvent } from "./reviewed-template";
55
+ import { cleanupPlanAssets, resolvePlanAssetFiles } from "./plan-assets";
56
+ import { writeProviderAcceptedMutationTargetEvidence, writeRecoveryCapsule, type RecoveryContractIdentity } from "./recovery";
57
+ import { createRunJournal, initialRunJournal, readRunJournal, updateRunJournal, type RunJournal, type RunJournalSnapshot } from "./run-journal";
58
+ import { currentProcessStartIdentity } from "./process-identity";
59
+
60
+ import { loadInstalledManifest, removePrivateStateFile } from "./storage";
61
+
62
+ export interface ConfirmedWriteKernel {
63
+ readonly isInputArray: (value: InputValue) => value is readonly (string | number | boolean | FileInputValue)[];
64
+ readonly isFileInputValue: (value: unknown) => value is FileInputValue;
65
+ readonly resolveCodeOwnedPluginOperation: (operation: GhostgetManifest["operations"][string], registry: ProviderPluginRegistry) => ProviderPluginOperationResolutionV1 | null;
66
+ readonly hasExactKeys: (value: Record<string, unknown>, keys: readonly string[]) => boolean;
67
+ readonly revalidatePreparedInvocation: (invocation: PreparedInvocation, registry: ProviderPluginRegistry) => {
68
+ readonly invocation: PreparedInvocation;
69
+ readonly operation: GhostgetManifest["operations"][string];
70
+ };
71
+ readonly planPath: (digest: string, environment: Readonly<Record<string, string | undefined>>) => string;
72
+ readonly acquireConfirmationClaim: (digest: string, runId: string, environment: Readonly<Record<string, string | undefined>>, now: Date) => ConfirmationClaimSnapshot;
73
+ readonly releaseConfirmationClaim: (snapshot: ConfirmationClaimSnapshot, environment: Readonly<Record<string, string | undefined>>) => boolean;
74
+ readonly authHash: (auth: InvocationAuthority) => string;
75
+ readonly finalOrigin: (value: string | null, origins: readonly string[]) => string | null;
76
+ readonly boundedRecoveryHandle: (value: string | undefined) => string | null;
77
+ readonly loadInstalledManifestWithRegistry: (adapterId: string, environment: Readonly<Record<string, string | undefined>>, registry: ProviderPluginRegistry) => ReturnType<typeof loadInstalledManifest>;
78
+ readonly resolveInvocationDuplicateRisk: (plan: InvocationPlan, requestedRunIds: readonly string[], environment: Readonly<Record<string, string | undefined>>) => InvocationDuplicateRiskV1 | undefined;
79
+ readonly claimDuplicateRiskSource: (binding: InvocationDuplicateRiskV1, successorRunId: string, environment: Readonly<Record<string, string | undefined>>, now: Date) => void;
80
+ readonly loadInvocationPlan: (digest: string, environment: Readonly<Record<string, string | undefined>>) => StoredPlan;
81
+ readonly planDigestHasRetainingJournal: (digest: string, environment: Readonly<Record<string, string | undefined>>) => boolean;
82
+ readonly validateFreshPlan: (stored: StoredPlan, environment: Readonly<Record<string, string | undefined>>, now: Date, registry: ProviderPluginRegistry, loadManifest: typeof loadInstalledManifest) => PreparedInvocation;
83
+ readonly isDispatchProgress: (value: unknown) => value is RunReceipt["dispatch"];
84
+ readonly ledgerPath: (adapterHash: string, authHashValue: string, operationId: string, inputHash: string, environment: Readonly<Record<string, string | undefined>>, duplicateIntentHash?: string) => string;
85
+ readonly acquireLedger: (path: string, entry: LedgerEntry, environment: Readonly<Record<string, string | undefined>>, now: Date) => | { readonly acquired: true; readonly snapshot: LedgerSnapshot }
86
+ | { readonly acquired: false; readonly existing: LedgerEntry };
87
+ readonly writeReceipt: (receipt: RunReceipt, environment: Readonly<Record<string, string | undefined>>) => void;
88
+ readonly runJournalReceipt: (journal: RunJournal) => RunReceipt;
89
+ readonly relativeStatePath: (path: string, environment: Readonly<Record<string, string | undefined>>) => string;
90
+ readonly projectRunJournal: (journal: RunJournal, environment: Readonly<Record<string, string | undefined>>) => void;
91
+ readonly repairInterruptedConfirmationClaims: (environment: Readonly<Record<string, string | undefined>>) => ConfirmationClaimRepairReport;
92
+ readonly repairInterruptedRunJournals: (environment: Readonly<Record<string, string | undefined>>, now: Date) => RunJournalRepairReport;
93
+ readonly foreignDataRecord: (value: unknown) => Record<string, unknown> | null;
94
+ readonly executionOutputLimit: (operation: GhostgetManifest["operations"][string]) => number;
95
+ readonly boundedThrownExecutorReason: (error: unknown) => string;
96
+ readonly boundedExecutionResult: (value: unknown, kind: | "browser"
97
+ | "provider"
98
+ | "web-session"
99
+ | "local-cli"
100
+ | "reviewed-template", maxOutputBytes: number) => BoundedExecution;
101
+ readonly readRunReceipt: (runId: string, environment: Readonly<Record<string, string | undefined>>) => RunReceipt;
102
+ }
103
+
104
+
105
+ type ConfirmOptions = Parameters<typeof confirmInvocation>[1];
106
+ type NativeOutcome = { readonly status: "fulfilled"; readonly value: unknown } | { readonly status: "rejected"; readonly reason: unknown };
107
+
108
+ /** Each instance belongs to exactly one confirmation, including its native ALS context. */
109
+ export function makeConfirmedWritePlatform(kernel: ConfirmedWriteKernel, original: ConfirmOptions) {
110
+ const environment = original.environment ?? process.env;
111
+ let portableContext: ReturnType<typeof createPortableProviderPluginCleanupContext> | null = null;
112
+ const attempt = <A>(phase: ConfirmedWritePhase, work: () => A) =>
113
+ confirmedWriteAttempt(phase, () => portableContext === null ? work() : portableContext.run(work));
114
+ const native = <A>(phase: ConfirmedWritePhase, work: () => Promise<A>) =>
115
+ Effect.tryPromise({
116
+ try: () => portableContext === null ? work() : portableContext.run(work),
117
+ catch: cause => new ConfirmedWriteFailure({ phase, cause }),
118
+ }).pipe(Effect.uninterruptible);
119
+ const {
120
+ isInputArray, isFileInputValue,
121
+ resolveCodeOwnedPluginOperation,
122
+ hasExactKeys,
123
+ revalidatePreparedInvocation,
124
+ planPath,
125
+ acquireConfirmationClaim,
126
+ releaseConfirmationClaim,
127
+ authHash,
128
+ finalOrigin,
129
+ boundedRecoveryHandle,
130
+ loadInstalledManifestWithRegistry,
131
+ resolveInvocationDuplicateRisk,
132
+ claimDuplicateRiskSource,
133
+ loadInvocationPlan,
134
+ planDigestHasRetainingJournal,
135
+ validateFreshPlan,
136
+ isDispatchProgress,
137
+ ledgerPath,
138
+ acquireLedger,
139
+ writeReceipt,
140
+ runJournalReceipt,
141
+ relativeStatePath,
142
+ projectRunJournal,
143
+ repairInterruptedConfirmationClaims,
144
+ repairInterruptedRunJournals,
145
+ foreignDataRecord,
146
+ executionOutputLimit,
147
+ boundedThrownExecutorReason,
148
+ boundedExecutionResult,
149
+ readRunReceipt
150
+ } = kernel;
151
+
152
+ const now = () => original.now ?? new Date();
153
+ function execution(checked: ReturnType<ConfirmedWriteKernel["revalidatePreparedInvocation"]>, planDigest: string, options: RunPreparedOptions) {
154
+ const registry = options.registry ?? providerPluginRegistry;
155
+ const invocation = checked.invocation;
156
+ const operation = checked.operation;
157
+ if (operation.risk === "R4") throw new Error("R4 capabilities are blocked by wrench");
158
+ if (operation.risk !== "R2" && operation.risk !== "R3") throw new Error("only R2 and R3 plans use confirmation");
159
+ const risk = operation.risk;
160
+ const isWrite = true;
161
+ const persistReceipt = options.persistReceipt ?? writeReceipt;
162
+ const observedTime = () => options.now ?? new Date();
163
+ const providerOperation = isProviderOperation(operation);
164
+ const webSessionOperation = isWebSessionOperation(operation);
165
+ const localCliOperation = isLocalCliOperation(operation);
166
+ const reviewedTemplateOperation = isReviewedTemplateOperation(operation);
167
+ const pluginResolution = resolveCodeOwnedPluginOperation(operation, registry);
168
+ const plannedDispatches = pluginResolution !== null
169
+ ? runProviderPluginPlanConformance(
170
+ pluginResolution.operation,
171
+ invocation.input,
172
+ )
173
+ : reviewedTemplateOperation
174
+ ? planReviewedTemplateDispatches(invocation.operationId, operation.risk, operation.reviewedTemplate)
175
+ : operation.browser === undefined
176
+ ? (() => {
177
+ throw new Error(DOM_ACTION_TRANSPORT_DISABLED_MESSAGE);
178
+ })()
179
+ : expandBrowserRecipe(operation.browser, invocation.input).dispatches;
180
+ if (
181
+ options.confirmedDispatches !== undefined
182
+ && canonicalJson(plannedDispatches) !== canonicalJson(options.confirmedDispatches)
183
+ ) {
184
+ throw new Error(
185
+ "planned dispatch schedule changed before execution; preview the action again",
186
+ );
187
+ }
188
+ const planned = plannedDispatches.length;
189
+ const currentProviderContractHash = providerOperation
190
+ ? providerContractHash(
191
+ getProviderContract(operation.provider, registry),
192
+ registry,
193
+ )
194
+ : null;
195
+ const currentWebSessionContractHash = webSessionOperation
196
+ ? webSessionContractHash(
197
+ getWebSessionContract(operation.webSession, registry),
198
+ registry,
199
+ )
200
+ : null;
201
+ const currentLocalCliContractIdentity = localCliOperation
202
+ ? localCliContractIdentity(operation.localCli, registry)
203
+ : null;
204
+ const currentReviewedTemplateContractHash = reviewedTemplateOperation
205
+ ? reviewedTemplateHash(operation.reviewedTemplate)
206
+ : null;
207
+ const currentPortablePluginContract =
208
+ pluginResolution?.portableIdentity ?? null;
209
+ const recoveryContract = (): RecoveryContractIdentity => {
210
+ if (currentPortablePluginContract !== null) {
211
+ return {
212
+ transport: "portable-provider-plugin",
213
+ identity: currentPortablePluginContract,
214
+ };
215
+ }
216
+ if (providerOperation) {
217
+ if (currentProviderContractHash === null) throw new Error("official provider contract hash is unavailable");
218
+ return {
219
+ transport: "provider-api",
220
+ provider: operation.provider.provider,
221
+ action: operation.provider.action,
222
+ version: operation.provider.contractVersion,
223
+ hash: currentProviderContractHash,
224
+ };
225
+ }
226
+ if (webSessionOperation) {
227
+ if (currentWebSessionContractHash === null) throw new Error("authenticated web contract hash is unavailable");
228
+ return {
229
+ transport: "web-session-api",
230
+ site: operation.webSession.site,
231
+ action: operation.webSession.action,
232
+ version: operation.webSession.contractVersion,
233
+ hash: currentWebSessionContractHash,
234
+ };
235
+ }
236
+ if (localCliOperation) {
237
+ if (currentLocalCliContractIdentity === null) {
238
+ throw new Error("local CLI contract identity is unavailable");
239
+ }
240
+ return {
241
+ transport: "local-cli",
242
+ identity: currentLocalCliContractIdentity,
243
+ };
244
+ }
245
+ if (reviewedTemplateOperation) {
246
+ if (currentReviewedTemplateContractHash === null) throw new Error("reviewed template contract hash is unavailable");
247
+ return {
248
+ transport: "reviewed-template-api",
249
+ version: operation.reviewedTemplate.contractVersion,
250
+ hash: currentReviewedTemplateContractHash,
251
+ };
252
+ }
253
+ throw new Error(DOM_ACTION_TRANSPORT_DISABLED_MESSAGE);
254
+ };
255
+ const inputHash = sha256(canonicalJson(invocation.input));
256
+ const runId = options.runId ?? crypto.randomUUID();
257
+ if (!/^[0-9a-f-]{36}$/u.test(runId)) {
258
+ throw new Error("run ID is malformed");
259
+ }
260
+ const startedAt = (options.now ?? new Date()).toISOString();
261
+ const adapter = {
262
+ id: invocation.manifest.id,
263
+ version: invocation.manifest.version,
264
+ hash: manifestHash(invocation.manifest),
265
+ };
266
+ const auth = { id: invocation.auth.id, hash: authHash(invocation.auth), kind: invocation.auth.kind };
267
+ const durableWriteAuth = (): InvocationPlan["auth"] => {
268
+ if (!isWrite) throw new Error("read invocation has no durable write auth");
269
+ const selected = persistedAuthAuthority(invocation.auth);
270
+ return {
271
+ id: selected.id,
272
+ hash: authHash(selected),
273
+ kind: selected.kind,
274
+ };
275
+ };
276
+ const withTransport = (value: Omit<RunReceipt, "schemaVersion" | "transport" | "providerContractHash" | "webSessionContractHash" | "localCliContract" | "reviewedTemplateContractHash" | "portablePluginContract">): RunReceipt => {
277
+ if (currentPortablePluginContract !== null) {
278
+ return {
279
+ ...value,
280
+ schemaVersion: 6,
281
+ transport: "portable-provider-plugin",
282
+ portablePluginContract: currentPortablePluginContract,
283
+ };
284
+ }
285
+ if (providerOperation) {
286
+ if (currentProviderContractHash === null) throw new Error("official provider contract hash is unavailable");
287
+ return {
288
+ ...value,
289
+ schemaVersion: 3,
290
+ transport: "provider-api",
291
+ providerContractHash: currentProviderContractHash,
292
+ };
293
+ }
294
+ if (webSessionOperation) {
295
+ if (currentWebSessionContractHash === null) throw new Error("authenticated web contract hash is unavailable");
296
+ return {
297
+ ...value,
298
+ schemaVersion: 4,
299
+ transport: "web-session-api",
300
+ webSessionContractHash: currentWebSessionContractHash,
301
+ };
302
+ }
303
+ if (localCliOperation) {
304
+ if (currentLocalCliContractIdentity === null) {
305
+ throw new Error("local CLI contract identity is unavailable");
306
+ }
307
+ return {
308
+ ...value,
309
+ schemaVersion: 7,
310
+ transport: "local-cli",
311
+ localCliContract: currentLocalCliContractIdentity,
312
+ };
313
+ }
314
+ if (reviewedTemplateOperation) {
315
+ if (currentReviewedTemplateContractHash === null) throw new Error("reviewed template contract hash is unavailable");
316
+ return {
317
+ ...value,
318
+ schemaVersion: 5,
319
+ transport: "reviewed-template-api",
320
+ reviewedTemplateContractHash: currentReviewedTemplateContractHash,
321
+ };
322
+ }
323
+ return { ...value, schemaVersion: 2, transport: "browser" };
324
+ };
325
+ let journal: RunJournalSnapshot | null = null;
326
+ let durableReceipt: RunReceipt = withTransport({
327
+ runId,
328
+ planDigest,
329
+ adapter,
330
+ operation: invocation.operationId,
331
+ risk: operation.risk,
332
+ inputHash,
333
+ auth,
334
+ status: "pending",
335
+ dispatchStarted: false,
336
+ dispatch: { planned, started: 0, verified: 0 },
337
+ startedAt,
338
+ finishedAt: startedAt,
339
+ finalOrigin: null,
340
+ error: "execution was prepared but no durable final outcome was recorded",
341
+ });
342
+ let duplicateSourceClaimed = false;
343
+ const persistDispatchProgress = (
344
+ event:
345
+ | BrowserDispatchEvent
346
+ | ProviderDispatchEvent
347
+ | WebSessionDispatchEvent
348
+ | LocalCliDispatchEvent
349
+ | ReviewedTemplateDispatchEvent,
350
+ phase: "starting" | "verified",
351
+ ): Promise<void> => {
352
+ const expectedDispatch = plannedDispatches[event.index - 1];
353
+ const prior = durableReceipt.dispatch;
354
+ const expectedPrior = phase === "starting"
355
+ ? { planned, started: event.index - 1, verified: event.index - 1 }
356
+ : { planned, started: event.index, verified: event.index - 1 };
357
+ if (
358
+ !isDispatchProgress(event.progress)
359
+ || event.progress.planned !== planned
360
+ || event.index < 1
361
+ || event.index > planned
362
+ || expectedDispatch === undefined
363
+ || event.id !== expectedDispatch.id
364
+ || canonicalJson(prior) !== canonicalJson(expectedPrior)
365
+ || (phase === "starting"
366
+ ? event.progress.started !== event.index - 1
367
+ || event.progress.verified !== event.index - 1
368
+ : event.progress.started !== event.index
369
+ || event.progress.verified !== event.index)
370
+ ) {
371
+ return Promise.reject(new Error("dispatch progress diverged from the confirmed schedule"));
372
+ }
373
+ const dispatch = phase === "starting"
374
+ ? { planned, started: event.index, verified: event.progress.verified }
375
+ : event.progress;
376
+ if (!isDispatchProgress(dispatch)) return Promise.reject(new Error("dispatch progress is malformed"));
377
+ const progressAt = (options.now ?? new Date()).toISOString();
378
+ let next: RunReceipt = {
379
+ ...durableReceipt,
380
+ status: "pending",
381
+ dispatchStarted: dispatch.started > 0,
382
+ dispatch,
383
+ finishedAt: progressAt,
384
+ error: phase === "starting"
385
+ ? "a dispatch was durably marked before provider submission; a missing final outcome requires reconciliation"
386
+ : "verified dispatch progress was stored; execution has not reached a durable final outcome",
387
+ };
388
+ try {
389
+ if (
390
+ phase === "starting"
391
+ && event.index === 1
392
+ && options.duplicateRisk !== undefined
393
+ && !duplicateSourceClaimed
394
+ ) {
395
+ claimDuplicateRiskSource(
396
+ options.duplicateRisk,
397
+ runId,
398
+ options.environment,
399
+ options.now ?? new Date(),
400
+ );
401
+ duplicateSourceClaimed = true;
402
+ }
403
+ if (journal !== null) {
404
+ journal = updateRunJournal(journal, {
405
+ type: phase === "starting" ? "dispatch-started" : "dispatch-verified",
406
+ index: event.index,
407
+ at: progressAt,
408
+ }, options.environment);
409
+ next = runJournalReceipt(journal.journal);
410
+ durableReceipt = next;
411
+ } else {
412
+ persistReceipt(next, options.environment);
413
+ }
414
+ durableReceipt = next;
415
+ return Promise.resolve();
416
+ } catch (error) {
417
+ return Promise.reject(new Error("refusing provider dispatch because durable progress could not be stored", { cause: error }));
418
+ }
419
+ };
420
+ const persistProviderBoundMutationTarget = (
421
+ eventValue:
422
+ | WebSessionProviderAcceptedMutationTargetEvent
423
+ | WebSessionProviderBoundMutationTargetEvent,
424
+ ): Promise<void> => {
425
+ const event = foreignDataRecord(eventValue);
426
+ if (
427
+ event === null
428
+ || !hasExactKeys(event, ["id", "index", "target"])
429
+ || typeof event.id !== "string"
430
+ || !Number.isSafeInteger(event.index)
431
+ ) {
432
+ return Promise.reject(new Error(
433
+ "provider-accepted mutation target event is malformed",
434
+ ));
435
+ }
436
+ const index = event.index as number;
437
+ const expectedDispatch = plannedDispatches[index - 1];
438
+ const current = durableReceipt.dispatch;
439
+ if (
440
+ !isWrite
441
+ || (!webSessionOperation && !localCliOperation)
442
+ || journal === null
443
+ || expectedDispatch === undefined
444
+ || event.id !== expectedDispatch.id
445
+ || index < 1
446
+ || index > planned
447
+ || current.planned !== planned
448
+ || current.started !== index
449
+ || current.verified !== index - 1
450
+ ) {
451
+ return Promise.reject(new Error(
452
+ "provider-accepted mutation target diverged from the active dispatch",
453
+ ));
454
+ }
455
+ const contract = recoveryContract();
456
+ if (
457
+ contract.transport !== "web-session-api"
458
+ && contract.transport !== "local-cli"
459
+ ) {
460
+ return Promise.reject(new Error(
461
+ "provider-accepted mutation target requires a session or local CLI contract",
462
+ ));
463
+ }
464
+ try {
465
+ writeProviderAcceptedMutationTargetEvidence({
466
+ schemaVersion: 1,
467
+ runId,
468
+ acceptedAt: (options.now ?? new Date()).toISOString(),
469
+ planDigest,
470
+ adapter,
471
+ operation: invocation.operationId,
472
+ inputHash,
473
+ auth,
474
+ contract,
475
+ dispatch: {
476
+ id: event.id,
477
+ index,
478
+ planned,
479
+ },
480
+ target: event.target,
481
+ }, options.environment);
482
+ return Promise.resolve();
483
+ } catch (error) {
484
+ return Promise.reject(new Error(
485
+ "provider-accepted mutation target could not be stored",
486
+ { cause: error },
487
+ ));
488
+ }
489
+ };
490
+ const executionKind = providerOperation
491
+ ? "provider"
492
+ : webSessionOperation
493
+ ? "web-session"
494
+ : localCliOperation
495
+ ? "local-cli"
496
+ : reviewedTemplateOperation ? "reviewed-template" : "browser";
497
+ const exactTargetReconciliationKind =
498
+ pluginResolution?.operation.reconciliation?.kind;
499
+ const publicWebSessionOperation = webSessionOperation
500
+ && isPublicWebSessionInvocationAuthority(invocation.auth);
501
+
502
+ const requireJournal = () => {
503
+ if (journal === null) throw new Error("remote write has no run journal");
504
+ return journal;
505
+ };
506
+ const record = (event: Parameters<typeof updateRunJournal>[1]) => {
507
+ journal = updateRunJournal(requireJournal(), event, options.environment);
508
+ };
509
+ return {
510
+ startedAt, runId, inputHash, adapter, auth,
511
+ get journalTerminal() { return journal?.journal.phase === "terminal"; },
512
+ journalRequest: attempt("journal", () => {
513
+ if (planDigest === null) throw new Error("remote writes require a confirmation plan");
514
+ if (
515
+ options.confirmationClaim === undefined
516
+ || options.confirmationClaim.claim.digest !== planDigest
517
+ || options.confirmationClaim.claim.runId !== runId
518
+ ) {
519
+ throw new Error(
520
+ "remote writes require an exact durable confirmation ownership claim",
521
+ );
522
+ }
523
+ if (!isProviderPluginOperationName(invocation.operationId)) {
524
+ throw new Error("run journal operation name is malformed");
525
+ }
526
+ const contract = recoveryContract();
527
+ const timeoutMs = providerOperation
528
+ ? operation.provider.timeoutMs
529
+ : webSessionOperation
530
+ ? operation.webSession.timeoutMs
531
+ : localCliOperation
532
+ ? operation.localCli.timeoutMs
533
+ : reviewedTemplateOperation
534
+ ? operation.reviewedTemplate.state === "reviewed"
535
+ ? operation.reviewedTemplate.timeoutMs
536
+ : 10 * 60_000
537
+ : operation.browser?.timeoutMs ?? 10 * 60_000;
538
+ const processIdentity = currentProcessStartIdentity();
539
+ const operationId = invocation.operationId;
540
+ return () => {
541
+ journal = createRunJournal(initialRunJournal({
542
+ runId,
543
+ planDigest,
544
+ adapter,
545
+ operation: operationId,
546
+ risk,
547
+ inputHash,
548
+ auth: durableWriteAuth(),
549
+ contract: contract.transport === "portable-provider-plugin"
550
+ || contract.transport === "local-cli"
551
+ ? contract
552
+ : {
553
+ transport: contract.transport,
554
+ hash: contract.hash,
555
+ },
556
+ ...(options.duplicateRisk === undefined
557
+ ? {}
558
+ : {
559
+ duplicateIntent: {
560
+ schemaVersion: 1 as const,
561
+ intentHash: options.duplicateRisk.intentHash,
562
+ sourceRunId: options.duplicateRisk.sourceRunId,
563
+ },
564
+ }),
565
+ plannedDispatches: planned,
566
+ hasPlanAssets: options.hasPlanAssets === true,
567
+ owner: {
568
+ pid: process.pid,
569
+ token: crypto.randomUUID(),
570
+ ...processIdentity,
571
+ leaseUntil: new Date(
572
+ Date.parse(startedAt) + timeoutMs + 30_000,
573
+ ).toISOString(),
574
+ },
575
+ startedAt,
576
+ dedupeExpiresAt: new Date(
577
+ Date.parse(startedAt) + operation.dedupeWindowMs,
578
+ ).toISOString(),
579
+ }), options.environment);
580
+ };
581
+ }),
582
+ createJournal: (create: () => void) => attempt("journal", create),
583
+ removePlan: attempt("confirmation", () => removePrivateStateFile(planPath(planDigest, options.environment), options.environment)),
584
+ releaseClaim: attempt("confirmation", () => {
585
+ if (options.confirmationClaim === undefined) throw new Error("remote writes require an exact durable confirmation ownership claim");
586
+ return releaseConfirmationClaim(options.confirmationClaim, options.environment);
587
+ }),
588
+ record: (event: Parameters<typeof updateRunJournal>[1]) => attempt("journal", () => record(event)),
589
+ projectJournal: attempt("projection", () => projectRunJournal(requireJournal().journal, options.environment)),
590
+ refreshReceipt: attempt("projection", () => { durableReceipt = runJournalReceipt(requireJournal().journal); }),
591
+ persistProvisional: attempt("projection", () => persistReceipt(durableReceipt, options.environment)),
592
+ clock: () => attempt("journal", () => observedTime().toISOString()),
593
+ // Path and entry construction happen before the acquire-specific recovery branch.
594
+ ledgerRequest: attempt("journal", () => ({
595
+ path: ledgerPath(adapter.hash, auth.hash, invocation.operationId, inputHash, options.environment, options.duplicateRisk?.intentHash),
596
+ entry: {
597
+ schemaVersion: options.duplicateRisk === undefined ? 2 : 3,
598
+ keyHash: options.duplicateRisk?.intentHash ?? inputHash,
599
+ adapterHash: adapter.hash, authHash: auth.hash, inputHash, planDigest,
600
+ status: "pending", dispatch: durableReceipt.dispatch, runId, updatedAt: startedAt,
601
+ expiresAt: new Date(Date.parse(startedAt) + operation.dedupeWindowMs).toISOString(),
602
+ ...(options.duplicateRisk === undefined ? {} : { duplicateIntentHash: options.duplicateRisk.intentHash }),
603
+ } satisfies LedgerEntry,
604
+ })),
605
+ acquireLedger: (request: { readonly path: string; readonly entry: LedgerEntry }) => attempt("journal", () => acquireLedger(request.path, request.entry, options.environment, observedTime())),
606
+ ledgerRelativePath: (path: string) => attempt("journal", () => relativeStatePath(path, options.environment)),
607
+ readReceipt: (id: string) => attempt("projection", () => readRunReceipt(id, options.environment)),
608
+ storeCapsule: attempt("journal", () => writeRecoveryCapsule({
609
+ schemaVersion: 1, runId, createdAt: startedAt, planDigest, adapter,
610
+ operation: invocation.operationId, risk, input: invocation.input,
611
+ inputHash, auth: durableWriteAuth(), contract: recoveryContract(),
612
+ }, options.environment)),
613
+ outputLimit: attempt("dispatch", () => executionOutputLimit(operation)),
614
+ dispatch: native("dispatch", async () => {
615
+ if (options.preflightFailure !== undefined) {
616
+ throw options.preflightFailure;
617
+ }
618
+ return providerOperation
619
+ ? await (options.executeProvider ?? executeProviderOperation)(
620
+ invocation.manifest,
621
+ operation.provider,
622
+ invocation.input,
623
+ persistedAuthAuthority(invocation.auth),
624
+ {
625
+ registry,
626
+ ...(options.fileResolver === undefined ? {} : { fileResolver: options.fileResolver }),
627
+ ...(options.now === undefined ? {} : { now: options.now }),
628
+ ...(options.signal === undefined ? {} : { signal: options.signal }),
629
+ environment: options.environment,
630
+ beforeDispatch: (event) => persistDispatchProgress(event, "starting"),
631
+ afterDispatchVerified: (event) => persistDispatchProgress(event, "verified"),
632
+ },
633
+ )
634
+ : webSessionOperation
635
+ ? await runWebSessionOperationWithDeadline(
636
+ operation.webSession,
637
+ {
638
+ ...(options.fileResolver === undefined ? {} : { fileResolver: options.fileResolver }),
639
+ environment: options.environment,
640
+ ...(options.signal === undefined ? {} : { signal: options.signal }),
641
+ ...(options.registerCleanupBarrier === undefined
642
+ ? {}
643
+ : {
644
+ registerCleanupBarrier: options.registerCleanupBarrier,
645
+ }),
646
+ beforeDispatch: (event) => persistDispatchProgress(event, "starting"),
647
+ ...(isWrite
648
+ && exactTargetReconciliationKind
649
+ === "provider-accepted-target-presence"
650
+ ? {
651
+ afterProviderAcceptedMutationTarget:
652
+ persistProviderBoundMutationTarget,
653
+ }
654
+ : {}),
655
+ ...(isWrite
656
+ && exactTargetReconciliationKind
657
+ === "provider-bound-target-desired-state"
658
+ ? {
659
+ afterProviderBoundMutationTarget:
660
+ persistProviderBoundMutationTarget,
661
+ }
662
+ : {}),
663
+ afterDispatchVerified: (event) => persistDispatchProgress(event, "verified"),
664
+ },
665
+ async (executionOptions: WebSessionExecutionOptions) => {
666
+ if (
667
+ pluginResolution === null
668
+ || (
669
+ pluginResolution.binding.transport !== "web-session-api"
670
+ && pluginResolution.binding.transport !== "linked-device"
671
+ )
672
+ ) {
673
+ throw new Error(
674
+ "authenticated session operation resolved to the wrong plugin transport",
675
+ );
676
+ }
677
+ if (publicWebSessionOperation) {
678
+ if (pluginResolution.binding.transport !== "web-session-api") {
679
+ throw new Error(
680
+ "public access is available only to a web-session plugin binding",
681
+ );
682
+ }
683
+ const executePublic = options.executePublicWebSession
684
+ ?? pluginResolution.binding.executePublic;
685
+ if (executePublic === undefined) {
686
+ throw new Error(
687
+ "reviewed public web-session operation has no public runtime hook",
688
+ );
689
+ }
690
+ return executePublic(
691
+ invocation.manifest,
692
+ operation.webSession,
693
+ invocation.input,
694
+ executionOptions,
695
+ );
696
+ }
697
+ return (options.executeWebSession
698
+ ?? pluginResolution.binding.execute)(
699
+ invocation.manifest,
700
+ operation.webSession,
701
+ invocation.input,
702
+ persistedAuthAuthority(invocation.auth),
703
+ executionOptions,
704
+ );
705
+ },
706
+ )
707
+ : localCliOperation
708
+ ? await runLocalCliOperationWithDeadline(
709
+ operation.localCli,
710
+ {
711
+ ...(options.fileResolver === undefined
712
+ ? {}
713
+ : { fileResolver: options.fileResolver }),
714
+ environment: options.environment,
715
+ ...(options.signal === undefined
716
+ ? {}
717
+ : { signal: options.signal }),
718
+ ...(options.registerCleanupBarrier === undefined
719
+ ? {}
720
+ : { registerCleanupBarrier: options.registerCleanupBarrier }),
721
+ beforeDispatch: (event) =>
722
+ persistDispatchProgress(event, "starting"),
723
+ ...(isWrite
724
+ && exactTargetReconciliationKind
725
+ === "provider-accepted-target-presence"
726
+ ? {
727
+ afterProviderAcceptedMutationTarget:
728
+ persistProviderBoundMutationTarget,
729
+ }
730
+ : {}),
731
+ ...(isWrite
732
+ && exactTargetReconciliationKind
733
+ === "provider-bound-target-desired-state"
734
+ ? {
735
+ afterProviderBoundMutationTarget:
736
+ persistProviderBoundMutationTarget,
737
+ }
738
+ : {}),
739
+ afterDispatchVerified: (event) =>
740
+ persistDispatchProgress(event, "verified"),
741
+ },
742
+ async (executionOptions: LocalCliExecutionOptions) => {
743
+ if (
744
+ pluginResolution === null
745
+ || pluginResolution.binding.transport !== "local-cli"
746
+ ) {
747
+ throw new Error(
748
+ "local CLI operation resolved to the wrong plugin transport",
749
+ );
750
+ }
751
+ return (options.executeLocalCli
752
+ ?? pluginResolution.binding.execute)(
753
+ invocation.manifest,
754
+ operation.localCli,
755
+ invocation.input,
756
+ persistedAuthAuthority(invocation.auth),
757
+ executionOptions,
758
+ );
759
+ },
760
+ )
761
+ : reviewedTemplateOperation
762
+ ? await (options.executeReviewedTemplate ?? executeReviewedTemplateOperation)(
763
+ invocation.manifest,
764
+ invocation.operationId,
765
+ operation.reviewedTemplate,
766
+ invocation.input,
767
+ persistedAuthAuthority(invocation.auth),
768
+ {
769
+ beforeDispatch: (event) => persistDispatchProgress(event, "starting"),
770
+ afterDispatchVerified: (event) => persistDispatchProgress(event, "verified"),
771
+ },
772
+ )
773
+ : await (options.executeRecipe ?? executeBrowserRecipe)(
774
+ invocation.manifest,
775
+ operation.browser,
776
+ invocation.input,
777
+ persistedAuthAuthority(invocation.auth),
778
+ {
779
+ headed: options.headed,
780
+ ...(options.fileResolver === undefined ? {} : { fileResolver: options.fileResolver }),
781
+ beforeDispatch: (event) => persistDispatchProgress(event, "starting"),
782
+ afterDispatchVerified: (event) => persistDispatchProgress(event, "verified"),
783
+ },
784
+ );
785
+
786
+ }),
787
+ projectExecution: (outcome: NativeOutcome, maxOutputBytes: number) => attempt("projection", () => {
788
+ let execution: BoundedExecution;
789
+ if (outcome.status === "fulfilled") {
790
+ const rawExecution = outcome.value;
791
+ try {
792
+ execution = boundedExecutionResult(rawExecution, executionKind, maxOutputBytes);
793
+ } catch {
794
+ const { started } = durableReceipt.dispatch;
795
+ execution = {
796
+ status: started > 0 ? "indeterminate" : "failed",
797
+ output: null,
798
+ finalUrl: null,
799
+ dispatchStarted: started > 0,
800
+ dispatch: durableReceipt.dispatch,
801
+ error: GENERIC_EXECUTOR_TERMINATION,
802
+ };
803
+ }
804
+ } else {
805
+ const error = outcome.reason;
806
+ const { started } = durableReceipt.dispatch;
807
+ const preservedArtifactsError =
808
+ error instanceof PreservedBrowserArtifactsError
809
+ ? error
810
+ : error instanceof WebSessionCleanupUnverifiedError
811
+ && error.cause instanceof PreservedBrowserArtifactsError
812
+ ? error.cause
813
+ : null;
814
+ const cleanupRequired = error instanceof WebSessionCleanupUnverifiedError
815
+ || error instanceof WebSessionCleanupAdmissionBlockedError;
816
+ execution = {
817
+ status: started > 0 ? "indeterminate" : "failed",
818
+ output: null,
819
+ finalUrl: null,
820
+ dispatchStarted: started > 0,
821
+ dispatch: durableReceipt.dispatch,
822
+ error: preservedArtifactsError !== null
823
+ ? "provider browser cleanup could not be verified; private artifacts were preserved and durable cleanup admission requires ghostget doctor before retry"
824
+ : cleanupRequired
825
+ ? localCliOperation
826
+ ? "local CLI child/private-root cleanup could not be verified; durable cleanup admission blocks retry until ghostget doctor proves every pinned process group quiescent and removes the exact private root"
827
+ : "authenticated web cleanup could not be verified; durable cleanup admission blocks retry until ghostget doctor proves and completes exact browser-session recovery"
828
+ : boundedThrownExecutorReason(error),
829
+ ...(preservedArtifactsError === null
830
+ ? {}
831
+ : {
832
+ privateArtifactsPreserved: true,
833
+ recoveryHandle: preservedArtifactsError.recoveryHandle,
834
+ }),
835
+ };
836
+ }
837
+ const executionNoOp = "noOp" in execution && execution.noOp === true;
838
+ const validExecutionProgress = isDispatchProgress(execution.dispatch)
839
+ && execution.dispatch.planned === planned
840
+ && execution.readFailure === undefined
841
+ && execution.dispatch.started === durableReceipt.dispatch.started
842
+ && execution.dispatch.verified === durableReceipt.dispatch.verified
843
+ && execution.dispatchStarted === (execution.dispatch.started > 0)
844
+ && (!executionNoOp || (
845
+ isWrite
846
+ && planned > 0
847
+ && execution.status === "succeeded"
848
+ && execution.dispatch.started === 0
849
+ && execution.dispatch.verified === 0
850
+ && execution.dispatchStarted === false
851
+ ))
852
+ && (execution.status !== "succeeded" || executionNoOp || (
853
+ execution.dispatch.started === planned && execution.dispatch.verified === planned
854
+ ))
855
+ && (execution.status !== "failed" || execution.dispatch.started === 0)
856
+ && (execution.status !== "partial" || (
857
+ execution.dispatch.verified > 0
858
+ && execution.dispatch.started === execution.dispatch.verified
859
+ && execution.dispatch.verified < planned
860
+ ))
861
+ && (execution.status !== "indeterminate" || execution.dispatch.started > 0);
862
+ if (!validExecutionProgress) {
863
+ const { started } = durableReceipt.dispatch;
864
+ execution = {
865
+ status: started > 0 ? "indeterminate" : "failed",
866
+ output: null,
867
+ finalUrl: null,
868
+ dispatchStarted: started > 0,
869
+ dispatch: durableReceipt.dispatch,
870
+ error: "provider executor returned invalid dispatch progress",
871
+ };
872
+ }
873
+ const receiptStatus: RunReceipt["status"] = execution.status === "succeeded"
874
+ ? isWrite && !executionNoOp ? "submitted" : "succeeded"
875
+ : execution.status;
876
+ const publishedOutput = execution.output;
877
+ const finishedAt = (options.now ?? new Date()).toISOString();
878
+ const privateArtifactsPreserved = "privateArtifactsPreserved" in execution
879
+ && execution.privateArtifactsPreserved === true;
880
+ const recoveryHandle = boundedRecoveryHandle("recoveryHandle" in execution ? execution.recoveryHandle : undefined);
881
+ const privateArtifactRecoveryMessage = privateArtifactsPreserved
882
+ ? `private browser artifacts were preserved; ghostget doctor must prove and complete exact browser-session recovery before retry${recoveryHandle === null
883
+ ? ""
884
+ : `; recovery handle: ${recoveryHandle}`}`
885
+ : null;
886
+ const apiOperation = providerOperation
887
+ || webSessionOperation
888
+ || localCliOperation
889
+ || reviewedTemplateOperation;
890
+ const transportLabel = providerOperation
891
+ ? "official API"
892
+ : webSessionOperation
893
+ ? "authenticated web API"
894
+ : localCliOperation
895
+ ? "local CLI"
896
+ : reviewedTemplateOperation
897
+ ? "reviewed authenticated API"
898
+ : "browser";
899
+ const providerReason = apiOperation && execution.error !== undefined
900
+ ? redactSensitiveText(execution.error).slice(0, 2_000)
901
+ : null;
902
+ const withProviderReason = (message: string): string => providerReason === null
903
+ ? message
904
+ : `${message}; reason: ${providerReason}`;
905
+ let receipt: RunReceipt = withTransport({
906
+ runId,
907
+ planDigest,
908
+ adapter,
909
+ operation: invocation.operationId,
910
+ risk: operation.risk,
911
+ inputHash,
912
+ auth,
913
+ status: receiptStatus,
914
+ dispatchStarted: execution.dispatch.started > 0,
915
+ dispatch: execution.dispatch,
916
+ startedAt,
917
+ finishedAt,
918
+ finalOrigin: finalOrigin(execution.finalUrl, invocation.manifest.origins),
919
+ error: execution.error === undefined
920
+ ? null
921
+ : privateArtifactRecoveryMessage !== null
922
+ ? privateArtifactRecoveryMessage
923
+ : redactSensitiveText(receiptStatus === "partial"
924
+ ? withProviderReason(`${transportLabel} stopped after verified dispatches before completing the confirmed schedule; reconcile before retrying`)
925
+ : receiptStatus === "indeterminate"
926
+ ? withProviderReason(`${transportLabel} result is indeterminate after the dispatch boundary`)
927
+ : apiOperation
928
+ ? withProviderReason(`${transportLabel} operation failed before the dispatch boundary`)
929
+ : "browser recipe failed before the dispatch boundary"),
930
+ });
931
+
932
+ return {
933
+ terminalEvent: {
934
+ type: "finished", status: receiptStatus, finalOrigin: receipt.finalOrigin,
935
+ error: receipt.error, ...(executionNoOp ? { noOp: true as const } : {}), at: finishedAt,
936
+ } as const,
937
+ // This projection reads the one current journal cell after terminal CAS/readback.
938
+ result: (): InvocationResult => {
939
+ if (journal === null) throw new Error("remote write has no run journal");
940
+ if (journal.journal.phase === "terminal") {
941
+ receipt = runJournalReceipt(journal.journal);
942
+ return {
943
+ receipt,
944
+ output: publishedOutput,
945
+ replayed: false,
946
+ privateArtifactsPreserved,
947
+ ...(recoveryHandle === null ? {} : { recoveryHandle }),
948
+ };
949
+ }
950
+ const pending = runJournalReceipt(journal.journal);
951
+ return {
952
+ receipt: {
953
+ ...pending,
954
+ error: `${execution.dispatch.started > 0
955
+ ? `${transportLabel} execution crossed dispatch, but its final run journal could not be stored; reconcile this run before any retry`
956
+ : `${transportLabel} execution ended before dispatch, but its final run journal could not be stored; run ghostget doctor before retrying`}${privateArtifactRecoveryMessage === null
957
+ ? ""
958
+ : `; ${privateArtifactRecoveryMessage}`}`,
959
+ },
960
+ output: null,
961
+ replayed: false,
962
+ privateArtifactsPreserved,
963
+ ...(recoveryHandle === null ? {} : { recoveryHandle }),
964
+ };
965
+ },
966
+ };
967
+ }),
968
+ reloadJournal: attempt("journal", () => { const reloaded = readRunJournal(runId, options.environment); if (reloaded !== null) journal = reloaded; }),
969
+ result: (project: () => InvocationResult) => attempt("projection", project),
970
+ };
971
+ }
972
+
973
+ return {
974
+ initialPlan: (digest: string) => attempt("confirmation", () => loadInvocationPlan(digest, environment)),
975
+ configure: attempt("confirmation", () => ({ registry: original.registry ?? providerPluginRegistry, observedAt: now() })),
976
+ repairClaims: attempt("confirmation", () => repairInterruptedConfirmationClaims(environment)),
977
+ repairJournals: (at: Date) => attempt("journal", () => repairInterruptedRunJournals(environment, at)),
978
+ newRunId: attempt("confirmation", () => crypto.randomUUID()),
979
+ claim: (digest: string, runId: string, at: Date) => attempt("confirmation", () => acquireConfirmationClaim(digest, runId, environment, at)),
980
+ releaseClaim: (claim: ConfirmationClaimSnapshot) => attempt("confirmation", () => releaseConfirmationClaim(claim, environment)),
981
+ removePlan: (digest: string) => attempt("confirmation", () => removePrivateStateFile(planPath(digest, environment), environment)),
982
+ retainingJournal: (digest: string) => attempt("confirmation", () => planDigestHasRetainingJournal(digest, environment)),
983
+ cleanupAssets: (digest: string) => attempt("cleanup", () => cleanupPlanAssets(digest, environment)),
984
+ freshPlan: (stored: StoredPlan, registry: ProviderPluginRegistry, at: Date) => attempt("confirmation", () => {
985
+ const loadManifest: typeof loadInstalledManifest = original.loadManifest
986
+ ?? ((adapterId, selectedEnvironment = process.env) => loadInstalledManifestWithRegistry(adapterId, selectedEnvironment, registry));
987
+ return validateFreshPlan(stored, environment, at, registry, loadManifest);
988
+ }),
989
+ duplicateRisk: (stored: StoredPlan) => attempt("confirmation", () => stored.plan.duplicateRisk === undefined ? undefined : resolveInvocationDuplicateRisk(stored.plan, [stored.plan.duplicateRisk.sourceRunId], environment)),
990
+ prepareOptions: (invocation: PreparedInvocation, stored: StoredPlan, claim: ConfirmationClaimSnapshot, runId: string, registry: ProviderPluginRegistry) => attempt("confirmation", () => {
991
+ const fileInputs: FileInputValue[] = [];
992
+ for (const value of Object.values(invocation.input)) {
993
+ if (isInputArray(value)) { for (const item of value) if (isFileInputValue(item)) fileInputs.push(item); }
994
+ else if (isFileInputValue(value)) fileInputs.push(value);
995
+ }
996
+ if (fileInputs.length > 0) resolvePlanAssetFiles(fileInputs, stored.digest, environment);
997
+ const operation = invocation.manifest.operations[invocation.operationId];
998
+ if (operation?.risk !== "R2" && operation?.risk !== "R3") throw new Error("only R2 and R3 plans use confirmation");
999
+ return {
1000
+ headed: original.headed, environment, registry,
1001
+ ...(fileInputs.length === 0 ? {} : { fileResolver: (files) => Promise.resolve(resolvePlanAssetFiles(files, stored.digest, environment)) }),
1002
+ hasPlanAssets: fileInputs.length > 0, runId, confirmationClaim: claim,
1003
+ confirmedDispatches: stored.plan.dispatches,
1004
+ ...(stored.plan.duplicateRisk === undefined ? {} : { duplicateRisk: stored.plan.duplicateRisk }),
1005
+ ...(original.now === undefined ? {} : { now: original.now }),
1006
+ ...(original.executeRecipe === undefined ? {} : { executeRecipe: original.executeRecipe }),
1007
+ ...(original.executeProvider === undefined ? {} : { executeProvider: original.executeProvider }),
1008
+ ...(original.executeWebSession === undefined ? {} : { executeWebSession: original.executeWebSession }),
1009
+ ...(original.executeLocalCli === undefined ? {} : { executeLocalCli: original.executeLocalCli }),
1010
+ ...(original.executeReviewedTemplate === undefined ? {} : { executeReviewedTemplate: original.executeReviewedTemplate }),
1011
+ ...(original.signal === undefined ? {} : { signal: original.signal }),
1012
+ ...(original.persistReceipt === undefined ? {} : { persistReceipt: original.persistReceipt }),
1013
+ } satisfies RunPreparedOptions;
1014
+ }),
1015
+ admission: (invocation: PreparedInvocation, options: RunPreparedOptions) => attempt("admission", () => {
1016
+ const registry = options.registry ?? providerPluginRegistry;
1017
+ const checked = revalidatePreparedInvocation(invocation, registry);
1018
+ const portableIdentity = checked.invocation.portablePluginContract ?? null;
1019
+ const runId = options.runId ?? crypto.randomUUID();
1020
+ const pluginResolution = resolveCodeOwnedPluginOperation(checked.operation, registry);
1021
+ let cleanupIdentity: WebSessionCleanupAdmissionIdentity | null = null;
1022
+ if (portableIdentity === null && (isWebSessionOperation(checked.operation) || isLocalCliOperation(checked.operation))
1023
+ && pluginResolution !== null && !isPublicWebSessionInvocationAuthority(checked.invocation.auth)) {
1024
+ const executionIdentityHash = pluginResolution.binding.transport === "local-cli"
1025
+ ? sha256(canonicalJson({ transport: "local-cli", tool: pluginResolution.binding.tool, artifact: localCliToolArtifactForCurrentRuntime(pluginResolution.binding.tool) }))
1026
+ : registry.implementationHash(pluginResolution.binding).toString("hex");
1027
+ cleanupIdentity = {
1028
+ runId, pluginId: pluginResolution.plugin.id, pluginVersion: pluginResolution.plugin.version,
1029
+ pluginImplementationHash: registry.implementationHash(pluginResolution.binding).toString("hex"),
1030
+ adapterId: checked.invocation.manifest.id, adapterHash: manifestHash(checked.invocation.manifest),
1031
+ surfaceId: pluginResolution.binding.surfaceId, authId: checked.invocation.auth.id, authHash: authHash(checked.invocation.auth),
1032
+ transport: isLocalCliOperation(checked.operation) ? "local-cli" : "web-session-api", executionIdentityHash,
1033
+ };
1034
+ }
1035
+ return { invocation: checked.invocation, options: { ...options, runId }, portableIdentity, cleanupIdentity };
1036
+ }),
1037
+ acquireWeb: (identity: WebSessionCleanupAdmissionIdentity, at: Date | undefined) => attempt("admission", () => acquireWebSessionCleanupAdmission(identity, environment, at)),
1038
+ nativeCleanup: (work: () => Promise<void>) => native("cleanup", work),
1039
+ cleanup: (work: () => void) => attempt("cleanup", work),
1040
+ acquirePortable: (identity: PortableOperationIdentityV1, runId: string, at: Date | undefined) => attempt("admission", () => {
1041
+ const lease = acquirePortableProviderPluginInvocationLease(identity, runId, environment, at);
1042
+ const containment = createPortableProviderPluginInvocationLeaseContainmentController(lease, environment);
1043
+ const context = createPortableProviderPluginCleanupContext({ containment, cleanupComplete: containment.cleanupComplete });
1044
+ portableContext = context;
1045
+ return { context, release: () => releasePortableProviderPluginInvocationLease(containment.current, environment, new Date()) };
1046
+ }),
1047
+ checkExecution: (invocation: PreparedInvocation, options: RunPreparedOptions) => attempt("journal", () => revalidatePreparedInvocation(invocation, options.registry ?? providerPluginRegistry)),
1048
+ currentTime: (options: RunPreparedOptions) => attempt("journal", () => options.now ?? new Date()),
1049
+ execution: (checked: ReturnType<ConfirmedWriteKernel["revalidatePreparedInvocation"]>, digest: string, options: RunPreparedOptions) => attempt("journal", () => execution(checked, digest, options)),
1050
+ };
1051
+ }
1052
+
1053
+ export class ConfirmedWritePlatform extends Context.Tag("wrench/ConfirmedWritePlatform/v1")<
1054
+ ConfirmedWritePlatform, ReturnType<typeof makeConfirmedWritePlatform>
1055
+ >() { }