auramaxx 0.0.17 → 0.0.19

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 (296) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/app-build-manifest.json +70 -70
  3. package/.next/app-path-routes-manifest.json +2 -2
  4. package/.next/build-manifest.json +5 -5
  5. package/.next/prerender-manifest.json +30 -30
  6. package/.next/react-loadable-manifest.json +16 -16
  7. package/.next/server/app/_not-found/page.js.nft.json +1 -1
  8. package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  9. package/.next/server/app/_not-found.html +1 -1
  10. package/.next/server/app/_not-found.rsc +5 -5
  11. package/.next/server/app/api/[...doc]/page.js.nft.json +1 -1
  12. package/.next/server/app/api/[...doc]/page_client-reference-manifest.js +1 -1
  13. package/.next/server/app/api/agent-requests/route_client-reference-manifest.js +1 -1
  14. package/.next/server/app/api/apps/install/route.js.nft.json +1 -1
  15. package/.next/server/app/api/apps/install/route_client-reference-manifest.js +1 -1
  16. package/.next/server/app/api/apps/manifests/route_client-reference-manifest.js +1 -1
  17. package/.next/server/app/api/apps/static/[...path]/route_client-reference-manifest.js +1 -1
  18. package/.next/server/app/api/docs/plain/route.js.nft.json +1 -1
  19. package/.next/server/app/api/docs/plain/route_client-reference-manifest.js +1 -1
  20. package/.next/server/app/api/events/route_client-reference-manifest.js +1 -1
  21. package/.next/server/app/api/import-from-openclaw/[channel]/route.js.nft.json +1 -1
  22. package/.next/server/app/api/import-from-openclaw/[channel]/route_client-reference-manifest.js +1 -1
  23. package/.next/server/app/api/import-from-openclaw/route.js.nft.json +1 -1
  24. package/.next/server/app/api/import-from-openclaw/route_client-reference-manifest.js +1 -1
  25. package/.next/server/app/api/import-from-openclaw/validate/[channel]/route.js.nft.json +1 -1
  26. package/.next/server/app/api/import-from-openclaw/validate/[channel]/route_client-reference-manifest.js +1 -1
  27. package/.next/server/app/api/page.js.nft.json +1 -1
  28. package/.next/server/app/api/page_client-reference-manifest.js +1 -1
  29. package/.next/server/app/api/restart/route_client-reference-manifest.js +1 -1
  30. package/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
  31. package/.next/server/app/api/version/route_client-reference-manifest.js +1 -1
  32. package/.next/server/app/api/workspace/[id]/apps/[wid]/route_client-reference-manifest.js +1 -1
  33. package/.next/server/app/api/workspace/[id]/apps/route_client-reference-manifest.js +1 -1
  34. package/.next/server/app/api/workspace/[id]/export/route_client-reference-manifest.js +1 -1
  35. package/.next/server/app/api/workspace/[id]/route_client-reference-manifest.js +1 -1
  36. package/.next/server/app/api/workspace/config/route_client-reference-manifest.js +1 -1
  37. package/.next/server/app/api/workspace/import/route_client-reference-manifest.js +1 -1
  38. package/.next/server/app/api/workspace/route_client-reference-manifest.js +1 -1
  39. package/.next/server/app/app-legacy-do-not-use/page.js +3 -3
  40. package/.next/server/app/app-legacy-do-not-use/page.js.nft.json +1 -1
  41. package/.next/server/app/app-legacy-do-not-use/page_client-reference-manifest.js +1 -1
  42. package/.next/server/app/app-legacy-do-not-use.html +1 -1
  43. package/.next/server/app/app-legacy-do-not-use.rsc +6 -6
  44. package/.next/server/app/approve/[actionId]/page.js.nft.json +1 -1
  45. package/.next/server/app/approve/[actionId]/page_client-reference-manifest.js +1 -1
  46. package/.next/server/app/docs/[...doc]/page.js.nft.json +1 -1
  47. package/.next/server/app/docs/[...doc]/page_client-reference-manifest.js +1 -1
  48. package/.next/server/app/docs/page.js.nft.json +1 -1
  49. package/.next/server/app/docs/page_client-reference-manifest.js +1 -1
  50. package/.next/server/app/health/page.js.nft.json +1 -1
  51. package/.next/server/app/health/page_client-reference-manifest.js +1 -1
  52. package/.next/server/app/health.html +1 -1
  53. package/.next/server/app/health.rsc +5 -5
  54. package/.next/server/app/hello/page.js.nft.json +1 -1
  55. package/.next/server/app/hello/page_client-reference-manifest.js +1 -1
  56. package/.next/server/app/hello.html +1 -1
  57. package/.next/server/app/hello.rsc +5 -5
  58. package/.next/server/app/index.html +1 -1
  59. package/.next/server/app/index.rsc +6 -6
  60. package/.next/server/app/page.js +9 -3
  61. package/.next/server/app/page.js.nft.json +1 -1
  62. package/.next/server/app/page_client-reference-manifest.js +1 -1
  63. package/.next/server/app/privacy/page.js.nft.json +1 -1
  64. package/.next/server/app/privacy/page_client-reference-manifest.js +1 -1
  65. package/.next/server/app/privacy.html +1 -1
  66. package/.next/server/app/privacy.rsc +5 -5
  67. package/.next/server/app/share/[token]/page.js.nft.json +1 -1
  68. package/.next/server/app/share/[token]/page_client-reference-manifest.js +1 -1
  69. package/.next/server/app/terms/page.js.nft.json +1 -1
  70. package/.next/server/app/terms/page_client-reference-manifest.js +1 -1
  71. package/.next/server/app/terms.html +1 -1
  72. package/.next/server/app/terms.rsc +5 -5
  73. package/.next/server/app/yo/page.js +1 -1
  74. package/.next/server/app/yo/page.js.nft.json +1 -1
  75. package/.next/server/app/yo/page_client-reference-manifest.js +1 -1
  76. package/.next/server/app/yo.html +1 -1
  77. package/.next/server/app/yo.rsc +6 -6
  78. package/.next/server/app-paths-manifest.json +2 -2
  79. package/.next/server/chunks/1196.js +1 -1
  80. package/.next/server/chunks/{6415.js → 2123.js} +2 -2
  81. package/.next/server/chunks/2234.js +1 -1
  82. package/.next/server/chunks/4224.js +1 -0
  83. package/.next/server/chunks/4600.js +1 -1
  84. package/.next/server/chunks/5678.js +1 -1
  85. package/.next/server/chunks/5991.js +7 -0
  86. package/.next/server/chunks/6470.js +1 -1
  87. package/.next/server/chunks/7585.js +1 -1
  88. package/.next/server/chunks/7935.js +19 -11
  89. package/.next/server/chunks/8468.js +1 -1
  90. package/.next/server/chunks/{1350.js → 9323.js} +3 -3
  91. package/.next/server/chunks/9971.js +1 -1
  92. package/.next/server/functions-config-manifest.json +1 -1
  93. package/.next/server/middleware-build-manifest.js +1 -1
  94. package/.next/server/middleware-react-loadable-manifest.js +1 -1
  95. package/.next/server/pages/404.html +1 -1
  96. package/.next/server/pages/500.html +1 -1
  97. package/.next/server/server-reference-manifest.json +1 -1
  98. package/.next/static/chunks/{1168.aaac1edbb597fe5a.js → 1168.bb1c93965872e980.js} +1 -1
  99. package/.next/static/chunks/2891-bf010357841c9ce9.js +1 -0
  100. package/.next/static/chunks/{2927.7e00cc878d9a3f52.js → 2927.3976471e61df966e.js} +1 -1
  101. package/.next/static/chunks/{3573-1b41d7b8a000d015.js → 3573-667bc4c8f56afcff.js} +1 -1
  102. package/.next/static/chunks/{3a91511d-ba215c0b5dc21ba9.js → 3a91511d-da2c74cad6c6fc33.js} +2 -2
  103. package/.next/static/chunks/4114-a375178fd94c4d8a.js +1 -0
  104. package/.next/static/chunks/{4256.48407d9abad5ea33.js → 4256.aa831e4d8c3f65bc.js} +1 -1
  105. package/.next/static/chunks/{4919-7e7cdd5efc9f2110.js → 4919-0e698cd95504e355.js} +1 -1
  106. package/.next/static/chunks/{5336-233ec7ab3807267c.js → 5336-b775f449d7433604.js} +1 -1
  107. package/.next/static/chunks/{7338-3ce17a93614f1d77.js → 5362-2e8fcdd0bc6273ca.js} +4 -4
  108. package/.next/static/chunks/67-cb9bbb430499e803.js +1 -0
  109. package/.next/static/chunks/{8273-8e92d34180669ca9.js → 8273-3b9d882c5dd6a9a8.js} +1 -1
  110. package/.next/static/chunks/8291-8f6fb6554e692552.js +1 -0
  111. package/.next/static/chunks/8370-6537b6b27df58b23.js +1 -0
  112. package/.next/static/chunks/app/app-legacy-do-not-use/page-efa348da049c5873.js +1 -0
  113. package/.next/static/chunks/app/{error-63cbe72522239cff.js → error-04a18fd49a89ee17.js} +1 -1
  114. package/.next/static/chunks/app/layout-7093dda0d106f34d.js +1 -0
  115. package/.next/static/chunks/app/page-0ec7cf48550f71e6.js +1 -0
  116. package/.next/static/chunks/app/yo/{page-cf5d6540ea31c31c.js → page-48930746b135037e.js} +1 -1
  117. package/.next/static/chunks/{webpack-79ad58260e9b10b4.js → webpack-15ec0114c9d658c2.js} +1 -1
  118. package/.next/static/css/35991998d1b5eff0.css +3 -0
  119. package/.next/trace +28 -28
  120. package/README.md +2 -2
  121. package/bin/auramaxx.js +87 -3
  122. package/package.json +8 -4
  123. package/prisma/migrations/20260301225214_social_tables/migration.sql +81 -0
  124. package/prisma/migrations/20260302120000_add_notification_hash/migration.sql +8 -0
  125. package/prisma/migrations/20260302160000_add_public_key_hex/migration.sql +5 -0
  126. package/prisma/migrations/20260302170000_add_inbound_author_public_key/migration.sql +5 -0
  127. package/prisma/schema.prisma +272 -177
  128. package/shared/credential-field-schema.ts +10 -0
  129. package/skills/auramaxx/SKILL.md +33 -4
  130. package/skills/auramaxx/docs/AGENT_SETUP.md +7 -2
  131. package/src/app/UnlockPageClient.tsx +448 -43
  132. package/src/app/app-legacy-do-not-use/page.tsx +5 -3
  133. package/src/app/globals.css +1 -1
  134. package/src/components/AgentPicker.tsx +278 -0
  135. package/src/components/CreateAgentModal.tsx +192 -0
  136. package/src/components/NotificationDrawer.tsx +15 -13
  137. package/src/components/agent/AgentSidebar.tsx +2 -19
  138. package/src/components/agent/CredentialAgent.tsx +70 -37
  139. package/src/components/agent/CredentialForm.tsx +32 -7
  140. package/src/components/agent/CredentialList.tsx +149 -72
  141. package/src/components/agents/AgentsView.tsx +1035 -0
  142. package/src/components/design-system/TextAreaInput.tsx +78 -0
  143. package/src/components/design-system/index.ts +1 -0
  144. package/src/components/hub/HubView.tsx +58 -0
  145. package/src/components/layout/LeftRail.tsx +333 -40
  146. package/src/components/layout/ViewShell.tsx +125 -0
  147. package/src/components/layout/index.ts +1 -0
  148. package/src/components/social/PrimaryHubRegistrationGate.tsx +260 -0
  149. package/src/components/social/SocialView.tsx +904 -0
  150. package/src/hooks/useSocialNotifications.ts +123 -0
  151. package/src/lib/events.ts +10 -1
  152. package/src/lib/social-client.ts +319 -0
  153. package/src/lib/view-registry.ts +22 -4
  154. package/src/server/cli/commands/agent.ts +33 -7
  155. package/src/server/cli/commands/create.ts +209 -0
  156. package/src/server/cli/commands/onboard.ts +39 -0
  157. package/src/server/cli/commands/play.ts +68 -0
  158. package/src/server/cli/commands/register-agent.ts +181 -0
  159. package/src/server/cli/commands/social.ts +648 -0
  160. package/src/server/cli/commands/start.ts +4 -3
  161. package/src/server/cron/index.ts +5 -9
  162. package/src/server/cron/jobs/social-inbound.ts +596 -0
  163. package/src/server/cron/jobs/social-outbound.ts +70 -0
  164. package/src/server/index.ts +14 -0
  165. package/src/server/lib/agent-profiles.ts +10 -1
  166. package/src/server/lib/cold.ts +0 -45
  167. package/src/server/lib/credential-import.ts +53 -47
  168. package/src/server/lib/defaults.ts +11 -0
  169. package/src/server/lib/feature-flags.ts +3 -0
  170. package/src/server/lib/hub-auth.ts +159 -0
  171. package/src/server/lib/migrate-agent-profiles.ts +96 -0
  172. package/src/server/lib/notifications.ts +12 -1
  173. package/src/server/lib/permissions.ts +10 -0
  174. package/src/server/lib/project-scope.ts +33 -5
  175. package/src/server/lib/social/create.ts +176 -0
  176. package/src/server/lib/social/global-aura-id.ts +115 -0
  177. package/src/server/lib/social/hub-rpc-client.ts +99 -0
  178. package/src/server/lib/social/hub-session.ts +6 -0
  179. package/src/server/lib/social/public-key.ts +32 -0
  180. package/src/server/lib/social/register.ts +162 -0
  181. package/src/server/lib/social/sign.ts +95 -0
  182. package/src/server/lib/social/sync.ts +301 -0
  183. package/src/server/mcp/server.ts +142 -18
  184. package/src/server/mcp/tools.ts +552 -0
  185. package/src/server/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -1
  186. package/src/server/routes/agent-hub.ts +276 -0
  187. package/src/server/routes/agent-profiles.ts +145 -19
  188. package/src/server/routes/credentials.ts +49 -15
  189. package/src/server/routes/heartbeat.ts +3 -7
  190. package/src/server/routes/passkey.ts +7 -1
  191. package/src/server/routes/social.ts +869 -0
  192. package/src/server/routes/unlock.ts +17 -1
  193. package/src/server/routes/verified-credentials.ts +312 -0
  194. package/src/server/test-data/template.db +0 -0
  195. package/src/server/test-data/worker-1/agent-primary.json +12 -0
  196. package/src/server/test-data/worker-1/auramaxx.db +0 -0
  197. package/src/server/test-data/worker-1/backups/auramaxx.db.20260305_011404.bak +0 -0
  198. package/src/server/test-data/worker-1/backups/credentials.20260305_011404.cred-00ntf5h3.json +24 -0
  199. package/src/server/test-data/worker-1/backups/credentials.20260305_011404.cred-dh8fa9k3.json +24 -0
  200. package/src/server/test-data/worker-1/backups/credentials.20260305_011404.cred-prfhlyow.json +25 -0
  201. package/src/server/test-data/worker-1/backups/credentials.20260305_011404.cred-w26jd6zi.json +22 -0
  202. package/src/server/test-data/worker-1/config.json +33 -0
  203. package/src/server/test-data/worker-1/test.db +0 -0
  204. package/src/server/test-data/worker-10/agent-primary.json +12 -0
  205. package/src/server/test-data/worker-10/config.json +33 -0
  206. package/src/server/test-data/worker-10/test.db +0 -0
  207. package/src/server/test-data/worker-11/agent-primary.json +12 -0
  208. package/src/server/test-data/worker-11/config.json +33 -0
  209. package/src/server/test-data/worker-11/test.db +0 -0
  210. package/src/server/test-data/worker-12/agent-primary.json +12 -0
  211. package/src/server/test-data/worker-12/config.json +33 -0
  212. package/src/server/test-data/worker-12/credentials/cred-hboagned.json +25 -0
  213. package/src/server/test-data/worker-12/credentials/cred-nt14xd9t.json +24 -0
  214. package/src/server/test-data/worker-12/credentials/cred-qn59xefc.json +24 -0
  215. package/src/server/test-data/worker-12/test.db +0 -0
  216. package/src/server/test-data/worker-13/agent-primary.json +12 -0
  217. package/src/server/test-data/worker-13/approval-context.json +56 -0
  218. package/src/server/test-data/worker-13/approval-context.key +1 -0
  219. package/src/server/test-data/worker-13/config.json +33 -0
  220. package/src/server/test-data/worker-13/credentials-recently-deleted/cred-jn9yqy2m.json +20 -0
  221. package/src/server/test-data/worker-13/test.db +0 -0
  222. package/src/server/test-data/worker-14/agent-primary.json +12 -0
  223. package/src/server/test-data/worker-14/config.json +33 -0
  224. package/src/server/test-data/worker-14/test.db +0 -0
  225. package/src/server/test-data/worker-15/agent-primary.json +12 -0
  226. package/src/server/test-data/worker-15/config.json +33 -0
  227. package/src/server/test-data/worker-15/test.db +0 -0
  228. package/src/server/test-data/worker-15/update-check-4b305e76.json +5 -0
  229. package/src/server/test-data/worker-2/agent-primary.json +12 -0
  230. package/src/server/test-data/worker-2/config.json +33 -0
  231. package/src/server/test-data/worker-2/test.db +0 -0
  232. package/src/server/test-data/worker-3/agent-primary.json +12 -0
  233. package/src/server/test-data/worker-3/config.json +33 -0
  234. package/src/server/test-data/worker-3/test.db +0 -0
  235. package/src/server/test-data/worker-4/agent-primary.json +12 -0
  236. package/src/server/test-data/worker-4/config.json +33 -0
  237. package/src/server/test-data/worker-4/credentials/cred-bm6p74u5.json +24 -0
  238. package/src/server/test-data/worker-4/credentials/cred-r8eqb07i.json +25 -0
  239. package/src/server/test-data/worker-4/credentials/cred-ziv49y9h.json +24 -0
  240. package/src/server/test-data/worker-4/test.db +0 -0
  241. package/src/server/test-data/worker-5/agent-primary.json +12 -0
  242. package/src/server/test-data/worker-5/config.json +33 -0
  243. package/src/server/test-data/worker-5/credentials/cred-15gyfeht.json +25 -0
  244. package/src/server/test-data/worker-5/credentials/cred-lp0ocnfc.json +24 -0
  245. package/src/server/test-data/worker-5/credentials/cred-zfbwfxw0.json +24 -0
  246. package/src/server/test-data/worker-5/test.db +0 -0
  247. package/src/server/test-data/worker-6/agent-primary.json +12 -0
  248. package/src/server/test-data/worker-6/config.json +33 -0
  249. package/src/server/test-data/worker-6/credentials/cred-dx8li3ue.json +24 -0
  250. package/src/server/test-data/worker-6/credentials/cred-myeskqky.json +24 -0
  251. package/src/server/test-data/worker-6/credentials/cred-tam01qdt.json +25 -0
  252. package/src/server/test-data/worker-6/credentials/cred-wdh6rslw.json +22 -0
  253. package/src/server/test-data/worker-6/credentials-archive/cred-4wg53v1y.json +25 -0
  254. package/src/server/test-data/worker-6/test.db +0 -0
  255. package/src/server/test-data/worker-7/agent-primary.json +12 -0
  256. package/src/server/test-data/worker-7/config.json +33 -0
  257. package/src/server/test-data/worker-7/credentials/cred-ji7ec57d.json +25 -0
  258. package/src/server/test-data/worker-7/credentials/cred-mmgj0ypo.json +24 -0
  259. package/src/server/test-data/worker-7/credentials/cred-nb0ic93x.json +24 -0
  260. package/src/server/test-data/worker-7/test.db +0 -0
  261. package/src/server/test-data/worker-8/agent-primary.json +12 -0
  262. package/src/server/test-data/worker-8/config.json +33 -0
  263. package/src/server/test-data/worker-8/test.db +0 -0
  264. package/src/server/test-data/worker-9/agent-primary.json +12 -0
  265. package/src/server/test-data/worker-9/test.db +0 -0
  266. package/src/server/tests/cli/bin-entrypoint.test.ts +99 -0
  267. package/src/server/tests/cli/social.test.ts +83 -0
  268. package/src/server/tests/cron/social-inbound-followers.unit.test.ts +164 -0
  269. package/src/server/tests/cron/social-inbound-staleness.test.ts +193 -0
  270. package/src/server/tests/cron/social-outbound.test.ts +252 -0
  271. package/src/server/tests/endpoints/agent.test.ts +0 -16
  272. package/src/server/tests/lib/credential-field-schema.test.ts +448 -0
  273. package/src/server/tests/lib/project-scope.test.ts +49 -0
  274. package/src/server/tests/lib/social/create.test.ts +207 -0
  275. package/src/server/tests/lib/social/sign.test.ts +163 -0
  276. package/src/server/tests/lib/social/sync.test.ts +198 -0
  277. package/src/server/tests/mcp/server.test.ts +1 -3
  278. package/src/server/tests/mcp/tools.test.ts +219 -7
  279. package/src/server/tsconfig.tsbuildinfo +1 -1
  280. package/tsconfig.json +1 -0
  281. package/.next/server/chunks/2460.js +0 -7
  282. package/.next/server/chunks/5246.js +0 -1
  283. package/.next/static/chunks/3667-1db7bd03948e60df.js +0 -1
  284. package/.next/static/chunks/5442-b5bb869e832e8967.js +0 -1
  285. package/.next/static/chunks/6872-a8f1ad5fc35a1c35.js +0 -1
  286. package/.next/static/chunks/7394-4bdb1feefad1a74a.js +0 -1
  287. package/.next/static/chunks/7587-8542eae6e5139d27.js +0 -1
  288. package/.next/static/chunks/app/app-legacy-do-not-use/page-569fed5d1a2bfe79.js +0 -1
  289. package/.next/static/chunks/app/layout-af8d9969c7aeb758.js +0 -1
  290. package/.next/static/chunks/app/page-9bd0ec3ba4aa6cea.js +0 -1
  291. package/.next/static/css/88f92e1578b1e5d9.css +0 -3
  292. package/shared/agent-profile-schema.ts +0 -81
  293. package/src/internal/security/frontend-api-authorization-audit.md +0 -206
  294. package/src/server/lib/agent-profile-records.ts +0 -72
  295. /package/.next/static/{QPzVnl7GToqgYBq14V_iC → yEruVJH_cuht5hu5_1R00}/_buildManifest.js +0 -0
  296. /package/.next/static/{QPzVnl7GToqgYBq14V_iC → yEruVJH_cuht5hu5_1R00}/_ssgManifest.js +0 -0
