@researai/deepscientist 1.5.2 → 1.5.3

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 (93) hide show
  1. package/README.md +22 -0
  2. package/bin/ds.js +384 -0
  3. package/docs/en/00_QUICK_START.md +22 -0
  4. package/docs/zh/00_QUICK_START.md +22 -0
  5. package/install.sh +120 -4
  6. package/package.json +1 -1
  7. package/pyproject.toml +1 -1
  8. package/src/deepscientist/__init__.py +1 -1
  9. package/src/deepscientist/artifact/service.py +1 -1
  10. package/src/deepscientist/bash_exec/monitor.py +23 -4
  11. package/src/deepscientist/bash_exec/runtime.py +3 -0
  12. package/src/deepscientist/bash_exec/service.py +132 -4
  13. package/src/deepscientist/bridges/base.py +10 -19
  14. package/src/deepscientist/channels/discord_gateway.py +25 -2
  15. package/src/deepscientist/channels/feishu_long_connection.py +41 -3
  16. package/src/deepscientist/channels/qq.py +524 -64
  17. package/src/deepscientist/channels/qq_gateway.py +22 -3
  18. package/src/deepscientist/channels/relay.py +429 -90
  19. package/src/deepscientist/channels/slack_socket.py +29 -5
  20. package/src/deepscientist/channels/telegram_polling.py +25 -2
  21. package/src/deepscientist/channels/whatsapp_local_session.py +32 -4
  22. package/src/deepscientist/cli.py +27 -0
  23. package/src/deepscientist/config/models.py +6 -40
  24. package/src/deepscientist/config/service.py +164 -155
  25. package/src/deepscientist/connector_profiles.py +346 -0
  26. package/src/deepscientist/connector_runtime.py +88 -43
  27. package/src/deepscientist/daemon/api/handlers.py +47 -10
  28. package/src/deepscientist/daemon/api/router.py +2 -2
  29. package/src/deepscientist/daemon/app.py +682 -218
  30. package/src/deepscientist/mcp/server.py +60 -7
  31. package/src/deepscientist/migration.py +114 -0
  32. package/src/deepscientist/prompts/builder.py +30 -3
  33. package/src/deepscientist/qq_profiles.py +186 -0
  34. package/src/prompts/connectors/qq.md +42 -2
  35. package/src/prompts/system.md +85 -5
  36. package/src/skills/analysis-campaign/SKILL.md +11 -5
  37. package/src/skills/baseline/SKILL.md +66 -31
  38. package/src/skills/decision/SKILL.md +1 -1
  39. package/src/skills/experiment/SKILL.md +11 -5
  40. package/src/skills/finalize/SKILL.md +1 -1
  41. package/src/skills/idea/SKILL.md +1 -1
  42. package/src/skills/intake-audit/SKILL.md +1 -1
  43. package/src/skills/rebuttal/SKILL.md +1 -1
  44. package/src/skills/review/SKILL.md +1 -1
  45. package/src/skills/scout/SKILL.md +1 -1
  46. package/src/skills/write/SKILL.md +1 -1
  47. package/src/tui/package.json +1 -1
  48. package/src/ui/dist/assets/{AiManusChatView-CZpg376x.js → AiManusChatView-qzChi9uh.js} +14 -37
  49. package/src/ui/dist/assets/{AnalysisPlugin-CtHA22g3.js → AnalysisPlugin-CcC_-UqN.js} +1 -1
  50. package/src/ui/dist/assets/{AutoFigurePlugin-BSWmLMmF.js → AutoFigurePlugin-DD8LkJLe.js} +5 -5
  51. package/src/ui/dist/assets/{CliPlugin-CJ7jdm_s.js → CliPlugin-DJJFfVmW.js} +17 -110
  52. package/src/ui/dist/assets/{CodeEditorPlugin-DhInVGFf.js → CodeEditorPlugin-CrjkHNLh.js} +8 -8
  53. package/src/ui/dist/assets/{CodeViewerPlugin-D1n8S9r5.js → CodeViewerPlugin-obnD6G5R.js} +5 -5
  54. package/src/ui/dist/assets/{DocViewerPlugin-C4XM_kqk.js → DocViewerPlugin-DB9SUQVd.js} +3 -3
  55. package/src/ui/dist/assets/{GitDiffViewerPlugin-W6kS9r6v.js → GitDiffViewerPlugin-DZLlNlD2.js} +1 -1
  56. package/src/ui/dist/assets/{ImageViewerPlugin-DPeUx_Oz.js → ImageViewerPlugin-BGwfDZ0Y.js} +5 -5
  57. package/src/ui/dist/assets/{LabCopilotPanel-eAelUaub.js → LabCopilotPanel-dfLptQcR.js} +10 -10
  58. package/src/ui/dist/assets/{LabPlugin-BbOrBxKY.js → LabPlugin-CeGjAl3A.js} +1 -1
  59. package/src/ui/dist/assets/{LatexPlugin-C-HhkVXY.js → LatexPlugin-BBJ7kd1V.js} +7 -7
  60. package/src/ui/dist/assets/{MarkdownViewerPlugin-BDIzIBfh.js → MarkdownViewerPlugin-DKZi7BcB.js} +4 -4
  61. package/src/ui/dist/assets/{MarketplacePlugin-DAOJphwr.js → MarketplacePlugin-C_k-9jD0.js} +3 -3
  62. package/src/ui/dist/assets/{NotebookEditor-BsoMvDoU.js → NotebookEditor-4R88_BMO.js} +1 -1
  63. package/src/ui/dist/assets/{PdfLoader-fiC7RtHf.js → PdfLoader-DwEFQLrw.js} +1 -1
  64. package/src/ui/dist/assets/{PdfMarkdownPlugin-C5OxZBFK.js → PdfMarkdownPlugin-D-jdsqF8.js} +3 -3
  65. package/src/ui/dist/assets/{PdfViewerPlugin-CAbxQebk.js → PdfViewerPlugin-CmeBGDY0.js} +10 -10
  66. package/src/ui/dist/assets/{SearchPlugin-SE33Lb9B.js → SearchPlugin-Dlz2WKJ4.js} +1 -1
  67. package/src/ui/dist/assets/{Stepper-0Av7GfV7.js → Stepper-ClOgzWM3.js} +1 -1
  68. package/src/ui/dist/assets/{TextViewerPlugin-Daf2gJDI.js → TextViewerPlugin-DDQWxibk.js} +4 -4
  69. package/src/ui/dist/assets/{VNCViewer-BKrMUIOX.js → VNCViewer-CJXT0Nm8.js} +9 -9
  70. package/src/ui/dist/assets/{bibtex-JBdOEe45.js → bibtex-DLr4Rtk4.js} +1 -1
  71. package/src/ui/dist/assets/{code-B0TDFCZz.js → code-DgKK408Y.js} +1 -1
  72. package/src/ui/dist/assets/{file-content-3YtrSacz.js → file-content-6HBqQnvQ.js} +1 -1
  73. package/src/ui/dist/assets/{file-diff-panel-CJEg5OG1.js → file-diff-panel-Dhu0TbBM.js} +1 -1
  74. package/src/ui/dist/assets/{file-socket-CYQYdmB1.js → file-socket-CP3iwVZG.js} +1 -1
  75. package/src/ui/dist/assets/{file-utils-Cd1C9Ppl.js → file-utils-BsS-Aw68.js} +1 -1
  76. package/src/ui/dist/assets/{image-B33ctrvC.js → image-ByeK-Zcv.js} +1 -1
  77. package/src/ui/dist/assets/{index-BVXsmS7V.js → index-BLjo5--a.js} +9499 -8688
  78. package/src/ui/dist/assets/{index-BNQWqmJ2.js → index-BdsE0uRz.js} +11 -11
  79. package/src/ui/dist/assets/{index-9CLPVeZh.js → index-C-eX-N6A.js} +1 -1
  80. package/src/ui/dist/assets/{index-SwmFAld3.css → index-CuQhlrR-.css} +49 -2
  81. package/src/ui/dist/assets/{index-Buw_N1VQ.js → index-DyremSIv.js} +2 -2
  82. package/src/ui/dist/assets/{message-square-D0cUJ9yU.js → message-square-DnagiLnc.js} +1 -1
  83. package/src/ui/dist/assets/{monaco-UZLYkp2n.js → monaco-4kBFeprs.js} +1 -1
  84. package/src/ui/dist/assets/{popover-CTeiY-dK.js → popover-hRCXZzs2.js} +1 -1
  85. package/src/ui/dist/assets/{project-sync-Dbs01Xky.js → project-sync-O_85YuP6.js} +1 -1
  86. package/src/ui/dist/assets/{sigma-CM08S-xT.js → sigma-DvKopSnL.js} +1 -1
  87. package/src/ui/dist/assets/{tooltip-pDtzvU9p.js → tooltip-BmlPc6kc.js} +1 -1
  88. package/src/ui/dist/assets/{trash-YvPCP-da.js → trash-n-UvdZFR.js} +1 -1
  89. package/src/ui/dist/assets/{useCliAccess-Bavi74Ac.js → useCliAccess-WDd3_wIh.js} +1 -1
  90. package/src/ui/dist/assets/{useFileDiffOverlay-CVXY6oeg.js → useFileDiffOverlay-rXLIL2NF.js} +1 -1
  91. package/src/ui/dist/assets/{wrap-text-Cf4flRW7.js → wrap-text-qIYQ4a_W.js} +1 -1
  92. package/src/ui/dist/assets/{zoom-out-Hb0Z1YpT.js → zoom-out-fZXCEFsy.js} +1 -1
  93. package/src/ui/dist/index.html +2 -2
