six-degrees 0.4.10 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (414) hide show
  1. package/.next/standalone/.next/BUILD_ID +1 -1
  2. package/.next/standalone/.next/app-path-routes-manifest.json +1 -0
  3. package/.next/standalone/.next/build-manifest.json +3 -3
  4. package/.next/standalone/.next/prerender-manifest.json +9 -9
  5. package/.next/standalone/.next/routes-manifest.json +6 -0
  6. package/.next/standalone/.next/server/app/_global-error.html +1 -1
  7. package/.next/standalone/.next/server/app/_global-error.rsc +1 -1
  8. package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  9. package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  10. package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  11. package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
  12. package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  13. package/.next/standalone/.next/server/app/_not-found.html +1 -1
  14. package/.next/standalone/.next/server/app/_not-found.rsc +13 -13
  15. package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +13 -13
  16. package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +12 -12
  17. package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  18. package/.next/standalone/.next/server/app/api/admin-delete/route.js +1 -1
  19. package/.next/standalone/.next/server/app/api/admin-delete/route.js.nft.json +1 -1
  20. package/.next/standalone/.next/server/app/api/admin-update/route.js +1 -1
  21. package/.next/standalone/.next/server/app/api/admin-update/route.js.nft.json +1 -1
  22. package/.next/standalone/.next/server/app/api/bridges/route.js +1 -1
  23. package/.next/standalone/.next/server/app/api/bridges/route.js.nft.json +1 -1
  24. package/.next/standalone/.next/server/app/api/bulk-import/route.js +1 -1
  25. package/.next/standalone/.next/server/app/api/bulk-import/route.js.nft.json +1 -1
  26. package/.next/standalone/.next/server/app/api/company-scores/legacy/route.js +1 -1
  27. package/.next/standalone/.next/server/app/api/company-scores/legacy/route.js.nft.json +1 -1
  28. package/.next/standalone/.next/server/app/api/company-scores/route.js +1 -1
  29. package/.next/standalone/.next/server/app/api/company-scores/route.js.nft.json +1 -1
  30. package/.next/standalone/.next/server/app/api/connections/route.js +1 -1
  31. package/.next/standalone/.next/server/app/api/connections/route.js.nft.json +1 -1
  32. package/.next/standalone/.next/server/app/api/data/export/route.js.nft.json +1 -1
  33. package/.next/standalone/.next/server/app/api/data/import/route.js.nft.json +1 -1
  34. package/.next/standalone/.next/server/app/api/data/restart/route.js.nft.json +1 -1
  35. package/.next/standalone/.next/server/app/api/data/reveal/route.js.nft.json +1 -1
  36. package/.next/standalone/.next/server/app/api/data/route.js.nft.json +1 -1
  37. package/.next/standalone/.next/server/app/api/delete-cluster/route.js +1 -1
  38. package/.next/standalone/.next/server/app/api/delete-cluster/route.js.nft.json +1 -1
  39. package/.next/standalone/.next/server/app/api/ingest/route.js +1 -1
  40. package/.next/standalone/.next/server/app/api/ingest/route.js.nft.json +1 -1
  41. package/.next/standalone/.next/server/app/api/network/route.js +1 -1
  42. package/.next/standalone/.next/server/app/api/network/route.js.nft.json +1 -1
  43. package/.next/standalone/.next/server/app/api/notifications/route.js +1 -1
  44. package/.next/standalone/.next/server/app/api/notifications/route.js.nft.json +1 -1
  45. package/.next/standalone/.next/server/app/api/outreach/route.js +1 -1
  46. package/.next/standalone/.next/server/app/api/outreach/route.js.nft.json +1 -1
  47. package/.next/standalone/.next/server/app/api/queue/route.js +1 -1
  48. package/.next/standalone/.next/server/app/api/queue/route.js.nft.json +1 -1
  49. package/.next/standalone/.next/server/app/api/scraper/route.js +3 -2
  50. package/.next/standalone/.next/server/app/api/scraper/route.js.nft.json +1 -1
  51. package/.next/standalone/.next/server/app/api/settings/route.js +1 -1
  52. package/.next/standalone/.next/server/app/api/settings/route.js.nft.json +1 -1
  53. package/.next/standalone/.next/server/app/api/settings/sector-preview/route.js +1 -1
  54. package/.next/standalone/.next/server/app/api/settings/sector-preview/route.js.nft.json +1 -1
  55. package/.next/standalone/.next/server/app/api/settings/sector-suggestions/route.js +1 -1
  56. package/.next/standalone/.next/server/app/api/settings/sector-suggestions/route.js.nft.json +1 -1
  57. package/.next/standalone/.next/server/app/api/setup-profile/route.js +1 -1
  58. package/.next/standalone/.next/server/app/api/setup-profile/route.js.nft.json +1 -1
  59. package/.next/standalone/.next/server/app/api/social/crm/route/app-paths-manifest.json +3 -0
  60. package/.next/standalone/.next/server/app/api/social/crm/route/build-manifest.json +12 -0
  61. package/.next/standalone/.next/server/app/api/social/crm/route/server-reference-manifest.json +4 -0
  62. package/.next/standalone/.next/server/app/api/social/crm/route.js +10 -0
  63. package/.next/standalone/.next/server/app/api/social/crm/route.js.map +5 -0
  64. package/.next/standalone/.next/server/app/api/social/crm/route.js.nft.json +1 -0
  65. package/.next/standalone/.next/server/app/api/social/crm/route_client-reference-manifest.js +3 -0
  66. package/.next/standalone/.next/server/app/api/social/messages/route.js +4 -3
  67. package/.next/standalone/.next/server/app/api/social/messages/route.js.nft.json +1 -1
  68. package/.next/standalone/.next/server/app/api/social/route.js +4 -3
  69. package/.next/standalone/.next/server/app/api/social/route.js.nft.json +1 -1
  70. package/.next/standalone/.next/server/app/api/unlock/route.js +1 -1
  71. package/.next/standalone/.next/server/app/api/unlock/route.js.nft.json +1 -1
  72. package/.next/standalone/.next/server/app/api/update/route.js.nft.json +1 -1
  73. package/.next/standalone/.next/server/app/api/update-images/route.js +1 -1
  74. package/.next/standalone/.next/server/app/api/update-images/route.js.nft.json +1 -1
  75. package/.next/standalone/.next/server/app/api/users/route.js +1 -1
  76. package/.next/standalone/.next/server/app/api/users/route.js.nft.json +1 -1
  77. package/.next/standalone/.next/server/app/avatars/[file]/route.js.nft.json +1 -1
  78. package/.next/standalone/.next/server/app/import/page.js.nft.json +1 -1
  79. package/.next/standalone/.next/server/app/import/page_client-reference-manifest.js +1 -1
  80. package/.next/standalone/.next/server/app/import.html +1 -1
  81. package/.next/standalone/.next/server/app/import.rsc +15 -15
  82. package/.next/standalone/.next/server/app/import.segments/_full.segment.rsc +15 -15
  83. package/.next/standalone/.next/server/app/import.segments/_tree.segment.rsc +2 -2
  84. package/.next/standalone/.next/server/app/import.segments/import/__PAGE__.segment.rsc +14 -14
  85. package/.next/standalone/.next/server/app/index.html +1 -1
  86. package/.next/standalone/.next/server/app/index.rsc +15 -15
  87. package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +14 -14
  88. package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +15 -15
  89. package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  90. package/.next/standalone/.next/server/app/launch/page.js.nft.json +1 -1
  91. package/.next/standalone/.next/server/app/launch/page_client-reference-manifest.js +1 -1
  92. package/.next/standalone/.next/server/app/launch.html +2 -2
  93. package/.next/standalone/.next/server/app/launch.rsc +15 -15
  94. package/.next/standalone/.next/server/app/launch.segments/_full.segment.rsc +15 -15
  95. package/.next/standalone/.next/server/app/launch.segments/_tree.segment.rsc +2 -2
  96. package/.next/standalone/.next/server/app/launch.segments/launch/__PAGE__.segment.rsc +14 -14
  97. package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
  98. package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  99. package/.next/standalone/.next/server/app/paths/page.js.nft.json +1 -1
  100. package/.next/standalone/.next/server/app/paths/page_client-reference-manifest.js +1 -1
  101. package/.next/standalone/.next/server/app/paths.html +1 -1
  102. package/.next/standalone/.next/server/app/paths.rsc +15 -15
  103. package/.next/standalone/.next/server/app/paths.segments/_full.segment.rsc +15 -15
  104. package/.next/standalone/.next/server/app/paths.segments/_tree.segment.rsc +2 -2
  105. package/.next/standalone/.next/server/app/paths.segments/paths/__PAGE__.segment.rsc +14 -14
  106. package/.next/standalone/.next/server/app/profile/page.js.nft.json +1 -1
  107. package/.next/standalone/.next/server/app/profile/page_client-reference-manifest.js +1 -1
  108. package/.next/standalone/.next/server/app/profile.html +1 -1
  109. package/.next/standalone/.next/server/app/profile.rsc +15 -15
  110. package/.next/standalone/.next/server/app/profile.segments/_full.segment.rsc +15 -15
  111. package/.next/standalone/.next/server/app/profile.segments/_tree.segment.rsc +2 -2
  112. package/.next/standalone/.next/server/app/profile.segments/profile/__PAGE__.segment.rsc +14 -14
  113. package/.next/standalone/.next/server/app/queue/page.js.nft.json +1 -1
  114. package/.next/standalone/.next/server/app/queue/page_client-reference-manifest.js +1 -1
  115. package/.next/standalone/.next/server/app/queue.html +1 -1
  116. package/.next/standalone/.next/server/app/queue.rsc +15 -15
  117. package/.next/standalone/.next/server/app/queue.segments/_full.segment.rsc +15 -15
  118. package/.next/standalone/.next/server/app/queue.segments/_tree.segment.rsc +2 -2
  119. package/.next/standalone/.next/server/app/queue.segments/queue/__PAGE__.segment.rsc +14 -14
  120. package/.next/standalone/.next/server/app/scores/page.js.nft.json +1 -1
  121. package/.next/standalone/.next/server/app/scores/page_client-reference-manifest.js +1 -1
  122. package/.next/standalone/.next/server/app/scores.html +1 -1
  123. package/.next/standalone/.next/server/app/scores.rsc +15 -15
  124. package/.next/standalone/.next/server/app/scores.segments/_full.segment.rsc +15 -15
  125. package/.next/standalone/.next/server/app/scores.segments/_tree.segment.rsc +2 -2
  126. package/.next/standalone/.next/server/app/scores.segments/scores/__PAGE__.segment.rsc +14 -14
  127. package/.next/standalone/.next/server/app/settings/page.js.nft.json +1 -1
  128. package/.next/standalone/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  129. package/.next/standalone/.next/server/app/settings.html +1 -1
  130. package/.next/standalone/.next/server/app/settings.rsc +15 -15
  131. package/.next/standalone/.next/server/app/settings.segments/_full.segment.rsc +15 -15
  132. package/.next/standalone/.next/server/app/settings.segments/_tree.segment.rsc +2 -2
  133. package/.next/standalone/.next/server/app/settings.segments/settings/__PAGE__.segment.rsc +14 -14
  134. package/.next/standalone/.next/server/app/setup/page.js.nft.json +1 -1
  135. package/.next/standalone/.next/server/app/setup/page_client-reference-manifest.js +1 -1
  136. package/.next/standalone/.next/server/app/setup.html +1 -1
  137. package/.next/standalone/.next/server/app/setup.rsc +15 -15
  138. package/.next/standalone/.next/server/app/setup.segments/_full.segment.rsc +15 -15
  139. package/.next/standalone/.next/server/app/setup.segments/_tree.segment.rsc +2 -2
  140. package/.next/standalone/.next/server/app/setup.segments/setup/__PAGE__.segment.rsc +14 -14
  141. package/.next/standalone/.next/server/app/social/page.js.nft.json +1 -1
  142. package/.next/standalone/.next/server/app/social/page_client-reference-manifest.js +1 -1
  143. package/.next/standalone/.next/server/app/social.html +1 -1
  144. package/.next/standalone/.next/server/app/social.rsc +15 -15
  145. package/.next/standalone/.next/server/app/social.segments/_full.segment.rsc +15 -15
  146. package/.next/standalone/.next/server/app/social.segments/_tree.segment.rsc +2 -2
  147. package/.next/standalone/.next/server/app/social.segments/social/__PAGE__.segment.rsc +14 -14
  148. package/.next/standalone/.next/server/app-paths-manifest.json +1 -0
  149. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0bat8nx._.js → [root-of-the-server]__05_3-22._.js} +2 -2
  150. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0t3v1a1._.js +1 -1
  151. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__06mi9k2._.js → [root-of-the-server]__13dx4sd._.js} +7 -2
  152. package/.next/standalone/.next/server/chunks/[root-of-the-server]__15wc9e-._.js +3 -0
  153. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1kbl21f._.js +1 -1
  154. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1tir0vc._.js +8 -0
  155. package/.next/standalone/.next/server/chunks/[root-of-the-server]__20rwfdw._.js +8 -0
  156. package/.next/standalone/.next/server/chunks/_072jl0y._.js +1 -1
  157. package/.next/standalone/.next/server/chunks/_0ea9qwy._.js +1 -1
  158. package/.next/standalone/.next/server/chunks/_1_fynia._.js +7 -7
  159. package/.next/standalone/.next/server/chunks/_1itd1o_._.js +1 -1
  160. package/.next/standalone/.next/server/chunks/_1iwkh8t._.js +1 -1
  161. package/.next/standalone/.next/server/chunks/_1oqfuh8._.js +3 -0
  162. package/.next/standalone/.next/server/chunks/_1xh07bp._.js +1 -1
  163. package/.next/standalone/.next/server/chunks/_next-internal_server_app_api_social_crm_route_actions_0d1aflv.js +3 -0
  164. package/.next/standalone/.next/server/chunks/lib_0851sk0._.js +8 -0
  165. package/.next/standalone/.next/server/chunks/lib_1q7p2ro._.js +8 -0
  166. package/.next/standalone/.next/server/chunks/lib_1r6l86l._.js +8 -0
  167. package/.next/standalone/.next/server/chunks/lib_db-client_15le7cw.js +8 -8
  168. package/.next/standalone/.next/server/chunks/lib_linkedin-export_12imqxg.js +3 -0
  169. package/.next/standalone/.next/server/chunks/lib_rpc_04s8mzr.js +3 -0
  170. package/.next/standalone/.next/server/chunks/lib_rpc_053z7as.js +3 -0
  171. package/.next/standalone/.next/server/chunks/lib_rpc_0b_-2te.js +3 -0
  172. package/.next/standalone/.next/server/chunks/lib_rpc_0dhbxdm.js +3 -0
  173. package/.next/standalone/.next/server/chunks/lib_rpc_0dxlk31.js +3 -0
  174. package/.next/standalone/.next/server/chunks/lib_rpc_0f8b-qc.js +3 -0
  175. package/.next/standalone/.next/server/chunks/lib_rpc_0pzie7_.js +3 -0
  176. package/.next/standalone/.next/server/chunks/lib_rpc_0r76fq_.js +3 -0
  177. package/.next/standalone/.next/server/chunks/lib_rpc_0ut7pjp.js +3 -0
  178. package/.next/standalone/.next/server/chunks/lib_rpc_15zyqaz.js +3 -0
  179. package/.next/standalone/.next/server/chunks/lib_rpc_18zpjtq.js +3 -0
  180. package/.next/standalone/.next/server/chunks/lib_rpc_1a8xkiz.js +3 -0
  181. package/.next/standalone/.next/server/chunks/lib_rpc_1e_55xo.js +3 -0
  182. package/.next/standalone/.next/server/chunks/lib_rpc_1oxfd1_.js +3 -0
  183. package/.next/standalone/.next/server/chunks/lib_rpc_1s0lod0.js +3 -0
  184. package/.next/standalone/.next/server/chunks/lib_scoring_1-e4ur0.js +1 -1
  185. package/.next/standalone/.next/server/chunks/ssr/_01kacen._.js +3 -0
  186. package/.next/standalone/.next/server/chunks/ssr/_03l7vge._.js +3 -0
  187. package/.next/standalone/.next/server/chunks/ssr/{_1ap1tu_._.js → _0gtv9jb._.js} +2 -2
  188. package/.next/standalone/.next/server/chunks/ssr/_0jdkqgi._.js +3 -0
  189. package/.next/standalone/.next/server/chunks/ssr/_0xi-fbs._.js +3 -0
  190. package/.next/standalone/.next/server/chunks/ssr/_0xmwmfv._.js +3 -0
  191. package/.next/standalone/.next/server/chunks/ssr/_1-b9hj7._.js +9 -0
  192. package/.next/standalone/.next/server/chunks/ssr/_112ewem._.js +3 -0
  193. package/.next/standalone/.next/server/chunks/ssr/_16ab63q._.js +3 -0
  194. package/.next/standalone/.next/server/chunks/ssr/_1fxm0-s._.js +3 -0
  195. package/.next/standalone/.next/server/chunks/ssr/_1ra1d3r._.js +9 -0
  196. package/.next/standalone/.next/server/chunks/ssr/app_141w189._.js +3 -0
  197. package/.next/standalone/.next/server/chunks/ssr/app_page_1fazqbf.js +22 -10
  198. package/.next/standalone/.next/server/chunks/ssr/app_paths_page_1ol90m7.js +2 -2
  199. package/.next/standalone/.next/server/chunks/ssr/app_profile_page_0u3-l5y.js +1 -1
  200. package/.next/standalone/.next/server/chunks/ssr/app_queue_page_0c6znd6.js +6 -5
  201. package/.next/standalone/.next/server/chunks/ssr/app_settings_page_1f8mjj_.js +1 -1
  202. package/.next/standalone/.next/server/chunks/ssr/app_setup_page_1-282zx.js +19 -3
  203. package/.next/standalone/.next/server/chunks/ssr/app_social_page_0h7zxda.js +1 -1
  204. package/.next/standalone/.next/server/chunks/ssr/lib_0i8v-s3._.js +1 -1
  205. package/.next/standalone/.next/server/chunks/ssr/lib_13rapwj._.js +3 -0
  206. package/.next/standalone/.next/server/chunks/ssr/lib_galaxy-lab_108_lu-.js +3 -0
  207. package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
  208. package/.next/standalone/.next/server/middleware-manifest.json +5 -5
  209. package/.next/standalone/.next/server/pages/404.html +1 -1
  210. package/.next/standalone/.next/server/pages/500.html +1 -1
  211. package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
  212. package/.next/standalone/.next/server/server-reference-manifest.json +1 -1
  213. package/.next/standalone/.next/static/chunks/03bziijw5srol.js +1 -0
  214. package/.next/standalone/.next/static/chunks/0bgq8wmopl_ig.js +7 -0
  215. package/.next/standalone/.next/static/chunks/0ezus6p2eb-vg.js +1 -0
  216. package/.next/standalone/.next/static/chunks/0g_fy2034bp0x.js +34 -0
  217. package/.next/standalone/.next/static/chunks/0q5dru4q6gxl5.js +22 -0
  218. package/.next/standalone/.next/static/chunks/0w-byiwb2zax8.js +2 -0
  219. package/.next/standalone/.next/static/chunks/1g7jcovdejc9-.js +1 -0
  220. package/.next/standalone/.next/static/chunks/1i51yylxdzht1.js +1 -0
  221. package/.next/{static/chunks/1xptlvtcz6654.css → standalone/.next/static/chunks/1x_ysly42-teq.css} +1 -1
  222. package/.next/standalone/.next/static/chunks/1zf8swaasjf1e.js +43 -0
  223. package/.next/standalone/.next/static/chunks/2rqtpccruavf2.js +1 -0
  224. package/.next/standalone/.next/static/chunks/2wgz9ietkqqe6.js +1 -0
  225. package/.next/standalone/.next/static/chunks/30x3oq_st7a5r.js +1 -0
  226. package/.next/standalone/.next/static/chunks/355ryio1b7sm1.js +1 -0
  227. package/.next/standalone/.next/static/chunks/3ap5hgma7n0o7.js +1 -0
  228. package/.next/standalone/.next/static/chunks/3fci6qi39-1n5.js +1 -0
  229. package/.next/standalone/.next/static/chunks/{24tvdun1cb3g1.js → 3sowbvbaj1s6n.js} +1 -1
  230. package/.next/standalone/CHANGELOG.md +235 -0
  231. package/.next/standalone/app/api/data/export/route.js +2 -1
  232. package/.next/standalone/app/api/ingest/route.js +40 -1
  233. package/.next/standalone/app/api/scraper/route.js +12 -2
  234. package/.next/standalone/app/api/settings/sector-preview/route.js +2 -1
  235. package/.next/standalone/app/api/social/crm/route.js +37 -0
  236. package/.next/standalone/app/api/social/messages/route.js +44 -8
  237. package/.next/standalone/app/api/social/route.js +80 -22
  238. package/.next/standalone/app/components/AppTabs.js +59 -0
  239. package/.next/standalone/app/components/ChainView.js +303 -89
  240. package/.next/standalone/app/components/CirclePanel.js +162 -0
  241. package/.next/standalone/app/components/CompanyScores.js +2 -2
  242. package/.next/standalone/app/components/FieldStep.js +4 -4
  243. package/.next/standalone/app/components/FilterPanel.js +121 -183
  244. package/.next/standalone/app/components/ForceGraph.js +41 -3
  245. package/.next/standalone/app/components/GalaxyLab.js +26 -31
  246. package/.next/standalone/app/components/LegacyScoresCard.js +2 -2
  247. package/.next/standalone/app/components/MapControls.js +154 -0
  248. package/.next/standalone/app/components/NoteDetail.js +118 -0
  249. package/.next/standalone/app/components/PathsAnalyzer.js +147 -110
  250. package/.next/standalone/app/components/PeopleMap.js +308 -0
  251. package/.next/standalone/app/components/ScanRadar.js +129 -0
  252. package/.next/standalone/app/components/ScanStatusBar.js +72 -27
  253. package/.next/standalone/app/components/SeparationView.js +626 -118
  254. package/.next/standalone/app/components/Sidebar.js +28 -2
  255. package/.next/standalone/app/components/settings/DataSection.js +12 -3
  256. package/.next/standalone/app/components/settings/NetworkHealthSection.js +1 -1
  257. package/.next/standalone/app/components/settings/SectorSection.js +2 -2
  258. package/.next/standalone/app/components/settings/TierSection.js +1 -1
  259. package/.next/standalone/app/components/settings/TitleSection.js +149 -0
  260. package/.next/standalone/app/components/views.js +15 -14
  261. package/.next/standalone/app/globals.css +11 -0
  262. package/.next/standalone/app/page.js +89 -156
  263. package/.next/standalone/app/paths/page.js +39 -27
  264. package/.next/standalone/app/profile/page.js +33 -5
  265. package/.next/standalone/app/queue/page.js +56 -34
  266. package/.next/standalone/app/scores/page.js +16 -66
  267. package/.next/standalone/app/settings/page.js +27 -13
  268. package/.next/standalone/app/setup/page.js +298 -205
  269. package/.next/standalone/app/social/Crm.js +683 -0
  270. package/.next/standalone/app/social/SocialHub.js +374 -0
  271. package/.next/standalone/app/social/Thread.js +90 -0
  272. package/.next/standalone/app/social/page.js +10 -351
  273. package/.next/standalone/lib/chain-layout.js +105 -35
  274. package/.next/standalone/lib/data-export.js +10 -4
  275. package/.next/standalone/lib/data-folder.js +9 -0
  276. package/.next/standalone/lib/data-import.js +13 -3
  277. package/.next/standalone/lib/degrees.js +0 -6
  278. package/.next/standalone/lib/dot-rings.js +25 -0
  279. package/.next/standalone/lib/galaxy-lab.js +65 -6
  280. package/.next/standalone/lib/ingest.js +2 -0
  281. package/.next/standalone/lib/island.js +40 -0
  282. package/.next/standalone/lib/linkedin-export.js +267 -46
  283. package/.next/standalone/lib/linkedin-limits.js +6 -2
  284. package/.next/standalone/lib/map-heat.js +20 -0
  285. package/.next/standalone/lib/network-health.js +1 -1
  286. package/.next/standalone/lib/notifications.js +121 -0
  287. package/.next/standalone/lib/people-map.js +82 -0
  288. package/.next/standalone/lib/rarity.js +45 -3
  289. package/.next/standalone/lib/reach.js +7 -6
  290. package/.next/standalone/lib/rpc.js +15 -4
  291. package/.next/standalone/lib/scan-pace.js +46 -0
  292. package/.next/standalone/lib/scoring.js +39 -8
  293. package/.next/standalone/lib/scraper-client.js +19 -6
  294. package/.next/standalone/lib/sector-focus.js +4 -4
  295. package/.next/standalone/lib/separation.js +137 -10
  296. package/.next/standalone/lib/settings-effects.js +20 -11
  297. package/.next/standalone/lib/settings.js +3 -0
  298. package/.next/standalone/lib/social-crm.js +415 -0
  299. package/.next/standalone/lib/social-store.js +13 -6
  300. package/.next/standalone/lib/tier-grid.js +99 -0
  301. package/.next/standalone/lib/title-ranking.js +83 -0
  302. package/.next/standalone/package-lock.json +2 -2
  303. package/.next/standalone/package.json +1 -1
  304. package/.next/standalone/scripts/scrape.py +177 -35
  305. package/.next/static/chunks/03bziijw5srol.js +1 -0
  306. package/.next/static/chunks/0bgq8wmopl_ig.js +7 -0
  307. package/.next/static/chunks/0ezus6p2eb-vg.js +1 -0
  308. package/.next/static/chunks/0g_fy2034bp0x.js +34 -0
  309. package/.next/static/chunks/0q5dru4q6gxl5.js +22 -0
  310. package/.next/static/chunks/0w-byiwb2zax8.js +2 -0
  311. package/.next/static/chunks/1g7jcovdejc9-.js +1 -0
  312. package/.next/static/chunks/1i51yylxdzht1.js +1 -0
  313. package/.next/{standalone/.next/static/chunks/1xptlvtcz6654.css → static/chunks/1x_ysly42-teq.css} +1 -1
  314. package/.next/static/chunks/1zf8swaasjf1e.js +43 -0
  315. package/.next/static/chunks/2rqtpccruavf2.js +1 -0
  316. package/.next/static/chunks/2wgz9ietkqqe6.js +1 -0
  317. package/.next/static/chunks/30x3oq_st7a5r.js +1 -0
  318. package/.next/static/chunks/355ryio1b7sm1.js +1 -0
  319. package/.next/static/chunks/3ap5hgma7n0o7.js +1 -0
  320. package/.next/static/chunks/3fci6qi39-1n5.js +1 -0
  321. package/.next/static/chunks/{24tvdun1cb3g1.js → 3sowbvbaj1s6n.js} +1 -1
  322. package/CHANGELOG.md +235 -0
  323. package/lib/chain-layout.js +105 -35
  324. package/lib/data-export.js +10 -4
  325. package/lib/data-folder.js +9 -0
  326. package/lib/data-import.js +13 -3
  327. package/lib/degrees.js +0 -6
  328. package/lib/dot-rings.js +25 -0
  329. package/lib/galaxy-lab.js +65 -6
  330. package/lib/ingest.js +2 -0
  331. package/lib/island.js +40 -0
  332. package/lib/linkedin-export.js +267 -46
  333. package/lib/linkedin-limits.js +6 -2
  334. package/lib/map-heat.js +20 -0
  335. package/lib/network-health.js +1 -1
  336. package/lib/notifications.js +121 -0
  337. package/lib/people-map.js +82 -0
  338. package/lib/rarity.js +45 -3
  339. package/lib/reach.js +7 -6
  340. package/lib/rpc.js +15 -4
  341. package/lib/scan-pace.js +46 -0
  342. package/lib/scoring.js +39 -8
  343. package/lib/scraper-client.js +19 -6
  344. package/lib/sector-focus.js +4 -4
  345. package/lib/separation.js +137 -10
  346. package/lib/settings-effects.js +20 -11
  347. package/lib/settings.js +3 -0
  348. package/lib/social-crm.js +415 -0
  349. package/lib/social-store.js +13 -6
  350. package/lib/tier-grid.js +99 -0
  351. package/lib/title-ranking.js +83 -0
  352. package/package.json +1 -1
  353. package/scripts/scrape.py +177 -35
  354. package/.next/standalone/.next/server/chunks/[root-of-the-server]__08f5awg._.js +0 -8
  355. package/.next/standalone/.next/server/chunks/[root-of-the-server]__17hjtzl._.js +0 -8
  356. package/.next/standalone/.next/server/chunks/[root-of-the-server]__20tmem7._.js +0 -3
  357. package/.next/standalone/.next/server/chunks/lib_18cr1gi._.js +0 -8
  358. package/.next/standalone/.next/server/chunks/lib_rpc_03i8xef.js +0 -3
  359. package/.next/standalone/.next/server/chunks/lib_rpc_08fc3rp.js +0 -3
  360. package/.next/standalone/.next/server/chunks/lib_rpc_08r2t_q.js +0 -3
  361. package/.next/standalone/.next/server/chunks/lib_rpc_0btpv1t.js +0 -3
  362. package/.next/standalone/.next/server/chunks/lib_rpc_0c9d9ti.js +0 -8
  363. package/.next/standalone/.next/server/chunks/lib_rpc_0cm2ev3.js +0 -3
  364. package/.next/standalone/.next/server/chunks/lib_rpc_0iovswz.js +0 -3
  365. package/.next/standalone/.next/server/chunks/lib_rpc_0is8flp.js +0 -3
  366. package/.next/standalone/.next/server/chunks/lib_rpc_0v8ajtd.js +0 -3
  367. package/.next/standalone/.next/server/chunks/lib_rpc_0xgmvvt.js +0 -3
  368. package/.next/standalone/.next/server/chunks/lib_rpc_144mzbf.js +0 -3
  369. package/.next/standalone/.next/server/chunks/lib_rpc_1bggi0n.js +0 -3
  370. package/.next/standalone/.next/server/chunks/lib_rpc_1dkcu9f.js +0 -3
  371. package/.next/standalone/.next/server/chunks/lib_rpc_1o0jb6w.js +0 -3
  372. package/.next/standalone/.next/server/chunks/ssr/_0c_y09g._.js +0 -3
  373. package/.next/standalone/.next/server/chunks/ssr/_0ezj4e0._.js +0 -3
  374. package/.next/standalone/.next/server/chunks/ssr/_0j4-lc3._.js +0 -3
  375. package/.next/standalone/.next/server/chunks/ssr/_0l8cxmo._.js +0 -3
  376. package/.next/standalone/.next/server/chunks/ssr/_0xkur4p._.js +0 -3
  377. package/.next/standalone/.next/server/chunks/ssr/_14iv_zy._.js +0 -3
  378. package/.next/standalone/.next/server/chunks/ssr/_15x6q32._.js +0 -3
  379. package/.next/standalone/.next/server/chunks/ssr/_1errbuf._.js +0 -3
  380. package/.next/standalone/.next/server/chunks/ssr/_212xw8q._.js +0 -3
  381. package/.next/standalone/.next/server/chunks/ssr/app_components_19c2d9c._.js +0 -3
  382. package/.next/standalone/.next/server/chunks/ssr/app_scores_page_09wepvv.js +0 -3
  383. package/.next/standalone/.next/static/chunks/07dyt47mp4s9-.js +0 -1
  384. package/.next/standalone/.next/static/chunks/0a5iadnmt9u9k.js +0 -1
  385. package/.next/standalone/.next/static/chunks/160wjb73ddwqd.js +0 -6
  386. package/.next/standalone/.next/static/chunks/1c_n0973t07l0.js +0 -2
  387. package/.next/standalone/.next/static/chunks/1e1n0kkz-psdb.js +0 -1
  388. package/.next/standalone/.next/static/chunks/206rm6j42gl_a.js +0 -1
  389. package/.next/standalone/.next/static/chunks/236f0jtp68j_h.js +0 -1
  390. package/.next/standalone/.next/static/chunks/2coxzjm_k6ilr.js +0 -1
  391. package/.next/standalone/.next/static/chunks/2kfhc58df2xyo.js +0 -1
  392. package/.next/standalone/.next/static/chunks/2pw6_osz1n_te.js +0 -1
  393. package/.next/standalone/.next/static/chunks/39hr8d-b2ibtw.js +0 -31
  394. package/.next/standalone/.next/static/chunks/3zw8bv7-mc4n6.js +0 -33
  395. package/.next/standalone/app/components/OrbitGraph.js +0 -737
  396. package/.next/standalone/app/social/Conversations.js +0 -246
  397. package/.next/static/chunks/07dyt47mp4s9-.js +0 -1
  398. package/.next/static/chunks/0a5iadnmt9u9k.js +0 -1
  399. package/.next/static/chunks/160wjb73ddwqd.js +0 -6
  400. package/.next/static/chunks/1c_n0973t07l0.js +0 -2
  401. package/.next/static/chunks/1e1n0kkz-psdb.js +0 -1
  402. package/.next/static/chunks/206rm6j42gl_a.js +0 -1
  403. package/.next/static/chunks/236f0jtp68j_h.js +0 -1
  404. package/.next/static/chunks/2coxzjm_k6ilr.js +0 -1
  405. package/.next/static/chunks/2kfhc58df2xyo.js +0 -1
  406. package/.next/static/chunks/2pw6_osz1n_te.js +0 -1
  407. package/.next/static/chunks/39hr8d-b2ibtw.js +0 -31
  408. package/.next/static/chunks/3zw8bv7-mc4n6.js +0 -33
  409. /package/.next/standalone/.next/static/{7zWJnSkfSoRhRsqY6ZKtD → I-cj79yE2IZ8taX2dZGp3}/_buildManifest.js +0 -0
  410. /package/.next/standalone/.next/static/{7zWJnSkfSoRhRsqY6ZKtD → I-cj79yE2IZ8taX2dZGp3}/_clientMiddlewareManifest.js +0 -0
  411. /package/.next/standalone/.next/static/{7zWJnSkfSoRhRsqY6ZKtD → I-cj79yE2IZ8taX2dZGp3}/_ssgManifest.js +0 -0
  412. /package/.next/static/{7zWJnSkfSoRhRsqY6ZKtD → I-cj79yE2IZ8taX2dZGp3}/_buildManifest.js +0 -0
  413. /package/.next/static/{7zWJnSkfSoRhRsqY6ZKtD → I-cj79yE2IZ8taX2dZGp3}/_clientMiddlewareManifest.js +0 -0
  414. /package/.next/static/{7zWJnSkfSoRhRsqY6ZKtD → I-cj79yE2IZ8taX2dZGp3}/_ssgManifest.js +0 -0
