@researai/deepscientist 1.5.8 → 1.5.11

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 (148) hide show
  1. package/LICENSE +186 -21
  2. package/README.md +108 -95
  3. package/assets/branding/connector-qq.png +0 -0
  4. package/assets/branding/connector-rokid.png +0 -0
  5. package/assets/branding/connector-weixin.png +0 -0
  6. package/assets/branding/projects.png +0 -0
  7. package/bin/ds.js +172 -13
  8. package/docs/assets/branding/projects.png +0 -0
  9. package/docs/en/00_QUICK_START.md +308 -70
  10. package/docs/en/01_SETTINGS_REFERENCE.md +3 -0
  11. package/docs/en/02_START_RESEARCH_GUIDE.md +112 -0
  12. package/docs/en/04_LINGZHU_CONNECTOR_GUIDE.md +62 -179
  13. package/docs/en/09_DOCTOR.md +41 -5
  14. package/docs/en/10_WEIXIN_CONNECTOR_GUIDE.md +137 -0
  15. package/docs/en/11_LICENSE_AND_RISK.md +256 -0
  16. package/docs/en/12_GUIDED_WORKFLOW_TOUR.md +427 -0
  17. package/docs/en/13_CORE_ARCHITECTURE_GUIDE.md +297 -0
  18. package/docs/en/14_PROMPT_SKILLS_AND_MCP_GUIDE.md +506 -0
  19. package/docs/en/99_ACKNOWLEDGEMENTS.md +4 -1
  20. package/docs/en/README.md +79 -0
  21. package/docs/images/lingzhu/rokid-agent-platform-create.png +0 -0
  22. package/docs/images/weixin/weixin-plugin-entry.png +0 -0
  23. package/docs/images/weixin/weixin-plugin-entry.svg +33 -0
  24. package/docs/images/weixin/weixin-qr-confirm.svg +30 -0
  25. package/docs/images/weixin/weixin-quest-media-flow.svg +44 -0
  26. package/docs/images/weixin/weixin-settings-bind.svg +57 -0
  27. package/docs/zh/00_QUICK_START.md +315 -74
  28. package/docs/zh/01_SETTINGS_REFERENCE.md +3 -0
  29. package/docs/zh/02_START_RESEARCH_GUIDE.md +112 -0
  30. package/docs/zh/04_LINGZHU_CONNECTOR_GUIDE.md +62 -193
  31. package/docs/zh/09_DOCTOR.md +41 -5
  32. package/docs/zh/10_WEIXIN_CONNECTOR_GUIDE.md +144 -0
  33. package/docs/zh/11_LICENSE_AND_RISK.md +256 -0
  34. package/docs/zh/12_GUIDED_WORKFLOW_TOUR.md +423 -0
  35. package/docs/zh/13_CORE_ARCHITECTURE_GUIDE.md +296 -0
  36. package/docs/zh/14_PROMPT_SKILLS_AND_MCP_GUIDE.md +506 -0
  37. package/docs/zh/99_ACKNOWLEDGEMENTS.md +4 -1
  38. package/docs/zh/README.md +126 -0
  39. package/install.sh +0 -34
  40. package/package.json +3 -3
  41. package/pyproject.toml +2 -2
  42. package/src/deepscientist/__init__.py +1 -1
  43. package/src/deepscientist/annotations.py +343 -0
  44. package/src/deepscientist/artifact/arxiv.py +484 -37
  45. package/src/deepscientist/artifact/metrics.py +1 -3
  46. package/src/deepscientist/artifact/service.py +1347 -111
  47. package/src/deepscientist/arxiv_library.py +275 -0
  48. package/src/deepscientist/bash_exec/service.py +9 -0
  49. package/src/deepscientist/bridges/builtins.py +2 -0
  50. package/src/deepscientist/bridges/connectors.py +447 -0
  51. package/src/deepscientist/channels/__init__.py +2 -0
  52. package/src/deepscientist/channels/builtins.py +3 -1
  53. package/src/deepscientist/channels/qq.py +1 -1
  54. package/src/deepscientist/channels/qq_gateway.py +1 -1
  55. package/src/deepscientist/channels/relay.py +7 -1
  56. package/src/deepscientist/channels/weixin.py +59 -0
  57. package/src/deepscientist/channels/weixin_ilink.py +317 -0
  58. package/src/deepscientist/config/models.py +22 -2
  59. package/src/deepscientist/config/service.py +431 -60
  60. package/src/deepscientist/connector/__init__.py +4 -0
  61. package/src/deepscientist/connector/connector_profiles.py +481 -0
  62. package/src/deepscientist/connector/lingzhu_support.py +668 -0
  63. package/src/deepscientist/connector/qq_profiles.py +206 -0
  64. package/src/deepscientist/connector/weixin_support.py +663 -0
  65. package/src/deepscientist/connector_profiles.py +1 -374
  66. package/src/deepscientist/connector_runtime.py +2 -0
  67. package/src/deepscientist/daemon/api/handlers.py +295 -5
  68. package/src/deepscientist/daemon/api/router.py +16 -1
  69. package/src/deepscientist/daemon/app.py +1130 -61
  70. package/src/deepscientist/doctor.py +5 -2
  71. package/src/deepscientist/gitops/diff.py +120 -29
  72. package/src/deepscientist/lingzhu_support.py +1 -182
  73. package/src/deepscientist/mcp/server.py +14 -5
  74. package/src/deepscientist/prompts/builder.py +29 -1
  75. package/src/deepscientist/qq_profiles.py +1 -196
  76. package/src/deepscientist/quest/node_traces.py +152 -2
  77. package/src/deepscientist/quest/service.py +169 -43
  78. package/src/deepscientist/quest/stage_views.py +172 -9
  79. package/src/deepscientist/registries/baseline.py +56 -4
  80. package/src/deepscientist/runners/codex.py +55 -3
  81. package/src/deepscientist/weixin_support.py +1 -0
  82. package/src/prompts/connectors/lingzhu.md +3 -1
  83. package/src/prompts/connectors/weixin.md +230 -0
  84. package/src/prompts/system.md +9 -0
  85. package/src/skills/idea/SKILL.md +16 -0
  86. package/src/skills/idea/references/literature-survey-template.md +24 -0
  87. package/src/skills/idea/references/related-work-playbook.md +4 -0
  88. package/src/skills/idea/references/selection-gate.md +9 -0
  89. package/src/skills/write/SKILL.md +1 -1
  90. package/src/tui/package.json +1 -1
  91. package/src/ui/dist/assets/{AiManusChatView-m2FNtwbn.js → AiManusChatView-D0mTXG4-.js} +156 -48
  92. package/src/ui/dist/assets/{AnalysisPlugin-BMTF8EGL.js → AnalysisPlugin-Db0cTXxm.js} +1 -1
  93. package/src/ui/dist/assets/{CliPlugin-BEOWgxCI.js → CliPlugin-DrV8je02.js} +164 -9
  94. package/src/ui/dist/assets/{CodeEditorPlugin-BCXvjqmb.js → CodeEditorPlugin-QXMSCH71.js} +8 -8
  95. package/src/ui/dist/assets/{CodeViewerPlugin-DaJcy3nD.js → CodeViewerPlugin-7hhtWj_E.js} +5 -5
  96. package/src/ui/dist/assets/{DocViewerPlugin-ByfeIq4K.js → DocViewerPlugin-BWMSnRJe.js} +3 -3
  97. package/src/ui/dist/assets/{GitDiffViewerPlugin-Cksf3VZ-.js → GitDiffViewerPlugin-7J9h9Vy_.js} +20 -21
  98. package/src/ui/dist/assets/{ImageViewerPlugin-CFz-OsTS.js → ImageViewerPlugin-CHJl_0lr.js} +5 -5
  99. package/src/ui/dist/assets/{LabCopilotPanel-CJ1cJzoX.js → LabCopilotPanel-1qSow1es.js} +11 -11
  100. package/src/ui/dist/assets/{LabPlugin-BF3dVJwa.js → LabPlugin-eQpPPCEp.js} +2 -1
  101. package/src/ui/dist/assets/{LatexPlugin-DDkwZ6Sj.js → LatexPlugin-BwRfi89Z.js} +7 -7
  102. package/src/ui/dist/assets/{MarkdownViewerPlugin-HAuvurcT.js → MarkdownViewerPlugin-836PVQWV.js} +4 -4
  103. package/src/ui/dist/assets/{MarketplacePlugin-BtoTYy2C.js → MarketplacePlugin-C2y_556i.js} +3 -3
  104. package/src/ui/dist/assets/{NotebookEditor-CSJYx7b-.js → NotebookEditor-BRzJbGsn.js} +12 -12
  105. package/src/ui/dist/assets/{NotebookEditor-DQgRezm_.js → NotebookEditor-DIX7Mlzu.js} +1 -1
  106. package/src/ui/dist/assets/{PdfLoader-DPa_-fv6.js → PdfLoader-DzRaTAlq.js} +14 -7
  107. package/src/ui/dist/assets/{PdfMarkdownPlugin-BZpXOEjm.js → PdfMarkdownPlugin-DZUfIUnp.js} +73 -6
  108. package/src/ui/dist/assets/{PdfViewerPlugin-BT8a6wGR.js → PdfViewerPlugin-BwtICzue.js} +103 -34
  109. package/src/ui/dist/assets/PdfViewerPlugin-DQ11QcSf.css +3627 -0
  110. package/src/ui/dist/assets/{SearchPlugin-D_blveZi.js → SearchPlugin-DHeIAMsx.js} +1 -1
  111. package/src/ui/dist/assets/{TextViewerPlugin-Btx0M3hX.js → TextViewerPlugin-C3tCmFox.js} +5 -4
  112. package/src/ui/dist/assets/{VNCViewer-DImJO4rO.js → VNCViewer-CQsKVm3t.js} +10 -10
  113. package/src/ui/dist/assets/bot-BEA2vWuK.js +21 -0
  114. package/src/ui/dist/assets/branding/logo-rokid.png +0 -0
  115. package/src/ui/dist/assets/browser-BAcuE0Xj.js +2895 -0
  116. package/src/ui/dist/assets/{code-BUfXGJSl.js → code-XfbSR8K2.js} +1 -1
  117. package/src/ui/dist/assets/{file-content-VqamwI3X.js → file-content-BjxNaIfy.js} +1 -1
  118. package/src/ui/dist/assets/{file-diff-panel-C_wOoS7a.js → file-diff-panel-D_lLVQk0.js} +1 -1
  119. package/src/ui/dist/assets/{file-socket-D2bTuMVP.js → file-socket-D9x_5vlY.js} +1 -1
  120. package/src/ui/dist/assets/{image-BZkGJ4mM.js → image-BhWT33W1.js} +1 -1
  121. package/src/ui/dist/assets/{index-DdRW6RMJ.js → index--c4iXtuy.js} +12 -12
  122. package/src/ui/dist/assets/{index-CxkvSeKw.js → index-BDxipwrC.js} +2 -2
  123. package/src/ui/dist/assets/{index-DjggJovS.js → index-DZTZ8mWP.js} +14934 -9613
  124. package/src/ui/dist/assets/{index-DXZ1daiJ.css → index-Dqj-Mjb4.css} +2 -13
  125. package/src/ui/dist/assets/index-PJbSbPTy.js +25 -0
  126. package/src/ui/dist/assets/{monaco-DHMc7kKM.js → monaco-K8izTGgo.js} +1 -1
  127. package/src/ui/dist/assets/{pdf-effect-queue-DSw_D3RV.js → pdf-effect-queue-DfBors6y.js} +16 -1
  128. package/src/ui/dist/assets/pdf.worker.min-yatZIOMy.mjs +21 -0
  129. package/src/ui/dist/assets/{popover-B85oCgCS.js → popover-yFK1J4fL.js} +1 -1
  130. package/src/ui/dist/assets/{project-sync-DOMCcPac.js → project-sync-PENr2zcz.js} +1 -74
  131. package/src/ui/dist/assets/select-CAbJDfYv.js +1690 -0
  132. package/src/ui/dist/assets/{sigma-BO2rQrl3.js → sigma-DEuYJqTl.js} +1 -1
  133. package/src/ui/dist/assets/{index-D9QIGcmc.js → square-check-big-omoSUmcd.js} +2 -13
  134. package/src/ui/dist/assets/{trash-BsVEH_dV.js → trash--F119N47.js} +1 -1
  135. package/src/ui/dist/assets/{useCliAccess-b8L6JuZm.js → useCliAccess-D31UR23I.js} +1 -1
  136. package/src/ui/dist/assets/{useFileDiffOverlay-BY7uA9hV.js → useFileDiffOverlay-BH6KcMzq.js} +1 -1
  137. package/src/ui/dist/assets/{wrap-text-BwyVuUIK.js → wrap-text-CZ613PM5.js} +1 -1
  138. package/src/ui/dist/assets/{zoom-out-RDpLugQP.js → zoom-out-BgDLAv3z.js} +1 -1
  139. package/src/ui/dist/index.html +2 -2
  140. package/src/ui/dist/assets/AutoFigurePlugin-BGxN8Umr.css +0 -3056
  141. package/src/ui/dist/assets/AutoFigurePlugin-DxPdMUNb.js +0 -8149
  142. package/src/ui/dist/assets/PdfViewerPlugin-BJXtIwj_.css +0 -260
  143. package/src/ui/dist/assets/Stepper-DH2k75Vo.js +0 -158
  144. package/src/ui/dist/assets/bibtex-B-Hqu0Sg.js +0 -189
  145. package/src/ui/dist/assets/file-utils--zJCPN1i.js +0 -109
  146. package/src/ui/dist/assets/message-square-FUIPIhU2.js +0 -16
  147. package/src/ui/dist/assets/pdfjs-DU1YE8WO.js +0 -3
  148. package/src/ui/dist/assets/tooltip-B1OspAkx.js +0 -108
