@researai/deepscientist 1.5.13 → 1.5.15

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 (142) hide show
  1. package/README.md +8 -0
  2. package/assets/branding/logo-raster.png +0 -0
  3. package/bin/ds.js +134 -49
  4. package/docs/en/00_QUICK_START.md +2 -2
  5. package/docs/en/01_SETTINGS_REFERENCE.md +20 -4
  6. package/docs/en/03_QQ_CONNECTOR_GUIDE.md +19 -0
  7. package/docs/en/05_TUI_GUIDE.md +466 -96
  8. package/docs/en/10_WEIXIN_CONNECTOR_GUIDE.md +20 -0
  9. package/docs/en/14_PROMPT_SKILLS_AND_MCP_GUIDE.md +2 -0
  10. package/docs/en/16_TELEGRAM_CONNECTOR_GUIDE.md +134 -0
  11. package/docs/en/17_WHATSAPP_CONNECTOR_GUIDE.md +126 -0
  12. package/docs/en/18_FEISHU_CONNECTOR_GUIDE.md +136 -0
  13. package/docs/en/README.md +8 -0
  14. package/docs/zh/00_QUICK_START.md +2 -2
  15. package/docs/zh/01_SETTINGS_REFERENCE.md +20 -4
  16. package/docs/zh/03_QQ_CONNECTOR_GUIDE.md +19 -0
  17. package/docs/zh/05_TUI_GUIDE.md +465 -82
  18. package/docs/zh/10_WEIXIN_CONNECTOR_GUIDE.md +20 -0
  19. package/docs/zh/14_PROMPT_SKILLS_AND_MCP_GUIDE.md +2 -0
  20. package/docs/zh/16_TELEGRAM_CONNECTOR_GUIDE.md +134 -0
  21. package/docs/zh/17_WHATSAPP_CONNECTOR_GUIDE.md +126 -0
  22. package/docs/zh/18_FEISHU_CONNECTOR_GUIDE.md +136 -0
  23. package/docs/zh/README.md +8 -0
  24. package/install.sh +2 -0
  25. package/package.json +1 -1
  26. package/pyproject.toml +1 -1
  27. package/src/deepscientist/__init__.py +1 -1
  28. package/src/deepscientist/artifact/charts.py +567 -0
  29. package/src/deepscientist/artifact/guidance.py +50 -10
  30. package/src/deepscientist/artifact/metrics.py +228 -5
  31. package/src/deepscientist/artifact/schemas.py +3 -0
  32. package/src/deepscientist/artifact/service.py +4004 -538
  33. package/src/deepscientist/bash_exec/models.py +23 -0
  34. package/src/deepscientist/bash_exec/monitor.py +147 -67
  35. package/src/deepscientist/bash_exec/runtime.py +218 -156
  36. package/src/deepscientist/bash_exec/service.py +79 -64
  37. package/src/deepscientist/bash_exec/shells.py +87 -0
  38. package/src/deepscientist/bridges/connectors.py +51 -2
  39. package/src/deepscientist/config/models.py +6 -3
  40. package/src/deepscientist/config/service.py +7 -2
  41. package/src/deepscientist/connector/lingzhu_support.py +23 -4
  42. package/src/deepscientist/connector/weixin_support.py +122 -1
  43. package/src/deepscientist/daemon/api/handlers.py +75 -4
  44. package/src/deepscientist/daemon/api/router.py +1 -0
  45. package/src/deepscientist/daemon/app.py +869 -236
  46. package/src/deepscientist/doctor.py +51 -0
  47. package/src/deepscientist/file_lock.py +48 -0
  48. package/src/deepscientist/gitops/diff.py +167 -1
  49. package/src/deepscientist/mcp/server.py +331 -21
  50. package/src/deepscientist/process_control.py +161 -0
  51. package/src/deepscientist/prompts/builder.py +275 -491
  52. package/src/deepscientist/quest/service.py +2336 -145
  53. package/src/deepscientist/quest/stage_views.py +305 -29
  54. package/src/deepscientist/runners/base.py +2 -0
  55. package/src/deepscientist/runners/codex.py +88 -5
  56. package/src/deepscientist/runners/runtime_overrides.py +17 -1
  57. package/src/deepscientist/shared.py +6 -1
  58. package/src/prompts/contracts/shared_interaction.md +13 -4
  59. package/src/prompts/system.md +984 -1985
  60. package/src/skills/analysis-campaign/SKILL.md +31 -2
  61. package/src/skills/analysis-campaign/references/artifact-orchestration.md +1 -1
  62. package/src/skills/analysis-campaign/references/writing-facing-slice-examples.md +65 -0
  63. package/src/skills/baseline/SKILL.md +267 -994
  64. package/src/skills/baseline/references/baseline-checklist-template.md +21 -32
  65. package/src/skills/baseline/references/baseline-plan-template.md +41 -57
  66. package/src/skills/decision/SKILL.md +19 -2
  67. package/src/skills/experiment/SKILL.md +8 -2
  68. package/src/skills/finalize/SKILL.md +18 -0
  69. package/src/skills/idea/SKILL.md +78 -0
  70. package/src/skills/idea/references/idea-generation-playbook.md +100 -0
  71. package/src/skills/idea/references/outline-seeding-example.md +60 -0
  72. package/src/skills/intake-audit/SKILL.md +1 -1
  73. package/src/skills/optimize/SKILL.md +1644 -0
  74. package/src/skills/rebuttal/SKILL.md +2 -1
  75. package/src/skills/review/SKILL.md +2 -1
  76. package/src/skills/write/SKILL.md +80 -12
  77. package/src/skills/write/references/outline-evidence-contract-example.md +107 -0
  78. package/src/tui/dist/app/AppContainer.js +1445 -52
  79. package/src/tui/dist/components/Composer.js +1 -1
  80. package/src/tui/dist/components/ConfigScreen.js +190 -36
  81. package/src/tui/dist/components/GradientStatusText.js +1 -20
  82. package/src/tui/dist/components/InputPrompt.js +41 -32
  83. package/src/tui/dist/components/LoadingIndicator.js +1 -1
  84. package/src/tui/dist/components/Logo.js +61 -38
  85. package/src/tui/dist/components/MainContent.js +10 -3
  86. package/src/tui/dist/components/WelcomePanel.js +4 -12
  87. package/src/tui/dist/components/messages/AssistantMessage.js +1 -1
  88. package/src/tui/dist/components/messages/BashExecOperationMessage.js +3 -3
  89. package/src/tui/dist/components/messages/OperationMessage.js +1 -1
  90. package/src/tui/dist/index.js +28 -1
  91. package/src/tui/dist/layouts/DefaultAppLayout.js +3 -3
  92. package/src/tui/dist/lib/api.js +17 -0
  93. package/src/tui/dist/lib/connectors.js +261 -0
  94. package/src/tui/dist/semantic-colors.js +29 -19
  95. package/src/tui/package.json +1 -1
  96. package/src/ui/dist/assets/{AiManusChatView-CnJcXynW.js → AiManusChatView-DDjbFnbt.js} +12 -12
  97. package/src/ui/dist/assets/{AnalysisPlugin-DeyzPEhV.js → AnalysisPlugin-Yb5IdmaU.js} +1 -1
  98. package/src/ui/dist/assets/CliPlugin-e64sreyu.js +31037 -0
  99. package/src/ui/dist/assets/{CodeEditorPlugin-B-xicq1e.js → CodeEditorPlugin-C4D2TIkU.js} +8 -8
  100. package/src/ui/dist/assets/{CodeViewerPlugin-DT54ysXa.js → CodeViewerPlugin-BVoNZIvC.js} +5 -5
  101. package/src/ui/dist/assets/{DocViewerPlugin-DQtKT-VD.js → DocViewerPlugin-CLChbllo.js} +3 -3
  102. package/src/ui/dist/assets/{GitDiffViewerPlugin-hqHbCfnv.js → GitDiffViewerPlugin-C4xeFyFQ.js} +20 -20
  103. package/src/ui/dist/assets/{ImageViewerPlugin-OcVo33jV.js → ImageViewerPlugin-OiMUAcLi.js} +5 -5
  104. package/src/ui/dist/assets/{LabCopilotPanel-DdGwhEUV.js → LabCopilotPanel-BjD2ThQF.js} +11 -11
  105. package/src/ui/dist/assets/{LabPlugin-Ciz1gDaX.js → LabPlugin-DQPg-NrB.js} +2 -2
  106. package/src/ui/dist/assets/{LatexPlugin-BhmjNQRC.js → LatexPlugin-CI05XAV9.js} +7 -7
  107. package/src/ui/dist/assets/{MarkdownViewerPlugin-BzdVH9Bx.js → MarkdownViewerPlugin-DpeBLYZf.js} +4 -4
  108. package/src/ui/dist/assets/{MarketplacePlugin-DmyHspXt.js → MarketplacePlugin-DolE58Q2.js} +3 -3
  109. package/src/ui/dist/assets/{NotebookEditor-BTVYRGkm.js → NotebookEditor-7Qm2rSWD.js} +11 -11
  110. package/src/ui/dist/assets/{NotebookEditor-BMXKrDRk.js → NotebookEditor-C1kWaxKi.js} +1 -1
  111. package/src/ui/dist/assets/{PdfLoader-CvcjJHXv.js → PdfLoader-BfOHw8Zw.js} +1 -1
  112. package/src/ui/dist/assets/{PdfMarkdownPlugin-DW2ej8Vk.js → PdfMarkdownPlugin-BulDREv1.js} +2 -2
  113. package/src/ui/dist/assets/{PdfViewerPlugin-CmlDxbhU.js → PdfViewerPlugin-C-daaOaL.js} +10 -10
  114. package/src/ui/dist/assets/{SearchPlugin-DAjQZPSv.js → SearchPlugin-CjpaiJ3A.js} +1 -1
  115. package/src/ui/dist/assets/{TextViewerPlugin-C-nVAZb_.js → TextViewerPlugin-BxIyqPQC.js} +5 -5
  116. package/src/ui/dist/assets/{VNCViewer-D7-dIYon.js → VNCViewer-HAg9mF7M.js} +10 -10
  117. package/src/ui/dist/assets/{bot-C_G4WtNI.js → bot-0DYntytV.js} +1 -1
  118. package/src/ui/dist/assets/{code-Cd7WfiWq.js → code-B20Slj_w.js} +1 -1
  119. package/src/ui/dist/assets/{file-content-B57zsL9y.js → file-content-DT24KFma.js} +1 -1
  120. package/src/ui/dist/assets/{file-diff-panel-DVoheLFq.js → file-diff-panel-DK13YPql.js} +1 -1
  121. package/src/ui/dist/assets/{file-socket-B5kXFxZP.js → file-socket-B4T2o4nR.js} +1 -1
  122. package/src/ui/dist/assets/{image-LLOjkMHF.js → image-DSeR_sDS.js} +1 -1
  123. package/src/ui/dist/assets/{index-hOUOWbW2.js → index-BrFje2Uk.js} +2 -2
  124. package/src/ui/dist/assets/{index-Dxa2eYMY.js → index-BwRJaoTl.js} +1 -1
  125. package/src/ui/dist/assets/{index-CLQauncb.js → index-D_E4281X.js} +5418 -28620
  126. package/src/ui/dist/assets/{index-C3r2iGrp.js → index-DnYB3xb1.js} +12 -12
  127. package/src/ui/dist/assets/{index-BQG-1s2o.css → index-G7AcWcMu.css} +43 -2
  128. package/src/ui/dist/assets/{monaco-BGGAEii3.js → monaco-LExaAN3Y.js} +1 -1
  129. package/src/ui/dist/assets/{pdf-effect-queue-DlEr1_y5.js → pdf-effect-queue-BJk5okWJ.js} +1 -1
  130. package/src/ui/dist/assets/{popover-CWJbJuYY.js → popover-D3Gg_FoV.js} +1 -1
  131. package/src/ui/dist/assets/{project-sync-CRJiucYO.js → project-sync-C_ygLlVU.js} +1 -1
  132. package/src/ui/dist/assets/{select-CoHB7pvH.js → select-CpAK6uWm.js} +2 -2
  133. package/src/ui/dist/assets/{sigma-D5aJWR8J.js → sigma-DEccaSgk.js} +1 -1
  134. package/src/ui/dist/assets/{square-check-big-DUK_mnkS.js → square-check-big-uUfyVsbD.js} +1 -1
  135. package/src/ui/dist/assets/{trash-ChU3SEE3.js → trash-CXvwwSe8.js} +1 -1
  136. package/src/ui/dist/assets/{useCliAccess-BrJBV3tY.js → useCliAccess-Bnop4mgR.js} +1 -1
  137. package/src/ui/dist/assets/{useFileDiffOverlay-C2OQaVWc.js → useFileDiffOverlay-B8eUAX0I.js} +1 -1
  138. package/src/ui/dist/assets/{wrap-text-C7Qqh-om.js → wrap-text-9vbOBpkW.js} +1 -1
  139. package/src/ui/dist/assets/{zoom-out-rtX0FKya.js → zoom-out-BgVMmOW4.js} +1 -1
  140. package/src/ui/dist/index.html +2 -2
  141. package/uv.lock +1 -1
  142. package/src/ui/dist/assets/CliPlugin-CB1YODQn.js +0 -5905
