@fased/fased 0.1.62 → 0.1.64

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 (341) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/README.md +38 -30
  3. package/config/dependency-ownership.json +1 -1
  4. package/config/signer-policies/README.md +49 -0
  5. package/config/signer-policies/agent.json.template +32 -0
  6. package/config/signer-policies/mining.json.template +69 -0
  7. package/config/signer-policies/network.json.template +5 -0
  8. package/config/signer-policies/vault.json.template +70 -0
  9. package/config/signer-protocol-v2.json +76 -0
  10. package/config/typescript-noemit-baseline.json +680 -0
  11. package/dist/agents/auth-profiles/store.js +267 -31
  12. package/dist/agents/bash-tools.exec.js +10 -4
  13. package/dist/agents/current-model-catalog.js +3 -3
  14. package/dist/agents/memory-search.js +7 -1
  15. package/dist/agents/model-catalog-access.js +1 -2
  16. package/dist/agents/model-catalog-snapshot.js +1 -1
  17. package/dist/agents/model-catalog-source.js +1 -1
  18. package/dist/agents/model-metadata.js +1 -1
  19. package/dist/agents/model-selection.js +2 -28
  20. package/dist/agents/models-config.providers.js +6 -6
  21. package/dist/agents/pi-embedded-runner/compact.js +15 -7
  22. package/dist/agents/pi-embedded-runner/model.js +1 -1
  23. package/dist/agents/pi-embedded-runner/run/attempt.js +24 -13
  24. package/dist/agents/pi-embedded-runner/run.js +2 -0
  25. package/dist/agents/pi-embedded-runner/tool-split.js +40 -2
  26. package/dist/agents/pi-tool-definition-adapter.js +16 -1
  27. package/dist/agents/pi-tools.js +2 -1
  28. package/dist/agents/sandbox/config.js +1 -1
  29. package/dist/agents/sandbox/context.js +27 -2
  30. package/dist/agents/skills/env-overrides.js +38 -28
  31. package/dist/agents/skills/trust.js +93 -0
  32. package/dist/agents/skills/workspace.js +128 -0
  33. package/dist/agents/skills-clawhub.js +36 -11
  34. package/dist/agents/skills-marketplace-scan.js +49 -0
  35. package/dist/agents/tools/marketplace-offer-draft-tool.js +1 -1
  36. package/dist/agents/tools/mining-tool.js +1 -1
  37. package/dist/agents/tools/session-status-tool.js +1 -1
  38. package/dist/agents/tools/wallet-action-tool.js +172 -36
  39. package/dist/agents/tools/wallet-skill-policy.js +65 -23
  40. package/dist/agents/tools/wallet-tool.js +7 -11
  41. package/dist/auto-reply/reply/directive-handling.fast-lane.js +2 -1
  42. package/dist/auto-reply/reply/directive-handling.impl.js +2 -1
  43. package/dist/auto-reply/reply/directive-handling.model.js +2 -1
  44. package/dist/auto-reply/reply/directive-handling.persist.js +2 -2
  45. package/dist/auto-reply/reply/get-reply-directives-apply.js +2 -0
  46. package/dist/auto-reply/reply/get-reply-inline-actions.js +25 -1
  47. package/dist/auto-reply/reply/model-selection.js +7 -4
  48. package/dist/auto-reply/reply/session-reset-model.js +4 -4
  49. package/dist/brand.js +1 -1
  50. package/dist/build-info.json +3 -3
  51. package/dist/canvas-host/a2ui/.bundle.hash +1 -1
  52. package/dist/channel-web.js +2 -2
  53. package/dist/cli/gateway-cli/run.js +8 -0
  54. package/dist/cli/program/command-registry.js +1 -1
  55. package/dist/cli/program/register.onboard.js +2 -3
  56. package/dist/cli/program/register.wallet.js +16 -123
  57. package/dist/cli/providers-cli.js +1 -1
  58. package/dist/cli/sat-cli.js +129 -2
  59. package/dist/cli/update-cli/update-command.js +260 -11
  60. package/dist/commands/auth-choice.apply.api-providers.js +3 -3
  61. package/dist/commands/doctor-config-flow.js +1 -1
  62. package/dist/commands/doctor-legacy-config.js +2 -13
  63. package/dist/commands/model-picker.js +6 -6
  64. package/dist/commands/models/auth.js +1 -1
  65. package/dist/commands/onboard-auth.config-gateways.js +1 -1
  66. package/dist/commands/onboard-auth.credentials.js +32 -8
  67. package/dist/commands/onboard-non-interactive/local/wallet-config.js +28 -27
  68. package/dist/commands/onboard-non-interactive/local.js +5 -1
  69. package/dist/commands/wallet.js +568 -1911
  70. package/dist/config/config.js +2 -2
  71. package/dist/config/io.js +64 -2
  72. package/dist/config/schema.help.js +8 -7
  73. package/dist/config/schema.labels.js +5 -4
  74. package/dist/config/zod-schema.agent-runtime.js +1 -0
  75. package/dist/config/zod-schema.js +5 -0
  76. package/dist/control-ui/assets/{app-DLil9VO1.js → app-bR-fO5ty.js} +1511 -1555
  77. package/dist/control-ui/assets/{config-CU5Zi8Dh.js → config-LeImukCW.js} +10 -10
  78. package/dist/control-ui/assets/{federation-D10Q4eP_.js → federation-DgA9e-Pn.js} +364 -422
  79. package/dist/control-ui/assets/{index-DdE_NIXo.js → index-ckCxHwjy.js} +3 -3
  80. package/dist/control-ui/assets/{mining-BdhMn18r.js → mining-BgHFh0ei.js} +134 -114
  81. package/dist/control-ui/assets/mining-commit-DFpB_4JN.js +2 -0
  82. package/dist/control-ui/assets/{open-external-url-DwSiwMPl.js → open-external-url-ulF1B-sA.js} +3 -3
  83. package/dist/control-ui/assets/{providers-QMn4w2_f.js → providers-CIx9fxc6.js} +2 -2
  84. package/dist/control-ui/assets/{wallet-C2g294QT.js → wallet-DvStR_AT.js} +259 -377
  85. package/dist/control-ui/index.html +1 -1
  86. package/dist/control-ui/version.json +1 -1
  87. package/dist/daemon/systemd-system.js +56 -50
  88. package/dist/extensions/sat-mining/src/protocol-contract.js +43 -3
  89. package/dist/extensions/sat-mining/src/rpc-read.js +235 -67
  90. package/dist/extensions/sat-mining/src/sat-bond-layout.js +5 -2
  91. package/dist/extensions/sat-mining/src/signer-codec-manifest.js +406 -0
  92. package/dist/extensions/sat-mining/src/solana-submit.js +1038 -397
  93. package/dist/extensions/sat-mining/src/submission-ledger.js +349 -0
  94. package/dist/federation/auto-connect.js +98 -8
  95. package/dist/federation/marketplace-capability-adapter.js +33 -7
  96. package/dist/federation/marketplace-delivery-outbox.js +196 -0
  97. package/dist/federation/marketplace-delivery.js +351 -215
  98. package/dist/federation/marketplace-direct-payment.js +272 -141
  99. package/dist/federation/marketplace-escrow.js +607 -249
  100. package/dist/federation/marketplace-settlement-ledger.js +352 -0
  101. package/dist/federation/offers.js +9 -7
  102. package/dist/federation/peer-auth-v2.js +529 -0
  103. package/dist/gateway/a2a-http.js +425 -96
  104. package/dist/gateway/a2a-settlement.js +259 -153
  105. package/dist/gateway/a2a-task-store.js +524 -0
  106. package/dist/gateway/auth.js +38 -2
  107. package/dist/gateway/config-reload.js +46 -33
  108. package/dist/gateway/durable-capacity.js +31 -0
  109. package/dist/gateway/federation-http.js +540 -135
  110. package/dist/gateway/federation-marketplace-run-store.js +397 -0
  111. package/dist/gateway/federation-marketplace.js +535 -230
  112. package/dist/gateway/mutating-admin-rpc-rate-limit.js +5 -0
  113. package/dist/gateway/security-path.js +1 -1
  114. package/dist/gateway/server-http.js +1084 -1205
  115. package/dist/gateway/server-methods/models.js +1 -1
  116. package/dist/gateway/server-methods/secrets.js +40 -0
  117. package/dist/gateway/server-methods.js +1 -2
  118. package/dist/gateway/server-reload-handlers.js +196 -66
  119. package/dist/gateway/server-runtime-config.js +1 -1
  120. package/dist/gateway/server-secrets-runtime.js +288 -0
  121. package/dist/gateway/server.impl.js +73 -42
  122. package/dist/gateway/sessions-patch.js +3 -5
  123. package/dist/index.js +4 -2
  124. package/dist/infra/fasedhub.js +64 -1
  125. package/dist/infra/local-source-paired-update.js +538 -0
  126. package/dist/infra/restart.js +41 -10
  127. package/dist/infra/update-runner.js +96 -0
  128. package/dist/light-update-status.js +11 -11
  129. package/dist/memory/backend-config.js +2 -1
  130. package/dist/memory/session-files.js +16 -2
  131. package/dist/plugin-sdk/{accounts-DxblobYM.js → accounts-Brn3-uWN.js} +1 -1
  132. package/dist/plugin-sdk/{actions-D4UNimWa.js → actions-DRu135h0.js} +2 -2
  133. package/dist/plugin-sdk/{active-listener-CFuy106s.js → active-listener-B_NMtgPL.js} +1 -1
  134. package/dist/plugin-sdk/{audio-preflight-Bwi2Oq03.js → audio-preflight-BYxAt775.js} +27 -27
  135. package/dist/plugin-sdk/{audit-Cs0K7LBp.js → audit-C5d2z5U1.js} +1 -1
  136. package/dist/plugin-sdk/{channel-access-D5aHoTY8.js → channel-access-CcFRdRPO.js} +1 -1
  137. package/dist/plugin-sdk/{channel-access-configure-Dvch9mMQ.js → channel-access-configure-DaI5wevh.js} +1 -1
  138. package/dist/plugin-sdk/channel-plugin-common.d.ts +3 -3
  139. package/dist/plugin-sdk/channel-plugin-common.js +4 -4
  140. package/dist/plugin-sdk/{chunk-B0FUGc5A.js → chunk-CyoyqpbG.js} +1 -1
  141. package/dist/plugin-sdk/{chunk-items-DzT4L4Ty.js → chunk-items-DIRHG2gJ.js} +1 -1
  142. package/dist/plugin-sdk/command-status.d.ts +2 -2
  143. package/dist/plugin-sdk/command-status.js +27 -27
  144. package/dist/plugin-sdk/{commands-registry-DmDRhRAU.js → commands-registry-BfqdEWdF.js} +2 -2
  145. package/dist/plugin-sdk/{common-CfUOowOp.js → common-HCcXU-NR.js} +1 -1
  146. package/dist/plugin-sdk/{config-mxULv0fQ.js → config-DlWVeLFB.js} +7113 -6877
  147. package/dist/plugin-sdk/{config-schema-BrkmtFxB.js → config-schema-BOT7kHb6.js} +6779 -3696
  148. package/dist/plugin-sdk/device-pair.d.ts +1 -1
  149. package/dist/plugin-sdk/discord.d.ts +6 -6
  150. package/dist/plugin-sdk/discord.js +19 -19
  151. package/dist/plugin-sdk/file-lock.js +35 -4
  152. package/dist/plugin-sdk/{filesystem-manager-ZoHU8oA7.js → filesystem-manager-DoI8IBNF.js} +1 -1
  153. package/dist/plugin-sdk/{group-mentions-BHRff6j8.d.ts → group-mentions-q3hUTCt7.d.ts} +1 -1
  154. package/dist/plugin-sdk/{handle-action-DbVCF9HZ.js → handle-action-hvwapMKg.js} +13 -13
  155. package/dist/plugin-sdk/{helpers-BXbdgs5H.js → helpers-DMBU4oTo.js} +1 -1
  156. package/dist/plugin-sdk/{image-ops-D7jPxaph.js → image-ops-DQi0n_Tt.js} +1 -1
  157. package/dist/plugin-sdk/index.d.ts +21 -39
  158. package/dist/plugin-sdk/index.js +29 -29
  159. package/dist/plugin-sdk/{login-BRxww6nv.js → login-Ca583Okc.js} +3 -3
  160. package/dist/plugin-sdk/{login-qr-DJXsk9XH.js → login-qr-CGciBSNP.js} +3 -3
  161. package/dist/plugin-sdk/{manager-Y2S_5IlI.js → manager-BegWxNYX.js} +4 -4
  162. package/dist/plugin-sdk/{markdown-tables-DHLFQqaF.js → markdown-tables-DCOXEUfZ.js} +1 -1
  163. package/dist/plugin-sdk/{media-Czo4carg.js → media-CAdQF5TG.js} +47 -47
  164. package/dist/plugin-sdk/{memory-search-DTjIMw8J.js → memory-search-CrFPOeTH.js} +4 -1
  165. package/dist/plugin-sdk/{monitor-M_ta2lMc.js → monitor-BCFDaqJ-.js} +31 -31
  166. package/dist/plugin-sdk/{monitor-wYVncM9w.js → monitor-BgbjRiPM.js} +32 -32
  167. package/dist/plugin-sdk/{openresponses-http-B2GmeSt4.js → openresponses-http-DFf87SMM.js} +27 -27
  168. package/dist/plugin-sdk/{optional-runtime-dependency-DdMbxS24.js → optional-runtime-dependency-0dznm3ZO.js} +1 -1
  169. package/dist/plugin-sdk/{outbound-Db8mRN6P.js → outbound-BgVDdHxx.js} +6 -6
  170. package/dist/plugin-sdk/{outbound-attachment-BqFjcypb.js → outbound-attachment-QqTnIhZL.js} +2 -2
  171. package/dist/plugin-sdk/{pairing-message-B3bHYGaw.d.ts → pairing-message-CS-9BHug.d.ts} +2 -2
  172. package/dist/plugin-sdk/{pairing-message-B3GC_7CD.js → pairing-message-D0CoLHRJ.js} +1 -1
  173. package/dist/plugin-sdk/{pairing-store-CQikgIbw.js → pairing-store-BChahgI2.js} +2 -2
  174. package/dist/plugin-sdk/{pi-model-discovery-CY3m5zJP.js → pi-model-discovery-BX_JLcEJ.js} +1 -1
  175. package/dist/plugin-sdk/{pi-model-discovery-runtime-DSh1T2pn.js → pi-model-discovery-runtime-DxTMz_XF.js} +2 -2
  176. package/dist/plugin-sdk/provider-web-search-config-contract.d.ts +2 -2
  177. package/dist/plugin-sdk/{pw-ai-AFUqn-tQ.js → pw-ai-natjh3rm.js} +3 -3
  178. package/dist/plugin-sdk/{qmd-manager-DSyx77KY.js → qmd-manager-CgRaTxUq.js} +1 -1
  179. package/dist/plugin-sdk/{query-expansion-CAI2kKLQ.js → query-expansion-BG3FgzF7.js} +14 -3
  180. package/dist/plugin-sdk/{replies-Fzh9PhXl.js → replies-ChYo5ZQM.js} +2 -2
  181. package/dist/plugin-sdk/{resolve-outbound-target-KpF3co6T.js → resolve-outbound-target-D-KGgHJZ.js} +1 -1
  182. package/dist/plugin-sdk/{resolve-route-CDl74O19.js → resolve-route-C8tvQm8Z.js} +1 -1
  183. package/dist/plugin-sdk/{retry-policy-D4uiqUOG.js → retry-policy-C6SjKjQA.js} +1 -1
  184. package/dist/plugin-sdk/{runtime-Bb6KRnCC.js → runtime-2B9_WKoI.js} +8 -3
  185. package/dist/plugin-sdk/{runtime-dependency-DLDmB94f.js → runtime-dependency-DczkvPxn.js} +2 -2
  186. package/dist/plugin-sdk/sat-runtime.d.ts +549 -309
  187. package/dist/plugin-sdk/sat-runtime.js +29 -29
  188. package/dist/plugin-sdk/{send-C4k4JuJN.js → send-B6KkGKZD.js} +6 -6
  189. package/dist/plugin-sdk/{send-D-uDKGIq.js → send-CFzGKJXo.js} +6 -6
  190. package/dist/plugin-sdk/{send-Du8ykP1k.js → send-DCO441U7.js} +8 -8
  191. package/dist/plugin-sdk/{send-5nFktR1n.js → send-gDoGA-7e.js} +5 -5
  192. package/dist/plugin-sdk/{send-Bfzf11VY.js → send-q9HXrSwK.js} +7 -7
  193. package/dist/plugin-sdk/{session-CS-Td9EM.js → session-DHD4ZKyG.js} +2 -2
  194. package/dist/plugin-sdk/{skill-commands-CFAlXWHk.js → skill-commands-FKEPLS7T.js} +201 -26
  195. package/dist/plugin-sdk/{slack-CxE1mfzj.js → slack-BUTt8cxK.js} +39 -39
  196. package/dist/plugin-sdk/{slack-actions-BcMkNv1Z.js → slack-actions-DhbMcp8M.js} +11 -11
  197. package/dist/plugin-sdk/slack.d.ts +6 -6
  198. package/dist/plugin-sdk/slack.js +9 -9
  199. package/dist/plugin-sdk/{sticker-cache-CGzGWu00.js → sticker-cache-D0wHVqjt.js} +3 -3
  200. package/dist/plugin-sdk/{tables-bFXyRpkx.js → tables-B3jr0Pvq.js} +1 -1
  201. package/dist/plugin-sdk/{targets-DH7a9lKB.js → targets-CtSbTxYX.js} +1 -1
  202. package/dist/plugin-sdk/{telegram-actions-DaEcYQ8r.js → telegram-actions-CC_qN4v0.js} +30 -30
  203. package/dist/plugin-sdk/telegram.d.ts +6 -6
  204. package/dist/plugin-sdk/telegram.js +8 -8
  205. package/dist/plugin-sdk/{thread-bindings.lifecycle-CgOvvdw8.js → thread-bindings.lifecycle-QbkNl-m6.js} +3 -3
  206. package/dist/plugin-sdk/{thread-bindings.state-CQpk44Hq.js → thread-bindings.state-D9hg_M1c.js} +2 -2
  207. package/dist/plugin-sdk/tmp-fased-dir-xOTGlzBE.d.ts +486 -0
  208. package/dist/plugin-sdk/{types-vcJrC_uv.d.ts → types-C3avNytw.d.ts} +3 -4
  209. package/dist/plugin-sdk/{types-DyhXnYUr.d.ts → types-D0kDG8l0.d.ts} +3 -2
  210. package/dist/plugin-sdk/{types-DFAW-n1J.d.ts → types-DWfbSAaM.d.ts} +2 -2
  211. package/dist/plugin-sdk/{types.plugin-_fbu40Re.d.ts → types.plugin-BcSBq42x.d.ts} +8 -3
  212. package/dist/plugin-sdk/{web-CInY-lCv.js → web-BXsJ1vqe.js} +32 -32
  213. package/dist/plugin-sdk/{whatsapp-actions-BtSc2CBv.js → whatsapp-actions-Bj_rOncr.js} +13 -13
  214. package/dist/plugin-sdk/{whatsapp-heartbeat-Bg9TuYzl.js → whatsapp-heartbeat-B1WHkJIg.js} +2 -2
  215. package/dist/plugin-sdk/whatsapp.d.ts +13 -13
  216. package/dist/plugin-sdk/whatsapp.js +12 -12
  217. package/dist/plugin-sdk/{wsl-C4DGFkOd.js → wsl-J-HscWxS.js} +3 -3
  218. package/dist/plugin-sdk/{zod-schema.providers-core-BEbNizPe.d.ts → zod-schema.providers-core-CIzCjXH9.d.ts} +61 -61
  219. package/dist/providers/refresh.js +1 -1
  220. package/dist/providers/registry.js +1 -1
  221. package/dist/secrets/apply.js +1 -1
  222. package/dist/secrets/runtime.js +1 -1
  223. package/dist/wallet/external-submission-ledger.js +376 -0
  224. package/dist/wallet/legacy-embedded-keystore.js +40 -0
  225. package/dist/wallet/local-socket-signer-archive.js +30 -0
  226. package/dist/wallet/local-socket-signer-lifecycle.js +97 -0
  227. package/dist/wallet/local-socket-signer-policy.js +166 -0
  228. package/dist/wallet/local-socket-signer-protocol.js +561 -118
  229. package/dist/wallet/native-signer-wallet-id.js +25 -0
  230. package/dist/wallet/providers/alchemy-adapter.js +3 -0
  231. package/dist/wallet/providers/local-socket-signer-adapter.js +380 -105
  232. package/dist/wallet/providers/turnkey-adapter.js +292 -34
  233. package/dist/wallet/providers/wallet-standard-adapter.js +100 -0
  234. package/dist/wallet/signer-network-admin.js +184 -0
  235. package/dist/wallet/signer-protocol-v2.generated.js +90 -0
  236. package/dist/wallet/solana-bond-signing.js +157 -69
  237. package/dist/wallet/solana-reviewed-transaction.js +375 -0
  238. package/dist/wallet/solana-swap.js +520 -64
  239. package/dist/wallet/solana-transaction-inspection.js +106 -4
  240. package/dist/wallet/solana-trigger.js +658 -320
  241. package/dist/wallet/turnkey-reviewed-state.js +370 -0
  242. package/dist/wallet/wallet-approval-auth.js +17 -28
  243. package/dist/wallet/wallet-atomic-state.js +53 -0
  244. package/dist/wallet/wallet-canary.js +0 -1
  245. package/dist/wallet/wallet-inbound-events.js +16 -11
  246. package/dist/wallet/wallet-policy-simulation.js +1 -1
  247. package/dist/wallet/wallet-policy.js +90 -36
  248. package/dist/wallet/wallet-provider-capabilities.js +28 -4
  249. package/dist/wallet/wallet-provider-registry.js +57 -44
  250. package/dist/wallet/wallet-provider-resolver.js +62 -63
  251. package/dist/wallet/wallet-runtime-config.js +28 -41
  252. package/dist/wallet/wallet-secrets-store.js +22 -0
  253. package/dist/wallet/wallet-send-approvals.js +1891 -589
  254. package/dist/wallet/wallet-send-execution-ledger.js +292 -0
  255. package/dist/wallet/wallet-settlement-links.js +14 -13
  256. package/dist/wallet/wallet-standard-review.js +353 -0
  257. package/dist/wallet/wallet-status.js +0 -9
  258. package/dist/wallet/wallet-task-ledger.js +16 -2
  259. package/dist/web/auto-reply/heartbeat-runner.js +1 -1
  260. package/dist/web/auto-reply/monitor.js +2 -2
  261. package/dist/wizard/host-security-capability.js +14 -23
  262. package/dist/wizard/onboarding.finalize.js +63 -184
  263. package/dist/wizard/onboarding.host-security.js +117 -1367
  264. package/dist/wizard/onboarding.js +172 -217
  265. package/dist/wizard/onboarding.wallet.js +232 -785
  266. package/extensions/acpx/package.json +1 -1
  267. package/extensions/bluebubbles/package.json +1 -1
  268. package/extensions/copilot-proxy/package.json +1 -1
  269. package/extensions/diagnostics-otel/package.json +1 -1
  270. package/extensions/fased-federation/package.json +1 -1
  271. package/extensions/google-gemini-cli-auth/package.json +1 -1
  272. package/extensions/imessage/package.json +1 -1
  273. package/extensions/irc/package.json +1 -1
  274. package/extensions/line/package.json +1 -1
  275. package/extensions/llm-task/package.json +1 -1
  276. package/extensions/lobster/package.json +1 -1
  277. package/extensions/matrix/CHANGELOG.md +12 -0
  278. package/extensions/matrix/package.json +1 -1
  279. package/extensions/mattermost/package.json +1 -1
  280. package/extensions/memory-core/package.json +2 -2
  281. package/extensions/memory-lancedb/package.json +1 -1
  282. package/extensions/minimax-portal-auth/package.json +1 -1
  283. package/extensions/msteams/CHANGELOG.md +12 -0
  284. package/extensions/msteams/package.json +1 -1
  285. package/extensions/nextcloud-talk/package.json +1 -1
  286. package/extensions/nostr/CHANGELOG.md +12 -0
  287. package/extensions/nostr/package.json +1 -1
  288. package/extensions/open-prose/package.json +1 -1
  289. package/extensions/sat-mining/index.ts +498 -153
  290. package/extensions/sat-mining/package.json +1 -1
  291. package/extensions/sat-mining/signer-codec-schema.v1.json +374 -0
  292. package/extensions/sat-mining/src/audit-store.ts +50 -5
  293. package/extensions/sat-mining/src/claim-service.ts +85 -14
  294. package/extensions/sat-mining/src/config.ts +9 -0
  295. package/extensions/sat-mining/src/cycle-progress.ts +16 -8
  296. package/extensions/sat-mining/src/epoch-service.ts +9 -5
  297. package/extensions/sat-mining/src/gateway-runner.ts +21 -1
  298. package/extensions/sat-mining/src/protocol-contract.ts +45 -1
  299. package/extensions/sat-mining/src/recovery-service.ts +312 -0
  300. package/extensions/sat-mining/src/round-watcher.ts +391 -81
  301. package/extensions/sat-mining/src/rpc-read.ts +330 -89
  302. package/extensions/sat-mining/src/runtime.ts +14 -0
  303. package/extensions/sat-mining/src/sat-bond-layout.ts +6 -0
  304. package/extensions/sat-mining/src/signer-codec-manifest.ts +429 -0
  305. package/extensions/sat-mining/src/solana-submit.ts +1199 -428
  306. package/extensions/sat-mining/src/submission-ledger.ts +433 -0
  307. package/extensions/signal/package.json +1 -1
  308. package/extensions/synology-chat/package.json +1 -1
  309. package/extensions/tlon/package.json +1 -1
  310. package/extensions/twitch/CHANGELOG.md +12 -0
  311. package/extensions/twitch/package.json +1 -1
  312. package/extensions/voice-call/CHANGELOG.md +12 -0
  313. package/extensions/voice-call/package.json +1 -1
  314. package/extensions/zalo/CHANGELOG.md +12 -0
  315. package/extensions/zalo/package.json +1 -1
  316. package/extensions/zalouser/CHANGELOG.md +12 -0
  317. package/extensions/zalouser/package.json +1 -1
  318. package/install.sh +4955 -0
  319. package/package.json +23 -5
  320. package/scripts/fased-host-updater.mjs +1421 -0
  321. package/scripts/fased-host-updaterctl.mjs +186 -0
  322. package/scripts/fased-managed-service.sh +36 -0
  323. package/scripts/fased-managed-updater.mjs +3065 -156
  324. package/scripts/fased-signer-enroll-hosting.sh +275 -0
  325. package/scripts/fased-signer-network-hosting.sh +143 -0
  326. package/scripts/fased-signer-owner-policy.mjs +1430 -0
  327. package/scripts/fased-signer-policy-hosting.sh +50 -0
  328. package/scripts/fased-signer-policy-local.sh +26 -0
  329. package/scripts/hosted-release-manifest.mjs +185 -0
  330. package/scripts/install-fased-signerd.sh +183 -108
  331. package/scripts/install-managed-runtime.mjs +212 -10
  332. package/scripts/managed-runtime-layout.mjs +75 -3
  333. package/scripts/migrate-hosted-signer-v2.mjs +58 -0
  334. package/scripts/start-managed.sh +169 -360
  335. package/dist/control-ui/assets/mining-commit-R-6nu87p.js +0 -2
  336. package/dist/plugin-sdk/tmp-fased-dir-CS6zsYEE.d.ts +0 -77
  337. package/dist/wallet/local-socket-signer-broker.js +0 -224
  338. package/dist/wallet/local-socket-signer-custody.js +0 -42
  339. package/dist/wallet/providers/embedded-keystore-adapter.js +0 -402
  340. package/dist/wallet/providers/privy-adapter.js +0 -80
  341. package/dist/wallet/wallet-custody.js +0 -1836