@@ -0,0 +1,206 @@
1
+ from __future__ import annotations
2
+
3
+ from copy import deepcopy
4
+ from typing import Any
5
+
6
+ from ..shared import slugify
7
+
8
+
9
+ QQ_PROFILE_ID_PREFIX = "qq-profile"
10
+ def default_qq_profile() -> dict[str, Any]:
11
+ return {
12
+ "profile_id": None,
13
+ "enabled": True,
14
+ "app_id": None,
15
+ "app_secret": None,
16
+ "app_secret_env": None,
17
+ "bot_name": "DeepScientist",
18
+ "main_chat_id": None,
19
+ }
20
+
21
+
22
+ def _as_text(value: Any) -> str | None:
23
+ text = str(value or "").strip()
24
+ return text or None
25
+
26
+
27
+ def _qq_profile_is_configured(profile: dict[str, Any] | None) -> bool:
28
+ if not isinstance(profile, dict):
29
+ return False
30
+ app_id = _as_text(profile.get("app_id"))
31
+ app_secret = _as_text(profile.get("app_secret"))
32
+ app_secret_env = _as_text(profile.get("app_secret_env"))
33
+ return bool(app_id and (app_secret or app_secret_env))
34
+
35
+
36
+ def _normalize_secret_pair(payload: dict[str, Any], direct_key: str, env_key: str) -> None:
37
+ direct = _as_text(payload.get(direct_key))
38
+ env_name = _as_text(payload.get(env_key))
39
+ payload[direct_key] = direct
40
+ payload[env_key] = None if direct else env_name
41
+
42
+
43
+ def _profile_id_seed(*, profile_id: Any, app_id: Any, bot_name: Any, index: int) -> str:
44
+ explicit = _as_text(profile_id)
45
+ if explicit:
46
+ return explicit
47
+ app_text = _as_text(app_id)
48
+ if app_text:
49
+ return f"qq-{app_text}"
50
+ bot_text = slugify(str(bot_name or "").strip(), default="")
51
+ if bot_text:
52
+ return f"{QQ_PROFILE_ID_PREFIX}-{bot_text}"
53
+ return f"{QQ_PROFILE_ID_PREFIX}-{index:03d}"
54
+
55
+
56
+ def _unique_profile_id(seed: str, *, used: set[str]) -> str:
57
+ base = slugify(seed, default=QQ_PROFILE_ID_PREFIX)
58
+ candidate = base
59
+ suffix = 2
60
+ while candidate in used:
61
+ candidate = f"{base}-{suffix}"
62
+ suffix += 1
63
+ used.add(candidate)
64
+ return candidate
65
+
66
+
67
+ def list_qq_profiles(config: dict[str, Any] | None) -> list[dict[str, Any]]:
68
+ normalized = normalize_qq_connector_config(config)
69
+ profiles = normalized.get("profiles")
70
+ return [dict(item) for item in profiles] if isinstance(profiles, list) else []
71
+
72
+
73
+ def find_qq_profile(
74
+ config: dict[str, Any] | None,
75
+ *,
76
+ profile_id: str | None = None,
77
+ app_id: str | None = None,
78
+ ) -> dict[str, Any] | None:
79
+ normalized_profile_id = _as_text(profile_id)
80
+ normalized_app_id = _as_text(app_id)
81
+ for profile in list_qq_profiles(config):
82
+ if normalized_profile_id and str(profile.get("profile_id") or "").strip() == normalized_profile_id:
83
+ return profile
84
+ if normalized_app_id and str(profile.get("app_id") or "").strip() == normalized_app_id:
85
+ return profile
86
+ return None
87
+
88
+
89
+ def merge_qq_profile_config(shared_config: dict[str, Any] | None, profile: dict[str, Any]) -> dict[str, Any]:
90
+ normalized = normalize_qq_connector_config(shared_config)
91
+ merged = deepcopy(normalized)
92
+ merged.pop("profiles", None)
93
+ app_secret = _as_text(profile.get("app_secret"))
94
+ app_secret_env = _as_text(profile.get("app_secret_env"))
95
+ merged.update(
96
+ {
97
+ "profile_id": str(profile.get("profile_id") or "").strip() or None,
98
+ "app_id": _as_text(profile.get("app_id")),
99
+ "app_secret": app_secret,
100
+ "app_secret_env": None if app_secret else app_secret_env,
101
+ "bot_name": _as_text(profile.get("bot_name")) or str(normalized.get("bot_name") or "DeepScientist"),
102
+ "main_chat_id": _as_text(profile.get("main_chat_id")),
103
+ "enabled": bool(normalized.get("enabled", False)) and bool(profile.get("enabled", True)),
104
+ "transport": "gateway_direct",
105
+ }
106
+ )
107
+ return merged
108
+
109
+
110
+ def qq_profile_label(profile: dict[str, Any] | None) -> str:
111
+ if not isinstance(profile, dict):
112
+ return "QQ"
113
+ bot_name = _as_text(profile.get("bot_name"))
114
+ app_id = _as_text(profile.get("app_id"))
115
+ if bot_name and app_id:
116
+ return f"{bot_name} · {app_id}"
117
+ if bot_name:
118
+ return bot_name
119
+ if app_id:
120
+ return f"QQ · {app_id}"
121
+ return "QQ"
122
+
123
+
124
+ def normalize_qq_connector_config(config: dict[str, Any] | None) -> dict[str, Any]:
125
+ payload = deepcopy(config or {})
126
+ shared_defaults = {
127
+ "enabled": False,
128
+ "transport": "gateway_direct",
129
+ "app_id": None,
130
+ "app_secret": None,
131
+ "app_secret_env": None,
132
+ "bot_name": "DeepScientist",
133
+ "command_prefix": "/",
134
+ "main_chat_id": None,
135
+ "require_at_in_groups": True,
136
+ "auto_bind_dm_to_active_quest": True,
137
+ "gateway_restart_on_config_change": True,
138
+ "auto_send_main_experiment_png": True,
139
+ "auto_send_analysis_summary_png": True,
140
+ "auto_send_slice_png": True,
141
+ "auto_send_paper_pdf": True,
142
+ "enable_markdown_send": False,
143
+ "enable_file_upload_experimental": False,
144
+ "profiles": [],
145
+ }
146
+ shared = {**shared_defaults, **payload}
147
+ shared["transport"] = "gateway_direct"
148
+ shared["command_prefix"] = _as_text(shared.get("command_prefix")) or "/"
149
+ shared["bot_name"] = _as_text(shared.get("bot_name")) or "DeepScientist"
150
+ _normalize_secret_pair(shared, "app_secret", "app_secret_env")
151
+
152
+ raw_profiles = payload.get("profiles")
153
+ items = list(raw_profiles) if isinstance(raw_profiles, list) else []
154
+ legacy_profile_seed = {
155
+ "app_id": payload.get("app_id"),
156
+ "app_secret": payload.get("app_secret"),
157
+ "app_secret_env": payload.get("app_secret_env"),
158
+ "bot_name": payload.get("bot_name"),
159
+ "main_chat_id": payload.get("main_chat_id"),
160
+ }
161
+ if not items:
162
+ has_direct_profile_seed = any(_as_text(legacy_profile_seed.get(key)) for key in ("app_id", "app_secret", "main_chat_id"))
163
+ has_env_profile_seed = bool(_as_text(legacy_profile_seed.get("app_secret_env")))
164
+ if has_direct_profile_seed or has_env_profile_seed or bool(payload.get("enabled")):
165
+ items = [legacy_profile_seed]
166
+
167
+ profiles: list[dict[str, Any]] = []
168
+ used_ids: set[str] = set()
169
+ for index, raw in enumerate(items, start=1):
170
+ if not isinstance(raw, dict):
171
+ continue
172
+ current = {**default_qq_profile(), **raw}
173
+ current["app_id"] = _as_text(current.get("app_id"))
174
+ current["app_secret"] = _as_text(current.get("app_secret"))
175
+ current["app_secret_env"] = _as_text(current.get("app_secret_env")) or shared["app_secret_env"]
176
+ _normalize_secret_pair(current, "app_secret", "app_secret_env")
177
+ current["bot_name"] = _as_text(current.get("bot_name")) or shared["bot_name"]
178
+ current["main_chat_id"] = _as_text(current.get("main_chat_id"))
179
+ current["enabled"] = _qq_profile_is_configured(current)
180
+ current["profile_id"] = _unique_profile_id(
181
+ _profile_id_seed(
182
+ profile_id=current.get("profile_id"),
183
+ app_id=current.get("app_id"),
184
+ bot_name=current.get("bot_name"),
185
+ index=index,
186
+ ),
187
+ used=used_ids,
188
+ )
189
+ profiles.append(current)
190
+
191
+ shared["profiles"] = profiles
192
+ shared["enabled"] = any(bool(item.get("enabled")) for item in profiles)
193
+ if len(profiles) == 1:
194
+ mirror = profiles[0]
195
+ shared["app_id"] = mirror.get("app_id")
196
+ shared["app_secret"] = mirror.get("app_secret")
197
+ shared["app_secret_env"] = mirror.get("app_secret_env")
198
+ shared["bot_name"] = mirror.get("bot_name")
199
+ shared["main_chat_id"] = mirror.get("main_chat_id")
200
+ else:
201
+ shared["app_id"] = None
202
+ shared["app_secret"] = None
203
+ shared["app_secret_env"] = None
204
+ shared["main_chat_id"] = None
205
+
206
+ return shared