@@ -0,0 +1,123 @@
1
+ 'use client';
2
+
3
+ import { useState, useEffect, useCallback, useMemo, useRef } from 'react';
4
+ import { useWebSocket } from '@/context/WebSocketContext';
5
+ import { WALLET_EVENTS, type WalletEvent, type NotificationCreatedData } from '@/lib/events';
6
+ import { api, Api } from '@/lib/api';
7
+
8
+ export interface SocialNotification {
9
+ id: string;
10
+ type: string;
11
+ category: string;
12
+ title: string;
13
+ message: string;
14
+ read: boolean;
15
+ dismissed: boolean;
16
+ metadata: string | null;
17
+ hash: string | null;
18
+ createdAt: string;
19
+ agentId: string | null;
20
+ }
21
+
22
+ interface SocialNotificationMeta {
23
+ socialType?: 'reaction' | 'reply' | 'follow';
24
+ actorAuraId?: number;
25
+ actorPublicKey?: string;
26
+ eventHash?: string;
27
+ }
28
+
29
+ interface FetchResponse {
30
+ success: boolean;
31
+ notifications: SocialNotification[];
32
+ total: number;
33
+ }
34
+
35
+ interface UpdateResponse {
36
+ success: boolean;
37
+ updated: number;
38
+ }
39
+
40
+ export function parseSocialMeta(raw: string | null): SocialNotificationMeta {
41
+ if (!raw) return {};
42
+ try {
43
+ return JSON.parse(raw) as SocialNotificationMeta;
44
+ } catch {
45
+ return {};
46
+ }
47
+ }
48
+
49
+ export function useSocialNotifications(agentId: string | null) {
50
+ const [notifications, setNotifications] = useState<SocialNotification[]>([]);
51
+ const [loading, setLoading] = useState(false);
52
+ const mountedRef = useRef(true);
53
+ const { subscribe, connected } = useWebSocket();
54
+
55
+ const fetchNotifications = useCallback(async () => {
56
+ setLoading(true);
57
+ try {
58
+ const qs = agentId ? `agentId=${encodeURIComponent(agentId)}&limit=50` : 'limit=50';
59
+ const data = await api.get<FetchResponse>(Api.Wallet, `/social/notifications?${qs}`);
60
+ if (mountedRef.current && data.success) {
61
+ setNotifications(data.notifications);
62
+ }
63
+ } catch {
64
+ // Silently ignore - social may not be enabled
65
+ } finally {
66
+ if (mountedRef.current) setLoading(false);
67
+ }
68
+ }, [agentId]);
69
+
70
+ // Initial fetch
71
+ useEffect(() => {
72
+ mountedRef.current = true;
73
+ fetchNotifications();
74
+ return () => { mountedRef.current = false; };
75
+ }, [agentId, fetchNotifications]);
76
+
77
+ // Refetch on reconnect
78
+ useEffect(() => {
79
+ if (connected) fetchNotifications();
80
+ }, [connected, agentId, fetchNotifications]);
81
+
82
+ // Listen for real-time notification:created events
83
+ useEffect(() => {
84
+ const unsub = subscribe(WALLET_EVENTS.NOTIFICATION_CREATED, (event) => {
85
+ const data = (event as WalletEvent).data as NotificationCreatedData;
86
+ if (data.category !== 'social') return;
87
+ if (agentId && data.agentId !== agentId) return;
88
+ // Refetch to get full notification data
89
+ fetchNotifications();
90
+ });
91
+ return unsub;
92
+ }, [subscribe, agentId, fetchNotifications]);
93
+
94
+ const unreadCount = useMemo(
95
+ () => notifications.filter((n) => !n.read && !n.dismissed).length,
96
+ [notifications],
97
+ );
98
+
99
+ const markRead = useCallback(async (ids: string[]) => {
100
+ if (ids.length === 0) return;
101
+ setNotifications((prev) => prev.map((n) => ids.includes(n.id) ? { ...n, read: true } : n));
102
+ try {
103
+ await api.post<UpdateResponse>(Api.Wallet, '/social/notifications/read', { ids });
104
+ } catch { /* best-effort */ }
105
+ }, []);
106
+
107
+ const dismiss = useCallback(async (ids: string[]) => {
108
+ if (ids.length === 0) return;
109
+ setNotifications((prev) => prev.map((n) => ids.includes(n.id) ? { ...n, dismissed: true, read: true } : n));
110
+ try {
111
+ await api.post<UpdateResponse>(Api.Wallet, '/social/notifications/dismiss', { ids });
112
+ } catch { /* best-effort */ }
113
+ }, []);
114
+
115
+ return {
116
+ notifications,
117
+ unreadCount,
118
+ loading,
119
+ markRead,
120
+ dismiss,
121
+ refresh: fetchNotifications,
122
+ };
123
+ }
package/src/lib/events.ts CHANGED
@@ -216,6 +216,13 @@ export interface CredentialAccessedData {
216
216
  tokenHash?: string;
217
217
  }