@@ -6,6 +6,241 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.5.0] - 2026-10-02
10
+
11
+ ### Added
12
+ - **Rank titles your way** (Settings → Scores → Titles). Set how much each kind of title counts, out
13
+ of 10, or start from a preset: Founders first, Investors, or A role I'm looking for. Add the roles
14
+ you're after ("account executive", "head of growth", "recruiter") with their own points, and
15
+ everyone whose title has those words rises, whatever their level: for outbound to one role, or
16
+ hiring for it. Saving rescores everyone and says how many people changed tier, and a card's score
17
+ says "your ranking" where it moved a title. Nothing set scores exactly as before.
18
+
19
+ ### Changed
20
+ - **The Scan page is a launch, not a manual.** It opens with what the scanner is and what it never does
21
+ (never posts or messages anyone, never sees your password, nothing leaves this Mac, stops when you
22
+ say), then the four steps on a rail: Get ready, Sign in, Who you know, Who they know. The one
23
+ you're on pulses, with one big button that does it and how long it takes; any other opens with a
24
+ tap. While it runs, "Your scanner is working" and Stop sit right under the step. How many people
25
+ this round and who first are beside the radar; the daily budget, how much of each list, the hidden
26
+ window, retrying hidden lists and paused lists are in Fine-tune, closed until you want them. The
27
+ log is one tap away, and opens by itself when a run stops badly. Scan has the same header as every
28
+ page now.
29
+ - **Paths reads like Network Circle.** Its notch is now Map and Companies: companies count for more
30
+ than industries, and an industry opens from a company's card (its industry is a link there). Whether
31
+ the map's bubbles are companies or your connections (what was the People tab) is a switch at the
32
+ top of the Filters panel. Filters use the same tiers × degrees grid as Network Circle. Colour is by
33
+ sector or by **Heat**: for companies, how strong your people there are (the average power of the
34
+ five strongest); for people, how strong their cluster is, person for person; size still says how
35
+ many. And **Physics**: switch it to Live to drag bubbles (the ones joined to them follow) and change
36
+ Spread, Pull to their group and Pull along lines. Still, as before, is the default. Old links to
37
+ the People or Industries tab still land on the map.
38
+ - **Heat: power as a thermal map.** A new Colour by in Network Circle's Physics: every dot coloured
39
+ from cold violet through red and orange to white hot by where their power score ranks among the
40
+ people showing, with a soft glow behind the hotter ones that pools where powerful people cluster,
41
+ like a thermal camera. Works with every layout, the replay and the filter grid.
42
+ - **Separation all the way right counts every mutual, scanned or not.** It aims at whoever you share
43
+ the most mutual connections with, even when most of them are in circles you haven't scanned: an
44
+ unscanned mutual is still a likely introduction, so it no longer holds someone back. The ones the
45
+ app can draw are lines as before; the rest are grey dots, each with a faint line in, with "+37 more
46
+ mutuals, in circles not scanned yet". The person glows less the less of it is scanned. The Next
47
+ names are in the same order and say their mutual counts. Narrow the tiers (S only, say) first to
48
+ aim at the strongest.
49
+ - **The right panel follows the circle you're in.** Open someone's circle in Bridge Chains and the
50
+ panel is about them: how many people are in their circle, tier chips to see just one tier, a
51
+ search inside it, the people you added from it (with Scan… for the ones ready), then everyone
52
+ else in it, strongest first. Click anyone for their card.
53
+ - **Orbit is a layout of the Galaxy now**, in Physics next to Rings and Clusters, not a separate
54
+ view: every tier held on its own orbit, S nearest you, and each connection's circle tucked in
55
+ behind them. It's still the Galaxy, so you can drag dots and move every slider on it.
56
+ - **Light up a branch on hover starts off.** It dimmed and relit thousands of dots on every hover,
57
+ which flickered; it's still in Physics if you want it, and moving from one dot to the next no
58
+ longer flashes the whole Galaxy in between.
59
+ - **Social is part of Outlink.** Outlink has the same header as every page, and its views sit in
60
+ the notch: Circles, To add, Pending, and Messages & follow-ups, which is the whole Social tab (your
61
+ LinkedIn export, the live messages sync, the CRM with its inbox, follow-ups, pipeline, sent and
62
+ received). Adding people and keeping track of them are now one place. The Social tab is gone from
63
+ the header, and old links to it land on Messages & follow-ups.
64
+ - **Notifications open.** Click one and it takes over the right panel with the detail: who it is
65
+ about, their tier and score, who you met them through, and, for an S or A, that they're a
66
+ valuable person to know; with buttons to open their card, their circle in Bridge Chains, or
67
+ them in Separation. A notification naming several people lists them all.
68
+ - **New notifications:** someone you met through a bridge added you back ("Ana added you back,
69
+ through Yuki"); a circle scan found people at top companies (a company score of 8 or more), named
70
+ in one notification; and a scan finished ("Scan done: Yuki's circle", "Check for new: done").
71
+ Accepted requests and high-value new connections now say who they are about, and older ones are
72
+ matched by the name in their title.
73
+ - **Paths has the same header as the map.** The app's tabs stay at the top, with Paths lit, and Map,
74
+ People, Industries and Companies sit in the notch under them, as the map page's views do; a tab
75
+ for Network Circle, Degrees or Separation takes you straight to it. Paths' filters moved into a
76
+ Filters panel on the left, like the map page's, with new ways to read the map: what a bubble's
77
+ size means (your people there, the S and A among them, or the ones you already know; on People,
78
+ cluster value, size or S tier) and which names show (the biggest, all, or none).
79
+ - **All the way to easy, Separation aims at whoever the most of your connections lead to**, with
80
+ every one of those lines drawn, the strongest first among equals; the "Next" names are the
81
+ runners-up by the same measure. It used to aim at the top of the list, which could be someone
82
+ with many mutual connections by LinkedIn's count but only one the app could draw.
83
+ - **The Scan page has a radar, and a speed: Slow, Medium or Fast.** Mapping the 2nd degree starts
84
+ from a round Scan button in the middle of a radar: its sweep turns while a scan runs, and the
85
+ ring round it fills with today's searches against your budget. Beside it, three speeds. Fast is
86
+ how the scanner has always run, and nothing is faster; Medium waits 45 seconds before each page
87
+ of results and 3 minutes after every 10, Slow 90 seconds and 5 minutes, and both keep profiles
88
+ further apart (90 and 120 seconds). Each says about how many searches an hour it makes and how
89
+ long the rest of today's budget would take. The daily budget stays the cap at every speed:
90
+ slower spreads it out, it doesn't shrink it. The speed is saved with the budget and the scanner
91
+ says which it is using at the start of every run; the Scan page's time estimates follow it.
92
+ - **Scores moved into Settings.** Your field, how tiers are graded and every company's score are a
93
+ Scores section in Settings (the gear), and the Scores tab is gone from the header. Old links to
94
+ the Scores tab land in the same place in Settings.
95
+ - **Profile has Insights.** An Insights button beside Profile shows network health: how much of
96
+ your 2nd degree you reach two or more ways, the effective reach of your connections, and the
97
+ connections who reach the most people no one else does. It was at the top of the Scores tab.
98
+ - **Paths → People.** A new view beside the company map, drawn the same way but with your
99
+ connections as the bubbles: each is sized by the value of the cluster behind them (S tier counts
100
+ 3, A tier 2, everyone else 1; or by cluster size, or by S tier inside), coloured by their sector
101
+ and grouped like the companies. A white centre is the share of their cluster you can only reach
102
+ through them, a gold ring means S-tier people inside, and a line joins two connections whose
103
+ clusters share people. Point at someone for their numbers and how their cluster compares to the
104
+ rest you've scanned; click to open it in Bridge Chains. Shows the connections you've scanned,
105
+ or all of them, with the ones not scanned yet as small grey dots.
106
+ - **Separation's map draws connections as pills and people as cards** on a computer (a phone keeps
107
+ the dots). Each connection shows their initials, tier and what they are to the people on the
108
+ map: the only door to someone, the best way in for some, or only another way in; the ones with
109
+ a solid line are lit. Each person is a card with rank, role, score and how many ways in. With
110
+ the slider all the way to easy, the one person becomes a large card with LinkedIn's mutual
111
+ count and how many of those are drawn. Everything still glides as the slider moves.
112
+ - **Separation's list is a ledger.** A title bar says how many are shown and that the order
113
+ follows the slider. The way in shows the connection's face with "only way in" or how many more
114
+ ways there are under it, a new column shows how rare the way in is as a bar (full and cyan for
115
+ one door, short and orange for the warmest), and the power score sits beside its bar.
116
+ - **Separation is a tab of its own**, beside Degrees in the header, instead of a view inside
117
+ Degrees. It uses the same Filters grid as Degrees. Degrees keeps Bridge Chains, Pyramid and List.
118
+ - **Separation has a slider.** One slider reorders everyone as you drag it: the rarest ways in
119
+ at one end (strong people only one of your connections knows), the easiest at the other
120
+ (strong people you share the most mutual connections with), and each person's own score alone
121
+ in the middle, as the list has always been. A line under it says in words what it is
122
+ surfacing. It only reorders: scores, tiers and ranks stay as they are, and it claims no odds.
123
+ Double-click the slider, or tap 50, to go back to the middle. It replaces the Power / Ways in
124
+ switch. The tier chips and the rarity chips ("Only show") sit in the same card.
125
+ - **Separation's map follows the slider, and glides.** From the rare end to the middle it fans out
126
+ to ten people, each with the one or two doors that lead to them. Towards the easy end it
127
+ closes in on fewer people, and at the end on one: the person you're aiming at, with rings
128
+ round them and every connection of yours who leads to them drawn in, the top-scored one solid.
129
+ As the slider moves, people, connections and lines slide to their new places and newcomers
130
+ fade in (still, with Reduce Motion on). Pick anyone in the list, or one of the "Next" names
131
+ under the map, to aim at them instead. Beside each name it says how many mutual connections
132
+ lead to them: "only way in", "3 ways in", or, where LinkedIn's own count is higher than the
133
+ connections the app can draw, "37 mutuals · 6 mapped".
134
+ - **Path, in Separation.** A Path button beside the tier chips narrows everyone to one sector or
135
+ one company. The picker opens under the button with the sectors (inferred, as in Paths), the
136
+ biggest companies and a box to type in, and goes away once you've picked; the button then
137
+ shows your pick with a × to clear it.
138
+ - **Separation's heading no longer sits under the notch.**
139
+ - **One filter for tiers and degrees, the same in Network Circle and in Degrees.** The Filters
140
+ panel shows the five tiers as bigger rows, each with six dots, one for each degree. Tap a tier
141
+ to hide or show it, as before. Tap a dot to show or hide that degree for that tier alone:
142
+ S tier at 2nd degree without everyone else's, say. Tap a number at the top to switch a degree
143
+ for every tier at once. A dot with nobody behind it is faint and can't be tapped, each row
144
+ says how many people it is showing, and the last people on screen can't be switched off.
145
+ Network Circle starts with your own connections; Degrees starts with everything. In Degrees
146
+ the rows used to pick one bridge tier at a time; now 1st degree is your bridges and 2nd is
147
+ the people in their circles, each by their own tier.
148
+ - **The Galaxy's physics is the main thing in Network Circle's Filters panel.** The sliders,
149
+ layouts, colours, find and replay sit straight in the panel under the tiers, always on, with
150
+ no switch to turn on first and no "experimental" box. With no slider moved the Galaxy is laid
151
+ out as before; pointing at a dot now lights up its branch unless you turn that off.
152
+ - **Pyramid and List left Network Circle.** Its views are the Galaxy and Orbit. Pyramid and List
153
+ stay in Degrees.
154
+ - **An opened circle shows the chains that lead on from it.** Anyone you added from that circle
155
+ and then scanned keeps their dot in the circle, and the cluster their scan formed sits outside
156
+ it, joined to that dot by a line, so you can see where each path came from. Inside a cluster,
157
+ anyone you added and scanned in turn has their own dot marked and a line on to their cluster,
158
+ for as far as your scans reach. Every dot in a cluster is joined to its middle by a faint line.
159
+ Each cluster says whose circle it is, how many are in it and which degree that is. Point at
160
+ one and the way back to the middle lights up; click and it opens with the whole trail. The
161
+ people you added sit together on the side the chains lead off to, their names taking turns
162
+ above and below so they don't overlap, and the view zooms to fit when it opens.
163
+ - **Bridge Chains shows the bridges you connected with yourself.** Someone you met through a
164
+ bridge's circle is a link in that bridge's chain, not a bridge of their own: they're inside its
165
+ circle.
166
+ - **A bridge's ring now counts the people you added through it.** The ring sits tight on the
167
+ dot, with one bar for each person: orange once they've been scanned and have a cluster of
168
+ their own, green while they're ready for a scan. A small green number at the top right says
169
+ how many are ready, in place of the "ready" label. How much of a bridge's own list is scanned
170
+ is in its tooltip.
171
+ - **A crowded Bridge Chains is easier to read.** When your bridges need more than one ring, the
172
+ rings sit further apart, each with its own guide line, and the counts under each name wait
173
+ until you point at the bridge.
174
+ - **Orbit moved to Network Circle.** It shows the size of every circle at once, which is a
175
+ picture of the network, not a way to follow a chain. The corner switch in Degrees is gone.
176
+ - **Views are in the notch, under the tabs.** Galaxy, Orbit, Pyramid and List in Network Circle,
177
+ and Bridge Chains, Separation, Pyramid and List in Degrees, switch from the notch that hangs
178
+ under the top tabs, instead of a list at the top of the Filters panel. A scan's progress
179
+ shows in the same notch, beside them. The Filters panel now only filters.
180
+
181
+ ### Fixed
182
+ - The scanner's log, shown on the Scan page, says "scanning" throughout; a few lines still said the
183
+ old word.
184
+ - The "Scanning…" dot in the right panel pulses again; the animation it named was never defined.
185
+ - **Separation's map lines now meet every box.** Each connection sits level with the people it
186
+ leads to, so a connection with one person is on that person's row and the line between them runs
187
+ straight; lines start and end exactly on the pill and the card, with a small dot where they plug in.
188
+ - **"Hide the Chrome window while scanning" did nothing for scans started on the Scan page.**
189
+ Its own buttons (Scan, Check for new, Auto scan and the rest) sent their request without the
190
+ setting, so Chrome opened anyway; only scans started from a person's card or Resume were hidden.
191
+ Every scan start now goes through one place that adds it.
192
+ - **"Only way in" could show beside someone with two ways in drawn.** Rarity trusted the mutual
193
+ count a scan saved even when the app had since found them through more of your connections
194
+ than that. It now takes whichever is larger.
195
+
196
+ ## [0.4.11] - 2026-09-29
197
+
198
+ ### Added
199
+ - **The Social tab is a personal CRM.** Everyone you've been in touch with on LinkedIn is one contact,
200
+ connection or not: a list on the left with views and a search box, the person on the right (stacked
201
+ on a phone). The views:
202
+ - **Inbox:** they wrote last, or there's something unread. Unread first.
203
+ - **Awaiting reply:** you wrote last and nothing came back for 7 days or more (you can change the 7).
204
+ - **Follow-ups due:** a follow-up date of today or before.
205
+ - **Pipeline:** a board of the people you've given a stage.
206
+ - **Sent:** connection requests you sent (from your export's Invitations.csv, with the date, and the
207
+ note you wrote if Keep my messages is on), requests the app tracked (*Mark sent*), each accepted
208
+ once they're a connection and pending until then, and messages awaiting a reply.
209
+ - **Received:** requests to you from people who aren't connections yet.
210
+ - **All:** everyone and every group conversation, newest first.
211
+
212
+ Each person's panel shows their tier, company and how you're connected (and who can introduce you,
213
+ for 2nd degree), their conversations (the messages themselves if you keep them), requests both ways,
214
+ and **your own stage** (New, Contacted, Replied, Meeting, Won/Partner, Not now; it suggests one),
215
+ **tags**, **notes** and **next follow-up date**. Those are yours, so they're always kept, in a file of
216
+ their own per profile in the app's data folder, and never sent anywhere. Search covers names,
217
+ companies, tags and notes, and what was said while the messages are kept; in All it finds anyone in
218
+ your network, so you can add a note before you've written to them. **Export CSV** saves the table
219
+ (name, company, profile, connection, stage, tags, last contact, who wrote last, next follow-up,
220
+ notes) as a spreadsheet file. The list shows 100 at a time, so thousands of conversations stay quick.
221
+ - **Every conversation in your export, not just your connections'.** People who aren't connections
222
+ are listed by the name the export gives them, marked *Not a connection*, with their profile link when
223
+ it has one. Past (archived), current and ones only you wrote in are all there, from every folder;
224
+ group conversations are listed and marked; Sponsored Messages and InMails are marked, and adverts are
225
+ kept out of the Inbox. Unsent drafts are left out and said so.
226
+ - **Read my whole history**, beside *Sync messages now*: the live messages sync keeps scrolling your
227
+ messages list until no new conversations load (at most 60 scrolls, about 1,000 conversations), at the
228
+ same slow, fixed pace, with no random waits. The daily sync still reads just the top. The sync now
229
+ also brings in people who aren't your connections (with their name) and group conversations (without
230
+ any words), and each sync adds to what earlier ones found instead of replacing it.
231
+ - **Delete my CRM notes**, and *Forget it* asks, separately, whether to delete them too, so notes
232
+ aren't lost by accident.
233
+
234
+ ### Changed
235
+ - **"Save a copy of my network" carries the Social tab too.** A new box, ticked by default,
236
+ adds its findings, your CRM stages, tags, notes and follow-ups, and any messages you keep,
237
+ so moving to a new computer brings everything. Importing the copy puts them in place and
238
+ keeps this computer's beside the old network. Untick it to leave them behind.
239
+ - **Waiting on you has become the CRM's Inbox**, and Conversations is part of the CRM.
240
+ - **The Social tab keeps the names of people who aren't your connections**, from your export and the
241
+ sync, since nothing else in the app says who they are. A connection's name still isn't copied: the
242
+ app has it already.
243
+
9
244
  ## [0.4.10] - 2026-09-29
