From b30588d416b56718a2f15134e4b6fa0214f7a3a2 Mon Sep 17 00:00:00 2001 From: LoopX Agent <337587101+loopx-agent@users.noreply.github.com> Date: Mon, 26 Oct 2026 00:09:08 +0800 Subject: [PATCH] Keep saved research readable when execution is read-only Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com> --- .../goal-research-analysis.tsx | 23 ++++++++------ .../readonly-analysis-after-1280.png | Bin 0 -> 131063 bytes .../readonly-analysis-after-390.png | Bin 0 -> 68449 bytes .../readonly-analysis-before-1280.png | Bin 0 -> 129881 bytes .../readonly-analysis-before-390.png | Bin 0 -> 66100 bytes .../readonly-analysis-help-390.png | Bin 0 -> 89792 bytes docs/development/design.md | 7 +++++ .../research-history.mjs | 29 ++++++++++++++++++ 8 files changed, 49 insertions(+), 10 deletions(-) create mode 100644 docs/development/assets/research-report-episodes/readonly-analysis-after-1280.png create mode 100644 docs/development/assets/research-report-episodes/readonly-analysis-after-390.png create mode 100644 docs/development/assets/research-report-episodes/readonly-analysis-before-1280.png create mode 100644 docs/development/assets/research-report-episodes/readonly-analysis-before-390.png create mode 100644 docs/development/assets/research-report-episodes/readonly-analysis-help-390.png diff --git a/apps/presentation/dashboard/src/features/personal-workspace/goal-research-analysis.tsx b/apps/presentation/dashboard/src/features/personal-workspace/goal-research-analysis.tsx index 3958880b0e..ddae7532b6 100644 --- a/apps/presentation/dashboard/src/features/personal-workspace/goal-research-analysis.tsx +++ b/apps/presentation/dashboard/src/features/personal-workspace/goal-research-analysis.tsx @@ -1,5 +1,5 @@ import {useCallback, useEffect, useLayoutEffect, useRef, useState} from "react"; -import {ArrowLeft, ArrowUpRight, ChevronDown, CircleHelp, LoaderCircle, RefreshCw, Sparkles, TriangleAlert, X} from "lucide-react"; +import {ArrowLeft, ArrowUpRight, ChevronDown, CircleHelp, Eye, LoaderCircle, RefreshCw, Sparkles, TriangleAlert, X} from "lucide-react"; import type {AssessmentCitation, TrajectoryAssessment, TrajectoryEpisode, TrajectoryOutputLanguage, TrajectoryContinuation} from "../../../../../../loopx/control_plane/capabilities/trajectory_assessment_types.js"; import {ResearchProgress, episodeQuestionGroups, metricCopy, verdictLabel, type ResearchProgressPack} from "./goal-research-progress"; import {requestJson} from "../../data/chat"; @@ -209,7 +209,7 @@ export function ResearchAnalysisButton({analysis: a}: {analysis: Controller}) { // explanation is optional; cached results and unavailable states are reads. const readOnly = unavailable || a.readError || a.pack?.result || a.pending || a.recordedRunning; return
{reasonCopy(a.pack?.reason_code ?? a.snapshot?.catalog.reason_code, a.zh)}
: null} {a.pack ?{a.zh ? "分析请求设置(固定):" : "Fixed analysis request: "}{a.pack.config.model} / {a.pack.config.effort} / {a.pack.config.service_tier}
: null} @@ -217,7 +217,7 @@ export function ResearchAnalysisButton({analysis: a}: {analysis: Controller}) { ? (a.zh ? "本次分析同时生成固定历史快照的中文展示译文;问题、决策、结论与历史正文按字段核对版本,缺失项保留原文。" : "This analysis also translates the frozen history snapshot: questions, decisions, findings and historical text are version-bound field by field; missing translations retain their originals.") : (a.zh ? "问题解读、理由与限制说明按界面语言生成;此轨迹包未包含整页历史译文。" : "Questions, reasons and limitations follow the interface language; this pack does not include whole-history translation.")}{a.zh ? "切换语言后单独缓存,需点击才生成。浏览、切换原文和展开内容不调用模型;译文未经独立校对,引用保留原文。" : "Each language is cached separately and generated only on request. Browsing, original-text switching and expansion never call a model. Translation is not independently checked; citations retain their originals."}
-{a.pack?.result ? (a.zh ? "所选范围已有分析,查看不调用 Agent。" : "An analysis exists for the selected scope. Viewing never invokes an Agent.") : a.pack?.continuation?.can_reuse ? (a.zh ? "本次复用已有判读,不调用模型;原用量保留在上一版。" : "This action reuses existing judgments without a model call; original usage stays with the previous version.") : (a.zh ? "点击深度分析使用本机 Codex 账户,单独产生分析用量;普通浏览不调用 Agent。" : "Deep analysis uses the local Codex account with separate analysis usage; ordinary browsing never invokes an Agent.")}
+{a.readOnly ? (a.zh ? "此来源只读;浏览已有分析不调用 Agent。" : "This source is read-only; viewing saved analyses never invokes an Agent.") : a.pack?.result ? (a.zh ? "所选范围已有分析,查看不调用 Agent。" : "An analysis exists for the selected scope. Viewing never invokes an Agent.") : a.pack?.continuation?.can_reuse ? (a.zh ? "本次复用已有判读,不调用模型;原用量保留在上一版。" : "This action reuses existing judgments without a model call; original usage stays with the previous version.") : (a.zh ? "点击深度分析使用本机 Codex 账户,单独产生分析用量;普通浏览不调用 Agent。" : "Deep analysis uses the local Codex account with separate analysis usage; ordinary browsing never invokes an Agent.")}
{a.zh ? "只分析已授权的固定轨迹,不改变研究历史。范围、方法与来源版本可在结果中查看。" : "Analyzes an authorized fixed trajectory without changing research history. Scope, method and source revisions remain available with the result."}
{a.pack?.continuation ?{a.zh ? "接续保留上一版。新增、更正或删除可能影响旧判断,本版重新评估全部片段;仅证据内容与配置等价时复用已有判读。浏览与来源更新不启动分析。" : "Continuation preserves the previous version. Additions, corrections and removals can affect earlier judgments, so all episodes are reassessed. Existing judgments are reused only for equivalent evidence and configuration. Browsing and source updates never start analysis."}
: null}{zh ? "部分历史分析暂不可读" : "Some historical analyses cannot be read"} · {a.snapshot.catalog.unreadable_pack_ids.length}
: null}{zh ? "可阅读已保存的分析与原文;此来源不能启动、重试或取消分析。" : "Saved analyses and originals remain readable. This source cannot start, retry or cancel analysis."}
: null}{p.selection.exhaustive ? (zh ? "覆盖此固定分析范围;不等于覆盖全部研究。" : "Covers this fixed analysis scope, not necessarily the whole research history.") : (zh ? "定向抽样,仅分析选定研究片段;范围外或未捕获的工作未纳入,不代表全程。" : "Purposive sample of selected episodes; work outside this scope or not captured is excluded. It does not represent the whole run.")}
{zh ? "正向诊断、证据采用与最终任务收益分别判断。没有综合分数,也不据此推断因果或整体研究质量。引用存在不代表模型解释已被独立核验。" : "Diagnostic value, evidence adoption and task utility are separate. There is no total score, causal claim or whole-run quality rating. Citations do not independently verify model interpretations."}
{result ? Object.values(metricCopy).map(label =>{label[zh ? 0 : 1]} · {label[zh ? 2 : 3]}
) : null} @@ -504,15 +507,15 @@ export function ResearchAnalysisPanel({analysis: a, onHistory}: {analysis: Contr {p.result_attempt && p.result_attempt !== p.job?.attempt ?{zh ? "当前重试尚未完成,显示上次已保存分析。" : "The current retry is incomplete; showing the previously saved analysis."}
{a.pending ?
{a.pending ?
{p.job?.status === "cancelled" ? (zh ? "分析已取消,研究历史保持可读。" : "Analysis cancelled. Research history remains readable.") : (zh ? "上次分析未完成;可重试,原失败记录会保留。" : "The last analysis did not complete. Retry preserves its failure record.")}
: null} + {failed && !a.pending ?{p.job?.status === "cancelled" ? (zh ? "分析已取消,研究历史保持可读。" : "Analysis cancelled. Research history remains readable.") : !available ? (zh ? "上次分析未完成,失败记录保留。" : "The last analysis did not complete; its failure record is preserved.") : (zh ? "上次分析未完成;可重试,原失败记录会保留。" : "The last analysis did not complete. Retry preserves its failure record.")}
: null} {failed && !a.pending ?{zh ? "本次尝试" : "This attempt"} · {p.job?.analysis_wall_seconds == null ? (zh ? "耗时未知" : "Time unknown") : `${p.job.analysis_wall_seconds.toFixed(1)}s`} · {zh ? "输入" : "input"} {p.job?.usage?.input_tokens?.toLocaleString() ?? "—"} / {zh ? "输出" : "output"} {p.job?.usage?.output_tokens?.toLocaleString() ?? "—"} tokens
: null} diff --git a/docs/development/assets/research-report-episodes/readonly-analysis-after-1280.png b/docs/development/assets/research-report-episodes/readonly-analysis-after-1280.png new file mode 100644 index 0000000000000000000000000000000000000000..b7c8ae979ece178caa6dded2c124487d8dd68257 GIT binary patch literal 131063 zcmce;^;?zO_XSEfQi?RvARyfx64C~#bazQN2%8S2krq%$3VL<43$M8R0bQ5J(~L@&vuW?eETQoIF3zf`m#e8>)>r~4 a_nWAkZ-T7Pht2|RIWBvy6oDm#k`G-ScgHvC!Xz8``<y6!uu6F^5MolT#g>?eB2B%f^7%_i{IuUTrcOAn4@2n^uiQwsu78d_4jQBhwa
z2hwVJbcM%g7?#T4%tq%b%c0UM{N`Blvrh71iB@UBZBE$C7-LN4sx!6U#wFSxDCqEr
zH#l6-{#rr1Umh44@46z4nc`ouq4UD!Sg#>Ht{3%3$0UdErNN&dO@7oEmwV~y>9QzM
zuVzE6W*`>}$G+77nly%Z>sMXb?}Yf#ey*+x*)iAB5^kf!k&wDF<;Qmq{Fhc&*SA*(u6p8W4e}WD-4R3#
zmV>(5$NzV4g!CNZhes`*+c!S_#1Sj)h?kmGoR>sNVfx?A6Vl^>Bsh2Z**T}ax6zu$
zn2tUKv`xcL?vb5~SM%za8?2QeH-%ynB<<|)8-T?NGS-KkdEn-O;>7v;KB!;^H26Re
zFSVBz;WR={6?a`i+?vl4_jUz>Zz~JfUQGR@MQ>1WK$z8?$6z
E;18{VJ4Wjblx`Twx@p7C7&|J$&cy-7x7rtBF-MP?|H
z5|V5pGb>~zdz56BC`rl;MTM-gQW8oDA;~OJ%6Yu=?SG!vt?SPDcwBd`JHJQYe!}Pd
ze!ZUKIF9G>G=IPE=}X#=lpXOpfwM=~#ShXN