package/README.md CHANGED
@@ -26,6 +26,9 @@
26
26
  <p align="center">
27
27
  <a href="docs/en/00_QUICK_START.md">Quick Start</a> •
28
28
  <a href="docs/en/02_START_RESEARCH_GUIDE.md">Start Research Guide</a> •
29
+ <a href="docs/en/16_TELEGRAM_CONNECTOR_GUIDE.md">Telegram</a> •
30
+ <a href="docs/en/17_WHATSAPP_CONNECTOR_GUIDE.md">WhatsApp</a> •
31
+ <a href="docs/en/18_FEISHU_CONNECTOR_GUIDE.md">Feishu</a> •
29
32
  <a href="docs/en/10_WEIXIN_CONNECTOR_GUIDE.md"><img src="assets/branding/connector-weixin.png" alt="Weixin" width="14" height="14" /> Weixin</a> •
30
33
  <a href="docs/en/03_QQ_CONNECTOR_GUIDE.md"><img src="assets/branding/connector-qq.png" alt="QQ" width="14" height="14" /> QQ</a> •
31
34
  <a href="docs/en/04_LINGZHU_CONNECTOR_GUIDE.md"><img src="assets/branding/connector-rokid.png" alt="Rokid" width="14" height="14" /> Rokid</a> •