10
245
 
11
246
  ### Changed
@@ -20,6 +20,7 @@ export async function POST(request) {
20
20
  let body = {};
21
21
  try { body = await request.json(); } catch { /* no options: the defaults */ }
22
22
  const includePhotos = body?.photos !== false;
23
+ const includeSocial = body?.social !== false;
23
24
 
24
25
  const dir = dataDir();
25
26
  sweepLeftovers(dir);
@@ -27,7 +28,7 @@ export async function POST(request) {
27
28
  try {
28
29
  const file = path.join(work, 'export.sixdegrees');
29
30
  const made = buildExport(getDb(), {
30
- dir, outFile: file, includePhotos, appVersion: APP_VERSION, schemaSql: SCHEMA_SQL,
31
+ dir, outFile: file, includePhotos, includeSocial, appVersion: APP_VERSION, schemaSql: SCHEMA_SQL,
31
32
  });
32
33
  const fd = openSync(file, 'r');
33
34
  rmSync(work, { recursive: true, force: true });
@@ -2,6 +2,7 @@ import { db as supabase } from '../../../lib/db';
2
2
  import { uniqueByProfile, splitAlreadyConnected, toIsoDate, refreshNotifications, mutualCountOf } from '../../../lib/ingest';
3
3
  import { tiePairs } from '../../../lib/ties';
4
4
  import { promoteToFirstDegree } from '../../../lib/promote';
5
+ import { addedBackNotification, topCompanyNotification } from '../../../lib/notifications';
5
6
  import { localPhoto } from '../../../lib/photos';
6
7
 
7
8
  function parseHeadline(h) {
@@ -113,6 +114,8 @@ export async function POST(request) {
113
114
  const newRecords = records.filter(r => !existingUrls.has(r.profile_url));
114
115
  const existingRecords = records.filter(r => existingUrls.has(r.profile_url));
115
116
  let promoted = 0;
117
+ // Who was promoted, and through whom, for an "added you back" notification once they're rescored.
118
+ const addedBack = [];
116
119
 
117
120
  // Someone you met through a bridge and have now actually connected with.
118
121
  //
@@ -144,7 +147,7 @@ export async function POST(request) {
144
147
  ...(rec.profile_image_url ? { profile_image_url: rec.profile_image_url } : {}),
145
148
  },
146
149
  });
147
- if (res.promoted) promoted += 1;
150
+ if (res.promoted) { promoted += 1; addedBack.push(res); }
148
151
  }
149
152
  }
150
153
 
@@ -240,6 +243,41 @@ export async function POST(request) {
240
243
  } catch (e) { /* notifications are optional */ }
241
244
  }
