perp-cli 0.9.8 → 0.12.0

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 (254) hide show
  1. package/README.md +30 -25
  2. package/dist/_stderr-filter.d.ts +0 -0
  3. package/dist/_stderr-filter.js +30 -0
  4. package/dist/agent-wallet/expiry.d.ts +15 -0
  5. package/dist/agent-wallet/expiry.js +26 -0
  6. package/dist/agent-wallet/lighter-keystore.d.ts +34 -0
  7. package/dist/agent-wallet/lighter-keystore.js +232 -0
  8. package/dist/agent-wallet/passphrase.d.ts +13 -0
  9. package/dist/agent-wallet/passphrase.js +54 -0
  10. package/dist/agent-wallet/signing-strategy.d.ts +67 -0
  11. package/dist/agent-wallet/signing-strategy.js +51 -0
  12. package/dist/agent-wallet/store.d.ts +37 -0
  13. package/dist/agent-wallet/store.js +172 -0
  14. package/dist/api/public/_http.d.ts +15 -0
  15. package/dist/api/public/_http.js +24 -0
  16. package/dist/api/public/hyperliquid.js +36 -24
  17. package/dist/api/public/lighter.d.ts +7 -1
  18. package/dist/api/public/lighter.js +85 -37
  19. package/dist/api/public/pacifica.js +47 -18
  20. package/dist/bot/engine.js +23 -4
  21. package/dist/bot/presets.js +3 -3
  22. package/dist/bot/strategies/funding-arb-strategy.d.ts +0 -23
  23. package/dist/bot/strategies/funding-arb-strategy.js +41 -176
  24. package/dist/bot/strategies/funding-auto.js +2 -2
  25. package/dist/bridge-engine.js +102 -49
  26. package/dist/cli-spec.js +1 -2
  27. package/dist/commands/account.js +2 -500
  28. package/dist/commands/agent.d.ts +8 -2
  29. package/dist/commands/agent.js +1903 -366
  30. package/dist/commands/alerts.js +3 -1
  31. package/dist/commands/arb/index.js +37 -7
  32. package/dist/commands/arb-auto.js +44 -11
  33. package/dist/commands/arb.js +83 -34
  34. package/dist/commands/bot.d.ts +1 -1
  35. package/dist/commands/bot.js +20 -16
  36. package/dist/commands/bridge.d.ts +1 -1
  37. package/dist/commands/bridge.js +4 -4
  38. package/dist/commands/funds.d.ts +1 -1
  39. package/dist/commands/funds.js +18 -68
  40. package/dist/commands/jobs.d.ts +1 -1
  41. package/dist/commands/jobs.js +2 -2
  42. package/dist/commands/manage.d.ts +1 -1
  43. package/dist/commands/manage.js +12 -84
  44. package/dist/commands/plan.d.ts +1 -1
  45. package/dist/commands/plan.js +2 -2
  46. package/dist/commands/portfolio.d.ts +19 -3
  47. package/dist/commands/portfolio.js +747 -163
  48. package/dist/commands/rebalance.d.ts +1 -1
  49. package/dist/commands/rebalance.js +18 -12
  50. package/dist/commands/risk.js +2 -6
  51. package/dist/commands/settings.js +30 -53
  52. package/dist/commands/trade.js +16 -15
  53. package/dist/commands/wallet.d.ts +2 -1
  54. package/dist/commands/wallet.js +1122 -176
  55. package/dist/config.d.ts +9 -0
  56. package/dist/config.js +55 -9
  57. package/dist/errors.d.ts +64 -0
  58. package/dist/errors.js +82 -2
  59. package/dist/event-stream.js +12 -1
  60. package/dist/exchanges/aster-typed-data.d.ts +156 -0
  61. package/dist/exchanges/aster-typed-data.js +189 -0
  62. package/dist/exchanges/aster.d.ts +60 -11
  63. package/dist/exchanges/aster.js +278 -136
  64. package/dist/exchanges/hyperliquid.d.ts +14 -0
  65. package/dist/exchanges/hyperliquid.js +88 -34
  66. package/dist/exchanges/lighter-spot.js +19 -5
  67. package/dist/exchanges/lighter.d.ts +66 -0
  68. package/dist/exchanges/lighter.js +213 -31
  69. package/dist/exchanges/pacifica-typed-data.d.ts +75 -0
  70. package/dist/exchanges/pacifica-typed-data.js +74 -0
  71. package/dist/exchanges/pacifica.d.ts +29 -1
  72. package/dist/exchanges/pacifica.js +94 -6
  73. package/dist/funding/rates.js +59 -21
  74. package/dist/guardrail/contracts.d.ts +14 -0
  75. package/dist/guardrail/contracts.js +72 -0
  76. package/dist/guardrail/perp-guardrail.d.ts +21 -0
  77. package/dist/guardrail/perp-guardrail.js +145 -0
  78. package/dist/index.d.ts +1 -1
  79. package/dist/index.js +307 -362
  80. package/dist/jobs.js +3 -1
  81. package/dist/mcp-server.js +92 -88
  82. package/dist/position-history.js +47 -4
  83. package/dist/settings.d.ts +69 -3
  84. package/dist/settings.js +3 -2
  85. package/dist/signer/index.d.ts +2 -0
  86. package/dist/signer/index.js +2 -0
  87. package/dist/signer/ows-evm.d.ts +32 -0
  88. package/dist/signer/ows-evm.js +86 -0
  89. package/dist/signer/ows-loader.d.ts +2 -0
  90. package/dist/signer/ows-loader.js +6 -0
  91. package/dist/signer/ows-solana.d.ts +25 -0
  92. package/dist/signer/ows-solana.js +56 -0
  93. package/dist/smart-order.d.ts +8 -2
  94. package/dist/smart-order.js +46 -11
  95. package/dist/strategies/funding-arb.js +29 -8
  96. package/dist/trade-validator.js +30 -5
  97. package/dist/utils.d.ts +10 -0
  98. package/dist/utils.js +19 -0
  99. package/package.json +4 -2
  100. package/skills/perp-cli/SKILL.md +23 -14
  101. package/skills/perp-cli/references/agent-operations.md +6 -4
  102. package/skills/perp-cli/references/commands.md +55 -34
  103. package/skills/perp-cli/scripts/preflight.sh +4 -4
  104. package/skills/perp-cli/scripts/spot-perp-scan.sh +2 -2
  105. package/dist/__tests__/alert-logic.test.d.ts +0 -1
  106. package/dist/__tests__/alert-logic.test.js +0 -107
  107. package/dist/__tests__/arb-auto-3dex.test.d.ts +0 -1
  108. package/dist/__tests__/arb-auto-3dex.test.js +0 -397
  109. package/dist/__tests__/arb-history-stats.test.d.ts +0 -1
  110. package/dist/__tests__/arb-history-stats.test.js +0 -176
  111. package/dist/__tests__/arb-logic.test.d.ts +0 -1
  112. package/dist/__tests__/arb-logic.test.js +0 -84
  113. package/dist/__tests__/arb-manage.test.d.ts +0 -1
  114. package/dist/__tests__/arb-manage.test.js +0 -253
  115. package/dist/__tests__/arb-new-features.test.d.ts +0 -1
  116. package/dist/__tests__/arb-new-features.test.js +0 -457
  117. package/dist/__tests__/arb-sizing.test.d.ts +0 -1
  118. package/dist/__tests__/arb-sizing.test.js +0 -48
  119. package/dist/__tests__/arb-state.test.d.ts +0 -1
  120. package/dist/__tests__/arb-state.test.js +0 -284
  121. package/dist/__tests__/arb-userflow.test.d.ts +0 -1
  122. package/dist/__tests__/arb-userflow.test.js +0 -945
  123. package/dist/__tests__/arb-utils.test.d.ts +0 -1
  124. package/dist/__tests__/arb-utils.test.js +0 -264
  125. package/dist/__tests__/bot-conditions.test.d.ts +0 -1
  126. package/dist/__tests__/bot-conditions.test.js +0 -341
  127. package/dist/__tests__/client-id-tracker.test.d.ts +0 -1
  128. package/dist/__tests__/client-id-tracker.test.js +0 -137
  129. package/dist/__tests__/commands/new-atomic-commands.test.d.ts +0 -1
  130. package/dist/__tests__/commands/new-atomic-commands.test.js +0 -502
  131. package/dist/__tests__/commands/order-intent.test.d.ts +0 -1
  132. package/dist/__tests__/commands/order-intent.test.js +0 -600
  133. package/dist/__tests__/commands/trade-commands.test.d.ts +0 -1
  134. package/dist/__tests__/commands/trade-commands.test.js +0 -821
  135. package/dist/__tests__/config.test.d.ts +0 -1
  136. package/dist/__tests__/config.test.js +0 -86
  137. package/dist/__tests__/cross-chain-margin.test.d.ts +0 -1
  138. package/dist/__tests__/cross-chain-margin.test.js +0 -287
  139. package/dist/__tests__/dex-asset-map.test.d.ts +0 -1
  140. package/dist/__tests__/dex-asset-map.test.js +0 -191
  141. package/dist/__tests__/errors.test.d.ts +0 -1
  142. package/dist/__tests__/errors.test.js +0 -110
  143. package/dist/__tests__/event-stream.test.d.ts +0 -1
  144. package/dist/__tests__/event-stream.test.js +0 -276
  145. package/dist/__tests__/exchanges/interface.test.d.ts +0 -1
  146. package/dist/__tests__/exchanges/interface.test.js +0 -132
  147. package/dist/__tests__/exchanges/mock-adapter.d.ts +0 -69
  148. package/dist/__tests__/exchanges/mock-adapter.js +0 -137
  149. package/dist/__tests__/execution-log.test.d.ts +0 -1
  150. package/dist/__tests__/execution-log.test.js +0 -106
  151. package/dist/__tests__/funding-calc.test.d.ts +0 -1
  152. package/dist/__tests__/funding-calc.test.js +0 -71
  153. package/dist/__tests__/funding-history.test.d.ts +0 -1
  154. package/dist/__tests__/funding-history.test.js +0 -343
  155. package/dist/__tests__/funding-rates.test.d.ts +0 -1
  156. package/dist/__tests__/funding-rates.test.js +0 -342
  157. package/dist/__tests__/funding.test.d.ts +0 -1
  158. package/dist/__tests__/funding.test.js +0 -173
  159. package/dist/__tests__/gap-logic.test.d.ts +0 -1
  160. package/dist/__tests__/gap-logic.test.js +0 -43
  161. package/dist/__tests__/hip3-dex.test.d.ts +0 -1
  162. package/dist/__tests__/hip3-dex.test.js +0 -234
  163. package/dist/__tests__/integration/agent-features.integration.test.d.ts +0 -1
  164. package/dist/__tests__/integration/agent-features.integration.test.js +0 -553
  165. package/dist/__tests__/integration/atomic-commands.integration.test.d.ts +0 -13
  166. package/dist/__tests__/integration/atomic-commands.integration.test.js +0 -246
  167. package/dist/__tests__/integration/bridge-simulation.integration.test.d.ts +0 -1
  168. package/dist/__tests__/integration/bridge-simulation.integration.test.js +0 -453
  169. package/dist/__tests__/integration/bridge-strict.integration.test.d.ts +0 -1
  170. package/dist/__tests__/integration/bridge-strict.integration.test.js +0 -812
  171. package/dist/__tests__/integration/bridge.integration.test.d.ts +0 -1
  172. package/dist/__tests__/integration/bridge.integration.test.js +0 -309
  173. package/dist/__tests__/integration/cli-e2e.integration.test.d.ts +0 -1
  174. package/dist/__tests__/integration/cli-e2e.integration.test.js +0 -202
  175. package/dist/__tests__/integration/dex-arb.integration.test.d.ts +0 -1
  176. package/dist/__tests__/integration/dex-arb.integration.test.js +0 -116
  177. package/dist/__tests__/integration/envelope-consistency.integration.test.d.ts +0 -13
  178. package/dist/__tests__/integration/envelope-consistency.integration.test.js +0 -205
  179. package/dist/__tests__/integration/hip3-dex.integration.test.d.ts +0 -1
  180. package/dist/__tests__/integration/hip3-dex.integration.test.js +0 -147
  181. package/dist/__tests__/integration/hyperliquid.integration.test.d.ts +0 -1
  182. package/dist/__tests__/integration/hyperliquid.integration.test.js +0 -79
  183. package/dist/__tests__/integration/lighter.integration.test.d.ts +0 -1
  184. package/dist/__tests__/integration/lighter.integration.test.js +0 -53
  185. package/dist/__tests__/integration/new-commands-e2e.integration.test.d.ts +0 -9
  186. package/dist/__tests__/integration/new-commands-e2e.integration.test.js +0 -236
  187. package/dist/__tests__/integration/order-verification.integration.test.d.ts +0 -1
  188. package/dist/__tests__/integration/order-verification.integration.test.js +0 -321
  189. package/dist/__tests__/integration/pacifica.integration.test.d.ts +0 -1
  190. package/dist/__tests__/integration/pacifica.integration.test.js +0 -75
  191. package/dist/__tests__/integration/response-shapes.integration.test.d.ts +0 -1
  192. package/dist/__tests__/integration/response-shapes.integration.test.js +0 -278
  193. package/dist/__tests__/liquidity.test.d.ts +0 -1
  194. package/dist/__tests__/liquidity.test.js +0 -225
  195. package/dist/__tests__/plan-executor.test.d.ts +0 -1
  196. package/dist/__tests__/plan-executor.test.js +0 -314
  197. package/dist/__tests__/position-history.test.d.ts +0 -1
  198. package/dist/__tests__/position-history.test.js +0 -367
  199. package/dist/__tests__/retry.test.d.ts +0 -1
  200. package/dist/__tests__/retry.test.js +0 -310
  201. package/dist/__tests__/risk-assessment.test.d.ts +0 -1
  202. package/dist/__tests__/risk-assessment.test.js +0 -288
  203. package/dist/__tests__/security-adversarial.test.d.ts +0 -1
  204. package/dist/__tests__/security-adversarial.test.js +0 -574
  205. package/dist/__tests__/strategies.test.d.ts +0 -1
  206. package/dist/__tests__/strategies.test.js +0 -539
  207. package/dist/__tests__/trade-execution.test.d.ts +0 -1
  208. package/dist/__tests__/trade-execution.test.js +0 -129
  209. package/dist/__tests__/trade-validator.test.d.ts +0 -1
  210. package/dist/__tests__/trade-validator.test.js +0 -655
  211. package/dist/__tests__/utils.test.d.ts +0 -1
  212. package/dist/__tests__/utils.test.js +0 -76
  213. package/dist/arb/reconcile.d.ts +0 -28
  214. package/dist/arb/reconcile.js +0 -162
  215. package/dist/commands/alert.d.ts +0 -2
  216. package/dist/commands/alert.js +0 -421
  217. package/dist/commands/analytics.d.ts +0 -3
  218. package/dist/commands/analytics.js +0 -311
  219. package/dist/commands/arb-manage.d.ts +0 -5
  220. package/dist/commands/arb-manage.js +0 -5
  221. package/dist/commands/dashboard.d.ts +0 -3
  222. package/dist/commands/dashboard.js +0 -176
  223. package/dist/commands/deposit.d.ts +0 -4
  224. package/dist/commands/deposit.js +0 -573
  225. package/dist/commands/dex.d.ts +0 -3
  226. package/dist/commands/dex.js +0 -120
  227. package/dist/commands/env.d.ts +0 -2
  228. package/dist/commands/env.js +0 -136
  229. package/dist/commands/funding.d.ts +0 -3
  230. package/dist/commands/funding.js +0 -554
  231. package/dist/commands/gap.d.ts +0 -2
  232. package/dist/commands/gap.js +0 -305
  233. package/dist/commands/health.d.ts +0 -2
  234. package/dist/commands/health.js +0 -67
  235. package/dist/commands/multileg.d.ts +0 -16
  236. package/dist/commands/multileg.js +0 -174
  237. package/dist/commands/pnl.d.ts +0 -12
  238. package/dist/commands/pnl.js +0 -243
  239. package/dist/commands/run.d.ts +0 -3
  240. package/dist/commands/run.js +0 -202
  241. package/dist/commands/stream.d.ts +0 -5
  242. package/dist/commands/stream.js +0 -200
  243. package/dist/commands/withdraw.d.ts +0 -3
  244. package/dist/commands/withdraw.js +0 -187
  245. package/dist/exchanges/edgex.d.ts +0 -62
  246. package/dist/exchanges/edgex.js +0 -552
  247. package/dist/exchanges/nado.d.ts +0 -52
  248. package/dist/exchanges/nado.js +0 -223
  249. package/dist/funding/index.d.ts +0 -3
  250. package/dist/funding/index.js +0 -3
  251. package/dist/retry.d.ts +0 -74
  252. package/dist/retry.js +0 -129
  253. package/dist/ws/market-feed.d.ts +0 -74
  254. package/dist/ws/market-feed.js +0 -227
