@researai/deepscientist 1.5.15 → 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 (202) hide show
  1. package/README.md +385 -104
  2. package/bin/ds.js +1241 -110
  3. package/docs/en/00_QUICK_START.md +100 -19
  4. package/docs/en/01_SETTINGS_REFERENCE.md +34 -1
  5. package/docs/en/02_START_RESEARCH_GUIDE.md +7 -0
  6. package/docs/en/05_TUI_GUIDE.md +6 -0
  7. package/docs/en/06_RUNTIME_AND_CANVAS.md +4 -3
  8. package/docs/en/09_DOCTOR.md +25 -8
  9. package/docs/en/14_PROMPT_SKILLS_AND_MCP_GUIDE.md +63 -13
  10. package/docs/en/15_CODEX_PROVIDER_SETUP.md +37 -11
  11. package/docs/en/19_EXTERNAL_CONTROLLER_GUIDE.md +226 -0
  12. package/docs/en/19_LOCAL_BROWSER_AUTH.md +70 -0
  13. package/docs/en/20_WORKSPACE_MODES_GUIDE.md +250 -0
  14. package/docs/en/21_LOCAL_MODEL_BACKENDS_GUIDE.md +283 -0
  15. package/docs/en/91_DEVELOPMENT.md +237 -0
  16. package/docs/en/README.md +24 -2
  17. package/docs/zh/00_QUICK_START.md +89 -19
  18. package/docs/zh/01_SETTINGS_REFERENCE.md +34 -1
  19. package/docs/zh/02_START_RESEARCH_GUIDE.md +7 -0
  20. package/docs/zh/05_TUI_GUIDE.md +6 -0
  21. package/docs/zh/09_DOCTOR.md +26 -9
  22. package/docs/zh/14_PROMPT_SKILLS_AND_MCP_GUIDE.md +63 -13
  23. package/docs/zh/15_CODEX_PROVIDER_SETUP.md +37 -11
  24. package/docs/zh/19_EXTERNAL_CONTROLLER_GUIDE.md +226 -0
  25. package/docs/zh/19_LOCAL_BROWSER_AUTH.md +68 -0
  26. package/docs/zh/20_WORKSPACE_MODES_GUIDE.md +251 -0
  27. package/docs/zh/21_LOCAL_MODEL_BACKENDS_GUIDE.md +281 -0
  28. package/docs/zh/README.md +24 -2
  29. package/install.sh +46 -4
  30. package/package.json +2 -1
  31. package/pyproject.toml +1 -1
  32. package/src/deepscientist/__init__.py +1 -1
  33. package/src/deepscientist/acp/envelope.py +6 -0
  34. package/src/deepscientist/artifact/service.py +647 -22
  35. package/src/deepscientist/bash_exec/service.py +234 -9
  36. package/src/deepscientist/bridges/connectors.py +8 -2
  37. package/src/deepscientist/cli.py +115 -19
  38. package/src/deepscientist/codex_cli_compat.py +367 -22
  39. package/src/deepscientist/config/models.py +2 -1
  40. package/src/deepscientist/config/service.py +183 -13
  41. package/src/deepscientist/daemon/api/handlers.py +255 -31
  42. package/src/deepscientist/daemon/api/router.py +9 -0
  43. package/src/deepscientist/daemon/app.py +1146 -105
  44. package/src/deepscientist/diagnostics/__init__.py +6 -0
  45. package/src/deepscientist/diagnostics/runner_failures.py +130 -0
  46. package/src/deepscientist/doctor.py +207 -3
  47. package/src/deepscientist/gitops/__init__.py +10 -1
  48. package/src/deepscientist/gitops/diff.py +129 -0
  49. package/src/deepscientist/gitops/service.py +4 -1
  50. package/src/deepscientist/mcp/server.py +39 -0
  51. package/src/deepscientist/prompts/builder.py +275 -34
  52. package/src/deepscientist/quest/layout.py +15 -2
  53. package/src/deepscientist/quest/service.py +707 -55
  54. package/src/deepscientist/quest/stage_views.py +6 -1
  55. package/src/deepscientist/runners/codex.py +143 -43
  56. package/src/deepscientist/shared.py +19 -0
  57. package/src/deepscientist/skills/__init__.py +2 -2
  58. package/src/deepscientist/skills/installer.py +196 -5
  59. package/src/deepscientist/skills/registry.py +66 -0
  60. package/src/prompts/connectors/qq.md +18 -8
  61. package/src/prompts/connectors/weixin.md +16 -6
  62. package/src/prompts/contracts/shared_interaction.md +14 -2
  63. package/src/prompts/system.md +23 -5
  64. package/src/prompts/system_copilot.md +56 -0
  65. package/src/skills/analysis-campaign/SKILL.md +1 -0
  66. package/src/skills/baseline/SKILL.md +8 -0
  67. package/src/skills/decision/SKILL.md +8 -0
  68. package/src/skills/experiment/SKILL.md +8 -0
  69. package/src/skills/figure-polish/SKILL.md +1 -0
  70. package/src/skills/finalize/SKILL.md +1 -0
  71. package/src/skills/idea/SKILL.md +1 -0
  72. package/src/skills/intake-audit/SKILL.md +8 -0
  73. package/src/skills/mentor/SKILL.md +217 -0
  74. package/src/skills/mentor/references/correction-rules.md +210 -0
  75. package/src/skills/mentor/references/knowledge-profile.md +91 -0
  76. package/src/skills/mentor/references/persona-profile.md +138 -0
  77. package/src/skills/mentor/references/taste-profile.md +128 -0
  78. package/src/skills/mentor/references/thought-style-profile.md +138 -0
  79. package/src/skills/mentor/references/work-profile.md +289 -0
  80. package/src/skills/mentor/references/workflow-profile.md +240 -0
  81. package/src/skills/optimize/SKILL.md +1 -0
  82. package/src/skills/rebuttal/SKILL.md +1 -0
  83. package/src/skills/review/SKILL.md +1 -0
  84. package/src/skills/scout/SKILL.md +8 -0
  85. package/src/skills/write/SKILL.md +1 -0
  86. package/src/tui/dist/app/AppContainer.js +19 -11
  87. package/src/tui/dist/index.js +4 -1
  88. package/src/tui/dist/lib/api.js +33 -3
  89. package/src/tui/package.json +1 -1
  90. package/src/ui/dist/assets/AiManusChatView-Bv-Z8YpU.js +204 -0
  91. package/src/ui/dist/assets/AnalysisPlugin-BCKAfjba.js +1 -0
  92. package/src/ui/dist/assets/CliPlugin-BCKcpc35.js +109 -0
  93. package/src/ui/dist/assets/CodeEditorPlugin-DbOfSJ8K.js +2 -0
  94. package/src/ui/dist/assets/CodeViewerPlugin-CbaFRrUU.js +270 -0
  95. package/src/ui/dist/assets/DocViewerPlugin-DAjLVeQD.js +7 -0
  96. package/src/ui/dist/assets/GitCommitViewerPlugin-CIUqbUDO.js +1 -0
  97. package/src/ui/dist/assets/GitDiffViewerPlugin-CQACjoAA.js +6 -0
  98. package/src/ui/dist/assets/GitSnapshotViewer-0r4nLPke.js +30 -0
  99. package/src/ui/dist/assets/ImageViewerPlugin-nBOmI2v_.js +26 -0
  100. package/src/ui/dist/assets/LabCopilotPanel-BHxOxF4z.js +14 -0
  101. package/src/ui/dist/assets/LabPlugin-BKoZGs95.js +22 -0
  102. package/src/ui/dist/assets/LatexPlugin-ZwtV8pIp.js +25 -0
  103. package/src/ui/dist/assets/MarkdownViewerPlugin-DKqVfKyW.js +128 -0
  104. package/src/ui/dist/assets/MarketplacePlugin-BwxStZ9D.js +13 -0
  105. package/src/ui/dist/assets/NotebookEditor-BEQhaQbt.js +81 -0
  106. package/src/ui/dist/assets/{NotebookEditor-CccQYZjX.css → NotebookEditor-BHH8rdGj.css} +1 -1
  107. package/src/ui/dist/assets/NotebookEditor-BOr3x3Ej.css +1 -0
  108. package/src/ui/dist/assets/NotebookEditor-DB9N_T9q.js +361 -0
  109. package/src/ui/dist/assets/PdfLoader-Cy5jtWrr.css +1 -0
  110. package/src/ui/dist/assets/PdfLoader-eWBONbQP.js +16 -0
  111. package/src/ui/dist/assets/PdfMarkdownPlugin-D22YOZL3.js +1 -0
  112. package/src/ui/dist/assets/PdfViewerPlugin-c-RK9DLM.js +17 -0
  113. package/src/ui/dist/assets/PdfViewerPlugin-nwwE-fjJ.css +1 -0
  114. package/src/ui/dist/assets/SearchPlugin-CxF9ytAx.js +16 -0
  115. package/src/ui/dist/assets/SearchPlugin-DA4en4hK.css +1 -0
  116. package/src/ui/dist/assets/TextViewerPlugin-C5xqeeUH.js +54 -0
  117. package/src/ui/dist/assets/VNCViewer-BoLGLnHz.js +11 -0
  118. package/src/ui/dist/assets/bot-DREQOxzP.js +6 -0
  119. package/src/ui/dist/assets/browser-CTB2jwNe.js +8 -0
  120. package/src/ui/dist/assets/chevron-up-C9Qpx4DE.js +6 -0
  121. package/src/ui/dist/assets/code-WlFHE7z_.js +6 -0
  122. package/src/ui/dist/assets/file-content-BZMz3RYp.js +1 -0
  123. package/src/ui/dist/assets/file-diff-panel-CQhw0jS2.js +1 -0
  124. package/src/ui/dist/assets/file-jump-queue-DA-SdG__.js +1 -0
  125. package/src/ui/dist/assets/file-socket-CfQPKQKj.js +1 -0
  126. package/src/ui/dist/assets/git-commit-horizontal-DxZ8DCZh.js +6 -0
  127. package/src/ui/dist/assets/image-Bgl4VIyx.js +6 -0
  128. package/src/ui/dist/assets/index-BpV6lusQ.css +33 -0
  129. package/src/ui/dist/assets/index-CBNVuWcP.js +2496 -0
  130. package/src/ui/dist/assets/index-CwNu1aH4.js +11 -0
  131. package/src/ui/dist/assets/index-DrUnlf6K.js +1 -0
  132. package/src/ui/dist/assets/index-NW-h8VzN.js +1 -0
  133. package/src/ui/dist/assets/monaco-CiHMMNH_.js +1 -0
  134. package/src/ui/dist/assets/pdf-effect-queue-J8OnM0jE.js +6 -0
  135. package/src/ui/dist/assets/plugin-monaco-C8UgLomw.js +19 -0
  136. package/src/ui/dist/assets/plugin-notebook-HbW2K-1c.js +169 -0
  137. package/src/ui/dist/assets/plugin-pdf-CR8hgQBV.js +357 -0
  138. package/src/ui/dist/assets/plugin-terminal-MXFIPun8.js +227 -0
  139. package/src/ui/dist/assets/popover-CLc0pPP8.js +1 -0
  140. package/src/ui/dist/assets/project-sync-C9IdzdZW.js +1 -0
  141. package/src/ui/dist/assets/select-Cs2PmzwL.js +11 -0
  142. package/src/ui/dist/assets/sigma-ClKcHAXm.js +6 -0
  143. package/src/ui/dist/assets/trash-DwpbFr3w.js +11 -0
  144. package/src/ui/dist/assets/useCliAccess-NQ8m0Let.js +1 -0
  145. package/src/ui/dist/assets/useFileDiffOverlay-FuhcnKiw.js +1 -0
  146. package/src/ui/dist/assets/wrap-text-BC-Hltpd.js +11 -0
  147. package/src/ui/dist/assets/zoom-out-E_gaeAxL.js +11 -0
  148. package/src/ui/dist/index.html +5 -2
  149. package/src/ui/dist/assets/AiManusChatView-DDjbFnbt.js +0 -26597
  150. package/src/ui/dist/assets/AnalysisPlugin-Yb5IdmaU.js +0 -123
  151. package/src/ui/dist/assets/CliPlugin-e64sreyu.js +0 -31037
  152. package/src/ui/dist/assets/CodeEditorPlugin-C4D2TIkU.js +0 -427
  153. package/src/ui/dist/assets/CodeViewerPlugin-BVoNZIvC.js +0 -905
  154. package/src/ui/dist/assets/DocViewerPlugin-CLChbllo.js +0 -278
  155. package/src/ui/dist/assets/GitDiffViewerPlugin-C4xeFyFQ.js +0 -2661
  156. package/src/ui/dist/assets/ImageViewerPlugin-OiMUAcLi.js +0 -500
  157. package/src/ui/dist/assets/LabCopilotPanel-BjD2ThQF.js +0 -4104
  158. package/src/ui/dist/assets/LabPlugin-DQPg-NrB.js +0 -2677
  159. package/src/ui/dist/assets/LatexPlugin-CI05XAV9.js +0 -1792
  160. package/src/ui/dist/assets/MarkdownViewerPlugin-DpeBLYZf.js +0 -308
  161. package/src/ui/dist/assets/MarketplacePlugin-DolE58Q2.js +0 -413
  162. package/src/ui/dist/assets/NotebookEditor-7Qm2rSWD.js +0 -4214
  163. package/src/ui/dist/assets/NotebookEditor-C1kWaxKi.js +0 -84873
  164. package/src/ui/dist/assets/NotebookEditor-C3VQ7ylN.css +0 -1405
  165. package/src/ui/dist/assets/PdfLoader-BfOHw8Zw.js +0 -25468
  166. package/src/ui/dist/assets/PdfLoader-C-Y707R3.css +0 -49
  167. package/src/ui/dist/assets/PdfMarkdownPlugin-BulDREv1.js +0 -409
  168. package/src/ui/dist/assets/PdfViewerPlugin-C-daaOaL.js +0 -3095
  169. package/src/ui/dist/assets/PdfViewerPlugin-DQ11QcSf.css +0 -3627
  170. package/src/ui/dist/assets/SearchPlugin-CjpaiJ3A.js +0 -741
  171. package/src/ui/dist/assets/SearchPlugin-DDMrGDkh.css +0 -379
  172. package/src/ui/dist/assets/TextViewerPlugin-BxIyqPQC.js +0 -472
  173. package/src/ui/dist/assets/VNCViewer-HAg9mF7M.js +0 -18821
  174. package/src/ui/dist/assets/awareness-C0NPR2Dj.js +0 -292
  175. package/src/ui/dist/assets/bot-0DYntytV.js +0 -21
  176. package/src/ui/dist/assets/browser-BAcuE0Xj.js +0 -2895
  177. package/src/ui/dist/assets/code-B20Slj_w.js +0 -17
  178. package/src/ui/dist/assets/file-content-DT24KFma.js +0 -377
  179. package/src/ui/dist/assets/file-diff-panel-DK13YPql.js +0 -92
  180. package/src/ui/dist/assets/file-jump-queue-r5XKgJEV.js +0 -16
  181. package/src/ui/dist/assets/file-socket-B4T2o4nR.js +0 -58
  182. package/src/ui/dist/assets/function-B5QZkkHC.js +0 -1895
  183. package/src/ui/dist/assets/image-DSeR_sDS.js +0 -18
  184. package/src/ui/dist/assets/index-BrFje2Uk.js +0 -120
  185. package/src/ui/dist/assets/index-BwRJaoTl.js +0 -25
  186. package/src/ui/dist/assets/index-D_E4281X.js +0 -221322
  187. package/src/ui/dist/assets/index-DnYB3xb1.js +0 -159
  188. package/src/ui/dist/assets/index-G7AcWcMu.css +0 -12594
  189. package/src/ui/dist/assets/monaco-LExaAN3Y.js +0 -623
  190. package/src/ui/dist/assets/pdf-effect-queue-BJk5okWJ.js +0 -47
  191. package/src/ui/dist/assets/pdf_viewer-e0g1is2C.js +0 -8206
  192. package/src/ui/dist/assets/popover-D3Gg_FoV.js +0 -476
  193. package/src/ui/dist/assets/project-sync-C_ygLlVU.js +0 -297
  194. package/src/ui/dist/assets/select-CpAK6uWm.js +0 -1690
  195. package/src/ui/dist/assets/sigma-DEccaSgk.js +0 -22
  196. package/src/ui/dist/assets/square-check-big-uUfyVsbD.js +0 -17
  197. package/src/ui/dist/assets/trash-CXvwwSe8.js +0 -32
  198. package/src/ui/dist/assets/useCliAccess-Bnop4mgR.js +0 -957
  199. package/src/ui/dist/assets/useFileDiffOverlay-B8eUAX0I.js +0 -53
  200. package/src/ui/dist/assets/wrap-text-9vbOBpkW.js +0 -35
  201. package/src/ui/dist/assets/yjs-DncrqiZ8.js +0 -11243
  202. package/src/ui/dist/assets/zoom-out-BgVMmOW4.js +0 -34