242
245
 
246
+ // A circle scan that found people at top companies: one notification naming them (lib/notifications.js).
247
+ if (degree === 2 && bridgeId && newRecords.length) {
248
+ try {
249
+ const found = [];
250
+ const urls = newRecords.map((r) => r.profile_url).filter(Boolean);
251
+ for (let i = 0; i < urls.length; i += 100) {
252
+ let q = supabase.from('linkedin_connections').select('id, name, company, company_prestige_score, power_score, tier')
253
+ .in('profile_url', urls.slice(i, i + 100)).eq('source_connection_id', bridgeId);
254
+ if (userId) q = q.eq('user_id', userId);
255
+ const { data } = await q;
256
+ found.push(...(data || []));
257
+ }
258
+ const { data: via } = await supabase.from('linkedin_connections').select('id, name').eq('id', bridgeId).single();
259
+ const note = topCompanyNotification({ people: found, via, userId });
260
+ if (note) await supabase.from('notifications').insert([note]);
261
+ } catch (e) { /* notifications are optional */ }
262
+ }
263
+
264
+ // Someone you met through a bridge who is now a connection: "added you back, through …".
265
+ if (addedBack.length) {
266
+ try {
267
+ const rows = [];
268
+ for (const res of addedBack.slice(0, 10)) {
269
+ const { data: person } = await supabase.from('linkedin_connections')
270
+ .select('id, name, tier, power_score, headline, unlocked_from_bridge_id').eq('id', res.id).single();
271
+ if (!person) continue;
272
+ const viaId = res.originId ?? person.unlocked_from_bridge_id;
273
+ const via = viaId != null ? { id: viaId, name: res.originName || null } : null;
274
+ const note = addedBackNotification({ person, via, userId });
275
+ if (note) rows.push(note);
276
+ }
277
+ if (rows.length) await supabase.from('notifications').insert(rows);
278
+ } catch (e) { /* notifications are optional */ }
279
+ }
280
+
243
281
  // Auto-detect accepted pending requests: if a pending person just showed up as d1
