Conversation
…t runtime Store all OAuth fields (access/refresh token, expiry, client creds, markers) in a single keychain item per MCP as one JSON blob, so macOS "Always Allow" prompts once instead of eight times. Per-field access goes through get/set/delete_oauth_storage_secret accessors using a dedicated rmw lock (distinct from the coarse CAS lock, since file locks are non-reentrant); reads are lock-free. No migration/back-compat by design. Also add an offline context-accounting runtime (disable_external_services) so Web session switching no longer spins up MCP and triggers keychain popups. Rewrite ~150 test call sites to use the accessors instead of raw keychain keys. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The thinking summary used a wider icon column (0.92rem) with a centered lightbulb and a larger gap (0.18rem) than the tool rows (0.68rem column, left-aligned box, 0.14rem gap), so the whole row read as more indented. Match the tool-row grid and compensate the outline glyph's ~21% intrinsic left padding with a negative margin so its visible left edge lines up. Bump styles.css cache-bust token and sync the frontend static tests. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The .message-agent:has(.message-tool-cards) grid had no explicit columns, so its implicit column sized to max-content; each child's width:min(40rem, 100%) then resolved 100% against that oversized column and landed at ~40rem. Inside an indented pipeline step body (agent box narrower than 40rem) the column overflowed several rem past the step's right edge. Constrain the column to minmax(0, 1fr) so it never exceeds the agent box, and add min-width:0 on the body so long content wraps instead of forcing overflow. Bump styles.css cache-bust token and add a regression assertion. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The /status inline panel truncated long "Context" (背景信息) rows to a single line with an ellipsis, hiding the candidate/step/usage detail users open /status to read. Drop nowrap/overflow/ellipsis on the plain .session-status-row dd and let the value wrap within its column (overflow-wrap: anywhere). The copyable session-id row is unaffected — it uses the .session-status-row-copy variant whose value span keeps its own single-line ellipsis + copy button. Bump styles cache-bust token 307 → 308. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
When a session is running the sidebar was rebuilt at high frequency — every SSE stream frame via render() and every 2.5s background poll via refreshSessionsSidebar() — each doing a full replaceChildren(). That destroyed the row under the pointer, so :hover backgrounds and the hover-revealed action buttons (.thread-actions / .project-actions) flickered on and off. Mirror the existing message-stack hover throttle: route the streaming and background repaints through renderSessionsAuto(), which defers the rebuild while the pointer is inside the sidebar (bookkeeping only, no DOM churn) and catches up once on pointerleave. A stable .session-rail container carries the pointerenter/leave guard, bound once. User-driven structural actions (expand/collapse/pin/switch) still call renderProjectThreadNavigation / renderSessions directly and repaint immediately. Session data keeps refreshing on state; only the paint is deferred, so other sessions' spinner/time lag at most one poll and snap back the instant the pointer leaves. Bump app.js cache-bust token 305 → 306. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The composer project picker menu opens upward (bottom:100%) anchored to its trigger. A fixed CSS max-height cannot reference the trigger's real distance to the viewport top (it varies with composer height, breakpoint, window size), so in short windows the menu's top (the search box) overflowed the viewport. - Add clampDraftMenuToViewport(): on open, measure the picker's getBoundingClientRect and set inline max-height to min(25rem, available-space) so the top never clips; applied to all four composer draft menus (project/new-project/mode/pipeline). CSS max-height stays as a pre-JS fallback. - Restructure the project menu into three regions: fixed search header, a single scrollable .draft-session-menu-list, and a fixed .draft-session-menu-footer (divider + New/No project). The menu itself is now flex-column + overflow:hidden, so only the middle project list scrolls; search and footer stay put. - Bump styles.css -> web-repl-ui-310, app.js -> web-repl-ui-308; sync frontend static tests. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
用户从未开放过 pipeline 工作区模态这一功能,但标题栏仍暴露「View pipeline」按钮,且候选选择时会自动弹出该面板,与主区内联的流水线体验重复。 - pipelineWorkspaceEntryVisible() 恒返回 false,标题栏按钮永久隐藏 - 移除候选选择时的两处 maybeOpenPipelineSelectionWorkspace 自动弹出调用 - 保留按钮 markup / 点击处理 / 隐藏 tab / components/pipeline.js 渲染代码, 以及 maybeOpenPipelineSelectionWorkspace、pipelineSelectionRequiresWorkspace 函数(当前无调用),改动可回滚 - 视觉审计新增 openLegacyPipelineWorkspace:临时揭开隐藏按钮并复用其真实点击 处理打开工作区,继续覆盖该工作区的视觉回归 - app.js 缓存 token 308→311,同步 test_frontend_static.py 断言并重构对应用例 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
流水线运行中输出面板「一闪一闪」、且用户 X 关掉后又被弹回:loadSession 无条件调用 outputController.reset(),reset() 强制关闭面板并清掉「首次自动 展开」守卫(autoOpenedOnce),随后 refresh() 又把它自动弹开。session.resync .required 在运行中反复触发(权限确认、input_required 等),每次重走 loadSession → reset(强关) → refresh(弹开),肉眼即持续闪烁,并覆盖用户的手动关闭。 紧邻的 clearDetailsOpenOverrides 早已用「仅真正切换会话」守卫,唯独 reset() 漏了这层守卫。 - 抽出 switchedSession 判定,reset() 与 clearDetailsOpenOverrides 共用 - 仅真正切换会话时复位(清空上个会话产物并重新武装一次性自动展开);同会话 resync/重载只 refresh(),保留 isOpen 与 autoOpenedOnce - 新建会话草稿/草稿落地两条路径的 reset() 不受影响 - 新增 test_output_panel_reset_only_on_session_switch_in_load_session 回归守卫 - app.js token 311→312,同步 test_frontend_static 断言 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
escapeHtml's `/"/g` and `/'/g` put a bare quote inside a regex literal;
Babel's webui JS lexer reads the quote as a JS string start and desyncs the
token stream, so every t() after escapeHtml in output_panel.js was silently
dropped from extraction. "Resource stacks" / "Template files" (used only here)
got marked obsolete and fell back to English — and the last `make translate`
fuzzy-matched them to wrong strings ("资源类型" / "模板已修复"), the "又换了"
the user saw. "Architecture diagram" survived only because pipeline.js also
uses it.
- output_panel.js: escape the two quote regexes as unicode (identical match),
with a comment forbidding a revert; bump output-panel v16→v17, app.js 312→313.
- webui.po (6 locales): restore correct translations for Resource stacks /
Template files; translate the never-extracted File no longer exists /
Close preview so test_webui_fully_translated stays green. .mo recompiled.
- test_webui_extraction.py: add test_output_panel_labels_are_extractable so a
revert to a bare-quote regex fails loudly instead of silently at next extract.
- test_frontend_static.py: sync cache-bust token assertions.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Composer thumbnails and in-message images now open a shared full-screen lightbox on click instead of being non-interactive (or, for composer thumbnails, deleting the attachment on any click). - Add components/image_lightbox.js: a singleton overlay appended to document.body, closed by backdrop click, the × button, or Esc. - Composer: the image chip is now a container with a preview button (opens the lightbox via an onPreviewImage callback wired from app.js) and a separate × button that removes the attachment. Clicking the thumbnail previews; only × deletes. - Message images: render as a button that opens the lightbox with the /api/images original. - Styles for the lightbox and the preview button; × button reset. - i18n: add "Preview image" (webui domain, all 6 locales). - Bump cache-bust tokens (styles 311, app 314, composer v18, new image-lightbox-v1) and sync test_frontend_static.py; update the composer chip test to assert preview-vs-delete behavior. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The new first-turn LLM session-title feature fires a fire-and-forget task that appends a session.updated event on brand-new sessions. Seven turn-message-lifecycle tests assert exact event sequences and were polluted by that orthogonal, nondeterministically-timed event. Disable pending_llm_title in those tests so they stay deterministic; titling is covered by its own dedicated tests. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Pipeline first turn sets an instant provisional sidebar title so the session appears immediately, then schedules a background LLM title that overwrites the placeholder once ready. A new title_provisional flag lets the in-flight LLM result overwrite the placeholder while a user rename freezes the title and wins over any late LLM result. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Two reload-vs-live divergences: - session.updated/session.started patched only currentSession (header), leaving sidebar rows on the stale title until the next background list poll (~2.5s). Fold currentSession back into the sidebar arrays in handleStreamEvent so the row and header refresh in the same frame. - normalizeStoredMessage dropped imageIds/fileRefs when rebuilding the transcript on session restore, so buildMessageAttachmentsElement saw undefined and rendered nothing (images vanished after reopening). Carry both fields through, matching the live user.message reducer. Bumps app.js cache-bust token to web-repl-ui-316. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add a "Developer mode" switch under Settings/General (off by default).
When enabled, a devOnly "Developer" tab appears in the workspace nav.
The Developer tab holds two features:
1. A "Highlight failed tool calls in red" toggle (off by default). Failed
tool styling is gated under body.dev-highlight-tool-errors so toggling
is instant and tool_cards.js stays untouched; when off, failed tool
calls look like any other call.
2. The existing restart-service control, moved here from General.
Backed by GET/PUT /api/settings/developer ({mode, highlightFailedTools},
both default false). Adds webui strings across all 6 locales, bumps
cache-bust tokens (api.js 305, workspace.js v50, app.js 317, styles.css
312), and syncs frontend static tests plus new backend tests.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The web app depends on the optional `http` extra (Starlette and Uvicorn), but neither the README nor the website documented it — users only learned this from the runtime error. Add the install step to the README web section, an Installation prerequisite to the Web App guide, and an Optional Features table to the installation guide (covering http and a2a). Mirror all additions across the six translated doc locales. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Mirror the English README change to all six translated READMEs: note that the web app needs the `http` extra and add the `pip install 'iac-code[http]'` step before `iac-code web`. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…test test_permission_answer_resolves_agent_loop_future asserts the full turn event sequence ends with turn.done. New sessions schedule a fire-and-forget LLM-title task that emits a trailing session.updated after turn.done, polluting the assertion on slower (Windows) runners. Set pending_llm_title=False after create_session to suppress it, matching the isolation pattern already applied to test_runtime_messages.py. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Overview
Web session-experience work plus supporting fixes. The branch focuses on the local Web app (
iac-code web): session titles, image handling, pipeline transcript polish, a Developer settings tab, and a batch of layout/flicker fixes. It also carries one cross-cutting commit that consolidates MCP OAuth keychain storage and adds an offline-context runtime so Web session switching no longer triggers keychain popups.Rebased onto latest
main(GLM 5.2 fast preview + CI/test stabilization). No textual conflicts; a semantic review confirmed no logical/functional clash between the two sides (notably the MCP storage striped-RLocklayer and the OAuth single-blob rmw lock use distinct file-lock keys and are compatible).Highlights
LLM session titles
(empty)and vanished from the sidebar; persisted + scheduled with a fallback title and retry.Web UX
Fixes
/statuscontext rows wrap; new-session project menu height capped.MCP + docs
fix(mcp,web): OAuth fields consolidated into a single keychain blob (macOS "Always Allow" prompts once, not 8×); addeddisable_external_servicesoffline-context runtime.httpextra required for the Web app in README, translated READMEs, and website docs (all 6 locales).Testing
make testscope (web / providers / mcp / services / i18n): 3030 passed, 2 skipped.🤖 Generated with Claude Code