Skip to content

Commit 46cae58

Browse files
committed
fix: stabilize codex live session refresh
1 parent 642fbda commit 46cae58

12 files changed

Lines changed: 457 additions & 48 deletions

File tree

.agents/skills/gettokens-domain-engineering/SKILL.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ This skill unifies the technical rules for building, styling, and debugging GetT
145145
- If a feed row exposes session-id copy, make it an independent click target that stops row-selection propagation. A successful click must provide visible feedback such as `已复制` / `Copied` rather than relying on a title change or silent clipboard write.
146146
- Timeline rows inside the detail pane should still be scan-friendly. Compress each row to one line, prefer short request ids / short clock times, and surface only the core timing metrics first (`total`, `TTFT`, `first token`). Treat secondary gap / stream metrics as secondary-width affordances, not as mandatory first-line content.
147147
- The detail shell and filter shell are workbench surfaces, not nested cards. Avoid stacking a second `border + shadow` card around the timeline, the filter bar, or the detail root unless a later design system rule explicitly requires that shell.
148+
- Long live-session detail panes should scroll inside the detail column, not by growing the whole workbench page. On wide layouts, keep the detail column sticky, cap it to the viewport, use `overflow-y-auto`, and set overscroll containment so chart/timeline inspection does not roll the page header away.
148149
- `projectName` is a display label owned by the CLIProxyAPI live tracker. The sidecar may enrich it from trusted local Codex session metadata (`CODEX_HOME || ~/.codex` session JSONL) before returning the live snapshot; GetTokens should only pass the optional DTO field through Wails/root bindings/frontend model and fall back to an explicit unknown-project label when absent. Do not add Wails/frontend compatibility lookup for old sidecars unless a later requirement explicitly reintroduces compatibility.
149150
- Account resource surfaces inside live-session details should reuse accounts-domain components such as `QuotaBars` and `BillingBalance`. Add a small adapter from live request `quota` / `billing` DTOs into account display shapes instead of copying quota or balance JSX into live sessions.
150151
- Never display raw request/response payloads, credentials, bearer tokens, cookies, or unredacted error bodies. Diagnostic copy must be redacted and bounded.
@@ -154,10 +155,14 @@ This skill unifies the technical rules for building, styling, and debugging GetT
154155
- Live-session history endpoints should be paginated from disk (`limit / offset / window / session_id`) and must not rebuild the old unbounded in-memory details list.
155156
- Treat Codex upstream HTTP fallback as an observable sticky state. GetTokens may infer and explain the fallback, but must not promise transparent recovery to WebSocket after Codex has already downgraded.
156157
- The frontend must support browser preview with mock snapshots, while desktop mode polls the real Wails snapshot. Show source state such as `live`, `cache`, or `preview` so stale sidecar state is not mistaken for no sessions.
158+
- Live-session refresh should use structural snapshot merging instead of replacing the whole React snapshot on every poll. Ignore clock-only changes such as `generatedAt`, preview/cache timestamps, active session duration, and active request streaming duration when the underlying sessions/requests did not change. Reuse unchanged session and request object references so the feed, selection, and detail pane do not flicker during polling.
159+
- Browser preview/cache detail polling should not rewrite detail state every second just to advance time. Let charts project the current active request through explicit `nowMs` model options, and keep detail/history state updates for real structural changes.
157160
- Request timing trend charts must be driven by request records, not decorative UI state. Put trend derivation in a pure model under `features/codex-live-sessions/model/`, merge the active request by `requestID`, sort points by `startedAt`, and accept an explicit `nowMs` option so live requests can be projected in tests.
158161
- For streaming / active / reconnecting requests without `completedAt`, project `totalDurationMs` from `nowMs - startedAt` with a bounded safety cap. Do not mutate the original request or invent first-event / first-token timings.
159162
- Only the current active request is allowed to use `nowMs - startedAt` projection in the timing trend. Historical request rows that still carry `streaming` / `reconnecting` from cache or stale sidecar state must keep their recorded `timing.totalDurationMs` or `completedAt - startedAt`; otherwise every total-duration point will grow together.
163+
- Live request `sequence` is a sidecar-owned lifecycle counter inside one Codex conversation/session. Memory retention may cap the retained request map, but pruning must only delete old requests; it must not renumber retained requests back to `1..50`. When a long session keeps only 50 requests, the first retained request may legitimately be `#6` and the newest `#55`.
160164
- Trend chart x positions should read as request sequence, not timestamp spacing. Keep request records sorted by `startedAt`, then render the visible slice as dense equal-step bars with `#sequence` as the x-axis label; do not stretch sparse requests across real elapsed gaps.
165+
- Trend chart data window is a fixed count cap, not a fixed time window. Keep only the latest capped request points for the chart model, so new requests push the sequence labels forward (`#50` -> `#51` -> `#52`) while the visible data volume remains bounded.
161166
- Trend chart viewport should be a fixed, non-scrollable audio waveform chart. The visible request count is width-driven: wider surfaces show more recent request bars, narrower surfaces show fewer, and the latest request stays anchored near the right edge. Do not reintroduce horizontal panning or auto-scroll follow logic.
162167
- Keep the live-session chart visually inside the page section, not as a nested card. Use the existing Swiss-industrial chart tokens, footer summaries below the graph, and live markers such as dashed strokes/rings for in-flight samples.
163168
- Request timing trend visuals should read like a forward-moving audio waveform, not a finance line, ECG trace, or candlestick chart. Render exactly one centered vertical amplitude bar per request for the selected timing metric; longer durations produce taller bars, and live rings distinguish in-flight samples.