244
282
  if (degree === 1 && records.length > 0) {
245
283
  try {
@@ -286,6 +324,7 @@ export async function POST(request) {
286
324
  title: `${p.name} accepted! +${XP_ACCEPT[p.tier] || 5} XP`,
287
325
  message: `${p.tier}-Tier connection — bridge their cluster to reach D3`,
288
326
  icon: '🤝',
327
+ data: { personId: p.id, viaId: p.source_connection_id ?? null, tier: p.tier ?? null },
289
328
  }))
290
329
  ).catch(() => {});
291
330
  }
@@ -7,6 +7,8 @@ import { scanProgress } from '../../../lib/scan-progress';
7
7
  import { linkedinState, writeLimits, liftCooldown } from '../../../lib/linkedin-limits';
8
8
  import { pausedList, readProgress, readUnclear } from '../../../lib/paused';
9
9
  import { getDb } from '../../../lib/db-client';
10
+ import { db as notesDb } from '../../../lib/db';
11
+ import { scanDoneNotification } from '../../../lib/notifications';
10
12
  import { registerScanState } from '../../../lib/scan-state';
11
13
  import { pendingImport } from '../../../lib/data-import';
12
14
  import { waitingPhotoCount } from '../../../lib/photos';
@@ -422,8 +424,12 @@ const ACTIONS = {
422
424
  company: { flag: '--company', needsName: true, label: 'Scanning' },
423
425
  // Photos an older version kept as links to LinkedIn: saved here, once each.
424
426
  // No browser, no search; every scan does the same at its end.
425
- // Experimental, for the Social tab: your messages list, once (who, when, unread; never text).
427
+ // Experimental, for the Social tab: your messages list, once (who, when, unread; the
428
+ // newest message's words only while Keep my messages is on).
426
429
  messages: { flag: '--messages', label: 'Reading your messages list' },
430
+ // The same, scrolling until the whole list has loaded (60 scrolls or about
431
+ // 1,000 conversations at most), for the Social tab's "Read my whole history".
432
+ 'messages-full': { flag: '--messages --full-history', label: 'Reading your whole messages history' },
427
433
  photos: { flag: '--save-photos', label: 'Saving profile photos to this computer' },
428
434
  };
