@researai/deepscientist 1.5.2 → 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 (114) hide show
  1. package/README.md +22 -0
  2. package/bin/ds.js +399 -175
  3. package/docs/en/00_QUICK_START.md +22 -0
  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 +22 -0
  20. package/docs/zh/01_SETTINGS_REFERENCE.md +14 -5
  21. package/docs/zh/99_ACKNOWLEDGEMENTS.md +1 -0
  22. package/install.sh +120 -4
  23. package/package.json +8 -4
  24. package/pyproject.toml +1 -1
  25. package/src/deepscientist/__init__.py +1 -1
  26. package/src/deepscientist/artifact/service.py +1 -1
  27. package/src/deepscientist/bash_exec/monitor.py +23 -4
  28. package/src/deepscientist/bash_exec/runtime.py +3 -0
  29. package/src/deepscientist/bash_exec/service.py +132 -4
  30. package/src/deepscientist/bridges/base.py +12 -20
  31. package/src/deepscientist/bridges/connectors.py +2 -1
  32. package/src/deepscientist/channels/discord_gateway.py +27 -4
  33. package/src/deepscientist/channels/feishu_long_connection.py +41 -3
  34. package/src/deepscientist/channels/qq.py +524 -64
  35. package/src/deepscientist/channels/qq_gateway.py +24 -5
  36. package/src/deepscientist/channels/relay.py +429 -90
  37. package/src/deepscientist/channels/slack_socket.py +31 -7
  38. package/src/deepscientist/channels/telegram_polling.py +27 -3
  39. package/src/deepscientist/channels/whatsapp_local_session.py +32 -4
  40. package/src/deepscientist/cli.py +31 -1
  41. package/src/deepscientist/config/models.py +13 -43
  42. package/src/deepscientist/config/service.py +216 -157
  43. package/src/deepscientist/connector_profiles.py +346 -0
  44. package/src/deepscientist/connector_runtime.py +88 -43
  45. package/src/deepscientist/daemon/api/handlers.py +53 -16
  46. package/src/deepscientist/daemon/api/router.py +2 -2
  47. package/src/deepscientist/daemon/app.py +747 -228
  48. package/src/deepscientist/mcp/server.py +60 -7
  49. package/src/deepscientist/migration.py +114 -0
  50. package/src/deepscientist/network.py +78 -0
  51. package/src/deepscientist/prompts/builder.py +50 -4
  52. package/src/deepscientist/qq_profiles.py +186 -0
  53. package/src/deepscientist/quest/service.py +1 -1
  54. package/src/deepscientist/skills/installer.py +77 -1
  55. package/src/prompts/connectors/qq.md +42 -2
  56. package/src/prompts/system.md +162 -6
  57. package/src/skills/analysis-campaign/SKILL.md +19 -5
  58. package/src/skills/baseline/SKILL.md +66 -31
  59. package/src/skills/decision/SKILL.md +1 -1
  60. package/src/skills/experiment/SKILL.md +11 -5
  61. package/src/skills/finalize/SKILL.md +1 -1
  62. package/src/skills/idea/SKILL.md +246 -4
  63. package/src/skills/intake-audit/SKILL.md +1 -1
  64. package/src/skills/rebuttal/SKILL.md +1 -1
  65. package/src/skills/review/SKILL.md +1 -1
  66. package/src/skills/scout/SKILL.md +1 -1
  67. package/src/skills/write/SKILL.md +152 -2
  68. package/src/tui/package.json +1 -1
  69. package/src/ui/dist/assets/{AiManusChatView-CZpg376x.js → AiManusChatView-BGLArZRn.js} +14 -37
  70. package/src/ui/dist/assets/{AnalysisPlugin-CtHA22g3.js → AnalysisPlugin-BgDGSigG.js} +1 -1
  71. package/src/ui/dist/assets/{AutoFigurePlugin-BSWmLMmF.js → AutoFigurePlugin-B65HD7L4.js} +5 -5
  72. package/src/ui/dist/assets/{CliPlugin-CJ7jdm_s.js → CliPlugin-CUqgsFHC.js} +17 -110
  73. package/src/ui/dist/assets/{CodeEditorPlugin-DhInVGFf.js → CodeEditorPlugin-CF5EdvaS.js} +8 -8
  74. package/src/ui/dist/assets/{CodeViewerPlugin-D1n8S9r5.js → CodeViewerPlugin-DEeU063D.js} +5 -5
  75. package/src/ui/dist/assets/{DocViewerPlugin-C4XM_kqk.js → DocViewerPlugin-Df-FuDlZ.js} +3 -3
  76. package/src/ui/dist/assets/{GitDiffViewerPlugin-W6kS9r6v.js → GitDiffViewerPlugin-RAnNaRxM.js} +1 -1
  77. package/src/ui/dist/assets/{ImageViewerPlugin-DPeUx_Oz.js → ImageViewerPlugin-DXJ0ZJGg.js} +5 -5
  78. package/src/ui/dist/assets/{LabCopilotPanel-eAelUaub.js → LabCopilotPanel-BlO-sKsj.js} +10 -10
  79. package/src/ui/dist/assets/{LabPlugin-BbOrBxKY.js → LabPlugin-BajPZW5v.js} +1 -1
  80. package/src/ui/dist/assets/{LatexPlugin-C-HhkVXY.js → LatexPlugin-F1OEol8D.js} +7 -7
  81. package/src/ui/dist/assets/{MarkdownViewerPlugin-BDIzIBfh.js → MarkdownViewerPlugin-MhUupqwT.js} +4 -4
  82. package/src/ui/dist/assets/{MarketplacePlugin-DAOJphwr.js → MarketplacePlugin-DxhIEsv0.js} +3 -3
  83. package/src/ui/dist/assets/{NotebookEditor-BsoMvDoU.js → NotebookEditor-q7TkhewC.js} +1 -1
  84. package/src/ui/dist/assets/{PdfLoader-fiC7RtHf.js → PdfLoader-B8ZOTKFc.js} +1 -1
  85. package/src/ui/dist/assets/{PdfMarkdownPlugin-C5OxZBFK.js → PdfMarkdownPlugin-xFPvzvWh.js} +3 -3
  86. package/src/ui/dist/assets/{PdfViewerPlugin-CAbxQebk.js → PdfViewerPlugin-EjEcsIB8.js} +10 -10
  87. package/src/ui/dist/assets/{SearchPlugin-SE33Lb9B.js → SearchPlugin-ixY-1lgW.js} +1 -1
  88. package/src/ui/dist/assets/{Stepper-0Av7GfV7.js → Stepper-gYFK2Pgz.js} +1 -1
  89. package/src/ui/dist/assets/{TextViewerPlugin-Daf2gJDI.js → TextViewerPlugin-Cym6pv_n.js} +4 -4
  90. package/src/ui/dist/assets/{VNCViewer-BKrMUIOX.js → VNCViewer-BPmIHcmK.js} +9 -9
  91. package/src/ui/dist/assets/{bibtex-JBdOEe45.js → bibtex-Btv6Wi7f.js} +1 -1
  92. package/src/ui/dist/assets/{code-B0TDFCZz.js → code-BlG7g85c.js} +1 -1
  93. package/src/ui/dist/assets/{file-content-3YtrSacz.js → file-content-DBT5OfTZ.js} +1 -1
  94. package/src/ui/dist/assets/{file-diff-panel-CJEg5OG1.js → file-diff-panel-BWXYzqHk.js} +1 -1
  95. package/src/ui/dist/assets/{file-socket-CYQYdmB1.js → file-socket-wDlx6byM.js} +1 -1
  96. package/src/ui/dist/assets/{file-utils-Cd1C9Ppl.js → file-utils-Ba3nJmH0.js} +1 -1
  97. package/src/ui/dist/assets/{image-B33ctrvC.js → image-BwtCyguk.js} +1 -1
  98. package/src/ui/dist/assets/{index-BNQWqmJ2.js → index-B-2scqCJ.js} +11 -11
  99. package/src/ui/dist/assets/{index-BVXsmS7V.js → index-Bz5AaWL7.js} +52383 -51440
  100. package/src/ui/dist/assets/{index-Buw_N1VQ.js → index-CfRpE209.js} +2 -2
  101. package/src/ui/dist/assets/{index-9CLPVeZh.js → index-DcqvKzeJ.js} +1 -1
  102. package/src/ui/dist/assets/{index-SwmFAld3.css → index-DpMZw8aM.css} +49 -2
  103. package/src/ui/dist/assets/{message-square-D0cUJ9yU.js → message-square-BnlyWVH0.js} +1 -1
  104. package/src/ui/dist/assets/{monaco-UZLYkp2n.js → monaco-CXe0pAVe.js} +1 -1
  105. package/src/ui/dist/assets/{popover-CTeiY-dK.js → popover-BCHmVhHj.js} +1 -1
  106. package/src/ui/dist/assets/{project-sync-Dbs01Xky.js → project-sync-Brk6kaOD.js} +1 -1
  107. package/src/ui/dist/assets/{sigma-CM08S-xT.js → sigma-D72eSUep.js} +1 -1
  108. package/src/ui/dist/assets/{tooltip-pDtzvU9p.js → tooltip-BMWd0dqX.js} +1 -1
  109. package/src/ui/dist/assets/{trash-YvPCP-da.js → trash-BIt_eWIS.js} +1 -1
  110. package/src/ui/dist/assets/{useCliAccess-Bavi74Ac.js → useCliAccess-N1hkTRrR.js} +1 -1
  111. package/src/ui/dist/assets/{useFileDiffOverlay-CVXY6oeg.js → useFileDiffOverlay-DPRPv6rv.js} +1 -1
  112. package/src/ui/dist/assets/{wrap-text-Cf4flRW7.js → wrap-text-E5-UheyP.js} +1 -1
  113. package/src/ui/dist/assets/{zoom-out-Hb0Z1YpT.js → zoom-out-D4TR-ZZ_.js} +1 -1
  114. package/src/ui/dist/index.html +2 -2