@@ -4,13 +4,23 @@ import json
4
4
  import os
5
5
  import subprocess
6
6
  import tempfile
7
- from shutil import copy2
8
7
  from copy import deepcopy
9
8
  from pathlib import Path
10
9
  from urllib.error import URLError
11
10
  from urllib.request import Request
12
11
 
13
- from ..codex_cli_compat import adapt_profile_only_provider_config, normalize_codex_reasoning_effort
12
+ from ..codex_cli_compat import (
13
+ active_provider_metadata_from_home,
14
+ adapt_profile_only_provider_config,
15
+ chat_wire_compatible_codex_version,
16
+ codex_cli_version,
17
+ format_codex_cli_version,
18
+ materialize_codex_runtime_home,
19
+ missing_provider_env_key,
20
+ missing_provider_env_key_from_text,
21
+ normalize_codex_reasoning_effort,
22
+ provider_base_url_looks_local,
23
+ )
14
24
  from ..connector.connector_profiles import PROFILEABLE_CONNECTOR_NAMES, list_connector_profiles, normalize_connector_config
15
25
  from ..connector_runtime import build_discovered_target, infer_connector_transport
16
26
  from ..home import repo_root
@@ -1192,6 +1202,14 @@ Use **Test** when the file exposes runtime dependencies.
1192
1202
  return "gpt-5.4"
