@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.
- package/CHANGELOG.md +458 -0
- package/LICENSE +21 -0
- package/README.md +1359 -0
- package/bunfig.toml +2 -0
- package/dist/apple-photos-client.js +704 -0
- package/dist/beeper-client.js +4602 -0
- package/dist/client.js +1605 -0
- package/dist/index-26yq8q16.js +31 -0
- package/dist/index-9t13b4k7.js +508 -0
- package/dist/index-axwm9b3v.js +5 -0
- package/dist/index-gwk7rbyj.js +30 -0
- package/dist/index.js +701 -0
- package/dist/messaging.js +1130 -0
- package/dist/omni-client.js +1406 -0
- package/dist/whatsapp-client.js +355 -0
- package/docs/imessage-direct-provider.md +106 -0
- package/docs/rental-listings.md +94 -0
- package/package.json +600 -0
- package/skills/ghostget/SKILL.md +251 -0
- package/skills/ghostget/agents/openai.yaml +4 -0
- package/skills/ghostget/references/adapter-contract.md +181 -0
- package/skills/ghostget/references/apple-photos.md +115 -0
- package/skills/ghostget/references/article-drafts.md +235 -0
- package/skills/ghostget/references/code-owned-provider-scaffold.md +134 -0
- package/skills/ghostget/references/cross-posting-video.md +139 -0
- package/skills/ghostget/references/cross-posting.md +82 -0
- package/skills/ghostget/references/derivation.md +390 -0
- package/skills/ghostget/references/hraness-social-profile-stats.json +269 -0
- package/skills/ghostget/references/install.md +66 -0
- package/skills/ghostget/references/linkedin-adapter.md +442 -0
- package/skills/ghostget/references/messaging.md +168 -0
- package/skills/ghostget/references/meta-comet-contract.md +153 -0
- package/skills/ghostget/references/platform-patterns.md +122 -0
- package/skills/ghostget/references/provider-plugins.md +496 -0
- package/skills/ghostget/references/reddit-flair.md +78 -0
- package/skills/ghostget/references/safety-and-state.md +214 -0
- package/skills/ghostget/references/settlement-and-duplicate-cleanup.md +79 -0
- package/skills/ghostget/references/social-platform-routing.md +66 -0
- package/skills/ghostget/references/social-profile-stats.md +195 -0
- package/skills/ghostget/references/social-video-platform-routing.md +89 -0
- package/skills/ghostget/references/whatsapp-adapter.md +101 -0
- package/skills/ghostget/references/x-adapter.md +332 -0
- package/skills/ghostget/references/x-ai-disclosure.md +71 -0
- package/src/apple-photos-cli.ts +639 -0
- package/src/apple-photos-client-types.ts +168 -0
- package/src/apple-photos-client.ts +201 -0
- package/src/apple-photos-contact-evidence.ts +838 -0
- package/src/apple-photos-local-source.ts +1131 -0
- package/src/args.ts +2053 -0
- package/src/article-draft-document.ts +417 -0
- package/src/article-draft-embeds.ts +123 -0
- package/src/article-draft-images.ts +214 -0
- package/src/assets/adapter-template/wrench-adapter.json +40 -0
- package/src/assets/adapters/beeper/wrench-web-adapter.json +1500 -0
- package/src/assets/adapters/beeper/wrench-web-adapter.v1.0.0.json +129 -0
- package/src/assets/adapters/beeper/wrench-web-adapter.v1.1.0.json +207 -0
- package/src/assets/adapters/beeper/wrench-web-adapter.v2.0.0.json +1463 -0
- package/src/assets/adapters/beeper/wrench-web-adapter.v2.1.0.json +1463 -0
- package/src/assets/adapters/beeper/wrench-web-adapter.v2.2.0.json +1475 -0
- package/src/assets/adapters/beeper/wrench-web-adapter.v2.3.0.json +1488 -0
- package/src/assets/adapters/bluesky/wrench-web-adapter.json +684 -0
- package/src/assets/adapters/bluesky/wrench-web-adapter.v1.0.0.json +581 -0
- package/src/assets/adapters/bluesky/wrench-web-adapter.v1.1.0.json +581 -0
- package/src/assets/adapters/bluesky/wrench-web-adapter.v1.2.0.json +581 -0
- package/src/assets/adapters/bluesky/wrench-web-adapter.v1.3.0.json +615 -0
- package/src/assets/adapters/bluesky/wrench-web-adapter.v1.4.0.json +684 -0
- package/src/assets/adapters/bluesky/wrench-web-adapter.v1.5.0.json +684 -0
- package/src/assets/adapters/bluesky/wrench-web-adapter.v1.6.0.json +684 -0
- package/src/assets/adapters/clasificados/wrench-web-adapter.json +54 -0
- package/src/assets/adapters/facebook/wrench-web-adapter.json +750 -0
- package/src/assets/adapters/facebook/wrench-web-adapter.v1.0.0.json +706 -0
- package/src/assets/adapters/facebook/wrench-web-adapter.v1.1.0.json +703 -0
- package/src/assets/adapters/facebook/wrench-web-adapter.v1.2.0.json +750 -0
- package/src/assets/adapters/facebook-group/wrench-web-adapter.json +682 -0
- package/src/assets/adapters/facebook-group/wrench-web-adapter.v1.0.0.json +583 -0
- package/src/assets/adapters/facebook-group/wrench-web-adapter.v1.1.0.json +682 -0
- package/src/assets/adapters/facebook-marketplace/wrench-web-adapter.json +437 -0
- package/src/assets/adapters/facebook-marketplace/wrench-web-adapter.v1.0.0.json +383 -0
- package/src/assets/adapters/facebook-marketplace/wrench-web-adapter.v1.1.0.json +437 -0
- package/src/assets/adapters/facebook-marketplace/wrench-web-adapter.v1.1.1.json +437 -0
- package/src/assets/adapters/facebook-page/wrench-web-adapter.json +880 -0
- package/src/assets/adapters/facebook-page/wrench-web-adapter.v1.0.0.json +740 -0
- package/src/assets/adapters/facebook-page/wrench-web-adapter.v1.1.0.json +880 -0
- package/src/assets/adapters/github/wrench-web-adapter.json +69 -0
- package/src/assets/adapters/github/wrench-web-adapter.v1.0.0.json +42 -0
- package/src/assets/adapters/github/wrench-web-adapter.v1.1.0.json +69 -0
- package/src/assets/adapters/gmail/wrench-adapter.json +161 -0
- package/src/assets/adapters/gmail/wrench-adapter.v1.2.0.json +157 -0
- package/src/assets/adapters/hacker-news/wrench-web-adapter.json +315 -0
- package/src/assets/adapters/hacker-news/wrench-web-adapter.v1.0.0.json +315 -0
- package/src/assets/adapters/imessage/wrench-web-adapter.json +236 -0
- package/src/assets/adapters/instagram/wrench-web-adapter.json +719 -0
- package/src/assets/adapters/instagram/wrench-web-adapter.v1.0.0.json +627 -0
- package/src/assets/adapters/instagram/wrench-web-adapter.v1.1.0.json +609 -0
- package/src/assets/adapters/instagram/wrench-web-adapter.v1.2.0.json +640 -0
- package/src/assets/adapters/instagram/wrench-web-adapter.v1.3.0.json +671 -0
- package/src/assets/adapters/instagram/wrench-web-adapter.v1.4.0.json +710 -0
- package/src/assets/adapters/instagram/wrench-web-adapter.v1.5.0.json +710 -0
- package/src/assets/adapters/instagram/wrench-web-adapter.v1.6.0.json +719 -0
- package/src/assets/adapters/instagram/wrench-web-adapter.v1.7.0.json +719 -0
- package/src/assets/adapters/linkedin/wrench-adapter.json +442 -0
- package/src/assets/adapters/linkedin/wrench-adapter.v0.4.0.json +124 -0
- package/src/assets/adapters/linkedin/wrench-adapter.v1.0.0.json +411 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.json +914 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.0.0.json +604 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.1.0.json +604 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.10.0.json +809 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.11.0.json +809 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.12.0.json +820 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.13.0.json +819 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.14.0.json +819 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.15.0.json +819 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.16.0.json +874 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.17.0.json +874 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.18.0.json +874 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.19.0.json +874 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.2.0.json +731 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.3.0.json +768 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.4.0.json +808 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.5.0.json +808 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.6.0.json +808 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.7.0.json +825 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.8.0.json +792 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.9.0.json +809 -0
- package/src/assets/adapters/reddit/wrench-web-adapter.json +855 -0
- package/src/assets/adapters/reddit/wrench-web-adapter.v1.0.0.json +577 -0
- package/src/assets/adapters/reddit/wrench-web-adapter.v1.1.0.json +604 -0
- package/src/assets/adapters/reddit/wrench-web-adapter.v1.10.0.json +712 -0
- package/src/assets/adapters/reddit/wrench-web-adapter.v1.11.0.json +712 -0
- package/src/assets/adapters/reddit/wrench-web-adapter.v1.12.0.json +712 -0
- package/src/assets/adapters/reddit/wrench-web-adapter.v1.2.0.json +653 -0
- package/src/assets/adapters/reddit/wrench-web-adapter.v1.3.0.json +712 -0
- package/src/assets/adapters/reddit/wrench-web-adapter.v1.4.0.json +713 -0
- package/src/assets/adapters/reddit/wrench-web-adapter.v1.5.0.json +712 -0
- package/src/assets/adapters/reddit/wrench-web-adapter.v1.6.0.json +713 -0
- package/src/assets/adapters/reddit/wrench-web-adapter.v1.7.0.json +713 -0
- package/src/assets/adapters/reddit/wrench-web-adapter.v1.8.0.json +713 -0
- package/src/assets/adapters/reddit/wrench-web-adapter.v1.9.0.json +713 -0
- package/src/assets/adapters/substack/wrench-web-adapter.json +936 -0
- package/src/assets/adapters/substack/wrench-web-adapter.v1.0.0.json +817 -0
- package/src/assets/adapters/substack/wrench-web-adapter.v1.1.0.json +812 -0
- package/src/assets/adapters/substack/wrench-web-adapter.v1.2.0.json +812 -0
- package/src/assets/adapters/substack/wrench-web-adapter.v1.3.0.json +902 -0
- package/src/assets/adapters/substack/wrench-web-adapter.v1.4.0.json +936 -0
- package/src/assets/adapters/substack/wrench-web-adapter.v1.5.0.json +936 -0
- package/src/assets/adapters/substack/wrench-web-adapter.v1.6.0.json +936 -0
- package/src/assets/adapters/threads/wrench-web-adapter.json +692 -0
- package/src/assets/adapters/threads/wrench-web-adapter.v1.0.0.json +629 -0
- package/src/assets/adapters/threads/wrench-web-adapter.v1.1.0.json +623 -0
- package/src/assets/adapters/threads/wrench-web-adapter.v1.2.0.json +622 -0
- package/src/assets/adapters/threads/wrench-web-adapter.v1.3.0.json +622 -0
- package/src/assets/adapters/threads/wrench-web-adapter.v1.4.0.json +622 -0
- package/src/assets/adapters/threads/wrench-web-adapter.v1.5.0.json +699 -0
- package/src/assets/adapters/threads/wrench-web-adapter.v1.6.0.json +693 -0
- package/src/assets/adapters/threads/wrench-web-adapter.v1.7.0.json +692 -0
- package/src/assets/adapters/tiktok/wrench-web-adapter.json +762 -0
- package/src/assets/adapters/tiktok/wrench-web-adapter.v1.0.0.json +681 -0
- package/src/assets/adapters/tiktok/wrench-web-adapter.v1.1.0.json +708 -0
- package/src/assets/adapters/tiktok/wrench-web-adapter.v1.2.0.json +762 -0
- package/src/assets/adapters/tiktok/wrench-web-adapter.v1.3.0.json +762 -0
- package/src/assets/adapters/twitch/wrench-web-adapter.json +42 -0
- package/src/assets/adapters/twitch/wrench-web-adapter.v1.0.0.json +42 -0
- package/src/assets/adapters/whatsapp/wrench-web-adapter.json +400 -0
- package/src/assets/adapters/whatsapp/wrench-web-adapter.v1.0.0.json +355 -0
- package/src/assets/adapters/whatsapp/wrench-web-adapter.v1.1.0.json +386 -0
- package/src/assets/adapters/whatsapp/wrench-web-adapter.v1.2.0.json +386 -0
- package/src/assets/adapters/whatsapp/wrench-web-adapter.v1.3.0.json +400 -0
- package/src/assets/adapters/x/wrench-adapter.json +749 -0
- package/src/assets/adapters/x/wrench-adapter.v1.0.0.json +700 -0
- package/src/assets/adapters/x/wrench-adapter.v1.1.0.json +707 -0
- package/src/assets/adapters/x/wrench-web-adapter.json +712 -0
- package/src/assets/adapters/x/wrench-web-adapter.v1.1.0.json +579 -0
- package/src/assets/adapters/x/wrench-web-adapter.v1.10.0.json +685 -0
- package/src/assets/adapters/x/wrench-web-adapter.v1.11.0.json +712 -0
- package/src/assets/adapters/x/wrench-web-adapter.v1.12.0.json +712 -0
- package/src/assets/adapters/x/wrench-web-adapter.v1.13.0.json +712 -0
- package/src/assets/adapters/x/wrench-web-adapter.v1.2.0.json +578 -0
- package/src/assets/adapters/x/wrench-web-adapter.v1.3.0.json +610 -0
- package/src/assets/adapters/x/wrench-web-adapter.v1.4.0.json +617 -0
- package/src/assets/adapters/x/wrench-web-adapter.v1.5.0.json +634 -0
- package/src/assets/adapters/x/wrench-web-adapter.v1.6.0.json +632 -0
- package/src/assets/adapters/x/wrench-web-adapter.v1.7.0.json +649 -0
- package/src/assets/adapters/x/wrench-web-adapter.v1.8.0.json +649 -0
- package/src/assets/adapters/x/wrench-web-adapter.v1.9.0.json +683 -0
- package/src/assets/adapters/youtube/wrench-web-adapter.json +561 -0
- package/src/assets/adapters/youtube/wrench-web-adapter.v1.0.0.json +465 -0
- package/src/assets/adapters/youtube/wrench-web-adapter.v1.1.0.json +519 -0
- package/src/assets/adapters/youtube/wrench-web-adapter.v1.2.0.json +561 -0
- package/src/assets/adapters/youtube/wrench-web-adapter.v1.3.0.json +561 -0
- package/src/assets/code-owned-provider-template/plugin.test.ts.template +54 -0
- package/src/assets/code-owned-provider-template/plugin.ts.template +102 -0
- package/src/assets/code-owned-provider-template/runtime.internal.test.ts.template +44 -0
- package/src/assets/code-owned-provider-template/runtime.ts.template +59 -0
- package/src/assets/code-owned-provider-template/wrench-adapter.json.template +33 -0
- package/src/assets/fonts/nebula-sans/LICENSE.txt +96 -0
- package/src/assets/fonts/nebula-sans/NebulaSans-Book.woff2 +0 -0
- package/src/assets/fonts/nebula-sans/PROVENANCE.md +14 -0
- package/src/assets.d.ts +4 -0
- package/src/auth.ts +1157 -0
- package/src/beeper-client-types.ts +169 -0
- package/src/beeper-client.ts +250 -0
- package/src/beeper-contact-interactions-cli.ts +180 -0
- package/src/beeper-contact-interactions.ts +1612 -0
- package/src/beeper-message-bundle-v1.ts +257 -0
- package/src/beeper-message-like-me-cli.ts +84 -0
- package/src/beeper-message-like-me-export.ts +2274 -0
- package/src/beeper-message-like-me-recovery.ts +931 -0
- package/src/beeper-message-like-me-source.ts +4034 -0
- package/src/browser-admission.ts +928 -0
- package/src/browser-snapshots.ts +260 -0
- package/src/browser.ts +3873 -0
- package/src/canonical-json.ts +60 -0
- package/src/catalog-cli.ts +638 -0
- package/src/cli.ts +208 -0
- package/src/client-types.ts +301 -0
- package/src/client.ts +2726 -0
- package/src/confirmed-write-failure.ts +44 -0
- package/src/confirmed-write-model.ts +72 -0
- package/src/confirmed-write-platform.ts +1055 -0
- package/src/confirmed-write-program.ts +224 -0
- package/src/confirmed-write-runtime.ts +16 -0
- package/src/cursor-token.ts +228 -0
- package/src/derivation-dnr-readiness.ts +367 -0
- package/src/derivation-file-chooser.ts +384 -0
- package/src/derivation-network-boundary.ts +835 -0
- package/src/derivation-network-guard.ts +782 -0
- package/src/derivation-network-proxy-helper.ts +440 -0
- package/src/derivation-network-proxy.ts +385 -0
- package/src/derive-command-helper.ts +693 -0
- package/src/derive-fixtures.ts +343 -0
- package/src/derive-review.ts +980 -0
- package/src/derive.ts +4754 -0
- package/src/fixtures/beeper-message-like-me-v1/accounts.ndjson +2 -0
- package/src/fixtures/beeper-message-like-me-v1/conversations.ndjson +1 -0
- package/src/fixtures/beeper-message-like-me-v1/manifest.json +1 -0
- package/src/fixtures/beeper-message-like-me-v1/messages.ndjson +2 -0
- package/src/fixtures/beeper-message-like-me-v1/participants.ndjson +3 -0
- package/src/fixtures/beeper-message-like-me-v1/reactions.ndjson +1 -0
- package/src/fixtures/beeper-message-like-me-v1/tombstones.ndjson +1 -0
- package/src/ghostget.ts +3672 -0
- package/src/gmail-capture.ts +1244 -0
- package/src/har-internal.ts +1242 -0
- package/src/har.ts +743 -0
- package/src/index.ts +218 -0
- package/src/invocation-read-platform.ts +55 -0
- package/src/invocation-read-program.ts +93 -0
- package/src/invocation-read-runtime.ts +11 -0
- package/src/linked-device-lifecycle-admission.ts +450 -0
- package/src/linked-device-lifecycle-journal.ts +1560 -0
- package/src/linked-device-lifecycle-runtime.ts +1602 -0
- package/src/local-cli-admission.ts +135 -0
- package/src/local-cli-contracts.ts +271 -0
- package/src/local-cli-execution.ts +98 -0
- package/src/local-cli-surface-contract.ts +1510 -0
- package/src/local-cli-tool-identity.ts +336 -0
- package/src/media/archive.ts +2325 -0
- package/src/media/args.ts +303 -0
- package/src/media/cli.ts +287 -0
- package/src/media/doctor.ts +475 -0
- package/src/media/ffmpeg.ts +1330 -0
- package/src/media/http-capture.ts +711 -0
- package/src/media/http-probe.ts +593 -0
- package/src/media/http.ts +387 -0
- package/src/media/index.ts +10 -0
- package/src/media/local-transcription.ts +613 -0
- package/src/media/lock.ts +321 -0
- package/src/media/manifest.ts +1712 -0
- package/src/media/metadata.ts +906 -0
- package/src/media/process.ts +723 -0
- package/src/media/revision.ts +166 -0
- package/src/media/runtime-closure.ts +1063 -0
- package/src/media/source-router.ts +187 -0
- package/src/media/transcriber-config.ts +1122 -0
- package/src/media/transcript-persistence-model.ts +15 -0
- package/src/media/transcript-persistence-platform.ts +89 -0
- package/src/media/transcript-persistence-program.ts +39 -0
- package/src/media/transcript-persistence-runtime.ts +24 -0
- package/src/media/transcript.ts +616 -0
- package/src/media/utf8-order.ts +30 -0
- package/src/media/whisper-cpp.ts +464 -0
- package/src/media/whisper-language.ts +58 -0
- package/src/media/yt-dlp.ts +439 -0
- package/src/message-like-me-agentic-messaging.ts +882 -0
- package/src/messaging-action-store.ts +793 -0
- package/src/messaging-runtime.ts +2136 -0
- package/src/messaging-store.ts +539 -0
- package/src/messaging-types.ts +1663 -0
- package/src/messaging.ts +334 -0
- package/src/model.ts +2671 -0
- package/src/oauth-google.ts +888 -0
- package/src/omni-client-types.ts +306 -0
- package/src/omni-client.ts +2225 -0
- package/src/omni-limits.ts +34 -0
- package/src/omni-model.ts +1614 -0
- package/src/omni-request.ts +379 -0
- package/src/omni-runtime.ts +1632 -0
- package/src/operation-deadline.ts +189 -0
- package/src/path-helper.ts +1656 -0
- package/src/pinned-https.ts +194 -0
- package/src/plan-assets.ts +832 -0
- package/src/platform-catalog.ts +1832 -0
- package/src/plugins/beeper-linked-device/plugin.ts +310 -0
- package/src/plugins/bluesky-web/plugin.ts +178 -0
- package/src/plugins/clasificados-web/plugin.ts +66 -0
- package/src/plugins/github-web/plugin.ts +80 -0
- package/src/plugins/gmail-official/plugin.ts +110 -0
- package/src/plugins/hacker-news-web/plugin.ts +52 -0
- package/src/plugins/imessage-direct/plugin.ts +195 -0
- package/src/plugins/imessage-direct/vendor/0001-fix-keep-AppleScript-send-payloads-out-of-child-argv.patch +1430 -0
- package/src/plugins/imessage-direct/vendor/0002-feat-rpc-add-exact-chat-lookup.patch +225 -0
- package/src/plugins/imessage-direct/vendor/provenance.json +80 -0
- package/src/plugins/linkedin-official/plugin.ts +84 -0
- package/src/plugins/linkedin-web/plugin.ts +412 -0
- package/src/plugins/meta-web/plugin.ts +317 -0
- package/src/plugins/reddit-web/flair.ts +492 -0
- package/src/plugins/reddit-web/plugin.ts +477 -0
- package/src/plugins/substack-web/plugin.ts +124 -0
- package/src/plugins/tiktok-web/plugin.ts +67 -0
- package/src/plugins/twitch-web/plugin.ts +54 -0
- package/src/plugins/whatsapp-linked-device/plugin.ts +105 -0
- package/src/plugins/x-official/plugin.ts +64 -0
- package/src/plugins/x-web/plugin.ts +488 -0
- package/src/plugins/youtube-web/plugin.ts +101 -0
- package/src/portable-run-recovery.ts +466 -0
- package/src/process-identity.ts +501 -0
- package/src/profile-clone-helper.ts +104 -0
- package/src/provider-catalog-views.ts +51 -0
- package/src/provider-context.ts +69 -0
- package/src/provider-contract-definitions-x.ts +45 -0
- package/src/provider-contract-definitions.ts +948 -0
- package/src/provider-contract-input-gmail.ts +176 -0
- package/src/provider-contract-input-linkedin.ts +77 -0
- package/src/provider-contract-input-x.ts +304 -0
- package/src/provider-contract-planning.ts +23 -0
- package/src/provider-contract-semantic-identity.ts +54 -0
- package/src/provider-contracts.ts +251 -0
- package/src/provider-http.ts +723 -0
- package/src/provider-plugin-auth.ts +36 -0
- package/src/provider-plugin-builtins.ts +229 -0
- package/src/provider-plugin-cleanup-barrier.ts +229 -0
- package/src/provider-plugin-cleanup-execution.ts +79 -0
- package/src/provider-plugin-cleanup-resource.ts +470 -0
- package/src/provider-plugin-contract-identity.ts +707 -0
- package/src/provider-plugin-host.ts +1972 -0
- package/src/provider-plugin-identifiers.ts +60 -0
- package/src/provider-plugin-invocation-lease.ts +889 -0
- package/src/provider-plugin-lifecycle-kernel.ts +566 -0
- package/src/provider-plugin-lifecycle.ts +1455 -0
- package/src/provider-plugin-module-analysis.ts +336 -0
- package/src/provider-plugin-package.ts +2530 -0
- package/src/provider-plugin-portable-authority.ts +106 -0
- package/src/provider-plugin-portable-catalog.ts +94 -0
- package/src/provider-plugin-portable-identity.ts +315 -0
- package/src/provider-plugin-portable-runtime.ts +2057 -0
- package/src/provider-plugin-protocol.ts +1625 -0
- package/src/provider-plugin-registry.ts +3342 -0
- package/src/provider-plugin-store.ts +2155 -0
- package/src/provider-plugin-test-fixture.ts +5 -0
- package/src/provider-plugin.ts +4907 -0
- package/src/provider-plugins.generated.ts +43 -0
- package/src/provider-plugins.ts +8 -0
- package/src/provider-subject.ts +41 -0
- package/src/provider.ts +315 -0
- package/src/providers/beeper-local-runtime.ts +5426 -0
- package/src/providers/beeper-local.ts +2739 -0
- package/src/providers/beeper-messaging.ts +462 -0
- package/src/providers/beeper-omni.ts +1159 -0
- package/src/providers/bluesky-web-runtime.ts +2895 -0
- package/src/providers/bluesky-web.ts +1614 -0
- package/src/providers/clasificados-web-runtime.ts +255 -0
- package/src/providers/clasificados-web.ts +503 -0
- package/src/providers/contact-projection.ts +301 -0
- package/src/providers/github-read-model.ts +94 -0
- package/src/providers/github-read-platform.ts +165 -0
- package/src/providers/github-read-program.ts +97 -0
- package/src/providers/github-web-runtime.ts +320 -0
- package/src/providers/github-web.ts +403 -0
- package/src/providers/gmail-api.ts +2834 -0
- package/src/providers/gmail-omni.ts +830 -0
- package/src/providers/gmail.ts +1130 -0
- package/src/providers/hacker-news-web-runtime.ts +220 -0
- package/src/providers/hacker-news-web.ts +809 -0
- package/src/providers/imessage-direct-install.ts +354 -0
- package/src/providers/imessage-direct-messaging.ts +532 -0
- package/src/providers/imessage-direct-omni.ts +400 -0
- package/src/providers/imessage-direct-runtime.ts +1498 -0
- package/src/providers/imessage-direct.ts +351 -0
- package/src/providers/instagram-video-foundations.ts +913 -0
- package/src/providers/instagram-web-profile-browser.ts +600 -0
- package/src/providers/iso-bmff.ts +338 -0
- package/src/providers/linkedin-company-platform.ts +42 -0
- package/src/providers/linkedin-company-program.ts +88 -0
- package/src/providers/linkedin-contact-failure.ts +84 -0
- package/src/providers/linkedin-contact-platform.ts +45 -0
- package/src/providers/linkedin-contact-program.ts +91 -0
- package/src/providers/linkedin-profile-activity-failure.ts +76 -0
- package/src/providers/linkedin-profile-activity-platform.ts +41 -0
- package/src/providers/linkedin-profile-activity-program.ts +67 -0
- package/src/providers/linkedin-read-failure.ts +52 -0
- package/src/providers/linkedin-self-platform.ts +53 -0
- package/src/providers/linkedin-self-program.ts +122 -0
- package/src/providers/linkedin-web-article-browser.ts +794 -0
- package/src/providers/linkedin-web-bootstrap.ts +146 -0
- package/src/providers/linkedin-web-contact.ts +830 -0
- package/src/providers/linkedin-web-feed-browser.ts +832 -0
- package/src/providers/linkedin-web-feed.ts +864 -0
- package/src/providers/linkedin-web-post-browser.ts +653 -0
- package/src/providers/linkedin-web-profile-browser.ts +683 -0
- package/src/providers/linkedin-web-runtime.ts +2388 -0
- package/src/providers/linkedin-web.ts +3305 -0
- package/src/providers/linkedin.ts +1885 -0
- package/src/providers/meta-bootstrap.ts +1043 -0
- package/src/providers/meta-facebook-group.ts +690 -0
- package/src/providers/meta-marketplace-relay.ts +747 -0
- package/src/providers/meta-omni.ts +373 -0
- package/src/providers/meta-relay-bundle.ts +1049 -0
- package/src/providers/meta-web-descriptors.ts +1740 -0
- package/src/providers/meta-web-runtime.ts +3388 -0
- package/src/providers/meta-web.ts +2931 -0
- package/src/providers/read-failure.ts +139 -0
- package/src/providers/reddit-omni.ts +462 -0
- package/src/providers/reddit-web-runtime.ts +1929 -0
- package/src/providers/reddit-web.ts +1611 -0
- package/src/providers/rental-listings.ts +460 -0
- package/src/providers/substack-omni.ts +406 -0
- package/src/providers/substack-video-mp4.ts +33 -0
- package/src/providers/substack-web-runtime.ts +2812 -0
- package/src/providers/substack-web.ts +1502 -0
- package/src/providers/tiktok-video-mp4.ts +33 -0
- package/src/providers/tiktok-web-runtime.ts +770 -0
- package/src/providers/tiktok-web.ts +2786 -0
- package/src/providers/twitch-web-runtime.ts +226 -0
- package/src/providers/twitch-web.ts +274 -0
- package/src/providers/whatsapp-account-identity.ts +55 -0
- package/src/providers/whatsapp-contact-projection-helper.ts +874 -0
- package/src/providers/whatsapp-contact-projection-protocol.ts +403 -0
- package/src/providers/whatsapp-interaction-projection-helper.ts +1452 -0
- package/src/providers/whatsapp-interaction-projection-protocol.ts +462 -0
- package/src/providers/whatsapp-message-export-projection-protocol.ts +532 -0
- package/src/providers/whatsapp-messaging.ts +132 -0
- package/src/providers/whatsapp-omni.ts +586 -0
- package/src/providers/whatsapp-web-runtime.ts +3108 -0
- package/src/providers/whatsapp-web.ts +1037 -0
- package/src/providers/x-image-provenance.ts +523 -0
- package/src/providers/x-made-with-ai.ts +106 -0
- package/src/providers/x-transaction-id.ts +413 -0
- package/src/providers/x-web-runtime.ts +3485 -0
- package/src/providers/x-web.ts +2332 -0
- package/src/providers/x.ts +1889 -0
- package/src/providers/youtube-web-runtime.ts +1400 -0
- package/src/providers/youtube-web.ts +1221 -0
- package/src/read-admission-runtime.ts +91 -0
- package/src/read-client.ts +521 -0
- package/src/read-effect-platform.ts +7 -0
- package/src/read-effect-runtime.ts +14 -0
- package/src/read-effect.ts +52 -0
- package/src/read-projection-admission.ts +866 -0
- package/src/read-projections.ts +3151 -0
- package/src/recovery.ts +1492 -0
- package/src/reviewed-template.ts +146 -0
- package/src/run-journal.ts +1272 -0
- package/src/runtime.ts +5116 -0
- package/src/scripts/generate-provider-plugin-catalog.ts +121 -0
- package/src/scripts/install-whatsapp-protocol.sh +232 -0
- package/src/scripts/recover-install-stages.ts +1491 -0
- package/src/scripts/resolve-state-home.ts +30 -0
- package/src/scripts/scaffold-web-provider.ts +973 -0
- package/src/scripts/sync-bundled-adapters.ts +695 -0
- package/src/session-secrets.ts +1183 -0
- package/src/state-helper.bunfig.toml +2 -0
- package/src/state-helper.ts +2914 -0
- package/src/storage.ts +3842 -0
- package/src/transport-policy.ts +2 -0
- package/src/usage.ts +182 -0
- package/src/version.ts +2 -0
- package/src/web-session-authentication-policy.ts +136 -0
- package/src/web-session-cleanup-admission.ts +2344 -0
- package/src/web-session-cleanup-join.ts +99 -0
- package/src/web-session-client.ts +996 -0
- package/src/web-session-contract-definitions.ts +820 -0
- package/src/web-session-contract-planning.ts +42 -0
- package/src/web-session-contracts.ts +228 -0
- package/src/web-session-cookies.ts +500 -0
- package/src/web-session-execution.ts +385 -0
- package/src/web-session-http.ts +373 -0
- package/src/web-session-read-errors.ts +19 -0
- package/src/web-session-read-runtime.ts +124 -0
- package/src/web-session-recovery.ts +818 -0
- package/src/web-session-sites.ts +58 -0
- package/src/web-session-template.ts +994 -0
- package/src/web-session.ts +93 -0
- package/src/whatsapp-client-binding.ts +21 -0
- package/src/whatsapp-client-types.ts +75 -0
- package/src/whatsapp-client.ts +395 -0
- package/src/whatsapp-export-coordinate.ts +17 -0
- package/src/whatsapp-message-bundle-v2.ts +195 -0
- package/src/whatsapp-message-like-me-cli.ts +195 -0
- package/src/whatsapp-message-like-me-export.ts +1072 -0
- package/src/whatsapp-message-like-me-source.ts +1012 -0
- package/tsconfig.json +67 -0
|
@@ -0,0 +1,793 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
|
|
3
|
+
import { canonicalJson, sha256 } from "./canonical-json";
|
|
4
|
+
import {
|
|
5
|
+
MESSAGING_RECEIPT_BINDING_CONTRACT_HASH,
|
|
6
|
+
MESSAGING_RECEIPT_BINDING_CONTRACT_ID,
|
|
7
|
+
MESSAGING_RECEIPT_BINDING_V1_CONTRACT_HASH,
|
|
8
|
+
MESSAGING_RECEIPT_BINDING_V1_CONTRACT_ID,
|
|
9
|
+
parseMessagingReceiptBindingV1,
|
|
10
|
+
parseMessagingReceiptBindingV2,
|
|
11
|
+
type MessagingPartJournalStateV1,
|
|
12
|
+
type MessagingPrivateProviderOutcomeV1,
|
|
13
|
+
type MessagingReceiptBinding,
|
|
14
|
+
type MessagingRunReceipt,
|
|
15
|
+
type MessagingRunV1,
|
|
16
|
+
} from "./messaging-types";
|
|
17
|
+
import type { ProviderPluginMessagingExpectedOwnPrefixV1 } from "./provider-plugin";
|
|
18
|
+
import type { MessagingCompositeInvocationPlanV1 } from "./runtime";
|
|
19
|
+
import {
|
|
20
|
+
openAuthenticatedPrivatePayload,
|
|
21
|
+
sealAuthenticatedPrivatePayload,
|
|
22
|
+
} from "./read-projections";
|
|
23
|
+
import {
|
|
24
|
+
createPrivateJsonIfAbsent,
|
|
25
|
+
ensurePrivateStateDirectory,
|
|
26
|
+
readPrivateStateFileIfPresent,
|
|
27
|
+
ghostgetStateHome,
|
|
28
|
+
writePrivateJsonIfUnchanged,
|
|
29
|
+
} from "./storage";
|
|
30
|
+
|
|
31
|
+
type Environment = Readonly<Record<string, string | undefined>>;
|
|
32
|
+
|
|
33
|
+
const MAX_PLAINTEXT_BYTES = 1024 * 1024;
|
|
34
|
+
const MAX_ENCRYPTED_BYTES = 2 * 1024 * 1024;
|
|
35
|
+
|
|
36
|
+
export type MessagingRunSnapshotV1 = {
|
|
37
|
+
readonly run: MessagingRunV1;
|
|
38
|
+
readonly contentSha256: string;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export type MessagingRunEventV1 =
|
|
42
|
+
| {
|
|
43
|
+
readonly type: "claimed";
|
|
44
|
+
readonly index: number;
|
|
45
|
+
readonly observedAcceptedPrefixCount: number;
|
|
46
|
+
readonly at: string;
|
|
47
|
+
}
|
|
48
|
+
| { readonly type: "dispatching"; readonly index: number; readonly at: string }
|
|
49
|
+
| {
|
|
50
|
+
readonly type: "accepted";
|
|
51
|
+
readonly index: number;
|
|
52
|
+
readonly providerMessageId: string;
|
|
53
|
+
readonly providerRevision: string | null;
|
|
54
|
+
readonly at: string;
|
|
55
|
+
}
|
|
56
|
+
| {
|
|
57
|
+
readonly type: "categorical-stop";
|
|
58
|
+
readonly index: number;
|
|
59
|
+
readonly partState: "failed-before-dispatch" | "failed-permanent";
|
|
60
|
+
readonly reason:
|
|
61
|
+
| "context-drift"
|
|
62
|
+
| "prefix-freshness-unproven"
|
|
63
|
+
| "provider-failed-before-dispatch"
|
|
64
|
+
| "journal-recovery-required";
|
|
65
|
+
readonly at: string;
|
|
66
|
+
}
|
|
67
|
+
| {
|
|
68
|
+
readonly type: "indeterminate";
|
|
69
|
+
readonly index: number;
|
|
70
|
+
readonly reason: "provider-result-indeterminate";
|
|
71
|
+
readonly privateProviderOutcome: MessagingPrivateProviderOutcomeV1 | null;
|
|
72
|
+
readonly at: string;
|
|
73
|
+
}
|
|
74
|
+
| {
|
|
75
|
+
readonly type: "indeterminate";
|
|
76
|
+
readonly index: number;
|
|
77
|
+
readonly reason: "journal-recovery-required";
|
|
78
|
+
readonly at: string;
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
function root(environment: Environment): string {
|
|
82
|
+
return join(ghostgetStateHome(environment), "messaging", "runs");
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function path(runId: string, environment: Environment): string {
|
|
86
|
+
if (!/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/u.test(runId)) {
|
|
87
|
+
throw new Error("messaging run ID is malformed");
|
|
88
|
+
}
|
|
89
|
+
return join(root(environment), `${runId}.json`);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function domain(runId: string): string {
|
|
93
|
+
return `wrench-messaging-run-v1:${runId}`;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function timestamp(value: string): string {
|
|
97
|
+
if (
|
|
98
|
+
!/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/u.test(value)
|
|
99
|
+
|| !Number.isFinite(Date.parse(value))
|
|
100
|
+
|| new Date(value).toISOString() !== value
|
|
101
|
+
) {
|
|
102
|
+
throw new Error("messaging run timestamp is malformed");
|
|
103
|
+
}
|
|
104
|
+
return value;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function record(value: unknown, label: string): Record<string, unknown> {
|
|
108
|
+
if (
|
|
109
|
+
typeof value !== "object"
|
|
110
|
+
|| value === null
|
|
111
|
+
|| Array.isArray(value)
|
|
112
|
+
|| Object.getPrototypeOf(value) !== Object.prototype
|
|
113
|
+
) throw new Error(`${label} is malformed`);
|
|
114
|
+
return value as Record<string, unknown>;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function exactKeys(value: Record<string, unknown>, keys: readonly string[], label: string): void {
|
|
118
|
+
const actual = Object.keys(value).sort();
|
|
119
|
+
const expected = [...keys].sort();
|
|
120
|
+
if (actual.length !== expected.length || actual.some((key, index) => key !== expected[index])) {
|
|
121
|
+
throw new Error(`${label} has unsupported fields`);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function digest(value: unknown, label: string): string {
|
|
126
|
+
if (typeof value !== "string" || !/^[a-f0-9]{64}$/u.test(value)) {
|
|
127
|
+
throw new Error(`${label} is malformed`);
|
|
128
|
+
}
|
|
129
|
+
return value;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function boundedText(value: unknown, label: string, maximum: number): string {
|
|
133
|
+
if (
|
|
134
|
+
typeof value !== "string"
|
|
135
|
+
|| value.length < 1
|
|
136
|
+
|| Buffer.byteLength(value, "utf8") > maximum
|
|
137
|
+
|| value.includes("\0")
|
|
138
|
+
) throw new Error(`${label} is malformed`);
|
|
139
|
+
return value;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function parsePrivateProviderOutcome(
|
|
143
|
+
value: unknown,
|
|
144
|
+
): MessagingPrivateProviderOutcomeV1 {
|
|
145
|
+
const source = record(value, "messaging private provider outcome");
|
|
146
|
+
exactKeys(source, [
|
|
147
|
+
"schemaVersion", "messagingContractId", "code",
|
|
148
|
+
], "messaging private provider outcome");
|
|
149
|
+
const messagingContractId = boundedText(
|
|
150
|
+
source.messagingContractId,
|
|
151
|
+
"messaging private provider outcome contract ID",
|
|
152
|
+
256,
|
|
153
|
+
);
|
|
154
|
+
const code = boundedText(
|
|
155
|
+
source.code,
|
|
156
|
+
"messaging private provider outcome code",
|
|
157
|
+
64,
|
|
158
|
+
);
|
|
159
|
+
if (
|
|
160
|
+
source.schemaVersion !== 1
|
|
161
|
+
|| !/^[a-z][a-z0-9.-]{0,255}$/u.test(messagingContractId)
|
|
162
|
+
|| !/^[a-z][a-z0-9_-]{0,63}$/u.test(code)
|
|
163
|
+
) throw new Error("messaging private provider outcome is malformed");
|
|
164
|
+
return Object.freeze({ schemaVersion: 1, messagingContractId, code });
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function denseArray(value: unknown, label: string, maximum: number): readonly unknown[] {
|
|
168
|
+
if (
|
|
169
|
+
!Array.isArray(value)
|
|
170
|
+
|| Object.getPrototypeOf(value) !== Array.prototype
|
|
171
|
+
|| value.length < 1
|
|
172
|
+
|| value.length > maximum
|
|
173
|
+
) throw new Error(`${label} is malformed`);
|
|
174
|
+
const expected = new Set<PropertyKey>([
|
|
175
|
+
"length",
|
|
176
|
+
...Array.from({ length: value.length }, (_unused, index) => String(index)),
|
|
177
|
+
]);
|
|
178
|
+
const keys = Reflect.ownKeys(value);
|
|
179
|
+
if (keys.length !== expected.size || keys.some((key) => !expected.has(key))) {
|
|
180
|
+
throw new Error(`${label} must be a dense array without extra fields`);
|
|
181
|
+
}
|
|
182
|
+
return value;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function assertRun(run: MessagingRunV1): void {
|
|
186
|
+
const acceptedProviderMessageIds = run.parts
|
|
187
|
+
.slice(0, run.provenPartCount)
|
|
188
|
+
.map((part) => part.providerMessageId);
|
|
189
|
+
if (
|
|
190
|
+
run.schemaVersion !== 1
|
|
191
|
+
|| run.format !== "wrench.messaging-run"
|
|
192
|
+
|| run.parts.length < 1
|
|
193
|
+
|| run.parts.length > 8
|
|
194
|
+
|| run.partCount !== run.parts.length
|
|
195
|
+
|| run.provenPartCount < 0
|
|
196
|
+
|| run.provenPartCount > run.partCount
|
|
197
|
+
|| !Number.isSafeInteger(run.observedAcceptedPrefixCount)
|
|
198
|
+
|| run.observedAcceptedPrefixCount < 0
|
|
199
|
+
|| run.observedAcceptedPrefixCount > run.provenPartCount
|
|
200
|
+
|| run.parts.slice(0, run.provenPartCount).some((part) => part.state !== "accepted")
|
|
201
|
+
|| acceptedProviderMessageIds.some((providerMessageId) => providerMessageId === null)
|
|
202
|
+
|| new Set(acceptedProviderMessageIds).size !== acceptedProviderMessageIds.length
|
|
203
|
+
|| run.parts.slice(run.provenPartCount).some((part, offset) =>
|
|
204
|
+
offset > 0 && part.state !== "unattempted")
|
|
205
|
+
) throw new Error("messaging run violates its ordered-prefix invariant");
|
|
206
|
+
const active = run.parts[run.provenPartCount];
|
|
207
|
+
if (
|
|
208
|
+
run.privateProviderOutcome !== null
|
|
209
|
+
&& (
|
|
210
|
+
run.state !== "indeterminate"
|
|
211
|
+
|| run.terminalReason !== "provider-result-indeterminate"
|
|
212
|
+
|| run.possibleSubmittedPartIndex !== run.provenPartCount
|
|
213
|
+
|| active?.state !== "indeterminate"
|
|
214
|
+
)
|
|
215
|
+
) throw new Error("messaging run private provider outcome is contradictory");
|
|
216
|
+
if (run.state === "pending") {
|
|
217
|
+
if (
|
|
218
|
+
run.provenPartCount === run.partCount
|
|
219
|
+
|| run.possibleSubmittedPartIndex !== null
|
|
220
|
+
|| run.terminalReason !== null
|
|
221
|
+
|| active === undefined
|
|
222
|
+
|| !["unattempted", "claimed", "dispatching"].includes(active.state)
|
|
223
|
+
) throw new Error("pending messaging run has contradictory state");
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
if (run.state === "submitted") {
|
|
227
|
+
if (
|
|
228
|
+
run.provenPartCount !== run.partCount
|
|
229
|
+
|| run.possibleSubmittedPartIndex !== null
|
|
230
|
+
|| run.terminalReason !== null
|
|
231
|
+
) throw new Error("submitted messaging run has contradictory state");
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
if (run.state === "failed") {
|
|
235
|
+
if (
|
|
236
|
+
run.provenPartCount !== 0
|
|
237
|
+
|| run.possibleSubmittedPartIndex !== null
|
|
238
|
+
|| run.terminalReason !== "context-drift"
|
|
239
|
+
&& run.terminalReason !== "prefix-freshness-unproven"
|
|
240
|
+
&& run.terminalReason !== "provider-failed-before-dispatch"
|
|
241
|
+
&& run.terminalReason !== "journal-recovery-required"
|
|
242
|
+
|| active === undefined
|
|
243
|
+
|| active.state !== "failed-before-dispatch" && active.state !== "failed-permanent"
|
|
244
|
+
) throw new Error("failed messaging run has contradictory state");
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
if (run.state === "partial") {
|
|
248
|
+
if (
|
|
249
|
+
run.provenPartCount < 1
|
|
250
|
+
|| run.provenPartCount >= run.partCount
|
|
251
|
+
|| run.possibleSubmittedPartIndex !== null
|
|
252
|
+
|| run.terminalReason !== "context-drift"
|
|
253
|
+
&& run.terminalReason !== "prefix-freshness-unproven"
|
|
254
|
+
&& run.terminalReason !== "provider-failed-before-dispatch"
|
|
255
|
+
&& run.terminalReason !== "journal-recovery-required"
|
|
256
|
+
|| active === undefined
|
|
257
|
+
|| active.state !== "failed-before-dispatch" && active.state !== "failed-permanent"
|
|
258
|
+
) throw new Error("partial messaging run has contradictory state");
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
if (
|
|
262
|
+
run.state !== "indeterminate"
|
|
263
|
+
|| run.provenPartCount >= run.partCount
|
|
264
|
+
|| run.possibleSubmittedPartIndex !== run.provenPartCount
|
|
265
|
+
|| run.terminalReason !== "provider-result-indeterminate"
|
|
266
|
+
&& run.terminalReason !== "journal-recovery-required"
|
|
267
|
+
|| active?.state !== "indeterminate"
|
|
268
|
+
) throw new Error("indeterminate messaging run has contradictory state");
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function parseRun(value: unknown): MessagingRunV1 {
|
|
272
|
+
const source = record(value, "messaging run");
|
|
273
|
+
const hasObservedAcceptedPrefixCount = Object.hasOwn(
|
|
274
|
+
source,
|
|
275
|
+
"observedAcceptedPrefixCount",
|
|
276
|
+
);
|
|
277
|
+
const hasPrivateProviderOutcome = Object.hasOwn(
|
|
278
|
+
source,
|
|
279
|
+
"privateProviderOutcome",
|
|
280
|
+
);
|
|
281
|
+
const hasContextBinding = Object.hasOwn(source, "contextBindingSha256");
|
|
282
|
+
const hasSourceCoordinate = Object.hasOwn(
|
|
283
|
+
source,
|
|
284
|
+
"sourceConversationCoordinateSha256",
|
|
285
|
+
);
|
|
286
|
+
if (hasContextBinding !== hasSourceCoordinate) {
|
|
287
|
+
throw new Error("messaging run has partial context evidence");
|
|
288
|
+
}
|
|
289
|
+
const hasCurrentContextEvidence = hasContextBinding
|
|
290
|
+
&& source.contextBindingSha256 !== null;
|
|
291
|
+
if (
|
|
292
|
+
hasContextBinding
|
|
293
|
+
&& ((source.contextBindingSha256 === null)
|
|
294
|
+
!== (source.sourceConversationCoordinateSha256 === null))
|
|
295
|
+
) throw new Error("messaging run has partial context evidence");
|
|
296
|
+
if (!hasObservedAcceptedPrefixCount && hasPrivateProviderOutcome) {
|
|
297
|
+
throw new Error("messaging run has unsupported fields");
|
|
298
|
+
}
|
|
299
|
+
exactKeys(source, [
|
|
300
|
+
"schemaVersion", "format", "runId", "planDigest", "routeRef", "contextRef",
|
|
301
|
+
"clientIntentSha256",
|
|
302
|
+
...(hasContextBinding
|
|
303
|
+
? ["contextBindingSha256", "sourceConversationCoordinateSha256"]
|
|
304
|
+
: []),
|
|
305
|
+
"turnDigest", "previewDigest",
|
|
306
|
+
"state", "partCount",
|
|
307
|
+
"provenPartCount",
|
|
308
|
+
...(hasObservedAcceptedPrefixCount ? ["observedAcceptedPrefixCount"] : []),
|
|
309
|
+
"possibleSubmittedPartIndex",
|
|
310
|
+
"terminalReason", "parts",
|
|
311
|
+
...(hasPrivateProviderOutcome ? ["privateProviderOutcome"] : []),
|
|
312
|
+
"startedAt", "recordedAt",
|
|
313
|
+
], "messaging run");
|
|
314
|
+
if (
|
|
315
|
+
source.schemaVersion !== 1
|
|
316
|
+
|| source.format !== "wrench.messaging-run"
|
|
317
|
+
|| typeof source.runId !== "string"
|
|
318
|
+
|| !/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/u.test(source.runId)
|
|
319
|
+
|| typeof source.routeRef !== "string"
|
|
320
|
+
|| !/^wmroute_[A-Za-z0-9_-]{22}$/u.test(source.routeRef)
|
|
321
|
+
|| typeof source.contextRef !== "string"
|
|
322
|
+
|| !/^wmcontext_[A-Za-z0-9_-]{22}$/u.test(source.contextRef)
|
|
323
|
+
|| source.state !== "pending"
|
|
324
|
+
&& source.state !== "submitted"
|
|
325
|
+
&& source.state !== "failed"
|
|
326
|
+
&& source.state !== "partial"
|
|
327
|
+
&& source.state !== "indeterminate"
|
|
328
|
+
|| typeof source.partCount !== "number"
|
|
329
|
+
|| !Number.isSafeInteger(source.partCount)
|
|
330
|
+
|| source.partCount < 1
|
|
331
|
+
|| source.partCount > 8
|
|
332
|
+
|| typeof source.provenPartCount !== "number"
|
|
333
|
+
|| !Number.isSafeInteger(source.provenPartCount)
|
|
334
|
+
|| source.provenPartCount < 0
|
|
335
|
+
|| source.provenPartCount > source.partCount
|
|
336
|
+
|| hasObservedAcceptedPrefixCount
|
|
337
|
+
&& (typeof source.observedAcceptedPrefixCount !== "number"
|
|
338
|
+
|| !Number.isSafeInteger(source.observedAcceptedPrefixCount)
|
|
339
|
+
|| source.observedAcceptedPrefixCount < 0
|
|
340
|
+
|| source.observedAcceptedPrefixCount > source.provenPartCount)
|
|
341
|
+
|| source.possibleSubmittedPartIndex !== null
|
|
342
|
+
&& (typeof source.possibleSubmittedPartIndex !== "number"
|
|
343
|
+
|| !Number.isSafeInteger(source.possibleSubmittedPartIndex)
|
|
344
|
+
|| source.possibleSubmittedPartIndex < 0
|
|
345
|
+
|| source.possibleSubmittedPartIndex >= source.partCount)
|
|
346
|
+
) throw new Error("messaging run is malformed");
|
|
347
|
+
const sourceParts = denseArray(source.parts, "messaging run parts", 8);
|
|
348
|
+
if (sourceParts.length !== source.partCount) throw new Error("messaging run is malformed");
|
|
349
|
+
const terminalReasons = new Set<MessagingRunV1["terminalReason"]>([
|
|
350
|
+
null,
|
|
351
|
+
"context-drift",
|
|
352
|
+
"prefix-freshness-unproven",
|
|
353
|
+
"provider-failed-before-dispatch",
|
|
354
|
+
"provider-result-indeterminate",
|
|
355
|
+
"journal-recovery-required",
|
|
356
|
+
]);
|
|
357
|
+
if (!terminalReasons.has(source.terminalReason as MessagingRunV1["terminalReason"])) {
|
|
358
|
+
throw new Error("messaging run terminal reason is malformed");
|
|
359
|
+
}
|
|
360
|
+
const partStates = new Set<MessagingPartJournalStateV1>([
|
|
361
|
+
"unattempted", "claimed", "dispatching", "accepted",
|
|
362
|
+
"failed-before-dispatch", "failed-permanent", "indeterminate",
|
|
363
|
+
]);
|
|
364
|
+
const seen = new Set<string>();
|
|
365
|
+
const parts = sourceParts.map((value_, index) => {
|
|
366
|
+
const part = record(value_, `messaging run part ${index + 1}`);
|
|
367
|
+
exactKeys(part, [
|
|
368
|
+
"partId", "text", "replyRef", "replyToProviderId", "direction", "bodySha256",
|
|
369
|
+
"state", "providerMessageId", "providerRevision", "delivery", "read",
|
|
370
|
+
], `messaging run part ${index + 1}`);
|
|
371
|
+
const partId = boundedText(part.partId, "messaging run part ID", 64);
|
|
372
|
+
if (!/^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/u.test(partId) || seen.has(partId)) {
|
|
373
|
+
throw new Error("messaging run part ID is malformed or repeated");
|
|
374
|
+
}
|
|
375
|
+
seen.add(partId);
|
|
376
|
+
if (
|
|
377
|
+
!partStates.has(part.state as MessagingPartJournalStateV1)
|
|
378
|
+
|| part.replyRef !== null
|
|
379
|
+
&& (typeof part.replyRef !== "string" || !/^wmreply_[A-Za-z0-9_-]{22}$/u.test(part.replyRef))
|
|
380
|
+
|| part.replyToProviderId !== null
|
|
381
|
+
&& (typeof part.replyToProviderId !== "string" || part.replyToProviderId.length < 1 || part.replyToProviderId.length > 4_096)
|
|
382
|
+
|| part.direction !== "outgoing"
|
|
383
|
+
|| part.providerMessageId !== null
|
|
384
|
+
&& (typeof part.providerMessageId !== "string" || part.providerMessageId.length < 1 || part.providerMessageId.length > 4_096)
|
|
385
|
+
|| part.providerRevision !== null
|
|
386
|
+
&& (typeof part.providerRevision !== "string" || part.providerRevision.length < 1 || part.providerRevision.length > 4_096)
|
|
387
|
+
|| part.state === "accepted" && part.providerMessageId === null
|
|
388
|
+
|| part.state !== "accepted" && part.providerMessageId !== null
|
|
389
|
+
|| part.state !== "accepted" && part.providerRevision !== null
|
|
390
|
+
|| part.delivery !== "unknown" && part.delivery !== "delivered" && part.delivery !== "failed"
|
|
391
|
+
|| part.read !== "unknown" && part.read !== "read"
|
|
392
|
+
) throw new Error("messaging run part is malformed");
|
|
393
|
+
const text = boundedText(part.text, "messaging run part text", 65_536);
|
|
394
|
+
const bodySha256 = digest(part.bodySha256, "messaging run part body digest");
|
|
395
|
+
if (sha256(text) !== bodySha256) {
|
|
396
|
+
throw new Error("messaging run part body digest disagrees");
|
|
397
|
+
}
|
|
398
|
+
return Object.freeze({
|
|
399
|
+
partId,
|
|
400
|
+
text,
|
|
401
|
+
replyRef: part.replyRef as string | null,
|
|
402
|
+
replyToProviderId: part.replyToProviderId as string | null,
|
|
403
|
+
direction: "outgoing" as const,
|
|
404
|
+
bodySha256,
|
|
405
|
+
state: part.state as MessagingPartJournalStateV1,
|
|
406
|
+
providerMessageId: part.providerMessageId as string | null,
|
|
407
|
+
providerRevision: part.providerRevision as string | null,
|
|
408
|
+
delivery: part.delivery as "unknown" | "delivered" | "failed",
|
|
409
|
+
read: part.read as "unknown" | "read",
|
|
410
|
+
});
|
|
411
|
+
});
|
|
412
|
+
const startedAt = timestamp(boundedText(source.startedAt, "messaging run start", 64));
|
|
413
|
+
const recordedAt = timestamp(boundedText(source.recordedAt, "messaging run record time", 64));
|
|
414
|
+
if (Date.parse(recordedAt) < Date.parse(startedAt)) throw new Error("messaging run time moved backward");
|
|
415
|
+
const parsed: MessagingRunV1 = Object.freeze({
|
|
416
|
+
schemaVersion: 1,
|
|
417
|
+
format: "wrench.messaging-run",
|
|
418
|
+
runId: source.runId,
|
|
419
|
+
planDigest: digest(source.planDigest, "messaging run plan digest"),
|
|
420
|
+
routeRef: source.routeRef,
|
|
421
|
+
contextRef: source.contextRef,
|
|
422
|
+
clientIntentSha256: digest(source.clientIntentSha256, "messaging run client intent"),
|
|
423
|
+
contextBindingSha256: hasCurrentContextEvidence
|
|
424
|
+
? digest(
|
|
425
|
+
source.contextBindingSha256,
|
|
426
|
+
"messaging run context binding",
|
|
427
|
+
)
|
|
428
|
+
: null,
|
|
429
|
+
sourceConversationCoordinateSha256: hasCurrentContextEvidence
|
|
430
|
+
? digest(
|
|
431
|
+
source.sourceConversationCoordinateSha256,
|
|
432
|
+
"messaging run source conversation coordinate",
|
|
433
|
+
)
|
|
434
|
+
: null,
|
|
435
|
+
turnDigest: digest(source.turnDigest, "messaging run turn digest"),
|
|
436
|
+
previewDigest: digest(source.previewDigest, "messaging run preview digest"),
|
|
437
|
+
state: source.state,
|
|
438
|
+
partCount: source.partCount,
|
|
439
|
+
provenPartCount: source.provenPartCount,
|
|
440
|
+
observedAcceptedPrefixCount: hasObservedAcceptedPrefixCount
|
|
441
|
+
? source.observedAcceptedPrefixCount as number
|
|
442
|
+
: source.provenPartCount,
|
|
443
|
+
possibleSubmittedPartIndex: source.possibleSubmittedPartIndex as number | null,
|
|
444
|
+
privateProviderOutcome: hasPrivateProviderOutcome
|
|
445
|
+
? source.privateProviderOutcome === null
|
|
446
|
+
? null
|
|
447
|
+
: parsePrivateProviderOutcome(source.privateProviderOutcome)
|
|
448
|
+
: null,
|
|
449
|
+
terminalReason: source.terminalReason as MessagingRunV1["terminalReason"],
|
|
450
|
+
parts: Object.freeze(parts),
|
|
451
|
+
startedAt,
|
|
452
|
+
recordedAt,
|
|
453
|
+
});
|
|
454
|
+
assertRun(parsed);
|
|
455
|
+
return parsed;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
/** Strict foreign-value parser used by encrypted storage and contract tests. */
|
|
459
|
+
export function parseMessagingRunV1(value: unknown): MessagingRunV1 {
|
|
460
|
+
return parseRun(value);
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
/** Exact proof material retained durably for a future checked provider reread. */
|
|
464
|
+
export function messagingExpectedOwnPrefix(
|
|
465
|
+
run: MessagingRunV1,
|
|
466
|
+
): ProviderPluginMessagingExpectedOwnPrefixV1["accepted"] {
|
|
467
|
+
assertRun(run);
|
|
468
|
+
return Object.freeze(run.parts.slice(0, run.provenPartCount).map((part) => {
|
|
469
|
+
if (part.state !== "accepted" || part.providerMessageId === null) {
|
|
470
|
+
throw new Error("messaging run accepted prefix is incomplete");
|
|
471
|
+
}
|
|
472
|
+
return Object.freeze({
|
|
473
|
+
providerMessageId: part.providerMessageId,
|
|
474
|
+
providerRevision: part.providerRevision,
|
|
475
|
+
direction: part.direction,
|
|
476
|
+
bodySha256: part.bodySha256,
|
|
477
|
+
replyToProviderId: part.replyToProviderId,
|
|
478
|
+
});
|
|
479
|
+
}));
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
function sealed(run: MessagingRunV1, environment: Environment): unknown {
|
|
483
|
+
return sealAuthenticatedPrivatePayload(
|
|
484
|
+
run,
|
|
485
|
+
domain(run.runId),
|
|
486
|
+
environment,
|
|
487
|
+
MAX_PLAINTEXT_BYTES,
|
|
488
|
+
);
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
export function initializeMessagingRun(
|
|
492
|
+
runId: string,
|
|
493
|
+
planDigest: string,
|
|
494
|
+
plan: MessagingCompositeInvocationPlanV1,
|
|
495
|
+
environment: Environment,
|
|
496
|
+
at: string,
|
|
497
|
+
): MessagingRunSnapshotV1 {
|
|
498
|
+
if (
|
|
499
|
+
plan.contextBindingSha256 === null
|
|
500
|
+
|| plan.sourceConversationCoordinateSha256 === null
|
|
501
|
+
) {
|
|
502
|
+
throw new Error(
|
|
503
|
+
"predecessor messaging plan lacks current context evidence; preview the action again",
|
|
504
|
+
);
|
|
505
|
+
}
|
|
506
|
+
ensurePrivateStateDirectory(root(environment), environment);
|
|
507
|
+
const startedAt = timestamp(at);
|
|
508
|
+
const run = parseRun({
|
|
509
|
+
schemaVersion: 1,
|
|
510
|
+
format: "wrench.messaging-run",
|
|
511
|
+
runId,
|
|
512
|
+
planDigest,
|
|
513
|
+
routeRef: plan.routeRef,
|
|
514
|
+
contextRef: plan.contextRef,
|
|
515
|
+
clientIntentSha256: plan.clientIntentSha256,
|
|
516
|
+
contextBindingSha256: plan.contextBindingSha256,
|
|
517
|
+
sourceConversationCoordinateSha256:
|
|
518
|
+
plan.sourceConversationCoordinateSha256,
|
|
519
|
+
turnDigest: plan.turnDigest,
|
|
520
|
+
previewDigest: plan.previewDigest,
|
|
521
|
+
state: "pending",
|
|
522
|
+
partCount: plan.parts.length,
|
|
523
|
+
provenPartCount: 0,
|
|
524
|
+
observedAcceptedPrefixCount: 0,
|
|
525
|
+
possibleSubmittedPartIndex: null,
|
|
526
|
+
privateProviderOutcome: null,
|
|
527
|
+
terminalReason: null,
|
|
528
|
+
parts: plan.parts.map((part) => ({
|
|
529
|
+
partId: part.partId,
|
|
530
|
+
text: part.text,
|
|
531
|
+
replyRef: part.replyRef,
|
|
532
|
+
replyToProviderId: part.replyToProviderId,
|
|
533
|
+
direction: "outgoing" as const,
|
|
534
|
+
bodySha256: sha256(part.text),
|
|
535
|
+
state: "unattempted" as const,
|
|
536
|
+
providerMessageId: null,
|
|
537
|
+
providerRevision: null,
|
|
538
|
+
delivery: "unknown" as const,
|
|
539
|
+
read: "unknown" as const,
|
|
540
|
+
})),
|
|
541
|
+
startedAt,
|
|
542
|
+
recordedAt: startedAt,
|
|
543
|
+
});
|
|
544
|
+
const envelope = sealed(run, environment);
|
|
545
|
+
const encoded = `${canonicalJson(envelope)}\n`;
|
|
546
|
+
const created = createPrivateJsonIfAbsent(path(runId, environment), envelope, {
|
|
547
|
+
environment,
|
|
548
|
+
privateParent: true,
|
|
549
|
+
});
|
|
550
|
+
if (!created.created) {
|
|
551
|
+
const existing = readMessagingRun(runId, environment);
|
|
552
|
+
if (
|
|
553
|
+
existing.run.planDigest !== planDigest
|
|
554
|
+
|| existing.run.turnDigest !== plan.turnDigest
|
|
555
|
+
|| existing.run.contextBindingSha256 !== plan.contextBindingSha256
|
|
556
|
+
|| existing.run.sourceConversationCoordinateSha256
|
|
557
|
+
!== plan.sourceConversationCoordinateSha256
|
|
558
|
+
) throw new Error("existing messaging run belongs to another confirmation");
|
|
559
|
+
return existing;
|
|
560
|
+
}
|
|
561
|
+
return Object.freeze({ run, contentSha256: sha256(encoded) });
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
export function readMessagingRun(
|
|
565
|
+
runId: string,
|
|
566
|
+
environment: Environment = process.env,
|
|
567
|
+
): MessagingRunSnapshotV1 {
|
|
568
|
+
const text = readPrivateStateFileIfPresent(
|
|
569
|
+
path(runId, environment),
|
|
570
|
+
MAX_ENCRYPTED_BYTES,
|
|
571
|
+
"encrypted messaging run",
|
|
572
|
+
environment,
|
|
573
|
+
);
|
|
574
|
+
if (text === null) throw new Error("messaging run was not found");
|
|
575
|
+
let envelope: unknown;
|
|
576
|
+
try {
|
|
577
|
+
envelope = JSON.parse(text) as unknown;
|
|
578
|
+
} catch {
|
|
579
|
+
throw new Error("encrypted messaging run is malformed");
|
|
580
|
+
}
|
|
581
|
+
const run = parseRun(openAuthenticatedPrivatePayload(
|
|
582
|
+
envelope,
|
|
583
|
+
domain(runId),
|
|
584
|
+
environment,
|
|
585
|
+
MAX_PLAINTEXT_BYTES,
|
|
586
|
+
));
|
|
587
|
+
if (run.runId !== runId) throw new Error("messaging run coordinate disagrees");
|
|
588
|
+
return Object.freeze({ run, contentSha256: sha256(text) });
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
export function readMessagingRunIfPresent(
|
|
592
|
+
runId: string,
|
|
593
|
+
environment: Environment = process.env,
|
|
594
|
+
): MessagingRunSnapshotV1 | null {
|
|
595
|
+
try {
|
|
596
|
+
return readMessagingRun(runId, environment);
|
|
597
|
+
} catch (error) {
|
|
598
|
+
if (error instanceof Error && error.message === "messaging run was not found") return null;
|
|
599
|
+
throw error;
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
export function transitionMessagingRun(
|
|
604
|
+
run: MessagingRunV1,
|
|
605
|
+
event: MessagingRunEventV1,
|
|
606
|
+
): MessagingRunV1 {
|
|
607
|
+
assertRun(run);
|
|
608
|
+
if (run.state !== "pending" || event.index !== run.provenPartCount) {
|
|
609
|
+
throw new Error("messaging run transition is outside the active prefix");
|
|
610
|
+
}
|
|
611
|
+
const current = run.parts[event.index];
|
|
612
|
+
if (current === undefined) throw new Error("messaging run transition index is invalid");
|
|
613
|
+
let nextState: MessagingPartJournalStateV1;
|
|
614
|
+
let providerMessageId = current.providerMessageId;
|
|
615
|
+
let providerRevision = current.providerRevision;
|
|
616
|
+
let state: MessagingRunV1["state"] = run.state;
|
|
617
|
+
let provenPartCount = run.provenPartCount;
|
|
618
|
+
let observedAcceptedPrefixCount = run.observedAcceptedPrefixCount;
|
|
619
|
+
let possibleSubmittedPartIndex = run.possibleSubmittedPartIndex;
|
|
620
|
+
let privateProviderOutcome = run.privateProviderOutcome;
|
|
621
|
+
let terminalReason = run.terminalReason;
|
|
622
|
+
if (event.type === "claimed") {
|
|
623
|
+
if (current.state !== "unattempted") throw new Error("messaging part was already claimed");
|
|
624
|
+
if (
|
|
625
|
+
!Number.isSafeInteger(event.observedAcceptedPrefixCount)
|
|
626
|
+
|| event.observedAcceptedPrefixCount < observedAcceptedPrefixCount
|
|
627
|
+
|| event.observedAcceptedPrefixCount > run.provenPartCount
|
|
628
|
+
) throw new Error("messaging accepted-prefix observation is not monotonic");
|
|
629
|
+
observedAcceptedPrefixCount = event.observedAcceptedPrefixCount;
|
|
630
|
+
nextState = "claimed";
|
|
631
|
+
} else if (event.type === "dispatching") {
|
|
632
|
+
if (current.state !== "claimed") throw new Error("messaging part was not claimed");
|
|
633
|
+
nextState = "dispatching";
|
|
634
|
+
} else if (event.type === "accepted") {
|
|
635
|
+
if (current.state !== "dispatching") throw new Error("messaging part did not cross dispatch");
|
|
636
|
+
nextState = "accepted";
|
|
637
|
+
providerMessageId = event.providerMessageId;
|
|
638
|
+
providerRevision = event.providerRevision;
|
|
639
|
+
provenPartCount += 1;
|
|
640
|
+
if (provenPartCount === run.partCount) state = "submitted";
|
|
641
|
+
} else if (event.type === "categorical-stop") {
|
|
642
|
+
if (current.state !== "unattempted" && current.state !== "claimed") {
|
|
643
|
+
throw new Error("categorical stop crossed the dispatch boundary");
|
|
644
|
+
}
|
|
645
|
+
nextState = event.partState;
|
|
646
|
+
state = provenPartCount === 0 ? "failed" : "partial";
|
|
647
|
+
terminalReason = event.reason;
|
|
648
|
+
} else {
|
|
649
|
+
if (current.state !== "dispatching" && current.state !== "claimed") {
|
|
650
|
+
throw new Error("indeterminate part was not active");
|
|
651
|
+
}
|
|
652
|
+
if (
|
|
653
|
+
event.reason === "provider-result-indeterminate"
|
|
654
|
+
&& event.privateProviderOutcome !== null
|
|
655
|
+
&& current.state !== "dispatching"
|
|
656
|
+
) throw new Error("private provider outcome did not cross dispatch");
|
|
657
|
+
nextState = "indeterminate";
|
|
658
|
+
state = "indeterminate";
|
|
659
|
+
possibleSubmittedPartIndex = event.index;
|
|
660
|
+
terminalReason = event.reason;
|
|
661
|
+
privateProviderOutcome = event.reason === "provider-result-indeterminate"
|
|
662
|
+
? event.privateProviderOutcome
|
|
663
|
+
: null;
|
|
664
|
+
}
|
|
665
|
+
const parts = run.parts.map((part, index) => index === event.index
|
|
666
|
+
? Object.freeze({ ...part, state: nextState, providerMessageId, providerRevision })
|
|
667
|
+
: part);
|
|
668
|
+
if (Date.parse(event.at) < Date.parse(run.recordedAt)) {
|
|
669
|
+
throw new Error("messaging run transition time moved backward");
|
|
670
|
+
}
|
|
671
|
+
return parseRun({
|
|
672
|
+
...run,
|
|
673
|
+
state,
|
|
674
|
+
provenPartCount,
|
|
675
|
+
observedAcceptedPrefixCount,
|
|
676
|
+
possibleSubmittedPartIndex,
|
|
677
|
+
privateProviderOutcome,
|
|
678
|
+
terminalReason,
|
|
679
|
+
parts,
|
|
680
|
+
recordedAt: timestamp(event.at),
|
|
681
|
+
});
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
export function updateMessagingRun(
|
|
685
|
+
snapshot: MessagingRunSnapshotV1,
|
|
686
|
+
event: MessagingRunEventV1,
|
|
687
|
+
environment: Environment = process.env,
|
|
688
|
+
): MessagingRunSnapshotV1 {
|
|
689
|
+
const run = transitionMessagingRun(snapshot.run, event);
|
|
690
|
+
const envelope = sealed(run, environment);
|
|
691
|
+
const encoded = `${canonicalJson(envelope)}\n`;
|
|
692
|
+
if (!writePrivateJsonIfUnchanged(path(run.runId, environment), envelope, {
|
|
693
|
+
expectedCurrentContentSha256: snapshot.contentSha256,
|
|
694
|
+
maximumExpectedCurrentBytes: MAX_ENCRYPTED_BYTES,
|
|
695
|
+
})) throw new Error("messaging run changed concurrently");
|
|
696
|
+
return Object.freeze({ run, contentSha256: sha256(encoded) });
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
export function messagingReceiptBinding(run: MessagingRunV1): MessagingReceiptBinding {
|
|
700
|
+
if (run.state === "pending") throw new Error("pending messaging run has no terminal receipt binding");
|
|
701
|
+
if (run.contextBindingSha256 === null) {
|
|
702
|
+
if (run.sourceConversationCoordinateSha256 !== null) {
|
|
703
|
+
throw new Error("messaging run has partial context evidence");
|
|
704
|
+
}
|
|
705
|
+
const predecessorWithoutReceipt = Object.freeze({
|
|
706
|
+
schemaVersion: 1 as const,
|
|
707
|
+
format: "wrench.messaging-receipt-binding" as const,
|
|
708
|
+
contractId: MESSAGING_RECEIPT_BINDING_V1_CONTRACT_ID,
|
|
709
|
+
contractHash: MESSAGING_RECEIPT_BINDING_V1_CONTRACT_HASH,
|
|
710
|
+
clientIntentSha256: run.clientIntentSha256,
|
|
711
|
+
routeRefSha256: sha256(run.routeRef),
|
|
712
|
+
contextRefSha256: sha256(run.contextRef),
|
|
713
|
+
turnDigest: run.turnDigest,
|
|
714
|
+
previewDigest: run.previewDigest,
|
|
715
|
+
runId: run.runId,
|
|
716
|
+
state: run.state,
|
|
717
|
+
partCount: run.partCount,
|
|
718
|
+
provenPartCount: run.provenPartCount,
|
|
719
|
+
recordedAt: run.recordedAt,
|
|
720
|
+
});
|
|
721
|
+
return parseMessagingReceiptBindingV1({
|
|
722
|
+
...predecessorWithoutReceipt,
|
|
723
|
+
receiptSha256: sha256(canonicalJson(predecessorWithoutReceipt)),
|
|
724
|
+
});
|
|
725
|
+
}
|
|
726
|
+
if (run.sourceConversationCoordinateSha256 === null) {
|
|
727
|
+
throw new Error("messaging run has partial context evidence");
|
|
728
|
+
}
|
|
729
|
+
const withoutReceipt = Object.freeze({
|
|
730
|
+
schemaVersion: 2 as const,
|
|
731
|
+
format: "wrench.messaging-receipt-binding" as const,
|
|
732
|
+
contractId: MESSAGING_RECEIPT_BINDING_CONTRACT_ID,
|
|
733
|
+
contractHash: MESSAGING_RECEIPT_BINDING_CONTRACT_HASH,
|
|
734
|
+
clientIntentSha256: run.clientIntentSha256,
|
|
735
|
+
contextBindingSha256: run.contextBindingSha256,
|
|
736
|
+
sourceConversationCoordinateSha256:
|
|
737
|
+
run.sourceConversationCoordinateSha256,
|
|
738
|
+
routeRefSha256: sha256(run.routeRef),
|
|
739
|
+
contextRefSha256: sha256(run.contextRef),
|
|
740
|
+
turnDigest: run.turnDigest,
|
|
741
|
+
previewDigest: run.previewDigest,
|
|
742
|
+
runId: run.runId,
|
|
743
|
+
state: run.state,
|
|
744
|
+
partCount: run.partCount,
|
|
745
|
+
provenPartCount: run.provenPartCount,
|
|
746
|
+
recordedAt: run.recordedAt,
|
|
747
|
+
});
|
|
748
|
+
return parseMessagingReceiptBindingV2({
|
|
749
|
+
...withoutReceipt,
|
|
750
|
+
receiptSha256: sha256(canonicalJson(withoutReceipt)),
|
|
751
|
+
});
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
export function messagingRunReceipt(run: MessagingRunV1): MessagingRunReceipt {
|
|
755
|
+
const binding = messagingReceiptBinding(run);
|
|
756
|
+
if (binding.schemaVersion === 1) {
|
|
757
|
+
return Object.freeze({
|
|
758
|
+
schemaVersion: 1,
|
|
759
|
+
format: "wrench.messaging-run-receipt",
|
|
760
|
+
planDigest: run.planDigest,
|
|
761
|
+
runId: run.runId,
|
|
762
|
+
state: binding.state,
|
|
763
|
+
partCount: binding.partCount,
|
|
764
|
+
provenPartCount: binding.provenPartCount,
|
|
765
|
+
clientIntentSha256: binding.clientIntentSha256,
|
|
766
|
+
routeRefSha256: binding.routeRefSha256,
|
|
767
|
+
contextRefSha256: binding.contextRefSha256,
|
|
768
|
+
turnDigest: binding.turnDigest,
|
|
769
|
+
previewDigest: binding.previewDigest,
|
|
770
|
+
receiptBindingSha256: binding.receiptSha256,
|
|
771
|
+
recordedAt: binding.recordedAt,
|
|
772
|
+
});
|
|
773
|
+
}
|
|
774
|
+
return Object.freeze({
|
|
775
|
+
schemaVersion: 2,
|
|
776
|
+
format: "wrench.messaging-run-receipt",
|
|
777
|
+
planDigest: run.planDigest,
|
|
778
|
+
runId: run.runId,
|
|
779
|
+
state: binding.state,
|
|
780
|
+
partCount: binding.partCount,
|
|
781
|
+
provenPartCount: binding.provenPartCount,
|
|
782
|
+
clientIntentSha256: binding.clientIntentSha256,
|
|
783
|
+
contextBindingSha256: binding.contextBindingSha256,
|
|
784
|
+
sourceConversationCoordinateSha256:
|
|
785
|
+
binding.sourceConversationCoordinateSha256,
|
|
786
|
+
routeRefSha256: binding.routeRefSha256,
|
|
787
|
+
contextRefSha256: binding.contextRefSha256,
|
|
788
|
+
turnDigest: binding.turnDigest,
|
|
789
|
+
previewDigest: binding.previewDigest,
|
|
790
|
+
receiptBindingSha256: binding.receiptSha256,
|
|
791
|
+
recordedAt: binding.recordedAt,
|
|
792
|
+
});
|
|
793
|
+
}
|