@researai/deepscientist 1.5.3 → 1.5.4

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 (90) hide show
  1. package/README.md +1 -1
  2. package/bin/ds.js +27 -187
  3. package/docs/en/00_QUICK_START.md +1 -1
  4. package/docs/en/01_SETTINGS_REFERENCE.md +13 -4
  5. package/docs/en/99_ACKNOWLEDGEMENTS.md +1 -0
  6. package/docs/images/connectors/discord-setup-overview.svg +52 -0
  7. package/docs/images/connectors/feishu-setup-overview.svg +53 -0
  8. package/docs/images/connectors/slack-setup-overview.svg +51 -0
  9. package/docs/images/connectors/telegram-setup-overview.svg +55 -0
  10. package/docs/images/connectors/whatsapp-setup-overview.svg +51 -0
  11. package/docs/images/lingzhu/lingzhu-openclaw-config.svg +17 -0
  12. package/docs/images/lingzhu/lingzhu-platform-values.svg +16 -0
  13. package/docs/images/lingzhu/lingzhu-settings-overview.svg +30 -0
  14. package/docs/images/qq/tencent-cloud-qq-chat.png +0 -0
  15. package/docs/images/qq/tencent-cloud-qq-register.png +0 -0
  16. package/docs/images/quickstart/00-home.png +0 -0
  17. package/docs/images/quickstart/01-start-research.png +0 -0
  18. package/docs/images/quickstart/02-list-quest.png +0 -0
  19. package/docs/zh/00_QUICK_START.md +1 -1
  20. package/docs/zh/01_SETTINGS_REFERENCE.md +14 -5
  21. package/docs/zh/99_ACKNOWLEDGEMENTS.md +1 -0
  22. package/package.json +8 -4
  23. package/pyproject.toml +1 -1
  24. package/src/deepscientist/__init__.py +1 -1
  25. package/src/deepscientist/bridges/base.py +2 -1
  26. package/src/deepscientist/bridges/connectors.py +2 -1
  27. package/src/deepscientist/channels/discord_gateway.py +2 -2
  28. package/src/deepscientist/channels/qq_gateway.py +2 -2
  29. package/src/deepscientist/channels/slack_socket.py +2 -2
  30. package/src/deepscientist/channels/telegram_polling.py +2 -1
  31. package/src/deepscientist/cli.py +4 -1
  32. package/src/deepscientist/config/models.py +7 -3
  33. package/src/deepscientist/config/service.py +52 -2
  34. package/src/deepscientist/daemon/api/handlers.py +6 -6
  35. package/src/deepscientist/daemon/app.py +65 -10
  36. package/src/deepscientist/network.py +78 -0
  37. package/src/deepscientist/prompts/builder.py +22 -3
  38. package/src/deepscientist/quest/service.py +1 -1
  39. package/src/deepscientist/skills/installer.py +77 -1
  40. package/src/prompts/system.md +78 -2
  41. package/src/skills/analysis-campaign/SKILL.md +8 -0
  42. package/src/skills/idea/SKILL.md +245 -3
  43. package/src/skills/write/SKILL.md +151 -1
  44. package/src/tui/package.json +1 -1
  45. package/src/ui/dist/assets/{AiManusChatView-qzChi9uh.js → AiManusChatView-BGLArZRn.js} +11 -11
  46. package/src/ui/dist/assets/{AnalysisPlugin-CcC_-UqN.js → AnalysisPlugin-BgDGSigG.js} +1 -1
  47. package/src/ui/dist/assets/{AutoFigurePlugin-DD8LkJLe.js → AutoFigurePlugin-B65HD7L4.js} +5 -5
  48. package/src/ui/dist/assets/{CliPlugin-DJJFfVmW.js → CliPlugin-CUqgsFHC.js} +9 -9
  49. package/src/ui/dist/assets/{CodeEditorPlugin-CrjkHNLh.js → CodeEditorPlugin-CF5EdvaS.js} +8 -8
  50. package/src/ui/dist/assets/{CodeViewerPlugin-obnD6G5R.js → CodeViewerPlugin-DEeU063D.js} +5 -5
  51. package/src/ui/dist/assets/{DocViewerPlugin-DB9SUQVd.js → DocViewerPlugin-Df-FuDlZ.js} +3 -3
  52. package/src/ui/dist/assets/{GitDiffViewerPlugin-DZLlNlD2.js → GitDiffViewerPlugin-RAnNaRxM.js} +1 -1
  53. package/src/ui/dist/assets/{ImageViewerPlugin-BGwfDZ0Y.js → ImageViewerPlugin-DXJ0ZJGg.js} +5 -5
  54. package/src/ui/dist/assets/{LabCopilotPanel-dfLptQcR.js → LabCopilotPanel-BlO-sKsj.js} +10 -10
  55. package/src/ui/dist/assets/{LabPlugin-CeGjAl3A.js → LabPlugin-BajPZW5v.js} +1 -1
  56. package/src/ui/dist/assets/{LatexPlugin-BBJ7kd1V.js → LatexPlugin-F1OEol8D.js} +7 -7
  57. package/src/ui/dist/assets/{MarkdownViewerPlugin-DKZi7BcB.js → MarkdownViewerPlugin-MhUupqwT.js} +4 -4
  58. package/src/ui/dist/assets/{MarketplacePlugin-C_k-9jD0.js → MarketplacePlugin-DxhIEsv0.js} +3 -3
  59. package/src/ui/dist/assets/{NotebookEditor-4R88_BMO.js → NotebookEditor-q7TkhewC.js} +1 -1
  60. package/src/ui/dist/assets/{PdfLoader-DwEFQLrw.js → PdfLoader-B8ZOTKFc.js} +1 -1
  61. package/src/ui/dist/assets/{PdfMarkdownPlugin-D-jdsqF8.js → PdfMarkdownPlugin-xFPvzvWh.js} +3 -3
  62. package/src/ui/dist/assets/{PdfViewerPlugin-CmeBGDY0.js → PdfViewerPlugin-EjEcsIB8.js} +10 -10
  63. package/src/ui/dist/assets/{SearchPlugin-Dlz2WKJ4.js → SearchPlugin-ixY-1lgW.js} +1 -1
  64. package/src/ui/dist/assets/{Stepper-ClOgzWM3.js → Stepper-gYFK2Pgz.js} +1 -1
  65. package/src/ui/dist/assets/{TextViewerPlugin-DDQWxibk.js → TextViewerPlugin-Cym6pv_n.js} +4 -4
  66. package/src/ui/dist/assets/{VNCViewer-CJXT0Nm8.js → VNCViewer-BPmIHcmK.js} +9 -9
  67. package/src/ui/dist/assets/{bibtex-DLr4Rtk4.js → bibtex-Btv6Wi7f.js} +1 -1
  68. package/src/ui/dist/assets/{code-DgKK408Y.js → code-BlG7g85c.js} +1 -1
  69. package/src/ui/dist/assets/{file-content-6HBqQnvQ.js → file-content-DBT5OfTZ.js} +1 -1
  70. package/src/ui/dist/assets/{file-diff-panel-Dhu0TbBM.js → file-diff-panel-BWXYzqHk.js} +1 -1
  71. package/src/ui/dist/assets/{file-socket-CP3iwVZG.js → file-socket-wDlx6byM.js} +1 -1
  72. package/src/ui/dist/assets/{file-utils-BsS-Aw68.js → file-utils-Ba3nJmH0.js} +1 -1
  73. package/src/ui/dist/assets/{image-ByeK-Zcv.js → image-BwtCyguk.js} +1 -1
  74. package/src/ui/dist/assets/{index-BdsE0uRz.js → index-B-2scqCJ.js} +11 -11
  75. package/src/ui/dist/assets/{index-BLjo5--a.js → index-Bz5AaWL7.js} +50793 -50661
  76. package/src/ui/dist/assets/{index-DyremSIv.js → index-CfRpE209.js} +2 -2
  77. package/src/ui/dist/assets/{index-C-eX-N6A.js → index-DcqvKzeJ.js} +1 -1
  78. package/src/ui/dist/assets/{index-CuQhlrR-.css → index-DpMZw8aM.css} +2 -2
  79. package/src/ui/dist/assets/{message-square-DnagiLnc.js → message-square-BnlyWVH0.js} +1 -1
  80. package/src/ui/dist/assets/{monaco-4kBFeprs.js → monaco-CXe0pAVe.js} +1 -1
  81. package/src/ui/dist/assets/{popover-hRCXZzs2.js → popover-BCHmVhHj.js} +1 -1
  82. package/src/ui/dist/assets/{project-sync-O_85YuP6.js → project-sync-Brk6kaOD.js} +1 -1
  83. package/src/ui/dist/assets/{sigma-DvKopSnL.js → sigma-D72eSUep.js} +1 -1
  84. package/src/ui/dist/assets/{tooltip-BmlPc6kc.js → tooltip-BMWd0dqX.js} +1 -1
  85. package/src/ui/dist/assets/{trash-n-UvdZFR.js → trash-BIt_eWIS.js} +1 -1
  86. package/src/ui/dist/assets/{useCliAccess-WDd3_wIh.js → useCliAccess-N1hkTRrR.js} +1 -1
  87. package/src/ui/dist/assets/{useFileDiffOverlay-rXLIL2NF.js → useFileDiffOverlay-DPRPv6rv.js} +1 -1
  88. package/src/ui/dist/assets/{wrap-text-qIYQ4a_W.js → wrap-text-E5-UheyP.js} +1 -1
  89. package/src/ui/dist/assets/{zoom-out-fZXCEFsy.js → zoom-out-D4TR-ZZ_.js} +1 -1
  90. package/src/ui/dist/index.html +2 -2