@@ -10,6 +10,7 @@ from urllib.request import Request, urlopen
10
10
  from websockets.exceptions import ConnectionClosed
11
11
  from websockets.sync.client import connect as websocket_connect
12
12
 
13
+ from ..connector_runtime import format_conversation_id
13
14
  from ..shared import read_json, utc_now, write_json
14
15
 
15
16
 
@@ -24,11 +25,17 @@ class DiscordGatewayService:
24
25
  config: dict[str, Any],
25
26
  on_event: Callable[[dict[str, Any]], None],
26
27
  log: Callable[[str, str], None] | None = None,
28
+ profile_id: str | None = None,
29
+ profile_label: str | None = None,
30
+ encode_profile_id: bool = False,
27
31
  ) -> None:
28
32
  self.home = home
29
33
  self.config = config
30
34
  self.on_event = on_event
31
35
  self.log = log or self._default_log
36
+ self.profile_id = str(profile_id or "").strip() or None
37
+ self.profile_label = str(profile_label or "").strip() or None
38
+ self._encode_profile_id = bool(encode_profile_id and self.profile_id)
32
39
  self._thread: threading.Thread | None = None
33
40
  self._stop_event = threading.Event()
34
41
  self._heartbeat_stop = threading.Event()
@@ -37,6 +44,8 @@ class DiscordGatewayService:
37
44
  self._seq: int | None = None
