dr-claw 1.1.2

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 (287) hide show
  1. package/LICENSE +1380 -0
  2. package/NOTICE +17 -0
  3. package/README.md +875 -0
  4. package/README.zh-CN.md +857 -0
  5. package/dist/api-docs.html +878 -0
  6. package/dist/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
  7. package/dist/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
  8. package/dist/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
  9. package/dist/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
  10. package/dist/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
  11. package/dist/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
  12. package/dist/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
  13. package/dist/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
  14. package/dist/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
  15. package/dist/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
  16. package/dist/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
  17. package/dist/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
  18. package/dist/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
  19. package/dist/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
  20. package/dist/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
  21. package/dist/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
  22. package/dist/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
  23. package/dist/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
  24. package/dist/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
  25. package/dist/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
  26. package/dist/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
  27. package/dist/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
  28. package/dist/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
  29. package/dist/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
  30. package/dist/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
  31. package/dist/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
  32. package/dist/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
  33. package/dist/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
  34. package/dist/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
  35. package/dist/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
  36. package/dist/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
  37. package/dist/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
  38. package/dist/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
  39. package/dist/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
  40. package/dist/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
  41. package/dist/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
  42. package/dist/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
  43. package/dist/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
  44. package/dist/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
  45. package/dist/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
  46. package/dist/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
  47. package/dist/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
  48. package/dist/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
  49. package/dist/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
  50. package/dist/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
  51. package/dist/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
  52. package/dist/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
  53. package/dist/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
  54. package/dist/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
  55. package/dist/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
  56. package/dist/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
  57. package/dist/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
  58. package/dist/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
  59. package/dist/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
  60. package/dist/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
  61. package/dist/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
  62. package/dist/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
  63. package/dist/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
  64. package/dist/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
  65. package/dist/assets/_basePickBy-C0n1uo4u.js +1 -0
  66. package/dist/assets/_baseUniq-BRNmhFkc.js +1 -0
  67. package/dist/assets/arc-DOMBmC_A.js +1 -0
  68. package/dist/assets/architectureDiagram-VXUJARFQ-CtMWglbD.js +36 -0
  69. package/dist/assets/blockDiagram-VD42YOAC-DK_gTkJy.js +122 -0
  70. package/dist/assets/c4Diagram-YG6GDRKO-DrDAvsn7.js +10 -0
  71. package/dist/assets/channel-BFOE7OyC.js +1 -0
  72. package/dist/assets/chunk-4BX2VUAB-CGbhdZcN.js +1 -0
  73. package/dist/assets/chunk-55IACEB6-sGeFSxen.js +1 -0
  74. package/dist/assets/chunk-B4BG7PRW-RxgHAU_P.js +165 -0
  75. package/dist/assets/chunk-DI55MBZ5-CvHUXZrx.js +220 -0
  76. package/dist/assets/chunk-FMBD7UC4-Sn7cXVNU.js +15 -0
  77. package/dist/assets/chunk-QN33PNHL-bDKOHppN.js +1 -0
  78. package/dist/assets/chunk-QZHKN3VN-B9kN5LGm.js +1 -0
  79. package/dist/assets/chunk-TZMSLE5B-CCBe6len.js +1 -0
  80. package/dist/assets/classDiagram-2ON5EDUG-DQoMNYlY.js +1 -0
  81. package/dist/assets/classDiagram-v2-WZHVMYZB-DQoMNYlY.js +1 -0
  82. package/dist/assets/clone-BulQSblH.js +1 -0
  83. package/dist/assets/cose-bilkent-S5V4N54A-r6sLiHBW.js +1 -0
  84. package/dist/assets/cytoscape.esm-5J0xJHOV.js +321 -0
  85. package/dist/assets/dagre-6UL2VRFP-AbsaR1Oa.js +4 -0
  86. package/dist/assets/defaultLocale-DX6XiGOO.js +1 -0
  87. package/dist/assets/diagram-PSM6KHXK-DjCvdUyy.js +24 -0
  88. package/dist/assets/diagram-QEK2KX5R-CqiocXkH.js +43 -0
  89. package/dist/assets/diagram-S2PKOQOG-D5ALeoII.js +24 -0
  90. package/dist/assets/erDiagram-Q2GNP2WA-C1AeOsiz.js +60 -0
  91. package/dist/assets/flowDiagram-NV44I4VS-C98ve_DO.js +162 -0
  92. package/dist/assets/ganttDiagram-JELNMOA3-3zdLvcxD.js +267 -0
  93. package/dist/assets/gitGraphDiagram-V2S2FVAM-nDuDMsHh.js +65 -0
  94. package/dist/assets/graph-Blm-emkR.js +1 -0
  95. package/dist/assets/index-B8Yutsnm.css +1 -0
  96. package/dist/assets/index-Cf6gyhc3.js +654 -0
  97. package/dist/assets/infoDiagram-HS3SLOUP-CmzE3q0R.js +2 -0
  98. package/dist/assets/init-Gi6I4Gst.js +1 -0
  99. package/dist/assets/journeyDiagram-XKPGCS4Q-CYaW7LrP.js +139 -0
  100. package/dist/assets/kanban-definition-3W4ZIXB7-mkmW7p0j.js +89 -0
  101. package/dist/assets/layout-Ct2kFIId.js +1 -0
  102. package/dist/assets/linear-CKG9EwIJ.js +1 -0
  103. package/dist/assets/mermaid.core-CaKcUnnd.js +250 -0
  104. package/dist/assets/mindmap-definition-VGOIOE7T-Bct-pYin.js +68 -0
  105. package/dist/assets/ordinal-Cboi1Yqb.js +1 -0
  106. package/dist/assets/pieDiagram-ADFJNKIX-C8wFD4Jr.js +30 -0
  107. package/dist/assets/quadrantDiagram-AYHSOK5B-CuiE_f46.js +7 -0
  108. package/dist/assets/requirementDiagram-UZGBJVZJ-C4v1CqU8.js +64 -0
  109. package/dist/assets/sankeyDiagram-TZEHDZUN-keAqqs-Y.js +10 -0
  110. package/dist/assets/sequenceDiagram-WL72ISMW-ClNbvKTB.js +145 -0
  111. package/dist/assets/stateDiagram-FKZM4ZOC-D8We-2vq.js +1 -0
  112. package/dist/assets/stateDiagram-v2-4FDKWEC3-BUURfjrE.js +1 -0
  113. package/dist/assets/timeline-definition-IT6M3QCI-DsvEMT5T.js +61 -0
  114. package/dist/assets/treemap-GDKQZRPO-TJrzN7Bj.js +162 -0
  115. package/dist/assets/vendor-codemirror-ansM-eig.js +33 -0
  116. package/dist/assets/vendor-react-BWF6YQQ3.js +8 -0
  117. package/dist/assets/vendor-xterm-DfcmCpbH.js +66 -0
  118. package/dist/assets/xychartDiagram-PRI3JC2R-BdATmY5F.js +7 -0
  119. package/dist/clear-cache.html +85 -0
  120. package/dist/convert-icons.md +53 -0
  121. package/dist/dr-claw.png +0 -0
  122. package/dist/favicon.png +0 -0
  123. package/dist/generate-icons.js +49 -0
  124. package/dist/icons/claude-ai-icon.svg +1 -0
  125. package/dist/icons/codex-white.svg +3 -0
  126. package/dist/icons/codex.svg +3 -0
  127. package/dist/icons/cursor-white.svg +12 -0
  128. package/dist/icons/cursor.svg +1 -0
  129. package/dist/icons/file.svg +16997 -0
  130. package/dist/icons/generate-icons.md +19 -0
  131. package/dist/icons/icon-128x128.png +0 -0
  132. package/dist/icons/icon-144x144.png +0 -0
  133. package/dist/icons/icon-152x152.png +0 -0
  134. package/dist/icons/icon-192x192.png +0 -0
  135. package/dist/icons/icon-384x384.png +0 -0
  136. package/dist/icons/icon-512x512.png +0 -0
  137. package/dist/icons/icon-72x72.png +0 -0
  138. package/dist/icons/icon-96x96.png +0 -0
  139. package/dist/icons/news/arxiv.svg +1 -0
  140. package/dist/icons/news/huggingface.svg +8 -0
  141. package/dist/icons/news/x-black.png +0 -0
  142. package/dist/icons/news/x-white.png +0 -0
  143. package/dist/icons/news/xiaohongshu.png +0 -0
  144. package/dist/index.html +51 -0
  145. package/dist/leveraged-cognition.png +0 -0
  146. package/dist/manifest.json +61 -0
  147. package/dist/screenshots/chat.png +0 -0
  148. package/dist/screenshots/new_claw.png +0 -0
  149. package/dist/screenshots/news_dashboard.png +0 -0
  150. package/dist/screenshots/project_dashboard.png +0 -0
  151. package/dist/screenshots/research_lab.png +0 -0
  152. package/dist/screenshots/skill_library.png +0 -0
  153. package/dist/screenshots/task_list.png +0 -0
  154. package/dist/screenshots/terminal_example.png +0 -0
  155. package/dist/screenshots/terminal_example1.png +0 -0
  156. package/dist/sw.js +58 -0
  157. package/dist/wechat-group-qr.jpg +0 -0
  158. package/package.json +133 -0
  159. package/public/api-docs.html +878 -0
  160. package/public/clear-cache.html +85 -0
  161. package/public/convert-icons.md +53 -0
  162. package/public/dr-claw.png +0 -0
  163. package/public/favicon.png +0 -0
  164. package/public/generate-icons.js +49 -0
  165. package/public/icons/claude-ai-icon.svg +1 -0
  166. package/public/icons/codex-white.svg +3 -0
  167. package/public/icons/codex.svg +3 -0
  168. package/public/icons/cursor-white.svg +12 -0
  169. package/public/icons/cursor.svg +1 -0
  170. package/public/icons/file.svg +16997 -0
  171. package/public/icons/generate-icons.md +19 -0
  172. package/public/icons/icon-128x128.png +0 -0
  173. package/public/icons/icon-144x144.png +0 -0
  174. package/public/icons/icon-152x152.png +0 -0
  175. package/public/icons/icon-192x192.png +0 -0
  176. package/public/icons/icon-384x384.png +0 -0
  177. package/public/icons/icon-512x512.png +0 -0
  178. package/public/icons/icon-72x72.png +0 -0
  179. package/public/icons/icon-96x96.png +0 -0
  180. package/public/icons/news/arxiv.svg +1 -0
  181. package/public/icons/news/huggingface.svg +8 -0
  182. package/public/icons/news/x-black.png +0 -0
  183. package/public/icons/news/x-white.png +0 -0
  184. package/public/icons/news/xiaohongshu.png +0 -0
  185. package/public/leveraged-cognition.png +0 -0
  186. package/public/manifest.json +61 -0
  187. package/public/screenshots/chat.png +0 -0
  188. package/public/screenshots/new_claw.png +0 -0
  189. package/public/screenshots/news_dashboard.png +0 -0
  190. package/public/screenshots/project_dashboard.png +0 -0
  191. package/public/screenshots/research_lab.png +0 -0
  192. package/public/screenshots/skill_library.png +0 -0
  193. package/public/screenshots/task_list.png +0 -0
  194. package/public/screenshots/terminal_example.png +0 -0
  195. package/public/screenshots/terminal_example1.png +0 -0
  196. package/public/sw.js +58 -0
  197. package/public/wechat-group-qr.jpg +0 -0
  198. package/scripts/export-skills-catalog-v2.mjs +746 -0
  199. package/scripts/fix-node-pty.js +45 -0
  200. package/scripts/gemini_image_core.py +470 -0
  201. package/scripts/install-python-tools.js +64 -0
  202. package/server/AGENTS.md +73 -0
  203. package/server/__tests__/gemini-legacy-thought.test.mjs +88 -0
  204. package/server/__tests__/runtimePorts.test.mjs +115 -0
  205. package/server/__tests__/session-delete.test.mjs +166 -0
  206. package/server/bin/compute-node +70 -0
  207. package/server/claude-sdk.js +799 -0
  208. package/server/cli-chat.js +488 -0
  209. package/server/cli.js +354 -0
  210. package/server/compute-node.js +439 -0
  211. package/server/constants/config.js +5 -0
  212. package/server/cursor-cli.js +340 -0
  213. package/server/database/auth.db +0 -0
  214. package/server/database/db.js +1852 -0
  215. package/server/database/init.sql +194 -0
  216. package/server/gemini-cli.js +1352 -0
  217. package/server/index.js +3154 -0
  218. package/server/load-env.js +71 -0
  219. package/server/local-gpu.js +860 -0
  220. package/server/middleware/auth.js +100 -0
  221. package/server/openai-codex.js +724 -0
  222. package/server/openrouter.js +890 -0
  223. package/server/project-token-usage.js +407 -0
  224. package/server/projects.js +3973 -0
  225. package/server/routes/agent.js +1272 -0
  226. package/server/routes/auth.js +128 -0
  227. package/server/routes/auto-research.js +558 -0
  228. package/server/routes/cli-auth.js +958 -0
  229. package/server/routes/codex.js +344 -0
  230. package/server/routes/commands.js +601 -0
  231. package/server/routes/compute.js +358 -0
  232. package/server/routes/cursor.js +795 -0
  233. package/server/routes/git.js +1348 -0
  234. package/server/routes/mcp-utils.js +48 -0
  235. package/server/routes/mcp.js +618 -0
  236. package/server/routes/news.js +711 -0
  237. package/server/routes/projects.js +633 -0
  238. package/server/routes/references.js +338 -0
  239. package/server/routes/settings.js +285 -0
  240. package/server/routes/skills.js +914 -0
  241. package/server/routes/taskmaster.js +2566 -0
  242. package/server/routes/telemetry.js +89 -0
  243. package/server/routes/user.js +155 -0
  244. package/server/scripts/fix_session_names.js +57 -0
  245. package/server/scripts/research-news/common_words.py +19 -0
  246. package/server/scripts/research-news/link_keywords.py +298 -0
  247. package/server/scripts/research-news/scan_existing_notes.py +266 -0
  248. package/server/scripts/research-news/scoring_utils.py +227 -0
  249. package/server/scripts/research-news/search_arxiv.py +842 -0
  250. package/server/scripts/research-news/search_huggingface.py +438 -0
  251. package/server/scripts/research-news/search_x.py +490 -0
  252. package/server/scripts/research-news/search_xiaohongshu.py +610 -0
  253. package/server/taskmaster-templates/README.md +150 -0
  254. package/server/taskmaster-templates/README.zh-CN.md +150 -0
  255. package/server/taskmaster-templates/ai-research-dataset.json +230 -0
  256. package/server/taskmaster-templates/ai-research-method-model.json +230 -0
  257. package/server/taskmaster-templates/ai-research-position-paper.json +230 -0
  258. package/server/telemetry.js +121 -0
  259. package/server/templates/AGENTS.md +103 -0
  260. package/server/templates/CLAUDE.md +104 -0
  261. package/server/templates/GEMINI.md +103 -0
  262. package/server/templates/cursor-project.md +108 -0
  263. package/server/templates/index.js +37 -0
  264. package/server/utils/__tests__/sessionTokenUsage.test.js +82 -0
  265. package/server/utils/auto-research-mailer.js +57 -0
  266. package/server/utils/cliResolution.js +170 -0
  267. package/server/utils/commandParser.js +303 -0
  268. package/server/utils/cursorCommand.js +61 -0
  269. package/server/utils/geminiApiKey.js +28 -0
  270. package/server/utils/gitConfig.js +24 -0
  271. package/server/utils/imageAttachmentFiles.js +35 -0
  272. package/server/utils/mcp-detector.js +198 -0
  273. package/server/utils/parsers/bibtex-parser.js +207 -0
  274. package/server/utils/permissions.js +100 -0
  275. package/server/utils/runtimePorts.js +212 -0
  276. package/server/utils/sessionFormatting.js +68 -0
  277. package/server/utils/sessionIndex.js +100 -0
  278. package/server/utils/sessionMode.js +95 -0
  279. package/server/utils/sessionTokenUsage.js +105 -0
  280. package/server/utils/taskmaster-websocket.js +129 -0
  281. package/server/utils/zotero-client.js +136 -0
  282. package/shared/errorClassifier.js +102 -0
  283. package/shared/geminiThinkingSupport.d.ts +22 -0
  284. package/shared/geminiThinkingSupport.js +96 -0
  285. package/shared/geminiThoughtParser.d.ts +8 -0
  286. package/shared/geminiThoughtParser.js +30 -0
  287. package/shared/modelConstants.js +139 -0