@@ -95,6 +98,8 @@ ds --yolo --here
95
98
 
96
99
  If `codex --login` is unavailable, run `codex` once and finish authentication there. After startup, open `http://127.0.0.1:20999`.
97
100
 
101
+ Linux and macOS remain the most battle-tested platforms. Native Windows support is now experimental; if you need the closest Linux-like terminal behavior, prefer WSL2.
102
+
98
103
  For detailed install, troubleshooting, PDF compile, and other launch modes, use:
99
104
 
100
105
  - [Quick Start](docs/en/00_QUICK_START.md)
@@ -108,6 +113,9 @@ For detailed install, troubleshooting, PDF compile, and other launch modes, use:
108
113
  - [Core Architecture Guide (English)](docs/en/13_CORE_ARCHITECTURE_GUIDE.md)
109
114
  - [Prompt, Skills, and MCP Guide (English)](docs/en/14_PROMPT_SKILLS_AND_MCP_GUIDE.md)
110
115
  - [Weixin Connector Guide (English)](docs/en/10_WEIXIN_CONNECTOR_GUIDE.md)
116
+ - [Telegram Connector Guide (English)](docs/en/16_TELEGRAM_CONNECTOR_GUIDE.md)
117
+ - [WhatsApp Connector Guide (English)](docs/en/17_WHATSAPP_CONNECTOR_GUIDE.md)
118
+ - [Feishu Connector Guide (English)](docs/en/18_FEISHU_CONNECTOR_GUIDE.md)
111
119
  - [QQ Connector Guide (English)](docs/en/03_QQ_CONNECTOR_GUIDE.md)
112
120
  - [Lingzhu / Rokid Guide (English)](docs/en/04_LINGZHU_CONNECTOR_GUIDE.md)