1193
1203
  return str(raw_model).strip()
1194
1204
 
1205
+ @staticmethod
1206
+ def _codex_effective_model(config: dict) -> str:
1207
+ requested = ConfigManager._codex_requested_model(config)
1208
+ profile = ConfigManager._codex_profile_name(config)
1209
+ if profile and not ConfigManager._codex_should_inherit_model(requested):
1210
+ return "inherit"
1211
+ return requested
1212
+
1195
1213
  @staticmethod
1196
1214
  def _codex_profile_name(config: dict) -> str:
1197
1215
  raw_profile = config.get("profile")
@@ -1233,10 +1251,11 @@ Use **Test** when the file exposes runtime dependencies.
1233
1251
 
1234
1252
  temp_home = tempfile.TemporaryDirectory(prefix="ds-codex-probe-")
1235
1253
  temp_root = Path(temp_home.name)
1236
- for filename in ("auth.json",):
1237
- source_path = expanded / filename
1238
- if source_path.exists():
1239
- copy2(source_path, temp_root / filename)
1254
+ materialize_codex_runtime_home(
1255
+ source_home=expanded,
1256
+ target_home=temp_root,
1257
+ profile=profile,
1258
+ )
1240
1259
  write_text(temp_root / "config.toml", adapted_text)
1241
1260
  return str(temp_root), warning, temp_home