@@ -0,0 +1,370 @@
1
+ import { withFileLock } from "../plugin-sdk/file-lock.js";
2
+ import "../infra/file-lock.js";
3
+ import { ensureWalletStateDir } from "./wallet-runtime-config.js";
4
+ import { WalletProviderError } from "./wallet-provider-adapter.js";
5
+ import { broadcastReviewedSolanaTransaction, buildReviewedSolanaTransaction, computeReviewedSolanaRequestDigest, reconcileReviewedSolanaTransaction, validateReviewedSolanaSignedTransaction } from "./solana-reviewed-transaction.js";
6
+ import syncFs from "node:fs";
7
+ import path from "node:path";
8
+ import { createHash, randomBytes, timingSafeEqual } from "node:crypto";
9
+
10
+ //#region src/wallet/turnkey-reviewed-state.ts
11
+ const STATE_FILE_NAME = "turnkey-reviews.v1.json";
12
+ const REVIEW_TTL_MS = 2 * 60 * 1e3;
13
+ const STATE_FILE_MODE = 384;
14
+ const MAX_REVIEW_RECORDS = 1e4;
15
+ const MAX_RETAINED_TERMINAL_REVIEWS = 5e3;
16
+ const TERMINAL_REVIEW_RETENTION_MS = 30 * 24 * 60 * 6e4;
17
+ const REVIEW_LOCK_OPTIONS = {
18
+ retries: {
19
+ retries: 80,
20
+ factor: 1.15,
21
+ minTimeout: 20,
22
+ maxTimeout: 200,
23
+ randomize: true
24
+ },
25
+ stale: 3e4
26
+ };
27
+ let reviewStateLock = Promise.resolve();
28
+ function statePath(env) {
29
+ return path.join(ensureWalletStateDir(env).rootDir, STATE_FILE_NAME);
30
+ }
31
+ function readFile(env) {
32
+ const filePath = statePath(env);
33
+ if (!syncFs.existsSync(filePath)) {
34
+ return {
35
+ version: 1,
36
+ reviews: []
37
+ };
38
+ }
39
+ try {
40
+ const parsed = JSON.parse(syncFs.readFileSync(filePath, "utf8"));
41
+ if (parsed.version === 1 && Array.isArray(parsed.reviews)) {
42
+ return parsed;
43
+ }
44
+ } catch {}
45
+ throw new WalletProviderError({
46
+ code: "wallet_provider_unavailable",
47
+ message: "Turnkey reviewed transaction state is invalid; repair it before signing",
48
+ retryable: false
49
+ });
50
+ }
51
+ function saveFile(file, env) {
52
+ const filePath = statePath(env);
53
+ const temporary = `${filePath}.${process.pid}.${randomBytes(6).toString("hex")}.tmp`;
54
+ let descriptor;
55
+ try {
56
+ descriptor = syncFs.openSync(temporary, "wx", STATE_FILE_MODE);
57
+ syncFs.writeFileSync(descriptor, `${JSON.stringify(file, null, 2)}\n`, "utf8");
58
+ syncFs.fsyncSync(descriptor);
59
+ syncFs.closeSync(descriptor);
60
+ descriptor = undefined;
61
+ syncFs.renameSync(temporary, filePath);
62
+ try {
63
+ syncFs.chmodSync(filePath, STATE_FILE_MODE);
64
+ } catch {}
65
+ try {
66
+ const directory = syncFs.openSync(path.dirname(filePath), "r");
67
+ try {
68
+ syncFs.fsyncSync(directory);
69
+ } finally {
70
+ syncFs.closeSync(directory);
71
+ }
72
+ } catch {}
73
+ } catch (error) {
74
+ if (descriptor !== undefined) {
75
+ syncFs.closeSync(descriptor);
76
+ }
77
+ try {
78
+ syncFs.unlinkSync(temporary);
79
+ } catch {}
80
+ throw error;
81
+ }
82
+ }
83
+ function secureEqual(left, right) {
84
+ const leftBytes = Buffer.from(left, "utf8");
85
+ const rightBytes = Buffer.from(right, "utf8");
86
+ return leftBytes.length === rightBytes.length && timingSafeEqual(leftBytes, rightBytes);
87
+ }
88
+ function compactReviewState(file, nowMs = Date.now()) {
89
+ const terminal = file.reviews.filter((review) => review.status === "broadcast" || review.status === "failed").toSorted((left, right) => Date.parse(left.createdAt) - Date.parse(right.createdAt));
90
+ const removable = new Set(terminal.filter((review, index) => nowMs - Date.parse(review.createdAt) > TERMINAL_REVIEW_RETENTION_MS || terminal.length - index > MAX_RETAINED_TERMINAL_REVIEWS).map((review) => review.preparedId));
91
+ return {
92
+ version: 1,
93
+ reviews: file.reviews.filter((review) => !removable.has(review.preparedId))
94
+ };
95
+ }
96
+ function requireReviewCapacity(file) {
97
+ if (file.reviews.length >= MAX_REVIEW_RECORDS) {
98
+ throw new WalletProviderError({
99
+ code: "wallet_provider_unavailable",
100
+ message: "Turnkey review state reached its durable safety limit; reconcile unresolved reviews",
101
+ retryable: false
102
+ });
103
+ }
104
+ }
105
+ function digest(value) {
106
+ return createHash("sha256").update(value, "utf8").digest("base64url");
107
+ }
108
+ function normalizedRpcUrlDigest(rpcUrl) {
109
+ let normalized;
110
+ try {
111
+ const parsed = new URL(rpcUrl.trim());
112
+ if (parsed.protocol !== "https:" && parsed.protocol !== "http:") {
113
+ throw new Error("unsupported protocol");
114
+ }
115
+ normalized = parsed.toString();
116
+ } catch {
117
+ throw new WalletProviderError({
118
+ code: "wallet_provider_invalid_config",
119
+ message: "Turnkey review requires a valid HTTP(S) Solana RPC URL"
120
+ });
121
+ }
122
+ return digest(normalized);
123
+ }
124
+ function preparedIdentity(params) {
125
+ return digest([
126
+ "fased-turnkey-reviewed-v1",
127
+ params.approvalRequestId,
128
+ params.requestDigest,
129
+ params.rpcUrlDigest,
130
+ params.authorizationContextDigest
131
+ ].join("\0"));
132
+ }
133
+ function toPrepareResult(record) {
134
+ return {
135
+ preparedId: record.preparedId,
136
+ signer: record.signer,
137
+ unsignedTxBase64: record.unsignedTxBase64,
138
+ messageBase64: record.messageBase64,
139
+ intentDigest: record.intentDigest,
140
+ expiresAt: record.expiresAt,
141
+ recentBlockhash: record.recentBlockhash,
142
+ lastValidBlockHeight: record.lastValidBlockHeight,
143
+ simulation: {
144
+ ok: true,
145
+ ...typeof record.simulationUnitsConsumed === "number" && Number.isFinite(record.simulationUnitsConsumed) ? { unitsConsumed: record.simulationUnitsConsumed } : {}
146
+ }
147
+ };
148
+ }
149
+ async function withReviewLock(env, task) {
150
+ const run = async () => await withFileLock(statePath(env), REVIEW_LOCK_OPTIONS, async () => await task());
151
+ const current = reviewStateLock.then(run, run);
152
+ reviewStateLock = current.then(() => undefined, () => undefined);
153
+ return await current;
154
+ }
155
+ async function prepareTurnkeyReviewedTransaction(params) {
156
+ const env = params.env ?? process.env;
157
+ const approvalRequestId = params.request.requestId?.trim();
158
+ if (!approvalRequestId) {
159
+ throw new WalletProviderError({
160
+ code: "wallet_provider_invalid_config",
161
+ message: "Turnkey preparation requires a stable approval request ID"
162
+ });
163
+ }
164
+ const requestDigest = computeReviewedSolanaRequestDigest({
165
+ request: params.request,
166
+ signer: params.signerAddress.trim()
167
+ });
168
+ const expectedRpcUrlDigest = normalizedRpcUrlDigest(params.rpcUrl);
169
+ const expectedAuthorizationContextDigest = digest(params.authorizationContext);
170
+ const preparedId = preparedIdentity({
171
+ approvalRequestId,
172
+ requestDigest,
173
+ rpcUrlDigest: expectedRpcUrlDigest,
174
+ authorizationContextDigest: expectedAuthorizationContextDigest
175
+ });
176
+ return await withReviewLock(env, async () => {
177
+ const file = compactReviewState(readFile(env));
178
+ const existing = file.reviews.find((review) => review.approvalRequestId === approvalRequestId || review.preparedId === preparedId);
179
+ if (existing) {
180
+ const sameImmutableRequest = existing.approvalRequestId === approvalRequestId && secureEqual(existing.preparedId, preparedId) && secureEqual(existing.signer, params.signerAddress.trim()) && secureEqual(existing.requestDigest, requestDigest) && secureEqual(existing.rpcUrlDigest, expectedRpcUrlDigest) && secureEqual(existing.authorizationContextDigest, expectedAuthorizationContextDigest);
181
+ if (!sameImmutableRequest) {
182
+ throw new WalletProviderError({
183
+ code: "wallet_provider_invalid_config",
184
+ message: "Turnkey approval request ID is already bound to a different immutable transaction",
185
+ retryable: false
186
+ });
187
+ }
188
+ if (existing.status === "failed") {
189
+ throw new WalletProviderError({
190
+ code: "wallet_provider_unavailable",
191
+ message: `${existing.failureReason ?? "Turnkey reviewed transaction failed"}; create a new approval request`,
192
+ retryable: false
193
+ });
194
+ }
195
+ if (existing.status === "prepared" && Date.parse(existing.expiresAt) <= Date.now()) {
196
+ throw new WalletProviderError({
197
+ code: "wallet_provider_invalid_config",
198
+ message: "Turnkey prepared review expired; create a new approval request",
199
+ retryable: false
200
+ });
201
+ }
202
+ return toPrepareResult(existing);
203
+ }
204
+ requireReviewCapacity(file);
205
+ const prepared = await buildReviewedSolanaTransaction({
206
+ request: params.request,
207
+ signerAddress: params.signerAddress,
208
+ rpcUrl: params.rpcUrl
209
+ });
210
+ if (!secureEqual(prepared.signer, params.signerAddress.trim())) {
211
+ throw new WalletProviderError({
212
+ code: "wallet_provider_unavailable",
213
+ message: "Turnkey transaction builder returned a different signer"
214
+ });
215
+ }
216
+ const now = Date.now();
217
+ const record = {
218
+ approvalRequestId,
219
+ preparedId,
220
+ createdAt: new Date(now).toISOString(),
221
+ expiresAt: new Date(now + REVIEW_TTL_MS).toISOString(),
222
+ status: "prepared",
223
+ signer: prepared.signer,
224
+ requestDigest,
225
+ intentDigest: prepared.intentDigest,
226
+ unsignedTxBase64: prepared.unsignedTxBase64,
227
+ messageBase64: prepared.messageBase64,
228
+ recentBlockhash: prepared.recentBlockhash,
229
+ lastValidBlockHeight: prepared.lastValidBlockHeight,
230
+ rpcUrlDigest: expectedRpcUrlDigest,
231
+ authorizationContextDigest: expectedAuthorizationContextDigest,
232
+ simulationUnitsConsumed: prepared.simulation.unitsConsumed
233
+ };
234
+ file.reviews.push(record);
235
+ saveFile(file, env);
236
+ return toPrepareResult(record);
237
+ });
238
+ }
239
+ async function executeTurnkeyReviewedTransaction(params) {
240
+ const env = params.env ?? process.env;
241
+ const preparedId = params.preparedId.trim();
242
+ if (!preparedId) {
243
+ throw new WalletProviderError({
244
+ code: "wallet_provider_invalid_config",
245
+ message: "Turnkey send requires a prepared review ID"
246
+ });
247
+ }
248
+ return await withReviewLock(env, async () => {
249
+ const file = readFile(env);
250
+ const review = file.reviews.find((record) => record.preparedId === preparedId);
251
+ if (!review) {
252
+ throw new WalletProviderError({
253
+ code: "wallet_provider_invalid_config",
254
+ message: "Turnkey prepared review is unknown; prepare and review the transaction again"
255
+ });
256
+ }
257
+ if (review.status === "broadcast" && review.txHash) {
258
+ return {
259
+ txHash: review.txHash,
260
+ signer: review.signer,
261
+ intentDigest: review.intentDigest,
262
+ idempotent: true
263
+ };
264
+ }
265
+ if (review.status === "broadcasting" || review.status === "unknown") {
266
+ if (!review.txHash) {
267
+ throw new WalletProviderError({
268
+ code: "wallet_provider_ambiguous",
269
+ message: "Turnkey broadcast is in progress or unknown; do not submit it again",
270
+ retryable: false
271
+ });
272
+ }
273
+ let reconciled;
274
+ try {
275
+ reconciled = await reconcileReviewedSolanaTransaction({
276
+ rpcUrl: params.rpcUrl,
277
+ expectedTxHash: review.txHash,
278
+ lastValidBlockHeight: review.lastValidBlockHeight
279
+ });
280
+ } catch {
281
+ throw new WalletProviderError({
282
+ code: "wallet_provider_ambiguous",
283
+ message: `Turnkey broadcast remains unknown; reconcile signature ${review.txHash} before any new send`,
284
+ retryable: false
285
+ });
286
+ }
287
+ if (reconciled.state === "landed") {
288
+ review.status = "broadcast";
289
+ saveFile(file, env);
290
+ return {
291
+ txHash: review.txHash,
292
+ signer: review.signer,
293
+ intentDigest: review.intentDigest,
294
+ idempotent: true
295
+ };
296
+ }
297
+ if (reconciled.state === "failed" || reconciled.state === "expired") {
298
+ review.status = "failed";
299
+ review.failureReason = reconciled.state === "failed" ? reconciled.reason : `reviewed transaction expired at block height ${review.lastValidBlockHeight} without landing`;
300
+ saveFile(file, env);
301
+ throw new WalletProviderError({
302
+ code: "wallet_provider_unavailable",
303
+ message: `${review.failureReason}; prepare and review a new transaction`,
304
+ retryable: false
305
+ });
306
+ }
307
+ throw new WalletProviderError({
308
+ code: "wallet_provider_ambiguous",
309
+ message: `Turnkey broadcast remains pending; reconcile signature ${review.txHash} before any new send`,
310
+ retryable: false
311
+ });
312
+ }
313
+ if (review.status === "failed") {
314
+ throw new WalletProviderError({
315
+ code: "wallet_provider_unavailable",
316
+ message: `${review.failureReason ?? "Turnkey reviewed transaction failed"}; prepare and review a new transaction`,
317
+ retryable: false
318
+ });
319
+ }
320
+ if (Date.parse(review.expiresAt) <= Date.now()) {
321
+ throw new WalletProviderError({
322
+ code: "wallet_provider_invalid_config",
323
+ message: "Turnkey prepared review expired; prepare and review a new transaction"
324
+ });
325
+ }
326
+ const expectedRequestDigest = computeReviewedSolanaRequestDigest({
327
+ request: params.request,
328
+ signer: params.signerAddress
329
+ });
330
+ if (review.approvalRequestId !== undefined && !secureEqual(review.approvalRequestId, params.request.requestId?.trim() ?? "") || !secureEqual(review.signer, params.signerAddress) || !secureEqual(review.requestDigest, expectedRequestDigest) || !secureEqual(review.rpcUrlDigest, normalizedRpcUrlDigest(params.rpcUrl)) || !secureEqual(review.authorizationContextDigest, digest(params.authorizationContext))) {
331
+ throw new WalletProviderError({
332
+ code: "wallet_provider_invalid_config",
333
+ message: "Turnkey execution context does not match the immutable prepared review"
334
+ });
335
+ }
336
+ const signedTxBase64 = await params.sign(review.unsignedTxBase64);
337
+ const verified = validateReviewedSolanaSignedTransaction({
338
+ unsignedTxBase64: review.unsignedTxBase64,
339
+ signedTxBase64,
340
+ signerAddress: review.signer
341
+ });
342
+ review.status = "broadcasting";
343
+ review.txHash = verified.txHash;
344
+ review.signedTxBase64 = verified.signedBytes.toString("base64");
345
+ saveFile(file, env);
346
+ try {
347
+ const txHash = await broadcastReviewedSolanaTransaction({
348
+ rpcUrl: params.rpcUrl,
349
+ signedTxBase64: review.signedTxBase64,
350
+ expectedTxHash: verified.txHash
351
+ });
352
+ review.status = "broadcast";
353
+ review.txHash = txHash;
354
+ saveFile(file, env);
355
+ return {
356
+ txHash,
357
+ signer: review.signer,
358
+ intentDigest: review.intentDigest,
359
+ idempotent: false
360
+ };
361
+ } catch (error) {
362
+ review.status = "unknown";
363
+ saveFile(file, env);
364
+ throw error;
365
+ }
366
+ });
367
+ }
368
+
369
+ //#endregion
370
+ export { executeTurnkeyReviewedTransaction, prepareTurnkeyReviewedTransaction };
@@ -1,4 +1,5 @@
1
1
  import { ensureWalletStateDir } from "./wallet-runtime-config.js";
