From 42d01f7de4caa5b7dbc15fb2192667abe8326eee Mon Sep 17 00:00:00 2001
From: LoopX Agent <337587101+loopx-agent@users.noreply.github.com>
Date: Sat, 24 Oct 2026 01:00:44 +0800
Subject: [PATCH 1/2] feat(research): expose attributed LoopX input share
Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com>
---
.../goal-research-progress.css | 3 +
.../goal-research-progress.tsx | 31 +++++
.../research-input-share/after-desktop.png | Bin 0 -> 150255 bytes
.../after-detail-narrow.png | Bin 0 -> 43931 bytes
.../after-expanded-desktop.png | Bin 0 -> 130404 bytes
.../after-help-narrow.png | Bin 0 -> 64817 bytes
.../after-partial-desktop.png | Bin 0 -> 108260 bytes
.../research-input-share/before-desktop.png | Bin 0 -> 143221 bytes
.../before-expanded-desktop.png | Bin 0 -> 109250 bytes
docs/reference/trajectory-assessment.md | 62 ++++++++++
.../trajectory_assessment_store.py | 15 +++
.../trajectory_assessment_types.ts | 13 ++
.../capabilities/trajectory_codex_usage.ts | 4 +-
.../capabilities/trajectory_request_usage.ts | 52 +++++++-
.../test_trajectory_assessment_store.py | 62 ++++++++++
.../trajectory_input_usage.test.ts | 114 ++++++++++++++++++
16 files changed, 354 insertions(+), 2 deletions(-)
create mode 100644 docs/development/assets/research-input-share/after-desktop.png
create mode 100644 docs/development/assets/research-input-share/after-detail-narrow.png
create mode 100644 docs/development/assets/research-input-share/after-expanded-desktop.png
create mode 100644 docs/development/assets/research-input-share/after-help-narrow.png
create mode 100644 docs/development/assets/research-input-share/after-partial-desktop.png
create mode 100644 docs/development/assets/research-input-share/before-desktop.png
create mode 100644 docs/development/assets/research-input-share/before-expanded-desktop.png
create mode 100644 tests/control_plane_ts/trajectory_input_usage.test.ts
diff --git a/apps/presentation/dashboard/src/features/personal-workspace/goal-research-progress.css b/apps/presentation/dashboard/src/features/personal-workspace/goal-research-progress.css
index fc359252b4..e623c5f33a 100644
--- a/apps/presentation/dashboard/src/features/personal-workspace/goal-research-progress.css
+++ b/apps/presentation/dashboard/src/features/personal-workspace/goal-research-progress.css
@@ -92,6 +92,9 @@
.research-usage-tail { border-inline-start: 2px solid var(--pw-caution); padding-inline-start: 16px; margin-block: 20px; }
.research-usage-tail strong { font-weight: 500; color: var(--pw-text); }
.research-request-coverage { border-block-start: 1px solid var(--pw-line); padding-block-start: 16px; margin-block: 20px; }
+.research-input-share-value { display: flex; align-items: baseline; flex-wrap: wrap; gap: 12px; }
+.research-input-share-value strong { font-size: var(--text-title); font-variant-numeric: tabular-nums; }
+.research-input-share-value > span { color: var(--pw-muted); }
.research-request-values { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px 24px; margin-block: 12px; }
.research-request-values > div { min-width: 0; }
.research-request-values dt { font-size: var(--text-body); color: var(--pw-muted); }
diff --git a/apps/presentation/dashboard/src/features/personal-workspace/goal-research-progress.tsx b/apps/presentation/dashboard/src/features/personal-workspace/goal-research-progress.tsx
index 19756f8b1e..0c6e8135e8 100644
--- a/apps/presentation/dashboard/src/features/personal-workspace/goal-research-progress.tsx
+++ b/apps/presentation/dashboard/src/features/personal-workspace/goal-research-progress.tsx
@@ -29,6 +29,36 @@ export function verdictLabel(metric: TrajectoryMetric, verdict: AssessmentVerdic
return verdict === "yes" ? (zh ? "有依据" : "Supported") : (zh ? "未建立" : "Not established");
}
+function LoopXInputShare({usage, zh}: {usage: TrajectorySolverUsage | undefined; zh: boolean}) {
+ const attribution = usage?.loopx_input;
+ const percent = (n: number) => new Intl.NumberFormat(zh ? "zh-CN" : "en", {style: "percent", maximumFractionDigits: 1}).format(n);
+ const measured = attribution && attribution.status !== "unavailable" ? attribution : null;
+ return {zh ? "同一捕获窗口内,LoopX packet 进入各次模型请求的 token 总量 ÷ 全部请求输入 token 总量,包含缓存输入。重复消费按各次请求计算,同一响应的重复回执只计一次。" : "LoopX packet tokens processed across requests divided by all request input tokens in the same capture, including cached input. Reprocessed context counts in each request; duplicate receipts for one response count once."} {zh ? "需要宿主提供逐请求 token 归因;包字节、字符数和命令次数不能替代。缺失归因保持未知;上下文压缩也不自动全部归为 LoopX。这个比例衡量结构开销,不能单独证明净效率损失或因果影响。" : "Requires host-supplied per-request token attribution, not packet bytes, characters or command counts. Missing attribution stays unknown; compaction is not automatically all attributed to LoopX. This measures input overhead, not net efficiency loss or causal impact."} {measured?.input_fraction == null ? "—" : percent(measured.input_fraction)}
+ {measured ? {measured.attributed_requests} / {measured.total_requests} {zh ? "次请求已归因" : "requests attributed"} : null}
+ {zh ? "未采集 packet 的 token 归因" : "Packet token attribution was not captured"} {zh ? "归因记录未通过核对,占比未知。" : "Attribution records could not be reconciled; share is unknown."} {zh ? "部分归因 · 全窗口占比未知" : "Partial attribution · whole-capture share unknown"} {zh ? "捕获输入为零,比例不适用。" : "Captured input is zero; the ratio is undefined."} {zh ? "假定未归因请求全非 LoopX / 全为 LoopX;不是置信区间。" : "Unattributed requests treated as none / all LoopX; not a confidence interval."} {attribution.status === "unavailable" ? attribution.reason : null} {zh ? "LoopX 输入占比" : "LoopX input share"}
{zh ? "归因明细" : "Attribution details"}
+ {measured ?
+
: {attribution.method}
{incomplete.usage ? <>{zh ? "其中末轮" : "Of which, unfinished Turn"} · {zh ? "输入" : "input"} {number(incomplete.usage.input_tokens)} · {zh ? "输出" : "output"} {number(incomplete.usage.output_tokens)} tokens> : (zh ? "末轮尚无计数观测,不记零" : "No counter observation for the unfinished Turn; not zero")}
{zh ? "仅含已持久化用量,不能据此判定运行已结束。" : "Persisted usage only; this does not establish termination."}
: null} +{zh ? "逐响应去重后,核对轮次与会话累计量、上下文压缩记录及会话计数。以下是独立核对口径,与上方会话计数不相加,也不改写 Goal 账本。" : "Deduplicated responses are reconciled against Turn and session totals, compaction records and session counters. This separate basis is not added to the session counters above and does not rewrite the Goal ledger."}
diff --git a/docs/development/assets/research-input-share/after-desktop.png b/docs/development/assets/research-input-share/after-desktop.png new file mode 100644 index 0000000000000000000000000000000000000000..dc95f7ceccc5990216ef39e5a14c5e43ef0a0be0 GIT binary patch literal 150255 zcmb5WWn9!-7e6Z9-7s{Ggp|_KB_b^nN-HRhq;z)*0wN+Hp|pTVcZZ}B(x8I0ibLOp zp69;0|IfXbcjufrGr!q;ueH9lCrVpWl?b06|I(#PL^srwbT3`PdUfd%#&=wF_&0@g z!>=!0LgRj@sHkmr>5|ur)I`~v9dfkY0ndU1ibB4BPppj5rK&HwNfo21%WIf5cJ)>m zS2#yLuA@JiZ0O=9Qo#lXOWv7MHdy>aH#c7L6-M8LKii%kuYZ|4R;Dm#h=cY&6UXi{*uM95la95QdWf)S=j#m<#HsT;rx9q85yTHq4<9 LAYJvfIVfXKzJ%ZqC#?F^}WObL9?y`SeK?zzGbgeh|6p
ziy+R|pTr( mg*~&!EkR{6+FRSQ*?@0J^m%&V9hOmAiUHUadM5pImQlM+u)le?X`dbX^)#
z7fC%3weQO{GUfN7w#4Yipy^Qnxd@_t1ZBz9To~OkTxFNyy`j_iK*UI&Qdai<7Yn#T
z6;il##lAak_{T9xd*jrV&7UA2=^oxE_-2?}-eq64Xr?Bs*}u0J~qQW6qWn0t`a0=z#FDBg8W9A`
z&^OOER;a~1pw3{yj2;)f=U-3Iy?o7!GAD6sA7Mf
zZ(0-7NxJbV3LH%wlGgPk-LPn|*g7QsQe2)Y#kXfFYH>%P`r>52loHV;IA?8HpdJkH
zUE?g)BDrV=`b}c$GMqg}>Y3h{-q!f^<%{Ov@@N3PfK{_d;9iZCPV>&ZoR)IeveRdI
zy;~z}N*=Z%+JjA4g}MK{kVcL=mB7g%2o{7kwHuR;(^
zeq1F@7#eb^+M=1tF9GMcFq6-n$9dD>RB^5?cGHHs{fR`{zL!?XX(BE&n_%d@bV?k{
zSSqN&L=s$p`CKoj`Y^8d-G$bOMQgI@T#cT?(K_$%C`ybO1qz8j30#{=cOTm%BM%!4
zCyS5a*J^y`^F^cT*tPQ)B2X=lez64hJ?`j35?{MSiP%v&7JmS3!kry>>nb^oBs9Fq
z=5I#Y56fIy$X!B$(Vz_7Tt7b@P@xJCog{Xrr~HEDke2wtSGQCso3yV%e+6QCp0_~)
zAAj#e#dzV^Ie|iZ6xK
Lp+>
zPlAHQGyGL{3Dz5GZHs7n2f<|F4my(n!{C>by6*#p{nAi+baEsv0~F~zHoi-va5|kw
zh^8?@evn;1IR4wul*b+&!qq<$Id7;Rv0!F$QwucIAr&J`BB^MB@KU-l9s<0xrer}PLV5{V|D&K4m+L<}?@WKRhi3fE{INwnXI
z0=#e>JCEbby`f3+OuV``N%(N)u9KYGRYKREUB1lLiZjJ0_8l7%J!W#@@>K|z4uxoR
zZo-~gm-oywIy*ZD(uNlywGHlZ&hb9%xw61)JX)m&e8^8V+>d3~B7BU;nmlDKj^}BW
z4WcW}J#2sR>LRKR7gy%$c6Ewfaz*DCyrKuRgk;{3TnUxhYdeTX5*;r@2iB)l!Zf-I
z&v&k;^($nx;~yrwgDfB3KZ1Ez7UjN##S>h?69_WRlP5krk+~!rJX5$l_jYL;;kkMM
z255VwhVHG!KC(K&Q8wAIjXzDW<%j`3%k+y!sJqP`>W0;L;C(CZK@|mC
zqypi{RF^{Wn)=NUr#7;isBv+etOpG-6>91NkE}mV7aYT~K<;s9@}4oqk(Xh%pRf8A
zI6G=^ZqfV}*I0_Xt^J$C(kn?BS(cTWERh>I1T2r}y=Lp>3?|g!PzwUIK8n?4W8ihF
zZ6EMFZ85ia2{TC1Bx{PNo@^7~H>n?VeE#*sFXz>