dexterm-cli 0.0.0-stage → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (377) hide show
  1. package/LICENSE +661 -0
  2. package/dist/packages/agents/src/audit.js +122 -0
  3. package/dist/packages/agents/src/bus.js +394 -0
  4. package/dist/packages/agents/src/executor.js +202 -0
  5. package/dist/packages/agents/src/gate.js +179 -0
  6. package/dist/packages/agents/src/identity.js +218 -0
  7. package/dist/packages/agents/src/index.js +15 -0
  8. package/dist/packages/agents/src/proposals.js +153 -0
  9. package/dist/packages/agents/src/schema.js +87 -0
  10. package/dist/packages/agents/src/sdk.js +140 -0
  11. package/dist/packages/agents/src/tiers.js +119 -0
  12. package/dist/packages/agents/src/tools.js +72 -0
  13. package/dist/packages/agents/src/ws.js +158 -0
  14. package/dist/packages/cli/assets/claude/analyst-system.md +76 -0
  15. package/dist/packages/cli/assets/claude/plugin/.claude-plugin/plugin.json +5 -0
  16. package/dist/packages/cli/assets/claude/plugin/skills/REGISTRY.json +16 -0
  17. package/dist/packages/cli/assets/claude/plugin/skills/larp-check/SKILL.md +75 -0
  18. package/dist/packages/cli/assets/claude/plugin/skills/rug-check/SKILL.md +74 -0
  19. package/dist/packages/cli/assets/claude/plugin/skills/token-dossier/SKILL.md +63 -0
  20. package/dist/packages/cli/assets/claude/plugin/skills/trench-scan/SKILL.md +44 -0
  21. package/dist/packages/cli/assets/claude/schemas/larp.schema.json +117 -0
  22. package/dist/packages/cli/assets/claude/schemas/trench-scan.schema.json +71 -0
  23. package/dist/packages/cli/assets/claude/schemas/verdict.schema.json +115 -0
  24. package/dist/packages/cli/bin/dexterm.mjs +11 -0
  25. package/dist/packages/cli/src/agent-cmd.js +336 -0
  26. package/dist/packages/cli/src/agent-roster.js +26 -0
  27. package/dist/packages/cli/src/alerts/adapters/desktop.js +40 -0
  28. package/dist/packages/cli/src/alerts/adapters/discord.js +36 -0
  29. package/dist/packages/cli/src/alerts/adapters/telegram.js +43 -0
  30. package/dist/packages/cli/src/alerts/adapters/toast.js +16 -0
  31. package/dist/packages/cli/src/alerts/creds.js +83 -0
  32. package/dist/packages/cli/src/alerts/format.js +41 -0
  33. package/dist/packages/cli/src/alerts/index.js +10 -0
  34. package/dist/packages/cli/src/alerts/notifier.js +53 -0
  35. package/dist/packages/cli/src/alerts/route.js +19 -0
  36. package/dist/packages/cli/src/alerts/types.js +1 -0
  37. package/dist/packages/cli/src/alerts-cmd.js +212 -0
  38. package/dist/packages/cli/src/analyst/backend.js +526 -0
  39. package/dist/packages/cli/src/analyst/results.js +38 -0
  40. package/dist/packages/cli/src/analyst/session.js +319 -0
  41. package/dist/packages/cli/src/audit.js +51 -0
  42. package/dist/packages/cli/src/backtest-cmd.js +208 -0
  43. package/dist/packages/cli/src/boot-state.js +62 -0
  44. package/dist/packages/cli/src/coin-target.js +2 -0
  45. package/dist/packages/cli/src/custom.js +261 -0
  46. package/dist/packages/cli/src/display.js +191 -0
  47. package/dist/packages/cli/src/doctor.js +375 -0
  48. package/dist/packages/cli/src/export.js +235 -0
  49. package/dist/packages/cli/src/feed.js +634 -0
  50. package/dist/packages/cli/src/fixtures.js +275 -0
  51. package/dist/packages/cli/src/follow-cmd.js +221 -0
  52. package/dist/packages/cli/src/images-config.js +59 -0
  53. package/dist/packages/cli/src/index.js +31 -0
  54. package/dist/packages/cli/src/kb-skill-cmd.js +240 -0
  55. package/dist/packages/cli/src/larp-record.js +21 -0
  56. package/dist/packages/cli/src/live/copy.js +80 -0
  57. package/dist/packages/cli/src/live/flow.js +233 -0
  58. package/dist/packages/cli/src/live/index.js +511 -0
  59. package/dist/packages/cli/src/live/intel.js +227 -0
  60. package/dist/packages/cli/src/live/newpairs.js +334 -0
  61. package/dist/packages/cli/src/live/screens/newpairs-live.js +219 -0
  62. package/dist/packages/cli/src/live/screens/token-live.js +459 -0
  63. package/dist/packages/cli/src/live/screens/watchlist-live.js +149 -0
  64. package/dist/packages/cli/src/live/token.js +585 -0
  65. package/dist/packages/cli/src/live/trade.js +929 -0
  66. package/dist/packages/cli/src/live/watchlist.js +178 -0
  67. package/dist/packages/cli/src/main.js +5785 -0
  68. package/dist/packages/cli/src/mcp-cmd.js +326 -0
  69. package/dist/packages/cli/src/narrative-cmd.js +76 -0
  70. package/dist/packages/cli/src/pack-cmd.js +323 -0
  71. package/dist/packages/cli/src/plan-cmd.js +278 -0
  72. package/dist/packages/cli/src/popup.js +810 -0
  73. package/dist/packages/cli/src/rules-cmd.js +131 -0
  74. package/dist/packages/cli/src/rules.js +358 -0
  75. package/dist/packages/cli/src/scan/command.js +57 -0
  76. package/dist/packages/cli/src/scan/engine.js +419 -0
  77. package/dist/packages/cli/src/scan/observe.js +64 -0
  78. package/dist/packages/cli/src/scan/ui.js +98 -0
  79. package/dist/packages/cli/src/screener/filter.js +241 -0
  80. package/dist/packages/cli/src/screener/presets.js +80 -0
  81. package/dist/packages/cli/src/screener/rows.js +146 -0
  82. package/dist/packages/cli/src/screener/rug.js +50 -0
  83. package/dist/packages/cli/src/screener/sort.js +52 -0
  84. package/dist/packages/cli/src/screener/view.js +183 -0
  85. package/dist/packages/cli/src/screens/analyst-session.js +343 -0
  86. package/dist/packages/cli/src/screens/analyst.js +326 -0
  87. package/dist/packages/cli/src/screens/boot.js +74 -0
  88. package/dist/packages/cli/src/screens/bubbles.js +255 -0
  89. package/dist/packages/cli/src/screens/coin.js +33 -0
  90. package/dist/packages/cli/src/screens/follows.js +201 -0
  91. package/dist/packages/cli/src/screens/kol.js +90 -0
  92. package/dist/packages/cli/src/screens/narrative.js +90 -0
  93. package/dist/packages/cli/src/screens/newpairs.js +150 -0
  94. package/dist/packages/cli/src/screens/overlay.js +257 -0
  95. package/dist/packages/cli/src/screens/plans.js +126 -0
  96. package/dist/packages/cli/src/screens/positions.js +176 -0
  97. package/dist/packages/cli/src/screens/rules.js +170 -0
  98. package/dist/packages/cli/src/screens/screener.js +459 -0
  99. package/dist/packages/cli/src/screens/social-panel.js +107 -0
  100. package/dist/packages/cli/src/screens/ticket.js +342 -0
  101. package/dist/packages/cli/src/screens/token.js +312 -0
  102. package/dist/packages/cli/src/screens/trending.js +181 -0
  103. package/dist/packages/cli/src/screens/watchlist.js +104 -0
  104. package/dist/packages/cli/src/skill-eval-build.js +109 -0
  105. package/dist/packages/cli/src/social/demo.js +96 -0
  106. package/dist/packages/cli/src/social/view.js +250 -0
  107. package/dist/packages/cli/src/state.js +463 -0
  108. package/dist/packages/cli/src/theme-cmd.js +126 -0
  109. package/dist/packages/cli/src/trade.js +319 -0
  110. package/dist/packages/cli/src/update.js +294 -0
  111. package/dist/packages/cli/src/version.js +3 -0
  112. package/dist/packages/cli/src/voice-cmd.js +119 -0
  113. package/dist/packages/cli/src/voice-hud.js +42 -0
  114. package/dist/packages/cli/src/wallet-cmd.js +440 -0
  115. package/dist/packages/cli/src/wallet-command.js +31 -0
  116. package/dist/packages/cli/src/watch-cmd.js +190 -0
  117. package/dist/packages/cli/src/widgets.js +208 -0
  118. package/dist/packages/cli/src/workspace-cmd.js +68 -0
  119. package/dist/packages/cli/src/x-cmd.js +154 -0
  120. package/dist/packages/custom/src/config.js +213 -0
  121. package/dist/packages/custom/src/index.js +10 -0
  122. package/dist/packages/custom/src/keymap.js +107 -0
  123. package/dist/packages/custom/src/layout.js +235 -0
  124. package/dist/packages/custom/src/packs.js +633 -0
  125. package/dist/packages/custom/src/rules.js +704 -0
  126. package/dist/packages/custom/src/schema.js +538 -0
  127. package/dist/packages/custom/src/themes.js +176 -0
  128. package/dist/packages/custom/src/widget-html.js +222 -0
  129. package/dist/packages/custom/src/workspace.js +141 -0
  130. package/dist/packages/data/src/analyst-actions.js +133 -0
  131. package/dist/packages/data/src/budget.js +88 -0
  132. package/dist/packages/data/src/cache.js +142 -0
  133. package/dist/packages/data/src/candles.js +168 -0
  134. package/dist/packages/data/src/clock.js +64 -0
  135. package/dist/packages/data/src/coin-target.js +82 -0
  136. package/dist/packages/data/src/credits.js +137 -0
  137. package/dist/packages/data/src/detail.js +97 -0
  138. package/dist/packages/data/src/fast-lane.js +321 -0
  139. package/dist/packages/data/src/fixtures.js +135 -0
  140. package/dist/packages/data/src/health.js +245 -0
  141. package/dist/packages/data/src/http.js +277 -0
  142. package/dist/packages/data/src/images/cache.js +141 -0
  143. package/dist/packages/data/src/images/decode-worker.js +28 -0
  144. package/dist/packages/data/src/images/decode.js +81 -0
  145. package/dist/packages/data/src/images/fetch.js +162 -0
  146. package/dist/packages/data/src/images/gif.js +214 -0
  147. package/dist/packages/data/src/images/index.js +10 -0
  148. package/dist/packages/data/src/images/jpeg.js +544 -0
  149. package/dist/packages/data/src/images/png.js +310 -0
  150. package/dist/packages/data/src/images/service.js +334 -0
  151. package/dist/packages/data/src/images/types.js +27 -0
  152. package/dist/packages/data/src/index.js +211 -0
  153. package/dist/packages/data/src/intel/helius-history.js +70 -0
  154. package/dist/packages/data/src/intel/holder-class.js +125 -0
  155. package/dist/packages/data/src/intel/rules.js +421 -0
  156. package/dist/packages/data/src/intel/scan.js +170 -0
  157. package/dist/packages/data/src/larp/evidence.js +288 -0
  158. package/dist/packages/data/src/larp/link-baseline.js +196 -0
  159. package/dist/packages/data/src/larp/links.js +137 -0
  160. package/dist/packages/data/src/larp/robots.js +90 -0
  161. package/dist/packages/data/src/larp/safe-fetch.js +389 -0
  162. package/dist/packages/data/src/limiter.js +341 -0
  163. package/dist/packages/data/src/model.js +12 -0
  164. package/dist/packages/data/src/paper.js +331 -0
  165. package/dist/packages/data/src/rpc-config.js +126 -0
  166. package/dist/packages/data/src/rpc-pool.js +166 -0
  167. package/dist/packages/data/src/social/chain.js +83 -0
  168. package/dist/packages/data/src/social/dedupe.js +205 -0
  169. package/dist/packages/data/src/social/feed.js +462 -0
  170. package/dist/packages/data/src/social/firewall.js +110 -0
  171. package/dist/packages/data/src/social/index.js +11 -0
  172. package/dist/packages/data/src/social/kol.js +269 -0
  173. package/dist/packages/data/src/social/model.js +11 -0
  174. package/dist/packages/data/src/social/rank.js +184 -0
  175. package/dist/packages/data/src/social/rss.js +113 -0
  176. package/dist/packages/data/src/social/summary.js +104 -0
  177. package/dist/packages/data/src/social/venues.js +186 -0
  178. package/dist/packages/data/src/sources/dexscreener.js +184 -0
  179. package/dist/packages/data/src/sources/geckoterminal.js +133 -0
  180. package/dist/packages/data/src/sources/goplus.js +161 -0
  181. package/dist/packages/data/src/sources/jupiter-swap.js +236 -0
  182. package/dist/packages/data/src/sources/jupiter.js +198 -0
  183. package/dist/packages/data/src/sources/pda.js +113 -0
  184. package/dist/packages/data/src/sources/pump-curve.js +58 -0
  185. package/dist/packages/data/src/sources/pumpportal.js +252 -0
  186. package/dist/packages/data/src/sources/rugcheck.js +71 -0
  187. package/dist/packages/data/src/sources/solana-rpc.js +299 -0
  188. package/dist/packages/data/src/sources/solana-ws.js +287 -0
  189. package/dist/packages/data/src/store.js +289 -0
  190. package/dist/packages/data/src/trade/bridge.js +464 -0
  191. package/dist/packages/data/src/trade/caps.js +323 -0
  192. package/dist/packages/data/src/trade/copy/auto.js +385 -0
  193. package/dist/packages/data/src/trade/copy/follows.js +103 -0
  194. package/dist/packages/data/src/trade/copy/index.js +9 -0
  195. package/dist/packages/data/src/trade/copy/mirror.js +91 -0
  196. package/dist/packages/data/src/trade/copy/stats.js +120 -0
  197. package/dist/packages/data/src/trade/copy/tracker.js +158 -0
  198. package/dist/packages/data/src/trade/copy/trades.js +87 -0
  199. package/dist/packages/data/src/trade/copy/types.js +6 -0
  200. package/dist/packages/data/src/trade/executor.js +933 -0
  201. package/dist/packages/data/src/trade/kill-switch.js +47 -0
  202. package/dist/packages/data/src/trade/link-store.js +52 -0
  203. package/dist/packages/data/src/trade/plans/confirm.js +157 -0
  204. package/dist/packages/data/src/trade/plans/engine.js +784 -0
  205. package/dist/packages/data/src/trade/plans/executor-live.js +220 -0
  206. package/dist/packages/data/src/trade/plans/executor-paper.js +44 -0
  207. package/dist/packages/data/src/trade/plans/executor.js +11 -0
  208. package/dist/packages/data/src/trade/plans/index.js +12 -0
  209. package/dist/packages/data/src/trade/plans/journal.js +77 -0
  210. package/dist/packages/data/src/trade/plans/mandate.js +129 -0
  211. package/dist/packages/data/src/trade/plans/plan.js +179 -0
  212. package/dist/packages/data/src/trade/plans/service.js +168 -0
  213. package/dist/packages/data/src/trade/plans/types.js +6 -0
  214. package/dist/packages/data/src/trade/rpc-exec.js +229 -0
  215. package/dist/packages/data/src/trade/signer/client.js +197 -0
  216. package/dist/packages/data/src/trade/signer/fast-wallet.js +154 -0
  217. package/dist/packages/data/src/trade/signer/host.js +97 -0
  218. package/dist/packages/data/src/trade/signer/main.js +121 -0
  219. package/dist/packages/data/src/trade/signer/probe-builder.js +85 -0
  220. package/dist/packages/data/src/trade/signer/protocol.js +92 -0
  221. package/dist/packages/data/src/trade/signer/service.js +400 -0
  222. package/dist/packages/data/src/trade/signer/sol-usd-feed.js +98 -0
  223. package/dist/packages/data/src/trade/sol-usd.js +86 -0
  224. package/dist/packages/data/src/trade/swap-build.js +108 -0
  225. package/dist/packages/data/src/trade/token-delta.js +201 -0
  226. package/dist/packages/data/src/trade/tx-decode.js +632 -0
  227. package/dist/packages/data/src/util.js +24 -0
  228. package/dist/packages/data/src/watchlist.js +176 -0
  229. package/dist/packages/data/src/x/api.js +256 -0
  230. package/dist/packages/data/src/x/budget.js +66 -0
  231. package/dist/packages/data/src/x/config.js +49 -0
  232. package/dist/packages/data/src/x/index.js +10 -0
  233. package/dist/packages/data/src/x/mock-server.js +217 -0
  234. package/dist/packages/data/src/x/oauth.js +242 -0
  235. package/dist/packages/data/src/x/pkce.js +21 -0
  236. package/dist/packages/data/src/x/source.js +285 -0
  237. package/dist/packages/data/src/x/token-store.js +134 -0
  238. package/dist/packages/data/src/x/transport.js +71 -0
  239. package/dist/packages/data/vendor/solana-web3/LICENSE +20 -0
  240. package/dist/packages/data/vendor/solana-web3/NOTICE.md +13 -0
  241. package/dist/packages/data/vendor/solana-web3/index.iife.min.js +20 -0
  242. package/dist/packages/mcp/assets/cases.json +1194 -0
  243. package/dist/packages/mcp/assets/kb/entries/cabal-and-coordinated-launches.md +28 -0
  244. package/dist/packages/mcp/assets/kb/entries/common-rug-patterns.md +40 -0
  245. package/dist/packages/mcp/assets/kb/entries/dbc-mechanics.md +30 -0
  246. package/dist/packages/mcp/assets/kb/entries/dev-wallet-behaviour.md +33 -0
  247. package/dist/packages/mcp/assets/kb/entries/glossary.md +35 -0
  248. package/dist/packages/mcp/assets/kb/entries/holder-concentration.md +30 -0
  249. package/dist/packages/mcp/assets/kb/entries/jito-bundles.md +29 -0
  250. package/dist/packages/mcp/assets/kb/entries/launchlab-curve.md +31 -0
  251. package/dist/packages/mcp/assets/kb/entries/lp-burn-vs-lock.md +31 -0
  252. package/dist/packages/mcp/assets/kb/entries/metadata-and-copycats.md +29 -0
  253. package/dist/packages/mcp/assets/kb/entries/mint-freeze-authority.md +31 -0
  254. package/dist/packages/mcp/assets/kb/entries/narrative-lifecycle.md +32 -0
  255. package/dist/packages/mcp/assets/kb/entries/pump-bonding-curve.md +33 -0
  256. package/dist/packages/mcp/assets/kb/entries/pumpswap-migration.md +29 -0
  257. package/dist/packages/mcp/assets/kb/entries/sniping-and-snipers.md +29 -0
  258. package/dist/packages/mcp/assets/kb/entries/token2022-extensions-risk.md +32 -0
  259. package/dist/packages/mcp/assets/kb/entries/wash-trading-fake-volume.md +33 -0
  260. package/dist/packages/mcp/assets/skills/rug-check.md +52 -0
  261. package/dist/packages/mcp/assets/skills/token-dossier.md +57 -0
  262. package/dist/packages/mcp/assets/skills/trench-scan.md +40 -0
  263. package/dist/packages/mcp/bin/dexterm-mcp.mjs +16 -0
  264. package/dist/packages/mcp/src/dossier.js +78 -0
  265. package/dist/packages/mcp/src/firewall.js +21 -0
  266. package/dist/packages/mcp/src/index.js +22 -0
  267. package/dist/packages/mcp/src/server.js +936 -0
  268. package/dist/packages/mcp/src/user-content.js +434 -0
  269. package/dist/packages/packs/src/backtest.js +183 -0
  270. package/dist/packages/packs/src/digest.js +95 -0
  271. package/dist/packages/packs/src/firewall.js +57 -0
  272. package/dist/packages/packs/src/index.js +20 -0
  273. package/dist/packages/packs/src/larp-eval.js +100 -0
  274. package/dist/packages/packs/src/narrative.js +309 -0
  275. package/dist/packages/packs/src/reason.js +135 -0
  276. package/dist/packages/packs/src/rug-model.js +137 -0
  277. package/dist/packages/packs/src/schema.js +54 -0
  278. package/dist/packages/packs/src/skill-backtest.js +300 -0
  279. package/dist/packages/packs/src/token-dossier.js +174 -0
  280. package/dist/packages/packs/src/verdicts.js +17 -0
  281. package/dist/packages/packs/src/wallet-dossier.js +105 -0
  282. package/dist/packages/popup/src/config.js +71 -0
  283. package/dist/packages/popup/src/dock.js +271 -0
  284. package/dist/packages/popup/src/host.js +210 -0
  285. package/dist/packages/popup/src/index.js +12 -0
  286. package/dist/packages/popup/src/loopback.js +53 -0
  287. package/dist/packages/popup/src/open.js +183 -0
  288. package/dist/packages/popup/src/pages/bubbles-lib.js +160 -0
  289. package/dist/packages/popup/src/pages/bubbles.css +23 -0
  290. package/dist/packages/popup/src/pages/bubbles.html +36 -0
  291. package/dist/packages/popup/src/pages/bubbles.js +214 -0
  292. package/dist/packages/popup/src/pages/chart-lib.js +262 -0
  293. package/dist/packages/popup/src/pages/chart.css +50 -0
  294. package/dist/packages/popup/src/pages/chart.html +56 -0
  295. package/dist/packages/popup/src/pages/chart.js +554 -0
  296. package/dist/packages/popup/src/pages/common.css +34 -0
  297. package/dist/packages/popup/src/pages/common.js +187 -0
  298. package/dist/packages/popup/src/pages/feeds.html +20 -0
  299. package/dist/packages/popup/src/pages/feeds.js +77 -0
  300. package/dist/packages/popup/src/pages/narrative.html +20 -0
  301. package/dist/packages/popup/src/pages/narrative.js +61 -0
  302. package/dist/packages/popup/src/pages/plans.css +30 -0
  303. package/dist/packages/popup/src/pages/plans.html +26 -0
  304. package/dist/packages/popup/src/pages/plans.js +82 -0
  305. package/dist/packages/popup/src/pages/roster.css +41 -0
  306. package/dist/packages/popup/src/pages/roster.html +25 -0
  307. package/dist/packages/popup/src/pages/roster.js +75 -0
  308. package/dist/packages/popup/src/pages/token.html +20 -0
  309. package/dist/packages/popup/src/pages/token.js +112 -0
  310. package/dist/packages/popup/src/pages/widget-sdk.js +224 -0
  311. package/dist/packages/popup/src/pages/widget.css +5 -0
  312. package/dist/packages/popup/src/pages/widget.html +21 -0
  313. package/dist/packages/popup/src/pages/widget.js +85 -0
  314. package/dist/packages/popup/src/protocol.js +31 -0
  315. package/dist/packages/popup/src/selftest.js +156 -0
  316. package/dist/packages/popup/src/server.js +582 -0
  317. package/dist/packages/popup/src/sinkproxy.js +167 -0
  318. package/dist/packages/popup/src/widget-html.js +231 -0
  319. package/dist/packages/popup/vendor/lightweight-charts/LICENSE +201 -0
  320. package/dist/packages/popup/vendor/lightweight-charts/NOTICE +2 -0
  321. package/dist/packages/popup/vendor/lightweight-charts/README.md +30 -0
  322. package/dist/packages/popup/vendor/lightweight-charts/VERSION +1 -0
  323. package/dist/packages/popup/vendor/lightweight-charts/lightweight-charts.standalone.production.js +7 -0
  324. package/dist/packages/recorder/bin/dexterm-recorder.mjs +11 -0
  325. package/dist/packages/recorder/src/cli.js +113 -0
  326. package/dist/packages/recorder/src/export.js +71 -0
  327. package/dist/packages/recorder/src/index.js +20 -0
  328. package/dist/packages/recorder/src/labels.js +151 -0
  329. package/dist/packages/recorder/src/open.js +29 -0
  330. package/dist/packages/recorder/src/recorder.js +553 -0
  331. package/dist/packages/recorder/src/schedule.js +43 -0
  332. package/dist/packages/recorder/src/schema.js +135 -0
  333. package/dist/packages/recorder/src/snapshot.js +191 -0
  334. package/dist/packages/recorder/src/sweep.js +52 -0
  335. package/dist/packages/recorder/src/verdicts.js +52 -0
  336. package/dist/packages/tui/src/bubbles.js +415 -0
  337. package/dist/packages/tui/src/canvas.js +370 -0
  338. package/dist/packages/tui/src/chrome.js +85 -0
  339. package/dist/packages/tui/src/color.js +133 -0
  340. package/dist/packages/tui/src/flush.js +62 -0
  341. package/dist/packages/tui/src/fmt.js +188 -0
  342. package/dist/packages/tui/src/fx-trade.js +278 -0
  343. package/dist/packages/tui/src/fx.js +527 -0
  344. package/dist/packages/tui/src/images/blocks.js +141 -0
  345. package/dist/packages/tui/src/images/coin.js +22 -0
  346. package/dist/packages/tui/src/images/detect.js +140 -0
  347. package/dist/packages/tui/src/images/index.js +7 -0
  348. package/dist/packages/tui/src/images/layer.js +146 -0
  349. package/dist/packages/tui/src/images/pixels.js +157 -0
  350. package/dist/packages/tui/src/images/protocols.js +87 -0
  351. package/dist/packages/tui/src/index.js +18 -0
  352. package/dist/packages/tui/src/keymap.js +357 -0
  353. package/dist/packages/tui/src/layout.js +11 -0
  354. package/dist/packages/tui/src/raster-font.js +195 -0
  355. package/dist/packages/tui/src/raster.js +213 -0
  356. package/dist/packages/tui/src/sound.js +177 -0
  357. package/dist/packages/tui/src/theme.js +182 -0
  358. package/dist/packages/tui/src/widgets.js +598 -0
  359. package/dist/packages/voice/src/controller.js +407 -0
  360. package/dist/packages/voice/src/doctor.js +131 -0
  361. package/dist/packages/voice/src/download.js +184 -0
  362. package/dist/packages/voice/src/engines.js +84 -0
  363. package/dist/packages/voice/src/index.js +20 -0
  364. package/dist/packages/voice/src/router.js +502 -0
  365. package/dist/packages/voice/src/stt/apple-helper.swift.js +103 -0
  366. package/dist/packages/voice/src/stt/apple.js +179 -0
  367. package/dist/packages/voice/src/stt/fake.js +34 -0
  368. package/dist/packages/voice/src/stt/whisper.js +112 -0
  369. package/dist/packages/voice/src/summary.js +44 -0
  370. package/dist/packages/voice/src/tts/espeak.js +131 -0
  371. package/dist/packages/voice/src/tts/fake.js +30 -0
  372. package/dist/packages/voice/src/tts/piper.js +206 -0
  373. package/dist/packages/voice/src/tts/say.js +132 -0
  374. package/dist/packages/voice/src/tts/voices.js +206 -0
  375. package/dist/packages/voice/src/types.js +3 -0
  376. package/package.json +38 -4
  377. package/README.md +0 -3