@@ -0,0 +1,136 @@
1
+ /**
2
+ * Zotero Client — interface for local Zotero desktop API.
3
+ *
4
+ * Local API: http://localhost:23119/api (Zotero desktop, no auth)
5
+ */
6
+
7
+ const LOCAL_BASE = 'http://localhost:23119/api';
8
+
9
+ // ---------------------------------------------------------------------------
10
+ // Helpers
11
+ // ---------------------------------------------------------------------------
12
+
13
+ async function fetchJson(url, options = {}) {
14
+ const res = await fetch(url, { ...options, signal: AbortSignal.timeout(15000) });
15
+ if (!res.ok) {
16
+ const text = await res.text().catch(() => '');
17
+ throw new Error(`Zotero API ${res.status}: ${text}`);
18
+ }
19
+ return res.json();
20
+ }
21
+
22
+ async function fetchBuffer(url, options = {}) {
23
+ const res = await fetch(url, { ...options, signal: AbortSignal.timeout(60000) });
24
+ if (!res.ok) {
25
+ throw new Error(`Zotero file fetch ${res.status}`);
26
+ }
27
+ const arrayBuffer = await res.arrayBuffer();
28
+ return Buffer.from(arrayBuffer);
29
+ }
30
+
31
+ function mapZoteroItem(raw) {
32
+ const d = raw.data || raw;
33
+ const creators = (d.creators || []).map((c) => ({
34
+ family: c.lastName || '',
35
+ given: c.firstName || '',
36
+ }));
37
+ return {
38
+ sourceId: d.key || raw.key,
39
+ title: d.title || 'Untitled',
40
+ authors: creators,
41
+ year: d.date ? parseInt(d.date.match(/\b(\d{4})\b/)?.[1], 10) || null : null,
42
+ abstract: d.abstractNote || null,
43
+ doi: d.DOI || null,
44
+ url: d.url || null,
45
+ journal: d.publicationTitle || d.journalAbbreviation || null,
46
+ itemType: d.itemType || 'article',
47
+ keywords: (d.tags || []).map((t) => t.tag),
48
+ citationKey: d.citationKey || d.extra?.match(/Citation Key:\s*(\S+)/i)?.[1] || null,
49
+ rawData: raw,
50
+ };
51
+ }
52
+
53
+ // ---------------------------------------------------------------------------
54
+ // Local client (Zotero Desktop — localhost:23119)
55
+ // ---------------------------------------------------------------------------
56
+
57
+ export class ZoteroLocalClient {
58
+ constructor() {
59
+ this.base = LOCAL_BASE;
60
+ }
61
+
62
+ /** Detailed status: { available, running, apiDisabled }. */
63
+ async checkStatus() {
64
+ try {
65
+ const res = await fetch(`${this.base}/users/0/items?limit=1`, { signal: AbortSignal.timeout(3000) });
66
+ if (res.ok) return { available: true, running: true, apiDisabled: false };
67
+ // 403 means Zotero is running but local API is disabled
68
+ return { available: false, running: true, apiDisabled: true };
69
+ } catch {
70
+ return { available: false, running: false, apiDisabled: false };
71
+ }
72
+ }
73
+
74
+ /** Returns true if the Zotero desktop app is reachable with API enabled. */
75
+ async isAvailable() {
76
+ const { available } = await this.checkStatus();
77
+ return available;
78
+ }
79
+
80
+ async getLibraries() {
81
+ // Local API only has the personal library (users/0).
82
+ return [{ id: 0, type: 'user', name: 'My Library' }];
83
+ }
84
+
85
+ async getCollections(libraryId = 0) {
86
+ const raw = await fetchJson(`${this.base}/users/${libraryId}/collections`);
87
+ return raw.map((c) => ({
88
+ key: c.data?.key || c.key,
89
+ name: c.data?.name || c.name || '',
90
+ parentKey: c.data?.parentCollection || null,
91
+ }));
92
+ }
93
+
94
+ async getItems(libraryId = 0, { collectionKey, query, limit = 50, start = 0 } = {}) {
95
+ const params = new URLSearchParams({ limit: String(limit), start: String(start), itemType: '-attachment' });
96
+ if (query) params.set('q', query);
97
+ const base = collectionKey
98
+ ? `${this.base}/users/${libraryId}/collections/${encodeURIComponent(collectionKey)}/items`
99
+ : `${this.base}/users/${libraryId}/items`;
100
+ const raw = await fetchJson(`${base}?${params}`);
101
+ return raw.map(mapZoteroItem);
102
+ }
103
+
104
+ async searchItems(libraryId = 0, query) {
105
+ return this.getItems(libraryId, { query });
106
+ }
107
+
108
+ async getItemPdf(libraryId = 0, itemKey) {
109
+ // Fetch children to find the PDF attachment.
110
+ const children = await fetchJson(`${this.base}/users/${libraryId}/items/${encodeURIComponent(itemKey)}/children`);
111
+ const pdfAttachment = children.find(
112
+ (c) => (c.data?.contentType || c.contentType) === 'application/pdf',
113
+ );
114
+ if (!pdfAttachment) return null;
115
+ const attachKey = pdfAttachment.data?.key || pdfAttachment.key;
116
+ return fetchBuffer(`${this.base}/users/${libraryId}/items/${attachKey}/file`);
117
+ }
118
+ }
119
+
120
+ // ---------------------------------------------------------------------------
121
+ // Factory: local-only
122
+ // ---------------------------------------------------------------------------
123
+
124
+ /**
125
+ * Returns {client, mode} where mode is 'local' | null.
126
+ */
127
+ export async function getZoteroClient() {
128
+ const local = new ZoteroLocalClient();
129
+ const localStatus = await local.checkStatus();
130
+
131
+ if (localStatus.available) {
132
+ return { client: local, mode: 'local', localRunning: true, localApiDisabled: false };
133
+ }
134
+
135
+ return { client: null, mode: null, localRunning: localStatus.running, localApiDisabled: localStatus.apiDisabled };
136
+ }
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Shared error classification for all agent providers.
3
+ * Single source of truth for error type detection and retryability.
4
+ */
5
+
6
+ export const ERROR_TYPES = /** @type {const} */ ({
7
+ USAGE_LIMIT: 'usage_limit',
8
+ OVERLOADED: 'overloaded',
9
+ NETWORK: 'network',
10
+ AUTH: 'auth',
11
+ UNKNOWN: 'unknown',
12
+ });
13
+
14
+ const RE_USAGE_LIMIT = /usage[_ ]limit|rate[_ ]limit|Too Many Requests|\b429\b|RESOURCE_EXHAUSTED|capacity exhausted|quota/i;
15
+ const RE_OVERLOADED = /overloaded|MODEL_CAPACITY_EXHAUSTED|No capacity available|server[_ ]error/i;
16
+ const RE_NETWORK = /network|ECONNREFUSED|ETIMEDOUT|ECONNRESET|ENOTFOUND|socket hang up/i;
17
+ const RE_AUTH = /\bauth\b|unauthorized|forbidden|authentication[_ ]failed|billing[_ ]error|invalid[_ ]api[_ ]key/i;
18
+
19
+ function makeResult(errorType) {
20
+ return { errorType, isRetryable: errorType !== ERROR_TYPES.AUTH };
21
+ }
22
+
23
+ /** @param {string} message */
24
+ export function classifyError(message) {
25
+ const msg = String(message || '');
26
+
27
+ if (RE_USAGE_LIMIT.test(msg)) return makeResult(ERROR_TYPES.USAGE_LIMIT);
28
+ if (RE_OVERLOADED.test(msg)) return makeResult(ERROR_TYPES.OVERLOADED);
29
+ if (RE_NETWORK.test(msg)) return makeResult(ERROR_TYPES.NETWORK);
30
+ if (RE_AUTH.test(msg)) return makeResult(ERROR_TYPES.AUTH);
31
+
32
+ return makeResult(ERROR_TYPES.UNKNOWN);
33
+ }
34
+
35
+ export const TRANSIENT_ERROR_TYPES = /** @type {const} */ ([
36
+ ERROR_TYPES.NETWORK,
37
+ ERROR_TYPES.OVERLOADED,
38
+ ]);
39
+
40
+ /**
41
+ * Per-provider SDK error code → ERROR_TYPES mappings.
42
+ *
43
+ * Claude (AssistantMessageError):
44
+ * 'authentication_failed' | 'billing_error' | 'rate_limit' |
45
+ * 'invalid_request' | 'server_error' | 'max_output_tokens' | 'unknown'
46
+ *
47
+ * Codex (ResponseError.code):
48
+ * 'server_error' | 'rate_limit_exceeded' | 'invalid_prompt' | ...
49
+ *
50
+ * Gemini (structured error codes from CLI stderr):
51
+ * 'RESOURCE_EXHAUSTED' | 'PERMISSION_DENIED' | 'INTERNAL' | ...
52
+ */
53
+ const CLAUDE_ERROR_MAP = {
54
+ rate_limit: ERROR_TYPES.USAGE_LIMIT,
55
+ authentication_failed: ERROR_TYPES.AUTH,
56
+ billing_error: ERROR_TYPES.AUTH,
57
+ server_error: ERROR_TYPES.OVERLOADED,
58
+ invalid_request: ERROR_TYPES.UNKNOWN,
59
+ max_output_tokens: ERROR_TYPES.UNKNOWN,
60
+ unknown: ERROR_TYPES.UNKNOWN,
61
+ };
62
+
63
+ const CODEX_ERROR_MAP = {
64
+ rate_limit_exceeded: ERROR_TYPES.USAGE_LIMIT,
65
+ server_error: ERROR_TYPES.OVERLOADED,
66
+ invalid_prompt: ERROR_TYPES.UNKNOWN,
67
+ };
68
+
69
+ const GEMINI_ERROR_MAP = {
70
+ RESOURCE_EXHAUSTED: ERROR_TYPES.USAGE_LIMIT,
71
+ PERMISSION_DENIED: ERROR_TYPES.AUTH,
72
+ UNAUTHENTICATED: ERROR_TYPES.AUTH,
73
+ INTERNAL: ERROR_TYPES.OVERLOADED,
74
+ UNAVAILABLE: ERROR_TYPES.OVERLOADED,
75
+ };
76
+
77
+ const PROVIDER_ERROR_MAPS = {
78
+ claude: CLAUDE_ERROR_MAP,
79
+ codex: CODEX_ERROR_MAP,
80
+ gemini: GEMINI_ERROR_MAP,
81
+ };
82
+
83
+ /**
84
+ * Classify from a provider SDK structured error code.
85
+ * @param {string} sdkErrorCode - e.g. 'rate_limit', 'rate_limit_exceeded', 'RESOURCE_EXHAUSTED'
86
+ * @param {'claude' | 'codex' | 'gemini'} [provider] - provider name for precise mapping; omit to search all
87
+ * @returns {{ errorType: string, isRetryable: boolean }}
88
+ */
89
+ export function classifySDKError(sdkErrorCode, provider) {
90
+ let errorType;
91
+
92
+ if (provider && PROVIDER_ERROR_MAPS[provider]) {
93
+ errorType = PROVIDER_ERROR_MAPS[provider][sdkErrorCode];
94
+ } else {
95
+ for (const map of Object.values(PROVIDER_ERROR_MAPS)) {
96
+ errorType = map[sdkErrorCode];
97
+ if (errorType) break;
98
+ }
99
+ }
100
+
101
+ return makeResult(errorType || ERROR_TYPES.UNKNOWN);
102
+ }
@@ -0,0 +1,22 @@
1
+ export type GeminiThinkingModeId =
2
+ | 'default'
3
+ | 'minimal'
4
+ | 'low'
5
+ | 'medium'
6
+ | 'high'
7
+ | 'dynamic'
8
+ | 'off'
9
+ | 'light'
10
+ | 'balanced'
11
+ | 'deep'
12
+ | 'max';
13
+
14
+ export type GeminiThinkingFamily = 'gemini-3' | 'gemini-2.5' | null;
15
+
16
+ export function getGeminiThinkingFamily(model: string): GeminiThinkingFamily;
17
+ export function getSupportedGeminiThinkingModes(model: string): GeminiThinkingModeId[];
18
+ export function supportsExplicitGeminiThinkingMode(model: string): boolean;
19
+ export function buildGeminiThinkingConfig(
20
+ model: string,
21
+ mode?: GeminiThinkingModeId | null,
22
+ ): { thinkingLevel?: 'MINIMAL' | 'LOW' | 'MEDIUM' | 'HIGH'; thinkingBudget?: number } | null;
@@ -0,0 +1,96 @@
1
+ export const GEMINI_THINKING_MODE_IDS = [
2
+ 'default',
3
+ 'minimal',
4
+ 'low',
5
+ 'medium',
6
+ 'high',
7
+ 'dynamic',
8
+ 'off',
9
+ 'light',
10
+ 'balanced',
11
+ 'deep',
12
+ 'max',
13
+ ];
14
+
15
+ function isGemini3Model(model) {
16
+ return typeof model === 'string' && model.startsWith('gemini-3');
17
+ }
18
+
19
+ function isGemini25Model(model) {
20
+ return typeof model === 'string' && model.startsWith('gemini-2.5');
21
+ }
22
+
23
+ export function getGeminiThinkingFamily(model) {
24
+ if (isGemini3Model(model)) return 'gemini-3';
25
+ if (isGemini25Model(model)) return 'gemini-2.5';
26
+ return null;
27
+ }
28
+
29
+ export function getSupportedGeminiThinkingModes(model) {
30
+ switch (model) {
31
+ case 'gemini-3.1-pro-preview':
32
+ return ['default', 'low', 'medium', 'high'];
33
+ case 'gemini-3.1-flash-lite-preview':
34
+ case 'gemini-3-flash-preview':
35
+ return ['default', 'minimal', 'low', 'medium', 'high'];
36
+ case 'gemini-2.5-pro':
37
+ return ['default', 'dynamic', 'light', 'balanced', 'deep', 'max'];
38
+ case 'gemini-2.5-flash':
39
+ case 'gemini-2.5-flash-lite':
40
+ return ['default', 'dynamic', 'off', 'light', 'balanced', 'deep'];
41
+ default:
42
+ return ['default'];
43
+ }
44
+ }
45
+
46
+ export function supportsExplicitGeminiThinkingMode(model) {
47
+ return getSupportedGeminiThinkingModes(model).length > 1;
48
+ }
49
+
50
+ export function buildGeminiThinkingConfig(model, mode) {
51
+ if (!model || !mode || mode === 'default') {
52
+ return null;
53
+ }
54
+
55
+ if (isGemini3Model(model)) {
56
+ const levelMap = {
57
+ minimal: 'MINIMAL',
58
+ low: 'LOW',
59
+ medium: 'MEDIUM',
60
+ high: 'HIGH',
61
+ };
62
+ const thinkingLevel = levelMap[mode];
63
+ return thinkingLevel ? { thinkingLevel } : null;
64
+ }
65
+
66
+ if (isGemini25Model(model)) {
67
+ const budgetMapByModel = {
68
+ 'gemini-2.5-pro': {
69
+ dynamic: -1,
70
+ light: 1024,
71
+ balanced: 8192,
72
+ deep: 24576,
73
+ max: 32768,
74
+ },
75
+ 'gemini-2.5-flash': {
76
+ dynamic: -1,
77
+ off: 0,
78
+ light: 1024,
79
+ balanced: 8192,
80
+ deep: 24576,
81
+ },
82
+ 'gemini-2.5-flash-lite': {
83
+ dynamic: -1,
84
+ off: 0,
85
+ light: 512,
86
+ balanced: 8192,
87
+ deep: 24576,
88
+ },
89
+ };
90
+
91
+ const budget = budgetMapByModel[model]?.[mode];
92
+ return Number.isInteger(budget) ? { thinkingBudget: budget } : null;
93
+ }
94
+
95
+ return null;
96
+ }
@@ -0,0 +1,8 @@
1
+ export type LegacyGeminiThoughtSegment = {
2
+ content: string;
3
+ isThinking: boolean;
4
+ };
5
+
6
+ export function splitLegacyGeminiThoughtContent(
7
+ text: string,
8
+ ): LegacyGeminiThoughtSegment[] | null;
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Splits Gemini legacy "[Thought: true]" markers in assistant text
3
+ * into structured segments with thinking/final flags.
4
+ *
5
+ * Shared between server (gemini-cli.js) and client (chatFormatting.ts)
6
+ * to ensure consistent parsing behavior.
7
+ *
8
+ * @param {string} text - The assistant message text to parse.
9
+ * @returns {{ content: string, isThinking: boolean }[] | null}
10
+ * Array of segments if markers are found, null otherwise.
11
+ */
12
+ export function splitLegacyGeminiThoughtContent(text) {
13
+ if (!text || typeof text !== 'string' || !/\[Thought:\s*true\]/i.test(text)) {
14
+ return null;
15
+ }
16
+
17
+ const segments = text
18
+ .split(/\n?\s*\[Thought:\s*true\]\s*/i)
19
+ .map((segment) => segment.trim())
20
+ .filter(Boolean);
21
+
22
+ if (segments.length < 2) {
23
+ return null;
24
+ }
25
+
26
+ return segments.map((segment, index) => ({
27
+ content: segment,
28
+ isThinking: index < segments.length - 1,
29
+ }));
30
+ }
@@ -0,0 +1,139 @@
1
+ /**
2
+ * Centralized Model Definitions
3
+ * Single source of truth for all supported AI models
4
+ */
5
+
6
+ /**
7
+ * Claude (Anthropic) Models
8
+ *
9
+ * Note: Claude uses two different formats:
10
+ * - SDK format ('sonnet', 'opus') - used by the UI and claude-sdk.js
11
+ * - API format ('claude-sonnet-4.5') - used by slash commands for display
12
+ */
13
+ export const CLAUDE_MODELS = {
14
+ // Models in SDK format (what the actual SDK accepts)
15
+ OPTIONS: [
16
+ { value: 'sonnet', label: 'Sonnet' },
17
+ { value: 'opus', label: 'Opus' },
18
+ { value: 'haiku', label: 'Haiku' },
19
+ { value: 'opusplan', label: 'Opus (Plan Mode Only)' },
20
+ { value: 'sonnet[1m]', label: 'Sonnet [1M]' },
21
+ { value: 'claude-opus-4-6', label: 'Opus 4.6' }
22
+ ],
23
+
24
+ DEFAULT: (typeof process !== 'undefined' && process.env?.ANTHROPIC_MODEL) || 'claude-opus-4-6'
25
+ };
26
+
27
+ /**
28
+ * Cursor Models
29
+ */
30
+ export const CURSOR_MODELS = {
31
+ OPTIONS: [
32
+ { value: 'gpt-5.2-high', label: 'GPT-5.2 High' },
33
+ { value: 'gemini-3-pro', label: 'Gemini 3 Pro' },
34
+ { value: 'opus-4.5-thinking', label: 'Claude 4.5 Opus (Thinking)' },
35
+ { value: 'gpt-5.2', label: 'GPT-5.2' },
36
+ { value: 'gpt-5.1', label: 'GPT-5.1' },
37
+ { value: 'gpt-5.1-high', label: 'GPT-5.1 High' },
38
+ { value: 'composer-1', label: 'Composer 1' },
39
+ { value: 'auto', label: 'Auto' },
40
+ { value: 'sonnet-4.5', label: 'Claude 4.5 Sonnet' },
41
+ { value: 'sonnet-4.5-thinking', label: 'Claude 4.5 Sonnet (Thinking)' },
42
+ { value: 'opus-4.5', label: 'Claude 4.5 Opus' },
43
+ { value: 'gpt-5.1-codex', label: 'GPT-5.1 Codex' },
44
+ { value: 'gpt-5.1-codex-high', label: 'GPT-5.1 Codex High' },
45
+ { value: 'gpt-5.1-codex-max', label: 'GPT-5.1 Codex Max' },
46
+ { value: 'gpt-5.1-codex-max-high', label: 'GPT-5.1 Codex Max High' },
47
+ { value: 'opus-4.1', label: 'Claude 4.1 Opus' },
48
+ { value: 'grok', label: 'Grok' }
49
+ ],
50
+
51
+ DEFAULT: 'gpt-5'
52
+ };
53
+
54
+ /**
55
+ * Codex (OpenAI) Models
56
+ */
57
+ export const CODEX_MODELS = {
58
+ OPTIONS: [
59
+ { value: 'gpt-5.4', label: 'GPT-5.4' },
60
+ { value: 'gpt-5.3-codex', label: 'GPT-5.3 Codex' },
61
+ { value: 'gpt-5.2-codex', label: 'GPT-5.2 Codex' },
62
+ { value: 'gpt-5.2', label: 'GPT-5.2' },
63
+ { value: 'gpt-5.1-codex-max', label: 'GPT-5.1 Codex Max' },
64
+ { value: 'o3', label: 'O3' },
65
+ { value: 'o4-mini', label: 'O4-mini' }
66
+ ],
67
+
68
+ DEFAULT: 'gpt-5.4'
69
+ };
70
+
71
+ /**
72
+ * OpenRouter Models
73
+ * Users can also type any model slug from https://openrouter.ai/models
74
+ */
75
+ export const OPENROUTER_MODELS = {
76
+ OPTIONS: [
77
+ // Anthropic
78
+ { value: 'anthropic/claude-sonnet-4', label: 'Claude Sonnet 4 (Anthropic)' },
79
+ { value: 'anthropic/claude-opus-4', label: 'Claude Opus 4 (Anthropic)' },
80
+ { value: 'anthropic/claude-haiku-3.5', label: 'Claude 3.5 Haiku (Anthropic)' },
81
+ // OpenAI
82
+ { value: 'openai/gpt-5', label: 'GPT-5 (OpenAI)' },
83
+ { value: 'openai/gpt-4.1', label: 'GPT-4.1 (OpenAI)' },
84
+ { value: 'openai/gpt-4.1-mini', label: 'GPT-4.1 Mini (OpenAI)' },
85
+ { value: 'openai/o3', label: 'O3 (OpenAI)' },
86
+ { value: 'openai/o4-mini', label: 'O4 Mini (OpenAI)' },
87
+ // Google
88
+ { value: 'google/gemini-2.5-pro', label: 'Gemini 2.5 Pro (Google)' },
89
+ { value: 'google/gemini-2.5-flash', label: 'Gemini 2.5 Flash (Google)' },
90
+ // DeepSeek
91
+ { value: 'deepseek/deepseek-r1', label: 'DeepSeek R1' },
92
+ { value: 'deepseek/deepseek-chat-v3-0324', label: 'DeepSeek V3 0324' },
93
+ // Meta
94
+ { value: 'meta-llama/llama-4-maverick', label: 'Llama 4 Maverick (Meta)' },
95
+ { value: 'meta-llama/llama-4-scout', label: 'Llama 4 Scout (Meta)' },
96
+ // Mistral
97
+ { value: 'mistralai/mistral-large', label: 'Mistral Large' },
98
+ { value: 'mistralai/codestral', label: 'Codestral (Mistral)' },
99
+ // Qwen
100
+ { value: 'qwen/qwen3-235b-a22b', label: 'Qwen3 235B' },
101
+ { value: 'qwen/qwen3-32b', label: 'Qwen3 32B' },
102
+ // xAI
103
+ { value: 'x-ai/grok-3', label: 'Grok 3 (xAI)' },
104
+ { value: 'x-ai/grok-3-mini', label: 'Grok 3 Mini (xAI)' },
105
+ // Cohere
106
+ { value: 'cohere/command-a', label: 'Command A (Cohere)' },
107
+ ],
108
+
109
+ ALLOWS_CUSTOM: true,
110
+
111
+ DEFAULT: (typeof process !== 'undefined' && process.env?.OPENROUTER_MODEL) || 'anthropic/claude-sonnet-4'
112
+ };
113
+
114
+ /**
115
+ * Local GPU Models (open-source models for self-hosted deployment)
116
+ */
117
+ export const LOCAL_MODELS = {
118
+ OPTIONS: [],
119
+
120
+ IS_LOCAL: true,
121
+
122
+ DEFAULT: ''
123
+ };
124
+
125
+ /**
126
+ * Gemini (Google) Models
127
+ */
128
+ export const GEMINI_MODELS = {
129
+ OPTIONS: [
130
+ { value: 'gemini-3.1-pro-preview', label: 'Gemini 3.1 Pro Preview' },
131
+ { value: 'gemini-3.1-flash-lite-preview', label: 'Gemini 3.1 Flash Lite Preview' },
132
+ { value: 'gemini-3-flash-preview', label: 'Gemini 3 Flash Preview' },
133
+ { value: 'gemini-2.5-pro', label: 'Gemini 2.5 Pro' },
134
+ { value: 'gemini-2.5-flash', label: 'Gemini 2.5 Flash' },
135
+ { value: 'gemini-2.5-flash-lite', label: 'Gemini 2.5 Flash Lite' }
136
+ ],
137
+
138
+ DEFAULT: 'gemini-3-flash-preview'
139
+ };