1242
1261
 
@@ -1254,32 +1273,142 @@ Use **Test** when the file exposes runtime dependencies.
1254
1273
  ]
1255
1274
  )
1256
1275
  else:
1257
- guidance.append("Run `codex --login` (or `codex`) once and finish authentication before starting DeepScientist.")
1276
+ guidance.append("Run `codex login` (or just `codex`) once and finish authentication before starting DeepScientist.")
1258
1277
  guidance.append(
1259
1278
  "If you use a custom Codex path, either set `runners.codex.binary` or launch with `ds --codex /absolute/path/to/codex`."
1260
1279
  )
1261
1280
  return guidance
1262
1281
 
1282
+ @staticmethod
1283
+ def _provider_profile_probe_hints(metadata: dict[str, object]) -> list[str]:
1284
+ base_url = str(metadata.get("base_url") or "").strip().lower()
1285
+ model = str(metadata.get("model") or "").strip().lower()
1286
+ provider = str(metadata.get("provider") or "").strip().lower()
1287
+ if "dashscope.aliyuncs.com" not in base_url and "bailian" not in provider and "qwen" not in model:
1288
+ return []
1289
+ if "coding.dashscope.aliyuncs.com" not in base_url:
1290
+ return [
1291
+ "Alibaba Bailian's generic DashScope / Qwen platform API is not supported by the Codex-backed DeepScientist path.",
1292
+ "If you want to use Qwen here, switch the profile to the Bailian Coding Plan endpoint: `https://coding.dashscope.aliyuncs.com/v1`.",
1293
+ ]
1294
+ return [
1295
+ "For Qwen on Alibaba Bailian, only the Coding Plan endpoint is supported here; do not switch back to the generic Bailian / DashScope Qwen API.",
1296
+ ]
1297
+
1298
+ @staticmethod
1299
+ def _local_provider_probe_hints(metadata: dict[str, object]) -> list[str]:
1300
+ base_url = str(metadata.get("base_url") or "").strip()
1301
+ wire_api = str(metadata.get("wire_api") or "").strip().lower()
1302
+ requires_openai_auth = metadata.get("requires_openai_auth")
1303
+ if not base_url:
1304
+ return []
1305
+ is_local_provider = provider_base_url_looks_local(base_url)
1306
+ if requires_openai_auth is not False and not is_local_provider:
1307
+ return []
1308
+ hints = [
1309
+ f"Verify the local provider directly: `curl {base_url}/models`.",
1310
+ f"Then verify the Responses API explicitly: `curl {base_url}/responses ...`.",
1311
+ "Latest Codex CLI requires `wire_api = \"responses\"`; chat-only provider configs are no longer accepted.",
1312
+ "If `/v1/chat/completions` works but `/v1/responses` fails, that backend is not currently compatible with the latest Codex runner.",
1313
+ "If the backend is chat-only and you still want to test it through Codex, try `@openai/codex@0.57.0` with top-level `model_provider` / `model` plus `wire_api = \"chat\"`.",
1314
+ "For local model backends, vLLM is the safest path. Ollama only works when its `/v1/responses` endpoint works; chat-only SGLang deployments will fail with the latest Codex.",
1315
+ ]
1316
+ if requires_openai_auth is not False:
1317
+ hints.insert(
1318
+ 0,
1319
+ "For local or self-hosted providers, add `requires_openai_auth = false` so DeepScientist can remove conflicting `OPENAI_*` auth variables.",
1320
+ )
1321
+ if not wire_api:
1322
+ hints.insert(0, "Your current provider config does not declare `wire_api`; set `wire_api = \"responses\"` first.")
1323
+ elif wire_api != "responses":
1324
+ hints.insert(0, f"Your current provider config uses `wire_api = \"{wire_api}\"`; switch it to `wire_api = \"responses\"` first.")
1325
+ return hints
1326
+
1327
+ @staticmethod
1328
+ def _missing_provider_env_guidance(
1329
+ *,
1330
+ profile: str,
1331
+ env_key: str,
1332
+ metadata: dict[str, object],
1333
+ ) -> list[str]:
1334
+ guidance = [
1335
+ f"Set `runners.codex.env.{env_key}` in `~/DeepScientist/config/runners.yaml`, or export `{env_key}` before launching `ds`.",
1336
+ ]
1337
+ if provider_base_url_looks_local(str(metadata.get("base_url") or "").strip()):
1338
+ guidance.append(
1339
+ f"If `{env_key}` is only a placeholder for a local OpenAI-compatible backend, any non-empty value such as `1234` is usually enough."
1340
+ )
1341
+ if metadata.get("requires_openai_auth") is not False:
1342
+ guidance.append(
1343
+ "Also add `requires_openai_auth = false` to that local provider profile so DeepScientist can remove conflicting `OPENAI_*` auth variables."
1344
+ )
1345
+ guidance.append(
1346
+ f"Before retrying DeepScientist, run a real request such as `codex exec --profile {profile} --json --cd /tmp --skip-git-repo-check -` and verify it returns `HELLO`."
1347
+ )
1348
+ return guidance
1349
+
1350
+ @staticmethod
1351
+ def _chat_wire_probe_version_block(
1352
+ metadata: dict[str, object],
1353
+ *,
1354
+ resolved_binary: str,
1355
+ ) -> tuple[tuple[int, int, int] | None, dict[str, object] | None]:
1356
+ wire_api = str(metadata.get("wire_api") or "").strip().lower()
1357
+ if wire_api != "chat":
1358
+ return None, None
1359
+ detected_version = codex_cli_version(str(resolved_binary or ""))
1360
+ required_version = chat_wire_compatible_codex_version()
1361
+ if detected_version == required_version:
1362
+ return detected_version, None
1363
+ required_text = format_codex_cli_version(required_version)
1364
+ detected_text = format_codex_cli_version(detected_version)
1365
+ errors = [
1366
+ "This provider uses `wire_api = \"chat\"`, but DeepScientist only probes chat-mode providers with `codex-cli 0.57.0`.",
1367
+ ]
1368
+ if detected_text:
1369
+ errors.append(f"Detected Codex CLI version: `{detected_text}`.")
1370
+ else:
1371
+ errors.append("DeepScientist could not determine the active Codex CLI version from the configured binary.")
1372
+ guidance = [
1373
+ "Install `npm install -g @openai/codex@0.57.0`, or point DeepScientist at a dedicated `0.57.0` binary with `ds --codex /absolute/path/to/codex`.",
1374
+ "If you want to stay on a newer Codex CLI, switch the provider/backend to `wire_api = \"responses\"` instead.",
1375
+ "For chat-mode fallback configs, keep the compatible top-level `model_provider` / `model` entries in `~/.codex/config.toml`.",
1376
+ ]
1377
+ return (
1378
+ detected_version,
1379
+ {
1380
+ "summary": f"Codex startup probe blocked by chat-mode provider compatibility. Required Codex CLI: `{required_text}`.",
1381
+ "errors": errors,
1382
+ "guidance": guidance,
1383
+ },
1384
+ )
1385
+
1263
1386
  def _codex_probe_failure_guidance(self, config: dict) -> tuple[list[str], list[str]]:
1264
1387
  profile = self._codex_profile_name(config)
1388
+ config_dir = str(config.get("config_dir") or "~/.codex").strip()
1389
+ metadata = active_provider_metadata_from_home(config_dir, profile=profile or None) if config_dir else {}
1265
1390
  if profile:
1391
+ provider_hints = self._provider_profile_probe_hints(metadata)
1392
+ local_hints = self._local_provider_probe_hints(metadata)
1266
1393
  return (
1267
1394
  [
1268
1395
  f"Codex profile `{profile}` did not complete the startup hello probe successfully.",
1269
1396
  ],
1270
1397
  [
1271
- f"Run `codex --profile {profile}` in a terminal and confirm that profile can start normally.",
1398
+ f"Run `codex exec --profile {profile} --json --cd /tmp --skip-git-repo-check -` in a terminal and confirm that a real `HELLO` request succeeds.",
1272
1399
  "If the profile uses a custom provider, make sure its API key, Base URL, and model configuration are available to Codex.",
1273
1400
  "If the provider expects the model from the Codex profile itself, set `model: inherit` in `~/DeepScientist/config/runners.yaml`.",
1401
+ *provider_hints,
1402
+ *local_hints,
1274
1403
  "Then run `ds doctor` and start DeepScientist again.",
1275
1404
  ],
1276
1405
  )
