server/src/channels/routes.ts:59-66 already names this as a fact rather than a bug report:
How many channels one page holds. The sidebar asked for all of them on every render, one row per
channel-agent pair, and nothing removes a channel: somebody who talks to their Bot daily
accumulates thousands, so a query that is instant in a demo returns thousands of rows on every
page load for every employee, and grows monotonically.
app/src/lib/channels/queries.ts:46 says the same thing from the frontend side. There is no
DELETE /api/channels/:channelId, no menu item, no way to remove a conversation from the roster
at all.
@copilotkit/runtime/v2's CopilotKitIntelligence client already exposes
deleteThread({threadId, userId, agentId}): Promise<void> — OpenBot never calls it.
Proposed: a hard delete. DELETE /api/channels/:channelId removes the channels row (which
cascades channel_memberships, channel_agents, and intelligence_channel_mappings — no
migration needed), then calls deleteThread on the local commit, in that order: local commit
first means a failed upstream call only leaves an invisible orphaned thread, never a channel that
looks alive with its history silently gone. Removal fans out over the existing
channel_activity Postgres NOTIFY topic so other tabs/replicas see it live. A sidebar row menu
(mirroring the destructive-item pattern already used in /skills) triggers it, with a two-step
inline confirm (same pattern as agent-profile.tsx's delete) since this is irreversible.
A soft-delete/trash-with-retention version is a reasonable follow-up (audit-retention.ts is
basically the exact batching + advisory-lock pattern it would reuse) but needs its own migration
and a recovery screen, so I'd rather scope this first PR to the hard delete and leave that as a
separate issue if there's interest.
Happy to open a PR for the hard-delete version if this approach sounds right.
server/src/channels/routes.ts:59-66already names this as a fact rather than a bug report:app/src/lib/channels/queries.ts:46says the same thing from the frontend side. There is noDELETE /api/channels/:channelId, no menu item, no way to remove a conversation from the rosterat all.
@copilotkit/runtime/v2'sCopilotKitIntelligenceclient already exposesdeleteThread({threadId, userId, agentId}): Promise<void>— OpenBot never calls it.Proposed: a hard delete.
DELETE /api/channels/:channelIdremoves thechannelsrow (whichcascades
channel_memberships,channel_agents, andintelligence_channel_mappings— nomigration needed), then calls
deleteThreadon the local commit, in that order: local commitfirst means a failed upstream call only leaves an invisible orphaned thread, never a channel that
looks alive with its history silently gone. Removal fans out over the existing
channel_activityPostgres NOTIFY topic so other tabs/replicas see it live. A sidebar row menu(mirroring the destructive-item pattern already used in
/skills) triggers it, with a two-stepinline confirm (same pattern as
agent-profile.tsx's delete) since this is irreversible.A soft-delete/trash-with-retention version is a reasonable follow-up (
audit-retention.tsisbasically the exact batching + advisory-lock pattern it would reuse) but needs its own migration
and a recovery screen, so I'd rather scope this first PR to the hard delete and leave that as a
separate issue if there's interest.
Happy to open a PR for the hard-delete version if this approach sounds right.