113
121
  - [Memory and MCP Guide (English)](docs/en/07_MEMORY_AND_MCP.md)
Binary file
package/bin/ds.js CHANGED
@@ -38,13 +38,14 @@ const UPDATE_CHECK_TTL_MS = 12 * 60 * 60 * 1000;
38
38
 
39
39
  const optionsWithValues = new Set(['--home', '--host', '--port', '--quest-id', '--mode', '--proxy', '--codex-profile', '--codex']);
40
40
 
41
- function buildCodexOverrideEnv({ yolo = false, profile = null, binary = null } = {}) {
41
+ function buildCodexOverrideEnv({ yolo = true, profile = null, binary = null } = {}) {
42
42
  const normalizedProfile = typeof profile === 'string' ? profile.trim() : '';
43
43
  const normalizedBinary = typeof binary === 'string' ? binary.trim() : '';
44
44
  const overrides = {};
45
45
  if (normalizedBinary) {
46
46
  overrides.DEEPSCIENTIST_CODEX_BINARY = normalizedBinary;
47
47
  }
48
+ overrides.DEEPSCIENTIST_CODEX_YOLO = yolo ? '1' : '0';
48
49
  if (!yolo) {
49
50
  if (normalizedProfile) {
50
51
  overrides.DEEPSCIENTIST_CODEX_PROFILE = normalizedProfile;
@@ -52,7 +53,6 @@ function buildCodexOverrideEnv({ yolo = false, profile = null, binary = null } =
52
53
  }
53
54
  return overrides;
54
55
  }
55
- overrides.DEEPSCIENTIST_CODEX_YOLO = '1';
56
56
  if (normalizedProfile) {
57
57
  overrides.DEEPSCIENTIST_CODEX_PROFILE = normalizedProfile;
58
58
  overrides.DEEPSCIENTIST_CODEX_MODEL = 'inherit';
@@ -69,6 +69,41 @@ function readOptionValue(argv, optionName) {
69
69
  return null;
70
70
  }
71
71
 
72
+ function parseBooleanFlagValue(rawValue) {
73
+ const normalized = String(rawValue || '').trim().toLowerCase();
74
+ if (!normalized) return null;
75
+ if (['1', 'true', 'yes', 'on', 'y'].includes(normalized)) return true;
76
+ if (['0', 'false', 'no', 'off', 'n'].includes(normalized)) return false;
77
+ return null;
78
+ }
79
+
80
+ function parseYoloArg(args, index, currentValue = true) {
81
+ const arg = args[index];
82
+ if (arg === '--yolo') {
83
+ const parsed = parseBooleanFlagValue(args[index + 1]);
84
+ if (parsed === null) {
85
+ return { matched: true, value: true, consumed: 1 };
86
+ }
87
+ return { matched: true, value: parsed, consumed: 2 };
88
+ }
89
+ if (typeof arg === 'string' && arg.startsWith('--yolo=')) {
90
+ const parsed = parseBooleanFlagValue(arg.slice('--yolo='.length));
91
+ return { matched: true, value: parsed === null ? true : parsed, consumed: 1 };
92
+ }
93
+ return { matched: false, value: currentValue, consumed: 0 };
94
+ }
95
+
96
+ function resolveYoloFlag(args, defaultValue = true) {
97
+ let value = defaultValue;
98
+ for (let index = 0; index < args.length; index += 1) {
99
+ const parsed = parseYoloArg(args, index, value);
100
+ if (!parsed.matched) continue;
101
+ value = parsed.value;
102
+ index += Math.max(0, parsed.consumed - 1);
103
+ }
104
+ return value;
105
+ }
106
+
72
107
  function printLauncherHelp() {
73
108
  console.log(`DeepScientist launcher
74
109
 
@@ -105,7 +140,7 @@ Launcher flags:
105
140
  --home <path> Use a custom DeepScientist home
106
141
  --here Create/use ./DeepScientist under the current working directory as home
107
142
  --proxy <url> Use an outbound HTTP/WS proxy for npm and Python runtime traffic
108
- --yolo Run Codex in YOLO mode: approval_policy=never and sandbox_mode=danger-full-access
143
+ --yolo [true|false] Control Codex YOLO mode. Default is true; pass false to restore on-request + workspace-write
109
144
  --codex-profile <id> Run DeepScientist with a specific Codex profile, for example \`m27\`
110
145
  --codex <path> Run DeepScientist with a specific Codex executable path for this launch
111
146
  --quest-id <id> Open the TUI on one quest directly
@@ -314,11 +349,11 @@ function fetchLatestPublishedVersion({ npmBinary, timeoutMs = 3500 }) {
314
349
  latestVersion: null,
315
350
  };
316
351
  }
317
- const result = spawnSync(npmBinary, ['view', UPDATE_PACKAGE_NAME, 'version', '--json'], {
352
+ const result = spawnSync(npmBinary, ['view', UPDATE_PACKAGE_NAME, 'version', '--json'], syncSpawnOptions({
318
353
  encoding: 'utf8',
319
354
  env: process.env,
320
355
  timeout: timeoutMs,
321
- });
356
+ }));
322
357
  if (result.error) {
323
358
  return {
324
359
  ok: false,
@@ -606,7 +641,7 @@ function renderBrandArtwork() {
606
641
  const result = spawnSync(
607
642
  chafa,
608
643
  ['--size', `${width}x${height}`, '--format', 'symbols', '--colors', '16', brandPath],
609
- { encoding: 'utf8' }
644
+ syncSpawnOptions({ encoding: 'utf8' })
610
645
  );
611
646
  if (result.status === 0 && result.stdout && result.stdout.trim()) {
612
647
  return result.stdout.replace(/\s+$/, '').split(/\r?\n/);
@@ -984,7 +1019,7 @@ function parseLauncherArgs(argv) {
984
1019
  let status = false;
985
1020
  let daemonOnly = false;
986
1021
  let skipUpdateCheck = false;
987
- let yolo = false;
1022
+ let yolo = true;
988
1023
  let codexProfile = null;
989
1024
  let codexBinary = null;
990
1025
 
@@ -1005,17 +1040,22 @@ function parseLauncherArgs(argv) {
1005
1040
  else if (arg === '--open-browser') openBrowser = true;
1006
1041
  else if (arg === '--daemon-only') daemonOnly = true;
1007
1042
  else if (arg === '--skip-update-check') skipUpdateCheck = true;
1008
- else if (arg === '--yolo') yolo = true;
1009
- else if (arg === '--codex-profile' && args[index + 1]) codexProfile = args[++index];
1010
- else if (arg === '--codex' && args[index + 1]) codexBinary = args[++index];
1011
- else if (arg === '--host' && args[index + 1]) host = args[++index];
1012
- else if (arg === '--port' && args[index + 1]) port = Number(args[++index]);
1013
- else if (arg === '--home' && args[index + 1]) home = path.resolve(args[++index]);
1014
- else if (arg === '--proxy' && args[index + 1]) proxy = args[++index];
1015
- else if (arg === '--quest-id' && args[index + 1]) questId = args[++index];
1016
- else if (arg === '--mode' && args[index + 1]) mode = normalizeMode(args[++index]);
1017
- else if (arg === '--help' || arg === '-h') return { help: true };
1018
- else if (!arg.startsWith('--')) return null;
1043
+ else {
1044
+ const parsedYolo = parseYoloArg(args, index, yolo);
1045
+ if (parsedYolo.matched) {
1046
+ yolo = parsedYolo.value;
1047
+ index += Math.max(0, parsedYolo.consumed - 1);
1048
+ } else if (arg === '--codex-profile' && args[index + 1]) codexProfile = args[++index];
1049
+ else if (arg === '--codex' && args[index + 1]) codexBinary = args[++index];
1050
+ else if (arg === '--host' && args[index + 1]) host = args[++index];
1051
+ else if (arg === '--port' && args[index + 1]) port = Number(args[++index]);
1052
+ else if (arg === '--home' && args[index + 1]) home = path.resolve(args[++index]);
1053
+ else if (arg === '--proxy' && args[index + 1]) proxy = args[++index];
1054
+ else if (arg === '--quest-id' && args[index + 1]) questId = args[++index];
1055
+ else if (arg === '--mode' && args[index + 1]) mode = normalizeMode(args[++index]);
1056
+ else if (arg === '--help' || arg === '-h') return { help: true };
1057
+ else if (!arg.startsWith('--')) return null;
1058
+ }
1019
1059
  }
1020
1060
 
1021
1061
  return {
@@ -1172,6 +1212,11 @@ function parseMigrateArgs(argv) {
1172
1212
  function findFirstPositionalArg(args) {
1173
1213
  for (let index = 0; index < args.length; index += 1) {
1174
1214
  const arg = args[index];
1215
+ const parsedYolo = parseYoloArg(args, index);
1216
+ if (parsedYolo.matched) {
1217
+ index += Math.max(0, parsedYolo.consumed - 1);
1218
+ continue;
1219
+ }
1175
1220
  if (optionsWithValues.has(arg)) {
1176
1221
  index += 1;
1177
1222
  continue;
@@ -1465,11 +1510,14 @@ function scheduleDeferredSourceCleanup({ sourceHome, targetHome }) {
1465
1510
  ' }',
1466
1511
  '})();',
1467
1512
  ].join('\n');
1468
- const child = spawn(process.execPath, ['-e', helperScript, String(process.pid), sourceHome, logPath], {
1469
- detached: true,
1470
- stdio: 'ignore',
1471
- env: process.env,
1472
- });
1513
+ const child = spawn(
1514
+ process.execPath,
1515
+ ['-e', helperScript, String(process.pid), sourceHome, logPath],
1516
+ detachedSpawnOptions({
1517
+ stdio: 'ignore',
1518
+ env: process.env,
1519
+ })
1520
+ );
1473
1521
  child.unref();
1474
1522
  }
1475
1523
 
@@ -1554,10 +1602,10 @@ function probePython(binary) {
1554
1602
  ' "patch": sys.version_info[2],',
1555
1603
  '}, ensure_ascii=False))',
1556
1604
  ].join('\n');
1557
- const result = spawnSync(binary, ['-c', snippet], {
1605
+ const result = spawnSync(binary, ['-c', snippet], syncSpawnOptions({
1558
1606
  encoding: 'utf8',
1559
1607
  env: process.env,
1560
- });
1608
+ }));
1561
1609
  if (result.error) {
1562
1610
  return {
1563
1611
  ok: false,
@@ -1830,6 +1878,7 @@ function runSync(binary, args, options = {}) {
1830
1878
  env: options.env || process.env,
1831
1879
  encoding: 'utf8',
1832
1880
  input: options.input,
1881
+ windowsHide: process.platform === 'win32',
1833
1882
  });
1834
1883
  if (result.error) {
1835
1884
  throw result.error;
@@ -1847,12 +1896,30 @@ function step(index, total, message) {
1847
1896
  console.log(`[${index}/${total}] ${message}`);
1848
1897
  }
1849
1898
 
1899
+ function detachedSpawnOptions(options = {}) {
1900
+ return {
1901
+ ...options,
1902
+ detached: true,
1903
+ windowsHide: process.platform === 'win32',
1904
+ };
1905
+ }
1906
+
1907
+ function syncSpawnOptions(options = {}) {
1908
+ return {
1909
+ ...options,
1910
+ windowsHide: process.platform === 'win32',
1911
+ };
1912
+ }
1913
+
1850
1914
  function verifyPythonRuntime(runtimePython) {
1851
1915
  const result = runSync(
1852
1916
  runtimePython,
1853
1917
  ['-c', 'import deepscientist.cli; import cryptography; import _cffi_backend; print("ok")'],
1854
1918
  { capture: true, allowFailure: true }
1855
1919
  );
1920
+ if (result.status !== 0 && result.stderr) {
1921
+ process.stderr.write(result.stderr);
1922
+ }
1856
1923
  return result.status === 0;
1857
1924
  }
1858
1925
 
@@ -1999,11 +2066,11 @@ function downloadFileWithNode(url, destinationPath) {
1999
2066
  ' process.exit(1);',
2000
2067
  '});',
2001
2068
  ].join('\n');
2002
- const result = spawnSync(process.execPath, ['-e', downloader, url, destinationPath, '45000'], {
2069
+ const result = spawnSync(process.execPath, ['-e', downloader, url, destinationPath, '45000'], syncSpawnOptions({
2003
2070
  cwd: repoRoot,
2004
2071
  stdio: 'inherit',
2005
2072
  env: process.env,
2006
- });
2073
+ }));
2007
2074
  if (result.error) {
2008
2075
  throw result.error;
2009
2076
  }
@@ -2054,11 +2121,11 @@ function installLocalUv(home) {
2054
2121
  shellArgs = [installerPath];
2055
2122
  }
2056
2123
 
2057
- const installResult = spawnSync(shellBinary, shellArgs, {
2124
+ const installResult = spawnSync(shellBinary, shellArgs, syncSpawnOptions({
2058
2125
  cwd: repoRoot,
2059
2126
  stdio: 'inherit',
2060
2127
  env: installEnv,
2061
- });
2128
+ }));
2062
2129
  if (installResult.error) {
2063
2130
  throw installResult.error;
2064
2131
  }
@@ -2155,6 +2222,20 @@ function ensureUvManagedPython(home, uvBinary, minimumVersionRequest) {
2155
2222
  return probe;
2156
2223
  }
2157
2224
 
2225
+ function resolveBackgroundPythonExecutable(runtimePython) {
2226
+ const normalized = String(runtimePython || '').trim();
2227
+ if (process.platform !== 'win32' || !normalized) {
2228
+ return normalized;
2229
+ }
2230
+ const runtimePath = path.resolve(normalized);
2231
+ const runtimeDir = path.dirname(runtimePath);
2232
+ const pythonwCandidate = path.join(runtimeDir, 'pythonw.exe');
2233
+ if (fs.existsSync(pythonwCandidate)) {
2234
+ return pythonwCandidate;
2235
+ }
2236
+ return runtimePath;
2237
+ }
2238
+
2158
2239
  function syncUvProjectEnvironment(home, uvBinary, pythonTarget, editable) {
2159
2240
  const args = ['sync', '--frozen', '--no-dev', '--compile-bytecode', '--python', pythonTarget];
2160
2241
  if (!editable) {
@@ -2323,6 +2404,13 @@ function normalizePythonCliArgs(args, home) {
2323
2404
  continue;
2324
2405
  }
2325
2406
  if (arg === '--yolo') {
2407
+ const parsed = parseBooleanFlagValue(args[index + 1]);
2408
+ if (parsed !== null) {
2409
+ index += 1;
2410
+ }
2411
+ continue;
2412
+ }
2413
+ if (typeof arg === 'string' && arg.startsWith('--yolo=')) {
2326
2414
  continue;
2327
2415
  }
2328
2416
  if (arg === '--codex-profile') {
@@ -2598,8 +2686,9 @@ function spawnManagedDaemonProcess({ home, runtimePython, host, port, proxy = nu
2598
2686
  const out = fs.openSync(logPath, 'a');
2599
2687
  const resolvedDaemonId = String(daemonId || crypto.randomUUID()).trim();
2600
2688
  const launcherPath = path.join(repoRoot, 'bin', 'ds.js');
2689
+ const backgroundPython = resolveBackgroundPythonExecutable(runtimePython);
2601
2690
  const child = spawn(
2602
- runtimePython,
2691
+ backgroundPython,
2603
2692
  [
2604
2693
  '-m',
2605
2694
  'deepscientist.cli',
@@ -2612,9 +2701,8 @@ function spawnManagedDaemonProcess({ home, runtimePython, host, port, proxy = nu
2612
2701
  '--port',
2613
2702
  String(port),
2614
2703
  ],
2615
- {
2704
+ detachedSpawnOptions({
2616
2705
  cwd: repoRoot,
2617
- detached: true,
2618
2706
  stdio: ['ignore', out, out],
2619
2707
  env: {
2620
2708
  ...process.env,
@@ -2625,7 +2713,7 @@ function spawnManagedDaemonProcess({ home, runtimePython, host, port, proxy = nu
2625
2713
  DS_DAEMON_ID: resolvedDaemonId,
2626
2714
  DS_DAEMON_MANAGED_BY: 'ds-launcher',
2627
2715
  },
2628
- }
2716
+ })
2629
2717
  );
2630
2718
  child.unref();
2631
2719
  const statePayload = {
@@ -2673,9 +2761,8 @@ function spawnDaemonSupervisor({ home, runtimePython, host, port, proxy = null,
2673
2761
  if (envPayload) {
2674
2762
  args.push('--env-json', envPayload);
2675
2763
  }
2676
- const child = spawn(process.execPath, args, {
2764
+ const child = spawn(process.execPath, args, detachedSpawnOptions({
2677
2765
  cwd: repoRoot,
2678
- detached: true,
2679
2766
  stdio: 'ignore',
2680
2767
  env: {
2681
2768
  ...process.env,
@@ -2683,7 +2770,7 @@ function spawnDaemonSupervisor({ home, runtimePython, host, port, proxy = null,
2683
2770
  DEEPSCIENTIST_NODE_BINARY: process.execPath,
2684
2771
  DEEPSCIENTIST_LAUNCHER_PATH: launcherPath,
2685
2772
  },
2686
- });
2773
+ }));
2687
2774
  child.unref();
2688
2775
  return child.pid || null;
2689
2776
  }
@@ -2902,7 +2989,7 @@ function killManagedProcess(pid, signal) {
2902
2989
  if (signal === 'SIGKILL') {
2903
2990
  taskkillArgs.push('/T', '/F');
2904
2991
  }
2905
- const result = spawnSync('taskkill', taskkillArgs, { stdio: 'ignore' });
2992
+ const result = spawnSync('taskkill', taskkillArgs, syncSpawnOptions({ stdio: 'ignore' }));
2906
2993
  return result.status === 0;
2907
2994
  }
2908
2995
  try {
@@ -3034,11 +3121,11 @@ function summarizeUpdateFailure(result) {
3034
3121
  function runNpmInstallLatest(home, npmBinary) {
3035
3122
  const args = ['install', '-g', `${UPDATE_PACKAGE_NAME}@latest`, '--no-audit', '--no-fund'];
3036
3123
  const startedAt = new Date().toISOString();
3037
- const result = spawnSync(npmBinary, args, {
3124
+ const result = spawnSync(npmBinary, args, syncSpawnOptions({
3038
3125
  encoding: 'utf8',
3039
3126
  env: process.env,
3040
3127
  timeout: 15 * 60 * 1000,
3041
- });
3128
+ }));
3042
3129
  const finishedAt = new Date().toISOString();
3043
3130
  const logPath = writeUpdateLog(
3044
3131
  home,
@@ -3132,12 +3219,11 @@ async function promptYesNo(question, { defaultValue = false } = {}) {
3132
3219
 
3133
3220
  function spawnDetachedNode(args, options = {}) {
3134
3221
  const out = options.logPath ? fs.openSync(options.logPath, 'a') : 'ignore';
3135
- const child = spawn(process.execPath, args, {
3222
+ const child = spawn(process.execPath, args, detachedSpawnOptions({
3136
3223
  cwd: options.cwd || repoRoot,
3137
- detached: true,
3138
3224
  stdio: ['ignore', out, out],
3139
3225
  env: options.env || process.env,
3140
- });
3226
+ }));
3141
3227
  child.unref();
3142
3228
  return child;
3143
3229
  }
@@ -3351,11 +3437,11 @@ function relaunchLauncherAfterUpdate(rawArgs, home) {
3351
3437
  message: 'DeepScientist was updated, but the new launcher path could not be resolved for relaunch.',
3352
3438
  };
3353
3439
  }
3354
- const result = spawnSync(process.execPath, [launcherPath, ...normalizeLauncherRelaunchArgs(rawArgs, home)], {
3440
+ const result = spawnSync(process.execPath, [launcherPath, ...normalizeLauncherRelaunchArgs(rawArgs, home)], syncSpawnOptions({
3355
3441
  cwd: repoRoot,
3356
3442
  stdio: 'inherit',
3357
3443
  env: process.env,
3358
- });
3444
+ }));
3359
3445
  if (result.error) {
3360
3446
  return {
3361
3447
  ok: false,
@@ -3619,7 +3705,7 @@ async function startDaemon(home, runtimePython, host, port, proxy = null, envOve
3619
3705
  function openBrowser(url) {
3620
3706
  const spawnDetached = (command, args) => {
3621
3707
  try {
3622
- const child = spawn(command, args, { detached: true, stdio: 'ignore' });
3708
+ const child = spawn(command, args, detachedSpawnOptions({ stdio: 'ignore' }));
3623
3709
  child.unref();
3624
3710
  return true;
3625
3711
  } catch {
@@ -3933,12 +4019,11 @@ async function migrateMain(rawArgs) {
3933
4019
  const child = spawn(
3934
4020
  process.execPath,
3935
4021
  [migratedLauncher, '--home', targetHome, '--daemon-only', '--no-browser', '--skip-update-check'],
3936
- {
4022
+ detachedSpawnOptions({
3937
4023
  cwd: path.join(targetHome, 'cli'),
3938
- detached: true,
3939
4024
  stdio: 'ignore',
3940
4025
  env: process.env,
3941
- }
4026
+ })
3942
4027
  );
3943
4028
  child.unref();
3944
4029
  restartMessage = 'Managed daemon restart scheduled from the migrated home.';
@@ -4097,7 +4182,7 @@ async function main() {
4097
4182
  const pythonRuntime = ensurePythonRuntime(home);
4098
4183
  const runtimePython = pythonRuntime.runtimePython;
4099
4184
  const codexOverrideEnv = buildCodexOverrideEnv({
4100
- yolo: args.includes('--yolo'),
4185
+ yolo: resolveYoloFlag(args, true),
4101
4186
  profile: readOptionValue(args, '--codex-profile'),
4102
4187
  binary: readOptionValue(args, '--codex'),
4103
4188
  });
@@ -13,7 +13,7 @@ You will do four things:
13
13
 
14
14
  The screenshots in this guide use the current live web UI at `deepscientist.cc:20999` as an example. Your local UI at `127.0.0.1:20999` should look the same or very close.
15
15
 
16
- Current platform support: DeepScientist currently supports Linux and macOS only. Windows is not supported in the current release.
16
+ Current platform support: DeepScientist fully supports Linux and macOS. Native Windows support is currently experimental; WSL2 remains the most battle-tested option when you need the closest Linux-like terminal behavior.
17
17
 
18
18
  ## Safety First: Isolate Before You Start
19
19
 
@@ -60,7 +60,7 @@ If you plan to use a provider-backed Codex profile instead of the default OpenAI
60
60
 
61
61
  ## 1. Install Node.js and DeepScientist
62
62
 
63
- DeepScientist currently supports Linux and macOS only.
63
+ DeepScientist fully supports Linux and macOS. Native Windows support is currently experimental, and WSL2 is still the most battle-tested option when you want Linux-like shell behavior.
64
64
 
65
65
  Before installing DeepScientist itself, install Node.js from the official page:
66
66
 
@@ -395,8 +395,8 @@ codex:
395
395
  profile: ""
396
396
  model: gpt-5.4
397
397
  model_reasoning_effort: xhigh
398
- approval_policy: on-request
399
- sandbox_mode: workspace-write
398
+ approval_policy: never
399
+ sandbox_mode: danger-full-access
400
400
  retry_on_failure: true
401
401
  retry_max_attempts: 5
402
402
  retry_initial_backoff_sec: 1.0
@@ -473,19 +473,35 @@ claude:
473
473
  **`approval_policy`**
474
474
 
475
475
  - Type: `string`
476
- - Default: `on-request`
476
+ - Default: `never`
477
477
  - UI label: `Approval policy`
478
478
  - Allowed values: `never`, `on-failure`, `on-request`, `untrusted`
479
479
  - Meaning: how the runner should ask for permission on privileged actions.
480
+ - Runtime note: the launcher now starts Codex in YOLO mode by default. Passing `ds --yolo false` temporarily restores the non-YOLO pair `approval_policy=on-request` and `sandbox_mode=workspace-write`.
480
481
 
481
482
  **`sandbox_mode`**
482
483
 
483
484
  - Type: `string`
484
- - Default: `workspace-write`
485
+ - Default: `danger-full-access`
485
486
  - UI label: `Sandbox mode`
486
487
  - Allowed values: `read-only`, `workspace-write`, `danger-full-access`
487
488
  - Meaning: filesystem / process access mode for the runner.
488
489
 
490
+ **`env`**
491
+
492
+ - Type: `mapping<string, string>`
493
+ - Default: `{}`
494
+ - UI availability:
495
+ - global settings: editable in the `runners` structured form as `env`
496
+ - project settings: `Project settings -> Codex environment`
497
+ - Project-settings behavior:
498
+ - click `Add` to create a new variable row
499
+ - `OPENAI_BASE_URL` and `OPENAI_API_KEY` are shown by default
500
+ - changes are not auto-saved; click `Save env vars`
501
+ - empty values are ignored and are not injected into the Codex process
502
+ - Meaning: extra environment variables passed to Codex when DeepScientist starts a Codex run.
503
+ - Common use: provider-backed Codex setups that need API keys or custom base URLs.
504
+
489
505
  **`retry_on_failure`**
490
506
 
491
507
  - Type: `boolean`
@@ -20,6 +20,7 @@ After finishing this guide, you should be able to:
20
20
  - use `/new`, `/use latest`, `/status`, and related commands from QQ
21
21
  - see the detected `openid` in the `Settings` page
22
22
  - run safe readiness checks and send probes from the `Settings` page
23
+ - receive auto-generated metric timeline images after each recorded main experiment when QQ is the bound quest connector
23
24
 
24
25
  ### Deployment checklist before you start
25
26
 
@@ -221,6 +222,24 @@ When the connector is fully working, you should usually see all of these:
221
222
  - clicking `Send probe` again no longer reports an empty delivery target
222
223
  - if a latest project already exists, plain text continues that project automatically; if no project exists yet, the bot returns help instead
223
224
 
225
+ ## 5.3 Automatic main-experiment metric charts
226
+
227
+ When QQ is the bound quest connector, DeepScientist now auto-sends metric timeline charts after each recorded main experiment.
228
+
229
+ Current behavior:
230
+
231
+ - one chart per metric
232
+ - the baseline is drawn as a horizontal dashed reference line when a baseline value exists
233
+ - the system automatically respects whether the metric is `higher is better` or `lower is better`
234
+ - any point that beats baseline gets a star marker
235
+ - the latest point is filled with a deep Morandi red
236
+ - earlier points are filled with a deep Morandi blue
237
+ - if multiple metrics are present, DeepScientist sends them sequentially with about a 2 second gap
238
+
239
+ These charts are generated from quest-local files and delivered as native QQ images.
240
+
241
+ If you need to disable this automatic chart delivery, turn off `auto_send_main_experiment_png` in the QQ connector config.
242
+
224
243
  ### 5.2 Error quick decoder
225
244
 
226
245
  | Message | What it usually means | What to do |