This release includes breaking changes for platform teams planning a safe upgrade.
Published 1mo
Developer Productivity
✓ No known CVEs patched
✓ No known CVEs patched in this version
Topics
agents
claude
claude-code
web
developer-tools
headless
+4 more
kanban
llm-tools
local-first
python
Summary
AI summaryUpdates html, unarchived, and live/wip/pending_tool/last_activity across a mixed release.
Full changelog
Added
- CCC now ships its own conversation-history indexer. A bundled
_history_index/package walks every Claude Code and Codex transcript on the machine into a SQLite FTS5 store at~/.claude-index/index.db. No separatepip install claude-indexstep, no manual launchd plist — the first time you search and no index exists, an inline OOBE prompt offers to build one in the background. A topbar pill ("📚 History · 12m ago") shows the freshness of the most-recently-indexed message, spins while ingesting, and exposes manual re-trigger on click. Ifsqlite-vecand Ollama (nomic-embed-text) are present locally, semantic embeddings come along for the ride:/api/search-history?semantic=1runs hybrid retrieval (top-K BM25 ∪ top-K vec, fused via RRF) and tags each result_source ∈ {bm25, vec, fused}. Sidebar rows that match via the vec path get a purple "semantic history" badge instead of the lexical-only blue "history" one, so the user can see when semantic recall is doing the work. Falls back to BM25 silently when sqlite-vec / Ollama isn't available — semantic is opportunistic, never a prerequisite. The on-disk format is unchanged; existing standaloneclaude-indexinstalls coexist on the same file. - New
🧹 Clearbutton on each group chat row alongside the existing ✏️ rename and 📦 archive. Clears the chat's message history (header + sidecar preserved), writes a system log line marking the wipe, and explicitly nudges all participants so they re-engage with the fresh whiteboard. Useful when a session got stuck in a no-op loop and a clean slate kicks the conversation forward. Backend: newPOST /api/group-chats/clearand_group_chat_clearhelper; the nudge re-fires_register_coordinationfirst so an idle-dropped chat comes back to life on clear. - URLs in fenced code blocks, in inline code that mixes a URL with other text, and in inline Bash tool-result output (
tool-result-output<pre>) are now clickable one-click anchors instead of plain text. New_linkifyEscapedUrls(html)helper post-processes already-HTML-escaped content and wrapshttps?://...substrings in<a target="_blank" rel="noopener">. Used insiderenderCodeBlock/highlightCodeand as a fallback inside therenderInlineinline-code branch when the content has a URL alongside other text. - Three changes to the group-chat sidebar workflow: (1) the reader pane now behaves like any other session view — clicking another conversation in the sidebar switches in-place instead of requiring a "← Back" click; (2) sessions can be dragged from the conversation list onto a chat row to add them as participants (new
/api/group-chats/add-participantendpoint,/group-chatis auto-injected into the session); (3) clicking the new "+" button on the section header creates an empty group chat (server now acceptssession_ids: []so a topic-only chat is valid). - Closed group chats stay visible until you archive them. The "In Group Chat" sidebar section now shows both active and recently closed coordinations — closed rows are ghosted with a small "closed" pill so you can still open the reader and review the conversation. Each row gets a 📦 Archive button that persistently moves the chat into the per-repo Archived section (rendered inline alongside session rows with a 💬 icon prefix). Cross-repo group chats appear in the Archived section of every participating repo. New endpoints:
POST /api/group-chats/archive,POST /api/group-chats/unarchive,GET /api/group-chats/archived?repo_path=…; the existingGET /api/group-chats/activenow returns both active and closed (unarchived) chats with astatusfield, and the topbar badge counts onlystatus === 'active'chats. - Group chat sidebar entries now expose more "is this thing actually moving?" signal: (1) each indented participant row carries a "last activity" chip (time since the session's transcript was last touched), a yellow WIP chip when the session has an in-flight tool, and a "waiting" chip on whoever the orchestrator would nudge next; (2) the chat row itself shows the chat file's last-modified timestamp inline, plus a sub-line summary like
Watcher → waiting on CHUCKso the next-expected speaker is obvious without opening the reader. Backend_list_group_chatsnow returns per-participantparticipant_meta(live/wip/pending_tool/last_activity) pluslast_author_hash,last_author_is_human, andwaiting_on_hashesfor the row hint, mirroring the nudge-targeting logic so the UI summary matches what the watcher would actually do. - Click the model pill on a session card to switch model + context. The model badge that used to be a read-only tooltip is now a button: tap it and a small popover opens with the right list for that session's engine — Claude (
opus-4-7,sonnet-4-6,haiku-4-5, with a 1M-context toggle for opus/sonnet), Codex (gpt-5.5,gpt-5-codex,o3,o3-mini), or Gemini (gemini-2.5-pro,gemini-2.5-flash). An "Other…" text input lets you type any model the underlying CLI accepts, so unreleased models work the day they ship. For live Claude sessions (TTY or CCC-spawned) the picker injects/model <alias>[1m]straight into the running process via the existing_inject_text_into_sessionroute — same plumbing as/api/inject-input. For Codex, Gemini, and dormant Claude (where the engines don't support runtime model swaps) the choice is persisted to a new~/.claude/command-center/session-overrides.jsonsidecar and applied as--model …on the next resume; the pill renders a small→ nextchip until then. Backed byPOST /api/session/<id>/model(and/clearto reset to the session default). - Multi-session coordination: Ctrl/Shift-click sessions in the conversation list, click "Coordinate…", enter a topic, and Claude Code sessions self-organize via a fresh per-topic group-chat file. Live-reader panel in the conv pane lets you follow and participate directly from the CCC.
- Group chat rows now show their participants in an indented list directly below each chat header, with names pulled from the chat's
name_map. Conversation rows whose session participates in any active or closed-but-unarchived chat get a new "💬 IN GROUP CHAT" badge in their signal chip row. Sessions in a chat used to be filtered out of the main In Progress list entirely; they're now visible there alongside the badge AND in the chat's indented list, so the user can see at a glance which conversations are currently coordinated. - Each participant in the indented list under a chat row now has a small
×button (visible on hover) that drops the session from the chat. NewPOST /api/group-chats/remove-participantupdates the sidecar; the watcher's nudge loop readssession_idsfresh each tick, so the removed session stops being nudged immediately. - Three related polish items for group chats: (1) message author tags now render as
<8-hash>: <name>instead of bare hashes — both forwards (skill writes the new format directly using the chat sidecar'sname_map) and backwards (the reader frontend's expansion converts old— b1216dcf 👋lines into— b1216dcf: CHUCK 👋); (2) each chat row gets a ✏️ rename button on hover that prompts for a new topic and updates the sidecar via a newPOST /api/group-chats/rename, with a system log line marking the change; (3) the "+" button on the In Group Chat header no longer prompts — it creates a chat with the default topic "empty chat" and you rename it via the ✏️ button afterwards. - Group chats now log every orchestrator action inline as a
> _<ts> — system: <action>_line in the chat file: chat creation, add participant, remove participant, archive/unarchive, and per-tick nudges (with the list of pinged session names). Watcher feedback loop is suppressed by advancing the in-memory mtime baseline past each system write so administrative log lines aren't treated as participant activity — without that, every "pinged" line would itself trigger another nudge a minute later.
Changed
- Stop the In Progress and GH Issues repo groups from reshuffling on every poll tick. When two folders' max-modified timestamps differ by less than 5 minutes, the previous-render order is preserved instead of swapping rank — so a fresh tool-call in repo B no longer bumps it above repo A every refresh. Brand-new folders still enter at their natural position.
- Tighten the
/group-chatskill so participants don't bail on a quiet chat. The user explicitly invited them, and the chat header's topic line counts as a topic — sessions used to read an empty file, conclude "no topic, no participants" and immediately👋 Leave, leaving every chat dead-on-arrival when participants didn't wake up at exactly the same time. New rules: introduce yourself once with a💬, wait through re-injections, and only👋 Leaveafter either (a) actually engaging two-way and resolving, (b) 10 minutes of dead silence (real-meeting rule), or (c) the topic is plainly the wrong room. The Leave action's bullet now points back at the joining section. - Tighten the
/group-chatskill's joining rules further after observing sessions still bailing on first read. New strict rules: (1) one post per skill invocation, then exit — posting💬 standing byand👋 Leaveback-to-back at the same timestamp is the bug being fixed; (2) don't evaluate the topic at all (placeholder topics, "Untitled" topics, etc., do not justify leaving — the user adds the real topic later); (3) re-arrival with no new content means exit silently, do not re-introduce; (4) explicit list of forbidden phrases ("no topic", "nothing to coordinate", "leaving — ping me later") that flag premature exits.👋 Leaveis now allowed only on work-resolved or 10-minute timeout, never on topic evaluation. - Merge the global topbar (Worktrees / Stats / Terminal / Vercel / History pills) into the main toolbar row, recovering ~33px of vertical space at the top of the page. The buttons now sit at the right end of
#convToolbarinstead of in their own fixed-position bar above the sidebar/main split. - When a Human posts to a group chat, the watcher's nudge now pings ONLY the agent who wrote immediately before the Human (the most likely intended recipient of the reply) instead of fanning out to everyone except the Human. Pinging everyone caused N-1 sessions to waste a turn introducing themselves to a question that wasn't for them. Falls back to the everyone-except-last-writer behavior when the last author is an agent or there's no prior agent in the tail (fresh-thread case). The regex that detects authors now matches both
<8-hex>agent tags and bareHumanmarkers.
Fixed
- Stop archived chats from being resurrected at server boot.
_start_coordination_watcherre-registered every recently-modified chat regardless of itsarchivedflag, so a chat the user explicitly archived via 📦 would silently come back to life on the next restart and the watcher would resume nudging participants of a chat they thought was closed for good. Worse: those participants might be in another active chat too, and would receive/group-chat chat="<old-archived-path>"injects for the wrong chat. The boot recovery now reads each chat's sidecar and skips entries witharchived: true. - Context-usage pill now resets after
/compact. Previously thectx N / limitfigure stayed pinned to the pre-compact peak because the JSONL extractor walked every assistant turn and letlatest/peakaccumulate over the whole file — pre-compact turns no longer contribute to the live context window, so this overstated the displayed usage until the next post-compact assistant turn. Fixed by detecting the{type: system, subtype: compact_boundary}event Claude Code emits at each compaction (manual or auto) and resetting the running totals at that boundary. The pill now reflects only the post-most-recent-compact segment, matching what the user sees in the TUI. The extracted dict now also carries acompact_countfield for downstream consumers. - Stop hanging
/api/conversations/allon cold cache —_resolve_session_cwdwas running anos.walkper stale-cwd row inside the cross-project bulk scan, compounding into a multi-minute hang. Resolution now happens lazily via the per-repofind_session_cwd/find_conversationspaths (already cached) instead. - Two related fixes for group-chat coordination races. (1)
_register_coordinationwas clobbering existing watcher entries — including resettinglast_nudgeto 0 — every time it was called. In the clear / add-participant flows that meant: between the register call and the explicit nudge that followed, the background watcher could tick, see the file changed ANDlast_nudge=0(debounce passed), and fire its OWN nudge, racing with our explicit one. Twopinged …log lines at the same second. Now the function preserves an existing entry'slast_nudgeand only refreshesmtime+last_activity. (2) Skill rule 0 added to Section 2: the chat file is the only source of truth — sessions must Read the file fresh inside the invocation before deciding whether they've already posted, never rely on conversational memory. Hopefully unblocks sessions like Chuck that hallucinate having posted when the file shows no such entry. - Remove duplicate "In Group Chat" header in archive mode —
renderArchiveListwas injecting a second copy on top of the onerenderConversationListalready rendered, leaving two stacked headers in the sidebar whenever a coordination was active. - Group-chat sessions were guessing their own identity when
$CLAUDE_SESSION_IDwas unset in their shell — picking thename_mapentry whose display name "felt right" for their role, sometimes posting under another participant's hash. Two fixes: (1) the orchestrator's inject command now passessid="<full-session-id>"explicitly, so the skill always has a reliable source regardless of shell env; (2) the skill's Identity step is rewritten as numbered substeps with explicit "do not guess" / "do not impersonate" wording — when sid is unavailable OR the hash isn't inname_map, the session is required to post a single💬flagging the problem and exit, never substitute another participant's identity. - Fix the "In Group Chat" sidebar section not appearing after creating a coordination — single-session chats were blocked by a stale min-2 client check, the section refresh only fired up to 15s later, and the change-detector compared list lengths instead of paths so identical-count swaps were missed.
- Show the "In Group Chat" sidebar section on a clean reload. The polling was wired inside
setArchiveFolderFilter, so it only kicked in when the user touched the folder filter — a fresh page load left_gcActiveChatsempty and the section silently never appeared. Polling is now set up once at boot. Bonus: the same handler used to start a new 15ssetIntervalon every folder change, leaking timers; that's gone too. - Fix session rows whose recorded cwd was moved by resolving the new folder from transcript file-path evidence.
- After viewing a group chat and clicking another conversation, the standard "Send to terminal" input box was missing. The reader had been replacing the entire conv-pane's
innerHTML, which created new DOM nodes with the same IDs but orphaned the boot-time element references that other handlers were bound to. The reader now renders into#conversationsViewonly and toggles the surrounding input bar's visibility, so the input/send wiring stays live across reader open/close cycles. Also covers chat-message author hash IDs (e.g.— 25ea49ae) being expanded into display names in the reader, with the short hash shown alongside each name in the indented participants list. - The watcher's nudge loop was pinging the most recent author on every tick, creating exactly the response loop the exclude logic was meant to prevent. The regex captured the full tail of the chat-header line (
b1216dcf: CHUCK 💬) and looked it up inname_mapby display_name (CHUCK) — every match missed,exclude_sidstayed None, last writer got nudged again, wrote a reply, watcher fired again, repeat. Match against the 8-char hash prefix instead and look it up againstsession_idsdirectly — works for both the new<hash>: <name> <emoji>format and the legacy bare-<hash> <emoji>one. - Stop the watcher from firing nudges on chats that have gone quiet — the recent fix to ping only the addressed agent had a hole when the trailing chat window contained nothing but system
pingedlines (no real author posts in the last 3K bytes). With no author detected, exclude/only logic stayed null, the nudge fell through to ping-everyone, wrote anotherpingedline, the next watcher tick saw that as a change and re-fired — self-perpetuating loop with no actual activity behind it. Two changes: (1) expanded the tail window from 3K to 12K bytes so the regex can see participant posts even after dozens of system entries pile up; (2) if the window still has no authors, the nudge skips with{"ok": true, "skipped": "no recent author"}— no inject, no log line, no follow-up tick. - Make
~/...paths in conversation messages clickable hyperlinks alongside the existing/Users/...paths, opening in the default editor via/api/open. - Stop the watcher from continuously re-pinging participants in 60-second loops with same-second duplicate log lines. Two changes inside
_coordination_watcher: (1) hold_coord_lockacross the read-of-last_nudge AND the write that claims it, so concurrent ticks can't both pass the debounce check and both fire — eliminates the same-secondpinged …dups; (2) belt-and-suspenders post-nudge baseline bump — after the nudge writes its log line, re-stat the file and overwriteentry["mtime"]so the next tick sees the post-log mtime as already-baselined and skips. The earlier in-line bump inside_group_chat_log_systemshould have covered this, but the loop kept happening; the explicit re-stat is defensive against any path that misses the bump.
Weekly OSS security release digest.
The CVE patches and breaking changes that affected production tools this week. One email, every Sunday.
No spam, unsubscribe anytime.
Share this release
About CCC
All releases →Related context
Related tools
Earlier breaking changes
- v5.0.1 Removes horizontal-drag gesture that collapsed conversation pane.
Beta — feedback welcome: [email protected]