2
+ import { serializeWalletState, writeWalletStateFileAtomically } from "./wallet-atomic-state.js";
2
3
  import syncFs from "node:fs";
3
4
  import path from "node:path";
4
5
  import { createHash, createPublicKey, randomBytes, timingSafeEqual, verify } from "node:crypto";
@@ -73,22 +74,12 @@ function rpIdFromHost(host) {
73
74
  }
74
75
  }
75
76
  function normalizeState(input) {
76
- if (!input || typeof input !== "object") {
77
- return {
78
- version: 2,
79
- passkeys: [],
80
- challenges: [],
81
- grants: []
82
- };
77
+ if (!input || typeof input !== "object" || Array.isArray(input)) {
78
+ throw new Error("wallet approval auth state must be an object");
83
79
  }
84
80
  const parsed = input;
85
- if (parsed.version !== 2) {
86
- return {
87
- version: 2,
88
- passkeys: [],
89
- challenges: [],
90
- grants: []
91
- };
81
+ if (parsed.version !== 2 || !Array.isArray(parsed.passkeys) || !Array.isArray(parsed.challenges) || !Array.isArray(parsed.grants)) {
82
+ throw new Error("wallet approval auth state has an unsupported shape");
92
83
  }
93
84
  const passkeys = Array.isArray(parsed.passkeys) ? parsed.passkeys.map((entry) => {
94
85
  if (!entry || typeof entry !== "object") {
@@ -109,6 +100,9 @@ function normalizeState(input) {
109
100
  transports: Array.isArray(value.transports) ? value.transports.map((v) => String(v)) : undefined
110
101
  };
111
102
  }).filter((entry) => entry !== null) : [];
103
+ if (passkeys.length !== parsed.passkeys.length) {
104
+ throw new Error("wallet approval auth state contains an invalid passkey");
105
+ }
112
106
  const challenges = Array.isArray(parsed.challenges) ? parsed.challenges.map((entry) => {
113
107
  if (!entry || typeof entry !== "object") {
114
108
  return null;
@@ -137,6 +131,9 @@ function normalizeState(input) {
137
131
  status: value.status
138
132
  };
139
133
  }).filter((entry) => entry !== null) : [];
134
+ if (challenges.length !== parsed.challenges.length || challenges.some((entry) => !entry.id || !entry.challenge || !entry.host || !entry.rpId)) {
135
+ throw new Error("wallet approval auth state contains an invalid challenge");
136
+ }
140
137
  const grants = Array.isArray(parsed.grants) ? parsed.grants.map((entry) => {
141
138
  if (!entry || typeof entry !== "object") {
142
139
  return null;
@@ -154,6 +151,9 @@ function normalizeState(input) {
154
151
  expiresAt: value.expiresAt
155
152
  };
156
153
  }).filter((entry) => entry !== null) : [];
154
+ if (grants.length !== parsed.grants.length || grants.some((entry) => !entry.host)) {
155
+ throw new Error("wallet approval auth state contains an invalid grant");
156
+ }
157
157
  return {
158
158
  version: 2,
159
159
  passkeys,
@@ -174,24 +174,13 @@ function loadState(env = process.env) {
174
174
  try {
175
175
  const raw = syncFs.readFileSync(statePath, "utf8");
176
176
  return normalizeState(JSON.parse(raw));
177
- } catch {
178
- return {
179
- version: 2,
180
- passkeys: [],
181
- challenges: [],
182
- grants: []
183
- };
177
+ } catch (error) {
178
+ throw new Error("wallet approval auth state is unreadable; refusing to reset credentials", { cause: error });
184
179
  }
185
180
  }
186
181
  function saveState(state, env = process.env) {
187
182
  const statePath = resolveStatePath(env);
188
- syncFs.writeFileSync(statePath, `${JSON.stringify(state, null, 2)}\n`, {
189
- encoding: "utf8",
190
- mode: 384
191
- });
192
- try {
193
- syncFs.chmodSync(statePath, 384);
194
- } catch {}
183
+ writeWalletStateFileAtomically(statePath, serializeWalletState(state));
195
184
  }
196
185
  function pruneState(state) {
197
186
  const now = nowMs();
@@ -0,0 +1,53 @@
1
+ import syncFs from "node:fs";
2
+ import path from "node:path";
3
+ import { randomBytes } from "node:crypto";
4
+
5
+ //#region src/wallet/wallet-atomic-state.ts
6
+ const WALLET_STATE_FILE_MODE = 384;
7
+ /**
8
+ * Replace a wallet state file durably without ever exposing a partially-written
9
+ * destination. Callers remain responsible for validating the full document
10
+ * before trusting it and for serializing read/modify/write operations.
11
+ */
12
+ function writeWalletStateFileAtomically(filePath, contents) {
13
+ const directoryPath = path.dirname(filePath);
14
+ const temporaryPath = path.join(directoryPath, `.${path.basename(filePath)}.${process.pid}.${randomBytes(8).toString("hex")}.tmp`);
15
+ let descriptor;
16
+ let renamed = false;
17
+ try {
18
+ descriptor = syncFs.openSync(temporaryPath, "wx", WALLET_STATE_FILE_MODE);
19
+ syncFs.writeFileSync(descriptor, contents, { encoding: "utf8" });
20
+ syncFs.fsyncSync(descriptor);
21
+ syncFs.closeSync(descriptor);
22
+ descriptor = undefined;
23
+ syncFs.renameSync(temporaryPath, filePath);
24
+ renamed = true;
25
+ try {
26
+ syncFs.chmodSync(filePath, WALLET_STATE_FILE_MODE);
27
+ } catch {}
28
+ const directoryDescriptor = syncFs.openSync(directoryPath, "r");
29
+ try {
30
+ syncFs.fsyncSync(directoryDescriptor);
31
+ } finally {
32
+ syncFs.closeSync(directoryDescriptor);
33
+ }
34
+ } catch (error) {
35
+ if (descriptor !== undefined) {
36
+ try {
37
+ syncFs.closeSync(descriptor);
38
+ } catch {}
39
+ }
40
+ if (!renamed) {
41
+ try {
42
+ syncFs.unlinkSync(temporaryPath);
43
+ } catch {}
44
+ }
45
+ throw error;
46
+ }
47
+ }
48
+ function serializeWalletState(value) {
49
+ return `${JSON.stringify(value, null, 2)}\n`;
50
+ }
51
+
52
+ //#endregion
53
+ export { serializeWalletState, writeWalletStateFileAtomically };
@@ -341,7 +341,6 @@ async function runWalletProviderCanaryReport(params) {
341
341
  actor: "wallet-canary",
342
342
  config: params.wallet,
343
343
  providerIdOverride: providerId,
344
- approvalHost: "127.0.0.1",
345
344
  env
346
345
  });
347
346
  pushStep(steps, {
@@ -1,5 +1,6 @@
1
1
  import { ensureWalletStateDir } from "./wallet-runtime-config.js";
2
2
  import { readWalletProviderRegistry, resolveWalletSelection } from "./wallet-provider-registry.js";
3
+ import { serializeWalletState, writeWalletStateFileAtomically } from "./wallet-atomic-state.js";
3
4
  import { buildWalletProviderCapabilityMatrix, providerSupportsChainOperation } from "./wallet-provider-capabilities.js";
4
5
  import { walletDiagnosticErrorString } from "./wallet-redaction.js";
5
6
  import { createWalletProviderAdapter, resolveWalletProviderId } from "./wallet-provider-resolver.js";
@@ -26,6 +27,7 @@ function normalizeProviderId(value) {
26
27
  case "local-socket-signer":
27
28
  case "alchemy":
28
29
  case "turnkey":
30
+ case "wallet-standard":
29
31
  case "privy": return value;
30
32
  default: return null;
31
33
  }
@@ -115,9 +117,12 @@ function defaultLedger() {
115
117
  }
116
118
  function normalizeLedger(raw) {
117
119
  if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
118
- return defaultLedger();
120
+ throw new Error("wallet inbound ledger must be an object");
119
121
  }
120
122
  const value = raw;
123
+ if (value.version !== 1 || !Array.isArray(value.snapshots) || !Array.isArray(value.events) || typeof value.updatedAt !== "string") {
124
+ throw new Error("wallet inbound ledger has an unsupported shape");
125
+ }
121
126
  const snapshots = Array.isArray(value.snapshots) ? value.snapshots.filter((entry) => {
122
127
  if (!entry || typeof entry !== "object") {
123
128
  return false;
@@ -134,6 +139,9 @@ function normalizeLedger(raw) {
134
139
  unit: entry.unit,
135
140
  updatedAt: entry.updatedAt
136
141
  })) : [];
142
+ if (snapshots.length !== value.snapshots.length) {
143
+ throw new Error("wallet inbound ledger contains an invalid balance snapshot");
144
+ }
137
145
  const events = Array.isArray(value.events) ? value.events.filter((entry) => {
138
146
  if (!entry || typeof entry !== "object") {
139
147
  return false;
@@ -164,11 +172,14 @@ function normalizeLedger(raw) {
164
172
  } : undefined,
165
173
  metadata: entry.metadata && typeof entry.metadata === "object" ? entry.metadata : undefined
166
174
  })) : [];
175
+ if (events.length !== value.events.length) {
176
+ throw new Error("wallet inbound ledger contains an invalid event");
177
+ }
167
178
  return {
168
179
  version: 1,
169
180
  snapshots,
170
181
  events,
171
- updatedAt: typeof value.updatedAt === "string" ? value.updatedAt : nowIso(),
182
+ updatedAt: value.updatedAt,
172
183
  lastReconciledAt: typeof value.lastReconciledAt === "string" ? value.lastReconciledAt : undefined
173
184
  };
174
185
  }
@@ -180,8 +191,8 @@ function readLedger(env = process.env) {
180
191
  try {
181
192
  const parsed = JSON.parse(syncFs.readFileSync(filePath, "utf8"));
182
193
  return normalizeLedger(parsed);
183
- } catch {
184
- return defaultLedger();
194
+ } catch (error) {
195
+ throw new Error("wallet inbound ledger is unreadable; refusing to reset event history", { cause: error });
185
196
  }
186
197
  }
187
198
  function writeLedger(ledger, env = process.env) {
@@ -191,13 +202,7 @@ function writeLedger(ledger, env = process.env) {
191
202
  version: 1,
192
203
  updatedAt: nowIso()
193
204
  };
194
- syncFs.writeFileSync(filePath, `${JSON.stringify(payload, null, 2)}\n`, {
195
- encoding: "utf8",
196
- mode: 384
197
- });
198
- try {
199
- syncFs.chmodSync(filePath, 384);
200
- } catch {}
205
+ writeWalletStateFileAtomically(filePath, serializeWalletState(payload));
201
206
  }
202
207
  function findWalletById(env, walletId) {
203
208
  if (!walletId) {
@@ -128,7 +128,7 @@ function simulateWalletPolicy(params) {
128
128
  diff
129
129
  };
130
130
  }
131
- checks.push(pass("wallet.approval", "Approval", "Autonomous mode may sign after custody and approval gates."));
131
+ checks.push(pass("wallet.approval", "Approval", "Autonomous mode may sign only after typed signer policy and cap checks."));
132
132
  return {
133
133
  ok: true,
134
134
  decision: "pass",