Skip to content

Apply live session titles as soon as the agent sends them - #526

Merged
xintaofei merged 3 commits into
xintaofei:mainfrom
Adam-Dalloul:feat/live-native-session-title
Aug 23, 2026
Merged

Apply live session titles as soon as the agent sends them#526
xintaofei merged 3 commits into
xintaofei:mainfrom
Adam-Dalloul:feat/live-native-session-title

Conversation

@Adam-Dalloul

Copy link
Copy Markdown
Contributor

The sidebar was waiting until you reopened a chat before it used the agent's own session name. That's why a running thread often sat there untitled even though the name already existed.

This listens for the ACP session title update and writes it as soon as it shows up. Nothing extra is polled. If the agent hasn't sent a name yet, the first prompt is used so the row isn't blank, and a rename you made yourself is left alone.

Agents that never send a live title keep the first-prompt name until they do.

Honor session_info_update.title immediately instead of waiting for the
next conversation fetch. Seed an unlocked first-prompt title so a live
chat is not Untitled while the agent is still working.
@xintaofei

Copy link
Copy Markdown
Owner

Thanks for this — the underlying complaint is real and worth fixing, and the way you've wired it up is mostly the way I'd have wired it up too. A few notes below: one thing I think has to change before this can land, and then some smaller stuff.

I checked out the branch and ran the suites, so the mechanical side is fine:

  • cargo check --features test-utils --all-targets — clean
  • cargo clippy --no-default-features --bin codeg-server --lib -- -D warnings — clean
  • cargo test --no-default-features --bin codeg-server --lib2712 passed, 0 failed (all 5 new tests green)
  • npx tsc --noEmit — clean
  • vitest run --no-file-parallelism324 files / 4462 tests passed

What I like

Routing the write through the lifecycle worker rather than doing it inline in emit_conversation_update is the right call — that keeps the protocol hot path DB-agnostic, and it puts the title write next to SessionStarted/TurnComplete, which is where this kind of persistence already lives. Reusing refresh_auto_title means you inherit the title_locked gate and the "never bump updated_at" rule for free, and seed_auto_title_if_empty is written in the same single-conditional-UPDATE style, so it's TOCTOU-safe against a concurrent rename the same way. Nice touch updating the "the remaining 5 event types" comments to 6 — that kind of drift is exactly what bites later.

The first-prompt seed also reuses delegation_child_title_seed, so for most agents it produces exactly what refresh_auto_title would compute from the same turn and the follow-up UPDATE is a genuine no-op. That was clearly deliberate.


Blocking: the chat-channel title sync gets silently switched off

A conversation can be bound to a Telegram forum topic, and the topic's name is only ever updated by chat_channel_manager.sync_conversation_title(...). New bindings default to title_sync_enabled: Set(true) (db/service/thread_binding_service.rs:90), so this is on for everyone using that integration.

The two ordinary automatic triggers for that sync both key off "the conditional UPDATE actually wrote a row":

  • commands/conversations.rs:1492-1505get_folder_conversation_with_live_core: refresh_auto_title(...) == Ok(true) → upsert + sync_conversation_title(...)
  • commands/conversations.rs:87-91list_all_conversations_core_with_codex_titlesrefresh_codex_auto_titles returns only the ids it wrote → notify_conversation_title_updates

The new NativeSessionTitle arm (acp/lifecycle.rs:313) writes the same title first and doesn't sync. So by the time either trigger runs, its title <> ? predicate is false, it reports 0 rows, and the sync never fires. Net effect: the DB row and the sidebar converge on the agent's generated title, while the bound topic keeps whatever it was named at bind time (for /new, truncate_title(task_description) from chat_channel/session_commands.rs:528). seed_auto_title_if_empty in manager.rs:1300 has the same omission — less reachable, since /new already creates the row titled, but it does bite a resumed binding whose row is still untitled.

To be fair to the PR: it's not unrecoverable — an import scan (conversations.rs:481, :748), a /resume rebind, or a manual rename will still push a title through. But none of those is part of a normal turn, so under ordinary use the topic just stays stale.

