auramaxx 1.0.0-alpha.4

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 (363) hide show
  1. package/LICENSE +26 -0
  2. package/README.md +112 -0
  3. package/bin/aurawallet.js +121 -0
  4. package/docs/ADAPTERS.md +467 -0
  5. package/docs/API.md +2679 -0
  6. package/docs/APPS.md +198 -0
  7. package/docs/ARCHITECTURE.md +350 -0
  8. package/docs/AUTH.md +698 -0
  9. package/docs/BEST-PRACTICES.md +121 -0
  10. package/docs/CLI.md +61 -0
  11. package/docs/DEVELOPING-APPS.md +452 -0
  12. package/docs/EXTENSION.md +97 -0
  13. package/docs/JOBS.md +33 -0
  14. package/docs/MCP.md +76 -0
  15. package/docs/PROTOCOL.md +142 -0
  16. package/docs/SETUP.md +219 -0
  17. package/docs/WORKSPACE.md +672 -0
  18. package/docs/agent-auth.md +63 -0
  19. package/docs/aura-file.md +48 -0
  20. package/docs/credentials.md +53 -0
  21. package/docs/external/getting-started.md +65 -0
  22. package/docs/external/overview.md +45 -0
  23. package/docs/external/use-cases.md +48 -0
  24. package/docs/external/why-aura.md +35 -0
  25. package/docs/jobs/connect-agent.md +77 -0
  26. package/docs/jobs/migrate-from-dotenv.md +79 -0
  27. package/docs/jobs/recover-from-lockout.md +72 -0
  28. package/docs/jobs/secure-ci.md +63 -0
  29. package/docs/oauth2.md +42 -0
  30. package/docs/passkeys.md +60 -0
  31. package/docs/security.md +540 -0
  32. package/docs/specs/aura-open-protocol.md +61 -0
  33. package/docs/specs/aura-provider-plugin.md +24 -0
  34. package/docs/specs/aura-registry-model.md +31 -0
  35. package/docs/specs/fixtures/invalid-bad-key.aura +1 -0
  36. package/docs/specs/fixtures/invalid-bad-unicode-escape.aura +1 -0
  37. package/docs/specs/fixtures/invalid-duplicate-key.aura +2 -0
  38. package/docs/specs/fixtures/valid-basic.aura +4 -0
  39. package/docs/specs/fixtures/valid-provider-ref.aura +1 -0
  40. package/docs/specs/fixtures/valid-quoted-escapes.aura +2 -0
  41. package/docs/templates/RELEASE_NOTES_TEMPLATE.md +22 -0
  42. package/docs/totp.md +40 -0
  43. package/docs/wallet/AI.md +508 -0
  44. package/docs/wallet/DEVELOPING-STRATEGIES.md +713 -0
  45. package/docs/wallet/README.md +47 -0
  46. package/docs/wallet/STRATEGY.md +89 -0
  47. package/next.config.ts +21 -0
  48. package/package.json +151 -0
  49. package/postcss.config.mjs +8 -0
  50. package/prisma/migrations/20260214170000_baseline/migration.sql +511 -0
  51. package/prisma/migrations/20260216214537_add_passkey_model/migration.sql +18 -0
  52. package/prisma/migrations/20260217150500_add_credential_access_audit/migration.sql +31 -0
  53. package/prisma/migrations/migration_lock.toml +3 -0
  54. package/prisma/schema.prisma +447 -0
  55. package/public/logo-chevron.svg +31 -0
  56. package/public/logo-concentric.svg +31 -0
  57. package/public/logo-crosshatch.svg +39 -0
  58. package/public/logo-dashed.svg +39 -0
  59. package/public/logo-horizontal.svg +31 -0
  60. package/public/logo-m56.svg +64 -0
  61. package/public/logo.webp +0 -0
  62. package/scripts/add-app.js +245 -0
  63. package/scripts/init.sh +57 -0
  64. package/scripts/migrate-apikeys-to-credentials.ts +35 -0
  65. package/scripts/sandbox-agent-flow.sh +235 -0
  66. package/scripts/sandbox.sh +175 -0
  67. package/scripts/validate-job-docs.mjs +125 -0
  68. package/server/abi/SwapHelper.json +438 -0
  69. package/server/cli/approval.ts +447 -0
  70. package/server/cli/commands/app.ts +204 -0
  71. package/server/cli/commands/cron.ts +24 -0
  72. package/server/cli/commands/doctor.ts +1007 -0
  73. package/server/cli/commands/env.ts +456 -0
  74. package/server/cli/commands/init.ts +752 -0
  75. package/server/cli/commands/mcp.ts +125 -0
  76. package/server/cli/commands/restore.ts +314 -0
  77. package/server/cli/commands/shell-hook.ts +468 -0
  78. package/server/cli/commands/start.ts +62 -0
  79. package/server/cli/commands/status.ts +59 -0
  80. package/server/cli/commands/stop.ts +14 -0
  81. package/server/cli/commands/token.ts +180 -0
  82. package/server/cli/commands/unlock.ts +49 -0
  83. package/server/cli/commands/vault.ts +417 -0
  84. package/server/cli/index.ts +328 -0
  85. package/server/cli/lib/aura-parser.ts +64 -0
  86. package/server/cli/lib/credential-create.ts +74 -0
  87. package/server/cli/lib/credential-resolve.ts +254 -0
  88. package/server/cli/lib/dotenv-migrate.ts +116 -0
  89. package/server/cli/lib/dotenv-parser.ts +146 -0
  90. package/server/cli/lib/http.ts +91 -0
  91. package/server/cli/lib/init-steps.ts +76 -0
  92. package/server/cli/lib/local-agent-trust.ts +45 -0
  93. package/server/cli/lib/process.ts +136 -0
  94. package/server/cli/lib/prompt.ts +85 -0
  95. package/server/cli/lib/theme.ts +240 -0
  96. package/server/cli/socket.ts +570 -0
  97. package/server/cli/transport-client.ts +50 -0
  98. package/server/cron/index.ts +137 -0
  99. package/server/cron/job.ts +31 -0
  100. package/server/cron/jobs/balance-sync.ts +436 -0
  101. package/server/cron/jobs/incoming-scan.ts +506 -0
  102. package/server/cron/jobs/native-price.ts +70 -0
  103. package/server/cron/jobs/orphan-cleanup.ts +40 -0
  104. package/server/cron/jobs/strategy-runner.ts +175 -0
  105. package/server/cron/scheduler.ts +125 -0
  106. package/server/index.ts +406 -0
  107. package/server/lib/adapters/factory.ts +110 -0
  108. package/server/lib/adapters/index.ts +19 -0
  109. package/server/lib/adapters/router.ts +297 -0
  110. package/server/lib/adapters/telegram.ts +645 -0
  111. package/server/lib/adapters/types.ts +89 -0
  112. package/server/lib/adapters/webhook.ts +95 -0
  113. package/server/lib/address.ts +49 -0
  114. package/server/lib/agent-auth/contracts.ts +1194 -0
  115. package/server/lib/agent-profiles.ts +328 -0
  116. package/server/lib/ai.ts +285 -0
  117. package/server/lib/api-registry/contracts.ts +86 -0
  118. package/server/lib/api-registry/validation.ts +172 -0
  119. package/server/lib/apikey-migration.ts +189 -0
  120. package/server/lib/app-installer.ts +505 -0
  121. package/server/lib/app-tokens.ts +247 -0
  122. package/server/lib/auth.ts +314 -0
  123. package/server/lib/batch.ts +242 -0
  124. package/server/lib/cold.ts +874 -0
  125. package/server/lib/config.ts +381 -0
  126. package/server/lib/credential-access-audit.ts +85 -0
  127. package/server/lib/credential-access-policy.ts +110 -0
  128. package/server/lib/credential-health.ts +343 -0
  129. package/server/lib/credential-import.ts +487 -0
  130. package/server/lib/credential-scope.ts +87 -0
  131. package/server/lib/credential-shares.ts +190 -0
  132. package/server/lib/credential-transport.ts +342 -0
  133. package/server/lib/credential-vault.ts +77 -0
  134. package/server/lib/credentials.ts +333 -0
  135. package/server/lib/crypto.ts +8 -0
  136. package/server/lib/db.ts +15 -0
  137. package/server/lib/defaults.ts +366 -0
  138. package/server/lib/dex/index.ts +80 -0
  139. package/server/lib/dex/relay.ts +235 -0
  140. package/server/lib/dex/types.ts +59 -0
  141. package/server/lib/dex/uniswap.ts +370 -0
  142. package/server/lib/e2e-agent/artifacts.ts +36 -0
  143. package/server/lib/e2e-agent/contracts.ts +112 -0
  144. package/server/lib/e2e-agent/validation.ts +135 -0
  145. package/server/lib/encrypt.ts +128 -0
  146. package/server/lib/error.ts +20 -0
  147. package/server/lib/events.ts +205 -0
  148. package/server/lib/hot.ts +357 -0
  149. package/server/lib/key-fingerprint.ts +28 -0
  150. package/server/lib/logger.ts +331 -0
  151. package/server/lib/network.ts +137 -0
  152. package/server/lib/notifications.ts +219 -0
  153. package/server/lib/oauth2-refresh.ts +241 -0
  154. package/server/lib/oursecret.ts +54 -0
  155. package/server/lib/passkey-credential.ts +360 -0
  156. package/server/lib/passkey.ts +68 -0
  157. package/server/lib/permissions.ts +248 -0
  158. package/server/lib/pino.ts +24 -0
  159. package/server/lib/policy-preview.ts +138 -0
  160. package/server/lib/price.ts +338 -0
  161. package/server/lib/prices.ts +34 -0
  162. package/server/lib/project-scope.ts +239 -0
  163. package/server/lib/resolve-action.ts +427 -0
  164. package/server/lib/resolve.ts +36 -0
  165. package/server/lib/sessions.ts +632 -0
  166. package/server/lib/solana/connection.ts +26 -0
  167. package/server/lib/solana/jupiter.ts +128 -0
  168. package/server/lib/solana/transfer.ts +108 -0
  169. package/server/lib/solana/wallet.ts +136 -0
  170. package/server/lib/strategy/emits.ts +21 -0
  171. package/server/lib/strategy/engine.ts +1305 -0
  172. package/server/lib/strategy/executor.ts +115 -0
  173. package/server/lib/strategy/hook-context.ts +158 -0
  174. package/server/lib/strategy/hooks.ts +990 -0
  175. package/server/lib/strategy/index.ts +28 -0
  176. package/server/lib/strategy/installer.ts +305 -0
  177. package/server/lib/strategy/loader.ts +256 -0
  178. package/server/lib/strategy/message.ts +235 -0
  179. package/server/lib/strategy/repository.ts +218 -0
  180. package/server/lib/strategy/session-logger.ts +693 -0
  181. package/server/lib/strategy/sources.ts +288 -0
  182. package/server/lib/strategy/state.ts +189 -0
  183. package/server/lib/strategy/templates.ts +403 -0
  184. package/server/lib/strategy/tick.ts +404 -0
  185. package/server/lib/strategy/types.ts +230 -0
  186. package/server/lib/swap.ts +3 -0
  187. package/server/lib/temp.ts +86 -0
  188. package/server/lib/token-metadata.ts +86 -0
  189. package/server/lib/token-safety.ts +200 -0
  190. package/server/lib/token-search.ts +444 -0
  191. package/server/lib/totp.ts +194 -0
  192. package/server/lib/transactions.ts +123 -0
  193. package/server/lib/transport.ts +75 -0
  194. package/server/lib/txhistory/decoder.ts +262 -0
  195. package/server/lib/txhistory/enricher.ts +652 -0
  196. package/server/lib/txhistory/index.ts +391 -0
  197. package/server/lib/txhistory/signatures.ts +59 -0
  198. package/server/lib/verified-summary.ts +421 -0
  199. package/server/mcp/profile-policy.ts +30 -0
  200. package/server/mcp/server.ts +619 -0
  201. package/server/mcp/tools.ts +523 -0
  202. package/server/middleware/auth.ts +119 -0
  203. package/server/middleware/requestLogger.ts +84 -0
  204. package/server/routes/actions.ts +459 -0
  205. package/server/routes/adapters.ts +703 -0
  206. package/server/routes/addressbook.ts +113 -0
  207. package/server/routes/ai.ts +34 -0
  208. package/server/routes/apikeys.ts +295 -0
  209. package/server/routes/apps.ts +601 -0
  210. package/server/routes/auth.ts +457 -0
  211. package/server/routes/backup.ts +340 -0
  212. package/server/routes/batch.ts +270 -0
  213. package/server/routes/bookmarks.ts +162 -0
  214. package/server/routes/credential-shares.ts +198 -0
  215. package/server/routes/credential-vaults.ts +154 -0
  216. package/server/routes/credentials.ts +1290 -0
  217. package/server/routes/dashboard.ts +71 -0
  218. package/server/routes/defaults.ts +124 -0
  219. package/server/routes/fund.ts +229 -0
  220. package/server/routes/import.ts +352 -0
  221. package/server/routes/launch.ts +665 -0
  222. package/server/routes/lock.ts +54 -0
  223. package/server/routes/logs.ts +68 -0
  224. package/server/routes/nuke.ts +111 -0
  225. package/server/routes/passkey-credentials.ts +99 -0
  226. package/server/routes/passkey.ts +346 -0
  227. package/server/routes/portfolio.ts +217 -0
  228. package/server/routes/price.ts +63 -0
  229. package/server/routes/resolve.ts +31 -0
  230. package/server/routes/security.ts +45 -0
  231. package/server/routes/send-evm.ts +241 -0
  232. package/server/routes/send-solana.ts +281 -0
  233. package/server/routes/send.ts +178 -0
  234. package/server/routes/setup.ts +210 -0
  235. package/server/routes/strategy.ts +894 -0
  236. package/server/routes/swap-evm.ts +353 -0
  237. package/server/routes/swap-solana.ts +177 -0
  238. package/server/routes/swap.ts +356 -0
  239. package/server/routes/token.ts +247 -0
  240. package/server/routes/unlock.ts +403 -0
  241. package/server/routes/wallet-assets.ts +361 -0
  242. package/server/routes/wallet-transactions.ts +515 -0
  243. package/server/routes/wallet.ts +710 -0
  244. package/server/types.ts +146 -0
  245. package/skills/aurawallet/SKILL.md +739 -0
  246. package/skills/aurawallet-setup/SKILL.md +74 -0
  247. package/skills/security-review/SKILL.md +148 -0
  248. package/src/app/api/agent-requests/route.ts +30 -0
  249. package/src/app/api/apps/install/route.ts +126 -0
  250. package/src/app/api/apps/manifests/route.ts +16 -0
  251. package/src/app/api/apps/static/[...path]/route.ts +57 -0
  252. package/src/app/api/events/route.ts +92 -0
  253. package/src/app/api/page.tsx +212 -0
  254. package/src/app/api/workspace/[id]/apps/[wid]/route.ts +119 -0
  255. package/src/app/api/workspace/[id]/apps/route.ts +81 -0
  256. package/src/app/api/workspace/[id]/export/route.ts +67 -0
  257. package/src/app/api/workspace/[id]/route.ts +168 -0
  258. package/src/app/api/workspace/auth.ts +34 -0
  259. package/src/app/api/workspace/config/route.ts +106 -0
  260. package/src/app/api/workspace/import/route.ts +127 -0
  261. package/src/app/api/workspace/route.ts +116 -0
  262. package/src/app/app/page.tsx +2122 -0
  263. package/src/app/apple-icon.png +0 -0
  264. package/src/app/docs/page.tsx +178 -0
  265. package/src/app/favicon.ico +0 -0
  266. package/src/app/globals.css +572 -0
  267. package/src/app/health/page.tsx +5 -0
  268. package/src/app/hello/page.tsx +15 -0
  269. package/src/app/icon.png +0 -0
  270. package/src/app/layout.tsx +34 -0
  271. package/src/app/page.tsx +986 -0
  272. package/src/app/providers.tsx +90 -0
  273. package/src/app/share/[token]/page.tsx +295 -0
  274. package/src/components/ChainSelector.tsx +144 -0
  275. package/src/components/HumanActionBar.tsx +695 -0
  276. package/src/components/NotificationDrawer.tsx +129 -0
  277. package/src/components/apps/AgentKeysApp.tsx +490 -0
  278. package/src/components/apps/App.tsx +153 -0
  279. package/src/components/apps/AppGrid.tsx +15 -0
  280. package/src/components/apps/DetailedAddressDrawer.tsx +325 -0
  281. package/src/components/apps/DraggableApp.tsx +562 -0
  282. package/src/components/apps/IFrameApp.tsx +73 -0
  283. package/src/components/apps/LogsApp.tsx +360 -0
  284. package/src/components/apps/SendApp.tsx +394 -0
  285. package/src/components/apps/SetupWizardApp.tsx +1004 -0
  286. package/src/components/apps/SystemDefaultsApp.tsx +845 -0
  287. package/src/components/apps/ThirdPartyApp.tsx +428 -0
  288. package/src/components/apps/TokenApp.tsx +319 -0
  289. package/src/components/apps/TransactionsApp.tsx +438 -0
  290. package/src/components/apps/WalletDetailApp.tsx +1505 -0
  291. package/src/components/apps/index.ts +13 -0
  292. package/src/components/design-system/Button.tsx +53 -0
  293. package/src/components/design-system/ChainIndicator.tsx +65 -0
  294. package/src/components/design-system/ChainSelector.tsx +137 -0
  295. package/src/components/design-system/ConfirmationModal.tsx +106 -0
  296. package/src/components/design-system/ConfirmationPopover.tsx +81 -0
  297. package/src/components/design-system/Drawer.tsx +123 -0
  298. package/src/components/design-system/FilterDropdown.tsx +72 -0
  299. package/src/components/design-system/Modal.tsx +206 -0
  300. package/src/components/design-system/Popover.tsx +142 -0
  301. package/src/components/design-system/TextInput.tsx +85 -0
  302. package/src/components/design-system/Toggle.tsx +58 -0
  303. package/src/components/design-system/index.ts +11 -0
  304. package/src/components/docs/DocsThemeToggle.tsx +49 -0
  305. package/src/components/health/CredentialHealthDashboard.tsx +214 -0
  306. package/src/components/icons/ChainIcons.tsx +72 -0
  307. package/src/components/layout/AppStoreDrawer.tsx +369 -0
  308. package/src/components/layout/ContentArea.tsx +21 -0
  309. package/src/components/layout/TabBar.tsx +278 -0
  310. package/src/components/layout/WalletSidebar.tsx +1033 -0
  311. package/src/components/layout/index.ts +4 -0
  312. package/src/components/marketing/AuraWalletSpecOverlay.tsx +635 -0
  313. package/src/components/marketing/DeviceMorphExperience.tsx +216 -0
  314. package/src/components/vault/ApiKeysConsole.tsx +1080 -0
  315. package/src/components/vault/AuditConsole.tsx +584 -0
  316. package/src/components/vault/CredentialDetail.tsx +455 -0
  317. package/src/components/vault/CredentialEmpty.tsx +55 -0
  318. package/src/components/vault/CredentialField.tsx +361 -0
  319. package/src/components/vault/CredentialForm.tsx +1212 -0
  320. package/src/components/vault/CredentialList.tsx +165 -0
  321. package/src/components/vault/CredentialRow.tsx +97 -0
  322. package/src/components/vault/CredentialShareModal.tsx +178 -0
  323. package/src/components/vault/CredentialVault.tsx +754 -0
  324. package/src/components/vault/CredentialWalletWidget.tsx +103 -0
  325. package/src/components/vault/ImportCredentialsModal.tsx +515 -0
  326. package/src/components/vault/LargeTypeModal.tsx +64 -0
  327. package/src/components/vault/PasswordGenerator.tsx +224 -0
  328. package/src/components/vault/TOTPDisplay.tsx +123 -0
  329. package/src/components/vault/VaultSidebar.tsx +413 -0
  330. package/src/components/vault/types.ts +54 -0
  331. package/src/context/AuthContext.tsx +337 -0
  332. package/src/context/PriceContext.tsx +113 -0
  333. package/src/context/ThemeContext.tsx +164 -0
  334. package/src/context/WebSocketContext.tsx +269 -0
  335. package/src/context/WorkspaceContext.tsx +668 -0
  336. package/src/hooks/index.ts +3 -0
  337. package/src/hooks/useAgentActions.ts +368 -0
  338. package/src/hooks/useBalance.ts +103 -0
  339. package/src/hooks/useBalances.ts +129 -0
  340. package/src/instrumentation.ts +12 -0
  341. package/src/lib/api.ts +449 -0
  342. package/src/lib/app-loader.ts +148 -0
  343. package/src/lib/app-registry.ts +178 -0
  344. package/src/lib/app-sdk.ts +157 -0
  345. package/src/lib/audit-console-adapter.ts +151 -0
  346. package/src/lib/auth-client.ts +75 -0
  347. package/src/lib/config.ts +74 -0
  348. package/src/lib/crypto.ts +112 -0
  349. package/src/lib/db.ts +21 -0
  350. package/src/lib/docs.ts +390 -0
  351. package/src/lib/events.ts +361 -0
  352. package/src/lib/pino.ts +24 -0
  353. package/src/lib/theme-handlers.ts +168 -0
  354. package/src/lib/theme.ts +351 -0
  355. package/src/lib/tokenData.ts +378 -0
  356. package/src/lib/vault-crypto.ts +129 -0
  357. package/src/lib/websocket-server.ts +302 -0
  358. package/src/lib/websocket-setup.ts +79 -0
  359. package/src/lib/wordlist.ts +2050 -0
  360. package/src/lib/workspace-handlers.ts +285 -0
  361. package/start.sh +80 -0
  362. package/tailwind.config.ts +99 -0
  363. package/tsconfig.json +42 -0