@@ -205,19 +205,12 @@ npm --prefix src/ui run build</pre>
205
205
  def connectors(self) -> list[dict]:
206
206
  return self.app.list_connector_statuses()
207
207
 
208
+ def connectors_availability(self) -> dict:
209
+ return self.app.connector_availability_summary()
210
+
208
211
  def baselines(self) -> list[dict]:
209
212
  return self.app.artifact_service.baselines.list_entries()
210
213
 
211
- def bridge_webhook(self, connector: str, *, method: str, path: str, raw_body: bytes, headers: dict[str, str], body: dict) -> tuple[int, dict, bytes | str] | dict:
212
- return self.app.handle_bridge_webhook(
213
- connector,
214
- method=method,
215
- path=path,
216
- raw_body=raw_body,
217
- headers=headers,
218
- body=body,
219
- )
220
-
221
214
  def qq_bindings(self) -> list[dict]:
222
215
  return self.app.list_qq_bindings()
223
216
 
@@ -246,12 +239,33 @@ npm --prefix src/ui run build</pre>
246
239
  preferred_connector_conversation_id = (
247
240
  str(body.get("preferred_connector_conversation_id") or "").strip() or None
248
241
  )
242
+ requested_connector_bindings = (
243
+ [dict(item) for item in body.get("requested_connector_bindings") if isinstance(item, dict)]
244
+ if isinstance(body.get("requested_connector_bindings"), list)
245
+ else []
246
+ )
247
+ force_connector_rebind_raw = body.get("force_connector_rebind")
248
+ force_connector_rebind = bool(force_connector_rebind_raw) and str(force_connector_rebind_raw).strip().lower() not in {
249
+ "0",
250
+ "false",
251
+ "no",
252
+ "off",
253
+ }
249
254
  requested_baseline_ref = body.get("requested_baseline_ref")