@@ -0,0 +1,55 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="1280" height="720" viewBox="0 0 1280 720" fill="none">
2
+ <rect width="1280" height="720" rx="32" fill="#F6FBFF"/>
3
+ <rect x="40" y="40" width="1200" height="640" rx="28" fill="#FFFFFF" stroke="#D6EAF8" stroke-width="2"/>
4
+ <circle cx="110" cy="108" r="34" fill="#26A5E4"/>
5
+ <path d="M125.4 88.5L118.2 123.1C117.7 125.5 116.4 126 114.5 124.9L103.3 116.7L97.9 121.9C97.3 122.5 96.8 123 95.5 123L96.3 111.6L117.2 92.7C118.1 91.9 117 91.4 115.8 92.1L90 108.4L78.9 104.9C76.5 104.1 76.4 102.4 79.4 101.2L122.6 84.5C124.6 83.8 126.4 85 125.4 88.5Z" fill="#fff"/>
6
+ <text x="164" y="98" fill="#0F172A" font-family="Arial, sans-serif" font-size="34" font-weight="700">Telegram Setup Flow</text>
7
+ <text x="164" y="138" fill="#475569" font-family="Arial, sans-serif" font-size="20">BotFather -&gt; DeepScientist polling -&gt; one DM -&gt; discovered target -&gt; probe</text>
8
+
9
+ <rect x="72" y="198" width="340" height="414" rx="26" fill="#F8FCFF" stroke="#B9DFF6" stroke-width="2"/>
10
+ <rect x="98" y="224" width="90" height="34" rx="17" fill="#E0F2FE"/>
11
+ <text x="122" y="246" fill="#0C4A6E" font-family="Arial, sans-serif" font-size="18" font-weight="700">Step 1</text>
12
+ <text x="98" y="292" fill="#0F172A" font-family="Arial, sans-serif" font-size="28" font-weight="700">Open BotFather</text>
13
+ <rect x="98" y="324" width="288" height="68" rx="18" fill="#fff" stroke="#D4EAF7"/>
14
+ <text x="122" y="352" fill="#0F172A" font-family="Arial, sans-serif" font-size="18" font-weight="700">Use /newbot</text>
15
+ <text x="122" y="378" fill="#475569" font-family="Arial, sans-serif" font-size="16">Create bot name + username</text>
16
+ <rect x="98" y="408" width="288" height="68" rx="18" fill="#fff" stroke="#D4EAF7"/>
17
+ <text x="122" y="436" fill="#0F172A" font-family="Arial, sans-serif" font-size="18" font-weight="700">Copy bot_token</text>
18
+ <text x="122" y="462" fill="#475569" font-family="Arial, sans-serif" font-size="16">Keep the full token from BotFather</text>
19
+ <rect x="98" y="492" width="288" height="92" rx="18" fill="#EAF7FF"/>
20
+ <text x="122" y="522" fill="#0F172A" font-family="Arial, sans-serif" font-size="18" font-weight="700">Official docs</text>
21
+ <text x="122" y="550" fill="#0369A1" font-family="Arial, sans-serif" font-size="16">core.telegram.org/bots</text>
22
+ <text x="122" y="574" fill="#0369A1" font-family="Arial, sans-serif" font-size="16">core.telegram.org/bots/tutorial</text>
23
+
24
+ <rect x="470" y="198" width="340" height="414" rx="26" fill="#F9FBFF" stroke="#CBDCF8" stroke-width="2"/>
25
+ <rect x="496" y="224" width="90" height="34" rx="17" fill="#EDE9FE"/>
26
+ <text x="520" y="246" fill="#4338CA" font-family="Arial, sans-serif" font-size="18" font-weight="700">Step 2</text>
27
+ <text x="496" y="292" fill="#0F172A" font-family="Arial, sans-serif" font-size="28" font-weight="700">Fill Settings</text>
28
+ <rect x="496" y="324" width="288" height="200" rx="18" fill="#fff" stroke="#D8E2F4"/>
29
+ <text x="520" y="356" fill="#0F172A" font-family="Arial, sans-serif" font-size="18" font-weight="700">Connector fields</text>
30
+ <text x="520" y="390" fill="#334155" font-family="Arial, sans-serif" font-size="17">Enabled: on</text>
31
+ <text x="520" y="422" fill="#334155" font-family="Arial, sans-serif" font-size="17">transport: polling</text>
32
+ <text x="520" y="454" fill="#334155" font-family="Arial, sans-serif" font-size="17">bot_token: paste from BotFather</text>
33
+ <text x="520" y="486" fill="#334155" font-family="Arial, sans-serif" font-size="17">command_prefix: /</text>
34
+ <text x="520" y="518" fill="#334155" font-family="Arial, sans-serif" font-size="17">require_mention_in_groups: optional</text>
35
+ <rect x="496" y="544" width="288" height="40" rx="14" fill="#111827"/>
36
+ <text x="594" y="570" fill="#fff" font-family="Arial, sans-serif" font-size="18" font-weight="700">Save first</text>
37
+
38
+ <rect x="868" y="198" width="340" height="414" rx="26" fill="#FBFCFF" stroke="#D8E2F4" stroke-width="2"/>
39
+ <rect x="894" y="224" width="90" height="34" rx="17" fill="#DCFCE7"/>
40
+ <text x="918" y="246" fill="#166534" font-family="Arial, sans-serif" font-size="18" font-weight="700">Step 3</text>
41
+ <text x="894" y="292" fill="#0F172A" font-family="Arial, sans-serif" font-size="28" font-weight="700">Send one DM</text>
42
+ <rect x="894" y="324" width="288" height="68" rx="18" fill="#fff" stroke="#E1EAF7"/>
43
+ <text x="918" y="352" fill="#0F172A" font-family="Arial, sans-serif" font-size="18" font-weight="700">Telegram message</text>
44
+ <text x="918" y="378" fill="#475569" font-family="Arial, sans-serif" font-size="16">Send /start or /help to the bot</text>
45
+ <rect x="894" y="408" width="288" height="68" rx="18" fill="#fff" stroke="#E1EAF7"/>
46
+ <text x="918" y="436" fill="#0F172A" font-family="Arial, sans-serif" font-size="18" font-weight="700">Back to Settings</text>
47
+ <text x="918" y="462" fill="#475569" font-family="Arial, sans-serif" font-size="16">Pick the discovered target instead of typing ids</text>
48
+ <rect x="894" y="492" width="288" height="92" rx="18" fill="#ECFDF5"/>
49
+ <text x="918" y="522" fill="#0F172A" font-family="Arial, sans-serif" font-size="18" font-weight="700">Final check</text>
50
+ <text x="918" y="550" fill="#166534" font-family="Arial, sans-serif" font-size="16">1. Click Check</text>
51
+ <text x="918" y="574" fill="#166534" font-family="Arial, sans-serif" font-size="16">2. Click Send probe</text>
52
+
53
+ <path d="M423 406H455" stroke="#94A3B8" stroke-width="8" stroke-linecap="round"/>
54
+ <path d="M821 406H853" stroke="#94A3B8" stroke-width="8" stroke-linecap="round"/>
55
+ </svg>
@@ -0,0 +1,51 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="1280" height="720" viewBox="0 0 1280 720" fill="none">
2
+ <rect width="1280" height="720" rx="32" fill="#F7FFF9"/>
3
+ <rect x="40" y="40" width="1200" height="640" rx="28" fill="#FFFFFF" stroke="#D7F1DF" stroke-width="2"/>
4
+ <circle cx="110" cy="108" r="34" fill="#25D366"/>
5
+ <path d="M126.4 108.3c-.4-.2-2.4-1.2-2.8-1.3-.4-.1-.6-.2-.9.2-.3.4-1.1 1.3-1.3 1.6-.2.3-.5.3-.9.1-.4-.2-1.7-.6-3.3-2-.8-.8-1.4-1.5-1.7-2-.2-.4 0-.6.2-.8l.6-.7c.2-.3.3-.4.4-.7.1-.3.1-.5 0-.7-.1-.2-.9-2.2-1.2-3-.3-.8-.7-.7-.9-.7H112c-.3 0-.7.1-1 .5-.4.4-1.4 1.4-1.4 3.4 0 2 1.5 3.9 1.7 4.2.2.3 2.8 4.3 7 6 .9.4 1.6.7 2.3.8 1 .3 1.9.3 2.6.2.8-.1 2.4-1 2.8-1.9.3-.9.3-1.8.2-2 0-.2-.4-.3-.8-.5Zm-7.5 10.2h-.1c-2.7 0-5.3-.7-7.6-2.1l-.5-.3-5.6 1.5 1.5-5.5-.3-.6c-1.5-2.4-2.3-5.1-2.3-7.9 0-8.3 6.7-15 15-15 4 0 7.8 1.6 10.7 4.4 2.8 2.9 4.4 6.7 4.4 10.7 0 8.3-6.7 15-15 15Z" fill="#fff"/>
6
+ <text x="164" y="98" fill="#0F172A" font-family="Arial, sans-serif" font-size="34" font-weight="700">WhatsApp Setup Flow</text>
7
+ <text x="164" y="138" fill="#475569" font-family="Arial, sans-serif" font-size="20">DeepScientist local session -&gt; choose QR or pairing code -&gt; persist session -&gt; one real message</text>
8
+
9
+ <rect x="72" y="198" width="340" height="414" rx="26" fill="#FBFFFC" stroke="#D6F3DE" stroke-width="2"/>
10
+ <rect x="98" y="224" width="90" height="34" rx="17" fill="#DCFCE7"/>
11
+ <text x="122" y="246" fill="#166534" font-family="Arial, sans-serif" font-size="18" font-weight="700">Step 1</text>
12
+ <text x="98" y="292" fill="#0F172A" font-family="Arial, sans-serif" font-size="28" font-weight="700">Choose local path</text>
13
+ <rect x="98" y="324" width="288" height="84" rx="18" fill="#fff" stroke="#E1F5E7"/>
14
+ <text x="122" y="352" fill="#0F172A" font-family="Arial, sans-serif" font-size="18" font-weight="700">Preferred transport</text>
15
+ <text x="122" y="378" fill="#475569" font-family="Arial, sans-serif" font-size="16">Use local_session, not Meta Cloud by default</text>
16
+ <rect x="98" y="424" width="288" height="84" rx="18" fill="#ECFDF5"/>
17
+ <text x="122" y="452" fill="#0F172A" font-family="Arial, sans-serif" font-size="18" font-weight="700">Pick login mode</text>
18
+ <text x="122" y="480" fill="#166534" font-family="Arial, sans-serif" font-size="16">QR browser</text>
19
+ <text x="122" y="502" fill="#166534" font-family="Arial, sans-serif" font-size="16">or pairing code on headless machines</text>
20
+ <rect x="98" y="524" width="288" height="60" rx="18" fill="#fff" stroke="#E1F5E7"/>
21
+ <text x="122" y="560" fill="#475569" font-family="Arial, sans-serif" font-size="16">Keep session_dir stable</text>
22
+
23
+ <rect x="470" y="198" width="340" height="414" rx="26" fill="#FBFFFC" stroke="#D6F3DE" stroke-width="2"/>
24
+ <rect x="496" y="224" width="90" height="34" rx="17" fill="#DBEAFE"/>
25
+ <text x="520" y="246" fill="#1D4ED8" font-family="Arial, sans-serif" font-size="18" font-weight="700">Step 2</text>
26
+ <text x="496" y="292" fill="#0F172A" font-family="Arial, sans-serif" font-size="28" font-weight="700">Fill Settings</text>
27
+ <rect x="496" y="324" width="288" height="220" rx="18" fill="#fff" stroke="#E1F5E7"/>
28
+ <text x="520" y="356" fill="#0F172A" font-family="Arial, sans-serif" font-size="18" font-weight="700">Connector fields</text>
29
+ <text x="520" y="390" fill="#334155" font-family="Arial, sans-serif" font-size="17">Enabled: on</text>
30
+ <text x="520" y="422" fill="#334155" font-family="Arial, sans-serif" font-size="17">transport: local_session</text>
31
+ <text x="520" y="454" fill="#334155" font-family="Arial, sans-serif" font-size="17">auth_method: qr_browser or pairing_code</text>
32
+ <text x="520" y="486" fill="#334155" font-family="Arial, sans-serif" font-size="17">session_dir: keep default unless isolated</text>
33
+ <text x="520" y="518" fill="#334155" font-family="Arial, sans-serif" font-size="17">command_prefix: /</text>
34
+ <rect x="496" y="560" width="288" height="24" rx="12" fill="#111827"/>
35
+
36
+ <rect x="868" y="198" width="340" height="414" rx="26" fill="#FBFFFC" stroke="#D6F3DE" stroke-width="2"/>
37
+ <rect x="894" y="224" width="90" height="34" rx="17" fill="#FDE68A"/>
38
+ <text x="918" y="246" fill="#92400E" font-family="Arial, sans-serif" font-size="18" font-weight="700">Step 3</text>
39
+ <text x="894" y="292" fill="#0F172A" font-family="Arial, sans-serif" font-size="28" font-weight="700">Login and verify</text>
40
+ <rect x="894" y="324" width="288" height="84" rx="18" fill="#fff" stroke="#E1F5E7"/>
41
+ <text x="918" y="352" fill="#0F172A" font-family="Arial, sans-serif" font-size="18" font-weight="700">Complete local login</text>
42
+ <text x="918" y="378" fill="#475569" font-family="Arial, sans-serif" font-size="16">Scan QR or confirm the pairing code flow</text>
43
+ <rect x="894" y="424" width="288" height="84" rx="18" fill="#fff" stroke="#E1F5E7"/>
44
+ <text x="918" y="452" fill="#0F172A" font-family="Arial, sans-serif" font-size="18" font-weight="700">Send one real message</text>
45
+ <text x="918" y="478" fill="#475569" font-family="Arial, sans-serif" font-size="16">Wait for a discovered target before probing</text>
46
+ <rect x="894" y="524" width="288" height="60" rx="18" fill="#FEFCE8"/>
47
+ <text x="918" y="560" fill="#92400E" font-family="Arial, sans-serif" font-size="16">Check -&gt; Send probe</text>
48
+
49
+ <path d="M423 406H455" stroke="#94A3B8" stroke-width="8" stroke-linecap="round"/>
50
+ <path d="M821 406H853" stroke="#94A3B8" stroke-width="8" stroke-linecap="round"/>
51
+ </svg>
@@ -0,0 +1,17 @@
1
+ <svg width="1400" height="880" viewBox="0 0 1400 880" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <rect width="1400" height="880" rx="36" fill="#F5F1EA"/>
3
+ <rect x="90" y="70" width="1220" height="740" rx="28" fill="#1F2430"/>
4
+ <text x="130" y="130" fill="#F4EFE7" font-size="34" font-family="Courier New, monospace" font-weight="700">Generated OpenClaw config snippet</text>
5
+ <text x="130" y="190" fill="#B8C3CC" font-size="24" font-family="Courier New, monospace">{</text>
6
+ <text x="170" y="230" fill="#B8C3CC" font-size="24" font-family="Courier New, monospace">"gateway": { "port": 18789, "http": { "endpoints": { "chatCompletions": { "enabled": true }}}},</text>
7
+ <text x="170" y="280" fill="#B8C3CC" font-size="24" font-family="Courier New, monospace">"plugins": { "entries": { "lingzhu": { "enabled": true, "config": {</text>
8
+ <text x="210" y="330" fill="#C8D7E0" font-size="24" font-family="Courier New, monospace">"authAk": "&lt;auth_ak&gt;",</text>
9
+ <text x="210" y="372" fill="#C8D7E0" font-size="24" font-family="Courier New, monospace">"agentId": "main",</text>
10
+ <text x="210" y="414" fill="#C8D7E0" font-size="24" font-family="Courier New, monospace">"includeMetadata": true,</text>
11
+ <text x="210" y="456" fill="#C8D7E0" font-size="24" font-family="Courier New, monospace">"requestTimeoutMs": 60000,</text>
12
+ <text x="210" y="498" fill="#C8D7E0" font-size="24" font-family="Courier New, monospace">"defaultNavigationMode": "0", "enableFollowUp": true,</text>
13
+ <text x="210" y="540" fill="#C8D7E0" font-size="24" font-family="Courier New, monospace">"followUpMaxCount": 3, "sessionMode": "per_user",</text>
14
+ <text x="210" y="582" fill="#C8D7E0" font-size="24" font-family="Courier New, monospace">"sessionNamespace": "lingzhu", "maxImageBytes": 5242880</text>
15
+ <text x="170" y="632" fill="#B8C3CC" font-size="24" font-family="Courier New, monospace">}}}}</text>
16
+ <text x="130" y="690" fill="#F0D9B5" font-size="22" font-family="Arial, sans-serif">DeepScientist generates the full snippet in Settings so you can copy it directly.</text>
17
+ </svg>
@@ -0,0 +1,16 @@
1
+ <svg width="1400" height="760" viewBox="0 0 1400 760" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <rect width="1400" height="760" rx="36" fill="#F4EFE7"/>
3
+ <rect x="90" y="80" width="1220" height="600" rx="28" fill="#FCFAF7" stroke="#D8D0C3"/>
4
+ <text x="130" y="150" fill="#2B2926" font-size="34" font-family="Arial, sans-serif" font-weight="700">Values to paste into the Lingzhu platform</text>
5
+
6
+ <rect x="130" y="210" width="1140" height="120" rx="22" fill="#E3EBEE" stroke="#A8BBC2"/>
7
+ <text x="165" y="260" fill="#314048" font-size="26" font-family="Arial, sans-serif" font-weight="700">SSE URL</text>
8
+ <text x="165" y="302" fill="#52616C" font-size="22" font-family="Arial, sans-serif">http://&lt;public-ip-or-domain&gt;:18789/metis/agent/api/sse</text>
9
+
10
+ <rect x="130" y="370" width="1140" height="120" rx="22" fill="#F1E5D8" stroke="#C8B99E"/>
11
+ <text x="165" y="420" fill="#3A352F" font-size="26" font-family="Arial, sans-serif" font-weight="700">Authorization / AK</text>
12
+ <text x="165" y="462" fill="#5B554E" font-size="22" font-family="Arial, sans-serif">Bearer &lt;the same auth_ak generated in DeepScientist&gt;</text>
13
+
14
+ <rect x="130" y="530" width="1140" height="100" rx="22" fill="#F7F2EC" stroke="#D8D0C3"/>
15
+ <text x="165" y="588" fill="#685F56" font-size="22" font-family="Arial, sans-serif">Reminder: devices need a public IP or public domain. Do not fill 127.0.0.1 here.</text>
16
+ </svg>
@@ -0,0 +1,30 @@
1
+ <svg width="1400" height="900" viewBox="0 0 1400 900" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <rect width="1400" height="900" rx="36" fill="#F5F1EA"/>
3
+ <rect x="70" y="70" width="1260" height="760" rx="28" fill="#FCFAF7" stroke="#D8D0C3"/>
4
+ <text x="110" y="140" fill="#2B2926" font-size="36" font-family="Arial, sans-serif" font-weight="700">Lingzhu Settings Overview</text>
5
+ <text x="110" y="182" fill="#7A7268" font-size="20" font-family="Arial, sans-serif">Fill endpoint, generate AK, then paste the public SSE URL and AK into the Lingzhu platform.</text>
6
+
7
+ <rect x="110" y="230" width="360" height="220" rx="24" fill="#F0E6D8" stroke="#C8B99E"/>
8
+ <text x="140" y="285" fill="#3A352F" font-size="28" font-family="Arial, sans-serif" font-weight="700">1. Gateway endpoint</text>
9
+ <text x="140" y="332" fill="#5B554E" font-size="20" font-family="Arial, sans-serif">- local_host</text>
10
+ <text x="140" y="365" fill="#5B554E" font-size="20" font-family="Arial, sans-serif">- gateway_port</text>
11
+ <text x="140" y="398" fill="#5B554E" font-size="20" font-family="Arial, sans-serif">- public_base_url</text>
12
+
13
+ <rect x="520" y="230" width="360" height="220" rx="24" fill="#DDE7EA" stroke="#A8BBC2"/>
14
+ <text x="550" y="285" fill="#2F3941" font-size="28" font-family="Arial, sans-serif" font-weight="700">2. Auth and identity</text>
15
+ <text x="550" y="332" fill="#52616C" font-size="20" font-family="Arial, sans-serif">- auth_ak</text>
16
+ <text x="550" y="365" fill="#52616C" font-size="20" font-family="Arial, sans-serif">- agent_id</text>
17
+ <text x="550" y="398" fill="#52616C" font-size="20" font-family="Arial, sans-serif">- system_prompt</text>
18
+
19
+ <rect x="930" y="230" width="290" height="220" rx="24" fill="#E8D7D7" stroke="#C4A8A8"/>
20
+ <text x="960" y="285" fill="#433232" font-size="28" font-family="Arial, sans-serif" font-weight="700">3. Probe</text>
21
+ <text x="960" y="332" fill="#685454" font-size="20" font-family="Arial, sans-serif">Run local health</text>
22
+ <text x="960" y="365" fill="#685454" font-size="20" font-family="Arial, sans-serif">then SSE smoke test</text>
23
+
24
+ <rect x="110" y="510" width="1110" height="250" rx="24" fill="#FBF7F2" stroke="#D8D0C3"/>
25
+ <text x="140" y="565" fill="#2B2926" font-size="28" font-family="Arial, sans-serif" font-weight="700">Generated values</text>
26
+ <text x="140" y="610" fill="#5B554E" font-size="20" font-family="Arial, sans-serif">- Local health URL: http://127.0.0.1:18789/metis/agent/api/health</text>
27
+ <text x="140" y="645" fill="#5B554E" font-size="20" font-family="Arial, sans-serif">- Local SSE URL: http://127.0.0.1:18789/metis/agent/api/sse</text>
28
+ <text x="140" y="680" fill="#5B554E" font-size="20" font-family="Arial, sans-serif">- Public SSE URL: http://&lt;public-ip&gt;:18789/metis/agent/api/sse</text>
29
+ <text x="140" y="715" fill="#5B554E" font-size="20" font-family="Arial, sans-serif">- OpenClaw config snippet and probe curl are generated below the form.</text>
30
+ </svg>
@@ -42,7 +42,7 @@ DeepScientist 现在使用 `uv` 管理锁定的本地 Python 运行时。如果
42
42
  如果你希望直接把“当前工作目录”作为 DeepScientist home,可以运行:
43
43
 
44
44
  ```bash
45
- ds --hero
45
+ ds --here
46
46
  ```
47
47
 
48
48
  它等价于 `ds --home "$PWD"`。
@@ -44,7 +44,7 @@
44
44
  ```yaml
45
45
  home: ~/DeepScientist
46
46
  default_runner: codex
47
- default_locale: zh-CN
47
+ default_locale: en-US # 或 zh-CN,首次打开 Web 时会按浏览器语言初始化
48
48
  daemon:
49
49
  session_restore_on_start: true
50
50
  max_concurrent_quests: 1
@@ -67,6 +67,14 @@ skills:
67
67
  sync_global_on_init: true
68
68
  sync_quest_on_create: true
69
69
  sync_quest_on_open: true
70
+ bootstrap:
71
+ codex_ready: false
72
+ codex_last_checked_at: null
73
+ codex_last_result: {}
74
+ locale_source: browser
75
+ locale_initialized_from_browser: true
76
+ locale_initialized_at: 2026-03-18T00:00:00+00:00
77
+ locale_initialized_browser_locale: en-US
70
78
  connectors:
71
79
  auto_ack: true
72
80
  milestone_push: true
@@ -109,11 +117,12 @@ acp:
109
117
  **`default_locale`**
110
118
 
111
119
  - 类型:`string`
112
- - 默认值:`zh-CN`
120
+ - 默认值:首次打开 Web 时按浏览器语言初始化,随后会落盘为 `zh-CN` 或 `en-US`
113
121
  - 允许值:`zh-CN`、`en-US`
114
122
  - 页面标签:`Default locale`
115
- - 作用:影响系统 prompt 的默认语言倾向以及部分 UI 默认文案。
116
- - 何时修改:希望整个系统默认说中文还是英文时修改。
123
+ - 作用:影响系统 prompt 与运行时文案默认使用的语言。
124
+ - 何时修改:希望整个系统固定偏向中文或英文时修改。
125
+ - 注意事项:第一次浏览器初始化完成后,如果你在 `Settings` 手动修改这里,DeepScientist 会把它视为用户显式选择,后续不再自动跟随浏览器覆盖。
117
126
 
118
127
  ### Daemon policy
119
128
 
@@ -1107,7 +1116,7 @@ servers:
1107
1116
  ```yaml
1108
1117
  # config.yaml
1109
1118
  default_runner: codex
1110
- default_locale: zh-CN
1119
+ default_locale: en-US
1111
1120
  ui:
1112
1121
  host: 0.0.0.0
1113
1122
  port: 20999
@@ -11,6 +11,7 @@ DeepScientist 的构思、架构设计与实现过程中,受到了许多优秀
11
11
  - AlphaEvolve
12
12
  - OpenEvolve
13
13
  - EvoScientist
14
+ - Orchestra-Research/AI-Research-SKILLs
14
15
 
15
16
  这些项目在自动化科研、开放式探索、进化式搜索、实验组织与智能研究代理等方向上的探索,为 DeepScientist 的设计提供了重要参照。
16
17
 
package/package.json CHANGED
@@ -1,13 +1,12 @@
1
1
  {
2
2
  "name": "@researai/deepscientist",
3
- "version": "1.5.3",
3
+ "version": "1.5.4",
4
4
  "description": "Local-first research operating system with a Python runtime and npm launcher",
5
5
  "license": "MIT",
6
6
  "files": [
7
7
  "AGENTS.md",
8
8
  "README.md",
9
- "docs/en/",
10
- "docs/zh/",
9
+ "docs/",
11
10
  "install.sh",
12
11
  "uv.lock",
13
12
  "assets/branding/",
@@ -36,7 +35,12 @@
36
35
  "research": "bin/ds.js"
37
36
  },
38
37
  "dependencies": {
39
- "@openai/codex": "^0.114.0"
38
+ "@openai/codex": "^0.114.0",
39
+ "ink": "npm:@jrichman/ink@6.4.6",
40
+ "ink-gradient": "^3.0.0",
41
+ "react": "^19.2.0",
42
+ "react-dom": "^19.2.0",
43
+ "string-width": "^8.1.0"
40
44
  },
41
45
  "scripts": {
42
46
  "ds": "node ./bin/ds.js",
package/pyproject.toml CHANGED
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "deepscientist"
7
- version = "1.5.3"
7
+ version = "1.5.4"
8
8
  description = "DeepScientist Core skeleton"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -5,4 +5,4 @@ __all__ = ["__version__"]
5
5
  try:
6
6
  __version__ = _package_version("deepscientist")
7
7
  except PackageNotFoundError: # pragma: no cover - source checkout fallback
8
- __version__ = "1.5.3"
8
+ __version__ = "1.5.4"
@@ -8,9 +8,10 @@ from hashlib import sha256
8
8
  from typing import Any
9
9
  from urllib.error import URLError
10
10
  from urllib.parse import parse_qs
11
- from urllib.request import Request, urlopen
11
+ from urllib.request import Request
12
12
 
13
13
  from ..connector_runtime import parse_conversation_id
14
+ from ..network import urlopen_with_proxy as urlopen
14
15
 
15
16
 
16
17
  @dataclass
@@ -8,8 +8,9 @@ from hmac import new as hmac_new
8
8
  from pathlib import Path
9
9
  from typing import Any
10
10
  from urllib.error import HTTPError
11
- from urllib.request import Request, urlopen
11
+ from urllib.request import Request
12
12
 
13
+ from ..network import urlopen_with_proxy as urlopen
13
14
  from ..shared import append_jsonl, ensure_dir, utc_now
14
15
  from .base import BaseConnectorBridge, BridgeWebhookResult
15
16
 
@@ -5,12 +5,12 @@ import platform
5
5
  import threading
6
6
  from pathlib import Path
7
7
  from typing import Any, Callable
8
- from urllib.request import Request, urlopen
8
+ from urllib.request import Request
9
9
 
10
10
  from websockets.exceptions import ConnectionClosed
11
- from websockets.sync.client import connect as websocket_connect
12
11
 
13
12
  from ..connector_runtime import format_conversation_id
13
+ from ..network import urlopen_with_proxy as urlopen, websocket_connect_with_proxy as websocket_connect
14
14
  from ..shared import read_json, utc_now, write_json
15
15
 
16
16
 
@@ -5,13 +5,13 @@ import threading
5
5
  import time
6
6
  from pathlib import Path
7
7
  from typing import Any, Callable
8
- from urllib.request import Request, urlopen
8
+ from urllib.request import Request
9
9
 
10
10
  from websockets.exceptions import ConnectionClosed
11
- from websockets.sync.client import connect as websocket_connect
12
11
 
13
12
  from ..bridges.connectors import QQConnectorBridge
14
13
  from ..connector_runtime import format_conversation_id
14
+ from ..network import urlopen_with_proxy as urlopen, websocket_connect_with_proxy as websocket_connect
15
15
  from ..qq_profiles import qq_profile_label
16
16
  from ..shared import read_json, utc_now, write_json
17
17
 
@@ -4,12 +4,12 @@ import json
4
4
  import threading
5
5
  from pathlib import Path
6
6
  from typing import Any, Callable
7
- from urllib.request import Request, urlopen
7
+ from urllib.request import Request
8
8
 
9
9
  from websockets.exceptions import ConnectionClosed
10
- from websockets.sync.client import connect as websocket_connect
11
10
 
12
11
  from ..connector_runtime import format_conversation_id
12
+ from ..network import urlopen_with_proxy as urlopen, websocket_connect_with_proxy as websocket_connect
13
13
  from ..shared import read_json, utc_now, write_json
14
14
 
15
15
 
@@ -4,9 +4,10 @@ import json
4
4
  import threading
5
5
  from pathlib import Path
6
6
  from typing import Any, Callable
7
- from urllib.request import Request, urlopen
7
+ from urllib.request import Request
8
8
 
9
9
  from ..connector_runtime import format_conversation_id
10
+ from ..network import urlopen_with_proxy as urlopen
10
11
  from ..shared import read_json, utc_now, write_json
11
12
 
12
13
 
@@ -9,7 +9,7 @@ import sys
9
9
  import webbrowser
10
10
  from pathlib import Path
11
11
  from urllib.error import URLError
12
- from urllib.request import Request, urlopen
12
+ from urllib.request import Request
13
13
 
14
14
  from .artifact import ArtifactService
15
15
  from .config import ConfigManager
@@ -18,6 +18,7 @@ from .doctor import render_doctor_report, run_doctor
18
18
  from .home import default_home, ensure_home_layout, repo_root
19
19
  from .memory import MemoryService
20
20
  from .migration import migrate_deepscientist_root
21
+ from .network import configure_runtime_proxy, urlopen_with_proxy as urlopen
21
22
  from .prompts import PromptBuilder
22
23
  from .quest import QuestService
23
24
  from .registries import BaselineRegistry
@@ -37,6 +38,7 @@ def _local_ui_url(host: str, port: int) -> str:
37
38
  def build_parser() -> argparse.ArgumentParser:
38
39
  parser = argparse.ArgumentParser(prog="ds", description="DeepScientist Core skeleton")
39
40
  parser.add_argument("--home", default=None, help="Override DeepScientist home")
41
+ parser.add_argument("--proxy", default=None, help="Explicit outbound HTTP/WS proxy, for example `http://127.0.0.1:7890`.")
40
42
 
41
43
  subparsers = parser.add_subparsers(dest="command", required=True)
42
44
 
@@ -473,6 +475,7 @@ def migrate_command(home: Path, target: str) -> int:
473
475
  def main(argv: list[str] | None = None) -> int:
474
476
  parser = build_parser()
475
477
  args = parser.parse_args(argv)
478
+ configure_runtime_proxy(args.proxy)
476
479
  home = resolve_home(args)
477
480
 
478
481
  if args.command == "init":
@@ -56,6 +56,10 @@ def default_config(home: Path) -> dict:
56
56
  "codex_ready": False,
57
57
  "codex_last_checked_at": None,
58
58
  "codex_last_result": {},
59
+ "locale_source": "default",
60
+ "locale_initialized_from_browser": False,
61
+ "locale_initialized_at": None,
62
+ "locale_initialized_browser_locale": None,
59
63
  },
60
64
  "connectors": {
61
65
  "auto_ack": True,
@@ -91,9 +95,9 @@ def default_runners() -> dict:
91
95
  "sandbox_mode": "workspace-write",
92
96
  "retry_on_failure": True,
93
97
  "retry_max_attempts": 5,
94
- "retry_initial_backoff_sec": 1.0,
95
- "retry_backoff_multiplier": 2.0,
96
- "retry_max_backoff_sec": 8.0,
98
+ "retry_initial_backoff_sec": 10.0,
99
+ "retry_backoff_multiplier": 6.0,
100
+ "retry_max_backoff_sec": 1800.0,
97
101
  # Increase MCP tool timeout so codex can wait for long `bash_exec(mode='await', ...)`
98
102
  # or other durable MCP calls without prematurely timing out.
99
103
  # Mirrors DS_2027's `codex.mcp_tool_timeout_sec` default.
@@ -6,7 +6,7 @@ import subprocess
6
6
  from copy import deepcopy
7
7
  from pathlib import Path
8
8
  from urllib.error import URLError
9
- from urllib.request import Request, urlopen
9
+ from urllib.request import Request
10
10
 
11
11
  from ..connector_profiles import PROFILEABLE_CONNECTOR_NAMES, normalize_connector_config
12
12
  from ..connector_runtime import infer_connector_transport
@@ -29,6 +29,7 @@ from ..qq_profiles import (
29
29
  normalize_qq_connector_config,
30
30
  qq_profile_label,
31
31
  )
32
+ from ..network import urlopen_with_proxy as urlopen
32
33
  from ..shared import read_json, read_text, read_yaml, resolve_runner_binary, run_command, sha256_text, utc_now, which, write_text, write_yaml
33
34
  from .models import (
34
35
  CONFIG_NAMES,
@@ -386,7 +387,7 @@ This page edits `{home_text}/config/runners.yaml`.
386
387
  - `claude` remains TODO / reserved in the current open-source release and is not runnable yet
387
388
  - keep `codex.model_reasoning_effort: xhigh` unless you explicitly want a lighter default
388
389
  - keep `codex.retry_on_failure: true` so transient Codex failures can resume automatically
389
- - keep retry timing near `1s / 2x / 8s max` unless you have a strong reason to slow recovery down
390
+ - keep retry timing near `10s / 6x / 1800s max` so Codex backs off exponentially and the last retry waits about 30 minutes
390
391
  - DeepScientist hard-limits one turn to at most `5` total attempts, even if the config says more
391
392
 
392
393
  ## Test behavior
@@ -1320,11 +1321,20 @@ Use **Test** when the file exposes runtime dependencies.
1320
1321
  prepared = deepcopy(payload)
1321
1322
  if name == "config":
1322
1323
  prepared.pop("reports", None)
1324
+ return self._normalize_config_payload(prepared)
1323
1325
  if name == "plugins":
1324
1326
  prepared = self._normalize_plugins_payload(prepared)
1325
1327
  elif name == "mcp_servers":
1326
1328
  prepared = self._normalize_mcp_payload(prepared)
1327
1329
  defaults = default_payload(name, self.home)
1330
+ if name == "runners":
1331
+ normalized = self._deep_merge(defaults, prepared)
1332
+ codex = normalized.get("codex")
1333
+ if isinstance(codex, dict) and self._looks_like_legacy_codex_retry_profile(codex):
1334
+ codex["retry_initial_backoff_sec"] = 10.0
1335
+ codex["retry_backoff_multiplier"] = 6.0
1336
+ codex["retry_max_backoff_sec"] = 1800.0
1337
+ return normalized
1328
1338
  if name == "connectors":
1329
1339
  normalized = deepcopy(defaults)
1330
1340
  for connector_name, connector_payload in prepared.items():
@@ -1358,6 +1368,46 @@ Use **Test** when the file exposes runtime dependencies.
1358
1368
  return normalized
1359
1369
  return self._deep_merge(defaults, prepared)
1360
1370
 
1371
+ def _normalize_config_payload(self, payload: dict) -> dict:
1372
+ defaults = default_payload("config", self.home)
1373
+ normalized = self._deep_merge(defaults, payload)
1374
+ bootstrap = normalized.get("bootstrap") if isinstance(normalized.get("bootstrap"), dict) else {}
1375
+ raw_bootstrap = payload.get("bootstrap") if isinstance(payload.get("bootstrap"), dict) else {}
1376
+ default_locale = str(defaults.get("default_locale") or "").strip()
1377
+ current_locale = str(normalized.get("default_locale") or "").strip()
1378
+ locale_source = str(raw_bootstrap.get("locale_source") or "").strip().lower()
1379
+ locale_initialized_from_browser = bool(
1380
+ raw_bootstrap.get("locale_initialized_from_browser", bootstrap.get("locale_initialized_from_browser", False))
1381
+ )
1382
+
1383
+ if locale_source not in {"default", "browser", "user"}:
1384
+ if current_locale and current_locale != default_locale:
1385
+ locale_source = "user"
1386
+ elif locale_initialized_from_browser:
1387
+ locale_source = "browser"
1388
+ else:
1389
+ locale_source = "default"
1390
+
1391
+ if locale_source == "browser":
1392
+ locale_initialized_from_browser = True
1393
+
1394
+ bootstrap["locale_source"] = locale_source
1395
+ bootstrap["locale_initialized_from_browser"] = locale_initialized_from_browser
1396
+ bootstrap["locale_initialized_at"] = bootstrap.get("locale_initialized_at")
1397
+ bootstrap["locale_initialized_browser_locale"] = bootstrap.get("locale_initialized_browser_locale")
1398
+ normalized["bootstrap"] = bootstrap
1399
+ return normalized
1400
+
1401
+ @staticmethod
1402
+ def _looks_like_legacy_codex_retry_profile(payload: dict) -> bool:
1403
+ try:
1404
+ initial = float(payload.get("retry_initial_backoff_sec"))
1405
+ multiplier = float(payload.get("retry_backoff_multiplier"))
1406
+ max_backoff = float(payload.get("retry_max_backoff_sec"))
1407
+ except (TypeError, ValueError):
1408
+ return False
1409
+ return abs(initial - 1.0) < 1e-9 and abs(multiplier - 2.0) < 1e-9 and abs(max_backoff - 8.0) < 1e-9
1410
+
1361
1411
  def _normalize_plugins_payload(self, payload: dict) -> dict:
1362
1412
  normalized = deepcopy(payload)
1363
1413
  if "load_paths" not in normalized and isinstance(normalized.get("search_paths"), list):
@@ -954,7 +954,7 @@ npm --prefix src/ui run build</pre>
954
954
  def latex_compile(self, project_id: str, folder_id: str, body: dict) -> dict:
955
955
  return self.app.latex_service.compile(
956
956
  project_id,
957
- unquote(folder_id),
957
+ folder_id,
958
958
  compiler=body.get("compiler"),
959
959
  main_file_id=body.get("main_file_id"),
960
960
  stop_on_first_error=body.get("stop_on_first_error"),
@@ -968,13 +968,13 @@ npm --prefix src/ui run build</pre>
968
968
  limit = int(limit_raw)
969
969
  except ValueError:
970
970
  limit = 10
971
- return self.app.latex_service.list_builds(project_id, unquote(folder_id), limit=limit)
971
+ return self.app.latex_service.list_builds(project_id, folder_id, limit=limit)
972
972
 
973
973
  def latex_build(self, project_id: str, folder_id: str, build_id: str) -> dict:
974
- return self.app.latex_service.get_build(project_id, unquote(folder_id), build_id)
974
+ return self.app.latex_service.get_build(project_id, folder_id, build_id)
975
975
 
976
976
  def latex_build_pdf(self, project_id: str, folder_id: str, build_id: str) -> tuple[int, dict, bytes]:
977
- payload, file_name = self.app.latex_service.get_build_pdf(project_id, unquote(folder_id), build_id)
977
+ payload, file_name = self.app.latex_service.get_build_pdf(project_id, folder_id, build_id)
978
978
  headers = {
979
979
  **self._asset_headers("application/pdf"),
980
980
  "Content-Disposition": f'inline; filename="{file_name}"',
@@ -982,11 +982,11 @@ npm --prefix src/ui run build</pre>
982
982
  return 200, headers, payload
983
983
 
984
984
  def latex_build_log(self, project_id: str, folder_id: str, build_id: str) -> tuple[int, dict, bytes]:
985
- text = self.app.latex_service.get_build_log_text(project_id, unquote(folder_id), build_id)
985
+ text = self.app.latex_service.get_build_log_text(project_id, folder_id, build_id)
986
986
  return 200, self._asset_headers("text/plain; charset=utf-8"), text.encode("utf-8")
987
987
 
988
988
  def latex_archive(self, project_id: str, folder_id: str) -> tuple[int, dict, bytes]:
989
- payload, file_name = self.app.latex_service.create_sources_archive(project_id, unquote(folder_id))
989
+ payload, file_name = self.app.latex_service.create_sources_archive(project_id, folder_id)
990
990
  headers = {
991
991
  **self._asset_headers("application/zip"),
992
992
  "Content-Disposition": f'attachment; filename="{file_name}"',