218
218
 
219
+ export interface NotificationCreatedData {
220
+ agentId: string;
221
+ notificationId: string;
222
+ category: string;
223
+ title: string;
224
+ }
225
+
219
226
  export interface BalanceUpdatedData {
220
227
  chain: string;
221
228
  type: 'native' | 'token';
@@ -234,7 +241,8 @@ export type WalletEventData =
234
241
  | ActionCreatedData
235
242
  | ActionResolvedData
236
243
  | CredentialChangedData
237
- | CredentialAccessedData;
244
+ | CredentialAccessedData
245
+ | NotificationCreatedData;
238
246
 
239
247
  // Event types as const for type safety
240
248
  export const WALLET_EVENTS = {
@@ -251,6 +259,7 @@ export const WALLET_EVENTS = {
251
259
  CREDENTIAL_CHANGED: 'credential:changed',
252
260
  CREDENTIAL_ACCESSED: 'credential:accessed',
253
261
  SECRET_ACCESSED: 'secret:accessed',
262
+ NOTIFICATION_CREATED: 'notification:created',
254
263
  } as const;
255
264
 
256
265
  export type WalletEventType = (typeof WALLET_EVENTS)[keyof typeof WALLET_EVENTS];
@@ -0,0 +1,319 @@
1
+ import { api, Api } from './api';
2
+
3
+ export interface SocialAgentInfo {
4
+ id: string;
5
+ name?: string;
6
+ isPrimary: boolean;
7
+ parentAgentId?: string;
8
+ }
9
+
10
+ export interface SocialMessageItem {
11
+ id: string;
12
+ agentId: string;
13
+ hash: string;
14
+ type: string;
15
+ body: string;
16
+ timestamp: number;
17
+ syncStatus?: string;
18
+ createdAt?: string;
19
+ }
20
+
21
+ export interface InboundMessageItem {
22
+ id: string;
23
+ agentId: string;
24
+ hash: string;
25
+ authorAuraId: number;
26
+ type: string;
27
+ body: string;
28
+ timestamp: number;
29
+ }
30
+
31
+ export interface FollowerItem {
32
+ id: string;
33
+ authorAuraId: number;
34
+ timestamp: number;
35
+ body: string;
36
+ }
37
+
38
+ export interface FollowingItem {
39
+ auraId: number;
40
+ timestamp: number;
41
+ }
42
+
43
+ export interface SyncStatus {
44
+ pending: number;
45
+ accepted: number;
46
+ rejected: number;
47
+ failed: number;
48
+ total: number;
49
+ }
50
+
51
+ export interface AgentProfile {
52
+ pfp: string;
53
+ handle: string;
54
+ displayName: string;
55
+ pfpIsSprite?: boolean;
56
+ }
57
+
58
+ export interface HubSubscriptionInfo {
59
+ id: string;
60
+ agentId: string;
61
+ hubUrl: string;
62
+ frontendUrl: string | null;
63
+ label: string | null;
64
+ auraId: number | null;
65
+ inboundSeq: number | null;
66
+ inboundMode: string | null;
67
+ joinedAt: string;
68
+ }
69
+
70
+ interface ProfileMessageItem {
71
+ body: string;
72
+ createdAt?: string;
73
+ timestamp?: number;
74
+ }
75
+
76
+ const DEFAULT_AGENT_AVATAR = 'https://auramaxx.sh/agent7.png';
77
+
78
+ function parseLatestProfile(messages: ProfileMessageItem[]): AgentProfile {
79
+ const latest: Record<string, { value: string; rank: number }> = {};
80
+ for (const msg of messages) {
81
+ try {
82
+ const parsed = JSON.parse(msg.body) as { type?: string; value?: string };
83
+ if (!parsed.type) continue;
84
+ const rank = msg.createdAt
85
+ ? Date.parse(msg.createdAt)
86
+ : typeof msg.timestamp === 'number'
87
+ ? msg.timestamp * 1000
88
+ : 0;
89
+ const existing = latest[parsed.type];
90
+ if (!existing || rank >= existing.rank) {
91
+ latest[parsed.type] = { value: parsed.value ?? '', rank };
92
+ }
93
+ } catch {
94
+ // ignore malformed profile rows
95
+ }
96
+ }
97
+
98
+ const pfpVal = latest.pfp?.value || DEFAULT_AGENT_AVATAR;
99
+ return {
100
+ pfp: pfpVal,
101
+ handle: latest.handle?.value || '',
102
+ displayName: latest.display_name?.value || '',
103
+ pfpIsSprite: latest.pfp_is_sprite?.value === 'true'
104
+ || (!latest.pfp_is_sprite && /\/agent\d+\.png$/.test(pfpVal)),
105
+ };
106
+ }
107
+
108
+ function tryParseBody(body: string): Record<string, unknown> {
109
+ try {
110
+ return JSON.parse(body) as Record<string, unknown>;
111
+ } catch {
112
+ return {};
113
+ }
114
+ }
115
+
116
+ function shouldApplyPostRemove(syncStatus?: string): boolean {
117
+ return syncStatus !== 'failed' && syncStatus !== 'rejected';
118
+ }
119
+
120
+ function extractRemovedPostHash(message: SocialMessageItem): string | null {
121
+ const parsed = tryParseBody(message.body);
122
+ const raw = parsed.targetPostHash ?? parsed.postHash ?? parsed.targetHash;
123
+ if (typeof raw !== 'string') return null;
124
+ const trimmed = raw.trim();
125
+ return trimmed ? trimmed : null;
126
+ }
127
+
128
+ export async function fetchSocialAgents(): Promise<SocialAgentInfo[]> {
129
+ const res = await api.get<{ agents: SocialAgentInfo[] }>(Api.Wallet, '/agents/credential');
130
+ return res.agents || [];
131
+ }
132
+
133
+ export interface GlobalFeedAuthorProfile {
134
+ handle: string | null;
135
+ displayName: string | null;
136
+ pfp: string | null;
137
+ pfpIsSprite: boolean;
138
+ }
139
+
140
+ export interface GlobalFeedItem {
141
+ postHash: string;
142
+ authorAuraId: number;
143
+ text: string;
144
+ updatedTs: number;
145
+ authorScore: number;
146
+ authorProfile: GlobalFeedAuthorProfile | null;
147
+ }
148
+
149
+ export async function fetchGlobalFeed(cursor?: string, hubUrl?: string): Promise<{ items: GlobalFeedItem[]; nextCursor: string | null }> {
150
+ const params: Record<string, string | number> = { limit: 50 };
151
+ if (cursor) params.cursor = cursor;
152
+ if (hubUrl) params.hubUrl = hubUrl;
153
+ const res = await api.get<{ ok: boolean; items: GlobalFeedItem[]; nextCursor: string | null }>(
154
+ Api.Wallet,
155
+ '/social/global-feed',
156
+ params,
157
+ );
158
+ return { items: res.items || [], nextCursor: res.nextCursor ?? null };
159
+ }
160
+
161
+ export async function fetchSocialFeed(agentId: string, hubUrl?: string): Promise<InboundMessageItem[]> {
162
+ const params: Record<string, string | number> = { agentId, type: 'post_add', limit: 50 };
163
+ if (hubUrl) params.hubUrl = hubUrl;
164
+
165
+ const [feedRes, ownPosts] = await Promise.all([
166
+ api.get<{ messages: InboundMessageItem[] }>(
167
+ Api.Wallet,
168
+ '/social/feed',
169
+ params,
170
+ ),
171
+ fetchSocialPosts(agentId, hubUrl),
172
+ ]);
173
+
174
+ const byHash = new Map<string, InboundMessageItem>();
175
+ for (const message of feedRes.messages || []) {
176
+ byHash.set(message.hash, message);
177
+ }
178
+ for (const post of ownPosts) {
179
+ if (byHash.has(post.hash)) continue;
180
+ byHash.set(post.hash, {
181
+ id: post.id,
182
+ agentId: post.agentId,
183
+ hash: post.hash,
184
+ authorAuraId: 0,
185
+ type: post.type,
186
+ body: post.body,
187
+ timestamp: post.timestamp,
188
+ });
189
+ }
190
+
191
+ return Array.from(byHash.values()).sort((a, b) => b.timestamp - a.timestamp);
192
+ }
193
+
194
+ export async function fetchSocialPosts(agentId: string, hubUrl?: string): Promise<SocialMessageItem[]> {
195
+ const params: Record<string, string | number> = { agentId, type: 'post_add', limit: 200 };
196
+ if (hubUrl) params.hubUrl = hubUrl;
197
+ const removeParams: Record<string, string | number> = { agentId, type: 'post_remove', limit: 200 };
198
+ if (hubUrl) removeParams.hubUrl = hubUrl;
199
+
200
+ const [addsRes, removesRes] = await Promise.all([
201
+ api.get<{ messages: SocialMessageItem[] }>(Api.Wallet, '/social/messages', params),
202
+ api.get<{ messages: SocialMessageItem[] }>(Api.Wallet, '/social/messages', removeParams),
203
+ ]);
204
+
205
+ const posts = addsRes.messages || [];
206
+ const removals = removesRes.messages || [];
207
+ if (removals.length === 0) return posts;
208
+
209
+ const removedHashes = new Set<string>();
210
+ for (const removeMessage of removals) {
211
+ if (!shouldApplyPostRemove(removeMessage.syncStatus)) continue;
212
+ const targetHash = extractRemovedPostHash(removeMessage);
213
+ if (targetHash) removedHashes.add(targetHash);
214
+ }
215
+
216
+ if (removedHashes.size === 0) return posts;
217
+ return posts.filter((post) => !removedHashes.has(post.hash));
218
+ }
219
+
220
+ export async function fetchSocialFollowers(agentId: string, hubUrl?: string): Promise<FollowerItem[]> {
221
+ const params: Record<string, string> = { agentId };
222
+ if (hubUrl) params.hubUrl = hubUrl;
223
+ const res = await api.get<{ followers: FollowerItem[] }>(Api.Wallet, '/social/followers', params);
224
+ return res.followers || [];
225
+ }
226
+
227
+ export async function fetchSocialFollowing(agentId: string, hubUrl?: string): Promise<FollowingItem[]> {
228
+ const params: Record<string, string> = { agentId };
229
+ if (hubUrl) params.hubUrl = hubUrl;
230
+ const res = await api.get<{ following: FollowingItem[] }>(Api.Wallet, '/social/following', params);
231
+ return res.following || [];
232
+ }
233
+
234
+ export async function fetchSocialStatus(agentId: string, hubUrl?: string): Promise<SyncStatus | null> {
235
+ const params: Record<string, string> = { agentId };
236
+ if (hubUrl) params.hubUrl = hubUrl;
237
+ const res = await api.get<{ status: SyncStatus | null }>(Api.Wallet, '/social/status', params);
238
+ return res.status || null;
239
+ }
240
+
241
+ export async function fetchSocialProfile(agentId: string): Promise<AgentProfile> {
242
+ const res = await api.get<{ messages: ProfileMessageItem[] }>(
243
+ Api.Wallet,
244
+ '/social/messages',
245
+ { agentId, type: 'user_data_add', limit: 200 },
246
+ );
247
+ return parseLatestProfile(res.messages || []);
248
+ }
249
+
250
+ export function buildLocalPendingPost(agentId: string, text: string): SocialMessageItem {
251
+ const nowMs = Date.now();
252
+ const nowIso = new Date(nowMs).toISOString();
253
+ const localId = `local:${agentId}:${nowMs}:${Math.random().toString(16).slice(2, 8)}`;
254
+ return {
255
+ id: localId,
256
+ agentId,
257
+ hash: localId,
258
+ type: 'post_add',
259
+ body: JSON.stringify({ text }),
260
+ timestamp: Math.floor(nowMs / 1000),
261
+ syncStatus: 'pending',
262
+ createdAt: nowIso,
263
+ };
264
+ }
265
+
266
+ export function submitPostLocalFirst(agentId: string, text: string, hubUrl?: string): {
267
+ localMessage: SocialMessageItem;
268
+ submit: Promise<SocialMessageItem>;
269
+ } {
270
+ const normalizedText = text.trim();
271
+ if (!normalizedText) {
272
+ throw new Error('text is required and must be a non-empty string');
273
+ }
274
+ const localMessage = buildLocalPendingPost(agentId, normalizedText);
275
+ const postBody: Record<string, string> = { agentId, text: normalizedText };
276
+ if (hubUrl) postBody.hubUrl = hubUrl;
277
+
278
+ const submit = api
279
+ .post<{ success: boolean; message: SocialMessageItem }>(Api.Wallet, '/social/post', postBody)
280
+ .then((res) => res.message);
281
+
282
+ return { localMessage, submit };
283
+ }
284
+
285
+ export async function removeSocialPost(agentId: string, targetPostHash: string, hubUrl?: string): Promise<void> {
286
+ const body: Record<string, string> = { agentId, targetPostHash };
287
+ if (hubUrl) body.hubUrl = hubUrl;
288
+ await api.post(Api.Wallet, '/social/post/remove', body);
289
+ }
290
+
291
+ export async function likeSocialPost(agentId: string, postHash: string, hubUrl?: string): Promise<void> {
292
+ const body: Record<string, string> = { agentId, postHash, reactionType: 'like' };
293
+ if (hubUrl) body.hubUrl = hubUrl;
294
+ await api.post(Api.Wallet, '/social/react', body);
295
+ }
296
+
297
+ // ─── Hub management ──────────────────────────────────────────────────
298
+
299
+ export async function fetchPrimaryHubUrl(): Promise<string> {
300
+ const res = await api.get<{ hubUrl?: string }>(Api.Wallet, '/agent-hub/default');
301
+ const hubUrl = typeof res.hubUrl === 'string' ? res.hubUrl.trim() : '';
302
+ return hubUrl;
303
+ }
304
+
305
+ export async function fetchHubSubscriptions(agentId: string): Promise<HubSubscriptionInfo[]> {
306
+ const res = await api.get<{ hubs: HubSubscriptionInfo[] }>(Api.Wallet, `/agent-hub/${agentId}/hubs`);
307
+ return res.hubs || [];
308
+ }
309
+
310
+ export async function joinHub(agentId: string, hubUrl: string, label?: string): Promise<HubSubscriptionInfo> {
311
+ const body: Record<string, string> = { hubUrl };
312
+ if (label) body.label = label;
313
+ const res = await api.post<{ hub: HubSubscriptionInfo }>(Api.Wallet, `/agent-hub/${agentId}/join`, body);
314
+ return res.hub;
315
+ }
316
+
317
+ export async function leaveHub(agentId: string, hubUrl: string): Promise<void> {
318
+ await api.post(Api.Wallet, `/agent-hub/${agentId}/leave`, { hubUrl });
319
+ }
@@ -22,14 +22,32 @@ export const DEFAULT_VIEWS: ViewDefinition[] = [
22
22
  pinned: true,
23
23
  },
24
24
  {
25
- id: 'wallet',
26
- label: 'Wallet',
27
- icon: 'Flame',
28
- description: 'Wallet balances, send, swap, and fund',
25
+ id: 'agents',
26
+ label: 'Verify',
27
+ icon: 'ShieldCheck',
28
+ description: 'Verified credentials and identity attestations',
29
29
  pinned: true,
30
30
  },
31
31
  ];
32
32
 
33
+ /** Social view — only shown when SOCIAL feature flag is enabled */
34
+ export const SOCIAL_VIEW: ViewDefinition = {
35
+ id: 'social',
36
+ label: 'Social',
37
+ icon: 'MessageCircle',
38
+ description: 'Social feed, posts, reactions, and follows',
39
+ pinned: true,
40
+ };
41
+
42
+ /** Hub view — renders a hub's frontend in an iframe */
43
+ export const HUB_VIEW: ViewDefinition = {
44
+ id: 'hub',
45
+ label: 'Hub',
46
+ icon: 'Globe',
47
+ description: 'Browse hub frontends',
48
+ pinned: true,
49
+ };
50
+
33
51
  /** Views that can be created from the + button */
34
52
  export const CREATABLE_VIEWS: ViewDefinition[] = [
35
53
  {
@@ -29,6 +29,8 @@ import {
29
29
  normalizeCredentialFieldsForType,
30
30
  getCredentialFieldValue,
31
31
  NOTE_CONTENT_KEY,
32
+ CREDENTIAL_FIELD_SCHEMA,
33
+ type CredentialType,
32
34
  } from '../../../../shared/credential-field-schema';
33
35
  import {
34
36
  createSecretGist,
@@ -718,8 +720,7 @@ export function parseArgs(args: string[]): {
718
720
  const flagTotp = parseableArgs.includes('--totp');
719
721
  const flagOneTime = parseableArgs.includes('--one-time');
720
722
  const flagDangerPlaintext = parseableArgs.includes('--danger-plaintext');
721
- const fieldIdx = parseableArgs.indexOf('--field');
722
- const fieldName = fieldIdx !== -1 ? parseableArgs[fieldIdx + 1] : undefined;
723
+ let fieldName: string | undefined;
723
724
  const secretEnvIdx = parseableArgs.indexOf('--env');
724
725
  const legacySecretEnvIdx = parseableArgs.indexOf('--name');
725
726
  const secretEnvName = secretEnvIdx !== -1
@@ -752,7 +753,7 @@ export function parseArgs(args: string[]): {
752
753
  : undefined;
753
754
 
754
755
  const knownValueFlags = new Set([
755
- '--field', '--name', '--env', '--agent', '--expires-after', '--password', '--location', '--reqId', '--req-id', '--requestId', '--request-id', '--profile', '--profile-version', '--profile-overrides', '--type', '--tags',
756
+ '--name', '--env', '--agent', '--expires-after', '--password', '--location', '--reqId', '--req-id', '--requestId', '--request-id', '--profile', '--profile-version', '--profile-overrides', '--type', '--tags',
756
757
  ]);
757
758
  const knownBooleanFlags = new Set(['--json', '--first', '--totp', '--one-time', '--danger-plaintext']);
758
759
 
@@ -766,6 +767,21 @@ export function parseArgs(args: string[]): {
766
767
  continue;
767
768
  }
768
769
 
770
+ // Handle --field specially: supports --field key=value and --field key
771
+ if (arg === '--field') {
772
+ const nextArg = parseableArgs[i + 1];
773
+ if (nextArg && !nextArg.startsWith('--')) {
774
+ const eqIdx = nextArg.indexOf('=');
775
+ if (eqIdx > 0) {
776
+ extraFields.push({ key: nextArg.slice(0, eqIdx), value: nextArg.slice(eqIdx + 1) });
777
+ } else {
778
+ fieldName = fieldName || nextArg;
779
+ }
780
+ i++;
781
+ }
782
+ continue;
783
+ }
784
+
769
785
  if (knownValueFlags.has(arg)) {
770
786
  i++;
771
787
  continue;
@@ -1050,6 +1066,14 @@ function resolveSetType(typeName: string | undefined): { requestType: string; no
1050
1066
  return { requestType, normalizedType, defaultFieldKey };
1051
1067
  }
1052
1068
 
1069
+ function resolveFieldSensitivity(credentialType: string, fieldKey: string): boolean {
1070
+ const schema = CREDENTIAL_FIELD_SCHEMA[credentialType as CredentialType];
1071
+ if (!schema) return false;
1072
+ const spec = schema.find((f) => f.key.toLowerCase() === fieldKey.toLowerCase()
1073
+ || (f.aliases || []).some((a) => a.toLowerCase() === fieldKey.toLowerCase()));
1074
+ return spec ? spec.sensitive : false;
1075
+ }
1076
+
1053
1077
  // ── Output formatting ──────────────────────────────────────────────────
1054
1078
 
1055
1079
  export function formatCredential(
@@ -1456,7 +1480,7 @@ export async function runAgentCli(args: string[]): Promise<number> {
1456
1480
 
1457
1481
  const setFields: Array<{ key: string; value: string; sensitive?: boolean }> = [
1458
1482
  { key: fieldKey, value, sensitive: true },
1459
- ...extraFields.map((f) => ({ key: f.key, value: f.value, sensitive: true })),
1483
+ ...extraFields.map((f) => ({ key: f.key, value: f.value, sensitive: resolveFieldSensitivity(resolvedType.requestType, f.key) })),
1460
1484
  ];
1461
1485
  const nextMeta = tags ? { tags } : undefined;
1462
1486
 
@@ -1487,13 +1511,15 @@ export async function runAgentCli(args: string[]): Promise<number> {
1487
1511
  retryCommandTemplate,
1488
1512
  originalCommand,
1489
1513
  });
1514
+ const effectiveType = decrypted.type || resolvedType.requestType;
1490
1515
  const existing = [...decrypted.fields];
1491
1516
  for (const next of setFields) {
1492
1517
  const index = existing.findIndex((f) => f.key.toLowerCase() === next.key.toLowerCase());
1518
+ const sensitive = next.sensitive ?? resolveFieldSensitivity(effectiveType, next.key);
1493
1519
  if (index >= 0) {
1494
- existing[index] = { ...existing[index], key: next.key, value: next.value, sensitive: true };
1520
+ existing[index] = { ...existing[index], key: next.key, value: next.value, sensitive };
1495
1521
  } else {
1496
- existing.push({ key: next.key, value: next.value, sensitive: true });
1522
+ existing.push({ key: next.key, value: next.value, sensitive });
1497
1523
  }
1498
1524
  }
1499
1525
  nextFields = existing;
@@ -1505,7 +1531,7 @@ export async function runAgentCli(args: string[]): Promise<number> {
1505
1531
  }
1506
1532
 
1507
1533
  const updated = await updateCredential(token, target.id, {
1508
- sensitiveFields: nextFields.map((f) => ({ key: f.key, value: f.value, sensitive: true })),
1534
+ sensitiveFields: nextFields.map((f) => ({ key: f.key, value: f.value, sensitive: f.sensitive ?? false })),
1509
1535
  ...(nextMeta ? { meta: nextMeta } : {}),
1510
1536
  });
1511
1537