250
255
  startup_contract = body.get("startup_contract")
251
256
  auto_start = body.get("auto_start") is True
252
257
  initial_message = str(body.get("initial_message") or "").strip()
253
258
  if not goal:
254
259
  return {"ok": False, "message": "Quest goal is required."}
260
+ if requested_connector_bindings and not force_connector_rebind:
261
+ conflicts = self.app.preview_connector_binding_conflicts(requested_connector_bindings)
262
+ if conflicts:
263
+ return 409, {
264
+ "ok": False,
265
+ "conflict": True,
266
+ "message": "One or more selected connector targets are already bound to another quest.",
267
+ "conflicts": conflicts,
268
+ }
255
269
  try:
256
270
  snapshot = self.app.create_quest(
257
271
  goal=goal,
@@ -259,6 +273,8 @@ npm --prefix src/ui run build</pre>
259
273
  quest_id=quest_id,
260
274
  source=source,
261
275
  preferred_connector_conversation_id=preferred_connector_conversation_id,
276
+ requested_connector_bindings=requested_connector_bindings,
277
+ force_connector_rebind=force_connector_rebind,
262
278
  requested_baseline_ref=requested_baseline_ref if isinstance(requested_baseline_ref, dict) else None,
263
279
  startup_contract=startup_contract if isinstance(startup_contract, dict) else None,
264
280
  )
