@elizaos/agent 2.0.0-alpha.385 → 2.0.0-alpha.393

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (377) hide show
  1. package/apps/app-companion/src/components/companion/CompanionAppView.d.ts.map +1 -1
  2. package/apps/app-companion/src/components/companion/CompanionAppView.js +6 -4
  3. package/apps/app-companion/src/components/companion/CompanionView.d.ts.map +1 -1
  4. package/apps/app-companion/src/components/companion/CompanionView.js +6 -4
  5. package/apps/app-knowledge/src/routes.d.ts +1 -1
  6. package/apps/app-knowledge/src/routes.d.ts.map +1 -1
  7. package/apps/app-knowledge/src/routes.js +1 -1
  8. package/apps/app-lifeops/src/api/client-lifeops.js +6 -0
  9. package/apps/app-lifeops/src/components/AppBlockerSettingsCard.d.ts +3 -0
  10. package/apps/app-lifeops/src/components/AppBlockerSettingsCard.d.ts.map +1 -0
  11. package/apps/app-lifeops/src/components/AppBlockerSettingsCard.js +189 -0
  12. package/apps/app-lifeops/src/components/BrowserBridgeSetupPanel.d.ts +2 -0
  13. package/apps/app-lifeops/src/components/BrowserBridgeSetupPanel.d.ts.map +1 -0
  14. package/apps/app-lifeops/src/components/BrowserBridgeSetupPanel.js +1065 -0
  15. package/apps/app-lifeops/src/components/BrowserBridgeStatusChip.d.ts +7 -0
  16. package/apps/app-lifeops/src/components/BrowserBridgeStatusChip.d.ts.map +1 -0
  17. package/apps/app-lifeops/src/components/BrowserBridgeStatusChip.js +71 -0
  18. package/apps/app-lifeops/src/components/DataSourcesStrip.d.ts +14 -0
  19. package/apps/app-lifeops/src/components/DataSourcesStrip.d.ts.map +1 -0
  20. package/apps/app-lifeops/src/components/DataSourcesStrip.js +36 -0
  21. package/apps/app-lifeops/src/components/EventEditorDrawer.d.ts +23 -0
  22. package/apps/app-lifeops/src/components/EventEditorDrawer.d.ts.map +1 -0
  23. package/apps/app-lifeops/src/components/EventEditorDrawer.js +514 -0
  24. package/apps/app-lifeops/src/components/LifeOpsActivitySignalsEffect.d.ts +2 -0
  25. package/apps/app-lifeops/src/components/LifeOpsActivitySignalsEffect.d.ts.map +1 -0
  26. package/apps/app-lifeops/src/components/LifeOpsActivitySignalsEffect.js +10 -0
  27. package/apps/app-lifeops/src/components/LifeOpsCalendarSection.d.ts +15 -0
  28. package/apps/app-lifeops/src/components/LifeOpsCalendarSection.d.ts.map +1 -0
  29. package/apps/app-lifeops/src/components/LifeOpsCalendarSection.js +515 -0
  30. package/apps/app-lifeops/src/components/LifeOpsChatAdapter.d.ts +34 -0
  31. package/apps/app-lifeops/src/components/LifeOpsChatAdapter.d.ts.map +1 -0
  32. package/apps/app-lifeops/src/components/LifeOpsChatAdapter.js +175 -0
  33. package/apps/app-lifeops/src/components/LifeOpsFeatureTogglesSection.d.ts +2 -0
  34. package/apps/app-lifeops/src/components/LifeOpsFeatureTogglesSection.d.ts.map +1 -0
  35. package/apps/app-lifeops/src/components/LifeOpsFeatureTogglesSection.js +99 -0
  36. package/apps/app-lifeops/src/components/LifeOpsHabitVisuals.d.ts +37 -0
  37. package/apps/app-lifeops/src/components/LifeOpsHabitVisuals.d.ts.map +1 -0
  38. package/apps/app-lifeops/src/components/LifeOpsHabitVisuals.js +88 -0
  39. package/apps/app-lifeops/src/components/LifeOpsInboxSection.d.ts +13 -0
  40. package/apps/app-lifeops/src/components/LifeOpsInboxSection.d.ts.map +1 -0
  41. package/apps/app-lifeops/src/components/LifeOpsInboxSection.js +490 -0
  42. package/apps/app-lifeops/src/components/LifeOpsLinkBankButton.d.ts +12 -0
  43. package/apps/app-lifeops/src/components/LifeOpsLinkBankButton.d.ts.map +1 -0
  44. package/apps/app-lifeops/src/components/LifeOpsLinkBankButton.js +92 -0
  45. package/apps/app-lifeops/src/components/LifeOpsLinkPaypalButton.d.ts +12 -0
  46. package/apps/app-lifeops/src/components/LifeOpsLinkPaypalButton.d.ts.map +1 -0
  47. package/apps/app-lifeops/src/components/LifeOpsLinkPaypalButton.js +160 -0
  48. package/apps/app-lifeops/src/components/LifeOpsMoneySection.d.ts +3 -0
  49. package/apps/app-lifeops/src/components/LifeOpsMoneySection.d.ts.map +1 -0
  50. package/apps/app-lifeops/src/components/LifeOpsMoneySection.js +373 -0
  51. package/apps/app-lifeops/src/components/LifeOpsNavRail.d.ts +9 -0
  52. package/apps/app-lifeops/src/components/LifeOpsNavRail.d.ts.map +1 -0
  53. package/apps/app-lifeops/src/components/LifeOpsNavRail.js +93 -0
  54. package/apps/app-lifeops/src/components/LifeOpsOperationalPanels.d.ts +4 -0
  55. package/apps/app-lifeops/src/components/LifeOpsOperationalPanels.d.ts.map +1 -0
  56. package/apps/app-lifeops/src/components/LifeOpsOperationalPanels.js +274 -0
  57. package/apps/app-lifeops/src/components/LifeOpsOverviewSection.d.ts +7 -0
  58. package/apps/app-lifeops/src/components/LifeOpsOverviewSection.d.ts.map +1 -0
  59. package/apps/app-lifeops/src/components/LifeOpsOverviewSection.js +657 -0
  60. package/apps/app-lifeops/src/components/LifeOpsPageSections.d.ts +42 -0
  61. package/apps/app-lifeops/src/components/LifeOpsPageSections.d.ts.map +1 -0
  62. package/apps/app-lifeops/src/components/LifeOpsPageSections.js +166 -0
  63. package/apps/app-lifeops/src/components/LifeOpsPageView.d.ts +2 -0
  64. package/apps/app-lifeops/src/components/LifeOpsPageView.d.ts.map +1 -0
  65. package/apps/app-lifeops/src/components/LifeOpsPageView.js +566 -0
  66. package/apps/app-lifeops/src/components/LifeOpsRemindersSection.d.ts +2 -0
  67. package/apps/app-lifeops/src/components/LifeOpsRemindersSection.d.ts.map +1 -0
  68. package/apps/app-lifeops/src/components/LifeOpsRemindersSection.js +749 -0
  69. package/apps/app-lifeops/src/components/LifeOpsResizableSidebar.d.ts +27 -0
  70. package/apps/app-lifeops/src/components/LifeOpsResizableSidebar.d.ts.map +1 -0
  71. package/apps/app-lifeops/src/components/LifeOpsResizableSidebar.js +106 -0
  72. package/apps/app-lifeops/src/components/LifeOpsScreenTimeSection.d.ts +2 -0
  73. package/apps/app-lifeops/src/components/LifeOpsScreenTimeSection.d.ts.map +1 -0
  74. package/apps/app-lifeops/src/components/LifeOpsScreenTimeSection.js +373 -0
  75. package/apps/app-lifeops/src/components/LifeOpsSectionContent.d.ts +10 -0
  76. package/apps/app-lifeops/src/components/LifeOpsSectionContent.d.ts.map +1 -0
  77. package/apps/app-lifeops/src/components/LifeOpsSectionContent.js +30 -0
  78. package/apps/app-lifeops/src/components/LifeOpsSelectionContext.d.ts +21 -0
  79. package/apps/app-lifeops/src/components/LifeOpsSelectionContext.d.ts.map +1 -0
  80. package/apps/app-lifeops/src/components/LifeOpsSelectionContext.js +30 -0
  81. package/apps/app-lifeops/src/components/LifeOpsSettingsSection.d.ts +20 -0
  82. package/apps/app-lifeops/src/components/LifeOpsSettingsSection.d.ts.map +1 -0
  83. package/apps/app-lifeops/src/components/LifeOpsSettingsSection.js +597 -0
  84. package/apps/app-lifeops/src/components/LifeOpsSetupGate.d.ts +13 -0
  85. package/apps/app-lifeops/src/components/LifeOpsSetupGate.d.ts.map +1 -0
  86. package/apps/app-lifeops/src/components/LifeOpsSetupGate.js +134 -0
  87. package/apps/app-lifeops/src/components/LifeOpsSleepSection.d.ts +10 -0
  88. package/apps/app-lifeops/src/components/LifeOpsSleepSection.d.ts.map +1 -0
  89. package/apps/app-lifeops/src/components/LifeOpsSleepSection.js +425 -0
  90. package/apps/app-lifeops/src/components/LifeOpsWorkspaceShell.d.ts +11 -0
  91. package/apps/app-lifeops/src/components/LifeOpsWorkspaceShell.d.ts.map +1 -0
  92. package/apps/app-lifeops/src/components/LifeOpsWorkspaceShell.js +49 -0
  93. package/apps/app-lifeops/src/components/LifeOpsWorkspaceView.d.ts +2 -0
  94. package/apps/app-lifeops/src/components/LifeOpsWorkspaceView.d.ts.map +1 -0
  95. package/apps/app-lifeops/src/components/LifeOpsWorkspaceView.js +1041 -0
  96. package/apps/app-lifeops/src/components/MessagingConnectorCards.d.ts +7 -0
  97. package/apps/app-lifeops/src/components/MessagingConnectorCards.d.ts.map +1 -0
  98. package/apps/app-lifeops/src/components/MessagingConnectorCards.js +643 -0
  99. package/apps/app-lifeops/src/components/MissingSourceCard.d.ts +9 -0
  100. package/apps/app-lifeops/src/components/MissingSourceCard.d.ts.map +1 -0
  101. package/apps/app-lifeops/src/components/MissingSourceCard.js +5 -0
  102. package/apps/app-lifeops/src/components/MobileSignalsSetupCard.d.ts +2 -0
  103. package/apps/app-lifeops/src/components/MobileSignalsSetupCard.d.ts.map +1 -0
  104. package/apps/app-lifeops/src/components/MobileSignalsSetupCard.js +163 -0
  105. package/apps/app-lifeops/src/components/SleepInspectionPanel.d.ts +8 -0
  106. package/apps/app-lifeops/src/components/SleepInspectionPanel.d.ts.map +1 -0
  107. package/apps/app-lifeops/src/components/SleepInspectionPanel.js +59 -0
  108. package/apps/app-lifeops/src/components/WebsiteBlockerSettingsCard.d.ts +3 -0
  109. package/apps/app-lifeops/src/components/WebsiteBlockerSettingsCard.d.ts.map +1 -0
  110. package/apps/app-lifeops/src/components/WebsiteBlockerSettingsCard.js +75 -0
  111. package/apps/app-lifeops/src/components/WhatsAppQrOverlay.d.ts +8 -0
  112. package/apps/app-lifeops/src/components/WhatsAppQrOverlay.d.ts.map +1 -0
  113. package/apps/app-lifeops/src/components/WhatsAppQrOverlay.js +48 -0
  114. package/apps/app-lifeops/src/components/lifeops-labels.d.ts +2 -0
  115. package/apps/app-lifeops/src/components/lifeops-labels.d.ts.map +1 -0
  116. package/apps/app-lifeops/src/components/lifeops-labels.js +5 -0
  117. package/apps/app-lifeops/src/events/index.d.ts +34 -0
  118. package/apps/app-lifeops/src/events/index.d.ts.map +1 -0
  119. package/apps/app-lifeops/src/events/index.js +23 -0
  120. package/apps/app-lifeops/src/hooks/useCalendarWeek.d.ts +28 -0
  121. package/apps/app-lifeops/src/hooks/useCalendarWeek.d.ts.map +1 -0
  122. package/apps/app-lifeops/src/hooks/useCalendarWeek.js +93 -0
  123. package/apps/app-lifeops/src/hooks/useDiscordConnector.d.ts +14 -0
  124. package/apps/app-lifeops/src/hooks/useDiscordConnector.d.ts.map +1 -0
  125. package/apps/app-lifeops/src/hooks/useDiscordConnector.js +126 -0
  126. package/apps/app-lifeops/src/hooks/useGoogleLifeOpsConnector.d.ts +29 -0
  127. package/apps/app-lifeops/src/hooks/useGoogleLifeOpsConnector.d.ts.map +1 -0
  128. package/apps/app-lifeops/src/hooks/useGoogleLifeOpsConnector.js +481 -0
  129. package/apps/app-lifeops/src/hooks/useIMessageConnector.d.ts +10 -0
  130. package/apps/app-lifeops/src/hooks/useIMessageConnector.d.ts.map +1 -0
  131. package/apps/app-lifeops/src/hooks/useIMessageConnector.js +50 -0
  132. package/apps/app-lifeops/src/hooks/useInbox.d.ts +34 -0
  133. package/apps/app-lifeops/src/hooks/useInbox.d.ts.map +1 -0
  134. package/apps/app-lifeops/src/hooks/useInbox.js +98 -0
  135. package/apps/app-lifeops/src/hooks/useLifeOpsActivitySignals.d.ts +2 -0
  136. package/apps/app-lifeops/src/hooks/useLifeOpsActivitySignals.d.ts.map +1 -0
  137. package/apps/app-lifeops/src/hooks/useLifeOpsActivitySignals.js +383 -0
  138. package/apps/app-lifeops/src/hooks/useLifeOpsAppState.d.ts +9 -0
  139. package/apps/app-lifeops/src/hooks/useLifeOpsAppState.d.ts.map +1 -0
  140. package/apps/app-lifeops/src/hooks/useLifeOpsAppState.js +83 -0
  141. package/apps/app-lifeops/src/hooks/useLifeOpsCapabilitiesStatus.d.ts +8 -0
  142. package/apps/app-lifeops/src/hooks/useLifeOpsCapabilitiesStatus.d.ts.map +1 -0
  143. package/apps/app-lifeops/src/hooks/useLifeOpsCapabilitiesStatus.js +60 -0
  144. package/apps/app-lifeops/src/hooks/useLifeOpsScheduleInspection.d.ts +18 -0
  145. package/apps/app-lifeops/src/hooks/useLifeOpsScheduleInspection.d.ts.map +1 -0
  146. package/apps/app-lifeops/src/hooks/useLifeOpsScheduleInspection.js +66 -0
  147. package/apps/app-lifeops/src/hooks/useLifeOpsScheduleState.d.ts +14 -0
  148. package/apps/app-lifeops/src/hooks/useLifeOpsScheduleState.d.ts.map +1 -0
  149. package/apps/app-lifeops/src/hooks/useLifeOpsScheduleState.js +79 -0
  150. package/apps/app-lifeops/src/hooks/useLifeOpsSection.d.ts +18 -0
  151. package/apps/app-lifeops/src/hooks/useLifeOpsSection.d.ts.map +1 -0
  152. package/apps/app-lifeops/src/hooks/useLifeOpsSection.js +78 -0
  153. package/apps/app-lifeops/src/hooks/useLifeOpsXConnector.d.ts +14 -0
  154. package/apps/app-lifeops/src/hooks/useLifeOpsXConnector.d.ts.map +1 -0
  155. package/apps/app-lifeops/src/hooks/useLifeOpsXConnector.js +160 -0
  156. package/apps/app-lifeops/src/hooks/useSignalConnector.d.ts +16 -0
  157. package/apps/app-lifeops/src/hooks/useSignalConnector.d.ts.map +1 -0
  158. package/apps/app-lifeops/src/hooks/useSignalConnector.js +198 -0
  159. package/apps/app-lifeops/src/hooks/useTelegramConnector.d.ts +21 -0
  160. package/apps/app-lifeops/src/hooks/useTelegramConnector.d.ts.map +1 -0
  161. package/apps/app-lifeops/src/hooks/useTelegramConnector.js +177 -0
  162. package/apps/app-lifeops/src/hooks/useWhatsAppConnector.d.ts +8 -0
  163. package/apps/app-lifeops/src/hooks/useWhatsAppConnector.d.ts.map +1 -0
  164. package/apps/app-lifeops/src/hooks/useWhatsAppConnector.js +58 -0
  165. package/apps/app-lifeops/src/hooks/useWhatsAppPairing.d.ts +12 -0
  166. package/apps/app-lifeops/src/hooks/useWhatsAppPairing.d.ts.map +1 -0
  167. package/apps/app-lifeops/src/hooks/useWhatsAppPairing.js +152 -0
  168. package/apps/app-lifeops/src/lifeops/discord-browser-scraper.js +1 -1
  169. package/apps/app-lifeops/src/lifeops/google-gmail.d.ts.map +1 -1
  170. package/apps/app-lifeops/src/lifeops/google-gmail.js +74 -26
  171. package/apps/app-lifeops/src/lifeops/google-managed-client.d.ts.map +1 -1
  172. package/apps/app-lifeops/src/lifeops/google-managed-client.js +4 -5
  173. package/apps/app-lifeops/src/lifeops/google-oauth.d.ts.map +1 -1
  174. package/apps/app-lifeops/src/lifeops/google-oauth.js +2 -2
  175. package/apps/app-lifeops/src/lifeops/owner-profile.d.ts.map +1 -1
  176. package/apps/app-lifeops/src/lifeops/owner-profile.js +1 -1
  177. package/apps/app-lifeops/src/lifeops/paypal-managed-client.d.ts.map +1 -1
  178. package/apps/app-lifeops/src/lifeops/paypal-managed-client.js +4 -5
  179. package/apps/app-lifeops/src/lifeops/plaid-managed-client.d.ts.map +1 -1
  180. package/apps/app-lifeops/src/lifeops/plaid-managed-client.js +4 -5
  181. package/apps/app-lifeops/src/lifeops/schedule-sync-client.js +2 -2
  182. package/apps/app-lifeops/src/lifeops/service-constants.d.ts +1 -0
  183. package/apps/app-lifeops/src/lifeops/service-constants.d.ts.map +1 -1
  184. package/apps/app-lifeops/src/lifeops/service-constants.js +1 -0
  185. package/apps/app-lifeops/src/lifeops/service-mixin-core.d.ts +1 -1
  186. package/apps/app-lifeops/src/lifeops/service-mixin-core.d.ts.map +1 -1
  187. package/apps/app-lifeops/src/lifeops/service-mixin-core.js +2 -1
  188. package/apps/app-lifeops/src/lifeops/service-mixin-imessage.js +1 -1
  189. package/apps/app-lifeops/src/lifeops/service-mixin-inbox.d.ts +3 -1
  190. package/apps/app-lifeops/src/lifeops/service-mixin-inbox.d.ts.map +1 -1
  191. package/apps/app-lifeops/src/lifeops/service-mixin-inbox.js +42 -13
  192. package/apps/app-lifeops/src/lifeops/service-mixin-reminders.d.ts.map +1 -1
  193. package/apps/app-lifeops/src/lifeops/service-mixin-reminders.js +3 -2
  194. package/apps/app-lifeops/src/lifeops/service-mixin-workflows.d.ts.map +1 -1
  195. package/apps/app-lifeops/src/lifeops/service-mixin-workflows.js +1 -1
  196. package/apps/app-lifeops/src/lifeops/service-normalize-calendar.d.ts.map +1 -1
  197. package/apps/app-lifeops/src/lifeops/service-normalize-calendar.js +3 -3
  198. package/apps/app-lifeops/src/lifeops/service-normalize-connector.d.ts +1 -1
  199. package/apps/app-lifeops/src/lifeops/service-normalize-connector.d.ts.map +1 -1
  200. package/apps/app-lifeops/src/lifeops/service-normalize-connector.js +2 -2
  201. package/apps/app-lifeops/src/lifeops/signal-auth.d.ts.map +1 -1
  202. package/apps/app-lifeops/src/lifeops/signal-auth.js +6 -3
  203. package/apps/app-lifeops/src/lifeops/telegram-auth.js +1 -1
  204. package/apps/app-lifeops/src/lifeops/travel-adapters/duffel.d.ts.map +1 -1
  205. package/apps/app-lifeops/src/lifeops/travel-adapters/duffel.js +31 -6
  206. package/apps/app-lifeops/src/lifeops/twilio.js +1 -1
  207. package/apps/app-lifeops/src/lifeops/x-poster.d.ts.map +1 -1
  208. package/apps/app-lifeops/src/lifeops/x-poster.js +13 -3
  209. package/apps/app-lifeops/src/lifeops-route.d.ts +74 -0
  210. package/apps/app-lifeops/src/lifeops-route.d.ts.map +1 -0
  211. package/apps/app-lifeops/src/lifeops-route.js +204 -0
  212. package/apps/app-lifeops/src/platform/lifeops-github.d.ts +15 -0
  213. package/apps/app-lifeops/src/platform/lifeops-github.d.ts.map +1 -0
  214. package/apps/app-lifeops/src/platform/lifeops-github.js +135 -0
  215. package/apps/app-lifeops/src/routes/cloud-features-routes.d.ts +2 -2
  216. package/apps/app-lifeops/src/routes/cloud-features-routes.d.ts.map +1 -1
  217. package/apps/app-lifeops/src/routes/cloud-features-routes.js +5 -3
  218. package/apps/app-lifeops/src/routes/lifeops-routes.d.ts.map +1 -1
  219. package/apps/app-lifeops/src/routes/lifeops-routes.js +15 -3
  220. package/apps/app-lifeops/src/routes/plugin.d.ts.map +1 -1
  221. package/apps/app-lifeops/src/routes/plugin.js +10 -1
  222. package/apps/app-lifeops/src/routes/travel-provider-relay-routes.d.ts +1 -1
  223. package/apps/app-lifeops/src/routes/travel-provider-relay-routes.d.ts.map +1 -1
  224. package/apps/app-lifeops/src/routes/travel-provider-relay-routes.js +4 -2
  225. package/apps/app-lifeops/src/routes/website-blocker-routes.d.ts +1 -1
  226. package/apps/app-lifeops/src/routes/website-blocker-routes.d.ts.map +1 -1
  227. package/apps/app-lifeops/src/ui.d.ts +11 -0
  228. package/apps/app-lifeops/src/ui.d.ts.map +1 -0
  229. package/apps/app-lifeops/src/ui.js +9 -0
  230. package/apps/app-lifeops/src/utils/lifeops-url.d.ts +5 -0
  231. package/apps/app-lifeops/src/utils/lifeops-url.d.ts.map +1 -0
  232. package/apps/app-lifeops/src/utils/lifeops-url.js +35 -0
  233. package/apps/app-lifeops/src/website-blocker/access.d.ts.map +1 -1
  234. package/apps/app-lifeops/src/website-blocker/access.js +1 -1
  235. package/apps/app-steward/src/routes/steward-compat-routes.d.ts +1 -1
  236. package/apps/app-steward/src/routes/steward-compat-routes.d.ts.map +1 -1
  237. package/apps/app-steward/src/routes/steward-compat-routes.js +3 -1
  238. package/apps/app-steward/src/routes/wallet-browser-compat-routes.d.ts +1 -1
  239. package/apps/app-steward/src/routes/wallet-browser-compat-routes.d.ts.map +1 -1
  240. package/apps/app-steward/src/routes/wallet-browser-compat-routes.js +3 -1
  241. package/apps/app-steward/src/routes/wallet-bsc-core-routes.d.ts.map +1 -1
  242. package/apps/app-steward/src/routes/wallet-bsc-core-routes.js +2 -1
  243. package/apps/app-steward/src/routes/wallet-compat-routes.d.ts +1 -1
  244. package/apps/app-steward/src/routes/wallet-compat-routes.d.ts.map +1 -1
  245. package/apps/app-steward/src/routes/wallet-compat-routes.js +3 -1
  246. package/apps/app-steward/src/routes/wallet-core-routes.d.ts.map +1 -1
  247. package/apps/app-steward/src/routes/wallet-core-routes.js +2 -1
  248. package/apps/app-steward/src/routes/wallet-trade-compat-routes.d.ts +1 -1
  249. package/apps/app-steward/src/routes/wallet-trade-compat-routes.d.ts.map +1 -1
  250. package/apps/app-steward/src/routes/wallet-trade-compat-routes.js +3 -1
  251. package/apps/app-training/src/core/dataset-generator.d.ts +1 -1
  252. package/apps/app-training/src/core/dataset-generator.d.ts.map +1 -1
  253. package/apps/app-training/src/core/roleplay-executor.d.ts +1 -1
  254. package/apps/app-training/src/core/roleplay-executor.d.ts.map +1 -1
  255. package/apps/app-training/src/core/trajectory-task-datasets.d.ts +1 -1
  256. package/apps/app-training/src/core/trajectory-task-datasets.d.ts.map +1 -1
  257. package/apps/app-training/src/index.d.ts +1 -2
  258. package/apps/app-training/src/index.d.ts.map +1 -1
  259. package/apps/app-training/src/index.js +1 -2
  260. package/apps/app-training/src/routes/training-routes.d.ts +1 -1
  261. package/apps/app-training/src/routes/training-routes.d.ts.map +1 -1
  262. package/apps/app-training/src/routes/training-routes.js +1 -1
  263. package/apps/app-training/src/routes/trajectory-routes.d.ts +1 -1
  264. package/apps/app-training/src/routes/trajectory-routes.d.ts.map +1 -1
  265. package/apps/app-training/src/routes/trajectory-routes.js +3 -1
  266. package/apps/app-training/src/services/training-service-like.d.ts +1 -1
  267. package/apps/app-training/src/services/training-service-like.d.ts.map +1 -1
  268. package/apps/app-training/src/services/training-service.d.ts +1 -1
  269. package/apps/app-training/src/services/training-service.d.ts.map +1 -1
  270. package/package.json +4 -4
  271. package/packages/agent/src/api/provider-switch-config.js +1 -1
  272. package/packages/agent/src/api/server-helpers-auth.d.ts.map +1 -1
  273. package/packages/agent/src/api/server-helpers-auth.js +5 -4
  274. package/packages/agent/src/providers/media-provider.d.ts +1 -1
  275. package/packages/agent/src/providers/media-provider.d.ts.map +1 -1
  276. package/packages/agent/src/providers/self-status.d.ts +1 -1
  277. package/packages/agent/src/providers/self-status.d.ts.map +1 -1
  278. package/packages/app-core/src/App.d.ts.map +1 -1
  279. package/packages/app-core/src/App.js +20 -12
  280. package/packages/app-core/src/api/auth.d.ts.map +1 -1
  281. package/packages/app-core/src/api/auth.js +5 -5
  282. package/packages/app-core/src/api/client-types-experience.d.ts +7 -0
  283. package/packages/app-core/src/api/client-types-experience.d.ts.map +1 -1
  284. package/packages/app-core/src/components/apps/helpers.d.ts +1 -1
  285. package/packages/app-core/src/components/apps/helpers.d.ts.map +1 -1
  286. package/packages/app-core/src/components/apps/helpers.js +1 -0
  287. package/packages/app-core/src/components/apps/internal-tool-apps.d.ts +11 -0
  288. package/packages/app-core/src/components/apps/internal-tool-apps.d.ts.map +1 -1
  289. package/packages/app-core/src/components/apps/internal-tool-apps.js +31 -0
  290. package/packages/app-core/src/components/apps/launch-history.d.ts +16 -0
  291. package/packages/app-core/src/components/apps/launch-history.d.ts.map +1 -0
  292. package/packages/app-core/src/components/apps/launch-history.js +77 -0
  293. package/packages/app-core/src/components/apps/per-app-config.d.ts +19 -0
  294. package/packages/app-core/src/components/apps/per-app-config.d.ts.map +1 -0
  295. package/packages/app-core/src/components/apps/per-app-config.js +99 -0
  296. package/packages/app-core/src/components/apps/useRegistryCatalog.d.ts +14 -0
  297. package/packages/app-core/src/components/apps/useRegistryCatalog.d.ts.map +1 -0
  298. package/packages/app-core/src/components/apps/useRegistryCatalog.js +52 -0
  299. package/packages/app-core/src/components/auth/LoginView.d.ts +1 -19
  300. package/packages/app-core/src/components/auth/LoginView.d.ts.map +1 -1
  301. package/packages/app-core/src/components/auth/LoginView.js +4 -99
  302. package/packages/app-core/src/components/character/CharacterExperienceWorkspace.d.ts.map +1 -1
  303. package/packages/app-core/src/components/character/CharacterExperienceWorkspace.js +14 -2
  304. package/packages/app-core/src/components/character/character-hub-helpers.d.ts.map +1 -1
  305. package/packages/app-core/src/components/character/character-hub-helpers.js +7 -0
  306. package/packages/app-core/src/components/character/character-hub-types.d.ts +7 -0
  307. package/packages/app-core/src/components/character/character-hub-types.d.ts.map +1 -1
  308. package/packages/app-core/src/components/onboarding/BootstrapStep.d.ts.map +1 -1
  309. package/packages/app-core/src/components/onboarding/BootstrapStep.js +3 -2
  310. package/packages/app-core/src/components/pages/AppDetailsView.d.ts +36 -0
  311. package/packages/app-core/src/components/pages/AppDetailsView.d.ts.map +1 -0
  312. package/packages/app-core/src/components/pages/AppDetailsView.js +289 -0
  313. package/packages/app-core/src/components/pages/AppsView.d.ts.map +1 -1
  314. package/packages/app-core/src/components/pages/AppsView.js +134 -42
  315. package/packages/app-core/src/components/shell/StartupShell.d.ts.map +1 -1
  316. package/packages/app-core/src/components/shell/StartupShell.js +2 -5
  317. package/packages/app-core/src/i18n/locales/en.json +256 -205
  318. package/packages/app-core/src/i18n/locales/es.json +256 -205
  319. package/packages/app-core/src/i18n/locales/ko.json +256 -205
  320. package/packages/app-core/src/i18n/locales/pt.json +256 -205
  321. package/packages/app-core/src/i18n/locales/tl.json +256 -205
  322. package/packages/app-core/src/i18n/locales/vi.json +256 -205
  323. package/packages/app-core/src/i18n/locales/zh-CN.json +256 -205
  324. package/packages/app-core/src/navigation/index.d.ts.map +1 -1
  325. package/packages/app-core/src/navigation/index.js +6 -0
  326. package/packages/app-core/src/services/auth-store.d.ts +1 -1
  327. package/packages/app-core/src/services/auth-store.d.ts.map +1 -1
  328. package/packages/app-core/src/services/auth-store.js +1 -1
  329. package/packages/app-core/src/shell/AppWindowRenderer.d.ts +17 -0
  330. package/packages/app-core/src/shell/AppWindowRenderer.d.ts.map +1 -0
  331. package/packages/app-core/src/shell/AppWindowRenderer.js +337 -0
  332. package/packages/app-core/src/shell/DetachedShellRoot.js +4 -1
  333. package/packages/app-core/src/shell/index.d.ts +1 -0
  334. package/packages/app-core/src/shell/index.d.ts.map +1 -1
  335. package/packages/app-core/src/shell/index.js +1 -0
  336. package/packages/app-core/src/state/startup-coordinator.d.ts.map +1 -1
  337. package/packages/app-core/src/state/startup-coordinator.js +0 -3
  338. package/packages/app-core/src/state/startup-phase-poll.d.ts.map +1 -1
  339. package/packages/app-core/src/state/startup-phase-poll.js +2 -4
  340. package/packages/app-core/src/state/usePluginsSkillsState.d.ts +6 -6
  341. package/packages/native-plugins/mobile-signals/src/definitions.d.ts +162 -0
  342. package/packages/native-plugins/mobile-signals/src/definitions.d.ts.map +1 -0
  343. package/packages/native-plugins/mobile-signals/src/definitions.js +1 -0
  344. package/packages/native-plugins/mobile-signals/src/index.d.ts +4 -0
  345. package/packages/native-plugins/mobile-signals/src/index.d.ts.map +1 -0
  346. package/packages/native-plugins/mobile-signals/src/index.js +6 -0
  347. package/packages/native-plugins/mobile-signals/src/web.d.ts +29 -0
  348. package/packages/native-plugins/mobile-signals/src/web.d.ts.map +1 -0
  349. package/packages/native-plugins/mobile-signals/src/web.js +269 -0
  350. package/packages/shared/src/app-hero-art.d.ts.map +1 -1
  351. package/packages/shared/src/app-hero-art.js +13 -0
  352. package/packages/shared/src/contracts/lifeops.d.ts +10 -0
  353. package/packages/shared/src/contracts/lifeops.d.ts.map +1 -1
  354. package/packages/shared/src/contracts/lifeops.js +5 -0
  355. package/packages/typescript/src/features/advanced-capabilities/experience/actions/record-experience.d.ts.map +1 -1
  356. package/packages/typescript/src/features/advanced-capabilities/experience/actions/record-experience.js +46 -4
  357. package/packages/typescript/src/features/advanced-capabilities/experience/evaluators/experienceEvaluator.d.ts.map +1 -1
  358. package/packages/typescript/src/features/advanced-capabilities/experience/evaluators/experienceEvaluator.js +34 -124
  359. package/packages/typescript/src/features/advanced-capabilities/experience/service.d.ts.map +1 -1
  360. package/packages/typescript/src/features/advanced-capabilities/experience/service.js +56 -0
  361. package/packages/typescript/src/features/advanced-capabilities/experience/types.d.ts +7 -0
  362. package/packages/typescript/src/features/advanced-capabilities/experience/types.d.ts.map +1 -1
  363. package/packages/typescript/src/features/advanced-capabilities/experience/utils/experienceText.d.ts +7 -0
  364. package/packages/typescript/src/features/advanced-capabilities/experience/utils/experienceText.d.ts.map +1 -0
  365. package/packages/typescript/src/features/advanced-capabilities/experience/utils/experienceText.js +123 -0
  366. package/apps/app-training/src/ui/FineTuningView.d.ts +0 -5
  367. package/apps/app-training/src/ui/FineTuningView.d.ts.map +0 -1
  368. package/apps/app-training/src/ui/FineTuningView.js +0 -388
  369. package/apps/app-training/src/ui/fine-tuning-panels.d.ts +0 -111
  370. package/apps/app-training/src/ui/fine-tuning-panels.d.ts.map +0 -1
  371. package/apps/app-training/src/ui/fine-tuning-panels.js +0 -130
  372. package/apps/app-training/src/ui/index.d.ts +0 -3
  373. package/apps/app-training/src/ui/index.d.ts.map +0 -1
  374. package/apps/app-training/src/ui/index.js +0 -2
  375. package/packages/app-core/src/api/trusted-local-request.d.ts +0 -4
  376. package/packages/app-core/src/api/trusted-local-request.d.ts.map +0 -1
  377. package/packages/app-core/src/api/trusted-local-request.js +0 -176