@@ -0,0 +1,404 @@
1
+ /**
2
+ * Tick Runner
3
+ * ===========
4
+ * Orchestrates a single tick for a strategy:
5
+ * 1. Fetch sources
6
+ * 2. Call tick hook → intents + state
7
+ * 3. Approve (if config.approve)
8
+ * 4. For each intent → call execute hook → action
9
+ * 5. Execute action → outcome
10
+ * 6. Call result hook → state updates + follow-ups
11
+ * 7. Follow-up intents re-enter step 3 (depth limit: 3)
12
+ */
13
+
14
+ import { createHash } from 'crypto';
15
+ import { StrategyManifest, StrategyConfig, HookResult, Intent, Action, ActionOutcome } from './types';
16
+ import { fetchAllSources } from './sources';
17
+ import { callHook } from './hooks';
18
+ import { executeAction } from './executor';
19
+ import { getState, updateState, getConfigOverrides, restoreState, persistState } from './state';
20
+ import { requestHumanApproval, requestActionToken } from './engine';
21
+ import { emitWalletEvent } from '../events';
22
+ import { getTokenHash } from '../auth';
23
+ import { getSessionBudget } from '../sessions';
24
+ import { processEmits } from './emits';
25
+ import { getDefaultSync } from '../defaults';
26
+ import {
27
+ startTickSession,
28
+ logTickEvent,
29
+ logError as sessionLogError,
30
+ endTickSession,
31
+ } from './session-logger';
32
+ import { getErrorMessage } from '../error';
33
+
34
+ /**
35
+ * Emit a strategy event via the existing event system.
36
+ */
37
+ function emitStrategyEvent(type: string, strategyId: string, data: Record<string, unknown>): void {
38
+ emitWalletEvent(type, { strategyId, ...data });
39
+ }
40
+
41
+ function getMaxFollowupDepth(): number {
42
+ return getDefaultSync<number>('ai.max_followup_depth', 3);
43
+ }
44
+
45
+ /** Last context hash per strategy — used to skip duplicate LLM calls */
46
+ const lastContextHash = new Map<string, string>();
47
+
48
+ /** Hash a string with SHA-256 (fast, collision-resistant) */
49
+ function hashContext(contextStr: string): string {
50
+ return createHash('sha256').update(contextStr).digest('hex').slice(0, 16);
51
+ }
52
+
53
+ /** Clear context hash for a strategy (e.g. on disable/reset) */
54
+ export function clearContextHash(id: string): void {
55
+ lastContextHash.delete(id);
56
+ }
57
+
58
+ /** Clear all context hashes (e.g. on engine shutdown) */
59
+ export function clearAllContextHashes(): void {
60
+ lastContextHash.clear();
61
+ }
62
+
63
+ /**
64
+ * Run a single tick for a strategy.
65
+ */
66
+ export async function runTick(
67
+ manifest: StrategyManifest,
68
+ token?: string,
69
+ ): Promise<void> {
70
+ const startTime = Date.now();
71
+ const tag = `[strategy:${manifest.id}]`;
72
+ const sessionId = startTickSession(manifest.id);
73
+
74
+ // Re-read state from DB to pick up changes from app UI (e.g. human moves)
75
+ await restoreState(manifest.id);
76
+ const state = getState(manifest.id);
77
+ const configOverrides = await getConfigOverrides(manifest.id);
78
+ const config: StrategyConfig = { ...manifest.config, ...configOverrides };
79
+
80
+ console.log(`${tag} ── tick start (sources=${manifest.sources.length})`);
81
+
82
+ // 1. Fetch sources
83
+ let sourceData: Record<string, unknown[]>;
84
+ try {
85
+ const srcStart = Date.now();
86
+ sourceData = await fetchAllSources(manifest, config, null, token);
87
+ const srcKeys = Object.keys(sourceData);
88
+ const srcCounts = srcKeys.map(k => `${k}:${sourceData[k].length}`).join(', ');
89
+ console.log(`${tag} sources fetched in ${Date.now() - srcStart}ms → {${srcCounts}}`);
90
+ logTickEvent(sessionId, 'Sources', `${srcCounts} (${Date.now() - srcStart}ms)`);
91
+ } catch (err) {
92
+ const errorAction = config.errors?.sourceFail || 'skip';
93
+ const errMsg = getErrorMessage(err);
94
+ console.error(`${tag} source fetch FAILED: ${errMsg} (policy=${errorAction})`);
95
+ emitStrategyEvent('strategy:error', manifest.id, { error: errMsg, phase: 'sources' });
96
+ sessionLogError(sessionId, err);
97
+
98
+ if (errorAction === 'pause') {
99
+ endTickSession(sessionId, 'error', Date.now() - startTime);
100
+ throw err; // Let engine handle pause
101
+ }
102
+ // skip or retry — for skip, just return; retry handled by engine
103
+ if (errorAction === 'skip') {
104
+ endTickSession(sessionId, 'error', Date.now() - startTime);
105
+ return;
106
+ }
107
+ endTickSession(sessionId, 'error', Date.now() - startTime);
108
+ throw err;
109
+ }
110
+
111
+ // 2. Build context and check if it changed since last tick
112
+ const context = {
113
+ sources: sourceData,
114
+ positions: state.positions || [],
115
+ state,
116
+ config,
117
+ permissions: manifest.permissions,
118
+ budget: token ? getSessionBudget(getTokenHash(token)) : { limits: {}, spent: {}, remaining: {} },
119
+ };
120
+ const contextStr = JSON.stringify(context);
121
+ const contextHash = hashContext(contextStr);
122
+ const prevHash = lastContextHash.get(manifest.id);
123
+
124
+ if (prevHash === contextHash) {
125
+ const dur = Date.now() - startTime;
126
+ console.log(`${tag} ── tick skipped in ${dur}ms (context unchanged, hash=${contextHash})`);
127
+ logTickEvent(sessionId, 'Context', `unchanged (hash=${contextHash}), skipped AI call`);
128
+ endTickSession(sessionId, 'completed', dur);
129
+ return;
130
+ }
131
+
132
+ // 3. Call tick hook
133
+ console.log(`${tag} calling tick hook...`);
134
+ const hookStart = Date.now();
135
+ const tickResult = await callHook(manifest, 'tick', context, token);
136
+ lastContextHash.set(manifest.id, contextHash);
137
+ const hookDur = Date.now() - hookStart;
138
+ console.log(`${tag} tick hook returned in ${hookDur}ms → intents=${tickResult.intents.length}, stateKeys=${Object.keys(tickResult.state).join(',') || '(none)'}`);
139
+ logTickEvent(sessionId, 'Hook', `${hookDur}ms, intents=${tickResult.intents.length}`);
140
+
141
+ // Update state from tick hook
142
+ if (tickResult.state && Object.keys(tickResult.state).length > 0) {
143
+ updateState(manifest.id, tickResult.state);
144
+ await persistState(manifest.id);
145
+ console.log(`${tag} state updated + persisted: ${JSON.stringify(tickResult.state).slice(0, 200)}`);
146
+ }
147
+
148
+ // Broadcast any emit events from the tick hook
149
+ processEmits(manifest.id, tickResult);
150
+
151
+ if (tickResult.log) {
152
+ console.log(`${tag} hook log: ${tickResult.log}`);
153
+ }
154
+
155
+ if (!tickResult.intents || tickResult.intents.length === 0) {
156
+ const dur = Date.now() - startTime;
157
+ console.log(`${tag} ── tick done in ${dur}ms (no intents)`);
158
+ logTickEvent(sessionId, 'Intents', 'none');
159
+ endTickSession(sessionId, 'completed', dur);
160
+ emitStrategyEvent('strategy:tick', manifest.id, {
161
+ intents: 0,
162
+ duration: dur,
163
+ state: getState(manifest.id),
164
+ });
165
+ return; // Nothing to do
166
+ }
167
+
168
+ console.log(`${tag} processing ${tickResult.intents.length} intent(s): ${tickResult.intents.map(i => i.type || 'unknown').join(', ')}`);
169
+
170
+ // Process intents (with follow-up support)
171
+ await processIntents(manifest, tickResult.intents, config, token, 0);
172
+
173
+ const dur = Date.now() - startTime;
174
+ console.log(`${tag} ── tick done in ${dur}ms (${tickResult.intents.length} intents processed)`);
175
+ logTickEvent(sessionId, 'Intents', `${tickResult.intents.length} processed`);
176
+ endTickSession(sessionId, 'completed', dur);
177
+ emitStrategyEvent('strategy:tick', manifest.id, {
178
+ intents: tickResult.intents.length,
179
+ duration: dur,
180
+ state: getState(manifest.id),
181
+ });
182
+ }
183
+
184
+ /**
185
+ * Process intents through approval → execute → result pipeline.
186
+ * Supports recursive follow-ups with depth limit.
187
+ */
188
+ export async function processIntents(
189
+ manifest: StrategyManifest,
190
+ intents: Intent[],
191
+ config: StrategyConfig,
192
+ token?: string,
193
+ depth: number = 0,
194
+ ): Promise<void> {
195
+ const tag = `[strategy:${manifest.id}]`;
196
+
197
+ const maxDepth = getMaxFollowupDepth();
198
+ if (depth >= maxDepth) {
199
+ console.warn(`${tag} follow-up depth limit reached (${maxDepth}), stopping`);
200
+ return;
201
+ }
202
+
203
+ if (depth > 0) {
204
+ console.log(`${tag} processing ${intents.length} follow-up intent(s) at depth ${depth}`);
205
+ }
206
+
207
+ // 3. Batch approve (if config.approve and no per-intent permissions)
208
+ // Intents with `permissions` array use per-action tokens instead of batch approval
209
+ const batchIntents = intents.filter(i => !i.permissions || !Array.isArray(i.permissions));
210
+ const actionIntents = intents.filter(i => i.permissions && Array.isArray(i.permissions));
211
+
212
+ let batchApproved = true;
213
+ if (config.approve && batchIntents.length > 0) {
214
+ console.log(`${tag} waiting for batch approval of ${batchIntents.length} intent(s)...`);
215
+ emitStrategyEvent('strategy:approve', manifest.id, { intents: batchIntents });
216
+ batchApproved = await requestHumanApproval(manifest.id, batchIntents);
217
+ if (!batchApproved) {
218
+ console.log(`${tag} batch intents REJECTED or timed out`);
219
+ // Still process action intents (they get their own approval)
220
+ if (actionIntents.length === 0) return;
221
+ } else {
222
+ console.log(`${tag} batch intents APPROVED`);
223
+ }
224
+ }
225
+
226
+ // Build the ordered list: approved batch intents + per-action intents
227
+ const toProcess: Array<{ intent: Intent; intentToken?: string }> = [];
228
+
229
+ // Add batch-approved intents (use strategy token). If approval was required and rejected, skip.
230
+ const approvedBatchIntents = batchApproved ? batchIntents : [];
231
+ for (const intent of approvedBatchIntents) {
232
+ toProcess.push({ intent, intentToken: token });
233
+ }
234
+
235
+ // Per-action intents: each gets its own approval + scoped token
236
+ for (const intent of actionIntents) {
237
+ console.log(`${tag} [${intent.type}] requesting per-action token...`);
238
+ const result = await requestActionToken(manifest.id, intent);
239
+ if (!result.approved) {
240
+ console.log(`${tag} [${intent.type}] action REJECTED or timed out`);
241
+ continue;
242
+ }
243
+ console.log(`${tag} [${intent.type}] action APPROVED (temp token)`);
244
+ toProcess.push({ intent, intentToken: result.token });
245
+ }
246
+
247
+ // 4-6. Execute each intent
248
+ const followUps: Intent[] = [];
249
+ let stateUpdated = false;
250
+
251
+ for (let i = 0; i < toProcess.length; i++) {
252
+ const { intent, intentToken } = toProcess[i];
253
+ const intentLabel = intent.type || `#${i}`;
254
+
255
+ try {
256
+ // 4. Determine action: pre-computed or via execute hook
257
+ let action: Action | null;
258
+ const intentAction = intent.action as Record<string, unknown> | undefined;
259
+
260
+ if (intentAction && typeof intentAction.endpoint === 'string' && typeof intentAction.method === 'string') {
261
+ // Pre-computed action — skip execute hook
262
+ action = {
263
+ endpoint: intentAction.endpoint,
264
+ method: intentAction.method,
265
+ body: intentAction.body as Record<string, unknown> | undefined,
266
+ headers: intentAction.headers as Record<string, string> | undefined,
267
+ };
268
+ console.log(`${tag} [${intentLabel}] using pre-computed action`);
269
+ } else if (manifest.hooks.execute) {
270
+ // Call execute hook
271
+ console.log(`${tag} [${intentLabel}] calling execute hook...`);
272
+ const execStart = Date.now();
273
+ const execResult = await callHook(manifest, 'execute', {
274
+ intent,
275
+ config,
276
+ });
277
+ console.log(`${tag} [${intentLabel}] execute hook returned in ${Date.now() - execStart}ms`);
278
+ action = extractAction(execResult);
279
+ } else {
280
+ console.warn(`${tag} [${intentLabel}] no action and no execute hook, skipping`);
281
+ continue;
282
+ }
283
+
284
+ if (!action) {
285
+ console.warn(`${tag} [${intentLabel}] execute hook returned NO action, skipping`);
286
+ continue;
287
+ }
288
+
289
+ console.log(`${tag} [${intentLabel}] action: ${action.method} ${action.endpoint}${action.body ? ' (has body)' : ''}`);
290
+
291
+ // 5. Execute action (use intentToken which may be a per-action temp token)
292
+ let outcome: ActionOutcome;
293
+ try {
294
+ const actionStart = Date.now();
295
+ outcome = await executeAction(action, manifest.id, intentToken, manifest.allowedHosts);
296
+ const actionMs = Date.now() - actionStart;
297
+ if (outcome.success) {
298
+ console.log(`${tag} [${intentLabel}] action OK in ${actionMs}ms`);
299
+ } else {
300
+ console.error(`${tag} [${intentLabel}] action FAILED in ${actionMs}ms: ${outcome.error}`);
301
+ }
302
+ } catch (err) {
303
+ const errMsg = getErrorMessage(err);
304
+ outcome = { success: false, error: errMsg };
305
+ const errorAction = config.errors?.executeFail || 'skip';
306
+
307
+ console.error(`${tag} [${intentLabel}] action threw: ${errMsg} (policy=${errorAction})`);
308
+ emitStrategyEvent('strategy:error', manifest.id, {
309
+ error: errMsg,
310
+ phase: 'execute',
311
+ intent,
312
+ });
313
+
314
+ if (errorAction === 'pause') {
315
+ throw err;
316
+ }
317
+ if (errorAction === 'skip') continue;
318
+ // retry handled by engine
319
+ }
320
+
321
+ // 6. Call result hook
322
+ if (manifest.hooks.result) {
323
+ console.log(`${tag} [${intentLabel}] calling result hook...`);
324
+ const resStart = Date.now();
325
+ const resultResult = await callHook(manifest, 'result', {
326
+ intent,
327
+ action,
328
+ outcome,
329
+ state: getState(manifest.id),
330
+ });
331
+ console.log(`${tag} [${intentLabel}] result hook returned in ${Date.now() - resStart}ms → followUps=${resultResult.intents.length}, stateKeys=${Object.keys(resultResult.state).join(',') || '(none)'}`);
332
+
333
+ if (resultResult.state && Object.keys(resultResult.state).length > 0) {
334
+ updateState(manifest.id, resultResult.state);
335
+ stateUpdated = true;
336
+ }
337
+
338
+ // Collect follow-up intents
339
+ if (resultResult.intents && resultResult.intents.length > 0) {
340
+ followUps.push(...resultResult.intents);
341
+ }
342
+
343
+ // Broadcast any emit events from the result hook
344
+ processEmits(manifest.id, resultResult);
345
+
346
+ if (resultResult.log) {
347
+ console.log(`${tag} [${intentLabel}] result log: ${resultResult.log}`);
348
+ }
349
+ }
350
+ } catch (err) {
351
+ const errMsg = getErrorMessage(err);
352
+ console.error(`${tag} [${intentLabel}] processing error: ${errMsg}`);
353
+ emitStrategyEvent('strategy:error', manifest.id, {
354
+ error: errMsg,
355
+ phase: 'intent',
356
+ intent,
357
+ });
358
+ }
359
+ }
360
+
361
+ // Process follow-up intents (recursive with depth limit)
362
+ if (followUps.length > 0) {
363
+ console.log(`${tag} ${followUps.length} follow-up intent(s) queued`);
364
+ await processIntents(manifest, followUps, config, token, depth + 1);
365
+ }
366
+
367
+ if (stateUpdated) {
368
+ await persistState(manifest.id).catch((err) => {
369
+ console.warn(`${tag} state persistence failed after intents:`, err);
370
+ });
371
+ }
372
+ }
373
+
374
+ /**
375
+ * Extract an Action from the execute hook result.
376
+ * The hook might return the action in intents[0] or directly in the response.
377
+ */
378
+ function extractAction(hookResult: HookResult): Action | null {
379
+ // Try intents[0] first (agent returned action as an intent)
380
+ if (hookResult.intents && hookResult.intents.length > 0) {
381
+ const intent = hookResult.intents[0];
382
+ if (intent.endpoint && intent.method) {
383
+ return {
384
+ endpoint: intent.endpoint as string,
385
+ method: intent.method as string,
386
+ body: intent.body as Record<string, unknown> | undefined,
387
+ headers: intent.headers as Record<string, string> | undefined,
388
+ };
389
+ }
390
+ }
391
+
392
+ // Try state as action (agent put action params in state)
393
+ const state = hookResult.state;
394
+ if (state && state.endpoint && state.method) {
395
+ return {
396
+ endpoint: state.endpoint as string,
397
+ method: state.method as string,
398
+ body: state.body as Record<string, unknown> | undefined,
399
+ headers: state.headers as Record<string, string> | undefined,
400
+ };
401
+ }
402
+
403
+ return null;
404
+ }
@@ -0,0 +1,230 @@
1
+ /**
2
+ * Strategy Engine Types
3
+ * =====================
4
+ * Core type definitions for the cron-based strategy engine.
5
+ */
6
+
7
+ /** Tick tier determines how often a strategy runs */
8
+ export type TickTier = 'sniper' | 'active' | 'standard' | 'slow' | 'maintenance';
9
+
10
+ /** Interval in ms for each tick tier */
11
+ export const TICK_INTERVALS: Record<TickTier, number> = {
12
+ sniper: 10_000,
13
+ active: 30_000,
14
+ standard: 60_000,
15
+ slow: 300_000,
16
+ maintenance: 3_600_000,
17
+ };
18
+
19
+ /** External data source definition from app.md manifest */
20
+ export interface SourceDef {
21
+ id: string;
22
+ url: string;
23
+ method: 'GET' | 'POST';
24
+ body?: Record<string, unknown>;
25
+ auth?: 'none' | 'header' | 'query' | 'bearer';
26
+ header?: string;
27
+ query?: string;
28
+ key?: string;
29
+ depends?: string;
30
+ job?: string;
31
+ optional?: boolean;
32
+ rateLimit?: string;
33
+ select?: Record<string, string>;
34
+ }
35
+
36
+ /** API key definition from app.md manifest */
37
+ export interface KeyDef {
38
+ id: string;
39
+ name: string;
40
+ required?: boolean;
41
+ description?: string;
42
+ }
43
+
44
+ /** Job definition for multi-interval strategies */
45
+ export interface JobDef {
46
+ id: string;
47
+ ticker: TickTier;
48
+ sources?: string[];
49
+ }
50
+
51
+ /** Hook instructions from app.md manifest */
52
+ export interface HooksDef {
53
+ init?: string;
54
+ tick?: string;
55
+ execute?: string;
56
+ result?: string;
57
+ shutdown?: string;
58
+ message?: string;
59
+ }
60
+
61
+ /** Error handling config */
62
+ export interface ErrorConfig {
63
+ sourceFail?: 'skip' | 'retry' | 'pause';
64
+ executeFail?: 'skip' | 'retry' | 'pause';
65
+ maxRetries?: number;
66
+ cooldown?: string;
67
+ }
68
+
69
+ /** Strategy config from app.md manifest */
70
+ export interface StrategyConfig {
71
+ wallet?: string;
72
+ approve?: boolean;
73
+ errors?: ErrorConfig;
74
+ [key: string]: unknown;
75
+ }
76
+
77
+ /** Parsed strategy manifest from app.md */
78
+ export interface StrategyManifest {
79
+ id: string;
80
+ name: string;
81
+ icon?: string;
82
+ category?: string;
83
+ size?: string;
84
+ autoStart?: boolean;
85
+ ticker?: TickTier;
86
+ jobs?: JobDef[];
87
+ sources: SourceDef[];
88
+ keys?: KeyDef[];
89
+ hooks: HooksDef;
90
+ config: StrategyConfig;
91
+ permissions: string[];
92
+ limits?: { fund?: number; send?: number };
93
+ allowedHosts?: string[];
94
+ }
95
+
96
+ /** Runtime state for an active strategy */
97
+ export interface StrategyRuntime {
98
+ manifest: StrategyManifest;
99
+ enabled: boolean;
100
+ running: boolean;
101
+ token?: string;
102
+ tokenHash?: string;
103
+ lastTick?: number;
104
+ lastError?: string;
105
+ errorCount: number;
106
+ pausedUntil?: number;
107
+ authFailureCount?: number;
108
+ }
109
+
110
+ /** Intent — what the agent wants to do (high-level) */
111
+ export interface Intent {
112
+ type: string;
113
+ [key: string]: unknown;
114
+ }
115
+
116
+ /** Action — how to do it (API call the engine executes) */
117
+ export interface Action {
118
+ endpoint: string;
119
+ method: string;
120
+ body?: Record<string, unknown>;
121
+ headers?: Record<string, string>;
122
+ }
123
+
124
+ /** Result of executing an action */
125
+ export interface ActionOutcome {
126
+ success: boolean;
127
+ data?: unknown;
128
+ error?: string;
129
+ }
130
+
131
+ /** A single emission from a hook to push data to the app iframe */
132
+ export interface HookEmit {
133
+ channel: string;
134
+ data: unknown;
135
+ }
136
+
137
+ /** Metadata from the AI provider, attached by callHook() */
138
+ export interface HookMeta {
139
+ model: string;
140
+ provider: string;
141
+ tokens: { input: number; output: number; cacheRead?: number };
142
+ durationMs: number;
143
+ costUsd?: number;
144
+ toolCallCount: number;
145
+ }
146
+
147
+ /** Result from calling a hook (LLM response) */
148
+ export interface HookResult {
149
+ intents: Intent[];
150
+ state: Record<string, unknown>;
151
+ log?: string;
152
+ reply?: string;
153
+ emit?: HookEmit | HookEmit[];
154
+ /** AI provider metadata — model, tokens, timing. Attached by callHook(). */
155
+ _meta?: HookMeta;
156
+ }
157
+
158
+ /** Context passed to the tick hook */
159
+ export interface TickContext {
160
+ sources: Record<string, unknown[]>;
161
+ positions?: unknown[];
162
+ state: Record<string, unknown>;
163
+ config: StrategyConfig;
164
+ wallets?: unknown[];
165
+ permissions: string[];
166
+ budget: { limits: Record<string, number>; spent: Record<string, number>; remaining: Record<string, number> };
167
+ }
168
+
169
+ /** Context passed to the execute hook */
170
+ export interface ExecuteContext {
171
+ intent: Intent;
172
+ wallet?: unknown;
173
+ config: StrategyConfig;
174
+ }
175
+
176
+ /** Context passed to the result hook */
177
+ export interface ResultContext {
178
+ intent: Intent;
179
+ action: Action;
180
+ outcome: ActionOutcome;
181
+ state: Record<string, unknown>;
182
+ }
183
+
184
+ /** Context passed to the init hook */
185
+ export interface InitContext {
186
+ config: StrategyConfig;
187
+ wallets?: unknown[];
188
+ state: Record<string, unknown>;
189
+ storage?: Record<string, unknown>;
190
+ }
191
+
192
+ /** Context passed to the shutdown hook */
193
+ export interface ShutdownContext {
194
+ positions?: unknown[];
195
+ state: Record<string, unknown>;
196
+ }
197
+
198
+ /** Context passed to the message hook */
199
+ export interface MessageContext {
200
+ message: string;
201
+ appId: string;
202
+ state: Record<string, unknown>;
203
+ config: StrategyConfig;
204
+ permissions: string[];
205
+ budget: { limits: Record<string, number>; spent: Record<string, number>; remaining: Record<string, number> };
206
+ }
207
+
208
+ /** Strategy status for REST API */
209
+ export interface StrategyStatus {
210
+ id: string;
211
+ name: string;
212
+ icon?: string;
213
+ ticker?: TickTier;
214
+ enabled: boolean;
215
+ running: boolean;
216
+ lastTick?: number;
217
+ lastError?: string;
218
+ errorCount: number;
219
+ pausedUntil?: number;
220
+ }
221
+
222
+ /** Pending approval for dashboard/REST */
223
+ export interface PendingApproval {
224
+ id: string;
225
+ strategyId: string;
226
+ intents: Intent[];
227
+ createdAt: number;
228
+ resolve: (approved: boolean) => void;
229
+ timer: NodeJS.Timeout;
230
+ }
@@ -0,0 +1,3 @@
1
+ // Re-export everything from the dex module for backwards compatibility
2
+ export * from './dex';
3
+ export { uniswapAdapter, SWAP_HELPER, WETH, getV4PoolKey, V4_HOOKS } from './dex/uniswap';