package/dist/jobs.js CHANGED
@@ -138,7 +138,9 @@ export function removeJob(id) {
138
138
  */
139
139
  function buildEnvString() {
140
140
  const keys = [
141
- "LIGHTER_PRIVATE_KEY", "LIGHTER_API_KEY", "LIGHTER_ACCOUNT_INDEX", "LIGHTER_API_KEY_INDEX",
141
+ // L2 agent key now lives in ~/.perp/lighter-agents/*.json (SSOT Rule #3),
142
+ // accountIndex/apiKeyIndex live in settings.agents.lighter — none need env passthrough.
143
+ "LIGHTER_PRIVATE_KEY",
142
144
  "PRIVATE_KEY", "pk",
143
145
  "PACIFICA_BUILDER_CODE", "NEXT_PUBLIC_BUILDER_CODE",
144
146
  "HL_REFERRAL_CODE", "LIGHTER_REFERRAL_CODE",
@@ -436,15 +436,15 @@ server.tool("suggest_command", "Given a natural language trading goal, suggest t
436
436
  else if (g.includes("grid") || g.includes("dca") || g.includes("bot")) {
437
437
  const symbol = extractSymbol(g) || "<symbol>";
438
438
  if (g.includes("grid")) {
439
- steps.push({ step: 1, command: `perp -e ${ex} --json market book ${symbol}`, description: "Check current price" }, { step: 2, command: `perp -e ${ex} --json bot quick-grid ${symbol}`, description: "Start grid bot", dangerous: true }, { step: 3, command: "perp --json jobs list", description: "Verify bot is running" });
439
+ steps.push({ step: 1, command: `perp -e ${ex} --json market book ${symbol}`, description: "Check current price" }, { step: 2, command: `perp -e ${ex} --json strategy quick-grid ${symbol}`, description: "Start grid bot", dangerous: true }, { step: 3, command: "perp --json background list", description: "Verify bot is running" });
440
440
  }
441
441
  else if (g.includes("dca")) {
442
442
  const amount = extractNumber(g) || "<amount>";
443
443
  const side = g.includes("sell") || g.includes("short") ? "sell" : "buy";
444
- steps.push({ step: 1, command: `perp -e ${ex} --json account balance`, description: "Check available balance" }, { step: 2, command: `perp -e ${ex} --json bot quick-dca ${symbol} ${side} ${amount} <interval>`, description: "Start DCA bot", dangerous: true }, { step: 3, command: "perp --json jobs list", description: "Verify bot is running" });
444
+ steps.push({ step: 1, command: `perp -e ${ex} --json account balance`, description: "Check available balance" }, { step: 2, command: `perp -e ${ex} --json strategy quick-dca ${symbol} ${side} ${amount} <interval>`, description: "Start DCA bot", dangerous: true }, { step: 3, command: "perp --json background list", description: "Verify bot is running" });
445
445
  }
446
446
  else {
447
- steps.push({ step: 1, command: "perp --json bot preset-list", description: "List available bot presets" }, { step: 2, command: `perp -e ${ex} --json bot quick-arb`, description: "Start arb bot", dangerous: true }, { step: 3, command: "perp --json jobs list", description: "Verify bot is running" });
447
+ steps.push({ step: 1, command: "perp --json strategy preset-list", description: "List available bot presets" }, { step: 2, command: `perp -e ${ex} --json strategy quick-arb`, description: "Start arb bot", dangerous: true }, { step: 3, command: "perp --json background list", description: "Verify bot is running" });
448
448
  }
449
449
  }
450
450
  else if (g.includes("backtest")) {
@@ -486,13 +486,13 @@ server.tool("suggest_command", "Given a natural language trading goal, suggest t
486
486
  steps.push({ step: 1, command: "perp --json rebalance check", description: "Check balance distribution" }, { step: 2, command: "perp --json rebalance plan", description: "Generate rebalance plan" }, { step: 3, command: "perp --json rebalance execute", description: "Execute rebalance", dangerous: true });
487
487
  }
488
488
  else if (g.includes("job") || g.includes("running") || g.includes("background")) {
489
- steps.push({ step: 1, command: "perp --json jobs list", description: "List running background jobs" });
489
+ steps.push({ step: 1, command: "perp --json background list", description: "List running background jobs" });
490
490
  }
491
491
  else if (g.includes("dex") || g.includes("hip-3") || g.includes("hip3")) {
492
492
  steps.push({ step: 1, command: "perp --json -e hl market hip3", description: "List HIP-3 deployed dexes" }, { step: 2, command: "perp --json -e hl --dex <name> market list", description: "Show markets on a specific dex" });
493
493
  }
494
494
  else if (g.includes("plan") || g.includes("composite") || g.includes("multi-step")) {
495
- steps.push({ step: 1, command: "perp plan example", description: "Show example execution plan format" }, { step: 2, command: "perp --json plan validate <file>", description: "Validate plan file" }, { step: 3, command: "perp --json plan execute <file> --dry-run", description: "Dry-run the plan", dangerous: true });
495
+ steps.push({ step: 1, command: "perp strategy plan example", description: "Show example execution plan format" }, { step: 2, command: "perp --json strategy plan validate <file>", description: "Validate plan file" }, { step: 3, command: "perp --json strategy plan execute <file> --dry-run", description: "Dry-run the plan", dangerous: true });
496
496
  }
497
497
  else if (g.includes("status") || g.includes("check") || g.includes("overview") || g.includes("portfolio")) {
498
498
  steps.push({ step: 1, command: `perp -e ${ex} --json status`, description: "Full account overview" }, { step: 2, command: `perp -e ${ex} --json account positions`, description: "Detailed positions" }, { step: 3, command: `perp -e ${ex} --json account orders`, description: "Open orders" }, { step: 4, command: "perp --json portfolio", description: "Cross-exchange portfolio summary" });
@@ -532,7 +532,7 @@ server.tool("suggest_command", "Given a natural language trading goal, suggest t
532
532
  steps.push({ step: 1, command: "perp settings show", description: "Show current settings" }, { step: 2, command: "perp settings referrals on", description: "Enable referral codes (no extra fees, just volume tracking)" });
533
533
  }
534
534
  else {
535
- steps.push({ step: 1, command: "perp agent capabilities", description: "List all available CLI capabilities" }, { step: 2, command: `perp -e ${ex} --json status`, description: "Check account status" }, { step: 3, command: "perp --json agent ping", description: "Check exchange connectivity" });
535
+ steps.push({ step: 1, command: `perp -e ${ex} --json status`, description: "Check account status" }, { step: 2, command: `perp -e ${ex} --json account balance`, description: "Check exchange connectivity via balance fetch" });
536
536
  }
537
537
  return {
538
538
  content: [{
@@ -831,7 +831,7 @@ server.tool("explain_command", "Explain what a perp CLI command does, its parame
831
831
  parameters: [],
832
832
  risks: sub === "send" || sub === "exchange" ? ["Transfers real funds cross-chain — verify addresses carefully"] : [],
833
833
  category: sub === "send" || sub === "exchange" ? "write" : "read",
834
- relatedCommands: ["perp bridge chains", "perp bridge quote"],
834
+ relatedCommands: ["perp funds bridge chains", "perp funds bridge quote"],
835
835
  };
836
836
  }
837
837
  else if (category === "funds") {
@@ -855,14 +855,14 @@ server.tool("explain_command", "Explain what a perp CLI command does, its parame
855
855
  relatedCommands: ["perp account positions", "perp portfolio"],
856
856
  };
857
857
  }
858
- else if (category === "bot" || category === "run") {
858
+ else if (category === "strategy" || category === "run") {
859
859
  explanation = {
860
860
  command,
861
861
  description: `Automated strategy: ${sub}. Runs as a background job.`,
862
862
  parameters: args.slice(2).map((a, i) => ({ name: `arg${i}`, value: a, description: "See --help" })),
863
- risks: ["Runs automated trades — monitor with 'perp jobs list'", "Use --dry-run to simulate first"],
863
+ risks: ["Runs automated trades — monitor with 'perp background list'", "Use --dry-run to simulate first"],
864
864
  category: "write",
865
- relatedCommands: ["perp jobs list", "perp jobs stop"],
865
+ relatedCommands: ["perp background list", "perp background stop"],
866
866
  };
867
867
  }
868
868
  else if (category === "history") {
@@ -878,11 +878,11 @@ server.tool("explain_command", "Explain what a perp CLI command does, its parame
878
878
  else if (category === "wallet") {
879
879
  explanation = {
880
880
  command,
881
- description: { show: "Show configured wallets with public addresses", list: "List configured wallets", balance: "Check on-chain USDC/SOL/ETH balances", generate: "Generate a new wallet keypair", import: "Import an existing private key", set: "Set private key for an exchange", use: "Set active wallet", remove: "Remove a wallet", rename: "Rename a wallet" }[sub] || `Wallet: ${sub}`,
881
+ description: { show: "Show configured wallets with public addresses", list: "List all wallets (OWS vault + legacy)", balance: "Check on-chain USDC/SOL/ETH balances", generate: "Generate a new OWS wallet (encrypted, multi-chain)", import: "Import key into OWS vault", migrate: "Migrate legacy wallets to OWS encrypted vault", set: "Set private key for an exchange (legacy)", use: "Set active wallet (OWS or legacy)", remove: "Remove a wallet", rename: "Rename a wallet", policy: "OWS signing policy: create/list/show/delete", key: "OWS API key: create/list/revoke", agent: "DEX-side agent delegation: approve/list/revoke/rotate/verify", deposit: "MoonPay multi-chain deposit (auto-converts to USDC)", setup: "One-click setup: wallet + guardrail policy + agent API key", backup: "Backup encrypted OWS vault", restore: "Restore OWS vault from encrypted backup", rotate: "Rotate wallet keys (new wallet + transfer assets)" }[sub] || `Wallet: ${sub}`,
882
882
  parameters: [],
883
- risks: sub === "generate" ? ["Store the private key securely — it cannot be recovered"] : sub === "remove" ? ["Wallet will be removed — ensure you have the private key backed up"] : [],
883
+ risks: sub === "generate" ? ["Save the mnemonic securely — it cannot be recovered. Keys are stored in ~/.ows/ encrypted vault."] : sub === "remove" ? ["Wallet will be removed — ensure you have backups"] : [],
884
884
  category: "read",
885
- relatedCommands: ["perp wallet show", "perp wallet balance"],
885
+ relatedCommands: ["perp wallet list", "perp wallet balance", "perp wallet agent list"],
886
886
  };
887
887
  }
888
888
  else if (category === "backtest") {
@@ -906,14 +906,14 @@ server.tool("explain_command", "Explain what a perp CLI command does, its parame
906
906
  };
907
907
  }
908
908
  else {
909
- const writeCommands = new Set(["trade", "funds", "manage", "rebalance"]);
909
+ const writeCommands = new Set(["trade", "funds", "manage"]);
910
910
  explanation = {
911
911
  command,
912
912
  description: `CLI command: ${command}. Run 'perp ${category} --help' for detailed usage.`,
913
913
  parameters: args.slice(1).map((a, i) => ({ name: `arg${i}`, value: a, description: "See --help for details" })),
914
914
  risks: writeCommands.has(category) ? ["This command may modify account state — review carefully"] : [],
915
915
  category: writeCommands.has(category) ? "write" : "read",
916
- relatedCommands: ["perp agent capabilities", "perp schema"],
916
+ relatedCommands: [`perp ${category} --help`],
917
917
  };
918
918
  }
919
919
  return { content: [{ type: "text", text: ok(explanation) }] };
@@ -1096,45 +1096,47 @@ server.resource("cli_schema", "perp://schema", { mimeType: "application/json", d
1096
1096
  check: { usage: "perp risk check --notional <usd> --leverage <n>", description: "Pre-trade risk check" },
1097
1097
  },
1098
1098
  },
1099
- bridge: {
1100
- description: "Cross-chain USDC bridge",
1099
+ strategy: {
1100
+ description: "Trading strategies — long-running bot algorithms + one-shot scripted plans",
1101
1101
  subcommands: {
1102
- chains: { usage: "perp bridge chains", description: "Supported chains" },
1103
- quote: { usage: "perp bridge quote --from <chain> --to <chain> --amount <n>", description: "Get quote" },
1104
- send: { usage: "perp bridge send --from <chain> --to <chain> --amount <n>", description: "Execute bridge" },
1105
- exchange: { usage: "perp bridge exchange --from <ex> --to <ex> --amount <n>", description: "Bridge between exchanges" },
1106
- status: { usage: "perp bridge status <orderId>", description: "Track bridge status" },
1107
- },
1108
- },
1109
- bot: {
1110
- description: "Automated trading bots",
1111
- subcommands: {
1112
- start: { usage: "perp bot start <config>", description: "Start bot from config file" },
1113
- twap: { usage: "perp bot twap <symbol> <side> <size> <duration>", description: "TWAP execution" },
1114
- grid: { usage: "perp bot grid <symbol> --range <pct> --grids <n> --size <usd>", description: "Grid trading bot" },
1115
- dca: { usage: "perp bot dca <symbol> <side> <amount> <interval>", description: "DCA bot" },
1116
- "funding-arb": { usage: "perp bot funding-arb", description: "Funding arb bot" },
1117
- "trailing-stop": { usage: "perp bot trailing-stop <symbol>", description: "Trailing stop bot" },
1118
- "quick-grid": { usage: "perp bot quick-grid <symbol>", description: "Quick grid bot" },
1119
- "quick-dca": { usage: "perp bot quick-dca <symbol> <side> <amount> <interval>", description: "Quick DCA bot" },
1120
- "quick-arb": { usage: "perp bot quick-arb", description: "Quick arb bot" },
1121
- "preset-list": { usage: "perp bot preset-list", description: "List available bot presets" },
1122
- preset: { usage: "perp bot preset <name> [symbol]", description: "Run a bot preset" },
1123
- example: { usage: "perp bot example", description: "Show example bot config" },
1102
+ start: { usage: "perp strategy start <config>", description: "Start bot from config file" },
1103
+ twap: { usage: "perp strategy twap <symbol> <side> <size> <duration>", description: "TWAP execution" },
1104
+ grid: { usage: "perp strategy grid <symbol> --range <pct> --grids <n> --size <usd>", description: "Grid trading bot" },
1105
+ dca: { usage: "perp strategy dca <symbol> <side> <amount> <interval>", description: "DCA bot" },
1106
+ "funding-arb": { usage: "perp strategy funding-arb", description: "Funding arb bot" },
1107
+ "trailing-stop": { usage: "perp strategy trailing-stop <symbol>", description: "Trailing stop bot" },
1108
+ "quick-grid": { usage: "perp strategy quick-grid <symbol>", description: "Quick grid bot" },
1109
+ "quick-dca": { usage: "perp strategy quick-dca <symbol> <side> <amount> <interval>", description: "Quick DCA bot" },
1110
+ "quick-arb": { usage: "perp strategy quick-arb", description: "Quick arb bot" },
1111
+ "preset-list": { usage: "perp strategy preset-list", description: "List available bot presets" },
1112
+ preset: { usage: "perp strategy preset <name> [symbol]", description: "Run a bot preset" },
1113
+ example: { usage: "perp strategy example", description: "Show example bot config" },
1114
+ "plan validate": { usage: "perp strategy plan validate <file>", description: "Validate scripted execution plan" },
1115
+ "plan execute": { usage: "perp strategy plan execute <file>", description: "Execute scripted plan" },
1116
+ "plan example": { usage: "perp strategy plan example", description: "Show plan format" },
1124
1117
  },
1125
1118
  },
1126
1119
  wallet: {
1127
- description: "Wallet management",
1120
+ description: "Wallet management (OWS encrypted vault + legacy + agent delegation + lifecycle)",
1128
1121
  subcommands: {
1129
1122
  show: { usage: "perp wallet show", description: "Show configured wallets with public addresses" },
1130
- list: { usage: "perp wallet list", description: "List wallets" },
1123
+ list: { usage: "perp wallet list", description: "List all wallets (OWS vault + legacy)" },
1131
1124
  balance: { usage: "perp wallet balance [name]", description: "On-chain balances" },
1132
- generate: { usage: "perp wallet generate solana|evm", description: "Generate new wallet" },
1133
- import: { usage: "perp wallet import solana|evm <privateKey>", description: "Import existing private key" },
1134
- set: { usage: "perp wallet set <exchange> <key>", description: "Set private key for exchange" },
1135
- use: { usage: "perp wallet use <name> [exchange]", description: "Set active wallet" },
1125
+ generate: { usage: "perp wallet generate [name]", description: "Generate new OWS wallet (encrypted, multi-chain)" },
1126
+ import: { usage: "perp wallet import <privateKey>", description: "Import key into OWS vault" },
1127
+ migrate: { usage: "perp wallet migrate", description: "Migrate legacy wallets to OWS vault" },
1128
+ set: { usage: "perp wallet set <exchange> <key>", description: "Set private key for exchange (legacy)" },
1129
+ use: { usage: "perp wallet use <name>", description: "Set active wallet (OWS or legacy)" },
1136
1130
  remove: { usage: "perp wallet remove <name>", description: "Remove a wallet" },
1137
1131
  rename: { usage: "perp wallet rename <oldName> <newName>", description: "Rename a wallet" },
1132
+ policy: { usage: "perp wallet policy <create|list|show|delete>", description: "OWS signing policies (guardrails for agent signing)" },
1133
+ key: { usage: "perp wallet key <create|list|revoke>", description: "OWS API keys (scoped agent access tokens)" },
1134
+ agent: { usage: "perp wallet agent <approve|list|revoke|rotate|verify>", description: "DEX-side agent wallet delegation (per-exchange)" },
1135
+ deposit: { usage: "perp wallet deposit <walletName>", description: "MoonPay multi-chain deposit (auto-converts to USDC)" },
1136
+ setup: { usage: "perp wallet setup", description: "One-click: wallet + guardrail policy + agent API key" },
1137
+ backup: { usage: "perp wallet backup", description: "Backup encrypted OWS vault" },
1138
+ restore: { usage: "perp wallet restore <file>", description: "Restore OWS vault from encrypted backup" },
1139
+ rotate: { usage: "perp wallet rotate --from <a> --to <b>", description: "Rotate wallet keys (create new + transfer assets)" },
1138
1140
  },
1139
1141
  },
1140
1142
  history: {
@@ -1160,44 +1162,37 @@ server.resource("cli_schema", "perp://schema", { mimeType: "application/json", d
1160
1162
  grid: { usage: "perp backtest grid", description: "Backtest grid strategy" },
1161
1163
  },
1162
1164
  },
1163
- plan: {
1164
- description: "Composite multi-step execution plans",
1165
- subcommands: {
1166
- validate: { usage: "perp plan validate <file>", description: "Validate plan" },
1167
- execute: { usage: "perp plan execute <file>", description: "Execute plan" },
1168
- example: { usage: "perp plan example", description: "Show plan format" },
1169
- },
1170
- },
1171
- rebalance: {
1172
- description: "Cross-exchange balance rebalancing",
1173
- subcommands: {
1174
- check: { usage: "perp rebalance check", description: "Check distribution" },
1175
- plan: { usage: "perp rebalance plan", description: "Generate plan" },
1176
- execute: { usage: "perp rebalance execute", description: "Execute rebalance" },
1177
- },
1178
- },
1179
- jobs: {
1180
- description: "Background job management",
1165
+ background: {
1166
+ description: "Background process supervisor (tmux sessions for strategies, alerts, etc.)",
1181
1167
  subcommands: {
1182
- list: { usage: "perp jobs list", description: "List running jobs" },
1183
- stop: { usage: "perp jobs stop <id>", description: "Stop a job" },
1184
- logs: { usage: "perp jobs logs <id>", description: "View job logs" },
1168
+ list: { usage: "perp background list", description: "List running jobs" },
1169
+ stop: { usage: "perp background stop <id>", description: "Stop a job" },
1170
+ logs: { usage: "perp background logs <id>", description: "View job logs" },
1171
+ remove: { usage: "perp background remove <id>", description: "Remove a job entry and its logs" },
1172
+ clean: { usage: "perp background clean", description: "Remove all stopped/done jobs" },
1185
1173
  },
1186
1174
  },
1187
1175
  funds: {
1188
- usage: "perp funds <deposit|withdraw|transfer|bridge> ...",
1189
- description: "Deposit, withdraw, bridge & transfer funds",
1176
+ usage: "perp funds <deposit|withdraw|transfer|bridge|rebalance|info> ...",
1177
+ description: "Fund movement — deposit, withdraw, transfer, cross-chain bridge, inter-exchange rebalance",
1190
1178
  subcommands: {
1191
- "deposit <exchange> <amount>": "Deposit USDC to exchange",
1192
- "withdraw <exchange> <amount>": "Withdraw USDC from exchange",
1193
- "transfer <amount> <address>": "HL internal transfer",
1194
- "bridge --from <chain> --to <chain> --amount <n> --recipient <addr>": "CCTP bridge",
1195
- info: "Show all deposit/withdraw routes & limits",
1179
+ "deposit <exchange> <amount>": "Deposit USDC to exchange (pacifica, hyperliquid, lighter)",
1180
+ "withdraw <exchange> <amount>": "Withdraw USDC from exchange (pacifica, hyperliquid, lighter)",
1181
+ "transfer <amount> <address>": "HL internal transfer (instant)",
1182
+ "bridge chains": "List supported chains and USDC addresses",
1183
+ "bridge quote --from <chain> --to <chain> --amount <n>": "Get bridge quotes from all providers",
1184
+ "bridge send --from <chain> --to <chain> --amount <n> [--provider cctp|relay|debridge]": "Execute bridge (auto-cheapest by default)",
1185
+ "bridge exchange --from <ex> --to <ex> --amount <n>": "Bridge USDC between exchanges (shortcut)",
1186
+ "bridge status <orderId>": "Check bridge order status",
1187
+ "rebalance check": "Show balances across exchanges",
1188
+ "rebalance plan": "Calculate optimal rebalancing moves",
1189
+ "rebalance execute [--auto-bridge]": "Execute rebalancing (withdraw → bridge → deposit)",
1190
+ info: "Show all deposit/withdraw/bridge/rebalance routes & limits",
1196
1191
  },
1197
1192
  },
1198
1193
  portfolio: { usage: "perp portfolio", description: "Cross-exchange portfolio summary" },
1199
1194
  status: { usage: "perp portfolio", description: "Full account overview" },
1200
- health: { usage: "perp agent ping", description: "Exchange connectivity check" },
1195
+ health: { usage: "perp account balance --json", description: "Exchange connectivity check via balance fetch" },
1201
1196
  settings: {
1202
1197
  description: "CLI settings",
1203
1198
  subcommands: {
@@ -1205,16 +1200,7 @@ server.resource("cli_schema", "perp://schema", { mimeType: "application/json", d
1205
1200
  referrals: { usage: "perp settings referrals on|off", description: "Toggle referral codes (no extra fees)" },
1206
1201
  },
1207
1202
  },
1208
- agent: {
1209
- description: "Agent discovery & execution",
1210
- subcommands: {
1211
- schema: { usage: "perp agent schema", description: "Full CLI schema as JSON" },
1212
- capabilities: { usage: "perp agent capabilities", description: "Capability list" },
1213
- plan: { usage: "perp agent plan '<goal>'", description: "Suggest commands for goal" },
1214
- exec: { usage: "perp agent exec <command...>", description: "Execute with JSON output" },
1215
- ping: { usage: "perp agent ping", description: "Health check" },
1216
- },
1217
- },
1203
+ // `agent` is now `wallet agent ...` — see `wallet.subcommands.agent` above.
1218
1204
  },
1219
1205
  tips: [
1220
1206
  "Always use --json for structured output when automating",
@@ -1253,12 +1239,30 @@ server.tool("trade_preview", "Preview a trade WITHOUT executing. Returns estimat
1253
1239
  const notional = estPrice ? Number(estPrice) * Number(size) : 0;
1254
1240
  const existingPos = positions.find(p => p.symbol.toUpperCase().includes(symbol.toUpperCase()));
1255
1241
  // Run validation
1242
+ // SSOT rule #2: validation failure must surface as a structured invalid
1243
+ // result, NOT be swallowed into a fake `{ valid: true }`. The previous
1244
+ // shape silently green-lit trades when the underlying balance/orderbook
1245
+ // fetches errored out.
1256
1246
  let validation;
1247
+ // For a preview the user has not yet committed leverage, so 1x is the
1248
+ // documented default exposed in the preview shape (no silent fallback —
1249
+ // the caller can override via existingPos.leverage when re-entering an
1250
+ // open position).
1251
+ const previewLeverage = existingPos?.leverage ?? 1;
1257
1252
  try {
1258
- validation = await validateTrade(adapter, { symbol, side, size: Number(size), leverage: existingPos?.leverage ?? 1 });
1259
- }
1260
- catch {
1261
- validation = { valid: true, checks: [], warnings: ["Validation unavailable — proceed with caution"] };
1253
+ validation = await validateTrade(adapter, { symbol, side, size: Number(size), leverage: previewLeverage });
1254
+ }
1255
+ catch (err) {
1256
+ const msg = err instanceof Error ? err.message : String(err);
1257
+ validation = {
1258
+ valid: false,
1259
+ checks: [{
1260
+ check: "symbol_valid",
1261
+ passed: false,
1262
+ message: `Validation could not run: ${msg}`,
1263
+ }],
1264
+ warnings: [],
1265
+ };
1262
1266
  }
1263
1267
  const preview = {
1264
1268
  order: { exchange, symbol, side, size, orderType, price: price ?? estPrice },
@@ -13,6 +13,32 @@ function ensureDir() {
13
13
  function genId() {
14
14
  return `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
15
15
  }
16
+ /**
17
+ * Validate position side from a stream event. SSOT rule #2: the stream must
18
+ * deliver a real side, never silently default to "long" — defaulting can flip
19
+ * a short into a long in the position history and corrupt P&L analytics.
20
+ */
21
+ function requireSide(value, eventType, exchange, symbol) {
22
+ if (value === "long" || value === "short")
23
+ return value;
24
+ throw new Error(`position_${eventType} for ${exchange}:${symbol} has missing/invalid side ` +
25
+ `(received ${JSON.stringify(value)}); refusing to silently default. ` +
26
+ `Fix the upstream adapter to emit { side: "long" | "short" } on every event.`);
27
+ }
28
+ /**
29
+ * Validate a stringified numeric field from a stream event. SSOT rule #2: do
30
+ * NOT silently default missing realizedPnl to "0", because we cannot
31
+ * distinguish "trade closed flat" from "adapter forgot the field".
32
+ */
33
+ function requireNumericString(value, fieldName, eventType, exchange, symbol) {
34
+ if (typeof value === "string" && value.trim() !== "" && Number.isFinite(Number(value)))
35
+ return value;
36
+ if (typeof value === "number" && Number.isFinite(value))
37
+ return String(value);
38
+ throw new Error(`position_${eventType} for ${exchange}:${symbol} has missing/invalid ${fieldName} ` +
39
+ `(received ${JSON.stringify(value)}); refusing to silently default. ` +
40
+ `Fix the upstream adapter to emit a numeric ${fieldName}.`);
41
+ }
16
42
  /** Append a position record to the log */
17
43
  export function logPosition(record) {
18
44
  ensureDir();
@@ -154,7 +180,10 @@ export function attachPositionLogger(onEvent) {
154
180
  if (event.type === "position_opened") {
155
181
  const id = genId();
156
182
  const symbol = String(data.symbol ?? "");
157
- const side = String(data.side ?? "long");
183
+ if (!symbol) {
184
+ throw new Error(`position_opened on ${exchange} missing symbol; refusing to silently default. Raw data: ${JSON.stringify(data)}`);
185
+ }
186
+ const side = requireSide(data.side, "opened", exchange, symbol);
158
187
  const key = `${exchange}:${symbol}`;
159
188
  openPositions.set(key, { id, openedAt: ts, entryPrice: String(data.entryPrice ?? "") });
160
189
  logPosition({
@@ -172,9 +201,15 @@ export function attachPositionLogger(onEvent) {
172
201
  }
173
202
  else if (event.type === "position_updated") {
174
203
  const symbol = String(data.symbol ?? "");
175
- const side = String(data.side ?? "long");
204
+ if (!symbol) {
205
+ throw new Error(`position_updated on ${exchange} missing symbol; refusing to silently default. Raw data: ${JSON.stringify(data)}`);
206
+ }
207
+ const side = requireSide(data.side, "updated", exchange, symbol);
176
208
  const key = `${exchange}:${symbol}`;
177
209
  const tracked = openPositions.get(key);
210
+ // tracked?.id ?? genId() is NOT a fallback in the SSOT sense: it covers the
211
+ // legitimate case where the stream delivers an update for a position the
212
+ // logger never saw open (out-of-order delivery, mid-session attach).
178
213
  logPosition({
179
214
  id: tracked?.id ?? genId(),
180
215
  exchange,
@@ -194,12 +229,20 @@ export function attachPositionLogger(onEvent) {
194
229
  }
195
230
  else if (event.type === "position_closed") {
196
231
  const symbol = String(data.symbol ?? "");
197
- const side = String(data.side ?? "long");
232
+ if (!symbol) {
233
+ throw new Error(`position_closed on ${exchange} missing symbol; refusing to silently default. Raw data: ${JSON.stringify(data)}`);
234
+ }
235
+ const side = requireSide(data.side, "closed", exchange, symbol);
198
236
  const key = `${exchange}:${symbol}`;
199
237
  const tracked = openPositions.get(key);
200
238
  const openedAt = tracked?.openedAt ?? ts;
201
239
  const duration = new Date(ts).getTime() - new Date(openedAt).getTime();
202
- const realizedPnl = String(data.unrealizedPnl ?? "0");
240
+ // SSOT rule #2: do NOT alias unrealizedPnl as realizedPnl. They mean
241
+ // different things: unrealizedPnl is the snapshot before close, and on
242
+ // a real close event the upstream adapter must emit realizedPnl
243
+ // explicitly. Aliasing them would have a flat-close (realizedPnl=0)
244
+ // pretend to be the last unrealized number from the previous tick.
245
+ const realizedPnl = requireNumericString(data.realizedPnl, "realizedPnl", "closed", exchange, symbol);
203
246
  logPosition({
204
247
  id: tracked?.id ?? genId(),
205
248
  exchange,
@@ -26,12 +26,76 @@ export interface AlertSettings {
26
26
  /** Cooldown per symbol in minutes after alert fires (default: 60) */
27
27
  cooldownMin: number;
28
28
  }
29
+ export interface AgentMeta {
30
+ agentName: string;
31
+ agentWalletName: string;
32
+ /**
33
+ * EVM address of the agent wallet (required for Aster + Hyperliquid).
34
+ * For Pacifica (Phase 2c): kept as a sentinel zero-address since the
35
+ * curve is Solana/Ed25519; the canonical address lives in
36
+ * `agentSolanaAddress`. For Lighter (Phase 2d): populated with the master
37
+ * EVM address (since L1 ChangePubKey is signed by the master EVM key); the
38
+ * canonical agent identity lives in `publicKey + apiKeyIndex`.
39
+ * The `0x${string}` literal is preserved to avoid disrupting Aster/HL strict typing.
40
+ */
41
+ agentEvmAddress: `0x${string}`;
42
+ /** EVM address of the master/owner wallet — required as `user` field in Aster/HL requests. Populated by `agent approve` (Step 4). */
43
+ userEvmAddress: `0x${string}`;
44
+ /**
45
+ * Solana base58 public key of the agent wallet (Phase 2c — Pacifica only).
46
+ * Optional for Aster/HL since they live on EVM curves.
47
+ */
48
+ agentSolanaAddress?: string;
49
+ /**
50
+ * Solana base58 public key of the master/owner wallet (Phase 2c — Pacifica only).
51
+ * Optional for Aster/HL.
52
+ */
53
+ userSolanaAddress?: string;
54
+ /**
55
+ * Lighter slot index (Phase 2d — Lighter only).
56
+ * Range: [4, 254] inclusive. Slots 0-3 are reserved by the Lighter frontend.
57
+ * Selected at approve time as `max(existing slots) + 1`.
58
+ */
59
+ apiKeyIndex?: number;
60
+ /**
61
+ * Lighter agent secp256k1 public key (hex, no `0x` prefix) (Phase 2d — Lighter only).
62
+ * Canonical identity for the Lighter L2 hot-path. Cross-checked against
63
+ * `GET /api/v1/apikeys?account_index=N` during `wallet agent verify lighter`.
64
+ */
65
+ publicKey?: string;
66
+ /**
67
+ * Lighter L2 account index (Phase 2d — Lighter only).
68
+ * Resolved at approve time from L1 EVM address via the Lighter REST API
69
+ * and cached so subsequent verify/trade calls don't need to re-query.
70
+ */
71
+ accountIndex?: number;
72
+ masterWalletName: string;
73
+ owsApiKeyId: string;
74
+ owsPolicyId: string;
75
+ expiresAt: string;
76
+ approvedAt: string;
77
+ permissions: {
78
+ canPerpTrade: boolean;
79
+ canSpotTrade: boolean;
80
+ canWithdraw: boolean;
81
+ };
82
+ asterApprovalNonce: string;
83
+ status?: "active" | "partial";
84
+ }
85
+ export interface AgentsByExchange {
86
+ aster?: Record<string, AgentMeta>;
87
+ hyperliquid?: Record<string, AgentMeta>;
88
+ pacifica?: Record<string, AgentMeta>;
89
+ lighter?: Record<string, AgentMeta>;
90
+ }
29
91
  export interface Settings {
30
92
  /** Default exchange when -e flag is omitted */
31
93
  defaultExchange: string;
32
- /** Enable referral/builder codes (default: false — opt-in only) */
33
- referrals: boolean;
34
- /** Per-exchange referral codes (used when referrals=true) */
94
+ /** Active OWS wallet name (used when no --ows or --wallet flag) */
95
+ owsActiveWallet: string;
96
+ /** Per-exchange referral / builder codes (always applied per SSOT Rule #3:
97
+ * no opt-out — Pacifica builder code has always been mandatory; HL/LT
98
+ * aligned 2026-05-01). */
35
99
  referralCodes: {
36
100
  pacifica: string;
37
101
  hyperliquid: string;
@@ -46,6 +110,8 @@ export interface Settings {
46
110
  fees: Record<string, ExchangeFees>;
47
111
  /** Alert configuration */
48
112
  alerts: AlertSettings;
113
+ /** OWS-native agent wallets registered per exchange (Phase 2a: Aster only) */
114
+ agents?: AgentsByExchange;
49
115
  }
50
116
  export declare function loadSettings(): Settings;
51
117
  export declare function saveSettings(settings: Settings): void;
package/dist/settings.js CHANGED
@@ -4,7 +4,7 @@ const PERP_DIR = resolve(process.env.HOME || "~", ".perp");
4
4
  const SETTINGS_FILE = resolve(PERP_DIR, "settings.json");
5
5
  const DEFAULTS = {
6
6
  defaultExchange: "",
7
- referrals: false,
7
+ owsActiveWallet: "",
8
8
  referralCodes: {
9
9
  pacifica: "",
10
10
  hyperliquid: "HYPERCASH",
@@ -42,7 +42,7 @@ export function loadSettings() {
42
42
  }
43
43
  return {
44
44
  defaultExchange: stored.defaultExchange ?? DEFAULTS.defaultExchange,
45
- referrals: stored.referrals ?? DEFAULTS.referrals,
45
+ owsActiveWallet: stored.owsActiveWallet ?? DEFAULTS.owsActiveWallet,
46
46
  referralCodes: {
47
47
  pacifica: stored.referralCodes?.pacifica ?? DEFAULTS.referralCodes.pacifica,
48
48
  hyperliquid: stored.referralCodes?.hyperliquid ?? DEFAULTS.referralCodes.hyperliquid,
@@ -60,6 +60,7 @@ export function loadSettings() {
60
60
  cooldownMin: stored.alerts?.cooldownMin ?? DEFAULTS.alerts.cooldownMin,
61
61
  },
62
62
  fees,
63
+ agents: stored.agents && typeof stored.agents === "object" ? stored.agents : undefined,
63
64
  };
64
65
  }
65
66
  catch {
@@ -1,3 +1,5 @@
1
1
  export type { EvmSigner, SolanaSigner } from "./interface.js";
2
2
  export { LocalEvmSigner } from "./evm-local.js";
3
3
  export { LocalSolanaSigner } from "./solana-local.js";
4
+ export { OwsEvmSigner } from "./ows-evm.js";
5
+ export { OwsSolanaSigner } from "./ows-solana.js";
@@ -1,2 +1,4 @@
1
1
  export { LocalEvmSigner } from "./evm-local.js";
2
2
  export { LocalSolanaSigner } from "./solana-local.js";
3
+ export { OwsEvmSigner } from "./ows-evm.js";
4
+ export { OwsSolanaSigner } from "./ows-solana.js";
@@ -0,0 +1,32 @@
1
+ import type { EvmSigner } from "./interface.js";
2
+ /**
3
+ * EVM signer backed by Open Wallet Standard.
4
+ * Keys never leave the OWS encrypted vault.
5
+ */
6
+ export declare class OwsEvmSigner implements EvmSigner {
7
+ private _address;
8
+ private _walletName;
9
+ private _passphrase;
10
+ private constructor();
11
+ static create(walletName: string, passphrase?: string): OwsEvmSigner;
12
+ getAddress(): string;
13
+ signTypedData(domain: Record<string, unknown>, types: Record<string, Array<{
14
+ name: string;
15
+ type: string;
16
+ }>>, value: Record<string, unknown>): Promise<string>;
17
+ signMessage(message: string | Uint8Array): Promise<string>;
18
+ }
19
+ /**
20
+ * Normalize OWS signature output to canonical 65-byte hex (0x-prefixed).
21
+ *
22
+ * OWS may return the signature in two shapes depending on version:
23
+ * (A) 65-byte hex with v already embedded — use as-is
24
+ * (B) 64-byte hex (r+s only) + separate recoveryId — append v
25
+ *
26
+ * For shape B, recoveryId may be either canonical (27/28) or raw (0/1).
27
+ * If raw, add 27 to canonicalize.
28
+ */
29
+ export declare function canonicalizeOwsSignature(result: {
30
+ signature: string;
31
+ recoveryId?: number;
32
+ }): string;