1277
1406
  return (
1278
1407
  [
1279
- "Run `codex --login` (or `codex`) once and complete login before starting DeepScientist.",
1408
+ "Run `codex login` (or just `codex`) once and complete login before starting DeepScientist.",
1280
1409
  ],
1281
1410
  [
1282
- "Run `codex --login` (or `codex`) in a terminal and complete login or first-run setup.",
1411
+ "Run `codex login` (or just `codex`) in a terminal and complete login or first-run setup.",
1283
1412
  "If `codex` is missing, install it explicitly with `npm install -g @openai/codex`.",
1284
1413
  "If the configured model is not available to your Codex account, update `~/DeepScientist/config/runners.yaml` and try again.",
1285
1414
  "Then run `ds doctor` and start DeepScientist again.",
@@ -1361,6 +1490,7 @@ Use **Test** when the file exposes runtime dependencies.
1361
1490
  resolved_binary = resolve_runner_binary(binary, runner_name="codex")
1362
1491
  profile = self._codex_profile_name(config)
1363
1492
  requested_model = self._codex_requested_model(config)
1493
+ effective_model = self._codex_effective_model(config)
1364
1494
  raw_reasoning_effort = config.get("model_reasoning_effort")
1365
1495
  requested_reasoning_effort = (
1366
1496
  str(raw_reasoning_effort).strip()
@@ -1376,9 +1506,9 @@ Use **Test** when the file exposes runtime dependencies.
1376
1506
  "resolved_binary": resolved_binary,
1377
1507
  "config_dir": str(config.get("config_dir") or "~/.codex"),
1378
1508
  "profile": profile,
1379
- "model": requested_model or "inherit",
1509
+ "model": effective_model or "inherit",
1380
1510
  "requested_model": requested_model or "inherit",
1381
- "effective_model": requested_model or "inherit",
1511
+ "effective_model": effective_model or "inherit",
1382
1512
  "approval_policy": str(config.get("approval_policy") or "on-request"),
1383
1513
  "sandbox_mode": str(config.get("sandbox_mode") or "workspace-write"),
1384
1514
  "reasoning_effort": reasoning_effort,
@@ -1416,10 +1546,37 @@ Use **Test** when the file exposes runtime dependencies.
1416
1546
  env["CODEX_HOME"] = prepared_home
1417
1547
  if profile_config_warning:
1418
1548
  compatibility_warnings.append(profile_config_warning)
1549
+ metadata = active_provider_metadata_from_home(env.get("CODEX_HOME") or config_dir, profile=profile or None)
1550
+ if metadata.get("requires_openai_auth") is False:
1551
+ env.pop("OPENAI_API_KEY", None)
1552
+ env.pop("OPENAI_BASE_URL", None)
1553
+ configured_provider_env_key = missing_provider_env_key(metadata, env)
1554
+ details["provider_env_key"] = str(metadata.get("env_key") or "").strip() or None
1555
+ details["provider_env_missing"] = bool(configured_provider_env_key)
1556
+ details["provider_wire_api"] = str(metadata.get("wire_api") or "").strip() or None
1557
+ detected_codex_version, chat_wire_block = self._chat_wire_probe_version_block(
1558
+ metadata,
1559
+ resolved_binary=resolved_binary,
1560
+ )
1561
+ if detected_codex_version is not None:
1562
+ details["codex_cli_version"] = format_codex_cli_version(detected_codex_version) or None
1419
1563
  prompt = "Reply with exactly HELLO."
1420
1564
  if reasoning_effort_warning:
1421
1565
  compatibility_warnings.append(reasoning_effort_warning)
1566
+ if profile and effective_model == "inherit" and not self._codex_should_inherit_model(requested_model):
1567
+ compatibility_warnings.append(
1568
+ f"Codex profile `{profile}` is provider-backed. DeepScientist is probing it with `model: inherit`."
1569
+ )
1422
1570
  base_warnings: list[str] = list(compatibility_warnings)
1571
+ if chat_wire_block is not None:
1572
+ return {
1573
+ "ok": False,
1574
+ "summary": str(chat_wire_block["summary"]),
1575
+ "warnings": base_warnings,
1576
+ "errors": list(chat_wire_block["errors"]),
1577
+ "details": details,
1578
+ "guidance": list(chat_wire_block["guidance"]),
1579
+ }
1423
1580
 
1424
1581
  def run_probe_once(model_for_command: str) -> tuple[list[str], subprocess.CompletedProcess[str] | None, subprocess.TimeoutExpired | None]:
1425
1582
  command = self._build_codex_probe_command(
@@ -1445,7 +1602,7 @@ Use **Test** when the file exposes runtime dependencies.
1445
1602
  return command, None, exc
1446
1603
  return command, result, None
1447
1604
 
1448
- command, result, timeout_error = run_probe_once(requested_model)
1605
+ command, result, timeout_error = run_probe_once(effective_model)
1449
1606
  if timeout_error is not None:
1450
1607
  details.update(
1451
1608
  {
@@ -1546,7 +1703,20 @@ Use **Test** when the file exposes runtime dependencies.
1546
1703
  if details.get("model_fallback_attempted") and not details.get("model_fallback_used"):
1547
1704
  warnings.append("DeepScientist also tried the current Codex default model, but that fallback probe did not succeed.")
1548
1705
  errors.extend(self._codex_probe_failure_guidance(config)[0])
1706
+ missing_env_key = missing_provider_env_key_from_text(stdout_text, stderr_text) or configured_provider_env_key
1549
1707
  failure_guidance = self._codex_probe_failure_guidance(config)[1]
1708
+ if not ok and missing_env_key and profile:
1709
+ errors.append(
1710
+ f"Codex profile `{profile}` requires environment variable `{missing_env_key}`, but DeepScientist did not receive it."
1711
+ )
1712
+ failure_guidance = [
1713
+ *self._missing_provider_env_guidance(
1714
+ profile=profile,
1715
+ env_key=missing_env_key,
1716
+ metadata=metadata,
1717
+ ),
1718
+ *failure_guidance,
1719
+ ]
1550
1720
  return {
1551
1721
  "ok": ok,
1552
1722
  "summary": "Codex startup probe completed." if ok else "Codex startup probe failed.",