There's precedent for the fix: work_task_update_core hit exactly this trap and was fixed by threading ChatChannelManager through (commands/work_task.rs:320). lifecycle_subscriber_task(db_conn, manager, bus, broker) doesn't have one today, so it'd need the same treatment. One thing worth copying rather than reinventing: don't await the Telegram call inline in the workereditForumTopic has a 60s timeout. The detached "sync until current" pattern at conversations.rs:1682 exists precisely for that and would serve both new paths.

Worth fixing: CodeBuddy will ping-pong the title on every turn

This one I only found by going and reading the pinned adapter bundle, so it's not obvious from the diff.

The write is intentionally left unlocked, which means the next detail load re-parses the session file and overwrites it via refresh_auto_title. That's only safe while the live string is byte-identical to what our parser produces. I checked:

  • Codex — parser reads session_index.jsonl thread_nametruncate_str(trim(name), 100); live path does the same. Match. ✅
  • Claudeclaude-agent-acp's sanitizeTitle collapses \s+ before sending, our capture_title_record only trims. Differs only for titles containing newlines/repeated spaces, and the adapter's lastTitle guard stops it re-sending, so worst case is a one-off flicker. Fine.
  • CodeBuddy — this one's a real loop. In the pinned @tencent-ai/codebuddy-code@2.137.1 (registry.rs:743), sendPendingTitleUpdate runs after every completed prompt (plus queue drains and background-task drains) and has no last-sent-title dedupe — it recomputes getEffectiveSessionTitle(history) and sends it unconditionally. And when there's no ai-title/custom-title/topic yet, its fallback is getFirstRealUserMessageTitle, which caps at 80 UTF-16 code units + "...", whereas our parsers/codebuddy.rs fallback goes through title_from_user_texttruncate_str(_, 100) (100 Unicode chars).

So for a CodeBuddy session with a first message longer than 80 chars and no generated title yet: turn ends → we write the 80-unit title → ~1.5s later syncTurnMetadata triggers a detail load → we write the 100-char title → next turn → 80-unit title again → … Each flip also broadcasts a conversation://changed upsert to every connected client. That's user-visible.

I don't think this means "don't do it" — just that the unlocked-write contract deserves to be stated in a comment, and that some guard is needed (normalise both sides, or don't let the live title replace a value the parser just wrote, or make the write authoritative).

Smaller stuff (non-blocking)

A per-connection dedupe would pay for itself. emit_with_state isn't free — under the state write lock it runs apply_event, bumps event_seq, pushes into the recent-events ring (the mid-turn-attach replay buffer), and broadcasts to every attached client, and then the frontend explicitly ignores it. Given CodeBuddy fires this every turn regardless of change, remembering the last emitted title on cb_state (right next to codex_open_goal, which already does this) and skipping identical ones would make all repeats free. One caveat if you do it: reset it on ConversationLinked, otherwise a title dropped while the row was still unbound would suppress its later valid resend.

Related and more of a design question: does this need to be a client-visible AcpEvent at all? SessionState already documents goal_active as "backend-internal, not on the client snapshot", so there's a precedent for a signal that never reaches the wire.

null means "clear", not "absent". SessionInfoUpdate.title is MaybeUndefined<String> and the ACP schema says "Set to null to clear." info.title.value() folds Null and Undefined into None, so explicit clears are dropped. I actually think dropping them is the right product call — clearing would just put the row back to "Untitled" — but session_title.rs:8 explains it as "typically goal/error metadata with no title", which isn't the real reason. Worth restating as a deliberate policy.

The seed isn't byte-identical for every parser. The invariant holds for agents that use title_from_user_text, but parsers/acp_native.rs:178 caps its fallback at 80 chars, so a >80-char first prompt gets seeded at 100 and then rewritten to 80 on the first detail load. Harmless (one-time), just not the clean no-op the comment implies.

Unbound connections drop the title silently. lifecycle.rs:319 returns Ok(()) when state.conversation_id is None, and per manager.rs:1613 a conversation opened from history isn't bound until the first prompt fires ConversationLinked. So a title published before the user's first prompt on a resumed connection is lost until the next prompt or detail load. That's fine behaviour — it just deserves a line of comment, since it reads like it can't happen.

