@researai/deepscientist 1.5.16 → 1.5.17

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 (82) hide show
  1. package/README.md +66 -23
  2. package/bin/ds.js +550 -19
  3. package/docs/en/00_QUICK_START.md +65 -5
  4. package/docs/en/01_SETTINGS_REFERENCE.md +1 -1
  5. package/docs/en/09_DOCTOR.md +14 -3
  6. package/docs/en/15_CODEX_PROVIDER_SETUP.md +12 -3
  7. package/docs/en/21_LOCAL_MODEL_BACKENDS_GUIDE.md +283 -0
  8. package/docs/en/91_DEVELOPMENT.md +237 -0
  9. package/docs/en/README.md +7 -3
  10. package/docs/zh/00_QUICK_START.md +54 -5
  11. package/docs/zh/01_SETTINGS_REFERENCE.md +1 -1
  12. package/docs/zh/09_DOCTOR.md +15 -4
  13. package/docs/zh/15_CODEX_PROVIDER_SETUP.md +12 -3
  14. package/docs/zh/21_LOCAL_MODEL_BACKENDS_GUIDE.md +281 -0
  15. package/docs/zh/README.md +7 -3
  16. package/install.sh +46 -4
  17. package/package.json +2 -1
  18. package/pyproject.toml +1 -1
  19. package/src/deepscientist/__init__.py +1 -1
  20. package/src/deepscientist/bridges/connectors.py +8 -2
  21. package/src/deepscientist/codex_cli_compat.py +185 -72
  22. package/src/deepscientist/config/service.py +154 -6
  23. package/src/deepscientist/daemon/api/handlers.py +130 -25
  24. package/src/deepscientist/daemon/api/router.py +5 -0
  25. package/src/deepscientist/daemon/app.py +446 -22
  26. package/src/deepscientist/diagnostics/__init__.py +6 -0
  27. package/src/deepscientist/diagnostics/runner_failures.py +130 -0
  28. package/src/deepscientist/doctor.py +207 -3
  29. package/src/deepscientist/prompts/builder.py +22 -4
  30. package/src/deepscientist/quest/service.py +413 -13
  31. package/src/deepscientist/runners/codex.py +59 -14
  32. package/src/deepscientist/shared.py +19 -0
  33. package/src/prompts/contracts/shared_interaction.md +3 -2
  34. package/src/prompts/system.md +13 -0
  35. package/src/prompts/system_copilot.md +13 -0
  36. package/src/tui/package.json +1 -1
  37. package/src/ui/dist/assets/{AiManusChatView-COFACy7V.js → AiManusChatView-Bv-Z8YpU.js} +44 -44
  38. package/src/ui/dist/assets/{AnalysisPlugin-DnSm0GZn.js → AnalysisPlugin-BCKAfjba.js} +1 -1
  39. package/src/ui/dist/assets/{CliPlugin-CvwCmDQ5.js → CliPlugin-BCKcpc35.js} +4 -4
  40. package/src/ui/dist/assets/{CodeEditorPlugin-cOqSa0xq.js → CodeEditorPlugin-DbOfSJ8K.js} +1 -1
  41. package/src/ui/dist/assets/{CodeViewerPlugin-itb0tltR.js → CodeViewerPlugin-CbaFRrUU.js} +3 -3
  42. package/src/ui/dist/assets/{DocViewerPlugin-DqKkiCI6.js → DocViewerPlugin-DAjLVeQD.js} +3 -3
  43. package/src/ui/dist/assets/{GitCommitViewerPlugin-DVgNHBCS.js → GitCommitViewerPlugin-CIUqbUDO.js} +1 -1
  44. package/src/ui/dist/assets/{GitDiffViewerPlugin-DxL2ezFG.js → GitDiffViewerPlugin-CQACjoAA.js} +1 -1
  45. package/src/ui/dist/assets/{GitSnapshotViewer-B_RQm1YZ.js → GitSnapshotViewer-0r4nLPke.js} +1 -1
  46. package/src/ui/dist/assets/{ImageViewerPlugin-tHqlXY3n.js → ImageViewerPlugin-nBOmI2v_.js} +3 -3
  47. package/src/ui/dist/assets/{LabCopilotPanel-ClMbq5Yu.js → LabCopilotPanel-BHxOxF4z.js} +1 -1
  48. package/src/ui/dist/assets/{LabPlugin-L_SuE8ow.js → LabPlugin-BKoZGs95.js} +1 -1
  49. package/src/ui/dist/assets/{LatexPlugin-B495DTXC.js → LatexPlugin-ZwtV8pIp.js} +1 -1
  50. package/src/ui/dist/assets/{MarkdownViewerPlugin-DG28-61B.js → MarkdownViewerPlugin-DKqVfKyW.js} +3 -3
  51. package/src/ui/dist/assets/{MarketplacePlugin-BiOGT-Kj.js → MarketplacePlugin-BwxStZ9D.js} +1 -1
  52. package/src/ui/dist/assets/{NotebookEditor-C-4Kt1p9.js → NotebookEditor-BEQhaQbt.js} +1 -1
  53. package/src/ui/dist/assets/{NotebookEditor-CVsj8h_T.js → NotebookEditor-DB9N_T9q.js} +23 -23
  54. package/src/ui/dist/assets/{PdfLoader-CASDQmxJ.js → PdfLoader-eWBONbQP.js} +1 -1
  55. package/src/ui/dist/assets/{PdfMarkdownPlugin-BFhwoKsY.js → PdfMarkdownPlugin-D22YOZL3.js} +1 -1
  56. package/src/ui/dist/assets/{PdfViewerPlugin-DcOzU9vd.js → PdfViewerPlugin-c-RK9DLM.js} +3 -3
  57. package/src/ui/dist/assets/{SearchPlugin-CHj7M58O.js → SearchPlugin-CxF9ytAx.js} +1 -1
  58. package/src/ui/dist/assets/{TextViewerPlugin-CB4DYfWO.js → TextViewerPlugin-C5xqeeUH.js} +2 -2
  59. package/src/ui/dist/assets/{VNCViewer-CjlbyCB3.js → VNCViewer-BoLGLnHz.js} +1 -1
  60. package/src/ui/dist/assets/{bot-CFkZY-JP.js → bot-DREQOxzP.js} +1 -1
  61. package/src/ui/dist/assets/{chevron-up-Dq5ofbht.js → chevron-up-C9Qpx4DE.js} +1 -1
  62. package/src/ui/dist/assets/{code-DLC6G24T.js → code-WlFHE7z_.js} +1 -1
  63. package/src/ui/dist/assets/{file-content-Dv4LoZec.js → file-content-BZMz3RYp.js} +1 -1
  64. package/src/ui/dist/assets/{file-diff-panel-Denq-lC3.js → file-diff-panel-CQhw0jS2.js} +1 -1
  65. package/src/ui/dist/assets/{file-socket-Cu4Qln7Y.js → file-socket-CfQPKQKj.js} +1 -1
  66. package/src/ui/dist/assets/{git-commit-horizontal-BUh6G52n.js → git-commit-horizontal-DxZ8DCZh.js} +1 -1
  67. package/src/ui/dist/assets/{image-B9HUUddG.js → image-Bgl4VIyx.js} +1 -1
  68. package/src/ui/dist/assets/{index-Cgla8biy.css → index-BpV6lusQ.css} +1 -1
  69. package/src/ui/dist/assets/{index-Gbl53BNp.js → index-CBNVuWcP.js} +363 -363
  70. package/src/ui/dist/assets/{index-wQ7RIIRd.js → index-CwNu1aH4.js} +1 -1
  71. package/src/ui/dist/assets/{index-B2B1sg-M.js → index-DrUnlf6K.js} +1 -1
  72. package/src/ui/dist/assets/{index-DRyx7vAc.js → index-NW-h8VzN.js} +1 -1
  73. package/src/ui/dist/assets/{pdf-effect-queue-ZtnHFCAi.js → pdf-effect-queue-J8OnM0jE.js} +1 -1
  74. package/src/ui/dist/assets/{popover-DL6h35vr.js → popover-CLc0pPP8.js} +1 -1
  75. package/src/ui/dist/assets/{project-sync-CsX08Qno.js → project-sync-C9IdzdZW.js} +1 -1
  76. package/src/ui/dist/assets/{select-DvmXt1yY.js → select-Cs2PmzwL.js} +1 -1
  77. package/src/ui/dist/assets/{sigma-7jpXazui.js → sigma-ClKcHAXm.js} +1 -1
  78. package/src/ui/dist/assets/{trash-xA7kFt8i.js → trash-DwpbFr3w.js} +1 -1
  79. package/src/ui/dist/assets/{useCliAccess-DsMwDjOp.js → useCliAccess-NQ8m0Let.js} +1 -1
  80. package/src/ui/dist/assets/{wrap-text-CwMn-iqb.js → wrap-text-BC-Hltpd.js} +1 -1
  81. package/src/ui/dist/assets/{zoom-out-R-GWEhzS.js → zoom-out-E_gaeAxL.js} +1 -1
  82. package/src/ui/dist/index.html +2 -2