docs-linhay/dev/20260523-session-distillation-codex-live-sessions-ui.md

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -82,10 +82,11 @@ Live sessions 的 detail 面板和筛选区本质上是工作台容器,不是
8282
1. 已完成请求优先使用 `timing.totalDurationMs`;缺失时才回退到 `completedAt - startedAt`
8383
2. 当前 active request 可以用 `nowMs - startedAt` 做实时投影,用于显示正在增长的 live 样本。
8484
3. 历史请求即使因为 cache 或 sidecar 残留仍带着 `streaming/reconnecting` 且没有 `completedAt`,也不能继续按 `nowMs` 投影;否则图上所有总耗时点会一起增长。
85-
4. 纯模型仍负责过滤最近窗口内的请求点,窗口外请求不参与 y 轴最大值;视图层不再用真实时间间隔决定 x 位置
85+
4. 纯模型仍负责过滤最近窗口内的请求点,但窗口语义是固定数量上限,不是固定时间段;窗口外请求不参与 y 轴最大值。
8686
5. x 轴语义固定为请求序号:按 `startedAt` 排序后的最近请求以等距密集柱形展示,标签使用 `#sequence`,不能再把稀疏请求按真实时间拉开。
87-
6. 图表类型按 forward-moving audio waveform 处理:一柱一请求,最新 request 锚在右侧;宽容器显示更多最近请求,窄容器显示更少请求,不提供横向滚动或拖动。
88-
7. 回归测试必须覆盖“stale streaming request + active request”并存时,只有 active request 增长,并覆盖窗口外旧请求被过滤;组件结构测试需覆盖非滚动 fixed viewport、请求序号 x 轴和一请求一柱边界。
87+
6. `sequence` 的真实源头在 CLIProxyAPI live tracker。内存裁剪只允许删除旧 request,不能把 retained requests 重新编号成 `1..50`;否则前端固定数量窗口永远只能显示到 `#50`,无法表达长会话继续推进。
88+
7. 图表类型按 forward-moving audio waveform 处理:一柱一请求,最新 request 锚在右侧;宽容器显示更多最近请求,窄容器显示更少请求,不提供横向滚动或拖动。
89+
8. 回归测试必须覆盖“stale streaming request + active request”并存时,只有 active request 增长,并覆盖固定数量窗口只保留最新请求;组件结构测试需覆盖非滚动 fixed viewport、请求序号 x 轴和一请求一柱边界。
8990