No deleted_at IS NULL on either predicate. Both new writes can touch an unlocked soft-deleted row. refresh_auto_title never had that guard either (its callers did), so this isn't something you introduced — mentioning it only because refresh_codex_auto_titles does filter DeletedAt.is_null() and it'd be nice to be consistent.

Version nit, in your favour. The new comment says "Claude ACP 0.71+ generated titles", but we pin @agentclientprotocol/claude-agent-acp@0.69.0 (registry.rs:458) — and 0.69.0 already ships maybeUpdateSessionTitle and the title-bearing session_info_update. So the feature is reachable today; the comment undersells it.

Scope. The first-prompt seed is a second feature under a title about live ACP titles. It's small and well-tested so I don't feel strongly, but note that for Codex the sidebar already picks up thread names on every list_all_conversations via refresh_codex_auto_titles, and there's the ~1.5s post-turn backfill too — so the seed's marginal win is smaller than it looks, while it does widen the missing-sync surface above.


Summary: approach ✅, layering ✅, tests ✅. The chat-channel sync needs to come along for the ride before this can merge, and I'd want the CodeBuddy ping-pong addressed or at least explicitly bounded. Everything under "smaller stuff" is genuinely optional. Happy to look again once the sync is threaded through — thanks again for digging into this corner, the "running thread sits there untitled" thing has been annoying for a while.

Push ACP and first-prompt titles through the detached chat-channel sync
so bound forum topics follow the sidebar. Skip identical live title
events so CodeBuddy cannot flip the name every turn.
@Adam-Dalloul

Copy link
Copy Markdown
Contributor Author

Wired the title through to the chat-channel sync, detached the same way as the fetch path. Identical live titles are skipped so CodeBuddy does not flip the sidebar every turn.

The skip-cache decides whether a live ACP title ever reaches the
lifecycle worker, and nothing exercised that path — the
`conversation_id` guard could have swallowed the feature whole and
every suite would still be green.

Cover it end to end through the wire: a changed title emits, an
identical repeat does not, a title published before the row binds is
dropped without poisoning the cache, and a title-less
`session_info_update` stays off the path. Pin the two invariants the
skip rests on: `ConversationLinked` forgets the cache, and the
installed chat-channel handle survives `clone_ref`. Pin the soft-delete
predicate on both auto-title primitives.

Also fold the skip's test-and-set into one write lock. Split across two
acquisitions, the `ConversationLinked` emitted by a concurrent
`send_prompt_linked` can land in the gap and clear the cache entry we
just wrote, re-admitting the repeat it was recorded to suppress.
@xintaofei

Copy link
Copy Markdown
Owner

Re-reviewed 575e7229. Both blockers are genuinely fixed — thanks for turning that around so quickly, and for picking the detached sync rather than awaiting Telegram inline in the worker. The Arc<OnceLock> on ConnectionManager is the right shape too: web/mod.rs:769 builds the desktop-hosted web AppState from a clone_ref, so the install propagates there for free.

I checked the two things the fix depends on and they hold:

  • ConversationLinked is emitted only on the non-already_linked path (manager.rs:1146: "this is the write that makes already_linked true for every subsequent prompt"), so the skip-cache is not wiped every turn and the dedupe actually bites.
  • ConnectionManager and ChatChannelManager are both .manage()d on the builder (lib.rs:243,245) before setup runs, so the app.state::<…>() lookups can't panic.

I found five things worth changing and, since I couldn't push to the branch, I've fixed them on my side — see the bottom.

What I changed

1. The skip's test-and-set wasn't atomic. It was a read-lock check, then a separate write-lock set, then emit_with_state taking the lock a third time. I folded the test and the set into one critical section.

To be straight about this one: I first justified it with a ConversationLinked-lands-in-the-gap race, and that race turns out to be unreachable — admission requires conversation_id.is_some() and every ConversationLinked producer requires is_none(), so they can't interleave. The change is still worth having (one lock acquisition instead of three, and an obviously-atomic test-and-set), but it's defensive, not a live bug fix. The comment now says that instead of claiming a race.

