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,695 @@
1
+ 'use client';
2
+
3
+ import React, { useState, useRef, useEffect } from 'react';
4
+ import { Check, X, ChevronUp, ChevronDown, Zap, ArrowUpRight, KeyRound, Clock, Shield, Info, AlertTriangle, Globe, Wallet, Timer, Lock } from 'lucide-react';
5
+ import { Button } from '@/components/design-system';
6
+ import type { HumanAction } from '@/hooks/useAgentActions';
7
+
8
+ interface HumanActionBarProps {
9
+ requests: HumanAction[];
10
+ resolveAction: (id: string, approved: boolean) => Promise<unknown>;
11
+ actionLoading: string | null;
12
+ }
13
+
14
+ // ---------------------------------------------------------------------------
15
+ // Verified summary types (mirrors server/lib/verified-summary.ts)
16
+ // ---------------------------------------------------------------------------
17
+
18
+ interface VerifiedFact {
19
+ label: string;
20
+ value: string;
21
+ raw?: string;
22
+ }
23
+
24
+ interface SummaryDiscrepancy {
25
+ field: string;
26
+ agentClaim: string;
27
+ actual: string;
28
+ severity: 'info' | 'warning' | 'critical';
29
+ }
30
+
31
+ interface VerifiedSummary {
32
+ action: string;
33
+ oneLiner: string;
34
+ facts: VerifiedFact[];
35
+ permissionLabels: string[];
36
+ limitLabels: string[];
37
+ walletAccessLabels: string[];
38
+ ttlLabel: string;
39
+ agentId: string;
40
+ discrepancies: SummaryDiscrepancy[];
41
+ verified: boolean;
42
+ generatedAt: string;
43
+ }
44
+
45
+ interface ActionMeta {
46
+ agentId?: string;
47
+ summary?: string;
48
+ limit?: number;
49
+ permissions?: string[];
50
+ limits?: Record<string, number>;
51
+ walletAccess?: string[];
52
+ ttl?: number;
53
+ action?: { endpoint?: string; method?: string; body?: Record<string, unknown> };
54
+ verifiedSummary?: VerifiedSummary;
55
+ }
56
+
57
+ // ---------------------------------------------------------------------------
58
+ // Helpers
59
+ // ---------------------------------------------------------------------------
60
+
61
+ function parseActionMeta(request: HumanAction): ActionMeta {
62
+ if (!request.metadata) return {};
63
+ try {
64
+ return JSON.parse(request.metadata);
65
+ } catch {
66
+ return {};
67
+ }
68
+ }
69
+
70
+ function formatSummary(request: HumanAction): string {
71
+ const meta = parseActionMeta(request);
72
+ if (meta.verifiedSummary?.oneLiner) return meta.verifiedSummary.oneLiner;
73
+ if (meta.summary) return meta.summary;
74
+ const agent = meta.agentId || 'agent';
75
+ const type = request.type.replace(/_/g, ' ');
76
+ if (request.amount) return `${agent} requesting ${request.amount} ETH`;
77
+ if (meta.limit) return `${agent} requesting ${meta.limit} ETH limit`;
78
+ return `${agent} — ${type}`;
79
+ }
80
+
81
+ function hasDiscrepancies(request: HumanAction): boolean {
82
+ const meta = parseActionMeta(request);
83
+ return (meta.verifiedSummary?.discrepancies?.length ?? 0) > 0;
84
+ }
85
+
86
+ function hasCriticalDiscrepancies(request: HumanAction): boolean {
87
+ const meta = parseActionMeta(request);
88
+ return (meta.verifiedSummary?.discrepancies?.some(d => d.severity === 'critical')) ?? false;
89
+ }
90
+
91
+ function getTypeIcon(type: string) {
92
+ switch (type) {
93
+ case 'agent_access': return <KeyRound size={10} />;
94
+ case 'fund': return <ArrowUpRight size={10} />;
95
+ case 'send': return <Zap size={10} />;
96
+ case 'action': return <Shield size={10} />;
97
+ default: return <Zap size={10} />;
98
+ }
99
+ }
100
+
101
+ function formatTypeLabel(type: string): string {
102
+ switch (type) {
103
+ case 'agent_access': return 'ACCESS';
104
+ case 'fund': return 'FUND';
105
+ case 'send': return 'SEND';
106
+ case 'action': return 'ACTION';
107
+ default: return type.toUpperCase().replace(/_/g, ' ');
108
+ }
109
+ }
110
+
111
+ function timeAgo(dateStr: string): string {
112
+ const diff = Date.now() - new Date(dateStr).getTime();
113
+ const mins = Math.floor(diff / 60000);
114
+ if (mins < 1) return 'just now';
115
+ if (mins < 60) return `${mins}m ago`;
116
+ const hrs = Math.floor(mins / 60);
117
+ if (hrs < 24) return `${hrs}h ago`;
118
+ return `${Math.floor(hrs / 24)}d ago`;
119
+ }
120
+
121
+ function formatTtl(ttl: number | undefined): string {
122
+ if (!ttl) return 'Default';
123
+ if (ttl < 60) return `${ttl}s`;
124
+ if (ttl < 3600) return `${Math.floor(ttl / 60)}m`;
125
+ return `${Math.floor(ttl / 3600)}h`;
126
+ }
127
+
128
+ // ---------------------------------------------------------------------------
129
+ // Detail section sub-component
130
+ // ---------------------------------------------------------------------------
131
+
132
+ function DetailSection({ icon, label, children, muted }: {
133
+ icon: React.ReactNode;
134
+ label: string;
135
+ children: React.ReactNode;
136
+ muted?: boolean;
137
+ }) {
138
+ return (
139
+ <div className="flex gap-2 py-1.5" style={{ opacity: muted ? 0.7 : 1 }}>
140
+ <div className="shrink-0 mt-0.5" style={{ color: 'var(--color-text-muted, #6b7280)' }}>
141
+ {icon}
142
+ </div>
143
+ <div className="min-w-0 flex-1">
144
+ <div
145
+ className="font-mono text-[8px] font-bold tracking-[0.15em] uppercase mb-1"
146
+ style={{ color: 'var(--color-text-faint, #9ca3af)' }}
147
+ >
148
+ {label}
149
+ </div>
150
+ <div
151
+ className="font-mono text-[10px]"
152
+ style={{
153
+ color: 'var(--color-text, #0a0a0a)',
154
+ fontStyle: muted ? 'italic' : undefined,
155
+ }}
156
+ >
157
+ {children}
158
+ </div>
159
+ </div>
160
+ </div>
161
+ );
162
+ }
163
+
164
+ // ---------------------------------------------------------------------------
165
+ // Detail panel
166
+ // ---------------------------------------------------------------------------
167
+
168
+ function ActionDetailPanel({ meta }: { meta: ActionMeta }) {
169
+ const vs = meta.verifiedSummary;
170
+ const hasWarnings = (vs?.discrepancies?.length ?? 0) > 0;
171
+
172
+ return (
173
+ <div
174
+ className="px-4 py-3"
175
+ style={{
176
+ background: 'var(--color-background-alt, #f4f4f5)',
177
+ borderTop: '1px solid var(--color-border, #e5e5e5)',
178
+ }}
179
+ >
180
+ {/* Discrepancy warning banner */}
181
+ {hasWarnings && vs?.discrepancies && (
182
+ <div
183
+ className="flex items-start gap-2 px-3 py-2 mb-3"
184
+ style={{
185
+ background: 'color-mix(in srgb, var(--color-warning, #f59e0b) 10%, transparent)',
186
+ border: '1px solid color-mix(in srgb, var(--color-warning, #f59e0b) 30%, transparent)',
187
+ }}
188
+ >
189
+ <AlertTriangle size={12} className="shrink-0 mt-0.5" style={{ color: 'var(--color-warning, #f59e0b)' }} />
190
+ <div className="font-mono text-[9px]" style={{ color: 'var(--color-text, #0a0a0a)' }}>
191
+ {vs.discrepancies.map((d, i) => (
192
+ <div key={i} className="mb-1 last:mb-0">
193
+ <span
194
+ className="font-bold uppercase text-[8px] mr-1"
195
+ style={{
196
+ color: d.severity === 'critical'
197
+ ? 'var(--color-danger, #ef4444)'
198
+ : d.severity === 'warning'
199
+ ? 'var(--color-warning, #f59e0b)'
200
+ : 'var(--color-text-muted, #6b7280)',
201
+ }}
202
+ >
203
+ {d.severity}
204
+ </span>
205
+ {d.field}: agent says &ldquo;{d.agentClaim}&rdquo;, actual: {d.actual}
206
+ </div>
207
+ ))}
208
+ </div>
209
+ </div>
210
+ )}
211
+
212
+ {/* AGENT SAYS */}
213
+ {meta.summary && (
214
+ <DetailSection icon={<Info size={10} />} label="AGENT SAYS" muted>
215
+ &ldquo;{meta.summary}&rdquo;
216
+ </DetailSection>
217
+ )}
218
+
219
+ {/* ACTION */}
220
+ {meta.action && (
221
+ <DetailSection icon={<Globe size={10} />} label="ACTION">
222
+ <div className="mb-1">
223
+ <span
224
+ className="font-bold px-1 py-0.5 mr-1"
225
+ style={{
226
+ background: 'var(--color-border, #e5e5e5)',
227
+ fontSize: '9px',
228
+ }}
229
+ >
230
+ {meta.action.method || 'POST'}
231
+ </span>
232
+ <span>{meta.action.endpoint}</span>
233
+ </div>
234
+ {vs?.facts && vs.facts.length > 0 && (
235
+ <div className="mt-1">
236
+ {vs.facts.map((fact, i) => (
237
+ <div key={i} className="flex gap-2">
238
+ <span style={{ color: 'var(--color-text-muted, #6b7280)' }}>{fact.label}:</span>
239
+ <span>{fact.value}</span>
240
+ </div>
241
+ ))}
242
+ </div>
243
+ )}
244
+ </DetailSection>
245
+ )}
246
+
247
+ {/* PERMISSIONS */}
248
+ {vs?.permissionLabels && vs.permissionLabels.length > 0 && (
249
+ <DetailSection icon={<Lock size={10} />} label="PERMISSIONS">
250
+ <div className="flex flex-wrap gap-1">
251
+ {vs.permissionLabels.map((label, i) => (
252
+ <span
253
+ key={i}
254
+ className="px-1.5 py-0.5 text-[8px] font-bold"
255
+ style={{
256
+ background: 'var(--color-border, #e5e5e5)',
257
+ color: 'var(--color-text-muted, #6b7280)',
258
+ }}
259
+ >
260
+ {label}
261
+ </span>
262
+ ))}
263
+ </div>
264
+ </DetailSection>
265
+ )}
266
+
267
+ {/* LIMITS */}
268
+ {vs?.limitLabels && vs.limitLabels.length > 0 && (
269
+ <DetailSection icon={<Shield size={10} />} label="LIMITS">
270
+ {vs.limitLabels.map((label, i) => (
271
+ <div key={i}>{label}</div>
272
+ ))}
273
+ </DetailSection>
274
+ )}
275
+
276
+ {/* WALLET ACCESS */}
277
+ {vs?.walletAccessLabels && vs.walletAccessLabels.length > 0 && (
278
+ <DetailSection icon={<Wallet size={10} />} label="WALLET ACCESS">
279
+ <div className="flex flex-wrap gap-1">
280
+ {vs.walletAccessLabels.map((addr, i) => (
281
+ <span
282
+ key={i}
283
+ className="px-1.5 py-0.5 text-[8px] font-mono"
284
+ style={{
285
+ background: 'var(--color-border, #e5e5e5)',
286
+ color: 'var(--color-text-muted, #6b7280)',
287
+ }}
288
+ >
289
+ {addr}
290
+ </span>
291
+ ))}
292
+ </div>
293
+ </DetailSection>
294
+ )}
295
+
296
+ {/* VALID FOR */}
297
+ {vs?.ttlLabel && (
298
+ <DetailSection icon={<Timer size={10} />} label="VALID FOR">
299
+ {vs.ttlLabel}
300
+ </DetailSection>
301
+ )}
302
+ </div>
303
+ );
304
+ }
305
+
306
+ // ---------------------------------------------------------------------------
307
+ // ActionCard
308
+ // ---------------------------------------------------------------------------
309
+
310
+ function ActionCard({ request, resolveAction, actionLoading, showBorder }: {
311
+ request: HumanAction;
312
+ resolveAction: (id: string, approved: boolean) => Promise<unknown>;
313
+ actionLoading: string | null;
314
+ showBorder: boolean;
315
+ }) {
316
+ const [detailExpanded, setDetailExpanded] = useState(false);
317
+ const approving = actionLoading === `resolve-${request.id}` || actionLoading === `approve-${request.id}`;
318
+ const rejecting = actionLoading === `reject-${request.id}`;
319
+ const loading = approving || rejecting;
320
+ const meta = parseActionMeta(request);
321
+ const hasMismatch = hasDiscrepancies(request);
322
+ const borderColor = hasMismatch
323
+ ? 'var(--color-warning, #f59e0b)'
324
+ : 'var(--color-danger, #ef4444)';
325
+
326
+ return (
327
+ <div>
328
+ <div
329
+ className="flex items-center gap-3 px-4 py-3"
330
+ style={{
331
+ borderBottom: showBorder && !detailExpanded ? '1px solid var(--color-border, #e5e5e5)' : undefined,
332
+ borderLeft: `3px solid ${borderColor}`,
333
+ background: 'var(--color-surface, #ffffff)',
334
+ }}
335
+ >
336
+ {/* Type badge */}
337
+ <div
338
+ className="flex items-center gap-1 px-1.5 py-0.5 shrink-0"
339
+ style={{
340
+ background: 'var(--color-background-alt, #f4f4f5)',
341
+ border: '1px solid var(--color-border, #e5e5e5)',
342
+ color: 'var(--color-text-muted, #6b7280)',
343
+ }}
344
+ >
345
+ {getTypeIcon(request.type)}
346
+ <span className="font-mono text-[8px] font-bold tracking-wider uppercase">
347
+ {formatTypeLabel(request.type)}
348
+ </span>
349
+ </div>
350
+
351
+ {/* Summary + timestamp */}
352
+ <div className="flex-1 min-w-0">
353
+ <div
354
+ className="font-mono text-[10px] truncate flex items-center gap-1.5"
355
+ style={{ color: 'var(--color-text, #0a0a0a)' }}
356
+ >
357
+ {formatSummary(request)}
358
+ {hasMismatch && (
359
+ <span
360
+ className="shrink-0 px-1 py-0 text-[7px] font-bold tracking-wider uppercase"
361
+ style={{
362
+ background: 'color-mix(in srgb, var(--color-warning, #f59e0b) 15%, transparent)',
363
+ color: 'var(--color-warning, #f59e0b)',
364
+ border: '1px solid color-mix(in srgb, var(--color-warning, #f59e0b) 30%, transparent)',
365
+ }}
366
+ >
367
+ MISMATCH
368
+ </span>
369
+ )}
370
+ </div>
371
+ <div
372
+ className="font-mono text-[8px] mt-0.5 flex items-center gap-1"
373
+ style={{ color: 'var(--color-text-faint, #9ca3af)' }}
374
+ >
375
+ <Clock size={7} />
376
+ {timeAgo(request.createdAt)}
377
+ </div>
378
+ </div>
379
+
380
+ {/* Detail toggle */}
381
+ {meta.verifiedSummary && (
382
+ <button
383
+ onClick={() => setDetailExpanded(!detailExpanded)}
384
+ className="flex items-center gap-1 shrink-0 hover:opacity-70"
385
+ style={{
386
+ background: 'none',
387
+ border: 'none',
388
+ cursor: 'pointer',
389
+ color: 'var(--color-text-muted, #6b7280)',
390
+ padding: '2px 4px',
391
+ }}
392
+ >
393
+ <Info size={10} />
394
+ {detailExpanded ? <ChevronUp size={10} /> : <ChevronDown size={10} />}
395
+ </button>
396
+ )}
397
+
398
+ {/* Actions */}
399
+ <div className="flex items-center gap-1.5 shrink-0">
400
+ <Button
401
+ variant="primary"
402
+ size="sm"
403
+ onClick={() => resolveAction(request.id, true)}
404
+ disabled={loading}
405
+ loading={approving}
406
+ icon={!approving ? <Check size={10} /> : undefined}
407
+ className="h-7 px-2"
408
+ >
409
+ APPROVE
410
+ </Button>
411
+ <Button
412
+ variant="danger"
413
+ size="sm"
414
+ onClick={() => resolveAction(request.id, false)}
415
+ disabled={loading}
416
+ loading={rejecting}
417
+ icon={!rejecting ? <X size={10} /> : undefined}
418
+ className="h-7 px-2"
419
+ >
420
+ REJECT
421
+ </Button>
422
+ </div>
423
+ </div>
424
+
425
+ {/* Expanded detail panel */}
426
+ <div
427
+ style={{
428
+ display: 'grid',
429
+ gridTemplateRows: detailExpanded ? '1fr' : '0fr',
430
+ transition: 'grid-template-rows 150ms ease',
431
+ borderLeft: `3px solid ${borderColor}`,
432
+ borderBottom: showBorder && detailExpanded ? '1px solid var(--color-border, #e5e5e5)' : undefined,
433
+ }}
434
+ >
435
+ <div style={{ overflow: 'hidden' }}>
436
+ {detailExpanded && <ActionDetailPanel meta={meta} />}
437
+ </div>
438
+ </div>
439
+ </div>
440
+ );
441
+ }
442
+
443
+ // ---------------------------------------------------------------------------
444
+ // Main bar
445
+ // ---------------------------------------------------------------------------
446
+
447
+ export const HumanActionBar: React.FC<HumanActionBarProps> = ({
448
+ requests,
449
+ resolveAction,
450
+ actionLoading,
451
+ }) => {
452
+ const [expanded, setExpanded] = useState(false);
453
+ const [detailExpanded, setDetailExpanded] = useState(false);
454
+ const pending = requests.filter(r => r.status === 'pending' && r.type !== 'notify');
455
+ const panelRef = useRef<HTMLDivElement>(null);
456
+
457
+ // Close expanded panel on outside click
458
+ useEffect(() => {
459
+ if (!expanded) return;
460
+ const handler = (e: MouseEvent) => {
461
+ if (panelRef.current && !panelRef.current.contains(e.target as Node)) {
462
+ setExpanded(false);
463
+ }
464
+ };
465
+ document.addEventListener('mousedown', handler);
466
+ return () => document.removeEventListener('mousedown', handler);
467
+ }, [expanded]);
468
+
469
+ // Auto-collapse when queue drains
470
+ useEffect(() => {
471
+ if (pending.length <= 1) setExpanded(false);
472
+ }, [pending.length]);
473
+
474
+ // Hidden when nothing pending
475
+ if (pending.length === 0) return null;
476
+
477
+ const first = pending[0];
478
+ const firstMeta = parseActionMeta(first);
479
+ const firstApproving = actionLoading === `resolve-${first.id}` || actionLoading === `approve-${first.id}`;
480
+ const firstRejecting = actionLoading === `reject-${first.id}`;
481
+ const firstLoading = firstApproving || firstRejecting;
482
+ const firstHasMismatch = hasDiscrepancies(first);
483
+ const firstBorderColor = firstHasMismatch
484
+ ? 'var(--color-warning, #f59e0b)'
485
+ : 'var(--color-danger, #ef4444)';
486
+
487
+ return (
488
+ <div className="relative z-30" ref={panelRef}>
489
+ {/* Expanded stack — grows upward */}
490
+ {expanded && pending.length > 1 && (
491
+ <div
492
+ className="absolute bottom-full left-0 right-0"
493
+ style={{
494
+ maxHeight: '320px',
495
+ overflowY: 'auto',
496
+ border: '1px solid var(--color-border, #e5e5e5)',
497
+ borderBottom: 'none',
498
+ }}
499
+ >
500
+ {/* Queue header */}
501
+ <div
502
+ className="px-4 py-2 sticky top-0 z-10"
503
+ style={{
504
+ background: 'var(--color-background-alt, #f4f4f5)',
505
+ borderBottom: '1px solid var(--color-border, #e5e5e5)',
506
+ }}
507
+ >
508
+ <span
509
+ className="font-mono text-[8px] font-bold tracking-[0.2em] uppercase"
510
+ style={{ color: 'var(--color-text-faint, #9ca3af)' }}
511
+ >
512
+ QUEUED ACTIONS ({pending.length - 1})
513
+ </span>
514
+ </div>
515
+
516
+ {/* Queued action cards */}
517
+ {pending.slice(1).map((req, i) => (
518
+ <ActionCard
519
+ key={req.id}
520
+ request={req}
521
+ resolveAction={resolveAction}
522
+ actionLoading={actionLoading}
523
+ showBorder={i < pending.length - 2}
524
+ />
525
+ ))}
526
+ </div>
527
+ )}
528
+
529
+ {/* Main bar */}
530
+ <div
531
+ style={{
532
+ background: 'var(--color-surface, #ffffff)',
533
+ borderTop: '1px solid var(--color-border, #e5e5e5)',
534
+ borderBottom: '1px solid var(--color-border, #e5e5e5)',
535
+ }}
536
+ >
537
+ <div
538
+ className="flex items-center h-14 px-4 gap-3"
539
+ style={{ borderLeft: `3px solid ${firstBorderColor}` }}
540
+ >
541
+ {/* Red count badge — flat rectangle, no radius */}
542
+ <div
543
+ className="flex items-center justify-center shrink-0"
544
+ style={{
545
+ minWidth: '24px',
546
+ height: '22px',
547
+ padding: '0 6px',
548
+ background: 'var(--color-danger, #ef4444)',
549
+ color: '#ffffff',
550
+ fontFamily: 'monospace',
551
+ fontSize: '11px',
552
+ fontWeight: 700,
553
+ letterSpacing: '0.05em',
554
+ }}
555
+ >
556
+ {pending.length}
557
+ </div>
558
+
559
+ {/* Label */}
560
+ <span
561
+ className="font-mono text-[10px] font-bold tracking-[0.2em] uppercase shrink-0"
562
+ style={{ color: 'var(--color-danger, #ef4444)' }}
563
+ >
564
+ PENDING
565
+ </span>
566
+
567
+ {/* Divider */}
568
+ <div
569
+ className="w-px h-6 shrink-0"
570
+ style={{ background: 'var(--color-border, #e5e5e5)' }}
571
+ />
572
+
573
+ {/* Type badge */}
574
+ <div
575
+ className="flex items-center gap-1 px-1.5 py-0.5 shrink-0"
576
+ style={{
577
+ background: 'var(--color-background-alt, #f4f4f5)',
578
+ border: '1px solid var(--color-border, #e5e5e5)',
579
+ color: 'var(--color-text-muted, #6b7280)',
580
+ }}
581
+ >
582
+ {getTypeIcon(first.type)}
583
+ <span className="font-mono text-[8px] font-bold tracking-[0.15em] uppercase">
584
+ {formatTypeLabel(first.type)}
585
+ </span>
586
+ </div>
587
+
588
+ {/* Summary */}
589
+ <div
590
+ className="font-mono text-[11px] truncate min-w-0 flex-1 flex items-center gap-1.5"
591
+ style={{ color: 'var(--color-text, #0a0a0a)' }}
592
+ >
593
+ {formatSummary(first)}
594
+ {firstHasMismatch && (
595
+ <span
596
+ className="shrink-0 px-1 py-0 text-[7px] font-bold tracking-wider uppercase"
597
+ style={{
598
+ background: 'color-mix(in srgb, var(--color-warning, #f59e0b) 15%, transparent)',
599
+ color: 'var(--color-warning, #f59e0b)',
600
+ border: '1px solid color-mix(in srgb, var(--color-warning, #f59e0b) 30%, transparent)',
601
+ }}
602
+ >
603
+ MISMATCH
604
+ </span>
605
+ )}
606
+ </div>
607
+
608
+ {/* Detail toggle */}
609
+ {firstMeta.verifiedSummary && (
610
+ <button
611
+ onClick={() => setDetailExpanded(!detailExpanded)}
612
+ className="flex items-center gap-1 shrink-0 hover:opacity-70"
613
+ style={{
614
+ background: 'none',
615
+ border: 'none',
616
+ cursor: 'pointer',
617
+ color: 'var(--color-text-muted, #6b7280)',
618
+ padding: '2px 4px',
619
+ }}
620
+ >
621
+ <Info size={10} />
622
+ {detailExpanded ? <ChevronUp size={10} /> : <ChevronDown size={10} />}
623
+ </button>
624
+ )}
625
+
626
+ {/* Action buttons */}
627
+ <div className="flex items-center gap-1.5 shrink-0">
628
+ <Button
629
+ variant="primary"
630
+ size="sm"
631
+ onClick={() => resolveAction(first.id, true)}
632
+ disabled={firstLoading}
633
+ loading={firstApproving}
634
+ icon={!firstApproving ? <Check size={10} /> : undefined}
635
+ className="h-7 px-2"
636
+ >
637
+ APPROVE
638
+ </Button>
639
+ <Button
640
+ variant="danger"
641
+ size="sm"
642
+ onClick={() => resolveAction(first.id, false)}
643
+ disabled={firstLoading}
644
+ loading={firstRejecting}
645
+ icon={!firstRejecting ? <X size={10} /> : undefined}
646
+ className="h-7 px-2"
647
+ >
648
+ REJECT
649
+ </Button>
650
+ </div>
651
+
652
+ {/* Expand toggle — only when multiple pending */}
653
+ {pending.length > 1 && (
654
+ <>
655
+ <div
656
+ className="w-px h-6 shrink-0"
657
+ style={{ background: 'var(--color-border, #e5e5e5)' }}
658
+ />
659
+ <button
660
+ onClick={() => setExpanded(!expanded)}
661
+ className="flex items-center gap-1.5 shrink-0 hover:opacity-70"
662
+ style={{
663
+ background: 'none',
664
+ border: 'none',
665
+ cursor: 'pointer',
666
+ color: 'var(--color-text-muted, #6b7280)',
667
+ padding: '4px 8px',
668
+ }}
669
+ >
670
+ <span className="font-mono text-[8px] font-bold tracking-[0.15em] uppercase">
671
+ +{pending.length - 1} MORE
672
+ </span>
673
+ {expanded ? <ChevronDown size={12} /> : <ChevronUp size={12} />}
674
+ </button>
675
+ </>
676
+ )}
677
+ </div>
678
+
679
+ {/* First card's expanded detail panel */}
680
+ <div
681
+ style={{
682
+ display: 'grid',
683
+ gridTemplateRows: detailExpanded ? '1fr' : '0fr',
684
+ transition: 'grid-template-rows 150ms ease',
685
+ borderLeft: `3px solid ${firstBorderColor}`,
686
+ }}
687
+ >
688
+ <div style={{ overflow: 'hidden' }}>
689
+ {detailExpanded && <ActionDetailPanel meta={firstMeta} />}
690
+ </div>
691
+ </div>
692
+ </div>
693
+ </div>
694
+ );
695
+ };