curious-astronaut 8.3.0__tar.gz

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 (312) hide show
  1. curious_astronaut-8.3.0/.gitignore +38 -0
  2. curious_astronaut-8.3.0/LICENSE +21 -0
  3. curious_astronaut-8.3.0/PKG-INFO +483 -0
  4. curious_astronaut-8.3.0/README.md +417 -0
  5. curious_astronaut-8.3.0/config.example.json +132 -0
  6. curious_astronaut-8.3.0/frontend/README.md +32 -0
  7. curious_astronaut-8.3.0/frontend/dist/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
  8. curious_astronaut-8.3.0/frontend/dist/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
  9. curious_astronaut-8.3.0/frontend/dist/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
  10. curious_astronaut-8.3.0/frontend/dist/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
  11. curious_astronaut-8.3.0/frontend/dist/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
  12. curious_astronaut-8.3.0/frontend/dist/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
  13. curious_astronaut-8.3.0/frontend/dist/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
  14. curious_astronaut-8.3.0/frontend/dist/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
  15. curious_astronaut-8.3.0/frontend/dist/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
  16. curious_astronaut-8.3.0/frontend/dist/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
  17. curious_astronaut-8.3.0/frontend/dist/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
  18. curious_astronaut-8.3.0/frontend/dist/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
  19. curious_astronaut-8.3.0/frontend/dist/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
  20. curious_astronaut-8.3.0/frontend/dist/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
  21. curious_astronaut-8.3.0/frontend/dist/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
  22. curious_astronaut-8.3.0/frontend/dist/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
  23. curious_astronaut-8.3.0/frontend/dist/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
  24. curious_astronaut-8.3.0/frontend/dist/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
  25. curious_astronaut-8.3.0/frontend/dist/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
  26. curious_astronaut-8.3.0/frontend/dist/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
  27. curious_astronaut-8.3.0/frontend/dist/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
  28. curious_astronaut-8.3.0/frontend/dist/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
  29. curious_astronaut-8.3.0/frontend/dist/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
  30. curious_astronaut-8.3.0/frontend/dist/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
  31. curious_astronaut-8.3.0/frontend/dist/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
  32. curious_astronaut-8.3.0/frontend/dist/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
  33. curious_astronaut-8.3.0/frontend/dist/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
  34. curious_astronaut-8.3.0/frontend/dist/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
  35. curious_astronaut-8.3.0/frontend/dist/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
  36. curious_astronaut-8.3.0/frontend/dist/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
  37. curious_astronaut-8.3.0/frontend/dist/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
  38. curious_astronaut-8.3.0/frontend/dist/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
  39. curious_astronaut-8.3.0/frontend/dist/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
  40. curious_astronaut-8.3.0/frontend/dist/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
  41. curious_astronaut-8.3.0/frontend/dist/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
  42. curious_astronaut-8.3.0/frontend/dist/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
  43. curious_astronaut-8.3.0/frontend/dist/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
  44. curious_astronaut-8.3.0/frontend/dist/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
  45. curious_astronaut-8.3.0/frontend/dist/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
  46. curious_astronaut-8.3.0/frontend/dist/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
  47. curious_astronaut-8.3.0/frontend/dist/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
  48. curious_astronaut-8.3.0/frontend/dist/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
  49. curious_astronaut-8.3.0/frontend/dist/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
  50. curious_astronaut-8.3.0/frontend/dist/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
  51. curious_astronaut-8.3.0/frontend/dist/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
  52. curious_astronaut-8.3.0/frontend/dist/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
  53. curious_astronaut-8.3.0/frontend/dist/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
  54. curious_astronaut-8.3.0/frontend/dist/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
  55. curious_astronaut-8.3.0/frontend/dist/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
  56. curious_astronaut-8.3.0/frontend/dist/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
  57. curious_astronaut-8.3.0/frontend/dist/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
  58. curious_astronaut-8.3.0/frontend/dist/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
  59. curious_astronaut-8.3.0/frontend/dist/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
  60. curious_astronaut-8.3.0/frontend/dist/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
  61. curious_astronaut-8.3.0/frontend/dist/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
  62. curious_astronaut-8.3.0/frontend/dist/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
  63. curious_astronaut-8.3.0/frontend/dist/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
  64. curious_astronaut-8.3.0/frontend/dist/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
  65. curious_astronaut-8.3.0/frontend/dist/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
  66. curious_astronaut-8.3.0/frontend/dist/assets/index-B2yJomae.js +611 -0
  67. curious_astronaut-8.3.0/frontend/dist/assets/index-RqIU_xH0.css +1 -0
  68. curious_astronaut-8.3.0/frontend/dist/favicon.svg +35 -0
  69. curious_astronaut-8.3.0/frontend/dist/icons.svg +24 -0
  70. curious_astronaut-8.3.0/frontend/dist/index.html +14 -0
  71. curious_astronaut-8.3.0/frontend/index.html +13 -0
  72. curious_astronaut-8.3.0/frontend/package-lock.json +4904 -0
  73. curious_astronaut-8.3.0/frontend/package.json +44 -0
  74. curious_astronaut-8.3.0/frontend/public/favicon.svg +35 -0
  75. curious_astronaut-8.3.0/frontend/public/icons.svg +24 -0
  76. curious_astronaut-8.3.0/frontend/src/App.tsx +344 -0
  77. curious_astronaut-8.3.0/frontend/src/README.md +125 -0
  78. curious_astronaut-8.3.0/frontend/src/api/README.md +124 -0
  79. curious_astronaut-8.3.0/frontend/src/api/agents.ts +565 -0
  80. curious_astronaut-8.3.0/frontend/src/api/graph.ts +390 -0
  81. curious_astronaut-8.3.0/frontend/src/api/index.ts +28 -0
  82. curious_astronaut-8.3.0/frontend/src/api/mentions.ts +133 -0
  83. curious_astronaut-8.3.0/frontend/src/api/search.ts +177 -0
  84. curious_astronaut-8.3.0/frontend/src/api/sessions.ts +440 -0
  85. curious_astronaut-8.3.0/frontend/src/api/settings.ts +219 -0
  86. curious_astronaut-8.3.0/frontend/src/api/sources.ts +150 -0
  87. curious_astronaut-8.3.0/frontend/src/api/sse.ts +64 -0
  88. curious_astronaut-8.3.0/frontend/src/app.css +205 -0
  89. curious_astronaut-8.3.0/frontend/src/detail/DetailPanel.tsx +471 -0
  90. curious_astronaut-8.3.0/frontend/src/detail/README.md +117 -0
  91. curious_astronaut-8.3.0/frontend/src/detail/detail.css +321 -0
  92. curious_astronaut-8.3.0/frontend/src/detail/useSelection.ts +208 -0
  93. curious_astronaut-8.3.0/frontend/src/figures/Lightbox.tsx +77 -0
  94. curious_astronaut-8.3.0/frontend/src/figures/README.md +58 -0
  95. curious_astronaut-8.3.0/frontend/src/graph/GraphExplorer.tsx +735 -0
  96. curious_astronaut-8.3.0/frontend/src/graph/README.md +207 -0
  97. curious_astronaut-8.3.0/frontend/src/graph/buildShape.ts +189 -0
  98. curious_astronaut-8.3.0/frontend/src/graph/canvas/GraphCanvas.tsx +264 -0
  99. curious_astronaut-8.3.0/frontend/src/graph/canvas/README.md +79 -0
  100. curious_astronaut-8.3.0/frontend/src/graph/clusterForce.ts +193 -0
  101. curious_astronaut-8.3.0/frontend/src/graph/controls/FindBar.tsx +124 -0
  102. curious_astronaut-8.3.0/frontend/src/graph/controls/GraphControls.tsx +408 -0
  103. curious_astronaut-8.3.0/frontend/src/graph/controls/Legend.tsx +59 -0
  104. curious_astronaut-8.3.0/frontend/src/graph/controls/README.md +145 -0
  105. curious_astronaut-8.3.0/frontend/src/graph/graph.css +488 -0
  106. curious_astronaut-8.3.0/frontend/src/graph/hooks/README.md +164 -0
  107. curious_astronaut-8.3.0/frontend/src/graph/hooks/useDiscovery.ts +183 -0
  108. curious_astronaut-8.3.0/frontend/src/graph/hooks/useEscapeClear.ts +45 -0
  109. curious_astronaut-8.3.0/frontend/src/graph/hooks/useMarquee.ts +162 -0
  110. curious_astronaut-8.3.0/frontend/src/graph/hooks/usePinning.ts +119 -0
  111. curious_astronaut-8.3.0/frontend/src/graph/hooks/useTimeline.ts +257 -0
  112. curious_astronaut-8.3.0/frontend/src/graph/model.ts +278 -0
  113. curious_astronaut-8.3.0/frontend/src/graph/theme.ts +140 -0
  114. curious_astronaut-8.3.0/frontend/src/index.css +90 -0
  115. curious_astronaut-8.3.0/frontend/src/library/README.md +55 -0
  116. curious_astronaut-8.3.0/frontend/src/library/Sources.tsx +324 -0
  117. curious_astronaut-8.3.0/frontend/src/library/sources.css +229 -0
  118. curious_astronaut-8.3.0/frontend/src/main.tsx +24 -0
  119. curious_astronaut-8.3.0/frontend/src/mentions/MentionSuggestions.tsx +196 -0
  120. curious_astronaut-8.3.0/frontend/src/mentions/README.md +272 -0
  121. curious_astronaut-8.3.0/frontend/src/mentions/mentions.css +104 -0
  122. curious_astronaut-8.3.0/frontend/src/mentions/parse.ts +245 -0
  123. curious_astronaut-8.3.0/frontend/src/mentions/useMentionSuggestions.ts +270 -0
  124. curious_astronaut-8.3.0/frontend/src/notation/MathText.tsx +59 -0
  125. curious_astronaut-8.3.0/frontend/src/notation/README.md +108 -0
  126. curious_astronaut-8.3.0/frontend/src/notation/latexToUnicode.ts +173 -0
  127. curious_astronaut-8.3.0/frontend/src/notation/prepareMath.ts +86 -0
  128. curious_astronaut-8.3.0/frontend/src/notation/splitMath.ts +156 -0
  129. curious_astronaut-8.3.0/frontend/src/scope/README.md +129 -0
  130. curious_astronaut-8.3.0/frontend/src/scope/resolve.ts +340 -0
  131. curious_astronaut-8.3.0/frontend/src/search/README.md +138 -0
  132. curious_astronaut-8.3.0/frontend/src/search/SearchControls.tsx +263 -0
  133. curious_astronaut-8.3.0/frontend/src/search/search.css +245 -0
  134. curious_astronaut-8.3.0/frontend/src/search/useDirectSearch.ts +217 -0
  135. curious_astronaut-8.3.0/frontend/src/settings/README.md +221 -0
  136. curious_astronaut-8.3.0/frontend/src/settings/SettingsModal.tsx +1810 -0
  137. curious_astronaut-8.3.0/frontend/src/settings/settings.css +639 -0
  138. curious_astronaut-8.3.0/frontend/src/shell/README.md +59 -0
  139. curious_astronaut-8.3.0/frontend/src/shell/SessionRow.tsx +172 -0
  140. curious_astronaut-8.3.0/frontend/src/shell/SideBar.tsx +448 -0
  141. curious_astronaut-8.3.0/frontend/src/shell/ThreadList.tsx +37 -0
  142. curious_astronaut-8.3.0/frontend/src/shell/saveQueue.ts +105 -0
  143. curious_astronaut-8.3.0/frontend/src/shell/shell.css +441 -0
  144. curious_astronaut-8.3.0/frontend/src/shell/useExplorations.ts +295 -0
  145. curious_astronaut-8.3.0/frontend/src/shell/useSessions.ts +76 -0
  146. curious_astronaut-8.3.0/frontend/src/store/README.md +91 -0
  147. curious_astronaut-8.3.0/frontend/src/store/explorations.ts +179 -0
  148. curious_astronaut-8.3.0/frontend/src/store/highlight.ts +52 -0
  149. curious_astronaut-8.3.0/frontend/src/store/index.ts +51 -0
  150. curious_astronaut-8.3.0/frontend/src/store/library.ts +77 -0
  151. curious_astronaut-8.3.0/frontend/src/store/threadPersistence.ts +127 -0
  152. curious_astronaut-8.3.0/frontend/src/store/transcript.ts +735 -0
  153. curious_astronaut-8.3.0/frontend/src/store/workspace.ts +991 -0
  154. curious_astronaut-8.3.0/frontend/src/teacher/HopDots.tsx +41 -0
  155. curious_astronaut-8.3.0/frontend/src/teacher/README.md +124 -0
  156. curious_astronaut-8.3.0/frontend/src/teacher/ScopePicker.tsx +172 -0
  157. curious_astronaut-8.3.0/frontend/src/teacher/Teacher.tsx +858 -0
  158. curious_astronaut-8.3.0/frontend/src/teacher/figures/FigCard.tsx +59 -0
  159. curious_astronaut-8.3.0/frontend/src/teacher/figures/README.md +49 -0
  160. curious_astronaut-8.3.0/frontend/src/teacher/figures/split.ts +65 -0
  161. curious_astronaut-8.3.0/frontend/src/teacher/history.ts +73 -0
  162. curious_astronaut-8.3.0/frontend/src/teacher/teacher.css +1355 -0
  163. curious_astronaut-8.3.0/frontend/src/teacher/transcript/AnswerMarkdown.tsx +230 -0
  164. curious_astronaut-8.3.0/frontend/src/teacher/transcript/BeatList.tsx +112 -0
  165. curious_astronaut-8.3.0/frontend/src/teacher/transcript/ChatMessage.tsx +483 -0
  166. curious_astronaut-8.3.0/frontend/src/teacher/transcript/README.md +160 -0
  167. curious_astronaut-8.3.0/frontend/src/teacher/transcript/provenance.ts +76 -0
  168. curious_astronaut-8.3.0/frontend/src/teacher/transcript/remarkCite.ts +115 -0
  169. curious_astronaut-8.3.0/frontend/src/teacher/useConversation.ts +1030 -0
  170. curious_astronaut-8.3.0/frontend/src/tour/README.md +127 -0
  171. curious_astronaut-8.3.0/frontend/src/tour/Tour.tsx +286 -0
  172. curious_astronaut-8.3.0/frontend/src/tour/steps.ts +455 -0
  173. curious_astronaut-8.3.0/frontend/src/tour/tour.css +159 -0
  174. curious_astronaut-8.3.0/frontend/src/ui/README.md +112 -0
  175. curious_astronaut-8.3.0/frontend/src/ui/theme.ts +137 -0
  176. curious_astronaut-8.3.0/frontend/src/ui/useResizablePanel.ts +219 -0
  177. curious_astronaut-8.3.0/frontend/test/README.md +118 -0
  178. curious_astronaut-8.3.0/frontend/tsconfig.app.json +26 -0
  179. curious_astronaut-8.3.0/frontend/tsconfig.json +7 -0
  180. curious_astronaut-8.3.0/frontend/tsconfig.node.json +24 -0
  181. curious_astronaut-8.3.0/frontend/vite.config.ts +32 -0
  182. curious_astronaut-8.3.0/hatch_build.py +78 -0
  183. curious_astronaut-8.3.0/pyproject.toml +256 -0
  184. curious_astronaut-8.3.0/src/curious_astronaut/README.md +199 -0
  185. curious_astronaut-8.3.0/src/curious_astronaut/__init__.py +13 -0
  186. curious_astronaut-8.3.0/src/curious_astronaut/agents/README.md +572 -0
  187. curious_astronaut-8.3.0/src/curious_astronaut/agents/__init__.py +25 -0
  188. curious_astronaut-8.3.0/src/curious_astronaut/agents/captions.py +65 -0
  189. curious_astronaut-8.3.0/src/curious_astronaut/agents/events.py +452 -0
  190. curious_astronaut-8.3.0/src/curious_astronaut/agents/factory.py +242 -0
  191. curious_astronaut-8.3.0/src/curious_astronaut/agents/library_figures.py +176 -0
  192. curious_astronaut-8.3.0/src/curious_astronaut/agents/orchestrators/README.md +94 -0
  193. curious_astronaut-8.3.0/src/curious_astronaut/agents/orchestrators/__init__.py +23 -0
  194. curious_astronaut-8.3.0/src/curious_astronaut/agents/orchestrators/lecturer/README.md +224 -0
  195. curious_astronaut-8.3.0/src/curious_astronaut/agents/orchestrators/lecturer/__init__.py +21 -0
  196. curious_astronaut-8.3.0/src/curious_astronaut/agents/orchestrators/lecturer/config.py +145 -0
  197. curious_astronaut-8.3.0/src/curious_astronaut/agents/orchestrators/lecturer/main.py +567 -0
  198. curious_astronaut-8.3.0/src/curious_astronaut/agents/orchestrators/researcher/README.md +278 -0
  199. curious_astronaut-8.3.0/src/curious_astronaut/agents/orchestrators/researcher/__init__.py +22 -0
  200. curious_astronaut-8.3.0/src/curious_astronaut/agents/orchestrators/researcher/config.py +106 -0
  201. curious_astronaut-8.3.0/src/curious_astronaut/agents/orchestrators/researcher/main.py +583 -0
  202. curious_astronaut-8.3.0/src/curious_astronaut/agents/orchestrators/researcher/tools.py +880 -0
  203. curious_astronaut-8.3.0/src/curious_astronaut/agents/orchestrators/router/README.md +195 -0
  204. curious_astronaut-8.3.0/src/curious_astronaut/agents/orchestrators/router/__init__.py +48 -0
  205. curious_astronaut-8.3.0/src/curious_astronaut/agents/orchestrators/router/config.py +181 -0
  206. curious_astronaut-8.3.0/src/curious_astronaut/agents/orchestrators/router/main.py +241 -0
  207. curious_astronaut-8.3.0/src/curious_astronaut/agents/orchestrators/summarizer/README.md +155 -0
  208. curious_astronaut-8.3.0/src/curious_astronaut/agents/orchestrators/summarizer/__init__.py +55 -0
  209. curious_astronaut-8.3.0/src/curious_astronaut/agents/orchestrators/summarizer/config.py +80 -0
  210. curious_astronaut-8.3.0/src/curious_astronaut/agents/orchestrators/summarizer/main.py +242 -0
  211. curious_astronaut-8.3.0/src/curious_astronaut/agents/prompts.py +361 -0
  212. curious_astronaut-8.3.0/src/curious_astronaut/agents/skills/citation-discipline.md +19 -0
  213. curious_astronaut-8.3.0/src/curious_astronaut/agents/skills/figures.md +28 -0
  214. curious_astronaut-8.3.0/src/curious_astronaut/agents/skills/numbered-papers.md +22 -0
  215. curious_astronaut-8.3.0/src/curious_astronaut/agents/skills/teaching-voice.md +15 -0
  216. curious_astronaut-8.3.0/src/curious_astronaut/agents/skills/workflows/lecture.md +38 -0
  217. curious_astronaut-8.3.0/src/curious_astronaut/agents/skills/workflows/research.md +58 -0
  218. curious_astronaut-8.3.0/src/curious_astronaut/agents/streams.py +239 -0
  219. curious_astronaut-8.3.0/src/curious_astronaut/agents/traversal.py +182 -0
  220. curious_astronaut-8.3.0/src/curious_astronaut/agents/workers/__init__.py +24 -0
  221. curious_astronaut-8.3.0/src/curious_astronaut/agents/workers/search/README.md +180 -0
  222. curious_astronaut-8.3.0/src/curious_astronaut/agents/workers/search/__init__.py +18 -0
  223. curious_astronaut-8.3.0/src/curious_astronaut/agents/workers/search/papers/__init__.py +14 -0
  224. curious_astronaut-8.3.0/src/curious_astronaut/agents/workers/search/papers/config.py +85 -0
  225. curious_astronaut-8.3.0/src/curious_astronaut/agents/workers/search/papers/main.py +561 -0
  226. curious_astronaut-8.3.0/src/curious_astronaut/agents/workers/search/web/__init__.py +14 -0
  227. curious_astronaut-8.3.0/src/curious_astronaut/agents/workers/search/web/config.py +48 -0
  228. curious_astronaut-8.3.0/src/curious_astronaut/agents/workers/search/web/main.py +131 -0
  229. curious_astronaut-8.3.0/src/curious_astronaut/app.py +149 -0
  230. curious_astronaut-8.3.0/src/curious_astronaut/cli.py +468 -0
  231. curious_astronaut-8.3.0/src/curious_astronaut/config.py +941 -0
  232. curious_astronaut-8.3.0/src/curious_astronaut/integrations/README.md +43 -0
  233. curious_astronaut-8.3.0/src/curious_astronaut/integrations/__init__.py +26 -0
  234. curious_astronaut-8.3.0/src/curious_astronaut/integrations/arxiv/README.md +191 -0
  235. curious_astronaut-8.3.0/src/curious_astronaut/integrations/arxiv/__init__.py +111 -0
  236. curious_astronaut-8.3.0/src/curious_astronaut/integrations/arxiv/categories.py +184 -0
  237. curious_astronaut-8.3.0/src/curious_astronaut/integrations/arxiv/client.py +92 -0
  238. curious_astronaut-8.3.0/src/curious_astronaut/integrations/arxiv/figures.py +187 -0
  239. curious_astronaut-8.3.0/src/curious_astronaut/integrations/arxiv/fulltext.py +169 -0
  240. curious_astronaut-8.3.0/src/curious_astronaut/integrations/arxiv/taxonomy.json +664 -0
  241. curious_astronaut-8.3.0/src/curious_astronaut/integrations/arxiv/vocab.py +74 -0
  242. curious_astronaut-8.3.0/src/curious_astronaut/integrations/caps.py +44 -0
  243. curious_astronaut-8.3.0/src/curious_astronaut/integrations/huggingface/README.md +83 -0
  244. curious_astronaut-8.3.0/src/curious_astronaut/integrations/huggingface/__init__.py +30 -0
  245. curious_astronaut-8.3.0/src/curious_astronaut/integrations/huggingface/client.py +54 -0
  246. curious_astronaut-8.3.0/src/curious_astronaut/integrations/huggingface/code_links.py +156 -0
  247. curious_astronaut-8.3.0/src/curious_astronaut/integrations/openalex/README.md +251 -0
  248. curious_astronaut-8.3.0/src/curious_astronaut/integrations/openalex/__init__.py +57 -0
  249. curious_astronaut-8.3.0/src/curious_astronaut/integrations/openalex/client.py +170 -0
  250. curious_astronaut-8.3.0/src/curious_astronaut/integrations/openalex/nodes.py +327 -0
  251. curious_astronaut-8.3.0/src/curious_astronaut/integrations/openalex/search.py +90 -0
  252. curious_astronaut-8.3.0/src/curious_astronaut/integrations/openalex/traversal.py +597 -0
  253. curious_astronaut-8.3.0/src/curious_astronaut/integrations/openalex/vocab.py +75 -0
  254. curious_astronaut-8.3.0/src/curious_astronaut/integrations/semantic_scholar/README.md +311 -0
  255. curious_astronaut-8.3.0/src/curious_astronaut/integrations/semantic_scholar/__init__.py +67 -0
  256. curious_astronaut-8.3.0/src/curious_astronaut/integrations/semantic_scholar/client.py +147 -0
  257. curious_astronaut-8.3.0/src/curious_astronaut/integrations/semantic_scholar/corpus/README.md +372 -0
  258. curious_astronaut-8.3.0/src/curious_astronaut/integrations/semantic_scholar/corpus/__init__.py +45 -0
  259. curious_astronaut-8.3.0/src/curious_astronaut/integrations/semantic_scholar/corpus/datasets.py +147 -0
  260. curious_astronaut-8.3.0/src/curious_astronaut/integrations/semantic_scholar/corpus/download.py +644 -0
  261. curious_astronaut-8.3.0/src/curious_astronaut/integrations/semantic_scholar/corpus/ingest.py +559 -0
  262. curious_astronaut-8.3.0/src/curious_astronaut/integrations/semantic_scholar/corpus/paths.py +178 -0
  263. curious_astronaut-8.3.0/src/curious_astronaut/integrations/semantic_scholar/corpus/source.py +623 -0
  264. curious_astronaut-8.3.0/src/curious_astronaut/integrations/semantic_scholar/nodes.py +170 -0
  265. curious_astronaut-8.3.0/src/curious_astronaut/integrations/semantic_scholar/search.py +107 -0
  266. curious_astronaut-8.3.0/src/curious_astronaut/integrations/semantic_scholar/traversal.py +653 -0
  267. curious_astronaut-8.3.0/src/curious_astronaut/integrations/semantic_scholar/vocab.py +67 -0
  268. curious_astronaut-8.3.0/src/curious_astronaut/optional.py +131 -0
  269. curious_astronaut-8.3.0/src/curious_astronaut/routes/README.md +402 -0
  270. curious_astronaut-8.3.0/src/curious_astronaut/routes/__init__.py +37 -0
  271. curious_astronaut-8.3.0/src/curious_astronaut/routes/agents.py +435 -0
  272. curious_astronaut-8.3.0/src/curious_astronaut/routes/graph.py +567 -0
  273. curious_astronaut-8.3.0/src/curious_astronaut/routes/search.py +486 -0
  274. curious_astronaut-8.3.0/src/curious_astronaut/routes/sessions.py +181 -0
  275. curious_astronaut-8.3.0/src/curious_astronaut/routes/settings.py +620 -0
  276. curious_astronaut-8.3.0/src/curious_astronaut/routes/sources.py +193 -0
  277. curious_astronaut-8.3.0/src/curious_astronaut/routes/sse.py +56 -0
  278. curious_astronaut-8.3.0/src/curious_astronaut/services/README.md +31 -0
  279. curious_astronaut-8.3.0/src/curious_astronaut/services/__init__.py +17 -0
  280. curious_astronaut-8.3.0/src/curious_astronaut/services/graph/README.md +323 -0
  281. curious_astronaut-8.3.0/src/curious_astronaut/services/graph/__init__.py +26 -0
  282. curious_astronaut-8.3.0/src/curious_astronaut/services/graph/bands.py +150 -0
  283. curious_astronaut-8.3.0/src/curious_astronaut/services/graph/budget.py +308 -0
  284. curious_astronaut-8.3.0/src/curious_astronaut/services/graph/build.py +551 -0
  285. curious_astronaut-8.3.0/src/curious_astronaut/services/graph/model.py +138 -0
  286. curious_astronaut-8.3.0/src/curious_astronaut/services/graph/shape.py +184 -0
  287. curious_astronaut-8.3.0/src/curious_astronaut/services/pdf/README.md +175 -0
  288. curious_astronaut-8.3.0/src/curious_astronaut/services/pdf/__init__.py +33 -0
  289. curious_astronaut-8.3.0/src/curious_astronaut/services/pdf/errors.py +24 -0
  290. curious_astronaut-8.3.0/src/curious_astronaut/services/pdf/fetch.py +141 -0
  291. curious_astronaut-8.3.0/src/curious_astronaut/services/pdf/floats.py +457 -0
  292. curious_astronaut-8.3.0/src/curious_astronaut/services/pdf/mine.py +129 -0
  293. curious_astronaut-8.3.0/src/curious_astronaut/services/pdf/resolve.py +105 -0
  294. curious_astronaut-8.3.0/src/curious_astronaut/services/pdf/text.py +55 -0
  295. curious_astronaut-8.3.0/src/curious_astronaut/services/search/README.md +91 -0
  296. curious_astronaut-8.3.0/src/curious_astronaut/services/search/__init__.py +37 -0
  297. curious_astronaut-8.3.0/src/curious_astronaut/services/search/discovery.py +363 -0
  298. curious_astronaut-8.3.0/src/curious_astronaut/services/search/naming.py +134 -0
  299. curious_astronaut-8.3.0/src/curious_astronaut/services/sources/README.md +261 -0
  300. curious_astronaut-8.3.0/src/curious_astronaut/services/sources/__init__.py +57 -0
  301. curious_astronaut-8.3.0/src/curious_astronaut/services/sources/embeddings.py +192 -0
  302. curious_astronaut-8.3.0/src/curious_astronaut/services/sources/errors.py +14 -0
  303. curious_astronaut-8.3.0/src/curious_astronaut/services/sources/extract.py +133 -0
  304. curious_astronaut-8.3.0/src/curious_astronaut/services/sources/figures.py +171 -0
  305. curious_astronaut-8.3.0/src/curious_astronaut/services/sources/ingest.py +173 -0
  306. curious_astronaut-8.3.0/src/curious_astronaut/services/sources/retrieval.py +228 -0
  307. curious_astronaut-8.3.0/src/curious_astronaut/services/sources/store.py +289 -0
  308. curious_astronaut-8.3.0/src/curious_astronaut/storage/README.md +172 -0
  309. curious_astronaut-8.3.0/src/curious_astronaut/storage/__init__.py +18 -0
  310. curious_astronaut-8.3.0/src/curious_astronaut/storage/cache.py +163 -0
  311. curious_astronaut-8.3.0/src/curious_astronaut/storage/sessions.py +325 -0
  312. curious_astronaut-8.3.0/src/curious_astronaut/storage/utils.py +52 -0