429
435
 
@@ -536,7 +542,7 @@ export async function POST(request) {
536
542
 
537
543
  // The two settings a person changes here. Neither starts anything.
538
544
  if (action === 'set-limits') {
539
- const limits = writeLimits(dataDir(), { daily: body.daily, monthly: body.monthly, profiles: body.profiles });
545
+ const limits = writeLimits(dataDir(), { daily: body.daily, monthly: body.monthly, profiles: body.profiles, pace: body.pace });
540
546
  return Response.json({ ok: true, limits });
541
547
  }
542
548
  if (action === 'lift-cooldown') {
@@ -708,6 +714,7 @@ export async function POST(request) {
708
714
  const reason = state.stderrTail.filter((l) => !/Warning|warnings\.warn\(/.test(l)).slice(-6);
709
715
  state.failure = reason.length ? reason : null;
710
716
  }
717
+ const stopped = state.stopping;
711
718
  push(state.stopping ? 'Stopped.' : code === 0 ? 'Finished.' : `Stopped (exit ${code}).`);
712
719
  state.stopping = false;
713
720
  state.running = false;
@@ -717,6 +724,9 @@ export async function POST(request) {
717
724
  state.recent = [{
718
725
  action: state.action, target: state.target, startedAt: state.startedAt, exitCode: code, failure: state.failure,
719
726
  }, ...state.recent].slice(0, 5);
727
+ // A scan that finished leaves a notification ("Scan done: …"); a stop or a failure doesn't.
728
+ const done = scanDoneNotification({ action: state.action, target: state.target, exitCode: code, stopped, log: state.log.slice(-12), userId: profileId || null });
729
+ if (done) Promise.resolve(notesDb.from('notifications').insert([done])).catch(() => {});
720
730
  // A setup's private folder (the download and what was unpacked from it).
721
731
  if (cleanup) {
722
732
  try { cleanup(); } catch { /* swept at the next setup (sweepSetupLeftovers) */ }
@@ -1,5 +1,5 @@
1
1
  import { getDb } from '../../../../lib/db-client';
2
- import { companyOverrides, readForScoring, scoringRows, sectorFocusOf, tierScaleOf } from '../../../../lib/rpc';
2
+ import { companyOverrides, readForScoring, scoringRows, sectorFocusOf, tierScaleOf, titleRankingOf } from '../../../../lib/rpc';
3
3
  import { parseSectorFocus, previewSectorFocus } from '../../../../lib/sector-focus';
4
4
 
5
5
  // Scores → Your sector, before you save: what a sector focus would change,
@@ -32,6 +32,7 @@ export async function POST(request) {
32
32
  from: sectorFocusOf(db),
33
33
  to,
34
34
  tierScale: tierScaleOf(db),
35
+ titles: titleRankingOf(db),
35
36
  }));
36
37
  } catch (err) {
37
38
  return Response.json({ error: err.message }, { status: 500 });
@@ -0,0 +1,37 @@
1
+ import { rmSync } from 'node:fs';
2
+ import { cleanCrm, patchCrm, emptyCrm } from '../../../../lib/social-crm';
3
+ import { socialFiles, readJson, writeJsonAtomic } from '../../../../lib/social-store';
4
+
5
+ // The Social tab's CRM: your own stage, tags, notes and next follow-up for each
6
+ // person, in crm-<profile>.json in the data folder (lib/social-crm.js has the
7
+ // shape). Yours, so always kept, whatever Keep my messages says; never sent
8
+ // anywhere, and not in "Save a copy of my network".
9
+ //
10
+ // GET the whole store
11
+ // PATCH { key, set: { stage?, tags?, notes?, followUp? } } one person's
12
+ // fields (null or '' clears one), or { settings: { awaitDays } }
13
+ // DELETE the file gone: only when you've said so, on its own confirm
14
+
15
+ export async function GET() {
16
+ const files = socialFiles();
17
+ if (!files) return Response.json({ crm: emptyCrm() });
18
+ return Response.json({ crm: cleanCrm(readJson(files.crm, null)) });
19
+ }
20
+
21
+ export async function PATCH(request) {
22
+ const files = socialFiles();
23
+ if (!files) return Response.json({ error: 'Pick a profile first.' }, { status: 400 });
24
+ let body;
25
+ try { body = await request.json(); } catch { return Response.json({ error: 'That isn’t readable.' }, { status: 400 }); }
26
+ // Read, change one person, write whole: two quick edits in a row can't
27
+ // leave half a file, and the second sees the first (Node runs one at a time).
28
+ const next = patchCrm(readJson(files.crm, null), { key: body?.key, set: body?.set, settings: body?.settings });
29
+ writeJsonAtomic(files.crm, next);
30
+ return Response.json({ ok: true, entry: body?.key ? next.people[body.key] || null : null, settings: next.settings });
31
+ }
32
+
33
+ export async function DELETE() {
34
+ const files = socialFiles();
35
+ if (files) rmSync(files.crm, { force: true });
36
+ return Response.json({ ok: true });
37
+ }
@@ -7,11 +7,14 @@ import { socialFiles, readJson, writeJsonAtomic, readMessages, deleteMessages }
7
7
  // two files and why they're apart).
8
8
  //
9
9
  // GET the list, no words: who, when, who wrote last,
10
- // unread, how many, the thread's link
10
+ // unread, how many, the thread's link; with the
11
+ // requests both ways and people's names, and the
12
+ // requests' notes when the messages are kept
11
13
  // GET ?id=<id> | ?with=<key> one thread, a page at a time (before, limit):
12
14
  // its messages only if they're kept
13
15
  // GET ?q=<words> the conversations whose kept messages say that
14
16
  // POST an import's messages, in parts
17
+ // PUT an import's request notes
15
18
  // DELETE the messages gone, the list kept
16
19
  //
17
20
  // Nothing here goes anywhere but this computer's browser: no route of the app
@@ -25,14 +28,20 @@ function state() {
25
28
  if (!files) return null;
26
29
  const social = readJson(files.social, {}) || {};
27
30
  const keep = social.keepMessages === true;
28
- const kept = keep ? readMessages(files.messages) : { threads: {}, previews: {} };
29
- return { files, social, keep, kept, list: conversationList(social.conversations, social.live) };
31
+ const kept = keep ? readMessages(files.messages) : { threads: {}, previews: {}, invites: {} };
32
+ const names = { ...(social.names || {}) };
33
+ for (const [link, v] of Object.entries(social.liveOthers || {})) if (v?.name && !names[link]) names[link] = v.name;
34
+ for (const g of social.liveGroups || []) for (const [link, n] of Object.entries(g.names || {})) if (!names[link]) names[link] = n;
35
+ const list = conversationList(social.conversations, social.live, { others: social.liveOthers, groups: social.liveGroups, names: social.names });
36
+ return { files, social, keep, kept, names, list };
30
37
  }
31
38
 
32
39
  /** A conversation's kept messages, oldest first, with the live sync's newest one after them when it's newer. */
33
40
  function messagesOf(c, kept) {
34
41
  const msgs = kept.threads[c.id]?.messages || [];
35
- const preview = !c.group && c.people.length === 1 ? kept.previews[c.people[0]] : null;
42
+ // The sync's newest message is kept by the link it gave: for someone who
43
+ // isn't a connection that can differ from the export's (liveKey).
44
+ const preview = !c.group && c.people.length === 1 ? kept.previews[c.people[0]] || (c.liveKey && kept.previews[c.liveKey]) : null;
36
45
  if (preview?.text && (!msgs.length || (preview.t || 0) > (msgs[msgs.length - 1].t || 0))) return [...msgs, preview];
37
46
  return msgs;
38
47
  }
@@ -70,7 +79,13 @@ export async function GET(request) {
70
79
  title: s.kept.threads[c.id]?.title || '',
71
80
  hasText: s.keep && messagesOf(c, s.kept).length > 0,
72
81
  }));
73
- return Response.json({ keepMessages: s.keep, conversations });
82
+ return Response.json({
83
+ keepMessages: s.keep,
84
+ conversations,
85
+ invitations: Array.isArray(s.social.invitations) ? s.social.invitations : [],
86
+ names: s.names,
87
+ inviteNotes: s.keep ? s.kept.invites || {} : {},
88
+ });
74
89
  }