@@ -0,0 +1,490 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { Button, client, Input, openExternalUrl, Spinner, useMediaQuery, useApp, } from "@elizaos/app-core";
3
+ import { LIFEOPS_INBOX_CHANNELS, } from "@elizaos/shared";
4
+ import { AlarmClock, ArrowLeft, AtSign, CalendarClock, ExternalLink, MessageCircle, MessageSquare, MessageSquareReply, Phone, Search, Send, Shield, Smartphone, Sparkles, } from "lucide-react";
5
+ import { useCallback, useEffect, useMemo, useState, } from "react";
6
+ import { useInbox, } from "../hooks/useInbox.js";
7
+ import { buildMessageChatPrefill, buildReplyPrefill, useLifeOpsChatLauncher, } from "./LifeOpsChatAdapter.js";
8
+ import { useLifeOpsSelection, } from "./LifeOpsSelectionContext.js";
9
+ const CHANNEL_STYLES = {
10
+ gmail: {
11
+ label: "Gmail",
12
+ bg: "bg-rose-500/16",
13
+ ring: "ring-rose-500/40",
14
+ text: "text-rose-300",
15
+ icon: _jsx(AtSign, { className: "h-3.5 w-3.5", "aria-hidden": true }),
16
+ },
17
+ x_dm: {
18
+ label: "X DMs",
19
+ bg: "bg-zinc-500/18",
20
+ ring: "ring-zinc-400/40",
21
+ text: "text-zinc-200",
22
+ icon: _jsx(MessageSquareReply, { className: "h-3.5 w-3.5", "aria-hidden": true }),
23
+ },
24
+ discord: {
25
+ label: "Discord",
26
+ bg: "bg-indigo-500/18",
27
+ ring: "ring-indigo-500/40",
28
+ text: "text-indigo-300",
29
+ icon: _jsx(MessageCircle, { className: "h-3.5 w-3.5", "aria-hidden": true }),
30
+ },
31
+ telegram: {
32
+ label: "Telegram",
33
+ bg: "bg-sky-500/18",
34
+ ring: "ring-sky-500/40",
35
+ text: "text-sky-300",
36
+ icon: _jsx(Send, { className: "h-3.5 w-3.5", "aria-hidden": true }),
37
+ },
38
+ signal: {
39
+ label: "Signal",
40
+ bg: "bg-blue-500/18",
41
+ ring: "ring-blue-500/40",
42
+ text: "text-blue-300",
43
+ icon: _jsx(Shield, { className: "h-3.5 w-3.5", "aria-hidden": true }),
44
+ },
45
+ imessage: {
46
+ label: "iMessage",
47
+ bg: "bg-emerald-500/18",
48
+ ring: "ring-emerald-500/40",
49
+ text: "text-emerald-300",
50
+ icon: _jsx(MessageSquare, { className: "h-3.5 w-3.5", "aria-hidden": true }),
51
+ },
52
+ whatsapp: {
53
+ label: "WhatsApp",
54
+ bg: "bg-lime-500/18",
55
+ ring: "ring-lime-500/40",
56
+ text: "text-lime-300",
57
+ icon: _jsx(Smartphone, { className: "h-3.5 w-3.5", "aria-hidden": true }),
58
+ },
59
+ sms: {
60
+ label: "SMS",
61
+ bg: "bg-amber-500/18",
62
+ ring: "ring-amber-500/40",
63
+ text: "text-amber-300",
64
+ icon: _jsx(Phone, { className: "h-3.5 w-3.5", "aria-hidden": true }),
65
+ },
66
+ };
67
+ const ALL_CHANNEL_STYLE = {
68
+ label: "All",
69
+ bg: "bg-violet-500/16",
70
+ ring: "ring-violet-500/40",
71
+ text: "text-violet-300",
72
+ icon: _jsx(MessageSquare, { className: "h-3.5 w-3.5", "aria-hidden": true }),
73
+ };
74
+ export const LIFEOPS_MESSAGE_CHANNELS = LIFEOPS_INBOX_CHANNELS.filter((channel) => channel !== "gmail");
75
+ export const LIFEOPS_MAIL_CHANNELS = ["gmail"];
76
+ const SMALL_GROUP_PARTICIPANT_CAP = 15;
77
+ const IMPORTANT_PRIORITY_SCORE_THRESHOLD = 70;
78
+ const MISSED_REPLY_GAP_MS = 24 * 60 * 60 * 1000;
79
+ const MISSED_MIN_PRIORITY = 50;
80
+ const ALL_GMAIL_ACCOUNTS = "__all__";
81
+ function styleFor(channel) {
82
+ return CHANNEL_STYLES[channel];
83
+ }
84
+ function styleForFilter(channel) {
85
+ return channel === "all"
86
+ ? ALL_CHANNEL_STYLE
87
+ : CHANNEL_STYLES[channel];
88
+ }
89
+ function messagesForThreadGroup(group) {
90
+ const messages = Array.isArray(group.messages) ? group.messages : [];
91
+ return messages.length > 0 ? messages : [group.latestMessage];
92
+ }
93
+ function computeMissedAgeMs(message) {
94
+ if (typeof message.repliedAt === "string" && message.repliedAt.length > 0) {
95
+ return null;
96
+ }
97
+ const received = Date.parse(message.receivedAt);
98
+ if (!Number.isFinite(received))
99
+ return null;
100
+ const age = Date.now() - received;
101
+ return age >= MISSED_REPLY_GAP_MS ? age : null;
102
+ }
103
+ function formatMissedAge(ageMs) {
104
+ const days = Math.floor(ageMs / (24 * 60 * 60 * 1000));
105
+ if (days >= 1)
106
+ return `${days}d ago`;
107
+ const hours = Math.max(1, Math.floor(ageMs / (60 * 60 * 1000)));
108
+ return `${hours}h ago`;
109
+ }
110
+ function formatRelativeTime(receivedAt) {
111
+ const parsed = Date.parse(receivedAt);
112
+ if (!Number.isFinite(parsed))
113
+ return "";
114
+ const diffMs = Date.now() - parsed;
115
+ const diffMin = Math.floor(diffMs / 60_000);
116
+ if (diffMin < 1)
117
+ return "now";
118
+ if (diffMin < 60)
119
+ return `${diffMin}m`;
120
+ const diffH = Math.floor(diffMin / 60);
121
+ if (diffH < 24)
122
+ return `${diffH}h`;
123
+ const diffD = Math.floor(diffH / 24);
124
+ return `${diffD}d`;
125
+ }
126
+ function formatAbsoluteTime(receivedAt) {
127
+ const parsed = Date.parse(receivedAt);
128
+ return Number.isFinite(parsed) ? new Date(parsed).toLocaleString() : "";
129
+ }
130
+ function initialsFor(name) {
131
+ const parts = name.trim().split(/\s+/).filter(Boolean);
132
+ if (parts.length === 0)
133
+ return "?";
134
+ if (parts.length === 1)
135
+ return parts[0]?.slice(0, 2).toUpperCase() ?? "?";
136
+ return `${parts[0]?.[0] ?? ""}${parts[1]?.[0] ?? ""}`.toUpperCase();
137
+ }
138
+ function ChannelChip({ channel, active, count, unread, onClick, }) {
139
+ const style = styleForFilter(channel);
140
+ return (_jsxs("button", { type: "button", onClick: () => onClick(channel), "aria-pressed": active, className: [
141
+ "group inline-flex items-center gap-1.5 rounded-full px-2.5 py-1 text-[11px] font-medium transition-colors",
142
+ active
143
+ ? `${style.bg} ${style.text} ring-1 ${style.ring}`
144
+ : "bg-bg-muted/30 text-muted hover:text-txt",
145
+ ].join(" "), children: [_jsx("span", { className: `shrink-0 ${active ? style.text : "text-muted/80"}`, children: style.icon }), _jsx("span", { children: style.label }), count > 0 ? (_jsx("span", { className: `ml-0.5 rounded-full px-1.5 text-[10px] tabular-nums ${unread > 0 ? `${style.text} bg-white/4` : "text-muted bg-white/4"}`, children: unread > 0 ? unread : count })) : null] }));
146
+ }
147
+ function GmailAccountChip({ label, active, onClick, }) {
148
+ const style = CHANNEL_STYLES.gmail;
149
+ return (_jsxs("button", { type: "button", onClick: onClick, "aria-pressed": active, className: [
150
+ "inline-flex items-center gap-1.5 rounded-full px-2.5 py-1 text-[11px] font-medium transition-colors",
151
+ active
152
+ ? `${style.bg} ${style.text} ring-1 ${style.ring}`
153
+ : "bg-bg-muted/30 text-muted hover:text-txt",
154
+ ].join(" "), children: [_jsx("span", { className: `shrink-0 ${active ? style.text : "text-muted/80"}`, children: style.icon }), _jsx("span", { children: label })] }));
155
+ }
156
+ function ThreadRow({ group, selected, onSelect, onReply, showAccountSubtitle, }) {
157
+ const message = group.latestMessage;
158
+ const style = styleFor(message.channel);
159
+ const subject = message.subject?.trim() || `${style.label} message`;
160
+ const score = group.maxPriorityScore ?? message.priorityScore ?? 0;
161
+ const category = group.priorityCategory ?? message.priorityCategory ?? null;
162
+ const isImportant = score >= IMPORTANT_PRIORITY_SCORE_THRESHOLD || category === "important";
163
+ const isPlanning = category === "planning";
164
+ const missedAge = computeMissedAgeMs(message);
165
+ const isMissed = missedAge !== null && score >= MISSED_MIN_PRIORITY;
166
+ const senderLabel = group.chatType === "group" && typeof group.participantCount === "number"
167
+ ? `${message.sender.displayName} (${group.participantCount})`
168
+ : message.sender.displayName;
169
+ return (_jsxs("div", { role: "option", "aria-selected": selected, tabIndex: 0, onClick: onSelect, onKeyDown: (e) => {
170
+ if (e.key === "Enter" || e.key === " ") {
171
+ e.preventDefault();
172
+ onSelect();
173
+ }
174
+ }, className: [
175
+ "group flex cursor-pointer items-start gap-3 border-l-2 px-3 py-2.5 transition-colors",
176
+ selected
177
+ ? "border-accent bg-accent/8"
178
+ : message.unread
179
+ ? "border-transparent bg-bg/30 hover:bg-bg-muted/30"
180
+ : "border-transparent hover:bg-bg-muted/20",
181
+ ].join(" "), children: [_jsxs("div", { className: "relative h-8 w-8 shrink-0", children: [_jsx("div", { className: `flex h-full w-full items-center justify-center overflow-hidden rounded-full text-[11px] font-semibold ${style.bg} ${style.text}`, children: message.sender.avatarUrl ? (_jsx("img", { src: message.sender.avatarUrl, alt: "", "aria-hidden": true, className: "h-full w-full object-cover" })) : (initialsFor(message.sender.displayName)) }), _jsx("span", { className: `pointer-events-none absolute -bottom-1 -right-1 inline-flex h-4 w-4 items-center justify-center rounded-full bg-bg ring-2 ring-bg ${style.text} [&>svg]:h-2.5 [&>svg]:w-2.5`, "aria-hidden": true, children: style.icon })] }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsxs("div", { className: "flex items-baseline justify-between gap-2", children: [_jsx("span", { className: `truncate text-sm ${message.unread
182
+ ? "font-semibold text-txt"
183
+ : "font-medium text-txt/85"}`, children: senderLabel }), _jsxs("div", { className: "flex shrink-0 items-center gap-1.5", children: [group.totalCount > 1 ? (_jsx("span", { className: "text-[10px] tabular-nums text-muted", children: group.totalCount })) : null, group.unreadCount > 0 ? (_jsx("span", { className: "h-1.5 w-1.5 rounded-full bg-accent" })) : null, _jsx("span", { className: "text-[10px] tabular-nums text-muted", children: formatRelativeTime(message.receivedAt) })] })] }), _jsx("div", { className: "mt-0.5 truncate text-xs text-muted", children: subject }), _jsx("div", { className: "mt-0.5 line-clamp-1 text-[11px] text-muted/70", children: message.snippet }), _jsxs("div", { className: "mt-1 flex flex-wrap items-center gap-1.5", children: [isImportant ? (_jsxs("span", { className: "inline-flex items-center gap-1 rounded-full bg-amber-500/16 px-1.5 py-0.5 text-[10px] font-semibold text-amber-300 ring-1 ring-amber-500/40", title: `Priority score ${score}`, children: [_jsx(Sparkles, { className: "h-2.5 w-2.5", "aria-hidden": true }), "Important"] })) : null, isPlanning ? (_jsxs("span", { className: "inline-flex items-center gap-1 rounded-full bg-sky-500/16 px-1.5 py-0.5 text-[10px] font-semibold text-sky-300 ring-1 ring-sky-500/40", children: [_jsx(CalendarClock, { className: "h-2.5 w-2.5", "aria-hidden": true }), "Planning"] })) : null, showAccountSubtitle && message.gmailAccountEmail ? (_jsx("span", { className: "truncate text-[10px] text-muted/70", children: message.gmailAccountEmail })) : null] })] }), _jsxs("div", { className: "flex shrink-0 flex-col items-end gap-1", children: [isMissed && missedAge !== null ? (_jsxs("span", { className: "inline-flex items-center gap-1 rounded-full bg-amber-500/16 px-1.5 py-0.5 text-[10px] font-semibold text-amber-300 ring-1 ring-amber-500/40", title: "Unreplied for over 24h", children: [_jsx(AlarmClock, { className: "h-2.5 w-2.5", "aria-hidden": true }), "Missed \u00B7 ", formatMissedAge(missedAge)] })) : null, _jsx("button", { type: "button", "aria-label": "Reply", onClick: (e) => {
184
+ e.stopPropagation();
185
+ onReply();
186
+ }, className: "mt-0.5 rounded-full p-1.5 text-muted opacity-0 transition-opacity hover:bg-bg-muted/40 hover:text-txt group-hover:opacity-100", children: _jsx(MessageSquareReply, { className: "h-3.5 w-3.5" }) })] })] }));
187
+ }
188
+ function MessageRow({ message, selected, onSelect, onReply, }) {
189
+ const style = styleFor(message.channel);
190
+ const subject = message.subject?.trim() || `${style.label} message`;
191
+ return (_jsxs("div", { role: "option", "aria-selected": selected, tabIndex: 0, onClick: onSelect, onKeyDown: (e) => {
192
+ if (e.key === "Enter" || e.key === " ") {
193
+ e.preventDefault();
194
+ onSelect();
195
+ }
196
+ }, className: [
197
+ "group flex cursor-pointer items-start gap-3 border-l-2 px-3 py-2.5 transition-colors",
198
+ selected
199
+ ? "border-accent bg-accent/8"
200
+ : message.unread
201
+ ? "border-transparent bg-bg/30 hover:bg-bg-muted/30"
202
+ : "border-transparent hover:bg-bg-muted/20",
203
+ ].join(" "), children: [_jsxs("div", { className: "relative h-8 w-8 shrink-0", children: [_jsx("div", { className: `flex h-full w-full items-center justify-center overflow-hidden rounded-full text-[11px] font-semibold ${style.bg} ${style.text}`, children: message.sender.avatarUrl ? (_jsx("img", { src: message.sender.avatarUrl, alt: "", "aria-hidden": true, className: "h-full w-full object-cover" })) : (initialsFor(message.sender.displayName)) }), _jsx("span", { className: `pointer-events-none absolute -bottom-1 -right-1 inline-flex h-4 w-4 items-center justify-center rounded-full bg-bg ring-2 ring-bg ${style.text} [&>svg]:h-2.5 [&>svg]:w-2.5`, "aria-hidden": true, children: style.icon })] }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsxs("div", { className: "flex items-baseline justify-between gap-2", children: [_jsx("span", { className: `truncate text-sm ${message.unread
204
+ ? "font-semibold text-txt"
205
+ : "font-medium text-txt/85"}`, children: message.sender.displayName }), _jsxs("div", { className: "flex shrink-0 items-center gap-1.5", children: [message.unread ? (_jsx("span", { className: "h-1.5 w-1.5 rounded-full bg-accent" })) : null, _jsx("span", { className: "text-[10px] tabular-nums text-muted", children: formatRelativeTime(message.receivedAt) })] })] }), _jsx("div", { className: "mt-0.5 truncate text-xs text-muted", children: subject }), _jsx("div", { className: "mt-0.5 line-clamp-1 text-[11px] text-muted/70", children: message.snippet })] }), _jsx("button", { type: "button", "aria-label": "Reply", onClick: (e) => {
206
+ e.stopPropagation();
207
+ onReply();
208
+ }, className: "mt-0.5 shrink-0 rounded-full p-1.5 text-muted opacity-0 transition-opacity hover:bg-bg-muted/40 hover:text-txt group-hover:opacity-100", children: _jsx(MessageSquareReply, { className: "h-3.5 w-3.5" }) })] }));
209
+ }
210
+ function ReaderPane({ threadGroup, onReply, onChat, onBack, }) {
211
+ const { t } = useApp();
212
+ const message = threadGroup?.latestMessage ?? null;
213
+ if (!message) {
214
+ return (_jsx("div", { className: "flex flex-1 items-center justify-center bg-bg/20 text-xs text-muted", children: _jsxs("div", { className: "flex flex-col items-center gap-2 text-center", children: [_jsx(MessageSquare, { className: "h-6 w-6 text-muted/50", "aria-hidden": true }), t("lifeopsInbox.selectMessage", {
215
+ defaultValue: "Select a message",
216
+ })] }) }));
217
+ }
218
+ const style = styleFor(message.channel);
219
+ const subject = message.subject?.trim() || `${style.label} message`;
220
+ const receivedAt = formatAbsoluteTime(message.receivedAt);
221
+ const threadMessages = threadGroup
222
+ ? [...messagesForThreadGroup(threadGroup)].sort((left, right) => Date.parse(left.receivedAt) - Date.parse(right.receivedAt))
223
+ : [];
224
+ return (_jsxs("div", { className: "flex flex-1 flex-col overflow-hidden bg-bg/10", children: [_jsxs("div", { className: "flex items-start gap-3 border-b border-border/12 px-5 py-4", children: [onBack ? (_jsx("button", { type: "button", "aria-label": t("lifeopsInbox.backToList", {
225
+ defaultValue: "Back to inbox list",
226
+ }), onClick: onBack, className: "mt-0.5 inline-flex h-8 w-8 shrink-0 items-center justify-center rounded-full text-muted transition-colors hover:bg-bg-muted/40 hover:text-txt", children: _jsx(ArrowLeft, { className: "h-4 w-4" }) })) : null, _jsx("div", { className: `flex h-10 w-10 shrink-0 items-center justify-center overflow-hidden rounded-full text-xs font-semibold ${style.bg} ${style.text}`, children: message.sender.avatarUrl ? (_jsx("img", { src: message.sender.avatarUrl, alt: "", "aria-hidden": true, className: "h-full w-full object-cover" })) : (initialsFor(message.sender.displayName)) }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("div", { className: "text-base font-semibold text-txt", children: subject }), _jsxs("div", { className: "mt-0.5 flex flex-wrap items-center gap-2 text-xs text-muted", children: [_jsxs("span", { className: `inline-flex items-center gap-1 ${style.text}`, children: [style.icon, style.label] }), _jsx("span", { children: "\u00B7" }), _jsx("span", { children: threadMessages.length > 1
227
+ ? `${threadMessages.length} messages`
228
+ : message.sender.displayName }), receivedAt ? (_jsxs(_Fragment, { children: [_jsx("span", { children: "\u00B7" }), _jsx("span", { children: receivedAt })] })) : null] })] })] }), _jsx("div", { className: "flex-1 overflow-y-auto px-5 py-4", children: _jsx("div", { className: "space-y-3", children: (threadMessages.length > 0 ? threadMessages : [message]).map((item) => {
229
+ const itemStyle = styleFor(item.channel);
230
+ return (_jsxs("article", { className: "rounded-xl border border-border/12 bg-bg/20 px-3 py-3", children: [_jsxs("div", { className: "flex items-start justify-between gap-3", children: [_jsxs("div", { className: "min-w-0", children: [_jsx("div", { className: "truncate text-sm font-medium text-txt", children: item.sender.displayName }), _jsxs("div", { className: "mt-0.5 flex flex-wrap items-center gap-1.5 text-[11px] text-muted", children: [_jsxs("span", { className: `inline-flex items-center gap-1 ${itemStyle.text}`, children: [itemStyle.icon, itemStyle.label] }), item.sender.email ? _jsx("span", { children: item.sender.email }) : null] })] }), _jsx("span", { className: "shrink-0 text-[11px] tabular-nums text-muted", children: formatAbsoluteTime(item.receivedAt) })] }), _jsx("p", { className: "mt-3 whitespace-pre-wrap text-sm leading-relaxed text-txt/85", children: item.snippet })] }, item.id));
231
+ }) }) }), _jsxs("div", { className: "flex flex-wrap items-center gap-2 border-t border-border/12 px-5 py-3", children: [_jsxs(Button, { size: "sm", className: "h-8 rounded-xl px-3 text-xs font-semibold", onClick: () => onReply(message), children: [_jsx(MessageSquareReply, { className: "mr-1.5 h-3.5 w-3.5" }), t("lifeopsInbox.reply", { defaultValue: "Reply" })] }), _jsxs(Button, { size: "sm", variant: "ghost", className: "h-8 rounded-xl px-3 text-xs font-semibold text-muted", onClick: () => onChat(message), children: [_jsx(MessageSquare, { className: "mr-1.5 h-3.5 w-3.5" }), t("common.chat", { defaultValue: "Chat" })] }), message.channel === "gmail" ? (_jsx(InboxUnsubscribeButton, { message: message })) : null, message.deepLink ? (_jsxs(Button, { size: "sm", variant: "ghost", className: "h-8 rounded-xl px-3 text-xs font-semibold text-muted", onClick: () => message.deepLink && void openExternalUrl(message.deepLink), children: [_jsx(ExternalLink, { className: "mr-1.5 h-3.5 w-3.5" }), t("lifeopsInbox.openSource", { defaultValue: "Open source" })] })) : null] })] }));
232
+ }
233
+ function InboxUnsubscribeButton({ message, }) {
234
+ const senderEmail = message.sender.email?.trim().toLowerCase() || null;
235
+ const [state, setState] = useState("idle");
236
+ const [note, setNote] = useState(null);
237
+ const onClick = useCallback(async () => {
238
+ if (!senderEmail)
239
+ return;
240
+ if (!window.confirm(`Unsubscribe from ${senderEmail} and create a Gmail filter that auto-trashes future mail?`)) {
241
+ return;
242
+ }
243
+ setState("working");
244
+ try {
245
+ const result = await client.unsubscribeLifeOpsEmailSender({
246
+ senderEmail,
247
+ blockAfter: true,
248
+ trashExisting: false,
249
+ confirmed: true,
250
+ });
251
+ setState(result.record.status === "succeeded" ? "done" : "error");
252
+ setNote(result.record.status);
253
+ }
254
+ catch (error) {
255
+ setState("error");
256
+ setNote(error instanceof Error ? error.message : String(error));
257
+ }
258
+ }, [senderEmail]);
259
+ // Hide entirely when the message has no parsed From email (chat channels,
260
+ // malformed Gmail headers). Showing a button that can't work is worse than
261
+ // not showing it.
262
+ if (!senderEmail)
263
+ return null;
264
+ const label = state === "working"
265
+ ? "Unsubscribing…"
266
+ : state === "done"
267
+ ? "Unsubscribed"
268
+ : state === "error"
269
+ ? `Failed${note ? `: ${note}` : ""}`
270
+ : "Unsubscribe";
271
+ return (_jsx(Button, { size: "sm", variant: "ghost", className: "h-8 rounded-xl px-3 text-xs font-semibold text-muted", disabled: state === "working" || state === "done", onClick: () => void onClick(), title: `Send RFC 8058 one-click unsubscribe to ${senderEmail} and create a Gmail filter`, children: label }));
272
+ }
273
+ function InboxListPane({ inbox, selectedMessageId, onSelect, onReply, emptyLabel, groupedMode, visibleThreadGroups, showGmailAccountSubtitles, }) {
274
+ const { t } = useApp();
275
+ const isEmpty = groupedMode
276
+ ? visibleThreadGroups.length === 0
277
+ : inbox.messages.length === 0;
278
+ return (_jsxs("div", { className: "flex min-h-0 flex-1 flex-col", children: [_jsx("div", { className: "border-b border-border/10 px-3 py-2", children: _jsxs("div", { className: "relative", children: [_jsx(Search, { className: "absolute left-2.5 top-2 h-3.5 w-3.5 text-muted" }), _jsx(Input, { value: inbox.searchQuery, onChange: (e) => inbox.setSearchQuery(e.target.value), placeholder: t("lifeopsInbox.search", {
279
+ defaultValue: "Search…",
280
+ }), "aria-label": t("lifeopsInbox.searchAria", {
281
+ defaultValue: "Search inbox",
282
+ }), className: "h-8 pl-8 text-xs" })] }) }), _jsx("div", { className: "min-h-0 flex-1 overflow-y-auto", role: "listbox", "aria-label": t("lifeopsInbox.listAria", {
283
+ defaultValue: "Messages",
284
+ }), children: isEmpty ? (_jsx("div", { className: "px-4 py-8 text-center text-xs text-muted", children: inbox.searchQuery
285
+ ? t("lifeopsInbox.noResults", {
286
+ defaultValue: "No matches.",
287
+ })
288
+ : t("lifeopsInbox.empty", {
289
+ defaultValue: emptyLabel ?? "Inbox clear.",
290
+ }) })) : groupedMode ? (visibleThreadGroups.map((group) => (_jsx(ThreadRow, { group: group, selected: group.latestMessage.id === selectedMessageId ||
291
+ messagesForThreadGroup(group).some((message) => message.id === selectedMessageId), onSelect: () => onSelect({ messageId: group.latestMessage.id }), onReply: () => onReply(group.latestMessage), showAccountSubtitle: showGmailAccountSubtitles }, group.threadId)))) : (inbox.messages.map((msg) => (_jsx(MessageRow, { message: msg, selected: msg.id === selectedMessageId, onSelect: () => onSelect({ messageId: msg.id }), onReply: () => onReply(msg) }, msg.id)))) })] }));
292
+ }
293
+ export function LifeOpsInboxSection(props = {}) {
294
+ const ctx = useLifeOpsSelection();
295
+ const selection = props.selection ?? ctx.selection;
296
+ const onSelect = props.onSelect ?? ctx.select;
297
+ const { t } = useApp();
298
+ const { openLifeOpsChat } = useLifeOpsChatLauncher();
299
+ const compactLayout = useMediaQuery("(max-width: 767px)");
300
+ const allowedChannels = props.channels ?? LIFEOPS_INBOX_CHANNELS;
301
+ // Derive the section mode from the channel set passed in by the route.
302
+ // Mail mode is gmail-only; everything else is the Messages section, which
303
+ // hides public channels and groups with more than 15 participants.
304
+ const isMailMode = allowedChannels.length === 1 && allowedChannels[0] === "gmail";
305
+ const isMessagesMode = !isMailMode;
306
+ const channelFilters = useMemo(() => allowedChannels.length > 1
307
+ ? ["all", ...allowedChannels]
308
+ : [...allowedChannels], [allowedChannels]);
309
+ const [selectedGmailAccount, setSelectedGmailAccount] = useState(ALL_GMAIL_ACCOUNTS);
310
+ const [missedOnly, setMissedOnly] = useState(false);
311
+ const chatTypeFilter = useMemo(() => isMessagesMode
312
+ ? ["dm", "group"]
313
+ : isMailMode
314
+ ? ["dm"]
315
+ : undefined, [isMessagesMode, isMailMode]);
316
+ const inbox = useInbox({
317
+ maxResults: 40,
318
+ channel: allowedChannels.length === 1 ? allowedChannels[0] : "all",
319
+ channels: allowedChannels,
320
+ groupByThread: true,
321
+ chatTypeFilter,
322
+ maxParticipants: isMessagesMode ? SMALL_GROUP_PARTICIPANT_CAP : undefined,
323
+ gmailAccountId: isMailMode && selectedGmailAccount !== ALL_GMAIL_ACCOUNTS
324
+ ? selectedGmailAccount
325
+ : undefined,
326
+ missedOnly: isMessagesMode ? missedOnly : false,
327
+ // Mail mode keeps recency-first because email priority is less actionable.
328
+ sortByPriority: isMessagesMode,
329
+ });
330
+ const selectedMessageId = selection.messageId ?? null;
331
+ // Build the messages-by-id index from both flat messages and thread groups
332
+ // so the reader pane can resolve the active message regardless of how we
333
+ // ended up selecting it.
334
+ const messageById = useMemo(() => {
335
+ const map = new Map();
336
+ for (const msg of inbox.messages)
337
+ map.set(msg.id, msg);
338
+ for (const group of inbox.threadGroups) {
339
+ map.set(group.latestMessage.id, group.latestMessage);
340
+ for (const msg of messagesForThreadGroup(group)) {
341
+ map.set(msg.id, msg);
342
+ }
343
+ }
344
+ return map;
345
+ }, [inbox.messages, inbox.threadGroups]);
346
+ const threadGroupByMessageId = useMemo(() => {
347
+ const map = new Map();
348
+ for (const group of inbox.threadGroups) {
349
+ map.set(group.latestMessage.id, group);
350
+ for (const msg of messagesForThreadGroup(group)) {
351
+ map.set(msg.id, group);
352
+ }
353
+ }
354
+ return map;
355
+ }, [inbox.threadGroups]);
356
+ const selectedMessage = (selectedMessageId ? messageById.get(selectedMessageId) : null) ?? null;
357
+ const selectedThread = (selectedMessageId ? threadGroupByMessageId.get(selectedMessageId) : null) ??
358
+ null;
359
+ // Distinct Gmail accounts visible in the current feed. Only show the chip
360
+ // group when more than one Gmail account has produced messages.
361
+ const gmailAccountOptions = useMemo(() => {
362
+ if (!isMailMode)
363
+ return [];
364
+ const seen = new Map();
365
+ for (const group of inbox.threadGroups) {
366
+ const id = group.latestMessage.gmailAccountId;
367
+ const email = group.latestMessage.gmailAccountEmail;
368
+ if (id && email && !seen.has(id))
369
+ seen.set(id, email);
370
+ }
371
+ if (seen.size === 0) {
372
+ for (const msg of inbox.messages) {
373
+ if (msg.gmailAccountId &&
374
+ msg.gmailAccountEmail &&
375
+ !seen.has(msg.gmailAccountId)) {
376
+ seen.set(msg.gmailAccountId, msg.gmailAccountEmail);
377
+ }
378
+ }
379
+ }
380
+ return Array.from(seen.entries()).map(([id, label]) => ({ id, label }));
381
+ }, [inbox.threadGroups, inbox.messages, isMailMode]);
382
+ const showGmailAccountChips = isMailMode && gmailAccountOptions.length > 1;
383
+ // Subtitle on every Gmail row when more than one account is connected, so
384
+ // users can tell apart two senders that exist in both inboxes.
385
+ const showGmailAccountSubtitles = showGmailAccountChips;
386
+ // Reset to "all" if the previously selected account disappears from the
387
+ // current feed (e.g. account disconnected).
388
+ useEffect(() => {
389
+ if (selectedGmailAccount !== ALL_GMAIL_ACCOUNTS &&
390
+ !gmailAccountOptions.some((opt) => opt.id === selectedGmailAccount)) {
391
+ setSelectedGmailAccount(ALL_GMAIL_ACCOUNTS);
392
+ }
393
+ }, [gmailAccountOptions, selectedGmailAccount]);
394
+ const channelCounts = useMemo(() => {
395
+ const base = Object.fromEntries(channelFilters.map((channel) => [channel, { total: 0, unread: 0 }]));
396
+ for (const message of inbox.messages) {
397
+ const all = base.all;
398
+ if (all) {
399
+ all.total++;
400
+ if (message.unread)
401
+ all.unread++;
402
+ }
403
+ const bucket = base[message.channel];
404
+ if (bucket) {
405
+ bucket.total++;
406
+ if (message.unread)
407
+ bucket.unread++;
408
+ }
409
+ }
410
+ return base;
411
+ }, [channelFilters, inbox.messages]);
412
+ // Build the navigable list (thread rows in grouped modes, flat messages
413
+ // otherwise) so j/k keyboard navigation walks the same items the user sees.
414
+ const navigableItems = useMemo(() => inbox.threadGroups.map((g) => g.latestMessage), [inbox.threadGroups]);
415
+ const navigableSelectedIndex = useMemo(() => navigableItems.findIndex((m) => m.id === selectedMessageId), [navigableItems, selectedMessageId]);
416
+ const selectByIndex = useCallback((index) => {
417
+ const msg = navigableItems[index];
418
+ if (msg)
419
+ onSelect({ messageId: msg.id });
420
+ }, [navigableItems, onSelect]);
421
+ const handleReply = useCallback((msg) => {
422
+ const label = styleFor(msg.channel).label;
423
+ const text = buildReplyPrefill({
424
+ channel: msg.channel === "gmail" ? "email" : label,
425
+ sender: msg.sender.displayName,
426
+ snippet: msg.snippet,
427
+ deepLink: msg.deepLink,
428
+ });
429
+ openLifeOpsChat(text, { messageId: msg.id });
430
+ }, [openLifeOpsChat]);
431
+ const handleChat = useCallback((msg) => {
432
+ openLifeOpsChat(buildMessageChatPrefill(msg), {
433
+ messageId: msg.id,
434
+ });
435
+ }, [openLifeOpsChat]);
436
+ useEffect(() => {
437
+ const handler = (e) => {
438
+ const tag = e.target.tagName;
439
+ if (tag === "INPUT" ||
440
+ tag === "TEXTAREA" ||
441
+ e.target.isContentEditable) {
442
+ return;
443
+ }
444
+ if (e.key === "j") {
445
+ e.preventDefault();
446
+ selectByIndex(Math.min(navigableSelectedIndex + 1, navigableItems.length - 1));
447
+ }
448
+ else if (e.key === "k") {
449
+ e.preventDefault();
450
+ selectByIndex(Math.max(navigableSelectedIndex - 1, 0));
451
+ }
452
+ else if (e.key === "r" && selectedMessage) {
453
+ e.preventDefault();
454
+ handleReply(selectedMessage);
455
+ }
456
+ };
457
+ window.addEventListener("keydown", handler);
458
+ return () => window.removeEventListener("keydown", handler);
459
+ }, [
460
+ handleReply,
461
+ navigableItems.length,
462
+ navigableSelectedIndex,
463
+ selectByIndex,
464
+ selectedMessage,
465
+ ]);
466
+ useEffect(() => {
467
+ if (!compactLayout &&
468
+ navigableItems.length > 0 &&
469
+ (!selectedMessageId || navigableSelectedIndex === -1)) {
470
+ onSelect({ messageId: navigableItems[0].id });
471
+ }
472
+ }, [
473
+ compactLayout,
474
+ navigableItems,
475
+ navigableSelectedIndex,
476
+ onSelect,
477
+ selectedMessageId,
478
+ ]);
479
+ return (_jsxs("section", { className: "flex h-full min-h-0 flex-col overflow-hidden rounded-3xl border border-border/16 bg-card/18", "data-testid": "lifeops-inbox-section", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-2 border-b border-border/12 px-3 py-2.5", children: [_jsx("div", { className: "mr-auto px-1 text-sm font-semibold text-txt", children: props.title ?? "Messages" }), channelFilters.map((ch) => {
480
+ const counts = channelCounts[ch];
481
+ return (_jsx(ChannelChip, { channel: ch, active: inbox.channel === ch, count: counts?.total ?? 0, unread: counts?.unread ?? 0, onClick: inbox.setChannel }, ch));
482
+ }), isMessagesMode ? (_jsxs("button", { type: "button", "aria-pressed": missedOnly, onClick: () => setMissedOnly((prev) => !prev), title: t("lifeopsInbox.missedTooltip", {
483
+ defaultValue: "Threads you have not replied to in 24h with priority ≥ 50",
484
+ }), className: [
485
+ "inline-flex items-center gap-1.5 rounded-full px-2.5 py-1 text-[11px] font-medium transition-colors",
486
+ missedOnly
487
+ ? "bg-amber-500/16 text-amber-300 ring-1 ring-amber-500/40"
488
+ : "bg-bg-muted/30 text-muted hover:text-txt",
489
+ ].join(" "), children: [_jsx(AlarmClock, { className: "h-3.5 w-3.5", "aria-hidden": true }), t("lifeopsInbox.missed", { defaultValue: "Missed" })] })) : null] }), showGmailAccountChips ? (_jsxs("div", { className: "flex flex-wrap items-center gap-2 border-b border-border/10 px-3 py-2", children: [_jsx(GmailAccountChip, { label: t("lifeopsInbox.allGmail", { defaultValue: "All Gmail" }), active: selectedGmailAccount === ALL_GMAIL_ACCOUNTS, onClick: () => setSelectedGmailAccount(ALL_GMAIL_ACCOUNTS) }), gmailAccountOptions.map((opt) => (_jsx(GmailAccountChip, { label: `Gmail · ${opt.label}`, active: selectedGmailAccount === opt.id, onClick: () => setSelectedGmailAccount(opt.id) }, opt.id)))] })) : null, inbox.error ? (_jsx("div", { className: "px-5 py-4 text-xs text-rose-300", children: inbox.error })) : inbox.loading && inbox.messages.length === 0 ? (_jsxs("div", { className: "flex items-center gap-2 px-5 py-8 text-xs text-muted", children: [_jsx(Spinner, { size: 14 }), t("lifeopsInbox.loading", { defaultValue: "Loading inbox…" })] })) : (_jsx("div", { className: "flex min-h-0 flex-1", children: compactLayout ? (selectedThread ? (_jsx(ReaderPane, { threadGroup: selectedThread, onReply: handleReply, onChat: handleChat, onBack: () => onSelect({ messageId: null }) })) : (_jsx(InboxListPane, { inbox: inbox, selectedMessageId: selectedMessageId, onSelect: onSelect, onReply: handleReply, emptyLabel: props.emptyLabel, groupedMode: true, visibleThreadGroups: inbox.threadGroups, showGmailAccountSubtitles: showGmailAccountSubtitles }))) : (_jsxs(_Fragment, { children: [_jsx("div", { className: "flex w-72 shrink-0 flex-col border-r border-border/12", children: _jsx(InboxListPane, { inbox: inbox, selectedMessageId: selectedMessageId, onSelect: onSelect, onReply: handleReply, emptyLabel: props.emptyLabel, groupedMode: true, visibleThreadGroups: inbox.threadGroups, showGmailAccountSubtitles: showGmailAccountSubtitles }) }), _jsx(ReaderPane, { threadGroup: selectedThread, onReply: handleReply, onChat: handleChat })] })) }))] }));
490
+ }
@@ -0,0 +1,12 @@
1
+ import { type JSX } from "react";
2
+ import type { LifeOpsPaymentSource } from "../lifeops/payment-types.js";
3
+ interface LifeOpsLinkBankButtonProps {
4
+ onLinked?: (source: LifeOpsPaymentSource) => void;
5
+ /** Optional override for the trigger label. Default: "Link bank with Plaid". */
6
+ label?: string;
7
+ /** Optional override for the disabled-state title. */
8
+ unavailableTitle?: string;
9
+ }
10
+ export declare function LifeOpsLinkBankButton({ onLinked, label, unavailableTitle, }: LifeOpsLinkBankButtonProps): JSX.Element;
11
+ export {};
12
+ //# sourceMappingURL=LifeOpsLinkBankButton.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LifeOpsLinkBankButton.d.ts","sourceRoot":"","sources":["../../../../../../../apps/app-lifeops/src/components/LifeOpsLinkBankButton.tsx"],"names":[],"mappings":"AAoBA,OAAO,EAAE,KAAK,GAAG,EAAoC,MAAM,OAAO,CAAC;AAEnE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AAExE,UAAU,0BAA0B;IAClC,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,oBAAoB,KAAK,IAAI,CAAC;IAClD,gFAAgF;IAChF,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,sDAAsD;IACtD,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,wBAAgB,qBAAqB,CAAC,EACpC,QAAQ,EACR,KAA8B,EAC9B,gBAAmH,GACpH,EAAE,0BAA0B,GAAG,GAAG,CAAC,OAAO,CAyG1C"}
@@ -0,0 +1,92 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * LifeOpsLinkBankButton — drives the Plaid Link flow for the Money page.
4
+ *
5
+ * Flow:
6
+ * 1. On mount of the modal, fetch a fresh `link_token` from Eliza Cloud via
7
+ * `client.createLifeOpsPlaidLinkToken()`.
8
+ * 2. Hand that token to `usePlaidLink` so the Plaid Link UI can drive the
9
+ * bank-selection / login / account-picker flow client-side.
10
+ * 3. On `onSuccess(public_token)`, exchange the token via
11
+ * `client.completeLifeOpsPlaidLink({ publicToken })`. Eliza Cloud
12
+ * exchanges the public_token for a long-lived `access_token`, never
13
+ * surfaces the secret to the browser, and we get back a fresh
14
+ * payment-source row.
15
+ * 4. Caller's `onLinked` runs the dashboard refresh.
16
+ *
17
+ * The Plaid `link_token` is short-lived; if the user opens the modal but
18
+ * cancels, we re-fetch on the next attempt. We do NOT persist link_tokens.
19
+ */
20
+ import { client, useApp } from "@elizaos/app-core";
21
+ import { Banknote, Loader2 } from "lucide-react";
22
+ import { useCallback, useEffect, useState } from "react";
23
+ import { usePlaidLink } from "react-plaid-link";
24
+ export function LifeOpsLinkBankButton({ onLinked, label = "Link bank with Plaid", unavailableTitle = "Plaid link is not available — Eliza Cloud not connected, or PLAID_* env vars not set in cloud.", }) {
25
+ const { elizaCloudConnected } = useApp();
26
+ const [linkToken, setLinkToken] = useState(null);
27
+ const [tokenError, setTokenError] = useState(null);
28
+ const [tokenLoading, setTokenLoading] = useState(false);
29
+ const [exchangeStatus, setExchangeStatus] = useState("idle");
30
+ const [exchangeError, setExchangeError] = useState(null);
31
+ const fetchLinkToken = useCallback(async () => {
32
+ setTokenLoading(true);
33
+ setTokenError(null);
34
+ try {
35
+ const data = await client.createLifeOpsPlaidLinkToken();
36
+ setLinkToken(data.linkToken);
37
+ }
38
+ catch (err) {
39
+ setTokenError(err instanceof Error ? err.message : String(err));
40
+ }
41
+ finally {
42
+ setTokenLoading(false);
43
+ }
44
+ }, []);
45
+ const onSuccess = useCallback(async (publicToken, _metadata) => {
46
+ setExchangeStatus("exchanging");
47
+ setExchangeError(null);
48
+ try {
49
+ const result = await client.completeLifeOpsPlaidLink({
50
+ publicToken,
51
+ });
52
+ setExchangeStatus("done");
53
+ // Burn the link_token — Plaid only allows it once, and we want a
54
+ // fresh one on the next attempt.
55
+ setLinkToken(null);
56
+ onLinked?.(result.source);
57
+ }
58
+ catch (err) {
59
+ setExchangeStatus("error");
60
+ setExchangeError(err instanceof Error ? err.message : String(err));
61
+ }
62
+ }, [onLinked]);
63
+ // We feed `usePlaidLink` an empty token when we don't have one yet —
64
+ // the hook tolerates this and just won't be `ready`. This keeps the
65
+ // hook unconditional (rules-of-hooks) and lets us drive opening on
66
+ // demand without two render passes.
67
+ const { open, ready } = usePlaidLink({
68
+ token: linkToken ?? "",
69
+ onSuccess,
70
+ });
71
+ // When we have a token AND Plaid Link is ready, open it automatically.
72
+ // The button click only triggers `fetchLinkToken`; `open()` runs in this
73
+ // effect once Link reports ready.
74
+ useEffect(() => {
75
+ if (linkToken && ready) {
76
+ open();
77
+ }
78
+ }, [linkToken, ready, open]);
79
+ const onClick = useCallback(() => {
80
+ setExchangeStatus("idle");
81
+ setExchangeError(null);
82
+ void fetchLinkToken();
83
+ }, [fetchLinkToken]);
84
+ const disabled = !elizaCloudConnected || tokenLoading || exchangeStatus === "exchanging";
85
+ return (_jsxs("div", { className: "flex flex-col gap-1", children: [_jsxs("button", { type: "button", onClick: onClick, disabled: disabled, title: !elizaCloudConnected ? unavailableTitle : undefined, className: "inline-flex items-center gap-1.5 rounded-md border border-border/30 bg-bg-muted/30 px-2.5 py-1 text-xs font-medium hover:bg-bg-muted/60 disabled:cursor-not-allowed disabled:opacity-50", children: [tokenLoading || exchangeStatus === "exchanging" ? (_jsx(Loader2, { className: "h-3.5 w-3.5 animate-spin", "aria-hidden": true })) : (_jsx(Banknote, { className: "h-3.5 w-3.5", "aria-hidden": true })), exchangeStatus === "exchanging"
86
+ ? "Linking…"
87
+ : exchangeStatus === "done"
88
+ ? "Linked"
89
+ : tokenLoading
90
+ ? "Preparing Plaid…"
91
+ : label] }), tokenError ? (_jsxs("span", { className: "text-[11px] text-rose-300", children: ["Plaid: ", tokenError] })) : null, exchangeError ? (_jsxs("span", { className: "text-[11px] text-rose-300", children: ["Exchange failed: ", exchangeError] })) : null] }));
92
+ }
@@ -0,0 +1,12 @@
1
+ import { type JSX } from "react";
2
+ import type { LifeOpsPaymentSource } from "../lifeops/payment-types.js";
3
+ interface LifeOpsLinkPaypalButtonProps {
4
+ onLinked?: (source: LifeOpsPaymentSource, capability: {
5
+ hasReporting: boolean;
6
+ hasIdentity: boolean;
7
+ }) => void;
8
+ label?: string;
9
+ }
10
+ export declare function LifeOpsLinkPaypalButton({ onLinked, label, }: LifeOpsLinkPaypalButtonProps): JSX.Element;
11
+ export {};
12
+ //# sourceMappingURL=LifeOpsLinkPaypalButton.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LifeOpsLinkPaypalButton.d.ts","sourceRoot":"","sources":["../../../../../../../apps/app-lifeops/src/components/LifeOpsLinkPaypalButton.tsx"],"names":[],"mappings":"AAoBA,OAAO,EAAE,KAAK,GAAG,EAA4C,MAAM,OAAO,CAAC;AAC3E,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AAExE,UAAU,4BAA4B;IACpC,QAAQ,CAAC,EAAE,CACT,MAAM,EAAE,oBAAoB,EAC5B,UAAU,EAAE;QAAE,YAAY,EAAE,OAAO,CAAC;QAAC,WAAW,EAAE,OAAO,CAAA;KAAE,KACxD,IAAI,CAAC;IACV,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAqBD,wBAAgB,uBAAuB,CAAC,EACtC,QAAQ,EACR,KAAqB,GACtB,EAAE,4BAA4B,GAAG,GAAG,CAAC,OAAO,CAwK5C"}