9091
这类问题不要先调 CSS 或动画。先检查纯模型输出:`points[].values.totalDurationMs``points[].isLive` 是否已经错误增长;如果模型输出错,修模型,不修图表。
9192

@@ -98,12 +99,38 @@ Live sessions 的 detail 面板和筛选区本质上是工作台容器,不是
9899
3. 下方“耗时”指标块负责维度切换,`总耗时 / TTFT / 首 token / 流式 / 排队 / 选号 / 连接 / 平均间隔 / 最大间隔` 都可以切换图表。
99100
4. 仍然保留 live request 的光圈标记,让正在增长的样本一眼可见。
100101
5. TTFT / first-token 等次级指标切换后也保持同一套音频波形语言,不再回到虚线趋势图或多线叠加。
101-
6. 动画只表达状态变化:切换指标时整组波形短暂淡入,实时刷新时只让最新 live 点光圈轻微呼吸;不要每秒重扫整条波形。
102+
6. 标签需要随请求序号向后推进,但窗口数据量保持恒定上限。新请求进入后丢弃最老请求,`最新样本` 和右侧轴标签从 `#50` 继续推进到 `#51/#52/...`
103+
7. 动画只表达状态变化:切换指标时整组波形短暂淡入,实时刷新时只让最新 live 点光圈轻微呼吸;不要每秒重扫整条波形。
102104

103105
### 9. 请求时间线只展示最近 15 条
104106

105107
`请求时间线` 是 detail 面板的扫描区,不是完整历史列表。完整历史仍由 detail/history 数据承载,但页面内只显示排序后的最近 15 条 request,标题行数也以实际展示数量为准,避免 live session 长时间运行后面板无限变长。
106108

109+
### 10. 右侧详情列要自包含滚动
110+
111+
长运行会话的详情内容会明显高于视口。宽屏双栏布局下,右侧详情列不能只靠外层 workbench 滚动访问底部,否则用户在请求耗时趋势或请求时间线附近滚动时,会把页头、搜索栏和左侧列表一起卷走。
112+
113+
稳定边界:
114+
115+
1. 右侧详情列继续保持 sticky,让详情上下文跟随左侧列表扫描。
116+
2. sticky 容器本身设置 viewport 高度上限,并启用纵向内部滚动。
117+
3. 使用 overscroll containment 阻止滚到详情列边界后继续把外层 workbench 带走。
118+
4. 回归测试至少锁住详情列的 `max-height``overflow-y-auto` 与 overscroll containment,浏览器验收要检查 detail 容器为独立 scroll container。
119+
120+
### 11. 刷新用结构差分合并,不用整包替换
121+
122+
Live sessions 是高频轮询页面,刷新时不能把每秒变化的时钟字段当成整页数据变化。否则即使业务结构没有变,React 也会反复替换 snapshot、列表和详情引用,表现为会话列表与图表区域持续闪烁。
123+
124+
稳定边界:
125+
126+
1. `CodexLiveSessionsFeature` 加载 snapshot 后先进入纯模型合并层,而不是直接 `setSnapshot(next)`
127+
2.`generatedAt`、preview/cache 时间戳、active session duration、active request streaming duration 等时钟型变化时,复用旧 snapshot 引用。
128+
3. 如果只有某个 session 或 request 发生结构变化,只替换该节点;未变化的 session / request 继续复用旧对象引用。
129+
4. browser preview/cache 下的 detail polling 不应每秒重写 detail state;图表需要增长感时,通过 `nowMs` 投影当前 active request。
130+
5. 回归测试要覆盖 clock-only refresh、cache preview clock refresh、局部结构变化三类场景,并验证未变化节点引用保持稳定。
131+
132+
本轮浏览器验收在 `#frame=codex&workspace=live-sessions` 的 CACHE 页面连续采样 4 次、约 3.6 秒,`来源 CACHE`、会话列表片段、`最新样本#50` 与页面滚动位置均保持稳定。
133+
107134
## 不纳入
108135