@@ -386,9 +402,19 @@ npm --prefix src/ui run build</pre>
386
402
  }
387
403
 
388
404
  def quest_bindings(self, quest_id: str, body: dict) -> dict | tuple[int, dict]:
405
+ requested_bindings = (
406
+ [dict(item) for item in body.get("bindings") if isinstance(item, dict)]
407
+ if isinstance(body.get("bindings"), list)
408
+ else []
409
+ )
389
410
  conversation_id = str(body.get("conversation_id") or body.get("source") or "").strip() or None
411
+ connector_name = str(body.get("connector") or "").strip() or None
390
412
  force_raw = body.get("force")
391
413
  force = bool(force_raw) and str(force_raw).strip().lower() not in {"0", "false", "no", "off"}
414
+ if requested_bindings:
415
+ return self.app.update_quest_bindings(quest_id, requested_bindings, force=force)
416
+ if connector_name:
417
+ return self.app.update_quest_connector_binding(quest_id, connector_name, conversation_id, force=force)
392
418
  return self.app.update_quest_binding(quest_id, conversation_id, force=force)
393
419
 
394
420
  def quest_session(self, quest_id: str) -> dict:
@@ -455,6 +481,7 @@ npm --prefix src/ui run build</pre>
455
481
  def bash_sessions(self, quest_id: str, path: str) -> list[dict]:
456
482
  query = self.parse_query(path)
457
483
  status = ((query.get("status") or [""])[0] or "").strip() or None
484
+ kind = ((query.get("kind") or [""])[0] or "").strip() or None
458
485
  chat_session_id = ((query.get("chat_session_id") or [""])[0] or "").strip() or None
459
486
  limit_raw = ((query.get("limit") or ["200"])[0] or "200").strip()
460
487
  try:
@@ -475,6 +502,7 @@ npm --prefix src/ui run build</pre>
475
502
  return self.app.bash_exec_service.list_sessions(
476
503
  quest_root,
477
504
  status=status,
505
+ kind=kind,
478
506
  agent_ids=agent_ids or None,
479
507
  agent_instance_ids=agent_instance_ids or None,
480
508
  chat_session_id=chat_session_id,
@@ -492,12 +520,14 @@ npm --prefix src/ui run build</pre>
492
520
  query = self.parse_query(path)
493
521
  limit_raw = ((query.get("limit") or ["200"])[0] or "200").strip()
494
522
  before_seq_raw = ((query.get("before_seq") or [""])[0] or "").strip()
523
+ after_seq_raw = ((query.get("after_seq") or [""])[0] or "").strip()
495
524
  order = ((query.get("order") or ["asc"])[0] or "asc").strip().lower()
496
525
  try:
497
526
  limit = max(1, min(int(limit_raw), 1000))
498
527
  except ValueError:
499
528
  limit = 200
500
529
  before_seq = int(before_seq_raw) if before_seq_raw.isdigit() else None
530
+ after_seq = int(after_seq_raw) if after_seq_raw.isdigit() else None
501
531
  quest_root = self.app.quest_service._quest_root(quest_id)
502
532
  try:
503
533
  entries, meta = self.app.bash_exec_service.read_log_entries(
@@ -505,6 +535,7 @@ npm --prefix src/ui run build</pre>
505
535
  bash_id,
506
536
  limit=limit,
507
537
  before_seq=before_seq,
538
+ after_seq=after_seq,
508
539
  order=order,
509
540
  )
510
541
  except FileNotFoundError:
@@ -521,15 +552,21 @@ npm --prefix src/ui run build</pre>
521
552
 
522
553
  def bash_stop(self, quest_id: str, bash_id: str, body: dict) -> dict | tuple[int, dict]:
523
554
  quest_root = self.app.quest_service._quest_root(quest_id)
555
+ wait = bool(body.get("wait"))
556
+ timeout_seconds_raw = body.get("timeout_seconds")
557
+ timeout_seconds = timeout_seconds_raw if isinstance(timeout_seconds_raw, int) and timeout_seconds_raw > 0 else None
524
558
  try:
525
559
  session = self.app.bash_exec_service.request_stop(
526
560
  quest_root,
527
561
  bash_id,
528
562
  reason=str(body.get("reason") or "").strip() or None,
529
563
  user_id="web-react",
564
+ force=bool(body.get("force")),
530
565
  )
531
566
  except FileNotFoundError:
532
567
  return 404, {"success": False, "status": "not_found"}
568
+ if wait:
569
+ session = self.app.bash_exec_service.wait_for_session(quest_root, bash_id, timeout_seconds=timeout_seconds)
533
570
  return {
534
571
  "success": True,
535
572
  "status": session.get("status"),
@@ -917,7 +954,7 @@ npm --prefix src/ui run build</pre>
917
954
  def latex_compile(self, project_id: str, folder_id: str, body: dict) -> dict:
918
955
  return self.app.latex_service.compile(
919
956
  project_id,
920
- unquote(folder_id),
957
+ folder_id,
921
958
  compiler=body.get("compiler"),
922
959
  main_file_id=body.get("main_file_id"),
923
960
  stop_on_first_error=body.get("stop_on_first_error"),
@@ -931,13 +968,13 @@ npm --prefix src/ui run build</pre>
931
968
  limit = int(limit_raw)
932
969
  except ValueError:
933
970
  limit = 10
934
- 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)
935
972
 
936
973
  def latex_build(self, project_id: str, folder_id: str, build_id: str) -> dict:
937
- 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)
938
975
 
939
976
  def latex_build_pdf(self, project_id: str, folder_id: str, build_id: str) -> tuple[int, dict, bytes]:
940
- 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)
941
978
  headers = {
942
979
  **self._asset_headers("application/pdf"),
943
980
  "Content-Disposition": f'inline; filename="{file_name}"',
@@ -945,11 +982,11 @@ npm --prefix src/ui run build</pre>
945
982
  return 200, headers, payload
946
983
 
947
984
  def latex_build_log(self, project_id: str, folder_id: str, build_id: str) -> tuple[int, dict, bytes]:
948
- 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)
949
986
  return 200, self._asset_headers("text/plain; charset=utf-8"), text.encode("utf-8")