@@ -523,7 +523,10 @@ npm --prefix src/ui run build</pre>
523
523
  }
524
524
 
525
525
  def quest(self, quest_id: str) -> dict:
526
- return self.app.quest_service.snapshot(quest_id)
526
+ try:
527
+ return self.app.quest_service.snapshot(quest_id)
528
+ except FileNotFoundError:
529
+ return 404, {"ok": False, "message": f"Unknown quest `{quest_id}`."}
527
530
 
528
531
  def quest_delete(self, quest_id: str, body: dict | None = None) -> dict | tuple[int, dict]:
529
532
  source = "web"
@@ -536,6 +539,7 @@ npm --prefix src/ui run build</pre>
536
539
  "title": body.get("title") if "title" in body else None,
537
540
  "active_anchor": body.get("active_anchor") if "active_anchor" in body else None,
538
541
  "default_runner": body.get("default_runner") if "default_runner" in body else None,
542
+ "workspace_mode": body.get("workspace_mode") if "workspace_mode" in body else None,
539
543
  }
540
544
  if all(value is None for value in updates.values()):
541
545
  return {
@@ -585,7 +589,10 @@ npm --prefix src/ui run build</pre>
585
589
  }
586
590
 
587
591
  def quest_session(self, quest_id: str) -> dict:
588
- snapshot = self.app.quest_service.snapshot_fast(quest_id)
592
+ try:
593
+ snapshot = self.app.quest_service.snapshot_fast(quest_id)
594
+ except FileNotFoundError:
595
+ return 404, {"ok": False, "message": f"Unknown quest `{quest_id}`."}
589
596
  for kind in ("details", "canvas", "git_canvas"):
590
597
  try:
591
598
  self.app.quest_service.prime_projection(quest_id, kind)
@@ -1474,19 +1481,25 @@ npm --prefix src/ui run build</pre>
1474
1481
  )
1475
1482
 
1476
1483
  def documents(self, quest_id: str) -> list[dict]:
1477
- return self.app.quest_service.list_documents(quest_id)
1484
+ try:
1485
+ return self.app.quest_service.list_documents(quest_id)
1486
+ except FileNotFoundError:
1487
+ return 404, {"ok": False, "message": f"Unknown quest `{quest_id}`."}
1478
1488
 
1479
1489
  def explorer(self, quest_id: str, path: str) -> dict:
1480
1490
  query = self.parse_query(path)
1481
1491
  revision = ((query.get("revision") or [""])[0] or "").strip() or None
1482
1492
  mode = ((query.get("mode") or [""])[0] or "").strip() or None
1483
1493
  profile = ((query.get("profile") or [""])[0] or "").strip() or None
1484
- return self.app.quest_service.explorer(
1485
- quest_id,
1486
- revision=revision,
1487
- mode=mode,
1488
- profile=profile,
1489
- )
1494
+ try:
1495
+ return self.app.quest_service.explorer(
1496
+ quest_id,
1497
+ revision=revision,
1498
+ mode=mode,
1499
+ profile=profile,
1500
+ )
1501
+ except FileNotFoundError:
1502
+ return 404, {"ok": False, "message": f"Unknown quest `{quest_id}`."}
1490
1503
 
1491
1504
  def quest_search(self, quest_id: str, path: str) -> dict:
1492
1505
  query = self.parse_query(path)
@@ -1495,7 +1508,90 @@ npm --prefix src/ui run build</pre>
1495
1508
  limit = int(((query.get("limit") or ["50"])[0] or "50").strip())
1496
1509
  except ValueError:
1497
1510
  limit = 50