@@ -0,0 +1,38 @@
1
+ # Secrets & credentials — NEVER commit these
2
+ # .env: the not-yet-rewritten parts of the app still read it during the v2
3
+ # rewrite; config.json: the new settings file for already-rewritten parts.
4
+ .env
5
+ config.json
6
+ .config-location
7
+
8
+ # Data
9
+ data/
10
+ *.db
11
+
12
+ # Python
13
+ .venv/
14
+ __pycache__/
15
+ *.pyc
16
+ .uv/
17
+
18
+ # Tooling caches (uv run nox)
19
+ .nox/
20
+ .mypy_cache/
21
+ .pytest_cache/
22
+ .ruff_cache/
23
+
24
+ # Node / frontend
25
+ node_modules/
26
+ frontend/dist/
27
+ frontend/node_modules/
28
+
29
+ # OS / editor
30
+ .DS_Store
31
+ .vscode/
32
+ .idea/
33
+
34
+ # Personal brainstorm inbox (filed into OnePager, then cleared)
35
+ todos.md
36
+
37
+ # Claude Code local settings (machine-specific; the skill under .claude/skills is tracked)
38
+ .claude/settings.local.json
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Charles Patrick James <charles.patrick.james@gmail.com>
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,483 @@
1
+ Metadata-Version: 2.5
2
+ Name: curious-astronaut
3
+ Version: 8.3.0
4
+ Summary: Curious Astronaut — an interactive citation/similarity graph explorer with an AI teacher that narrates a field's history over the graph.
5
+ Project-URL: Homepage, https://github.com/patrickjames0132/curious-astronaut
6
+ Project-URL: Repository, https://github.com/patrickjames0132/curious-astronaut
7
+ Project-URL: Issues, https://github.com/patrickjames0132/curious-astronaut/issues
8
+ Project-URL: Changelog, https://github.com/patrickjames0132/curious-astronaut/blob/main/docs/history.md
9
+ Author-email: Charles Patrick James <charles.patrick.james@gmail.com>
10
+ License: MIT License
11
+
12
+ Copyright (c) 2026 Charles Patrick James <charles.patrick.james@gmail.com>
13
+
14
+ Permission is hereby granted, free of charge, to any person obtaining a copy
15
+ of this software and associated documentation files (the "Software"), to deal
16
+ in the Software without restriction, including without limitation the rights
17
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
18
+ copies of the Software, and to permit persons to whom the Software is
19
+ furnished to do so, subject to the following conditions:
20
+
21
+ The above copyright notice and this permission notice shall be included in all
22
+ copies or substantial portions of the Software.
23
+
24
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
25
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
27
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
28
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
29
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30
+ SOFTWARE.
31
+ License-File: LICENSE
32
+ Keywords: arxiv,citations,knowledge-graph,literature-review,llm,research,semantic-scholar
33
+ Classifier: Development Status :: 4 - Beta
34
+ Classifier: Environment :: Web Environment
35
+ Classifier: Framework :: Flask
36
+ Classifier: Intended Audience :: Education
37
+ Classifier: Intended Audience :: Science/Research
38
+ Classifier: License :: OSI Approved :: MIT License
39
+ Classifier: Operating System :: OS Independent
40
+ Classifier: Programming Language :: Python :: 3
41
+ Classifier: Programming Language :: Python :: 3.11
42
+ Classifier: Programming Language :: Python :: 3.12
43
+ Classifier: Programming Language :: Python :: 3.13
44
+ Classifier: Programming Language :: Python :: 3.14
45
+ Classifier: Topic :: Education
46
+ Classifier: Topic :: Scientific/Engineering :: Information Analysis
47
+ Classifier: Typing :: Typed
48
+ Requires-Python: >=3.11
49
+ Requires-Dist: anthropic>=0.40
50
+ Requires-Dist: click>=8.1
51
+ Requires-Dist: flask-cors>=4.0
52
+ Requires-Dist: flask>=3.0
53
+ Requires-Dist: huggingface-hub>=0.28
54
+ Requires-Dist: platformdirs>=4.0
55
+ Requires-Dist: pydantic-ai-slim[anthropic,google,openai]>=2.5.0
56
+ Requires-Dist: pydantic>=2.7
57
+ Requires-Dist: sqlite-vec>=0.1.6
58
+ Provides-Extra: corpus
59
+ Requires-Dist: duckdb>=1.5; extra == 'corpus'
60
+ Provides-Extra: pdf
61
+ Requires-Dist: pymupdf>=1.24; extra == 'pdf'
62
+ Provides-Extra: sources
63
+ Requires-Dist: sentence-transformers>=5.6; extra == 'sources'
64
+ Requires-Dist: torch>=2.9; extra == 'sources'
65
+ Description-Content-Type: text/markdown
66
+
67
+ # Curious Astronaut
68
+
69
+ **Explore how research papers connect — and have an AI teacher narrate the
70
+ story of how a field got here.**
71
+
72
+ Drop in a paper and Curious Astronaut renders a **Connected-Papers-style interactive
73
+ graph** of how it links to the literature — the papers it cites (its
74
+ intellectual ancestors), the papers that cite it (its descendants), and its
75
+ nearest neighbors by meaning. Then wander: double-click any node to re-center
76
+ the graph on it and keep exploring.
77
+
78
+ Each **exploration** contains a permanent **General** discussion and a thread
79
+ for each graph. Switching threads restores that graph and its conversation.
80
+ Paper citations highlight the current graph; graph icons open or resume a graph
81
+ thread directly. Type **@** and pick another discussion from the suggestions to
82
+ bring it into the agent's context. Lectures remain part of their thread's history, including follow-up
83
+ questions after a reload. Existing saved sessions migrate lazily when opened.
84
+
85
+ It connects to the academic-graph ecosystem **dynamically** — there's no local
86
+ corpus of papers to store. The only things kept on disk are a small cache of
87
+ the graphs you've looked at, your saved sessions, and the library of sources
88
+ you upload (embedded locally; nothing leaves your machine).
89
+
90
+ **Why it exists.** Curious Astronaut is free software (MIT), built to put research and
91
+ self-teaching within reach of anyone who wants to learn something — not to
92
+ become a product. It runs on your own machine, and the sources you upload
93
+ never leave it. If something with more reach ever does this better, good;
94
+ until then this exists, and you can have it.
95
+
96
+ > **The teacher runs on whichever model you point it at, including free ones.**
97
+ > The graph explorer is keyless and free. For the AI teacher, pick a vendor:
98
+ > **[Ollama](https://ollama.com)** runs a model on your own machine — no key,
99
+ > no signup, no cost, nothing leaving the computer — or **Google AI Studio**
100
+ > issues a free-tier key, or use paid **Anthropic** / **OpenAI** if you have
101
+ > one. Any OpenAI-compatible server (Groq, OpenRouter, LM Studio) works
102
+ > through the OpenAI entry too. It is a **per-agent** choice, so a sensible
103
+ > setup runs the lecturer locally and leaves the one agent that needs web
104
+ > search on a cloud vendor. See [docs/configuration.md](docs/configuration.md).
105
+
106
+ ```
107
+ ┌──────────┐ find seed ┌─────────┐ whole graph ┌──────────────────┐
108
+ │ search │ ───────────▶ │ backend │ ─── seed/refs/ ──▶ │ Semantic Scholar│
109
+ │ (S2+LLM)│ (title/id) │ (Flask) │ citations │ — or — │
110
+ └──────────┘ └────┬────┘ (one provider) │ OpenAlex │
111
+ │ /api/graph (thin cache) └──────────────────┘
112
+ ▼
113
+ ┌───────────────────────┐
114
+ │ React + force graph │ ← the interactive map you explore
115
+ └───────────────────────┘
116
+ ```
117
+
118
+ **Stack:** Python/Flask + uv · [PydanticAI](https://ai.pydantic.dev) agents on
119
+ Claude · React + TypeScript (strict) + Vite + Redux Toolkit ·
120
+ [`react-force-graph-2d`](https://github.com/vasturiano/react-force-graph) ·
121
+ [OpenAlex](https://openalex.org) (citations) +
122
+ [Semantic Scholar Academic Graph API](https://api.semanticscholar.org/api-docs/) ·
123
+ [ar5iv](https://ar5iv.org) + pymupdf-mined open-access PDFs for figures/full text ·
124
+ [Hugging Face Papers](https://huggingface.co/papers) for code & artifacts ·
125
+ sentence-transformers + sqlite-vec for the local library. Runs locally.
126
+
127
+ ---
128
+
129
+ ## Setup
130
+
131
+ The toolchain (python, uv, nodejs, trivy, gh) is pinned in `.tool-versions` —
132
+ [mise](https://mise.jdx.dev) installs it all with `mise install` (mise reads
133
+ the asdf-format file and works on Windows and macOS alike). With mise in
134
+ place, `bin/setup.bat` (Windows) or `bin/setup.sh` (macOS/Linux) does the full
135
+ bootstrap: pinned tools, a full `uv sync` (dev tooling, the notebook
136
+ `research` group, and every optional extra), and the frontend install + build.
137
+ Without mise, `uv` and `Node.js` installed any other way work fine too.
138
+
139
+ > **Three capabilities are optional extras**, because they are most of the
140
+ > install and a reader who only wants the graph and the teacher needs none of
141
+ > them — core is **83 MB** against **1.0 GB** with everything:
142
+ >
143
+ > | Extra | Gives you | Cost |
144
+ > | --- | --- | --- |
145
+ > | *(core)* | the graph explorer and the AI teacher | 83 MB |
146
+ > | `pdf` | figures, tables and algorithm boxes mined from PDFs | +54 MB |
147
+ > | `corpus` | the offline Semantic Scholar citations corpus | +44 MB |
148
+ > | `sources` | search over your own uploaded books and PDFs | +~800 MB |
149
+ >
150
+ > `bin/setup` installs all of them. To pick: `uv sync --extra pdf`, or
151
+ > `pip install 'curious-astronaut[pdf,corpus]'`. Nothing breaks without one — the feature
152
+ > says it isn't installed and names the command. See
153
+ > [docs/first-run.md](docs/first-run.md).
154
+
155
+ > **Windows pulls a CUDA build of torch** (~1.8GB, from PyTorch's `cu130`
156
+ > index) with the `sources` extra, so the local embedder can use a GPU if you
157
+ > have one — PyPI's Windows wheel is CPU-only, and that's the whole difference
158
+ > between ~80 and ~1500 chunks/s at ingest. It falls back to CPU on a machine
159
+ > without a GPU, and macOS/Linux resolve torch from PyPI as usual. See
160
+ > [`sources.embedding.device`](docs/configuration.md).
161
+
162
+ ### 1. Configure
163
+
164
+ ```bash
165
+ cp config.example.json config.json
166
+ ```
167
+
168
+ All configuration lives in `config.json` (gitignored — it holds your keys; no
169
+ environment variables, ever). Every field must be *present* and is validated at
170
+ startup by Pydantic, so a malformed value fails fast with a clear message — but
171
+ the API-key values may be left blank. For the **full value-by-value reference**
172
+ (every tunable, its default, and *why* it's there), see
173
+ **[docs/configuration.md](docs/configuration.md)**.
174
+
175
+ The two **data-source keys are completely optional** — Curious Astronaut explores the graph
176
+ fully without them, just on tighter public rate limits. The **Anthropic key is
177
+ the one that unlocks the AI teacher** (lectures, research Q&A, library chat); the
178
+ graph explorer runs fine without it, but the Assistant panel needs it.
179
+
180
+ - **`providers.s2.api_key`** — **optional.** A free
181
+ [Semantic Scholar API key](https://www.semanticscholar.org/product/api) is
182
+ recommended (the keyless pool is tight), but keyless works.
183
+ - **`providers.openalex.api_key`** — **optional.** OpenAlex (the citation
184
+ source) runs keyless on its `mailto` polite pool ($0.10/day of metered search
185
+ — plenty for browsing); a free key at
186
+ [openalex.org/settings/api](https://openalex.org/settings/api) lifts it to
187
+ $1/day. Set `providers.openalex.mailto` to your email either way.
188
+ - **`llm.providers`** — **one block per LLM vendor; fill in at least one to
189
+ power the agents.** Four are wired, and two of them cost nothing:
190
+ - **`ollama.base_url`** — **free and local.** Point it at a running
191
+ [Ollama](https://ollama.com) server (normally `http://localhost:11434/v1`,
192
+ keep the `/v1`). No key, no signup, no quota, and the conversation never
193
+ leaves your machine. The one trade: a local model can't search the web, so
194
+ that scout goes quiet rather than inventing sources.
195
+ - **`google.api_key`** — **free tier.** A key from
196
+ [Google AI Studio](https://aistudio.google.com/apikey), quota-limited.
197
+ The free quota covers the **flash** models; the pro ones answer
198
+ `429 RESOURCE_EXHAUSTED`, so pick a `gemini-*-flash*` for each agent.
199
+ A key on a *paid* project with its credits spent fails the same way even
200
+ on flash — if everything 429s, check billing rather than the model name.
201
+ - **`anthropic.api_key`** — paid. A
202
+ [Claude API key](https://console.anthropic.com/settings/keys).
203
+ - **`openai.api_key`** / **`openai.base_url`** — paid at OpenAI, but the
204
+ `base_url` points the same vendor block at **any OpenAI-compatible
205
+ server** (Groq, OpenRouter, Together, LM Studio), several with free tiers.
206
+
207
+ Which vendor each agent uses is set per agent in `llm.agents`
208
+ (`"model": "ollama:qwen3:8b"`), so mixing them is normal — and each vendor
209
+ in Settings ▸ Agents ▸ Model Providers has a **"Use for all agents"** button
210
+ that puts the lecturer and researcher on its advanced model and the
211
+ summarizer and scouts on its light one, in one click.
212
+
213
+ ### 2. Build the frontend & run
214
+
215
+ **Single-server** (serves the built frontend + API together):
216
+
217
+ ```bash
218
+ cd frontend && npm install && npm run build && cd ..
219
+ uv run astronaut serve # http://127.0.0.1:5000
220
+ uv run astronaut serve --port 5050 # ...or another port (--host to expose it)
221
+ uv run astronaut --version # the installed version
222
+ ```
223
+
224
+ **Development** (two terminals, hot-reloading frontend):
225
+
226
+ ```bash
227
+ uv run astronaut serve # Terminal 1 — API
228
+ cd frontend && npm run dev # Terminal 2 — http://localhost:5173
229
+ ```
230
+
231
+ The Vite dev server proxies `/api/*` to Flask.
232
+
233
+ ---
234
+
235
+ ## Using it
236
+
237
+ 1. **Find a paper** — everything starts in the **chat bar**; there is no
238
+ separate search box. Ask a research question and the assistant goes
239
+ looking, or **type `@`** to name a paper: suggestions appear as you type,
240
+ and picking one attaches that exact paper to your message. What happens
241
+ next depends on what you said — the same rule a pasted id has always
242
+ followed, that naming one paper and nothing else is a statement of intent:
243
+
244
+ | What you send | What it does |
245
+ | --- | --- |
246
+ | `@` + a paper you picked | lands on that paper's graph |
247
+ | `@` + words nothing matched | searches properly and lists what it found |
248
+ | a question with `@…` in it | answers *from* that paper, leaving your graph alone |
249
+ | an **arXiv id / URL** | goes straight to the graph, no model involved |
250
+
251
+ The suggestions arrive in two passes: your **cached papers appear as you
252
+ type** (a local scan — free, offline, no wait), then a real search fills the
253
+ list out when you pause, re-ranked so the paper whose title most nearly *is*
254
+ what you typed leads. While that second pass runs, the panel says which
255
+ phase it is on — *"Searching Semantic Scholar"*, *"Working out which paper
256
+ 'dqn' is"* — rather than a bare spinner. So a paper you have never opened
257
+ is still nameable, and one you have seen shows up instantly. (There was a
258
+ **🔍 Find papers** toggle for this until
259
+ v7.18.0. It made the same words mean different things depending on a
260
+ button's state, and it couldn't express "answer from *this* paper" at all.)
261
+ A search streams: cached papers appear immediately (⚡ **opens instantly**
262
+ marks the ones whose whole neighborhood is cached, so a click costs no API
263
+ call), then the scout's finds arrive lookup by lookup with a trace chip
264
+ each. The **Filters** popover holds a publication-year window (1800 → now)
265
+ and **fields of study** — hard limits, not hints, and they bind the
266
+ assistant's own paper searches too (though never an `@` lookup: you named
267
+ that paper, so a filter has no business hiding it). Citation links on the graph are never
268
+ filtered.
269
+ 2. **Read the map** — 🟡 seed · 🔵 references · 🟢 citations.
270
+ (💗 found-by-search too, on a session saved before v7.3.0 — free-text hits
271
+ used to be drawn as edgeless pink dots; now the assistant numbers what it
272
+ finds and you promote a paper onto the graph by clicking its citation.)
273
+ **Every paper that cites the seed is one `citations` relation** — from the
274
+ historic giants to work published this month — and **which of them matters is
275
+ yours to decide** with the year and citation-count sliders, not ours to
276
+ decide with a threshold. (They were two relations, *Field Landmarks* and
277
+ *Latest Publications*, until v7.17.0.)
278
+
279
+ Behind that one relation are still **two queries**, because they have to be:
280
+ the most-cited citers come back ranked by citation count (under **OpenAlex**,
281
+ the true all-time top-cited from a sorted `cites:` query; under **Semantic
282
+ Scholar**, the whole citation history when the offline corpus serves it or the
283
+ seed's citer list is fully reachable live — the graph's own note names which
284
+ source is behind them; see
285
+ [docs/citation-coverage.md](docs/citation-coverage.md) for how the two sources
286
+ compare), with **how many to show measured per-seed from the real citer pool**
287
+ (an old classic maps out large, a young hot paper stays tight — the STOP rule
288
+ in `services/graph/budget.py`). A second query runs **per recent year**, and
289
+ it is the only reason recent work appears at all: citation counts measure
290
+ attention *and* elapsed time together, so a paper from this year never
291
+ survives a citation ranking. Its start year is **sized per-seed from fitted
292
+ constants** so an old classic's coverage reaches back to meet its dense
293
+ cluster instead of leaving a gap (see `services/graph/bands.py`). (Every term
294
+ here — *landmark*, *band*, *tail edge*, the sizing rules — is defined once,
295
+ with a worked example, in
296
+ [docs/landmark-vocabulary.md](docs/landmark-vocabulary.md).) Node size =
297
+ citations; thick links = influential citations; a
298
+ dashed ring = discovered by the teacher mid-chat. Click a node for
299
+ details (TL;DR, abstract/PDF links, arXiv & Semantic Scholar category
300
+ tags, figures — mined straight from the open-access PDF for journal
301
+ papers, tables and algorithms included — code & artifacts);
302
+ **double-click to re-seed** on it — journal papers included. LaTeX math (`$…$`) renders throughout — titles,
303
+ abstracts, lecture beats, answers, and figure captions — via KaTeX.
304
+ 3. **Declutter** — a new graph opens onto the *graph*: the controls sit folded
305
+ to a slim bar (click its header to open them) and no paper is selected until
306
+ you click one. Inside: Force ↔ Timeline layouts (x = publication date), relation
307
+ on/off **filter chips**, a dual-knob **year slider** and a dual-knob
308
+ **citation-count slider** (a log-scale min…max window over the papers on
309
+ screen — a display filter, no re-query), drag-to-pin, focus-on-hover, and a
310
+ **Refresh** that busts this seed's day-cached snapshot to re-fetch fresh from
311
+ Semantic Scholar. **Hand-pick a scope** with the node selector:
312
+ **alt-drag** a marquee to add papers to the teacher's scope (additive —
313
+ several sweeps build one cluster), **shift-click** to add/remove one, and
314
+ **alt-click** empty (or **Clear**) to reset. Picked papers ring cyan and the
315
+ rest dim; the teacher then grounds only in your selection — even papers a
316
+ filter later hides, which stay drawn with a dotted ring. Click the
317
+ **Curious Astronaut** brand anytime to go home. By default the app **sizes each graph
318
+ for you** (how many landmark citers to ship, where the Latest bands start —
319
+ per seed); turn **"Size graphs automatically" off** in Settings ▸ Graph to
320
+ have it ship everything it can and size the bands yourself, and each filter
321
+ chip gains a **count slider** to trim how many of that relation you see.
322
+ 4. **Learn** (the 🎓 Assistant panel):
323
+ - **Lecture** — a narrated tour of the papers **you have on screen**,
324
+ oldest first, over the graph as you built it (lectures never expand it —
325
+ only the research agent does). Ask for it in the chat bar: *"lecture me
326
+ on these"*. (There was a Lecture button in the panel until v7.21.0, and a
327
+ `/lecture` command until v7.23.0.)
328
+ **What it covers is your choice, not a menu's:**
329
+ filter to the references and you get the story of how the field arrived
330
+ here; keep only recent work and you get the current frontier; alt-drag a
331
+ cluster and it narrates those; scope it to a single paper and it teaches
332
+ that paper in chapters, reading its full text for the real math — **any**
333
+ paper, not just the seed, so learning about something you found no longer
334
+ means re-seeding the graph on it first. Four mode buttons did that carving
335
+ for you until v7.17.0 — and in doing so *overrode* whatever you had
336
+ filtered or selected.
337
+ **What a turn is about follows one rule, for lectures and questions
338
+ alike:** what your message asks for, else your hand-picked selection,
339
+ else what your filters show. So the **filter chips** (the seed included
340
+ — it has its own chip), the **year range**, the **citation window** and
341
+ the per-chip count sliders set the *default* — narrow the view and
342
+ "lecture me on these" or "which of these used dropout?" covers what's
343
+ left. A selection outranks them (a picked paper a slider later hides
344
+ stays in scope and stays drawn, dotted). And **the message outranks
345
+ both** — *"lecture me on the references"*, *"what do the citations say
346
+ about entropy?"*, *"on the seed"*, *"on the Bekenstein paper and Hawking
347
+ 1975"*, *"what does Hawking 1975 argue?"*, *"lecture me on the whole
348
+ graph"* (the lot, past every filter), plus a period on top of any of
349
+ those — *"the references from the 2010s"*, *"the last five years"* (a
350
+ period also bounds what the researcher goes looking for that turn).
351
+ Those papers become the selection — ringed, drawn even if a chip or
352
+ slider hides them, narrated or answered over — and stay selected
353
+ afterwards, exactly as if you'd marqueed them (Esc clears). Each reply
354
+ says what it was scoped to (*"Scoped to the references, 2010–2019 · 12
355
+ papers"*). A bare period (*"the papers between 2016 and 2017"*) narrows
356
+ what you were already looking at rather than reaching past the year
357
+ slider. A message that asks for papers the graph doesn't have says so
358
+ instead of quietly answering over everything. When a lecture finishes
359
+ the whole of it stays lit (click its bubble any time to light it again,
360
+ Esc to clear).
361
+ **One choice the scope can't make for you, so your words make it:**
362
+ say *history* — *"the story of"*, *"how we got here"* — and the papers
363
+ are told as a chronological arc; otherwise they are grouped into their
364
+ key themes. Summary is the default because a chronological arc is a
365
+ strong claim to make about an arbitrary selection.
366
+ The lecture is nudged to span the whole publication history it is given —
367
+ both ends, not just the oldest, most-cited papers. Length is tunable
368
+ (`min_beats`/`max_beats` in the lecturer's config `extras`, default 7–12).
369
+ **The lecture arrives as a reply in the conversation**, beats and all,
370
+ behind its own caret — open when it lands, folding itself when the next
371
+ lecture arrives, so a conversation with four lectures in it stays
372
+ readable. Beats light up their papers and carry the papers' **real
373
+ figures** inline — click to enlarge.
374
+ So the docked panel is one thing: the conversation, with the 📚 source
375
+ scope and ▽ filters on its caret row. It was a stack of two folding
376
+ sections from v7.10.0, a **Lecture** section above a **Chat** one, which
377
+ was itself a fix for an older shape where the two took turns and asking a
378
+ question tucked away the lecture you were reading.
379
+ **You don't have to say "lecture".** *"Summarize these papers for me"*,
380
+ *"what's the story here?"* — the lecturer answers just the same (the
381
+ last of those arrives framed as history, because that is what it asked
382
+ for). The composer works out which assistant you meant: phrasings that
383
+ name a lecture outright and point at nothing in particular are matched
384
+ outright, and anything less obvious — including *which* papers — is
385
+ settled by a quick classifier: *"summarize this"* is a question about
386
+ the paper you have open, *"summarize these papers"* is a lecture, and
387
+ no pattern tells those apart. Every turn it guessed on says which
388
+ assistant answered and offers the other in a click, so a wrong guess
389
+ costs one line rather than a re-typed question.
390
+ - **Ask** — the research agent answers grounded in what it actually
391
+ reads, streaming its tool steps live (read / expand / search the
392
+ literature / search the web / search your sources / show a figure). It
393
+ consults **every source it has** before it commits to an answer — it
394
+ can't know which one holds it until it looks, and an answer written from
395
+ memory over the textbook you uploaded is the failure that matters. The
396
+ sources also feed each other: the web names things (a chip, a model, a
397
+ lab result) and the literature indexes them, so a web finding sends the
398
+ agent back after **the paper behind the announcement** — the one thing of
399
+ the two you can seed a whole graph on. Answers render in full **Markdown + math**,
400
+ and their inline `[n]` citations are **clickable** — click one to spotlight
401
+ that paper on the graph, click it again to clear. (Lecture beats cite the
402
+ same way.) A tiny glyph on each chip says which click you're about to
403
+ make: **one node lit** spotlights a paper already on the canvas, **three
404
+ nodes wired together** builds that paper's own graph. Answers also cite their whole grounding set — click the bubble
405
+ to re-light them all. A claim drawn from **your own uploaded sources** is
406
+ attributed with its real title and page, resolved server-side rather than
407
+ written out by the model.
408
+ Underneath each answer, a line says what actually grounded it — which of
409
+ your sources, which papers, how much of the web — computed from what the
410
+ agent did, not from what it claims. When nothing grounded it, it says that too: Curious Astronaut grounds
411
+ answers in real material, and is honest when it can't. A lecture gets the
412
+ same line, saying how many papers it narrated.
413
+ **And a turn tells you which graph it came from, once that stops being
414
+ obvious.** The conversation survives a graph load — seed something new and
415
+ your questions come with you — so a transcript can hold turns about
416
+ several different graphs at once. Those older turns quietly go inert:
417
+ their citations grey out, because the papers they point at are no longer
418
+ loaded. So any turn answered over a graph other than the one you are
419
+ looking at says *"From the “…” graph"* at the top, which is the difference
420
+ between a dead link and an answer that explains itself.
421
+ - **No graph open? Then the assistant *is* the page.** Curious Astronaut opens on a
422
+ centred chat bar — no graph and no uploaded library required — and the
423
+ same agent answers seedless, searching your library through its tools
424
+ rather than being handed passages, so a greeting stays a greeting and a
425
+ real question gets looked up first. Its citations are the way *in*:
426
+ clicking one **maps that paper** — builds its graph, with nothing selected
427
+ on it — and the conversation comes with you rather than being cleared, so a
428
+ survey answer is a doorway into the graph instead of a list of links out
429
+ to someone else's site. When a graph appears the chat collapses into the
430
+ side panel beside it (and a 🎓 button at the top-right of the canvas
431
+ brings it back) without dropping the thread or your place in it.
432
+ 5. **Your sources** (📚) — drop in PDFs (parallel, with live embedding
433
+ progress) or paste URLs; scope any conversation to a subset of them.
434
+ 6. **Explorations** — every sitting saves itself, so there is no Save button:
435
+ ask a question and it appears in the rail, named after what you asked, kept
436
+ up to date as you work. Leave an answer running and start another
437
+ exploration — it keeps going in the background and is waiting when you come
438
+ back.
439
+ 7. **Lost?** Hit the header's **?** for a guided coach-mark tour — it auto-runs
440
+ once on first launch (the search surface) and once more on your first graph
441
+ (the graph tools), spotlighting one control at a time; the bubble's title
442
+ doubles as a jump select that skips straight to whichever tip you came
443
+ back for.
444
+
445
+ ---
446
+
447
+ ## The codebase
448
+
449
+ The rewrite's first principle: **every package documents itself**. Start at
450
+ any folder's `README.md` — e.g. `src/curious_astronaut/agents/` (the two tiers —
451
+ orchestrators that own an outcome, workers that own one source each — plus
452
+ the event protocol and the streaming bridge), `services/sources/` (hybrid retrieval:
453
+ FTS5 + vectors + RRF), `frontend/src/README.md` (the render-tree map), or
454
+ `frontend/src/store/` (what earns a Redux slice and what stays local).
455
+
456
+ Quality gates: `uv run nox` runs the whole repo's — pre-commit hooks (file
457
+ hygiene; ruff incl. Google-style docstring rules; a repo-local
458
+ no-single-letter-identifiers AST check, notebooks included; pydoclint for
459
+ Args/Returns completeness; the frontend's prettier + oxlint incl. JSDoc
460
+ completeness and `id-length`, the frontend half of that same naming rule),
461
+ strict mypy, pytest (`test/`, 669 offline tests), and Vitest
462
+ (`frontend/test/`, offline too) — plus `cd frontend && npm run build`
463
+ (strict tsc + Vite) for the type/build check.
464
+
465
+ All of that also runs in CI (`.github/workflows/ci.yml`) on every push to
466
+ `main`, across `ubuntu-latest` and `windows-latest`. CI installs the pinned
467
+ toolchain from `.tool-versions` with mise, so it runs the *same* python / uv /
468
+ node / trivy the developer machines do — and because Trivy is genuinely on
469
+ PATH there, the security scan actually runs rather than skipping. It reuses
470
+ `bin/setup.sh` rather than restating it, with `CA_SKIP_TORCH=1`, which
471
+ drops the `sources` extra: nothing in the gate needs torch, and skipping it
472
+ avoids dragging the 37 Linux CUDA packages into every run (1.0 GB → 304 MB).
473
+ The `pdf` and `corpus` extras *are* installed there — those tests build real
474
+ PDFs and query real Parquet. A second workflow (`release.yml`) fires on
475
+ `v*` tags: it fails if the tag and `pyproject.toml`'s version disagree, then
476
+ builds the frontend, builds the sdist and wheel, verifies the artifacts carry
477
+ the bundled SPA and no local state, and publishes to TestPyPI and then PyPI via
478
+ trusted publishing (no stored token).
479
+
480
+ For the project's direction and past, two living docs sit beside the code:
481
+ **[OnePager.md](OnePager.md)** (the vision, the full feature stack, and the
482
+ open backlog) and **[docs/history.md](docs/history.md)** (the complete shipped
483
+ record, version by version).