2. No test coverage at all. This was the one that actually worried me. Nothing anywhere drove a session_info_update.title through emit_conversation_update — so the new conversation_id.is_some() guard could have swallowed the entire feature and all 2712 tests would still have been green. Added six:

  • session_info_title_emits_once_and_skips_an_identical_repeat — drives real wire JSON; a changed title emits, an identical one (and one differing only by surrounding whitespace) doesn't
  • session_info_title_dropped_while_unbound_is_accepted_after_link
  • session_info_without_a_title_emits_no_native_title — absent / null / whitespace-only
  • conversation_linked_clears_the_native_title_skip_cache
  • installed_chat_channel_survives_clone_ref — pins the Arc<OnceLock> sharing, i.e. the thing that makes the whole sync fix work through a clone
  • auto_title_writes_skip_soft_deleted_rows — pins the new deleted_at predicate on both primitives

Each was verified load-bearing rather than decorative: I removed each guard in turn and confirmed the matching test goes red, then restored.

guard removed result
dedupe condition → let admit = true ..._skips_an_identical_repeat FAILED
conversation_id.is_some() ..._dropped_while_unbound... FAILED
last_native_title = None in ConversationLinked conversation_linked_clears_... FAILED
both DeletedAt.is_null() filters auto_title_writes_skip_soft_deleted_rows FAILED
clone_ref gets a fresh OnceLock installed_chat_channel_survives_clone_ref FAILED

3. Documented the ConversationLinked clear honestly. It's a no-op today for the same reason the race in (1) is unreachable — unbound titles are never cached, and both producers fire only from the unbound state. Kept it, but the test doc now says it's pinned against a future rebind-a-live-connection path rather than pretending it's live.

4. "Recovered on the next detail load" isn't true for every agent. parsers/acp_native.rs records no session_info_update and can only ever title a session by its first prompt, so for a custom ACP agent a title dropped while unbound is just gone until the agent republishes. Scoped that claim in both comments.

5. Desktop install-ordering window. install_chat_channel sat at lib.rs:723, but the chat background tasks start at lib.rs:505. A /new landing in between would write its live title with no chat manager installed and skip the topic rename permanently — the later reconciliation passes only sync titles their own conditional UPDATE wrote, and that one has already converged. Moved the install above the chat block.

One thing I looked at and decided not to change

The detached spawn_sync_conversation_title_until_current has no single-flight, so an agent alternating distinct titles could stack concurrent syncs, each able to sit on Telegram's 60s editForumTopic. I went back and forth on this and concluded it's a pre-existing property of the shared sync design, not something this PR should fix: sync_conversation_title_until_current's own doc accepts concurrent syncs ("Two concurrent syncs for the same conversation converge on the same final value"), and per-conversation serialisation was already considered and rejected there because the lock would also be taken by the inline rename path and stall a user's rename behind a dead provider. The identical-repeat dedupe you added caps the ordinary case. Worth a follow-up against the sync design if we ever want background-only coalescing — not a blocker here.

Verification

