merge a /clear chain into one conversation

The list collapses each chain to its newest link — the only one that can be
resumed — carrying the root's title and start time, the summed message count
and a part badge. The detail splices the chain's transcripts oldest-first with
a divider between parts, server-side, so the client's index-window pagination
needed no change.

The divider says "context cleared — nothing above this is in memory", because
the whole risk of merging is that the history reads as continuous when the
agent's context is not. Delete cascades the chain and the confirm says how many.

Supersedes the "continues X" line from the previous commit: there is nowhere to
link to once the parent is scrolled up above you.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-07 00:44:33 +00:00
co-authored by Claude Opus 5
parent 3e01de100e
commit 47894702ac
10 changed files with 296 additions and 67 deletions
+8 -6
View File
@@ -74,10 +74,10 @@ chatRouter.get('/sessions/:id', async (ctx) => {
const windowed = limit != null || beforeRaw != null;
const messages = windowed ? detail.messages.slice(start, end) : detail.messages;
// The title the list shows this session under, and what it continues. Resolved against `detail.cwd`
// — the transcript's own directory — not the requested `cwd`, which on a deep link is still the
// default group and holds none of this session's neighbours. OpenCode has no lineage of its own, so
// it gets neither rather than a fabricated answer.
// The title the list shows this session under, and how many `/clear` parts it spans. Resolved against
// `detail.cwd` — the transcript's own directory — not the requested `cwd`, which on a deep link is
// still the default group and holds none of this session's neighbours. OpenCode has no chains of its
// own, so it gets neither rather than a fabricated answer.
const context = isOpenCodeSessionId(id) ? null : claudeSessionContext(email, detail.cwd, id);
return ctx.json({
@@ -86,11 +86,13 @@ chatRouter.get('/sessions/:id', async (ctx) => {
total,
offset: windowed ? start : 0,
title: context?.title ?? null,
continuedFrom: context?.continuedFrom ?? null,
partCount: context?.partCount ?? 1,
});
});
// DELETE /chat/sessions/:id[?cwd=] — remove a conversation from the owning harness's store.
// DELETE /chat/sessions/:id[?cwd=] — remove a conversation from the owning harness's store. For a
// Claude `/clear` chain that is every part of it: the list shows the chain as one conversation, so
// deleting it deletes one conversation.
chatRouter.delete('/sessions/:id', async (ctx) => {
const email = ctx.get('user').email;
const id = ctx.req.param('id');