1498
- return self.app.quest_service.search_files(quest_id, term=term, limit=limit)
1511
+ try:
1512
+ return self.app.quest_service.search_files(quest_id, term=term, limit=limit)
1513
+ except FileNotFoundError:
1514
+ return 404, {"ok": False, "message": f"Unknown quest `{quest_id}`."}
1515
+
1516
+ def quest_file_create_folder(self, quest_id: str, body: dict) -> dict | tuple[int, dict]:
1517
+ try:
1518
+ return self._fresh_quest_service().create_workspace_folder(
1519
+ quest_id,
1520
+ name=body.get("name"),
1521
+ parent_path=body.get("parent_path"),
1522
+ )
1523
+ except FileNotFoundError as exc:
1524
+ return 404, {"ok": False, "message": str(exc)}
1525
+ except FileExistsError as exc:
1526
+ return 409, {"ok": False, "message": str(exc)}
1527
+ except ValueError as exc:
1528
+ return 400, {"ok": False, "message": str(exc)}
1529
+
1530
+ def quest_file_upload(self, quest_id: str, body: dict) -> dict | tuple[int, dict]:
1531
+ file_name = str(body.get("file_name") or "").strip()
1532
+ content_base64 = str(body.get("content_base64") or "").strip()
1533
+ mime_type = str(body.get("mime_type") or "").strip() or None
1534
+ if not file_name:
1535
+ return 400, {"ok": False, "message": "`file_name` is required."}
1536
+ if not content_base64:
1537
+ return 400, {"ok": False, "message": "`content_base64` is required."}
1538
+ try:
1539
+ content = base64.b64decode(content_base64, validate=True)
1540
+ except (ValueError, TypeError):
1541
+ return 400, {"ok": False, "message": "Invalid `content_base64` payload."}
1542
+ try:
1543
+ return self._fresh_quest_service().upload_workspace_file(
1544
+ quest_id,
1545
+ file_name=file_name,
1546
+ content=content,
1547
+ mime_type=mime_type,
1548
+ parent_path=body.get("parent_path"),
1549
+ )
1550
+ except FileNotFoundError as exc:
1551
+ return 404, {"ok": False, "message": str(exc)}
1552
+ except FileExistsError as exc:
1553
+ return 409, {"ok": False, "message": str(exc)}
1554
+ except ValueError as exc:
1555
+ return 400, {"ok": False, "message": str(exc)}
1556
+
1557
+ def quest_file_rename(self, quest_id: str, body: dict) -> dict | tuple[int, dict]:
1558
+ try:
1559
+ return self._fresh_quest_service().rename_workspace_entry(
1560
+ quest_id,
1561
+ path=body.get("path"),
1562
+ new_name=body.get("new_name"),
1563
+ )
1564
+ except FileNotFoundError as exc:
1565
+ return 404, {"ok": False, "message": str(exc)}
1566
+ except FileExistsError as exc:
1567
+ return 409, {"ok": False, "message": str(exc)}
1568
+ except ValueError as exc:
1569
+ return 400, {"ok": False, "message": str(exc)}
1570
+
1571
+ def quest_file_move(self, quest_id: str, body: dict) -> dict | tuple[int, dict]:
1572
+ try:
1573
+ return self._fresh_quest_service().move_workspace_entries(
1574
+ quest_id,
1575
+ paths=body.get("paths"),
1576
+ target_parent_path=body.get("target_parent_path"),
1577
+ )
1578
+ except FileNotFoundError as exc:
1579
+ return 404, {"ok": False, "message": str(exc)}
1580
+ except FileExistsError as exc:
1581
+ return 409, {"ok": False, "message": str(exc)}
1582
+ except ValueError as exc:
1583
+ return 400, {"ok": False, "message": str(exc)}
1584
+
1585
+ def quest_file_delete(self, quest_id: str, body: dict) -> dict | tuple[int, dict]:
1586
+ try:
1587
+ return self._fresh_quest_service().delete_workspace_entries(
1588
+ quest_id,
1589
+ paths=body.get("paths"),
1590
+ )
1591
+ except FileNotFoundError as exc:
1592
+ return 404, {"ok": False, "message": str(exc)}
1593
+ except ValueError as exc:
1594
+ return 400, {"ok": False, "message": str(exc)}
1499
1595
 
1500
1596
  def document_asset(self, quest_id: str, path: str) -> tuple[int, dict, bytes]:
1501
1597
  quest_service = self._fresh_quest_service()
@@ -1519,7 +1615,10 @@ npm --prefix src/ui run build</pre>
1519
1615
  return 200, self._asset_headers(mime_type), path.read_bytes()
1520
1616
 
1521
1617
  def document_open(self, quest_id: str, body: dict) -> dict:
1522
- return self._fresh_quest_service().open_document(quest_id, body["document_id"])
1618
+ try:
1619
+ return self._fresh_quest_service().open_document(quest_id, body["document_id"])
1620
+ except FileNotFoundError:
1621
+ return 404, {"ok": False, "message": f"Unknown quest `{quest_id}`."}
1523
1622
 
1524
1623
  def document_asset_upload(self, quest_id: str, body: dict) -> dict:
1525
1624
  document_id = str(body.get("document_id") or "").strip()
@@ -1537,22 +1636,28 @@ npm --prefix src/ui run build</pre>
1537
1636
  content = base64.b64decode(content_base64, validate=True)
1538
1637
  except (ValueError, TypeError):
1539
1638
  return {"ok": False, "message": "Invalid `content_base64` payload."}
1540
- return self.app.quest_service.save_document_asset(
1541
- quest_id,
1542
- document_id,
1543
- file_name=file_name,
1544
- mime_type=mime_type or None,
1545
- content=content,
1546
- kind=kind,
1547
- )
1639
+ try:
1640
+ return self.app.quest_service.save_document_asset(
1641
+ quest_id,
1642
+ document_id,
1643
+ file_name=file_name,
1644
+ mime_type=mime_type or None,
1645
+ content=content,
1646
+ kind=kind,
1647
+ )
1648
+ except FileNotFoundError:
1649
+ return 404, {"ok": False, "message": f"Unknown quest `{quest_id}`."}
1548
1650
 
1549
1651
  def document_save(self, quest_id: str, document_id: str, body: dict) -> dict:
1550
- return self.app.quest_service.save_document(
1551
- quest_id,
1552
- document_id,
1553
- body["content"],
1554
- previous_revision=body.get("revision"),
1555
- )
1652
+ try:
1653
+ return self.app.quest_service.save_document(
1654
+ quest_id,
1655
+ document_id,
1656
+ body["content"],
1657
+ previous_revision=body.get("revision"),
1658
+ )
1659
+ except FileNotFoundError:
1660
+ return 404, {"ok": False, "message": f"Unknown quest `{quest_id}`."}
1556
1661
 
1557
1662
  def latex_init(self, project_id: str, body: dict) -> dict:
1558
1663
  return self.app.latex_service.init_project(
@@ -78,6 +78,11 @@ ROUTES: list[tuple[str, re.Pattern[str], str]] = [
78
78
  ("GET", re.compile(r"^/api/quests/(?P<quest_id>[^/]+)/documents$"), "documents"),
79
79
  ("GET", re.compile(r"^/api/quests/(?P<quest_id>[^/]+)/explorer$"), "explorer"),
80
80
  ("GET", re.compile(r"^/api/quests/(?P<quest_id>[^/]+)/search$"), "quest_search"),
81
+ ("POST", re.compile(r"^/api/quests/(?P<quest_id>[^/]+)/files/folder$"), "quest_file_create_folder"),
82
+ ("POST", re.compile(r"^/api/quests/(?P<quest_id>[^/]+)/files/upload$"), "quest_file_upload"),
83
+ ("POST", re.compile(r"^/api/quests/(?P<quest_id>[^/]+)/files/rename$"), "quest_file_rename"),
84
+ ("POST", re.compile(r"^/api/quests/(?P<quest_id>[^/]+)/files/move$"), "quest_file_move"),
85
+ ("POST", re.compile(r"^/api/quests/(?P<quest_id>[^/]+)/files/delete$"), "quest_file_delete"),
81
86
  ("GET", re.compile(r"^/api/quests/(?P<quest_id>[^/]+)/documents/asset$"), "document_asset"),
82
87
  ("POST", re.compile(r"^/api/quests/(?P<quest_id>[^/]+)/documents/open$"), "document_open"),
83
88
  ("POST", re.compile(r"^/api/quests/(?P<quest_id>[^/]+)/documents/assets$"), "document_asset_upload"),