38
45
  self._bot_user_id: str | None = None
39
46
  self._root = home / "logs" / "connectors" / "discord"
47
+ if self.profile_id:
48
+ self._root = self._root / "profiles" / self.profile_id
40
49
  self._runtime_path = self._root / "runtime.json"
41
50
 
42
51
  def start(self) -> bool:
@@ -71,7 +80,7 @@ class DiscordGatewayService:
71
80
  self._thread = threading.Thread(
72
81
  target=self._run,
73
82
  daemon=True,
74
- name="deepscientist-discord-gateway",
83
+ name=f"deepscientist-discord-gateway-{self.profile_id or 'default'}",
75
84
  )
76
85
  self._thread.start()
77
86
  return True
@@ -288,12 +297,22 @@ class DiscordGatewayService:
288
297
  "sender_id": sender_id,
289
298
  "sender_name": str(author.get("global_name") or author.get("username") or sender_id).strip(),
290
299
  "message_id": str(data.get("id") or "").strip(),
291
- "conversation_id": f"discord:{chat_type}:{channel_id}",
300
+ "conversation_id": self._conversation_id(chat_type, channel_id),
301
+ "profile_id": self.profile_id,
302
+ "profile_label": self.profile_label,
292
303
  "text": normalized_text,
293
304
  "mentioned": mentioned,
294
305
  "raw_event": data,
295
306
  }
296
307
 
308
+ def _conversation_id(self, chat_type: str, chat_id: str) -> str:
309
+ return format_conversation_id(
310
+ "discord",
311
+ chat_type,
312
+ chat_id,
313
+ profile_id=self.profile_id if self._encode_profile_id else None,
314
+ )
315
+
297
316
  @staticmethod
298
317
  def _strip_bot_mention(text: str, bot_user_id: str) -> str:
299
318
  cleaned = str(text or "").strip()
@@ -354,6 +373,10 @@ class DiscordGatewayService:
354
373
  state = read_json(self._runtime_path, {}) or {}
355
374
  if not isinstance(state, dict):
356
375
  state = {}
376
+ if self.profile_id:
377
+ state["profile_id"] = self.profile_id
378
+ if self.profile_label:
379
+ state["profile_label"] = self.profile_label
357
380
  state.update(patch)
358
381
  write_json(self._runtime_path, state)
359
382
 
@@ -9,6 +9,7 @@ from pathlib import Path
9
9
  from typing import Any, Callable