On the final state, with my changes applied:

  • cargo check --features test-utils --all-targets — clean
  • cargo clippy --no-default-features --bin codeg-server --lib -- -D warnings — clean
  • cargo check --no-default-features --bin codeg-mcp — clean
  • cargo test --no-default-features --bin codeg-server --lib2718 passed, 0 failed
  • cargo test -- --list | sort | uniq -d — empty (I'd fat-fingered a duplicate #[test] attribute at one point; this is how I caught it)
  • Frontend is byte-identical to 533ab885, whose vitest run was 324 files / 4462 tests green

Landing

Your branch is on a fork with maintainerCanModify: false, so I can't push the follow-up commit to this PR. It's sitting on task/84 as f91063fc on top of your 575e7229 — I'll land the two together from there, so there's nothing left for you to do.

Really nice work on this one. The layering was right from the first commit, and the Arc<OnceLock> install is a cleaner answer to the sync problem than the parameter-threading I'd suggested. 🙏

@Adam-Dalloul

Adam-Dalloul commented Aug 23, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for the thorough pass!

Landing it from task/84 works for me. I have turned on maintainer edits across my open PRs here too.

@xintaofei

Copy link
Copy Markdown
Owner

Thanks for opening up edit access — the follow-up is now in the PR itself as f91063fc, so please ignore the "it's sitting on task/84" note at the end of my last comment. Nothing for you to pull or cherry-pick; the branch fast-forwarded cleanly on top of your 575e7229.

PR head is now f91063fc, 13 files / +721 / -8, mergeable. Re-verified on that exact commit:

  • cargo check --features test-utils --all-targets — clean
  • cargo clippy --no-default-features --bin codeg-server --lib -- -D warnings — clean
  • cargo check --no-default-features --bin codeg-mcp — clean
  • cargo test --no-default-features --bin codeg-server --lib — 2718 passed, 0 failed
  • frontend untouched since 533ab885 (vitest 324 files / 4462 tests green there)

From my side this is good to merge. 🚀

@xintaofei
xintaofei merged commit cb4b5a6 into xintaofei:main Aug 23, 2026
7 checks passed
xintaofei added a commit that referenced this pull request Aug 23, 2026
Google Antigravity joins the built-in agents.
Sub-agent transcripts and task details slide in from the right instead of opening as a dialog you have to close.

## New

- **Google Antigravity is a built-in agent.** Install and launch it from the agent list — history, resume, skills, experts, Office tools, MCP and delegation all wired up — with a settings page for its four sign-in methods. Apple Silicon, Linux and Windows; Google publishes no Intel Mac build.
- **Panels slide in beside your work instead of covering it.** The mobile sidebar, aux panel, terminal, settings navigation, skill details and task details are inset drawers now, with nothing dimmed behind them.
- **Session viewers stack.** Sub-agent, work-task and grok child transcripts nest inside one another rather than burying each other, and no longer close when their card scrolls away or you switch tabs.
- **Repository items open in a side panel.** The full description, every label and the same Start action — without leaving for the forge and losing your filters, page and scroll position.
- **Choose which navigation rows the sidebar shows.** Automations, To-dos and the Repository panel each switch off in the view options and stay reachable from quick actions.
- **A session takes its title as soon as the agent picks one**, along with any forum topic bound to it. (#526, @Adam-Dalloul)
- **Workspace backgrounds accept GIF**, and animated ones play through the blur and the frosted panels.
- **Reclaim a finished task's leftover worktree.** One button in the task drawer removes the checkout and its work branch; the task stays on the board.

## Improved

- **Updated bundled agents.** DeepSeek Harness 0.6.0, OpenCode 1.18.21, Cline 3.0.57.
- **DeepSeek Harness 0.6.0 support.** Image prompts keep their pictures, compaction reports tokens and elapsed time, and forking, image upload and multi-provider models turn on when the agent advertises them.
- **The sidebar's view options are sorted out.** Toggles move into submenus so Sort by and Section order come first, and expand/collapse-all becomes its own header button that folds the flat sections too.
- **System messages show a preview.** Claude Code's post-`/compact` summary was a shut accordion; it renders clamped now, with a toggle only when there is more to see.

## Fixed

- **Codex sessions created by other tools display properly.** One recorded without Codex's usual event channel came back as a bare "Used 25 tools". (#452)
- **pi keeps its status lines out of its answers**, and routes retries to the shared banner. (#525)
- **A proxy typed without `http://` no longer breaks agent installs**, which used to abort with an unexplained `ERR_INVALID_URL`; `socks5://` addresses are left alone.
- **Claude's model picker no longer lists `null`.** Binding a provider with no model wrote literal nulls into `~/.claude/settings.json`.
- **Office lock files stop previewing themselves**, each costing a tab and a watcher over a `~$` file that can never render.
- **A GitLab account shows its initials when its avatar can't load**, instead of an empty circle.
- **The comment written back on an issue stops linking to the wrong page**, and a locally merged task no longer reads as work that shipped.
- **A repository row keeps its task badge.** A stale lookup blanked it, so an issue already being worked on offered Start — and got a second task.
- **A finished task's worktree removal isn't refused.** Merge recovery left a dead connection behind that read as an agent still working in there.
- **A task in review survives a cancelled session.** (#546, @lizzjin)
- **An agent home written as `~/…` resolves to the right place.** Hermes built a literal `~` directory beside its launch directory; Antigravity and DeepSeek were refused write access to the tree they actually use.

Thanks to @Adam-Dalloul and @lizzjin for contributing to this release.

-----------------------------

# 发布版本 0.28.0

Google Antigravity 加入内置智能体。
子会话、任务详情改从右侧滑出,不再是看完必须关掉的弹窗。

## 新增

- **Google Antigravity 成为内置智能体。** 可从智能体列表安装启动,历史会话、恢复、技能、专家、Office 工具、MCP 与委托均已接通,并有独立设置页配置它的四种登录方式。支持 Apple 芯片 Mac、Linux 与 Windows;Google 未发布 Intel Mac 版本。
- **面板改从侧边滑出,不再盖住整个界面。** 移动端侧边栏、辅助面板、终端、设置导航、技能详情、任务详情都换成留边抽屉,背后不再压一层灰罩。
- **会话查看器可以层叠。** 子智能体、看板任务、grok 子会话的转写层层嵌套而不互相盖掉,卡片滚出屏幕或切换标签页时也不会自己关掉。
- **仓库条目在侧边面板里打开。** 完整描述、全部标签和同一个「开始」操作都在,不必跳去网页再回来重找筛选、页码和滚动位置。
- **侧边栏显示哪些导航入口可以自己选。** 自动化、待办任务、仓库面板都能单独关掉,关掉后仍可从快捷操作进入。
- **智能体刚定下标题,会话就立刻改名**,绑定的论坛话题同步跟上。(#526@Adam-Dalloul)
- **工作区背景支持 GIF**,动图在磨砂与模糊图层下照常播放。
- **已结束任务残留的工作树可单独回收。** 任务抽屉里一个按钮删掉检出目录与工作分支,任务仍留在看板上。

## 改进

- **内置智能体版本更新。** DeepSeek Harness 0.6.0、OpenCode 1.18.21、Cline 3.0.57。
- **适配 DeepSeek Harness 0.6.0。** 带图提问不再丢图,上下文压缩会显示 token 数与耗时;会话分叉、图片上传、多供应商模型在智能体自报支持时自动开启。
- **侧边栏显示选项重新归置。** 各类开关收进子菜单,排序方式与分区顺序回到最上;全部展开/折叠独立成标题栏按钮,也能折叠聊天、最近这类扁平分区。
- **系统消息直接显示预览。** Claude Code 执行 `/compact` 后的续写摘要此前只是个折叠条,现在限高显示,内容超出时才出现展开按钮。

## 修复

- **其他工具创建的 Codex 会话能正常显示了。** 缺少 Codex 常规事件通道的记录打开后只剩一句「使用了 25 个工具」。(#452)
- **pi 不再把自己的状态提示混进回答**,重试改走统一提示条。(#525)
- **代理地址没写 `http://` 不再让智能体装不上**,此前会以一句没头没尾的 `ERR_INVALID_URL` 失败;`socks5://` 等已带协议的地址保持原样。
- **Claude 的模型选择器不再列出一排 `null`。** 绑定没有配置模型的供应商时,会往 `~/.claude/settings.json` 里写入 null。
- **Office 锁文件不再自动预览**,此前每个 `~$` 文件都白占一个标签页和一个监听进程。
- **头像加载不出来时 GitLab 账号显示名称首字母**,不再是个空白圆圈。
- **回写到议题下的评论不再链到无关页面**,就地合并的任务也不再宣称成果已经推送。
- **仓库条目的任务标记不会莫名消失。** 过期查询会把它抹掉,于是已在处理的议题又显示「开始」,再点一次就多一个重复任务。
- **已完成任务的工作树不再拒绝删除。** 合并恢复留下的死连接被当成「还有智能体在里面干活」。
- **进入评审的任务不会被会话取消顶掉。**(#546@lizzjin)
- **写成 `~/…` 的智能体主目录能正确解析。** Hermes 会在启动目录旁建出真名为 `~` 的文件夹;Antigravity 与 DeepSeek 则反被挡在真正所在的目录之外。

感谢 @Adam-Dalloul@lizzjin 为本次发布做出的贡献。
xintaofei added a commit that referenced this pull request Aug 24, 2026
Google Antigravity joins the built-in agents.
Sub-agent transcripts and task details slide in from the right instead of opening as a dialog you have to close.

## New

- **Google Antigravity is a built-in agent.** Install and launch it from the agent list — history, resume, skills, experts, Office tools, MCP and delegation all wired up — with a settings page for its four sign-in methods. Apple Silicon, Linux and Windows; Google publishes no Intel Mac build.
- **Panels slide in beside your work instead of covering it.** The mobile sidebar, aux panel, terminal, settings navigation, skill details and task details are inset drawers now, with nothing dimmed behind them.
- **Session viewers stack.** Sub-agent, work-task and grok child transcripts nest inside one another rather than burying each other, and no longer close when their card scrolls away or you switch tabs.
- **Repository items open in a side panel.** The full description, every label and the same Start action — without leaving for the forge and losing your filters, page and scroll position.
- **Choose which navigation rows the sidebar shows.** Automations, To-dos and the Repository panel each switch off in the view options and stay reachable from quick actions.
- **A session takes its title as soon as the agent picks one**, along with any forum topic bound to it. (#526, @Adam-Dalloul)
- **Workspace backgrounds accept GIF**, and animated ones play through the blur and the frosted panels.
- **Reclaim a finished task's leftover worktree.** One button in the task drawer removes the checkout and its work branch; the task stays on the board.
- **Tell the agent how to land the merge.** The merge dialog gains an optional instructions box — "prefer this branch's side on any conflict", "update the changelog on the way" — and a merge waiting in the queue lands under the instructions it was queued with. (0.28.1)

## Improved

- **Updated bundled agents.** DeepSeek Harness 0.6.0, OpenCode 1.18.21, Cline 3.0.57.
- **DeepSeek Harness 0.6.0 support.** Image prompts keep their pictures, compaction reports tokens and elapsed time, and forking, image upload and multi-provider models turn on when the agent advertises them.
- **The sidebar's view options are sorted out.** Toggles move into submenus so Sort by and Section order come first, and expand/collapse-all becomes its own header button that folds the flat sections too.
- **System messages show a preview.** Claude Code's post-`/compact` summary was a shut accordion; it renders clamped now, with a toggle only when there is more to see.

## Fixed

- **Codex sessions created by other tools display properly.** One recorded without Codex's usual event channel came back as a bare "Used 25 tools". (#452)
- **pi keeps its status lines out of its answers**, and routes retries to the shared banner. (#525)
- **A proxy typed without `http://` no longer breaks agent installs**, which used to abort with an unexplained `ERR_INVALID_URL`; `socks5://` addresses are left alone.
- **Claude's model picker no longer lists `null`.** Binding a provider with no model wrote literal nulls into `~/.claude/settings.json`.
- **Office lock files stop previewing themselves**, each costing a tab and a watcher over a `~$` file that can never render.
- **A GitLab account shows its initials when its avatar can't load**, instead of an empty circle.
- **The comment written back on an issue stops linking to the wrong page**, and a locally merged task no longer reads as work that shipped.
- **A repository row keeps its task badge.** A stale lookup blanked it, so an issue already being worked on offered Start — and got a second task.
- **A finished task's worktree removal isn't refused.** Merge recovery left a dead connection behind that read as an agent still working in there.
- **A task in review survives a cancelled session.** (#546, @lizzjin)
- **An agent home written as `~/…` resolves to the right place.** Hermes built a literal `~` directory beside its launch directory; Antigravity and DeepSeek were refused write access to the tree they actually use.
- **A merged task shows "worktree removed" right away**, instead of keeping the old badge until the board is refetched. (0.28.1)

Thanks to @Adam-Dalloul and @lizzjin for contributing to this release.

-----------------------------

# 发布版本 0.28.0 && 0.28.1

Google Antigravity 加入内置智能体。
子会话、任务详情改从右侧滑出,不再是看完必须关掉的弹窗。

## 新增

- **Google Antigravity 成为内置智能体。** 可从智能体列表安装启动,历史会话、恢复、技能、专家、Office 工具、MCP 与委托均已接通,并有独立设置页配置它的四种登录方式。支持 Apple 芯片 Mac、Linux 与 Windows;Google 未发布 Intel Mac 版本。
- **面板改从侧边滑出,不再盖住整个界面。** 移动端侧边栏、辅助面板、终端、设置导航、技能详情、任务详情都换成留边抽屉,背后不再压一层灰罩。
- **会话查看器可以层叠。** 子智能体、看板任务、grok 子会话的转写层层嵌套而不互相盖掉,卡片滚出屏幕或切换标签页时也不会自己关掉。
- **仓库条目在侧边面板里打开。** 完整描述、全部标签和同一个「开始」操作都在,不必跳去网页再回来重找筛选、页码和滚动位置。
- **侧边栏显示哪些导航入口可以自己选。** 自动化、待办任务、仓库面板都能单独关掉,关掉后仍可从快捷操作进入。
- **智能体刚定下标题,会话就立刻改名**,绑定的论坛话题同步跟上。(#526@Adam-Dalloul)
- **工作区背景支持 GIF**,动图在磨砂与模糊图层下照常播放。
- **已结束任务残留的工作树可单独回收。** 任务抽屉里一个按钮删掉检出目录与工作分支,任务仍留在看板上。
- **合并时可以给智能体补充说明。** 合并对话框新增可选的说明框——「冲突时以本分支为准」「顺手更新一下 changelog」——排队等待的合并也沿用入队时填的说明。(0.28.1)

## 改进

- **内置智能体版本更新。** DeepSeek Harness 0.6.0、OpenCode 1.18.21、Cline 3.0.57。
- **适配 DeepSeek Harness 0.6.0。** 带图提问不再丢图,上下文压缩会显示 token 数与耗时;会话分叉、图片上传、多供应商模型在智能体自报支持时自动开启。
- **侧边栏显示选项重新归置。** 各类开关收进子菜单,排序方式与分区顺序回到最上;全部展开/折叠独立成标题栏按钮,也能折叠聊天、最近这类扁平分区。
- **系统消息直接显示预览。** Claude Code 执行 `/compact` 后的续写摘要此前只是个折叠条,现在限高显示,内容超出时才出现展开按钮。

## 修复

- **其他工具创建的 Codex 会话能正常显示了。** 缺少 Codex 常规事件通道的记录打开后只剩一句「使用了 25 个工具」。(#452)
- **pi 不再把自己的状态提示混进回答**,重试改走统一提示条。(#525)
- **代理地址没写 `http://` 不再让智能体装不上**,此前会以一句没头没尾的 `ERR_INVALID_URL` 失败;`socks5://` 等已带协议的地址保持原样。
- **Claude 的模型选择器不再列出一排 `null`。** 绑定没有配置模型的供应商时,会往 `~/.claude/settings.json` 里写入 null。
- **Office 锁文件不再自动预览**,此前每个 `~$` 文件都白占一个标签页和一个监听进程。
- **头像加载不出来时 GitLab 账号显示名称首字母**,不再是个空白圆圈。
- **回写到议题下的评论不再链到无关页面**,就地合并的任务也不再宣称成果已经推送。
- **仓库条目的任务标记不会莫名消失。** 过期查询会把它抹掉,于是已在处理的议题又显示「开始」,再点一次就多一个重复任务。
- **已完成任务的工作树不再拒绝删除。** 合并恢复留下的死连接被当成「还有智能体在里面干活」。
- **进入评审的任务不会被会话取消顶掉。**(#546@lizzjin)
- **写成 `~/…` 的智能体主目录能正确解析。** Hermes 会在启动目录旁建出真名为 `~` 的文件夹;Antigravity 与 DeepSeek 则反被挡在真正所在的目录之外。
- **合并完成的任务立刻显示「工作树已删除」**,不必等整页刷新才更新徽章。(0.28.1)

感谢 @Adam-Dalloul@lizzjin 为本次发布做出的贡献。
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants