@researai/deepscientist 1.5.7 → 1.5.9
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.
- package/LICENSE +186 -21
- package/README.md +8 -4
- package/bin/ds.js +224 -9
- package/docs/en/00_QUICK_START.md +2 -2
- package/docs/en/07_MEMORY_AND_MCP.md +40 -3
- package/docs/en/99_ACKNOWLEDGEMENTS.md +1 -0
- package/docs/zh/00_QUICK_START.md +2 -2
- package/docs/zh/07_MEMORY_AND_MCP.md +40 -3
- package/docs/zh/99_ACKNOWLEDGEMENTS.md +1 -0
- package/install.sh +34 -0
- package/package.json +2 -2
- package/pyproject.toml +2 -2
- package/src/deepscientist/__init__.py +1 -1
- package/src/deepscientist/acp/envelope.py +1 -0
- package/src/deepscientist/artifact/metrics.py +814 -83
- package/src/deepscientist/artifact/schemas.py +1 -0
- package/src/deepscientist/artifact/service.py +2001 -229
- package/src/deepscientist/bash_exec/monitor.py +1 -1
- package/src/deepscientist/bash_exec/service.py +17 -9
- package/src/deepscientist/channels/qq.py +17 -0
- package/src/deepscientist/channels/relay.py +16 -0
- package/src/deepscientist/config/models.py +6 -0
- package/src/deepscientist/config/service.py +70 -2
- package/src/deepscientist/daemon/api/handlers.py +414 -14
- package/src/deepscientist/daemon/api/router.py +4 -0
- package/src/deepscientist/daemon/app.py +292 -21
- package/src/deepscientist/gitops/diff.py +6 -10
- package/src/deepscientist/mcp/server.py +191 -40
- package/src/deepscientist/prompts/builder.py +65 -19
- package/src/deepscientist/quest/node_traces.py +129 -2
- package/src/deepscientist/quest/service.py +140 -34
- package/src/deepscientist/quest/stage_views.py +175 -33
- package/src/deepscientist/registries/baseline.py +56 -4
- package/src/deepscientist/runners/codex.py +1 -1
- package/src/prompts/connectors/qq.md +1 -1
- package/src/prompts/contracts/shared_interaction.md +14 -0
- package/src/prompts/system.md +113 -32
- package/src/skills/analysis-campaign/SKILL.md +10 -14
- package/src/skills/baseline/SKILL.md +51 -38
- package/src/skills/baseline/references/baseline-plan-template.md +2 -0
- package/src/skills/decision/SKILL.md +12 -8
- package/src/skills/experiment/SKILL.md +28 -16
- package/src/skills/experiment/references/main-experiment-plan-template.md +2 -0
- package/src/skills/figure-polish/SKILL.md +1 -0
- package/src/skills/finalize/SKILL.md +3 -8
- package/src/skills/idea/SKILL.md +18 -8
- package/src/skills/idea/references/literature-survey-template.md +24 -0
- package/src/skills/idea/references/related-work-playbook.md +4 -0
- package/src/skills/idea/references/selection-gate.md +9 -0
- package/src/skills/intake-audit/SKILL.md +2 -8
- package/src/skills/rebuttal/SKILL.md +2 -8
- package/src/skills/review/SKILL.md +2 -8
- package/src/skills/scout/SKILL.md +2 -8
- package/src/skills/write/SKILL.md +53 -17
- package/src/skills/write/templates/DEEPSCIENTIST_NOTES.md +21 -0
- package/src/skills/write/templates/README.md +408 -0
- package/src/skills/write/templates/UPSTREAM_LICENSE.txt +21 -0
- package/src/skills/write/templates/aaai2026/README.md +534 -0
- package/src/skills/write/templates/aaai2026/aaai2026-unified-supp.tex +144 -0
- package/src/skills/write/templates/aaai2026/aaai2026-unified-template.tex +952 -0
- package/src/skills/write/templates/aaai2026/aaai2026.bib +111 -0
- package/src/skills/write/templates/aaai2026/aaai2026.bst +1493 -0
- package/src/skills/write/templates/aaai2026/aaai2026.sty +315 -0
- package/src/skills/write/templates/acl/README.md +50 -0
- package/src/skills/write/templates/acl/acl.sty +312 -0
- package/src/skills/write/templates/acl/acl_latex.tex +377 -0
- package/src/skills/write/templates/acl/acl_lualatex.tex +101 -0
- package/src/skills/write/templates/acl/acl_natbib.bst +1940 -0
- package/src/skills/write/templates/acl/anthology.bib.txt +26 -0
- package/src/skills/write/templates/acl/custom.bib +70 -0
- package/src/skills/write/templates/acl/formatting.md +326 -0
- package/src/skills/write/templates/asplos2027/main.tex +459 -0
- package/src/skills/write/templates/asplos2027/references.bib +135 -0
- package/src/skills/write/templates/colm2025/README.md +3 -0
- package/src/skills/write/templates/colm2025/colm2025_conference.bib +11 -0
- package/src/skills/write/templates/colm2025/colm2025_conference.bst +1440 -0
- package/src/skills/write/templates/colm2025/colm2025_conference.sty +218 -0
- package/src/skills/write/templates/colm2025/colm2025_conference.tex +305 -0
- package/src/skills/write/templates/colm2025/fancyhdr.sty +485 -0
- package/src/skills/write/templates/colm2025/math_commands.tex +508 -0
- package/src/skills/write/templates/colm2025/natbib.sty +1246 -0
- package/src/skills/write/templates/iclr2026/fancyhdr.sty +485 -0
- package/src/skills/write/templates/iclr2026/iclr2026_conference.bib +24 -0
- package/src/skills/write/templates/iclr2026/iclr2026_conference.bst +1440 -0
- package/src/skills/write/templates/iclr2026/iclr2026_conference.sty +246 -0
- package/src/skills/write/templates/iclr2026/iclr2026_conference.tex +414 -0
- package/src/skills/write/templates/iclr2026/math_commands.tex +508 -0
- package/src/skills/write/templates/iclr2026/natbib.sty +1246 -0
- package/src/skills/write/templates/icml2026/algorithm.sty +79 -0
- package/src/skills/write/templates/icml2026/algorithmic.sty +201 -0
- package/src/skills/write/templates/icml2026/example_paper.bib +75 -0
- package/src/skills/write/templates/icml2026/example_paper.tex +662 -0
- package/src/skills/write/templates/icml2026/fancyhdr.sty +864 -0
- package/src/skills/write/templates/icml2026/icml2026.bst +1443 -0
- package/src/skills/write/templates/icml2026/icml2026.sty +767 -0
- package/src/skills/write/templates/neurips2025/Makefile +36 -0
- package/src/skills/write/templates/neurips2025/extra_pkgs.tex +53 -0
- package/src/skills/write/templates/neurips2025/main.tex +38 -0
- package/src/skills/write/templates/neurips2025/neurips.sty +382 -0
- package/src/skills/write/templates/nsdi2027/main.tex +426 -0
- package/src/skills/write/templates/nsdi2027/references.bib +151 -0
- package/src/skills/write/templates/nsdi2027/usenix-2020-09.sty +83 -0
- package/src/skills/write/templates/osdi2026/main.tex +429 -0
- package/src/skills/write/templates/osdi2026/references.bib +150 -0
- package/src/skills/write/templates/osdi2026/usenix-2020-09.sty +83 -0
- package/src/skills/write/templates/sosp2026/main.tex +532 -0
- package/src/skills/write/templates/sosp2026/references.bib +148 -0
- package/src/tui/package.json +1 -1
- package/src/ui/dist/assets/{AiManusChatView-BS3V4ZOk.js → AiManusChatView-BKZ103sn.js} +110 -14
- package/src/ui/dist/assets/{AnalysisPlugin-DLPXQsmr.js → AnalysisPlugin-mTTzGAlK.js} +1 -1
- package/src/ui/dist/assets/{AutoFigurePlugin-C-Fr9knQ.js → AutoFigurePlugin-C_wWw4AP.js} +5 -5
- package/src/ui/dist/assets/{CliPlugin-Dd8AHzFg.js → CliPlugin-BH58n3GY.js} +9 -9
- package/src/ui/dist/assets/{CodeEditorPlugin-Dg-RepTl.js → CodeEditorPlugin-BKGRUH7e.js} +8 -8
- package/src/ui/dist/assets/{CodeViewerPlugin-D2J_3nyt.js → CodeViewerPlugin-BMADwFWJ.js} +5 -5
- package/src/ui/dist/assets/{DocViewerPlugin-ChRLLKNb.js → DocViewerPlugin-ZOnTIHLN.js} +3 -3
- package/src/ui/dist/assets/{GitDiffViewerPlugin-DgHfcved.js → GitDiffViewerPlugin-CQ7h1Djm.js} +830 -86
- package/src/ui/dist/assets/{ImageViewerPlugin-C89GZMBy.js → ImageViewerPlugin-GVS5MsnC.js} +5 -5
- package/src/ui/dist/assets/{LabCopilotPanel-BUfIwUcb.js → LabCopilotPanel-BZNv1JML.js} +10 -10
- package/src/ui/dist/assets/{LabPlugin-zvUmQUMq.js → LabPlugin-TWcJsdQA.js} +1 -1
- package/src/ui/dist/assets/{LatexPlugin-C1SSNuWp.js → LatexPlugin-DIjHiR2x.js} +7 -7
- package/src/ui/dist/assets/{MarkdownViewerPlugin-D2Mf5tU5.js → MarkdownViewerPlugin-D3ooGAH0.js} +4 -4
- package/src/ui/dist/assets/{MarketplacePlugin-CF4LgiS2.js → MarketplacePlugin-DfVfE9hN.js} +3 -3
- package/src/ui/dist/assets/{NotebookEditor-BM7Bgwlv.js → NotebookEditor-DDl0_Mc0.js} +1 -1
- package/src/ui/dist/assets/{index-Be0NAmh8.js → NotebookEditor-s8JhzuX1.js} +12 -155
- package/src/ui/dist/assets/{PdfLoader-Bc5qfD-Z.js → PdfLoader-C2Sf6SJM.js} +1 -1
- package/src/ui/dist/assets/{PdfMarkdownPlugin-sh1-IRcp.js → PdfMarkdownPlugin-CXFLoIsa.js} +3 -3
- package/src/ui/dist/assets/{PdfViewerPlugin-C_a7CpWG.js → PdfViewerPlugin-BYTmz2fK.js} +10 -10
- package/src/ui/dist/assets/{SearchPlugin-L4z3HcLf.js → SearchPlugin-CjWBI1O9.js} +1 -1
- package/src/ui/dist/assets/{Stepper-Dk4aQ3fN.js → Stepper-B0Dd8CxK.js} +1 -1
- package/src/ui/dist/assets/{TextViewerPlugin-BsNtlKVo.js → TextViewerPlugin-DdOBU3-S.js} +4 -4
- package/src/ui/dist/assets/{VNCViewer-BpeDcZ5_.js → VNCViewer-B8HGgLwQ.js} +9 -9
- package/src/ui/dist/assets/{bibtex-C4QI-bbj.js → bibtex-CKaefIN2.js} +1 -1
- package/src/ui/dist/assets/{code-DuMINRsg.js → code-BWAY76JP.js} +1 -1
- package/src/ui/dist/assets/{file-content-C3N-432K.js → file-content-C1NwU5oQ.js} +1 -1
- package/src/ui/dist/assets/{file-diff-panel-CffQ4ZMg.js → file-diff-panel-CywslwB9.js} +1 -1
- package/src/ui/dist/assets/{file-socket-CRH59PCO.js → file-socket-B4kzuOBQ.js} +1 -1
- package/src/ui/dist/assets/{file-utils-vYGtW2mI.js → file-utils-H2fjA46S.js} +1 -1
- package/src/ui/dist/assets/{image-DBVGaooo.js → image-D-NZM-6P.js} +1 -1
- package/src/ui/dist/assets/{index-B1P6hQRJ.js → index-7Chr1g9c.js} +3734 -1862
- package/src/ui/dist/assets/{index-DjSFDmgB.js → index-BdM1Gqfr.js} +2 -2
- package/src/ui/dist/assets/{index-BpjYH9Vg.js → index-CDxNdQdz.js} +1 -1
- package/src/ui/dist/assets/{index-Do9N28uB.css → index-DGIYDuTv.css} +163 -34
- package/src/ui/dist/assets/index-DHZJ_0TI.js +159 -0
- package/src/ui/dist/assets/{message-square-BsPDBhiY.js → message-square-BzjLiXir.js} +1 -1
- package/src/ui/dist/assets/{monaco-BTkdPojV.js → monaco-Cb2uKKe6.js} +1 -1
- package/src/ui/dist/assets/{popover-cWjCk-vc.js → popover-Bg72DGgT.js} +1 -1
- package/src/ui/dist/assets/{project-sync-CXn530xb.js → project-sync-Ce_0BglY.js} +1 -1
- package/src/ui/dist/assets/{sigma-04Jr12jg.js → sigma-DPaACDrh.js} +1 -1
- package/src/ui/dist/assets/{tooltip-BdVDl0G5.js → tooltip-C_mA6R0w.js} +1 -1
- package/src/ui/dist/assets/{trash-CB_GlQyC.js → trash-BvTgE5__.js} +1 -1
- package/src/ui/dist/assets/{useCliAccess-BL932NwS.js → useCliAccess-CgPeMOwP.js} +1 -1
- package/src/ui/dist/assets/{useFileDiffOverlay-B2WK7Tvq.js → useFileDiffOverlay-xPhz7P5B.js} +1 -1
- package/src/ui/dist/assets/{wrap-text-YC68g12z.js → wrap-text-C3Un3YQr.js} +1 -1
- package/src/ui/dist/assets/{zoom-out-C0RJvFiJ.js → zoom-out-BgxLa0Ri.js} +1 -1
- package/src/ui/dist/index.html +5 -2
- /package/src/ui/dist/assets/{index-CccQYZjX.css → NotebookEditor-CccQYZjX.css} +0 -0
|
@@ -448,7 +448,7 @@ def run_monitor(session_dir: Path) -> int:
|
|
|
448
448
|
_terminate_process(process, process_group_id)
|
|
449
449
|
stop_requested = True
|
|
450
450
|
|
|
451
|
-
if
|
|
451
|
+
if output_fd is not None and process.poll() is None:
|
|
452
452
|
cursor_payload = read_json(input_cursor_path, {}) or {}
|
|
453
453
|
offset = int(cursor_payload.get("offset") or 0)
|
|
454
454
|
input_entries = read_jsonl(input_path)
|
|
@@ -555,6 +555,7 @@ class BashExecService:
|
|
|
555
555
|
before_seq: int | None = None,
|
|
556
556
|
after_seq: int | None = None,
|
|
557
557
|
order: str = "asc",
|
|
558
|
+
prefer_visible: bool = False,
|
|
558
559
|
) -> tuple[list[dict[str, Any]], dict[str, Any]]:
|
|
559
560
|
if not self.meta_path(quest_root, bash_id).exists():
|
|
560
561
|
raise FileNotFoundError(f"Unknown bash session `{bash_id}`.")
|
|
@@ -579,9 +580,16 @@ class BashExecService:
|
|
|
579
580
|
entries = [entry for entry in entries if int(entry.get("seq") or 0) > normalized_after]
|
|
580
581
|
if normalized_before is not None:
|
|
581
582
|
entries = [entry for entry in entries if int(entry.get("seq") or 0) < normalized_before]
|
|
583
|
+
selection_pool = entries
|
|
584
|
+
if prefer_visible:
|
|
585
|
+
visible_entries = [
|
|
586
|
+
entry for entry in entries if str(entry.get("stream") or "") not in {"system", "prompt"}
|
|
587
|
+
]
|
|
588
|
+
if visible_entries:
|
|
589
|
+
selection_pool = visible_entries
|
|
582
590
|
normalized_limit = max(1, limit)
|
|
583
|
-
truncated = len(
|
|
584
|
-
selected =
|
|
591
|
+
truncated = len(selection_pool) > normalized_limit
|
|
592
|
+
selected = selection_pool[-normalized_limit:]
|
|
585
593
|
if order == "desc":
|
|
586
594
|
selected = list(reversed(selected))
|
|
587
595
|
tail_start_seq = int(selected[0].get("seq") or 0) if selected else None
|
|
@@ -1025,15 +1033,14 @@ class BashExecService:
|
|
|
1025
1033
|
if not normalized_data:
|
|
1026
1034
|
raise ValueError("terminal_input_required")
|
|
1027
1035
|
session = self.reconcile_session(quest_root, bash_id)
|
|
1028
|
-
if _normalize_string(session.get("kind")).lower() != "terminal":
|
|
1029
|
-
raise ValueError("not_terminal_session")
|
|
1030
1036
|
status = _normalize_string(session.get("status")).lower()
|
|
1031
1037
|
if status in TERMINAL_STATUSES:
|
|
1032
1038
|
raise ValueError("terminal_session_inactive")
|
|
1033
1039
|
runtime = self._terminal_runtime_manager.get_runtime(quest_root, bash_id)
|
|
1034
|
-
if runtime is None:
|
|
1040
|
+
if runtime is None and _normalize_string(session.get("kind")).lower() == "terminal":
|
|
1035
1041
|
raise ValueError("terminal_runtime_inactive")
|
|
1036
|
-
runtime
|
|
1042
|
+
if runtime is not None:
|
|
1043
|
+
runtime.write_input(normalized_data)
|
|
1037
1044
|
|
|
1038
1045
|
entry = {
|
|
1039
1046
|
"input_id": generate_id("tin"),
|
|
@@ -1081,9 +1088,10 @@ class BashExecService:
|
|
|
1081
1088
|
return True
|
|
1082
1089
|
|
|
1083
1090
|
def issue_terminal_attach_token(self, quest_root: Path, bash_id: str, *, ttl_seconds: int = 60) -> dict[str, Any]:
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1091
|
+
session = self.reconcile_session(quest_root, bash_id)
|
|
1092
|
+
status = _normalize_string(session.get("status")).lower()
|
|
1093
|
+
if status in TERMINAL_STATUSES:
|
|
1094
|
+
raise ValueError("terminal_session_inactive")
|
|
1087
1095
|
token = self._terminal_runtime_manager.issue_attach_token(
|
|
1088
1096
|
quest_root,
|
|
1089
1097
|
bash_id,
|
|
@@ -293,6 +293,20 @@ class QQRelayChannel(BaseChannel):
|
|
|
293
293
|
def matches_profile(item: dict[str, Any], profile_id: str) -> bool:
|
|
294
294
|
item_profile_id = str(item.get("profile_id") or "").strip()
|
|
295
295
|
return item_profile_id == profile_id or (not item_profile_id and len(profiles) == 1)
|
|
296
|
+
|
|
297
|
+
def count_profile_records(path: Path, profile_id: str) -> int:
|
|
298
|
+
total = 0
|
|
299
|
+
for raw in read_jsonl(path):
|
|
300
|
+
if not isinstance(raw, dict):
|
|
301
|
+
continue
|
|
302
|
+
record_profile_id = str(raw.get("profile_id") or "").strip()
|
|
303
|
+
if not record_profile_id:
|
|
304
|
+
parsed = parse_conversation_id(str(raw.get("conversation_id") or "").strip())
|
|
305
|
+
record_profile_id = str((parsed or {}).get("profile_id") or "").strip()
|
|
306
|
+
if record_profile_id == profile_id or (not record_profile_id and len(profiles) == 1):
|
|
307
|
+
total += 1
|
|
308
|
+
return total
|
|
309
|
+
|
|
296
310
|
profile_snapshots = []
|
|
297
311
|
for profile in profiles:
|
|
298
312
|
profile_id = str(profile.get("profile_id") or "").strip()
|
|
@@ -342,6 +356,9 @@ class QQRelayChannel(BaseChannel):
|
|
|
342
356
|
"discovered_targets": profile_targets,
|
|
343
357
|
"recent_conversations": profile_recent_conversations,
|
|
344
358
|
"bindings": profile_bindings,
|
|
359
|
+
"inbox_count": count_profile_records(self.inbox_path, profile_id),
|
|
360
|
+
"outbox_count": count_profile_records(self.outbox_path, profile_id),
|
|
361
|
+
"ignored_count": count_profile_records(self.ignored_path, profile_id),
|
|
345
362
|
"target_count": len(profile_targets),
|
|
346
363
|
"binding_count": len(profile_bindings),
|
|
347
364
|
"last_error": gateway_state.get("last_error") if isinstance(gateway_state, dict) else None,
|
|
@@ -282,6 +282,19 @@ class GenericRelayChannel(BaseChannel):
|
|
|
282
282
|
item_profile_id = str(item.get("profile_id") or "").strip()
|
|
283
283
|
return item_profile_id == profile_id or (not item_profile_id and len(profiles) == 1)
|
|
284
284
|
|
|
285
|
+
def count_profile_records(path: Path, profile_id: str) -> int:
|
|
286
|
+
total = 0
|
|
287
|
+
for raw in read_jsonl(path):
|
|
288
|
+
if not isinstance(raw, dict):
|
|
289
|
+
continue
|
|
290
|
+
record_profile_id = str(raw.get("profile_id") or "").strip()
|
|
291
|
+
if not record_profile_id:
|
|
292
|
+
parsed = parse_conversation_id(str(raw.get("conversation_id") or "").strip())
|
|
293
|
+
record_profile_id = str((parsed or {}).get("profile_id") or "").strip()
|
|
294
|
+
if record_profile_id == profile_id or (not record_profile_id and len(profiles) == 1):
|
|
295
|
+
total += 1
|
|
296
|
+
return total
|
|
297
|
+
|
|
285
298
|
profile_snapshots = []
|
|
286
299
|
for profile in profiles:
|
|
287
300
|
profile_id = str(profile.get("profile_id") or "").strip()
|
|
@@ -322,6 +335,9 @@ class GenericRelayChannel(BaseChannel):
|
|
|
322
335
|
"discovered_targets": profile_targets,
|
|
323
336
|
"recent_conversations": profile_recent_conversations,
|
|
324
337
|
"bindings": profile_bindings,
|
|
338
|
+
"inbox_count": count_profile_records(self.inbox_path, profile_id),
|
|
339
|
+
"outbox_count": count_profile_records(self.outbox_path, profile_id),
|
|
340
|
+
"ignored_count": count_profile_records(self.ignored_path, profile_id),
|
|
325
341
|
"target_count": len(profile_targets),
|
|
326
342
|
"binding_count": len(profile_bindings),
|
|
327
343
|
"last_error": profile_runtime_state.get("last_error") if isinstance(profile_runtime_state, dict) else None,
|
|
@@ -6,6 +6,7 @@ from pathlib import Path
|
|
|
6
6
|
CONFIG_NAMES = ("config", "runners", "connectors", "plugins", "mcp_servers")
|
|
7
7
|
REQUIRED_CONFIG_NAMES = ("config", "runners", "connectors")
|
|
8
8
|
OPTIONAL_CONFIG_NAMES = ("plugins", "mcp_servers")
|
|
9
|
+
SYSTEM_CONNECTOR_NAMES = ("qq", "telegram", "discord", "slack", "feishu", "whatsapp", "lingzhu")
|
|
9
10
|
|
|
10
11
|
|
|
11
12
|
@dataclass(frozen=True)
|
|
@@ -20,6 +21,10 @@ def config_filename(name: str) -> str:
|
|
|
20
21
|
return f"{name}.yaml"
|
|
21
22
|
|
|
22
23
|
|
|
24
|
+
def default_system_enabled_connectors() -> dict[str, bool]:
|
|
25
|
+
return {name: name == "qq" for name in SYSTEM_CONNECTOR_NAMES}
|
|
26
|
+
|
|
27
|
+
|
|
23
28
|
def default_config(home: Path) -> dict:
|
|
24
29
|
return {
|
|
25
30
|
"home": str(home),
|
|
@@ -65,6 +70,7 @@ def default_config(home: Path) -> dict:
|
|
|
65
70
|
"auto_ack": True,
|
|
66
71
|
"milestone_push": True,
|
|
67
72
|
"direct_chat_enabled": True,
|
|
73
|
+
"system_enabled": default_system_enabled_connectors(),
|
|
68
74
|
},
|
|
69
75
|
"cloud": {
|
|
70
76
|
"enabled": False,
|
|
@@ -37,6 +37,7 @@ from .models import (
|
|
|
37
37
|
OPTIONAL_CONFIG_NAMES,
|
|
38
38
|
REQUIRED_CONFIG_NAMES,
|
|
39
39
|
ConfigFileInfo,
|
|
40
|
+
SYSTEM_CONNECTOR_NAMES,
|
|
40
41
|
config_filename,
|
|
41
42
|
default_payload,
|
|
42
43
|
)
|
|
@@ -95,6 +96,33 @@ class ConfigManager:
|
|
|
95
96
|
def load_runners_config(self) -> dict:
|
|
96
97
|
return apply_runners_runtime_overrides(self.load_named_normalized("runners"))
|
|
97
98
|
|
|
99
|
+
def load_runtime_config(self) -> dict:
|
|
100
|
+
return self.load_named_normalized("config")
|
|
101
|
+
|
|
102
|
+
def system_connector_gates(self) -> dict[str, bool]:
|
|
103
|
+
config = self.load_runtime_config()
|
|
104
|
+
connectors = config.get("connectors") if isinstance(config.get("connectors"), dict) else {}
|
|
105
|
+
system_enabled = connectors.get("system_enabled") if isinstance(connectors.get("system_enabled"), dict) else {}
|
|
106
|
+
return {
|
|
107
|
+
name: self._coerce_bool(system_enabled.get(name), default=name == "qq")
|
|
108
|
+
for name in SYSTEM_CONNECTOR_NAMES
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
def system_enabled_connector_names(self) -> list[str]:
|
|
112
|
+
gates = self.system_connector_gates()
|
|
113
|
+
return [name for name in SYSTEM_CONNECTOR_NAMES if gates.get(name, False)]
|
|
114
|
+
|
|
115
|
+
def is_connector_system_enabled(self, name: str) -> bool:
|
|
116
|
+
normalized = str(name or "").strip().lower()
|
|
117
|
+
if not normalized:
|
|
118
|
+
return False
|
|
119
|
+
if normalized == "local":
|
|
120
|
+
return True
|
|
121
|
+
gates = self.system_connector_gates()
|
|
122
|
+
if normalized in gates:
|
|
123
|
+
return gates[normalized]
|
|
124
|
+
return True
|
|
125
|
+
|
|
98
126
|
def load_named_text(self, name: str, create_optional: bool = False) -> str:
|
|
99
127
|
path = self.path_for(name)
|
|
100
128
|
if create_optional and name in OPTIONAL_CONFIG_NAMES and not path.exists():
|
|
@@ -1047,6 +1075,12 @@ Use **Test** when the file exposes runtime dependencies.
|
|
|
1047
1075
|
checked_at = utc_now()
|
|
1048
1076
|
binary = str(config.get("binary") or "codex").strip() or "codex"
|
|
1049
1077
|
resolved_binary = resolve_runner_binary(binary, runner_name="codex")
|
|
1078
|
+
raw_reasoning_effort = config.get("model_reasoning_effort")
|
|
1079
|
+
reasoning_effort = (
|
|
1080
|
+
str(raw_reasoning_effort).strip()
|
|
1081
|
+
if raw_reasoning_effort is not None and str(raw_reasoning_effort).strip()
|
|
1082
|
+
else ("xhigh" if raw_reasoning_effort is None else None)
|
|
1083
|
+
)
|
|
1050
1084
|
details: dict[str, object] = {
|
|
1051
1085
|
"binary": binary,
|
|
1052
1086
|
"resolved_binary": resolved_binary,
|
|
@@ -1054,7 +1088,7 @@ Use **Test** when the file exposes runtime dependencies.
|
|
|
1054
1088
|
"model": str(config.get("model") or "gpt-5.4"),
|
|
1055
1089
|
"approval_policy": str(config.get("approval_policy") or "on-request"),
|
|
1056
1090
|
"sandbox_mode": str(config.get("sandbox_mode") or "workspace-write"),
|
|
1057
|
-
"reasoning_effort":
|
|
1091
|
+
"reasoning_effort": reasoning_effort,
|
|
1058
1092
|
"checked_at": checked_at,
|
|
1059
1093
|
}
|
|
1060
1094
|
if not resolved_binary:
|
|
@@ -1087,7 +1121,6 @@ Use **Test** when the file exposes runtime dependencies.
|
|
|
1087
1121
|
approval_policy = str(config.get("approval_policy") or "on-request").strip()
|
|
1088
1122
|
if approval_policy:
|
|
1089
1123
|
command.extend(["-c", f'approval_policy="{approval_policy}"'])
|
|
1090
|
-
reasoning_effort = str(config.get("model_reasoning_effort") or "xhigh").strip()
|
|
1091
1124
|
if reasoning_effort:
|
|
1092
1125
|
command.extend(["-c", f'model_reasoning_effort="{reasoning_effort}"'])
|
|
1093
1126
|
sandbox_mode = str(config.get("sandbox_mode") or "workspace-write").strip()
|
|
@@ -1378,6 +1411,8 @@ Use **Test** when the file exposes runtime dependencies.
|
|
|
1378
1411
|
normalized = self._deep_merge(defaults, payload)
|
|
1379
1412
|
bootstrap = normalized.get("bootstrap") if isinstance(normalized.get("bootstrap"), dict) else {}
|
|
1380
1413
|
raw_bootstrap = payload.get("bootstrap") if isinstance(payload.get("bootstrap"), dict) else {}
|
|
1414
|
+
connectors = normalized.get("connectors") if isinstance(normalized.get("connectors"), dict) else {}
|
|
1415
|
+
raw_connectors = payload.get("connectors") if isinstance(payload.get("connectors"), dict) else {}
|
|
1381
1416
|
default_locale = str(defaults.get("default_locale") or "").strip()
|
|
1382
1417
|
current_locale = str(normalized.get("default_locale") or "").strip()
|
|
1383
1418
|
locale_source = str(raw_bootstrap.get("locale_source") or "").strip().lower()
|
|
@@ -1401,6 +1436,25 @@ Use **Test** when the file exposes runtime dependencies.
|
|
|
1401
1436
|
bootstrap["locale_initialized_at"] = bootstrap.get("locale_initialized_at")
|
|
1402
1437
|
bootstrap["locale_initialized_browser_locale"] = bootstrap.get("locale_initialized_browser_locale")
|
|
1403
1438
|
normalized["bootstrap"] = bootstrap
|
|
1439
|
+
raw_system_enabled = raw_connectors.get("system_enabled") if isinstance(raw_connectors.get("system_enabled"), dict) else {}
|
|
1440
|
+
default_system_enabled = (
|
|
1441
|
+
defaults.get("connectors", {}).get("system_enabled")
|
|
1442
|
+
if isinstance(defaults.get("connectors"), dict)
|
|
1443
|
+
else {}
|
|
1444
|
+
)
|
|
1445
|
+
current_system_enabled = (
|
|
1446
|
+
connectors.get("system_enabled")
|
|
1447
|
+
if isinstance(connectors.get("system_enabled"), dict)
|
|
1448
|
+
else {}
|
|
1449
|
+
)
|
|
1450
|
+
connectors["system_enabled"] = {
|
|
1451
|
+
name: self._coerce_bool(
|
|
1452
|
+
raw_system_enabled.get(name, current_system_enabled.get(name)),
|
|
1453
|
+
default=bool(default_system_enabled.get(name, False)),
|
|
1454
|
+
)
|
|
1455
|
+
for name in SYSTEM_CONNECTOR_NAMES
|
|
1456
|
+
}
|
|
1457
|
+
normalized["connectors"] = connectors
|
|
1404
1458
|
return normalized
|
|
1405
1459
|
|
|
1406
1460
|
@staticmethod
|
|
@@ -1413,6 +1467,20 @@ Use **Test** when the file exposes runtime dependencies.
|
|
|
1413
1467
|
return False
|
|
1414
1468
|
return abs(initial - 1.0) < 1e-9 and abs(multiplier - 2.0) < 1e-9 and abs(max_backoff - 8.0) < 1e-9
|
|
1415
1469
|
|
|
1470
|
+
@staticmethod
|
|
1471
|
+
def _coerce_bool(value: object, *, default: bool = False) -> bool:
|
|
1472
|
+
if value is None:
|
|
1473
|
+
return default
|
|
1474
|
+
if isinstance(value, bool):
|
|
1475
|
+
return value
|
|
1476
|
+
if isinstance(value, str):
|
|
1477
|
+
normalized = value.strip().lower()
|
|
1478
|
+
if normalized in {"1", "true", "yes", "on", "y"}:
|
|
1479
|
+
return True
|
|
1480
|
+
if normalized in {"0", "false", "no", "off", "n", ""}:
|
|
1481
|
+
return False
|
|
1482
|
+
return bool(value)
|
|
1483
|
+
|
|
1416
1484
|
def _normalize_plugins_payload(self, payload: dict) -> dict:
|
|
1417
1485
|
normalized = deepcopy(payload)
|
|
1418
1486
|
if "load_paths" not in normalized and isinstance(normalized.get("search_paths"), list):
|