10
10
 
11
11
  from ..bridges.connectors import FeishuConnectorBridge
12
+ from ..connector_runtime import format_conversation_id, parse_conversation_id
12
13
  from ..shared import read_json, utc_now, write_json
13
14
 
14
15
 
@@ -29,17 +30,25 @@ class FeishuLongConnectionService:
29
30
  config: dict[str, Any],
30
31
  on_event: Callable[[dict[str, Any]], None],
31
32
  log: Callable[[str, str], None] | None = None,
33
+ profile_id: str | None = None,
34
+ profile_label: str | None = None,
35
+ encode_profile_id: bool = False,
32
36
  ) -> None:
33
37
  self.home = home
34
38
  self.config = config
35
39
  self.on_event = on_event
36
40
  self.log = log or self._default_log
41
+ self.profile_id = str(profile_id or "").strip() or None
42
+ self.profile_label = str(profile_label or "").strip() or None
43
+ self._encode_profile_id = bool(encode_profile_id and self.profile_id)
37
44
  self._thread: threading.Thread | None = None
38
45
  self._stop_event = threading.Event()
39
46
  self._loop: asyncio.AbstractEventLoop | None = None
40
47
  self._async_stop: asyncio.Event | None = None
41
48
  self._client: Any = None
42
49
  self._root = home / "logs" / "connectors" / "feishu"
50
+ if self.profile_id:
51
+ self._root = self._root / "profiles" / self.profile_id
43
52
  self._runtime_path = self._root / "runtime.json"
44
53
 
45
54
  def start(self) -> bool:
@@ -85,7 +94,7 @@ class FeishuLongConnectionService:
85
94
  self._thread = threading.Thread(
86
95
  target=self._run,
87
96
  daemon=True,
88
- name="deepscientist-feishu-long-connection",
97
+ name=f"deepscientist-feishu-long-connection-{self.profile_id or 'default'}",
89
98
  )
90
99
  self._thread.start()
91
100
  return True
@@ -201,16 +210,41 @@ class FeishuLongConnectionService:
201
210
  config=self.config,
202
211
  )
203
212
  for event in result.events:
204
- self.on_event(event)
213
+ normalized = self._normalize_event(event)
214
+ self.on_event(normalized)
205
215
  self._write_state(
206
216
  connected=True,
207
217
  connection_state="connected",
208
218
  auth_state="ready",
209
219
  last_event_at=utc_now(),
210
- last_conversation_id=event.get("conversation_id"),
220
+ last_conversation_id=normalized.get("conversation_id"),
211
221
  updated_at=utc_now(),
212
222
  )
213
223
 
224
+ def _normalize_event(self, event: dict[str, Any]) -> dict[str, Any]:
225
+ normalized = dict(event)
226
+ chat_type = str(normalized.get("chat_type") or "direct").strip().lower() or "direct"
227
+ group_id = str(normalized.get("group_id") or "").strip()
228
+ direct_id = str(normalized.get("direct_id") or "").strip()
229
+ chat_id = group_id if chat_type == "group" else direct_id
230
+ if not chat_id:
231
+ parsed = parse_conversation_id(normalized.get("conversation_id"))
232
+ if parsed is not None:
233
+ chat_type = str(parsed.get("chat_type") or chat_type).strip().lower() or chat_type
234
+ chat_id = str(parsed.get("chat_id") or "").strip()
235
+ normalized["conversation_id"] = self._conversation_id(chat_type, chat_id or "unknown")
236
+ normalized["profile_id"] = self.profile_id
237
+ normalized["profile_label"] = self.profile_label
238
+ return normalized
239
+
240
+ def _conversation_id(self, chat_type: str, chat_id: str) -> str:
241
+ return format_conversation_id(
242
+ "feishu",
243
+ chat_type,
244
+ chat_id,
245
+ profile_id=self.profile_id if self._encode_profile_id else None,
246
+ )
247
+
214
248
  @staticmethod
215
249
  def _sdk_bundle() -> dict[str, Any] | None:
216
250
  try:
@@ -239,6 +273,10 @@ class FeishuLongConnectionService:
239
273
  state = read_json(self._runtime_path, {}) or {}
240
274
  if not isinstance(state, dict):
241
275
  state = {}
276
+ if self.profile_id:
277
+ state["profile_id"] = self.profile_id
278
+ if self.profile_label:
279
+ state["profile_label"] = self.profile_label
242
280
  state.update(patch)
243
281
  write_json(self._runtime_path, state)
244
282