109136
- 不新增独立 `gettokens-codex-live-sessions` skill;当前规则继续归入 `gettokens-domain-engineering` 的 Codex Live Sessions 小节。

docs-linhay/memory/2026-05-27.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,13 @@
9494
- 会话沉淀:已更新 `.agents/skills/gettokens-domain-engineering/SKILL.md` 的 Codex Live Sessions 规则,明确布局/密度类图表问题必须伪造触发问题的边界数量样本,并用 DOM 断言检查 label overflow、x 轴重叠和 live ring 裁切;不升级 AGENTS,因为这是 live-session 领域内规则。
9595
- 剩余风险:真实 Wails runtime 当前没有 active 50 request 样本;本轮已用 browser preview 伪造 50 request 完成长会话验收,后续有真实样本时补 Wails 截图。
9696

97+
## Codex live-session detail contained scroll
98+
99+
- 根因:`CodexLiveSessionsWorkbench` 的右侧详情列只有 `xl:sticky xl:top-5`,没有高度上限和内部滚动;长详情内容会撑大外层 workbench,导致用户在请求耗时趋势或请求时间线附近滚动时,把整个页面页头、搜索栏和左侧列表一起卷走。
100+
- 修复:右侧详情列在宽屏下增加 `xl:max-h-[calc(100vh-2.5rem)]``xl:overflow-y-auto``xl:overscroll-contain` 与稳定滚动条,详情列成为独立滚动容器,图表本身仍保持固定非横向滚动视口。
101+
- 验证:先补红灯测试 `codex live session detail scroll is contained inside the detail column`,确认旧结构失败;实现后 `node --test frontend/src/features/codex-live-sessions/model.test.mjs` 47/47 通过,`npm --prefix frontend run typecheck``npm --prefix frontend run build` 通过。in-app browser 宽屏 DOM 验收确认 detail 为 `position: sticky``overflow-y: auto``overscroll-behavior-y: contain`,高度 924px、内容高度 1937px。
102+
- 沉淀:已更新 `.agents/skills/gettokens-domain-engineering/SKILL.md``docs-linhay/dev/20260523-session-distillation-codex-live-sessions-ui.md`,不升级 `AGENTS.md`
103+
97104
## CLIProxyAPI upstream merge planning
98105