@@ -0,0 +1,220 @@
1
+ // task-1363 — the LIVE fire path for the plan engine: an Executor that fills through the REAL signer process
2
+ // (devnet only, from the capped FAST WALLET). The engine is synchronous and tick-driven; the signer is a child
3
+ // process answered asynchronously. This adapter bridges the two WITHOUT the engine ever waiting:
4
+ //
5
+ // quote(leg) first call starts `signer.fill(...)` (build + policy + simulate + USD pricing in the SIGNER)
6
+ // and throws RpcOutage (the engine logs price.unusable/rpc-outage and retries on the next
7
+ // fresh tick, no attempt consumed). Once the signer has answered, the next quote() returns a
8
+ // Quote carrying the signer's verdict: policy/sim/caps refusals arrive as `simOk:false` with
9
+ // the signer's own reason, so the engine journals `policy.refused` with that reason.
10
+ // nextSignature() an INTENT id (`live_N`) the engine journals BEFORE anything is sent (CL-03).
11
+ // submit(leg) starts `signer.confirm(hash)` — the signer re-checks caps, reserves USD, has the fast
12
+ // wallet sign, verifies + re-simulates the bytes, submits and confirms — and returns
13
+ // `send-unknown` at once: the engine marks the leg in flight and settles it by `lookup`.
14
+ // lookup(intent) 'not-found' while the confirm runs; {confirmed, fill} / 'failed' once the signer answered;
15
+ // a transport error at confirm stays 'not-found' (the engine expires it after 90 s, never
16
+ // resends — the chain may still have it; the fast wallet is capped so exposure is bounded).
17
+ //
18
+ // What is real on devnet: the lamports the fill moves out of the fast wallet (fees of the wrap-and-unwrap
19
+ // sized to the plan's SOL leg), the signature, the on-chain memo naming the plan leg, the signer's USD
20
+ // per-trade/daily ledger. What is MODELLED: the token leg (Jupiter has no devnet, so no token changes hands);
21
+ // the fill is booked from the planned USD at the fire-time price and labelled `kind:'devnet-fill'` +
22
+ // `modelledTokens:true` in the journal. Nothing in this file signs, holds a key, or reaches mainnet: the signer
23
+ // refuses `fill` off devnet and never loads a fast wallet there.
24
+ import { RpcOutage } from "./executor.js";
25
+ /** How long a signer build is reused before it is rebuilt (the signer's own plan TTL is 90 s). */
26
+ export const LIVE_BUILD_TTL_MS = 60_000;
27
+ /** Remote refusals the signer formats as `reason: message`; anything else from the client is a transport problem. */
28
+ const REMOTE_REFUSAL = /^(network-mismatch|no-fast-wallet|build-failed|bad-request|panicked|no-wallet|signer-mismatch|plan-mismatch):\s*/;
29
+ export class LivePlanExecutor {
30
+ kind = 'live';
31
+ signer;
32
+ clock;
33
+ solUsd;
34
+ ttl;
35
+ onChange;
36
+ log;
37
+ builds = new Map();
38
+ pending = new Map();
39
+ intents = 0;
40
+ constructor(o) {
41
+ this.signer = o.signer;
42
+ this.clock = o.clock;
43
+ this.solUsd = o.solUsd;
44
+ this.ttl = o.buildTtlMs ?? LIVE_BUILD_TTL_MS;
45
+ this.onChange = o.onChange ?? (() => { });
46
+ this.log = o.log ?? (() => { });
47
+ }
48
+ /** The signer must be alive, on DEVNET, with a fast wallet loaded. Checked by the host before switching to live. */
49
+ async ready() {
50
+ if (!this.signer.alive)
51
+ return { ok: false, why: 'the signer process is not available' };
52
+ let status;
53
+ try {
54
+ status = await this.signer.status();
55
+ }
56
+ catch (e) {
57
+ return { ok: false, why: `signer status: ${msg(e)}` };
58
+ }
59
+ if (status.network !== 'devnet')
60
+ return { ok: false, why: `the signer is on ${status.network}; live plan fills run on devnet only` };
61
+ if (status.panic || status.ledgerLocked)
62
+ return { ok: false, why: 'live trading is panicked (dexterm wallet resume)' };
63
+ let fast;
64
+ try {
65
+ fast = await this.signer.fast();
66
+ }
67
+ catch (e) {
68
+ return { ok: false, why: `signer fast: ${msg(e)}` };
69
+ }
70
+ if (!fast.loaded || !fast.publicKey)
71
+ return { ok: false, why: fast.reason ?? 'no fast wallet is loaded in the signer' };
72
+ return { ok: true, network: 'devnet', fastWallet: fast.publicKey, balanceLamports: fast.balanceLamports };
73
+ }
74
+ /** One-line label for banners. */
75
+ describe(fastWallet) { return `LIVE · DEVNET · fast wallet${fastWallet ? ` ${fastWallet.slice(0, 4)}…${fastWallet.slice(-4)}` : ''}`; }
76
+ legId(req) { return `${req.plan.id}:${req.legKey}`; }
77
+ /** The SOL leg in lamports for this leg (buy: USD / SOL-USD; sell: tokens × price / SOL-USD). Null without a SOL price. */
78
+ lamportsFor(req) {
79
+ const sol = this.solUsd();
80
+ if (sol === null || !(sol > 0))
81
+ return null;
82
+ const usd = req.side === 'buy' ? (req.usd ?? 0) : req.tokenRaw !== null ? Number(req.tokenRaw) / 10 ** req.plan.decimals * req.priceUsd : 0;
83
+ if (!(usd > 0))
84
+ return null;
85
+ return BigInt(Math.round(usd / sol * 1e9));
86
+ }
87
+ quote(req) {
88
+ const id = this.legId(req);
89
+ const now = this.clock.now();
90
+ const b = this.builds.get(id);
91
+ if (b && b.state === 'building')
92
+ throw new RpcOutage();
93
+ if (b && b.state === 'outage') {
94
+ this.builds.delete(id);
95
+ throw new RpcOutage();
96
+ } // retried: a new build starts on the next quote
97
+ if (b && b.state === 'refused') {
98
+ this.builds.delete(id);
99
+ return this.refusedQuote(req, b.error ?? 'build refused', b.at);
100
+ }
101
+ if (b && b.state === 'built' && b.view && now - b.at < this.ttl && b.view.expiresAt > now)
102
+ return this.quoteFromView(req, b.view, b.at);
103
+ if (b)
104
+ this.builds.delete(id);
105
+ // start a build
106
+ const lamports = this.lamportsFor(req);
107
+ if (lamports === null) {
108
+ this.log(`live plans: no SOL/USD price yet to size ${id}; retrying`);
109
+ throw new RpcOutage();
110
+ }
111
+ const build = { state: 'building', view: null, error: null, at: now, usd: req.usd, tokenRaw: req.tokenRaw, priceUsd: req.priceUsd };
112
+ this.builds.set(id, build);
113
+ void this.signer.fill({ ref: id, mint: req.plan.mint, symbol: req.plan.symbol, decimals: req.plan.decimals, side: req.side, lamports: lamports.toString() })
114
+ .then((view) => { build.state = 'built'; build.view = view; build.at = this.clock.now(); this.onChange(); })
115
+ .catch((e) => {
116
+ const m = msg(e);
117
+ if (REMOTE_REFUSAL.test(m)) {
118
+ build.state = 'refused';
119
+ build.error = m;
120
+ }
121
+ else {
122
+ build.state = 'outage';
123
+ build.error = m;
124
+ this.log(`live plans: signer fill failed (${m}); retrying`);
125
+ }
126
+ this.onChange();
127
+ });
128
+ throw new RpcOutage();
129
+ }
130
+ refusedQuote(req, error, at) {
131
+ const m = REMOTE_REFUSAL.exec(error);
132
+ const reason = m ? m[1] : 'signer-refused';
133
+ return {
134
+ side: req.side, priceUsd: req.priceUsd, impactPct: 0, route: ['devnet-fill'], feeLamports: 0, tipLamports: 0,
135
+ simOk: false, simError: error, refusal: { reason, message: error.replace(REMOTE_REFUSAL, '') }, usd: req.usd, tokenRaw: req.tokenRaw, at,
136
+ };
137
+ }
138
+ /** The signer's verdict as a Quote: its policy / simulation / caps refusal (if any) travels with its own reason. */
139
+ quoteFromView(req, v, at) {
140
+ const bad = !v.policy.ok ? v.policy : !v.sim.ok ? { ok: false, reason: 'simulation-failed', message: v.sim.error ?? 'simulation failed' } : !v.precheck.ok ? v.precheck : null;
141
+ return {
142
+ side: req.side,
143
+ // a devnet fill has no route, so the signer cannot imply a price; the tick's price stands (on a Jupiter
144
+ // route the quote-implied price would come from the signer's view instead)
145
+ priceUsd: req.priceUsd,
146
+ impactPct: v.priceImpactPct ?? 0, route: [...v.route],
147
+ feeLamports: Number(v.feeLamports), tipLamports: 0,
148
+ simOk: bad === null, simError: bad ? `${bad.reason ?? 'refused'}: ${bad.message ?? ''}` : null,
149
+ refusal: bad ? { reason: bad.reason ?? 'signer-refused', message: bad.message ?? 'refused by the signer' } : null,
150
+ usd: req.usd, tokenRaw: req.tokenRaw, at,
151
+ };
152
+ }
153
+ nextSignature() { return `live_${++this.intents}`; }
154
+ submit(req) {
155
+ const id = this.legId(req);
156
+ const b = this.builds.get(id);
157
+ this.builds.delete(id);
158
+ if (!b || b.state !== 'built' || !b.view)
159
+ return { kind: 'send-failed', signature: req.signature, error: 'no fresh signer plan for this leg (rebuilt on the next tick)', feeLamports: 0 };
160
+ const view = b.view;
161
+ const p = { legId: id, hash: view.hash, state: 'confirming', outcome: null, error: null, fill: null, signature: null };
162
+ this.pending.set(req.signature, p);
163
+ void this.signer.confirm(view.hash)
164
+ .then((out) => {
165
+ p.outcome = out;
166
+ if (out.kind === 'filled') {
167
+ p.state = 'filled';
168
+ p.signature = out.signature;
169
+ p.fill = this.modelFill(req, out);
170
+ }
171
+ else if (out.kind === 'refused' || out.kind === 'send-failed') {
172
+ p.state = 'failed';
173
+ p.error = out.kind === 'refused' ? `${out.reason}: ${out.message}` : out.error;
174
+ p.signature = out.kind === 'send-failed' ? out.signature : null;
175
+ }
176
+ else {
177
+ p.state = 'unknown';
178
+ p.error = out.error;
179
+ p.signature = out.signature;
180
+ }
181
+ this.onChange();
182
+ })
183
+ .catch((e) => { p.state = 'unknown'; p.error = msg(e); this.log(`live plans: confirm transport error (${p.error}); outcome unknown, never resent`); this.onChange(); });
184
+ return { kind: 'send-unknown', signature: req.signature, error: 'live confirm in flight (fast wallet, devnet)', feeLamports: req.quote.feeLamports + req.quote.tipLamports };
185
+ }
186
+ /**
187
+ * The fill the engine books. SOL: the real on-chain delta the signer reported (fees of the wrap-and-unwrap).
188
+ * Tokens: MODELLED from the planned USD at the fire-time price (no token moves on devnet) and labelled so.
189
+ */
190
+ modelFill(req, out) {
191
+ const dec = req.plan.decimals;
192
+ const usd = req.usd ?? (req.tokenRaw !== null ? Number(req.tokenRaw) / 10 ** dec * req.quote.priceUsd : 0);
193
+ const tokens = req.tokenRaw ?? BigInt(Math.round(usd / req.quote.priceUsd * 10 ** dec));
194
+ return {
195
+ tokenDelta: req.quote.side === 'buy' ? tokens : -tokens,
196
+ solDelta: BigInt(out.fill.solDelta),
197
+ usd, priceUsd: req.quote.priceUsd,
198
+ kind: 'devnet-fill', signature: out.signature, modelledTokens: true, network: out.network,
199
+ };
200
+ }
201
+ lookup(signature) {
202
+ const p = this.pending.get(signature);
203
+ if (!p)
204
+ return 'not-found';
205
+ if (p.state === 'filled' && p.fill)
206
+ return { status: 'confirmed', fill: p.fill };
207
+ if (p.state === 'failed')
208
+ return { status: 'failed' };
209
+ return 'not-found'; // confirming, or an unknown outcome: never resent, expires in the engine
210
+ }
211
+ chainFill(signature) { return this.pending.get(signature)?.fill ?? null; }
212
+ /** Diagnostics for the TUI (what is building / in flight / settled). */
213
+ snapshot() {
214
+ return {
215
+ builds: [...this.builds.entries()].map(([leg, b]) => ({ leg, state: b.state, error: b.error })),
216
+ pending: [...this.pending.entries()].map(([intent, p]) => ({ leg: p.legId, intent, state: p.state, signature: p.signature, error: p.error })),
217
+ };
218
+ }
219
+ }
220
+ function msg(e) { return e instanceof Error ? e.message : String(e); }
@@ -0,0 +1,44 @@
1
+ // task-1355 — PAPER fire path for the plan engine (the default; no key, no wallet, no network, no mainnet).
2
+ //
3
+ // Implements the engine's Executor by booking fills deterministically at the fire-time tick price, exactly
4
+ // as paper trading does elsewhere (packages/data/src/paper.ts computeFill). The engine's signer-side policy
5
+ // (caps, allow-list, impact ceiling, panic, trigger re-confirm) still runs on every fire — this executor
6
+ // only stands in for "sign with the fast wallet + land", so a paper mandate exercises the full money-safe
7
+ // path with zero funds at risk. The real devnet/mainnet fire path (async Jupiter warm-quote +
8
+ // LiveExecutor.confirmAndSubmit) is a separate adapter and is intentionally not this one.
9
+ export class PaperPlanExecutor {
10
+ kind = 'paper';
11
+ clock;
12
+ solUsd;
13
+ fees;
14
+ impactPct;
15
+ chain = new Map();
16
+ sigN = 0;
17
+ constructor({ clock, solUsd = 150, baseFeeLamports = 5_000, priorityFeeLamports = 500_000, tipLamports = 100_000, impactPct = 0.4 }) {
18
+ this.clock = clock;
19
+ this.solUsd = solUsd;
20
+ this.fees = { baseFeeLamports, priorityFeeLamports, tipLamports };
21
+ this.impactPct = impactPct;
22
+ }
23
+ quote({ plan, side, usd, tokenRaw, priceUsd }) {
24
+ return {
25
+ side, priceUsd, impactPct: this.impactPct, route: ['Paper'],
26
+ feeLamports: this.fees.baseFeeLamports + this.fees.priorityFeeLamports, tipLamports: this.fees.tipLamports,
27
+ simOk: true, simError: null, usd: usd ?? null, tokenRaw: tokenRaw ?? null, at: this.clock.now(),
28
+ };
29
+ }
30
+ nextSignature() { return `PAPER${String(++this.sigN).padStart(6, '0')}`; }
31
+ computeFill(decimals, quote, usd, tokenRaw) {
32
+ const tokens = tokenRaw ?? BigInt(Math.round((usd / quote.priceUsd) * 10 ** decimals));
33
+ const usdValue = usd ?? Number(tokens) / 10 ** decimals * quote.priceUsd;
34
+ const lamports = BigInt(Math.round(usdValue / this.solUsd * 1e9));
35
+ return { tokenDelta: quote.side === 'buy' ? tokens : -tokens, solDelta: quote.side === 'buy' ? -lamports : lamports, usd: usdValue, priceUsd: quote.priceUsd };
36
+ }
37
+ submit({ plan, quote, signature, usd, tokenRaw }) {
38
+ const fill = this.computeFill(plan.decimals, quote, usd, tokenRaw);
39
+ this.chain.set(signature, { status: 'confirmed', fill });
40
+ return { kind: 'filled', signature, feeLamports: quote.feeLamports + quote.tipLamports, fill, roundTripMs: 0 };
41
+ }
42
+ lookup(signature) { return this.chain.get(signature) ?? 'not-found'; }
43
+ chainFill(signature) { return this.chain.get(signature)?.fill ?? null; }
44
+ }
@@ -0,0 +1,11 @@
1
+ // task-1355 — the EXECUTOR boundary the plan engine fires through.
2
+ //
3
+ // The engine never signs or sends. It calls an Executor that stands in for "quote -> build -> simulate ->
4
+ // sign (fast wallet) -> send -> confirm". In tests this is a scripted FixtureExecutor (no key, no wallet,
5
+ // no network; research/task-1292/sim/executor.mjs); in the app it is a live adapter bridging to the real
6
+ // signer-side LiveExecutor / PaperExecutor (plans/executor-live.ts). Caps/allow-list/panic are re-checked
7
+ // by the signer on every submit — the engine is only the trigger brain, never the key holder.
8
+ /** A transient RPC/transport failure: nothing fired, nothing sent; retried on the next fresh tick. */
9
+ export class RpcOutage extends Error {
10
+ constructor() { super('rpc outage'); this.name = 'RpcOutage'; }
11
+ }
@@ -0,0 +1,12 @@
1
+ // task-1355 — order-plan + mandate engine (SPEC §6.13). Front-end agnostic; lives in the signer trust
2
+ // domain alongside caps/killswitch/executor. Voice and agents can only PROPOSE; arming needs a
3
+ // signer-issued keypress/Phantom gesture proof; caps are frozen and the signer re-checks every fill.
4
+ export { PLAN_SCHEMA_VERSION, MATERIAL_FIELDS, PlanError, proposePlan, canonicalPlan, planHash, amendPlan, displayText, readBack, fmtUsd, priceForMc } from "./plan.js";
5
+ export { HARD, TIERS, MandateError, draftMandate, armMandate, revokeMandate, capsHash, mandateAlive } from "./mandate.js";
6
+ export { HOLD_MS, CHALLENGE_TTL_MS, ECHO_GUARD_MS, RISK_ACCEPT_TTL_MS, ConfirmError, Settings, ConfirmGate, gestureTier } from "./confirm.js";
7
+ export { Journal, redact, eventHash } from "./journal.js";
8
+ export { RpcOutage } from "./executor.js";
9
+ export { PaperPlanExecutor } from "./executor-paper.js";
10
+ export { LivePlanExecutor, LIVE_BUILD_TTL_MS } from "./executor-live.js";
11
+ export { PlanEngine, PolicyRefusal, TERMINAL } from "./engine.js";
12
+ export { PlanService, parseAmount, parseTrigger, parseExit, planStatusLabel, defaultPlansJournalPath } from "./service.js";
@@ -0,0 +1,77 @@
1
+ // task-1355 — the append-only, hash-chained JOURNAL (audit log + crash-recovery source of truth; §6.13.3/§6.13.12).
2
+ //
3
+ // Every state change is an event: {seq, ts, type, planId?, mandateId?, data, prev, hash}. `hash` covers
4
+ // the whole event including `prev`, so a removed, reordered or edited line breaks the chain and
5
+ // `verify()` reports exactly where. Signatures are journaled BEFORE a send (CL-03), so a crash between
6
+ // "sent" and "confirmed" is recoverable by looking the signature up, never by re-sending. Secrets never
7
+ // enter the journal: there is no field for keys, and `redact()` scrubs anything key-shaped defensively.
8
+ // Ported from the task-1292 reference simulation (research/task-1292/sim/journal.mjs), typed.
9
+ import { createHash } from 'node:crypto';
10
+ import { appendFileSync, readFileSync, existsSync, mkdirSync } from 'node:fs';
11
+ import { dirname } from 'node:path';
12
+ const KEY_SHAPED = /(?:[1-9A-HJ-NP-Za-km-z]{85,90})|(?:\[(?:\s*\d{1,3}\s*,){63}\s*\d{1,3}\s*\])/g;
13
+ export function redact(s) { return String(s).replace(KEY_SHAPED, '[REDACTED]'); }
14
+ export function eventHash(e) {
15
+ const body = JSON.stringify({ seq: e.seq, ts: e.ts, type: e.type, planId: e.planId ?? null, mandateId: e.mandateId ?? null, data: e.data ?? null, prev: e.prev });
16
+ return createHash('sha256').update(body).digest('hex');
17
+ }
18
+ export class Journal {
19
+ path;
20
+ clock;
21
+ events = [];
22
+ head = 'genesis';
23
+ dirReady = false;
24
+ /** `path` optional: when given, every event is also appended to a JSONL file (crash recovery / audit export). */
25
+ constructor({ path = null, clock }) {
26
+ this.path = path;
27
+ this.clock = clock;
28
+ if (path && existsSync(path))
29
+ this.load(path);
30
+ }
31
+ append(type, fields = {}) {
32
+ const base = {
33
+ seq: this.events.length + 1,
34
+ ts: this.clock.now(),
35
+ type,
36
+ planId: fields.planId ?? null,
37
+ mandateId: fields.mandateId ?? null,
38
+ data: fields.data ?? null,
39
+ prev: this.head,
40
+ };
41
+ const e = { ...base, hash: eventHash(base) };
42
+ this.events.push(e);
43
+ this.head = e.hash;
44
+ if (this.path) {
45
+ // task-1376: a fresh HOME has no ~/.dexterm yet; create it (owner-only) on the first write instead of ENOENT.
46
+ if (!this.dirReady) {
47
+ mkdirSync(dirname(this.path), { recursive: true, mode: 0o700 });
48
+ this.dirReady = true;
49
+ }
50
+ appendFileSync(this.path, redact(JSON.stringify(e)) + '\n');
51
+ }
52
+ return e;
53
+ }
54
+ load(path) {
55
+ const lines = readFileSync(path, 'utf8').split('\n').filter(Boolean);
56
+ this.events = lines.map((l) => JSON.parse(l));
57
+ this.head = this.events.length ? this.events[this.events.length - 1].hash : 'genesis';
58
+ }
59
+ /** Verify the chain. Returns {ok:true} or {ok:false, atSeq, why}. */
60
+ verify() {
61
+ let prev = 'genesis';
62
+ for (let i = 0; i < this.events.length; i++) {
63
+ const e = this.events[i];
64
+ if (e.seq !== i + 1)
65
+ return { ok: false, atSeq: e.seq, why: 'seq gap' };
66
+ if (e.prev !== prev)
67
+ return { ok: false, atSeq: e.seq, why: 'prev mismatch' };
68
+ const { hash, ...rest } = e;
69
+ if (eventHash(rest) !== hash)
70
+ return { ok: false, atSeq: e.seq, why: 'hash mismatch' };
71
+ prev = e.hash;
72
+ }
73
+ return { ok: true, count: this.events.length };
74
+ }
75
+ byPlan(planId) { return this.events.filter((e) => e.planId === planId); }
76
+ types() { return this.events.map((e) => e.type); }
77
+ }
@@ -0,0 +1,129 @@
1
+ // task-1355 — MANDATE schema, arming, and the signer-side admission check (SPEC §6.13.2).
2
+ //
3
+ // A mandate is a standing authorization. It is DRAFTED by anyone (voice, Claude, a preset, the user) and
4
+ // ARMED only by a gesture proof of kind 'keypress' or 'phantom' that the signer side itself issued and
5
+ // consumed. There is no `raiseCap()`; a mandate's caps are immutable after arming — a change is a new
6
+ // draft that needs a new arming gesture. Hard ceilings clamp every cap regardless of what was asked.
7
+ // Ported from the task-1292 reference simulation (research/task-1292/sim/mandate.mjs), typed and frozen.
8
+ import { createHash } from 'node:crypto';
9
+ export const HARD = Object.freeze({
10
+ totalUsd: 2000, perTradeUsd: 500, perCoinUsd: 1000, dailyLossUsd: 2000,
11
+ maxArmedPlans: 200, maxInFlight: 25, maxOpenPositions: 100,
12
+ maxSlippageBps: 1000, maxPriceImpactPct: 3,
13
+ tipBudgetLamports: 500_000_000, maxTipLamportsPerTx: 10_000_000, maxPriorityFeeLamportsPerTx: 10_000_000,
14
+ ttlMs: 24 * 3600_000, maxSendsPerSec: 20,
15
+ });
16
+ /** Confirmation tiers and the multiplier they apply to a mandate's per-trade cap for plans confirmed that way. */
17
+ export const TIERS = Object.freeze({
18
+ 'keypress': { rank: 0, perTradeMultiplier: 1.0, canArmMandate: true, canArmBatch: true },
19
+ 'ptt-held': { rank: 1, perTradeMultiplier: 0.5, canArmMandate: false, canArmBatch: false },
20
+ 'hands-free': { rank: 2, perTradeMultiplier: 0.25, canArmMandate: false, canArmBatch: false },
21
+ });
22
+ export class MandateError extends Error {
23
+ reason;
24
+ constructor(reason, message) {
25
+ super(message);
26
+ this.name = 'MandateError';
27
+ this.reason = reason;
28
+ }
29
+ }
30
+ function clamp(v, dflt, hardMax) {
31
+ const n = typeof v === 'number' ? v : Number(v);
32
+ if (!(n > 0) || !Number.isFinite(n))
33
+ return dflt;
34
+ return Math.min(n, hardMax);
35
+ }
36
+ /** Validate an untrusted mandate draft. Every cap is clamped to the hard ceiling; nothing is coerced upward. */
37
+ export function draftMandate(input, ctx) {
38
+ const x = input ?? {};
39
+ const now = ctx.clock.now();
40
+ const actions = Array.isArray(x.actions) ? x.actions.filter((a) => a === 'buy' || a === 'sell') : ['buy', 'sell'];
41
+ if (actions.length === 0)
42
+ throw new MandateError('bad-request', 'at least one of buy/sell');
43
+ const m = {
44
+ v: 1,
45
+ id: ctx.id ?? `mandate_${now.toString(36)}_${Math.random().toString(36).slice(2, 8)}`,
46
+ wallet: 'fast', // the capped fast wallet; the main wallet is never named here
47
+ budget: {
48
+ totalUsd: clamp(x.totalUsd, 100, HARD.totalUsd),
49
+ perTradeUsd: clamp(x.perTradeUsd, 20, HARD.perTradeUsd),
50
+ perCoinUsd: clamp(x.perCoinUsd, 40, HARD.perCoinUsd),
51
+ dailyLossUsd: clamp(x.dailyLossUsd, 50, HARD.dailyLossUsd),
52
+ },
53
+ limits: {
54
+ maxArmedPlans: clamp(x.maxArmedPlans, 20, HARD.maxArmedPlans),
55
+ maxInFlight: clamp(x.maxInFlight, 5, HARD.maxInFlight),
56
+ maxOpenPositions: clamp(x.maxOpenPositions, 20, HARD.maxOpenPositions),
57
+ maxSlippageBps: clamp(x.maxSlippageBps, 500, HARD.maxSlippageBps),
58
+ maxPriceImpactPct: clamp(x.maxPriceImpactPct, 3, HARD.maxPriceImpactPct),
59
+ maxSendsPerSec: clamp(x.maxSendsPerSec, 5, HARD.maxSendsPerSec),
60
+ },
61
+ fees: {
62
+ tipBudgetLamports: clamp(x.tipBudgetLamports, 50_000_000, HARD.tipBudgetLamports),
63
+ maxTipLamportsPerTx: clamp(x.maxTipLamportsPerTx, 1_000_000, HARD.maxTipLamportsPerTx),
64
+ maxPriorityFeeLamportsPerTx: clamp(x.maxPriorityFeeLamportsPerTx, 2_000_000, HARD.maxPriorityFeeLamportsPerTx),
65
+ },
66
+ allowed: {
67
+ actions,
68
+ safetyFilter: typeof x.safetyFilter === 'string' ? x.safetyFilter.slice(0, 32) : 'safety-v1',
69
+ mints: Array.isArray(x.mints) ? x.mints.filter((s) => typeof s === 'string').slice(0, 500) : null, // optional explicit allow-list
70
+ },
71
+ maxTier: (typeof x.maxTier === 'string' && x.maxTier in TIERS) ? x.maxTier : 'keypress',
72
+ createdAt: now,
73
+ expiresAt: now + clamp(x.ttlMs, 4 * 3600_000, HARD.ttlMs),
74
+ status: 'draft', armedAt: null, armedBy: null, revokedAt: null, revokedReason: null,
75
+ };
76
+ // perTrade can never exceed total; perCoin never exceeds total.
77
+ m.budget.perTradeUsd = Math.min(m.budget.perTradeUsd, m.budget.totalUsd);
78
+ m.budget.perCoinUsd = Math.min(m.budget.perCoinUsd, m.budget.totalUsd);
79
+ // Caps are immutable after drafting: a different cap is a different draft that needs its own gesture.
80
+ Object.freeze(m.budget);
81
+ Object.freeze(m.limits);
82
+ Object.freeze(m.fees);
83
+ Object.freeze(m.allowed);
84
+ return m;
85
+ }
86
+ /**
87
+ * Arm a mandate. Only a signer-issued, unconsumed 'keypress' or 'phantom' proof bound to THIS mandate's
88
+ * caps hash is accepted. Spoken proofs of any tier are refused: voice can never arm a mandate.
89
+ */
90
+ export function armMandate(m, proof, ctx) {
91
+ if (m.status !== 'draft')
92
+ throw new MandateError('not-draft', `mandate is ${m.status}`);
93
+ if (!proof || proof.kind !== 'gesture' || !proof.signerIssued)
94
+ throw new MandateError('no-gesture', 'arming needs a signer-issued gesture proof');
95
+ if (proof.gesture !== 'keypress' && proof.gesture !== 'phantom')
96
+ throw new MandateError('voice-cannot-arm', `a ${proof.gesture} proof cannot arm a mandate`);
97
+ if (proof.subject !== `mandate:${m.id}:${capsHash(m)}`)
98
+ throw new MandateError('proof-mismatch', 'the proof is for a different mandate or different caps');
99
+ if (proof.consumed)
100
+ throw new MandateError('proof-consumed', 'this proof was already used');
101
+ proof.consumed = true;
102
+ const now = ctx.clock.now();
103
+ if (now > m.expiresAt)
104
+ throw new MandateError('expired', 'mandate draft expired');
105
+ m.status = 'armed';
106
+ m.armedAt = now;
107
+ m.armedBy = { gesture: proof.gesture, proofId: proof.id };
108
+ return m;
109
+ }
110
+ export function revokeMandate(m, reason, ctx) {
111
+ if (m.status === 'revoked')
112
+ return m;
113
+ m.status = 'revoked';
114
+ m.revokedAt = ctx.clock.now();
115
+ m.revokedReason = String(reason).slice(0, 120);
116
+ return m;
117
+ }
118
+ /** Hash of the caps a gesture arms — the proof binds to these numbers, not to the mandate id alone. */
119
+ export function capsHash(m) {
120
+ return createHash('sha256').update(JSON.stringify({ b: m.budget, l: m.limits, f: m.fees, a: m.allowed, t: m.maxTier, e: m.expiresAt, w: m.wallet })).digest('hex').slice(0, 16);
121
+ }
122
+ /** Alive = armed and not expired. Expiry is checked on every call, never cached. */
123
+ export function mandateAlive(m, now) {
124
+ if (m.status !== 'armed')
125
+ return { ok: false, reason: m.status === 'draft' ? 'mandate-not-armed' : `mandate-${m.status}` };
126
+ if (now > m.expiresAt)
127
+ return { ok: false, reason: 'mandate-expired' };
128
+ return { ok: true };
129
+ }