@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.
- package/README.md +8 -0
- package/assets/branding/logo-raster.png +0 -0
- package/bin/ds.js +134 -49
- package/docs/en/00_QUICK_START.md +2 -2
- package/docs/en/01_SETTINGS_REFERENCE.md +20 -4
- package/docs/en/03_QQ_CONNECTOR_GUIDE.md +19 -0
- package/docs/en/05_TUI_GUIDE.md +466 -96
- package/docs/en/10_WEIXIN_CONNECTOR_GUIDE.md +20 -0
- package/docs/en/14_PROMPT_SKILLS_AND_MCP_GUIDE.md +2 -0
- package/docs/en/16_TELEGRAM_CONNECTOR_GUIDE.md +134 -0
- package/docs/en/17_WHATSAPP_CONNECTOR_GUIDE.md +126 -0
- package/docs/en/18_FEISHU_CONNECTOR_GUIDE.md +136 -0
- package/docs/en/README.md +8 -0
- package/docs/zh/00_QUICK_START.md +2 -2
- package/docs/zh/01_SETTINGS_REFERENCE.md +20 -4
- package/docs/zh/03_QQ_CONNECTOR_GUIDE.md +19 -0
- package/docs/zh/05_TUI_GUIDE.md +465 -82
- package/docs/zh/10_WEIXIN_CONNECTOR_GUIDE.md +20 -0
- package/docs/zh/14_PROMPT_SKILLS_AND_MCP_GUIDE.md +2 -0
- package/docs/zh/16_TELEGRAM_CONNECTOR_GUIDE.md +134 -0
- package/docs/zh/17_WHATSAPP_CONNECTOR_GUIDE.md +126 -0
- package/docs/zh/18_FEISHU_CONNECTOR_GUIDE.md +136 -0
- package/docs/zh/README.md +8 -0
- package/install.sh +2 -0
- package/package.json +1 -1
- package/pyproject.toml +1 -1
- package/src/deepscientist/__init__.py +1 -1
- package/src/deepscientist/artifact/charts.py +567 -0
- package/src/deepscientist/artifact/guidance.py +50 -10
- package/src/deepscientist/artifact/metrics.py +228 -5
- package/src/deepscientist/artifact/schemas.py +3 -0
- package/src/deepscientist/artifact/service.py +4004 -538
- package/src/deepscientist/bash_exec/models.py +23 -0
- package/src/deepscientist/bash_exec/monitor.py +147 -67
- package/src/deepscientist/bash_exec/runtime.py +218 -156
- package/src/deepscientist/bash_exec/service.py +79 -64
- package/src/deepscientist/bash_exec/shells.py +87 -0
- package/src/deepscientist/bridges/connectors.py +51 -2
- package/src/deepscientist/config/models.py +6 -3
- package/src/deepscientist/config/service.py +7 -2
- package/src/deepscientist/connector/lingzhu_support.py +23 -4
- package/src/deepscientist/connector/weixin_support.py +122 -1
- package/src/deepscientist/daemon/api/handlers.py +75 -4
- package/src/deepscientist/daemon/api/router.py +1 -0
- package/src/deepscientist/daemon/app.py +869 -236
- package/src/deepscientist/doctor.py +51 -0
- package/src/deepscientist/file_lock.py +48 -0
- package/src/deepscientist/gitops/diff.py +167 -1
- package/src/deepscientist/mcp/server.py +331 -21
- package/src/deepscientist/process_control.py +161 -0
- package/src/deepscientist/prompts/builder.py +275 -491
- package/src/deepscientist/quest/service.py +2336 -145
- package/src/deepscientist/quest/stage_views.py +305 -29
- package/src/deepscientist/runners/base.py +2 -0
- package/src/deepscientist/runners/codex.py +88 -5
- package/src/deepscientist/runners/runtime_overrides.py +17 -1
- package/src/deepscientist/shared.py +6 -1
- package/src/prompts/contracts/shared_interaction.md +13 -4
- package/src/prompts/system.md +984 -1985
- package/src/skills/analysis-campaign/SKILL.md +31 -2
- package/src/skills/analysis-campaign/references/artifact-orchestration.md +1 -1
- package/src/skills/analysis-campaign/references/writing-facing-slice-examples.md +65 -0
- package/src/skills/baseline/SKILL.md +267 -994
- package/src/skills/baseline/references/baseline-checklist-template.md +21 -32
- package/src/skills/baseline/references/baseline-plan-template.md +41 -57
- package/src/skills/decision/SKILL.md +19 -2
- package/src/skills/experiment/SKILL.md +8 -2
- package/src/skills/finalize/SKILL.md +18 -0
- package/src/skills/idea/SKILL.md +78 -0
- package/src/skills/idea/references/idea-generation-playbook.md +100 -0
- package/src/skills/idea/references/outline-seeding-example.md +60 -0
- package/src/skills/intake-audit/SKILL.md +1 -1
- package/src/skills/optimize/SKILL.md +1644 -0
- package/src/skills/rebuttal/SKILL.md +2 -1
- package/src/skills/review/SKILL.md +2 -1
- package/src/skills/write/SKILL.md +80 -12
- package/src/skills/write/references/outline-evidence-contract-example.md +107 -0
- package/src/tui/dist/app/AppContainer.js +1445 -52
- package/src/tui/dist/components/Composer.js +1 -1
- package/src/tui/dist/components/ConfigScreen.js +190 -36
- package/src/tui/dist/components/GradientStatusText.js +1 -20
- package/src/tui/dist/components/InputPrompt.js +41 -32
- package/src/tui/dist/components/LoadingIndicator.js +1 -1
- package/src/tui/dist/components/Logo.js +61 -38
- package/src/tui/dist/components/MainContent.js +10 -3
- package/src/tui/dist/components/WelcomePanel.js +4 -12
- package/src/tui/dist/components/messages/AssistantMessage.js +1 -1
- package/src/tui/dist/components/messages/BashExecOperationMessage.js +3 -3
- package/src/tui/dist/components/messages/OperationMessage.js +1 -1
- package/src/tui/dist/index.js +28 -1
- package/src/tui/dist/layouts/DefaultAppLayout.js +3 -3
- package/src/tui/dist/lib/api.js +17 -0
- package/src/tui/dist/lib/connectors.js +261 -0
- package/src/tui/dist/semantic-colors.js +29 -19
- package/src/tui/package.json +1 -1
- package/src/ui/dist/assets/{AiManusChatView-CnJcXynW.js → AiManusChatView-DDjbFnbt.js} +12 -12
- package/src/ui/dist/assets/{AnalysisPlugin-DeyzPEhV.js → AnalysisPlugin-Yb5IdmaU.js} +1 -1
- package/src/ui/dist/assets/CliPlugin-e64sreyu.js +31037 -0
- package/src/ui/dist/assets/{CodeEditorPlugin-B-xicq1e.js → CodeEditorPlugin-C4D2TIkU.js} +8 -8
- package/src/ui/dist/assets/{CodeViewerPlugin-DT54ysXa.js → CodeViewerPlugin-BVoNZIvC.js} +5 -5
- package/src/ui/dist/assets/{DocViewerPlugin-DQtKT-VD.js → DocViewerPlugin-CLChbllo.js} +3 -3
- package/src/ui/dist/assets/{GitDiffViewerPlugin-hqHbCfnv.js → GitDiffViewerPlugin-C4xeFyFQ.js} +20 -20
- package/src/ui/dist/assets/{ImageViewerPlugin-OcVo33jV.js → ImageViewerPlugin-OiMUAcLi.js} +5 -5
- package/src/ui/dist/assets/{LabCopilotPanel-DdGwhEUV.js → LabCopilotPanel-BjD2ThQF.js} +11 -11
- package/src/ui/dist/assets/{LabPlugin-Ciz1gDaX.js → LabPlugin-DQPg-NrB.js} +2 -2
- package/src/ui/dist/assets/{LatexPlugin-BhmjNQRC.js → LatexPlugin-CI05XAV9.js} +7 -7
- package/src/ui/dist/assets/{MarkdownViewerPlugin-BzdVH9Bx.js → MarkdownViewerPlugin-DpeBLYZf.js} +4 -4
- package/src/ui/dist/assets/{MarketplacePlugin-DmyHspXt.js → MarketplacePlugin-DolE58Q2.js} +3 -3
- package/src/ui/dist/assets/{NotebookEditor-BTVYRGkm.js → NotebookEditor-7Qm2rSWD.js} +11 -11
- package/src/ui/dist/assets/{NotebookEditor-BMXKrDRk.js → NotebookEditor-C1kWaxKi.js} +1 -1
- package/src/ui/dist/assets/{PdfLoader-CvcjJHXv.js → PdfLoader-BfOHw8Zw.js} +1 -1
- package/src/ui/dist/assets/{PdfMarkdownPlugin-DW2ej8Vk.js → PdfMarkdownPlugin-BulDREv1.js} +2 -2
- package/src/ui/dist/assets/{PdfViewerPlugin-CmlDxbhU.js → PdfViewerPlugin-C-daaOaL.js} +10 -10
- package/src/ui/dist/assets/{SearchPlugin-DAjQZPSv.js → SearchPlugin-CjpaiJ3A.js} +1 -1
- package/src/ui/dist/assets/{TextViewerPlugin-C-nVAZb_.js → TextViewerPlugin-BxIyqPQC.js} +5 -5
- package/src/ui/dist/assets/{VNCViewer-D7-dIYon.js → VNCViewer-HAg9mF7M.js} +10 -10
- package/src/ui/dist/assets/{bot-C_G4WtNI.js → bot-0DYntytV.js} +1 -1
- package/src/ui/dist/assets/{code-Cd7WfiWq.js → code-B20Slj_w.js} +1 -1
- package/src/ui/dist/assets/{file-content-B57zsL9y.js → file-content-DT24KFma.js} +1 -1
- package/src/ui/dist/assets/{file-diff-panel-DVoheLFq.js → file-diff-panel-DK13YPql.js} +1 -1
- package/src/ui/dist/assets/{file-socket-B5kXFxZP.js → file-socket-B4T2o4nR.js} +1 -1
- package/src/ui/dist/assets/{image-LLOjkMHF.js → image-DSeR_sDS.js} +1 -1
- package/src/ui/dist/assets/{index-hOUOWbW2.js → index-BrFje2Uk.js} +2 -2
- package/src/ui/dist/assets/{index-Dxa2eYMY.js → index-BwRJaoTl.js} +1 -1
- package/src/ui/dist/assets/{index-CLQauncb.js → index-D_E4281X.js} +5418 -28620
- package/src/ui/dist/assets/{index-C3r2iGrp.js → index-DnYB3xb1.js} +12 -12
- package/src/ui/dist/assets/{index-BQG-1s2o.css → index-G7AcWcMu.css} +43 -2
- package/src/ui/dist/assets/{monaco-BGGAEii3.js → monaco-LExaAN3Y.js} +1 -1
- package/src/ui/dist/assets/{pdf-effect-queue-DlEr1_y5.js → pdf-effect-queue-BJk5okWJ.js} +1 -1
- package/src/ui/dist/assets/{popover-CWJbJuYY.js → popover-D3Gg_FoV.js} +1 -1
- package/src/ui/dist/assets/{project-sync-CRJiucYO.js → project-sync-C_ygLlVU.js} +1 -1
- package/src/ui/dist/assets/{select-CoHB7pvH.js → select-CpAK6uWm.js} +2 -2
- package/src/ui/dist/assets/{sigma-D5aJWR8J.js → sigma-DEccaSgk.js} +1 -1
- package/src/ui/dist/assets/{square-check-big-DUK_mnkS.js → square-check-big-uUfyVsbD.js} +1 -1
- package/src/ui/dist/assets/{trash-ChU3SEE3.js → trash-CXvwwSe8.js} +1 -1
- package/src/ui/dist/assets/{useCliAccess-BrJBV3tY.js → useCliAccess-Bnop4mgR.js} +1 -1
- package/src/ui/dist/assets/{useFileDiffOverlay-C2OQaVWc.js → useFileDiffOverlay-B8eUAX0I.js} +1 -1
- package/src/ui/dist/assets/{wrap-text-C7Qqh-om.js → wrap-text-9vbOBpkW.js} +1 -1
- package/src/ui/dist/assets/{zoom-out-rtX0FKya.js → zoom-out-BgVMmOW4.js} +1 -1
- package/src/ui/dist/index.html +2 -2
- package/uv.lock +1 -1
- package/src/ui/dist/assets/CliPlugin-CB1YODQn.js +0 -5905
|
@@ -62,6 +62,26 @@ def _field(label: str, value: object, *, tone: str = "default") -> dict[str, Any
|
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
|
|
65
|
+
def _selection_score_summary(value: object) -> str | None:
|
|
66
|
+
if not isinstance(value, dict):
|
|
67
|
+
return None
|
|
68
|
+
parts: list[str] = []
|
|
69
|
+
for key, raw in value.items():
|
|
70
|
+
name = str(key or "").strip()
|
|
71
|
+
if not name:
|
|
72
|
+
continue
|
|
73
|
+
if isinstance(raw, float):
|
|
74
|
+
rendered = f"{raw:.4f}".rstrip("0").rstrip(".")
|
|
75
|
+
else:
|
|
76
|
+
rendered = str(raw).strip()
|
|
77
|
+
if not rendered:
|
|
78
|
+
continue
|
|
79
|
+
parts.append(f"{name}={rendered}")
|
|
80
|
+
if len(parts) >= 4:
|
|
81
|
+
break
|
|
82
|
+
return " · ".join(parts) or None
|
|
83
|
+
|
|
84
|
+
|
|
65
85
|
def _evaluation_summary(value: object) -> dict[str, Any]:
|
|
66
86
|
if not isinstance(value, dict):
|
|
67
87
|
return {}
|
|
@@ -215,6 +235,8 @@ class QuestStageViewBuilder:
|
|
|
215
235
|
def build(self) -> dict[str, Any]:
|
|
216
236
|
selection_type = str(self.selection.get("selection_type") or "").strip()
|
|
217
237
|
self.stage_key = self._resolve_effective_stage_key()
|
|
238
|
+
if selection_type == "idea_candidate":
|
|
239
|
+
return self._build_idea_candidate()
|
|
218
240
|
if selection_type == "branch_node" and self.stage_key not in {"experiment", "analysis", "paper"}:
|
|
219
241
|
return self._build_branch()
|
|
220
242
|
if self.stage_key == "baseline":
|
|
@@ -280,6 +302,8 @@ class QuestStageViewBuilder:
|
|
|
280
302
|
normalized = [str(item).strip() for item in raw if str(item).strip()]
|
|
281
303
|
if normalized:
|
|
282
304
|
return normalized
|
|
305
|
+
if str(self.selection.get("selection_type") or "").strip() == "idea_candidate":
|
|
306
|
+
return self._idea_candidate_scope_paths()
|
|
283
307
|
if str(self.selection.get("selection_type") or "").strip() == "branch_node":
|
|
284
308
|
return self._branch_scope_paths()
|
|
285
309
|
defaults = {
|
|
@@ -344,37 +368,83 @@ class QuestStageViewBuilder:
|
|
|
344
368
|
return True
|
|
345
369
|
return False
|
|
346
370
|
|
|
347
|
-
def _path_in_quest(self, raw_path: object) -> tuple[Path, str] | None:
|
|
371
|
+
def _path_in_quest(self, raw_path: object) -> tuple[Path, str, str] | None:
|
|
348
372
|
text = str(raw_path or "").strip()
|
|
349
373
|
if not text:
|
|
350
374
|
return None
|
|
351
375
|
path = Path(text)
|
|
376
|
+
candidates: list[Path] = []
|
|
352
377
|
if not path.is_absolute():
|
|
353
|
-
|
|
378
|
+
for base in (self.workspace_root, self.quest_root):
|
|
379
|
+
try:
|
|
380
|
+
candidates.append((base / text).resolve())
|
|
381
|
+
except OSError:
|
|
382
|
+
continue
|
|
354
383
|
else:
|
|
355
384
|
try:
|
|
356
|
-
path
|
|
385
|
+
candidates.append(path.resolve())
|
|
357
386
|
except OSError:
|
|
358
387
|
return None
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
except ValueError:
|
|
388
|
+
|
|
389
|
+
if not candidates:
|
|
362
390
|
return None
|
|
363
|
-
|
|
391
|
+
|
|
392
|
+
seen: set[str] = set()
|
|
393
|
+
unique_candidates: list[Path] = []
|
|
394
|
+
for candidate in candidates:
|
|
395
|
+
key = str(candidate)
|
|
396
|
+
if key in seen:
|
|
397
|
+
continue
|
|
398
|
+
seen.add(key)
|
|
399
|
+
unique_candidates.append(candidate)
|
|
400
|
+
|
|
401
|
+
existing_candidates: list[Path] = []
|
|
402
|
+
missing_candidates: list[Path] = []
|
|
403
|
+
for candidate in unique_candidates:
|
|
404
|
+
try:
|
|
405
|
+
if candidate.exists():
|
|
406
|
+
existing_candidates.append(candidate)
|
|
407
|
+
else:
|
|
408
|
+
missing_candidates.append(candidate)
|
|
409
|
+
except OSError:
|
|
410
|
+
missing_candidates.append(candidate)
|
|
411
|
+
|
|
412
|
+
ordered = [*existing_candidates, *missing_candidates]
|
|
413
|
+
|
|
414
|
+
workspace_root = self.workspace_root.resolve()
|
|
415
|
+
quest_root = self.quest_root.resolve()
|
|
416
|
+
for candidate in ordered:
|
|
417
|
+
if candidate.exists():
|
|
418
|
+
try:
|
|
419
|
+
relative = candidate.relative_to(workspace_root).as_posix()
|
|
420
|
+
return candidate, relative, "path"
|
|
421
|
+
except ValueError:
|
|
422
|
+
pass
|
|
423
|
+
try:
|
|
424
|
+
relative = candidate.relative_to(quest_root).as_posix()
|
|
425
|
+
return candidate, relative, "questpath"
|
|
426
|
+
except ValueError:
|
|
427
|
+
pass
|
|
428
|
+
try:
|
|
429
|
+
relative = candidate.relative_to(workspace_root).as_posix()
|
|
430
|
+
return candidate, relative, "path"
|
|
431
|
+
except ValueError:
|
|
432
|
+
continue
|
|
433
|
+
return None
|
|
364
434
|
|
|
365
435
|
def _document_id_for_path(self, raw_path: object) -> str | None:
|
|
366
436
|
resolved = self._path_in_quest(raw_path)
|
|
367
437
|
if resolved is None:
|
|
368
438
|
return None
|
|
369
|
-
path, relative = resolved
|
|
439
|
+
path, relative, document_scope = resolved
|
|
370
440
|
if path.exists() and path.is_file():
|
|
371
|
-
return f"
|
|
441
|
+
return f"{document_scope}::{relative}"
|
|
372
442
|
return None
|
|
373
443
|
|
|
374
444
|
def _relative_path_or_raw(self, raw_path: object) -> str | None:
|
|
375
445
|
resolved = self._path_in_quest(raw_path)
|
|
376
446
|
if resolved is not None:
|
|
377
|
-
_path, relative = resolved
|
|
447
|
+
_path, relative, _document_scope = resolved
|
|
378
448
|
return relative
|
|
379
449
|
text = str(raw_path or "").strip()
|
|
380
450
|
return text or None
|
|
@@ -383,7 +453,7 @@ class QuestStageViewBuilder:
|
|
|
383
453
|
resolved = self._path_in_quest(raw_path)
|
|
384
454
|
if resolved is None:
|
|
385
455
|
return None
|
|
386
|
-
path, _relative = resolved
|
|
456
|
+
path, _relative, _document_scope = resolved
|
|
387
457
|
if not path.exists() or not path.is_file():
|
|
388
458
|
return None
|
|
389
459
|
try:
|
|
@@ -464,7 +534,7 @@ class QuestStageViewBuilder:
|
|
|
464
534
|
"exists": path.exists(),
|
|
465
535
|
"scope": "external",
|
|
466
536
|
}
|
|
467
|
-
path, relative = resolved
|
|
537
|
+
path, relative, document_scope = resolved
|
|
468
538
|
exists = path.exists()
|
|
469
539
|
kind = "directory" if (exists and path.is_dir()) or expected_kind == "directory" else "file"
|
|
470
540
|
scope = self.quest_service._classify_relative_scope(relative)[0]
|
|
@@ -474,7 +544,7 @@ class QuestStageViewBuilder:
|
|
|
474
544
|
"description": description,
|
|
475
545
|
"path": relative,
|
|
476
546
|
"absolute_path": str(path),
|
|
477
|
-
"document_id": f"
|
|
547
|
+
"document_id": f"{document_scope}::{relative}" if exists and path.is_file() else None,
|
|
478
548
|
"kind": kind,
|
|
479
549
|
"exists": exists,
|
|
480
550
|
"scope": scope,
|
|
@@ -508,30 +578,88 @@ class QuestStageViewBuilder:
|
|
|
508
578
|
resolved = self._path_in_quest(raw_path)
|
|
509
579
|
if resolved is None:
|
|
510
580
|
return None
|
|
511
|
-
_path, relative = resolved
|
|
581
|
+
_path, relative, _document_scope = resolved
|
|
512
582
|
return relative
|
|
513
583
|
|
|
514
|
-
def _paper_latex_root(
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
584
|
+
def _paper_latex_root(
|
|
585
|
+
self,
|
|
586
|
+
bundle_manifest: dict[str, Any],
|
|
587
|
+
*,
|
|
588
|
+
compile_report: dict[str, Any] | None = None,
|
|
589
|
+
) -> str | None:
|
|
590
|
+
for candidate in (
|
|
591
|
+
bundle_manifest.get("latex_root_path"),
|
|
592
|
+
(compile_report or {}).get("latex_root_path"),
|
|
593
|
+
(compile_report or {}).get("main_file_path"),
|
|
594
|
+
):
|
|
595
|
+
resolved = self._path_in_quest(candidate)
|
|
596
|
+
if resolved is None:
|
|
597
|
+
continue
|
|
598
|
+
path, relative, _document_scope = resolved
|
|
599
|
+
if path.is_dir():
|
|
600
|
+
return relative
|
|
601
|
+
if path.suffix.lower() == ".tex":
|
|
602
|
+
return PurePosixPath(relative).parent.as_posix()
|
|
518
603
|
paper_root = self._paper_root()
|
|
519
604
|
for candidate in (paper_root / "latex", paper_root / "tex"):
|
|
520
605
|
if candidate.exists():
|
|
521
|
-
|
|
606
|
+
try:
|
|
607
|
+
return candidate.relative_to(self.workspace_root.resolve()).as_posix()
|
|
608
|
+
except ValueError:
|
|
609
|
+
return candidate.relative_to(self.quest_root).as_posix()
|
|
522
610
|
return None
|
|
523
611
|
|
|
524
|
-
def _paper_main_tex(
|
|
612
|
+
def _paper_main_tex(
|
|
613
|
+
self,
|
|
614
|
+
latex_root_rel: str | None,
|
|
615
|
+
*,
|
|
616
|
+
bundle_manifest: dict[str, Any] | None = None,
|
|
617
|
+
compile_report: dict[str, Any] | None = None,
|
|
618
|
+
) -> str | None:
|
|
619
|
+
for candidate in (
|
|
620
|
+
(compile_report or {}).get("main_file_path"),
|
|
621
|
+
bundle_manifest.get("main_tex_path") if isinstance(bundle_manifest, dict) else None,
|
|
622
|
+
bundle_manifest.get("latex_root_path") if isinstance(bundle_manifest, dict) else None,
|
|
623
|
+
(compile_report or {}).get("latex_root_path"),
|
|
624
|
+
):
|
|
625
|
+
resolved = self._path_in_quest(candidate)
|
|
626
|
+
if resolved is None:
|
|
627
|
+
continue
|
|
628
|
+
path, relative, _document_scope = resolved
|
|
629
|
+
if path.suffix.lower() == ".tex":
|
|
630
|
+
return relative
|
|
631
|
+
if path.is_dir():
|
|
632
|
+
preferred = path / "main.tex"
|
|
633
|
+
if preferred.exists():
|
|
634
|
+
nested = self._path_in_quest(preferred)
|
|
635
|
+
if nested is not None:
|
|
636
|
+
_resolved_path, nested_relative, _nested_scope = nested
|
|
637
|
+
return nested_relative
|
|
525
638
|
if not latex_root_rel:
|
|
526
639
|
return None
|
|
527
|
-
latex_root = self.
|
|
640
|
+
latex_root = (self.workspace_root / latex_root_rel).resolve()
|
|
641
|
+
if not latex_root.exists():
|
|
642
|
+
latex_root = (self.quest_root / latex_root_rel).resolve()
|
|
643
|
+
if latex_root.is_file() and latex_root.suffix.lower() == ".tex":
|
|
644
|
+
nested = self._path_in_quest(latex_root)
|
|
645
|
+
if nested is not None:
|
|
646
|
+
_resolved_path, nested_relative, _nested_scope = nested
|
|
647
|
+
return nested_relative
|
|
648
|
+
return None
|
|
528
649
|
preferred = latex_root / "main.tex"
|
|
529
650
|
if preferred.exists():
|
|
530
|
-
|
|
651
|
+
nested = self._path_in_quest(preferred)
|
|
652
|
+
if nested is not None:
|
|
653
|
+
_resolved_path, nested_relative, _nested_scope = nested
|
|
654
|
+
return nested_relative
|
|
531
655
|
candidates = sorted(latex_root.glob("*.tex"))
|
|
532
656
|
if not candidates:
|
|
533
657
|
return None
|
|
534
|
-
|
|
658
|
+
nested = self._path_in_quest(candidates[0])
|
|
659
|
+
if nested is None:
|
|
660
|
+
return None
|
|
661
|
+
_resolved_path, nested_relative, _nested_scope = nested
|
|
662
|
+
return nested_relative
|
|
535
663
|
|
|
536
664
|
def _paper_pdf_candidates(
|
|
537
665
|
self,
|
|
@@ -589,6 +717,13 @@ class QuestStageViewBuilder:
|
|
|
589
717
|
"artifacts/reports",
|
|
590
718
|
]
|
|
591
719
|
|
|
720
|
+
def _idea_candidate_scope_paths(self) -> list[str]:
|
|
721
|
+
candidate_id = str(self.selection.get("selection_ref") or self.selection.get("idea_id") or "").strip()
|
|
722
|
+
return [
|
|
723
|
+
*( [f"memory/ideas/_candidates/{candidate_id}"] if candidate_id else []),
|
|
724
|
+
"artifacts/reports",
|
|
725
|
+
]
|
|
726
|
+
|
|
592
727
|
def _experiment_scope_paths(self, run_id: str | None) -> list[str]:
|
|
593
728
|
return [
|
|
594
729
|
*( [f"experiments/main/{run_id}"] if run_id else []),
|
|
@@ -813,6 +948,25 @@ class QuestStageViewBuilder:
|
|
|
813
948
|
items.append(item)
|
|
814
949
|
return items
|
|
815
950
|
|
|
951
|
+
def _idea_candidate_stage_items(self) -> list[dict[str, Any]]:
|
|
952
|
+
candidate_id = str(self.selection.get("selection_ref") or self.selection.get("idea_id") or "").strip()
|
|
953
|
+
if not candidate_id:
|
|
954
|
+
return []
|
|
955
|
+
items: list[dict[str, Any]] = []
|
|
956
|
+
for item in self.artifacts:
|
|
957
|
+
payload = self._payload(item)
|
|
958
|
+
if str(payload.get("kind") or "").strip() != "idea":
|
|
959
|
+
continue
|
|
960
|
+
if str(payload.get("idea_id") or "").strip() != candidate_id:
|
|
961
|
+
continue
|
|
962
|
+
flow_type = str(payload.get("flow_type") or "").strip()
|
|
963
|
+
protocol_step = str(payload.get("protocol_step") or "").strip()
|
|
964
|
+
details = dict(payload.get("details") or {}) if isinstance(payload.get("details"), dict) else {}
|
|
965
|
+
submission_mode = str(details.get("submission_mode") or payload.get("submission_mode") or "").strip().lower()
|
|
966
|
+
if flow_type == "idea_submission" and (protocol_step == "candidate" or submission_mode == "candidate"):
|
|
967
|
+
items.append(item)
|
|
968
|
+
return items
|
|
969
|
+
|
|
816
970
|
def _build_idea(self) -> dict[str, Any]:
|
|
817
971
|
idea_items = self._idea_stage_items()
|
|
818
972
|
latest = idea_items[-1] if idea_items else None
|
|
@@ -840,6 +994,8 @@ class QuestStageViewBuilder:
|
|
|
840
994
|
draft_md_rel_path = self._relative_path_or_raw(draft_md_path)
|
|
841
995
|
draft_markdown = self._markdown_body_for_path(draft_md_path)
|
|
842
996
|
lineage_intent = str(payload.get("lineage_intent") or details.get("lineage_intent") or "").strip() or None
|
|
997
|
+
selection_scores = details.get("selection_scores")
|
|
998
|
+
selection_score_summary = _selection_score_summary(selection_scores)
|
|
843
999
|
note = (
|
|
844
1000
|
str(payload.get("summary") or payload.get("reason") or "").strip()
|
|
845
1001
|
or "No durable idea submission has been recorded yet."
|
|
@@ -883,6 +1039,11 @@ class QuestStageViewBuilder:
|
|
|
883
1039
|
_field("Problem", details.get("problem") or "Not recorded"),
|
|
884
1040
|
_field("Hypothesis", details.get("hypothesis") or "Not recorded"),
|
|
885
1041
|
_field("Mechanism", details.get("mechanism") or "Not recorded"),
|
|
1042
|
+
_field("Method Brief", details.get("method_brief") or "Not recorded"),
|
|
1043
|
+
_field("Selection Scores", selection_score_summary or "Not recorded"),
|
|
1044
|
+
_field("Mechanism Family", details.get("mechanism_family") or "Not recorded"),
|
|
1045
|
+
_field("Change Layer", details.get("change_layer") or "Not recorded"),
|
|
1046
|
+
_field("Source Lens", details.get("source_lens") or "Not recorded"),
|
|
886
1047
|
_field("Expected Gain", details.get("expected_gain") or "Not recorded"),
|
|
887
1048
|
_field("Risks", details.get("risks") or "Not recorded"),
|
|
888
1049
|
_field("Evidence Paths", details.get("evidence_paths") or "Not recorded"),
|
|
@@ -905,6 +1066,11 @@ class QuestStageViewBuilder:
|
|
|
905
1066
|
"problem": details.get("problem"),
|
|
906
1067
|
"hypothesis": details.get("hypothesis"),
|
|
907
1068
|
"mechanism": details.get("mechanism"),
|
|
1069
|
+
"method_brief": details.get("method_brief"),
|
|
1070
|
+
"selection_scores": selection_scores or None,
|
|
1071
|
+
"mechanism_family": details.get("mechanism_family"),
|
|
1072
|
+
"change_layer": details.get("change_layer"),
|
|
1073
|
+
"source_lens": details.get("source_lens"),
|
|
908
1074
|
"expected_gain": details.get("expected_gain"),
|
|
909
1075
|
"risks": details.get("risks") or [],
|
|
910
1076
|
"evidence_paths": details.get("evidence_paths") or [],
|
|
@@ -924,6 +1090,101 @@ class QuestStageViewBuilder:
|
|
|
924
1090
|
subviews=["overview", "details", "draft"] if draft_markdown else ["overview", "details"],
|
|
925
1091
|
)
|
|
926
1092
|
|
|
1093
|
+
def _build_idea_candidate(self) -> dict[str, Any]:
|
|
1094
|
+
candidate_items = self._idea_candidate_stage_items()
|
|
1095
|
+
latest = candidate_items[-1] if candidate_items else None
|
|
1096
|
+
payload = self._payload(latest or {})
|
|
1097
|
+
details = dict(payload.get("details") or {}) if isinstance(payload.get("details"), dict) else {}
|
|
1098
|
+
candidate_id = str(self.selection.get("selection_ref") or payload.get("idea_id") or "candidate").strip() or "candidate"
|
|
1099
|
+
title_text = (
|
|
1100
|
+
str(details.get("title") or self.selection.get("label") or candidate_id).strip() or candidate_id
|
|
1101
|
+
)
|
|
1102
|
+
paths = dict(payload.get("paths") or {}) if isinstance(payload.get("paths"), dict) else {}
|
|
1103
|
+
candidate_root = paths.get("candidate_root") or str(self.quest_root / "memory" / "ideas" / "_candidates" / candidate_id)
|
|
1104
|
+
idea_md_path = paths.get("idea_md") or str(Path(candidate_root) / "idea.md")
|
|
1105
|
+
draft_md_path = paths.get("idea_draft_md") or details.get("idea_draft_path") or str(Path(candidate_root) / "draft.md")
|
|
1106
|
+
idea_markdown = self._markdown_body_for_path(idea_md_path)
|
|
1107
|
+
draft_markdown = self._markdown_body_for_path(draft_md_path)
|
|
1108
|
+
idea_md_rel_path = self._relative_path_or_raw(idea_md_path)
|
|
1109
|
+
draft_md_rel_path = self._relative_path_or_raw(draft_md_path)
|
|
1110
|
+
candidate_root_rel_path = self._relative_path_or_raw(candidate_root)
|
|
1111
|
+
selection_scores = details.get("selection_scores")
|
|
1112
|
+
selection_score_summary = _selection_score_summary(selection_scores)
|
|
1113
|
+
note = (
|
|
1114
|
+
str(payload.get("summary") or payload.get("reason") or self.selection.get("summary") or "").strip()
|
|
1115
|
+
or "No durable candidate brief summary has been recorded yet."
|
|
1116
|
+
)
|
|
1117
|
+
lineage_intent = str(payload.get("lineage_intent") or details.get("lineage_intent") or "").strip() or None
|
|
1118
|
+
parent_branch = str(payload.get("parent_branch") or details.get("parent_branch") or self.selection.get("branch_name") or "").strip() or None
|
|
1119
|
+
foundation_reason = str(payload.get("foundation_reason") or details.get("foundation_reason") or "").strip() or None
|
|
1120
|
+
return self._base_payload(
|
|
1121
|
+
title=f"Candidate Brief · {title_text}",
|
|
1122
|
+
note=note,
|
|
1123
|
+
status=str(payload.get("status") or "candidate").strip() or "candidate",
|
|
1124
|
+
tags=[
|
|
1125
|
+
"candidate-brief",
|
|
1126
|
+
details.get("mechanism_family") or "",
|
|
1127
|
+
details.get("change_layer") or "",
|
|
1128
|
+
details.get("source_lens") or "",
|
|
1129
|
+
lineage_intent or "",
|
|
1130
|
+
],
|
|
1131
|
+
overview=[
|
|
1132
|
+
_field("Candidate ID", candidate_id),
|
|
1133
|
+
_field("Parent Branch", parent_branch or "Not recorded"),
|
|
1134
|
+
_field("Next Target", details.get("next_target") or "optimize"),
|
|
1135
|
+
_field("Candidate Root", candidate_root_rel_path or candidate_root),
|
|
1136
|
+
],
|
|
1137
|
+
key_facts=[
|
|
1138
|
+
_field("Problem", details.get("problem") or "Not recorded"),
|
|
1139
|
+
_field("Hypothesis", details.get("hypothesis") or "Not recorded"),
|
|
1140
|
+
_field("Mechanism", details.get("mechanism") or "Not recorded"),
|
|
1141
|
+
_field("Method Brief", details.get("method_brief") or "Not recorded"),
|
|
1142
|
+
_field("Selection Scores", selection_score_summary or "Not recorded"),
|
|
1143
|
+
_field("Mechanism Family", details.get("mechanism_family") or "Not recorded"),
|
|
1144
|
+
_field("Change Layer", details.get("change_layer") or "Not recorded"),
|
|
1145
|
+
_field("Source Lens", details.get("source_lens") or "Not recorded"),
|
|
1146
|
+
_field("Expected Gain", details.get("expected_gain") or "Not recorded"),
|
|
1147
|
+
_field("Foundation Reason", foundation_reason or "Not recorded"),
|
|
1148
|
+
],
|
|
1149
|
+
key_files=self._dedupe_files(
|
|
1150
|
+
[
|
|
1151
|
+
self._file_entry(candidate_root, label="Candidate Root", description="Branchless candidate brief workspace.", expected_kind="directory"),
|
|
1152
|
+
self._file_entry(idea_md_path, label="Candidate Markdown", description="Durable candidate brief document."),
|
|
1153
|
+
self._file_entry(draft_md_path, label="Candidate Draft", description="Long-form candidate brief draft."),
|
|
1154
|
+
]
|
|
1155
|
+
),
|
|
1156
|
+
history=self._artifact_history(candidate_items),
|
|
1157
|
+
details={
|
|
1158
|
+
"idea": {
|
|
1159
|
+
"idea_id": candidate_id,
|
|
1160
|
+
"title": title_text,
|
|
1161
|
+
"problem": details.get("problem"),
|
|
1162
|
+
"hypothesis": details.get("hypothesis"),
|
|
1163
|
+
"mechanism": details.get("mechanism"),
|
|
1164
|
+
"method_brief": details.get("method_brief"),
|
|
1165
|
+
"selection_scores": selection_scores or None,
|
|
1166
|
+
"mechanism_family": details.get("mechanism_family"),
|
|
1167
|
+
"change_layer": details.get("change_layer"),
|
|
1168
|
+
"source_lens": details.get("source_lens"),
|
|
1169
|
+
"expected_gain": details.get("expected_gain"),
|
|
1170
|
+
"next_target": details.get("next_target") or "optimize",
|
|
1171
|
+
"lineage_intent": lineage_intent,
|
|
1172
|
+
"parent_branch": parent_branch,
|
|
1173
|
+
"candidate_root": candidate_root_rel_path or candidate_root,
|
|
1174
|
+
"idea_path": idea_md_rel_path,
|
|
1175
|
+
"idea_markdown": idea_markdown,
|
|
1176
|
+
"draft_path": draft_md_rel_path,
|
|
1177
|
+
"draft_markdown": draft_markdown,
|
|
1178
|
+
"decision_reason": payload.get("reason"),
|
|
1179
|
+
},
|
|
1180
|
+
"latest_artifact": self._artifact_detail(latest, payload),
|
|
1181
|
+
},
|
|
1182
|
+
lineage_intent=lineage_intent,
|
|
1183
|
+
idea_draft_path=draft_md_rel_path,
|
|
1184
|
+
draft_available=bool(draft_markdown),
|
|
1185
|
+
subviews=["overview", "details", "draft"] if draft_markdown else ["overview", "details"],
|
|
1186
|
+
)
|
|
1187
|
+
|
|
927
1188
|
def _build_branch(self) -> dict[str, Any]:
|
|
928
1189
|
idea_items = [
|
|
929
1190
|
item
|
|
@@ -946,6 +1207,8 @@ class QuestStageViewBuilder:
|
|
|
946
1207
|
idea_title = str(latest_idea_details.get("title") or "").strip() or None
|
|
947
1208
|
idea_problem = str(latest_idea_details.get("problem") or "").strip() or None
|
|
948
1209
|
next_target = str(latest_idea_details.get("next_target") or "").strip() or None
|
|
1210
|
+
selection_scores = latest_idea_details.get("selection_scores")
|
|
1211
|
+
selection_score_summary = _selection_score_summary(selection_scores)
|
|
949
1212
|
lineage_intent = str(
|
|
950
1213
|
latest_idea_payload.get("lineage_intent")
|
|
951
1214
|
or latest_idea_details.get("lineage_intent")
|
|
@@ -1065,6 +1328,11 @@ class QuestStageViewBuilder:
|
|
|
1065
1328
|
key_facts=[
|
|
1066
1329
|
_field("Idea Title", idea_title or "Not recorded"),
|
|
1067
1330
|
_field("Idea Problem", idea_problem or "Not recorded"),
|
|
1331
|
+
_field("Method Brief", latest_idea_details.get("method_brief") or "Not recorded"),
|
|
1332
|
+
_field("Selection Scores", selection_score_summary or "Not recorded"),
|
|
1333
|
+
_field("Mechanism Family", latest_idea_details.get("mechanism_family") or "Not recorded"),
|
|
1334
|
+
_field("Change Layer", latest_idea_details.get("change_layer") or "Not recorded"),
|
|
1335
|
+
_field("Source Lens", latest_idea_details.get("source_lens") or "Not recorded"),
|
|
1068
1336
|
_field("Foundation", foundation_label or "Current head"),
|
|
1069
1337
|
_field("Foundation Reason", foundation_reason or "Not recorded"),
|
|
1070
1338
|
_field("Next Target", next_target or "Not recorded"),
|
|
@@ -1120,6 +1388,11 @@ class QuestStageViewBuilder:
|
|
|
1120
1388
|
"lineage_intent": lineage_intent,
|
|
1121
1389
|
"idea_title": idea_title,
|
|
1122
1390
|
"idea_problem": idea_problem,
|
|
1391
|
+
"method_brief": latest_idea_details.get("method_brief"),
|
|
1392
|
+
"selection_scores": selection_scores or None,
|
|
1393
|
+
"mechanism_family": latest_idea_details.get("mechanism_family"),
|
|
1394
|
+
"change_layer": latest_idea_details.get("change_layer"),
|
|
1395
|
+
"source_lens": latest_idea_details.get("source_lens"),
|
|
1123
1396
|
"next_target": next_target,
|
|
1124
1397
|
"idea_draft_path": idea_draft_rel_path,
|
|
1125
1398
|
"idea_draft_markdown": idea_draft_markdown,
|
|
@@ -1460,10 +1733,11 @@ class QuestStageViewBuilder:
|
|
|
1460
1733
|
},
|
|
1461
1734
|
)
|
|
1462
1735
|
|
|
1463
|
-
def _paper_files(self) -> list[dict[str, Any]]:
|
|
1736
|
+
def _paper_files(self, *, compile_report: dict[str, Any] | None = None) -> list[dict[str, Any]]:
|
|
1464
1737
|
bundle_manifest = self._paper_bundle_manifest()
|
|
1465
|
-
|
|
1466
|
-
|
|
1738
|
+
compile_report = compile_report if isinstance(compile_report, dict) else {}
|
|
1739
|
+
latex_root_rel = self._paper_latex_root(bundle_manifest, compile_report=compile_report)
|
|
1740
|
+
main_tex_rel = self._paper_main_tex(latex_root_rel, bundle_manifest=bundle_manifest, compile_report=compile_report)
|
|
1467
1741
|
pdf_candidates = self._paper_pdf_candidates(bundle_manifest, main_tex_rel=main_tex_rel)
|
|
1468
1742
|
paper_root = self._paper_root()
|
|
1469
1743
|
open_source_root = self._open_source_root()
|
|
@@ -1474,11 +1748,13 @@ class QuestStageViewBuilder:
|
|
|
1474
1748
|
for path in candidates
|
|
1475
1749
|
],
|
|
1476
1750
|
self._file_entry(paper_root / "selected_outline.json", label="Selected Outline", description="Chosen paper outline."),
|
|
1751
|
+
self._file_entry(paper_root / "outline" / "manifest.json", label="Outline Manifest", description="Author-facing paper outline manifest."),
|
|
1477
1752
|
self._file_entry(paper_root / "outline_selection.md", label="Outline Selection Note", description="Outline selection rationale."),
|
|
1478
1753
|
self._file_entry(paper_root / "draft.md", label="Draft Markdown", description="Current paper draft."),
|
|
1479
1754
|
self._file_entry(paper_root / "writing_plan.md", label="Writing Plan", description="Paper writing plan."),
|
|
1480
1755
|
self._file_entry(paper_root / "references.bib", label="References", description="Bibliography file."),
|
|
1481
1756
|
self._file_entry(paper_root / "claim_evidence_map.json", label="Claim-Evidence Map", description="Claim to evidence mapping."),
|
|
1757
|
+
self._file_entry(paper_root / "paper_line_state.json", label="Paper Line State", description="Derived summary state for the active paper line."),
|
|
1482
1758
|
self._file_entry(paper_root / "baseline_inventory.json", label="Baseline Inventory", description="Canonical and supplementary baseline inventory for writing."),
|
|
1483
1759
|
self._file_entry(paper_root / "build" / "compile_report.json", label="Compile Report", description="Paper build/compile report."),
|
|
1484
1760
|
self._file_entry(paper_root / "paper_bundle_manifest.json", label="Bundle Manifest", description="Final paper bundle manifest."),
|
|
@@ -1537,8 +1813,8 @@ class QuestStageViewBuilder:
|
|
|
1537
1813
|
if not isinstance(compile_report, dict):
|
|
1538
1814
|
compile_report = {}
|
|
1539
1815
|
bundle_manifest = self._paper_bundle_manifest()
|
|
1540
|
-
latex_root_rel = self._paper_latex_root(bundle_manifest)
|
|
1541
|
-
main_tex_rel = self._paper_main_tex(latex_root_rel)
|
|
1816
|
+
latex_root_rel = self._paper_latex_root(bundle_manifest, compile_report=compile_report)
|
|
1817
|
+
main_tex_rel = self._paper_main_tex(latex_root_rel, bundle_manifest=bundle_manifest, compile_report=compile_report)
|
|
1542
1818
|
references_bib = read_text(paper_root / "references.bib", "")
|
|
1543
1819
|
references_count = sum(1 for line in references_bib.splitlines() if line.lstrip().startswith("@"))
|
|
1544
1820
|
pdf_paths = self._paper_pdf_candidates(bundle_manifest, main_tex_rel=main_tex_rel)
|
|
@@ -1577,7 +1853,7 @@ class QuestStageViewBuilder:
|
|
|
1577
1853
|
_field("LaTeX Root", latex_root_rel or "Not recorded"),
|
|
1578
1854
|
_field("Main TeX", main_tex_rel or "Not recorded"),
|
|
1579
1855
|
],
|
|
1580
|
-
key_files=self._paper_files(),
|
|
1856
|
+
key_files=self._paper_files(compile_report=compile_report),
|
|
1581
1857
|
history=self._artifact_history(paper_items),
|
|
1582
1858
|
details={
|
|
1583
1859
|
"paper": {
|
|
@@ -17,6 +17,8 @@ class RunRequest:
|
|
|
17
17
|
approval_policy: str
|
|
18
18
|
sandbox_mode: str
|
|
19
19
|
turn_reason: str = "user_message"
|
|
20
|
+
turn_intent: str = "continue_stage"
|
|
21
|
+
turn_mode: str = "stage_execution"
|
|
20
22
|
reasoning_effort: str | None = None
|
|
21
23
|
turn_id: str | None = None
|
|
22
24
|
attempt_index: int = 1
|