99106
- 新建 space:`docs-linhay/spaces/20260527-cliproxyapi-upstream-merge/`,用于处理 `docs-linhay/references/CLIProxyAPI#gettokens/sidecar` 合并 `upstream/main@4b681031`
@@ -131,3 +138,27 @@
131138
- 更新机制:新增 `docs-linhay/scripts/update-taste-skill.sh`,默认 `--check` 只比较 lock 与远端哈希;显式 `--update` 才按 lock 的安装清单替换本地 skill 目录并更新 commit。
132139
- 会话沉淀:外部 Git skill 包安装流程已沉淀到 `.agents/skills/gettokens-codex-extensions-management/SKILL.md`,固定要求记录 source/ref/commit/source-path/local-path/skill-name,更新脚本默认只检查,显式 `--update` 才替换本地目录。
133140
- 风险:Taste Skill 包的 frontmatter 描述较长,可能增加 Codex skill discovery 上下文预算压力;若后续出现预算告警,优先收敛为高频子集。
141+
142+
## Codex live-session diff refresh
143+
144+
- 根因:`#frame=codex&workspace=live-sessions` 的 browser preview/cache 每秒刷新会改变 `generatedAt`、preview 时间戳、active duration、stream duration 等时钟字段;前端原先整包 `setSnapshot(next)`,导致列表、详情和图表引用反复替换,页面表现为持续闪烁。
145+
- 修复:新增 `mergeCodexLiveSessionsSnapshot`,对时钟型变化做结构归一比较;clock-only refresh 直接复用旧 snapshot,局部结构变化时只替换发生变化的 session/request。`CodexLiveSessionsFeature` 的 preview/cache、sidecar unavailable 和真实 Wails snapshot 都统一走差分合并。
146+
- 追加边界:browser preview/cache 下的 detail polling 停止每秒重写 detail state;图表的实时增长由已有 `nowMs` 投影承担。
147+
- 验证:`node --test frontend/src/features/codex-live-sessions/model.test.mjs` 50/50 通过,`npm --prefix frontend run typecheck``npm --prefix frontend run build` 通过,scoped `git diff --check` 通过。in-app browser 对 live-sessions CACHE 页面连续采样 4 次、约 3.6 秒,`来源 CACHE`、会话列表片段、`最新样本#50``scrollY` 均稳定。
148+
- 沉淀:已更新 `.agents/skills/gettokens-domain-engineering/SKILL.md``docs-linhay/dev/20260523-session-distillation-codex-live-sessions-ui.md`,不升级 `AGENTS.md`,因为这是 Codex live sessions 领域内刷新策略。
149+
150+
## Codex live-session rolling count window
151+
152+
- 用户反馈:请求耗时趋势图的标签仍希望随请求继续向后推进,但窗口数据量应保持恒定上限。
153+
- 修复:`buildCodexLiveRequestTimingTrend` 从固定时间窗过滤改为固定数量上限,默认只保留最新 50 个 request points;browser preview 每 6 秒合成一个新的 latest request,仍保留 50 条请求,因此 `最新样本` 和右侧轴标签会从 `#50` 继续推进到 `#51/#52/...`
154+
- 验证:`node --test frontend/src/features/codex-live-sessions/model.test.mjs` 51/51 通过,`npm --prefix frontend run typecheck``npm --prefix frontend run build`、scoped `git diff --check` 通过。in-app browser 验证首次为 `请求数50 / 最新样本#50`,约 7.2 秒后为 `请求数50 / 最新样本#51`,x 轴右侧标签从 `#50` 推进到 `#51`
155+
- 沉淀:已更新 `.agents/skills/gettokens-domain-engineering/SKILL.md``docs-linhay/dev/20260523-session-distillation-codex-live-sessions-ui.md`,不升级 `AGENTS.md`
156+
157+
## Codex live-session sidecar request sequence
158+
159+
- 用户追问:需要看 sidecar 侧的数据发生是否正确,不能只依赖 browser preview/mock 的滚动窗口。
160+
- 真实检查:只读查询正式 `8317` management endpoint 后,多个长会话 `requestCount=50 / requestsLen=50`,但 retained window 的 `firstSeq=1 / lastSeq=50`,说明运行中的 sidecar 会在裁剪后把 retained requests 重新编号。
161+
- 根因:CLIProxyAPI fork 的 live tracker 用 `len(session.requests)+1` 生成新请求序号,且 `pruneLiveSessionRequestsLocked` 在裁剪后按 retained slice 重写 `Sequence=index+1`;history 里最新请求也会被写成错误序号。
162+
- 修复:`docs-linhay/references/CLIProxyAPI/internal/gettokenshooks/live_sessions.go` 新增会话级 `nextRequestSequence`,新请求按会话生命周期单调递增;裁剪只删除旧请求,不重编号;合并 transient session 时保留已有 sequence 并推进 counter。
163+
- 验证:先补红灯测试复现 `req-5` 被重写成 `#1``req-54` history 被写成 `#51`;修复后 `go test ./internal/gettokenshooks -run 'TestLiveSessionsPrunesRequestsWithinLongSession|TestLiveSessionsHistoryPersistsTrimmedRequests' -count=1``go test ./internal/gettokenshooks -run 'LiveSessions' -count=1``go test ./internal/gettokenshooks -count=1` 均通过。再次只读查询 8317 仍是旧二进制行为,未对正式 sidecar 做重启或替换。
164+
- 沉淀:已更新 `gettokens-domain-engineering` 的 Codex Live Sessions 规则,明确 request `sequence` 是 sidecar-owned lifecycle counter,内存保留上限不能重编号。

docs-linhay/references/CLIProxyAPI

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
Subproject commit ef93d8c07973ad05595e0258065c068fb3e337f5
1+
Subproject commit 952cf213eb49965f8e4c055affc71f932e897058

0 commit comments

Comments
 (0)