950
987
 
951
988
  def latex_archive(self, project_id: str, folder_id: str) -> tuple[int, dict, bytes]:
952
- 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)
953
990
  headers = {
954
991
  **self._asset_headers("application/zip"),
955
992
  "Content-Disposition": f'attachment; filename="{file_name}"',
@@ -14,8 +14,7 @@ ROUTES: list[tuple[str, re.Pattern[str], str]] = [
14
14
  ("POST", re.compile(r"^/api/admin/shutdown$"), "admin_shutdown"),
15
15
  ("GET", re.compile(r"^/api/acp/status$"), "acp_status"),
16
16
  ("GET", re.compile(r"^/api/connectors$"), "connectors"),
17
- ("GET", re.compile(r"^/api/bridges/(?P<connector>[^/]+)/webhook$"), "bridge_webhook"),
18
- ("POST", re.compile(r"^/api/bridges/(?P<connector>[^/]+)/webhook$"), "bridge_webhook"),
17
+ ("GET", re.compile(r"^/api/connectors/availability$"), "connectors_availability"),
19
18
  ("GET", re.compile(r"^/api/connectors/qq/bindings$"), "qq_bindings"),
20
19
  ("POST", re.compile(r"^/api/connectors/qq/inbound$"), "qq_inbound"),
21
20
  ("GET", re.compile(r"^/api/connectors/(?P<connector>[^/]+)/bindings$"), "connector_bindings"),
@@ -30,6 +29,7 @@ ROUTES: list[tuple[str, re.Pattern[str], str]] = [
30
29
  ("DELETE", re.compile(r"^/api/quests/(?P<quest_id>[^/]+)/baseline-binding$"), "quest_baseline_unbind"),
31
30
  ("PATCH", re.compile(r"^/api/quests/(?P<quest_id>[^/]+)/settings$"), "quest_settings"),
32
31
  ("POST", re.compile(r"^/api/quests/(?P<quest_id>[^/]+)/bindings$"), "quest_bindings"),
32
+ ("PUT", re.compile(r"^/api/quests/(?P<quest_id>[^/]+)/bindings$"), "quest_bindings"),
33
33
  ("GET", re.compile(r"^/api/quests/(?P<quest_id>[^/]+)/session$"), "quest_session"),
34
34
  ("GET", re.compile(r"^/api/quests/(?P<quest_id>[^/]+)/events$"), "quest_events"),
35
35
  ("GET", re.compile(r"^/api/quests/(?P<quest_id>[^/]+)/artifacts$"), "quest_artifacts"),