75
90
 
76
91
  /** One message as it's kept, or null when it isn't one. */
@@ -97,8 +112,9 @@ export async function POST(request) {
97
112
  }
98
113
  let into;
99
114
  if (part === 0) {
100
- // A new import replaces the export's messages; the live sync's previews stay.
101
- into = { threads: {}, previews: s.kept.previews || {} };
115
+ // A new import replaces the export's messages; the live sync's previews
116
+ // and the requests' notes (PUT) stay.
117
+ into = { threads: {}, previews: s.kept.previews || {}, invites: s.kept.invites || {} };
102
118
  } else {
103
119
  into = readJson(s.files.incoming, null);
104
120
  if (!into?.threads) return Response.json({ error: 'The start of this import is missing. Choose the export folder again.' }, { status: 409 });
@@ -114,7 +130,8 @@ export async function POST(request) {
114
130
  }
115
131
  }
116
132
  if (part === parts - 1) {
117
- writeJsonAtomic(s.files.messages, into);
133
+ // The notes may have changed while the parts came in: the file's win.
134
+ writeJsonAtomic(s.files.messages, { ...into, invites: readMessages(s.files.messages).invites || into.invites || {} });
118
135
  rmSync(s.files.incoming, { force: true });
119
136
  } else {
120
137
  writeJsonAtomic(s.files.incoming, into);
@@ -122,6 +139,25 @@ export async function POST(request) {
122
139
  return Response.json({ ok: true, part, parts, done: part === parts - 1 });
123
140
  }
124
141
 
142
+ // An import's request notes: { invites: { id: note } }, the words sent with a
143
+ // connection request (Invitations.csv's Message). Written by a person like a
144
+ // message, so kept only while Keep my messages is on, in the same file, and
145
+ // gone with it. They replace the notes kept before: an export has all of them.
146
+ export async function PUT(request) {
147
+ const s = state();
148
+ if (!s) return Response.json({ error: 'Pick a profile first.' }, { status: 400 });
149
+ if (!s.keep) return Response.json({ error: 'Keep my messages is off, so nothing was saved.' }, { status: 409 });
150
+ let body;
151
+ try { body = await request.json(); } catch { return Response.json({ error: 'That isn’t readable.' }, { status: 400 }); }
152
+ const invites = {};
153
+ for (const [id, note] of Object.entries(body?.invites && typeof body.invites === 'object' ? body.invites : {}).slice(0, 50000)) {
154
+ if (id && id.length <= 400 && typeof note === 'string' && note.trim()) invites[id] = note.slice(0, 2000);
155
+ }
156
+ const kept = readJson(s.files.messages, {}) || {};
157
+ writeJsonAtomic(s.files.messages, { threads: kept.threads || {}, previews: kept.previews || {}, invites });
158
+ return Response.json({ ok: true, notes: Object.keys(invites).length });
159
+ }
160
+
125
161
  // The words gone, the list and the numbers kept: what switching Keep my
126
162
  // messages off does (the PATCH that turns it off deletes them as well).
127
163
  export async function DELETE() {