feat: auto-archive inactive chats with audit trail (#24642)

Adds a background job in `dbpurge` that periodically archives chats
inactive beyond a configurable threshold. Each archived root chat gets a
background audit entry tagged `chat_auto_archive`. Disabled by default.

* New `AutoArchiveInactiveChats` SQL query with LATERAL last-activity
subquery and partial index on archive candidates
* `site_configs`-backed `auto_archive_days` setting with admin-only PUT,
any-authenticated-user GET
* Cascade archive via `root_chat_id`; pinned chats and active threads
exempt
* Root-only audit dispatch on detached context, matching manual archive
(`patchChat`) behavior
* 11 subtests covering disabled no-op, boundary, deleted messages, child
activity, pinned exemption, multi-owner, idempotency, and batch
pagination

PR #24643 adds per-owner digest notifications.
PR #24704 adds the requisite UI controls.

> 🤖
This commit is contained in:
Cian Johnston
2026-04-24 14:18:28 +01:00
committed by GitHub
parent 346b46228f
commit a876287d36
26 changed files with 1504 additions and 46 deletions
+31 -1
View File
@@ -326,8 +326,38 @@ appear in the `files` field on subsequent
| Status | Meaning |
|-------------------|------------------------------------------------------------------------------|
| `waiting` | No pending work (newly created, finished, or interrupted). |
| `waiting` | Idle. Newly created, finished successfully, or interrupted. |
| `pending` | Queued for processing. |
| `running` | Agent is actively working. |
| `paused` | Agent is paused (for example, waiting for user input). |
| `completed` | Agent finished and the task is complete. |
| `error` | Agent encountered an error. |
| `requires_action` | Agent invoked a client-provided tool and needs the result before continuing. |
## Configuration
Deployment-wide chat settings are read and written under
`/api/experimental/chats/config/*`. Reading config requires authentication; writing requires
deployment-admin privileges.
### Auto-archive window
Chats whose newest non-deleted message is older than
`auto_archive_days` are automatically archived by a background job.
Pinned chats and chats belonging to a still-active thread are
exempt. `0` disables the feature; the default is 90.
```sh
# Read
curl -H "Coder-Session-Token: $CODER_SESSION_TOKEN" \
https://coder.example.com/api/experimental/chats/config/auto-archive-days
# { "auto_archive_days": 90 }
# Update
curl -X PUT -H "Coder-Session-Token: $CODER_SESSION_TOKEN" \
-H "Content-Type: application/json" \
-d '{"auto_archive_days": 60}' \
https://coder.example.com/api/experimental/chats/config/auto-archive-days
```
Accepted range: `0` to `3650` (~10 years).
@@ -0,0 +1,84 @@
# Conversation Auto-Archive
Coder Agents automatically archives long-inactive conversations so they
drop out of active chat lists without any user intervention. Archived
conversations are still visible (and can be unarchived) until they age
out of the separate retention window, at which point they are purged.
## How it works
A background process runs approximately every 10 minutes. On each tick
it scans the chat database for root conversations whose most recent
non-deleted message is older than the configured auto-archive window
and flips them from "active" to "archived". Cascaded children (chats
linked into a larger conversation via `root_chat_id`) are archived
alongside their parent so the conversation stays coherent.
Activity is defined as the most recent non-deleted message in the
conversation family, counting messages from every role. Root chats
whose status indicates ongoing work (`running`, `pending`, `paused`,
or `requires_action`) are never selected for auto-archiving.
Children inherit their root's archival decision.
Pinned root conversations (those with a non-zero pin order) are never
selected for auto-archiving. Children are archived alongside their
root regardless of individual pin status. Admins and users who want
to retain a conversation long after its last message should pin the
root.
## Interaction with retention
Auto-archive and deletion are two independent controls:
| Control | What it does | Default |
|---------------------|---------------------------------------------------------------------------|-------------------|
| Auto-archive window | Moves inactive chats to the archived state | 0 days (disabled) |
| Retention window | Deletes chats that have been archived long enough and orphaned chat files | 30 days |
A conversation needs to be inactive for `auto_archive_days`, then
archived for `retention_days`, before it is deleted. The two windows
stack additively. With auto-archive disabled by default, inactive
chats are never auto-archived; once an admin opts in by setting a
non-zero `auto_archive_days`, a conversation lives for at least
`auto_archive_days + retention_days` from its last message before it
is permanently removed.
Auto-archive (like manual archive) resets the per-chat retention
clock, so the full `retention_days` runs from the tick that archived
the chat, not from its last message.
Setting either value to `0` disables that step. Setting
`auto_archive_days` to `0` means inactive chats are never
auto-archived (users still archive manually). Setting
`retention_days` to `0` means archived chats are kept indefinitely.
## Configuration
The auto-archive window is stored as the
`agents_chat_auto_archive_days` key in the `site_configs` table.
The default is `0` (disabled); set to a positive number of days to
enable auto-archiving.
Use the admin API to read or update the value:
GET /api/experimental/chats/config/auto-archive-days
PUT /api/experimental/chats/config/auto-archive-days
## Rollout advice
Auto-archive is disabled by default, so upgrading to a release that
includes this feature will not archive any existing chats until an
admin opts in. The first tick after enabling auto-archive on a
deployment with a long history will process up to 1,000 root chats
(and their children). If your deployment has a large backlog, the
initial rollout will span many ticks. This is intentional and avoids
stalling the rest of `dbpurge` during the first run. To disable,
set `auto_archive_days` back to `0`.
## Audit trail
Each auto-archived root chat produces an audit log entry with the
background subsystem tag `chat_auto_archive`. Cascaded children are
not audited individually. The audit entry records the chat ID, owner
ID, and organization ID, and the diff shows `archived` flipping from
`false` to `true`.
@@ -4,6 +4,10 @@ Coder Agents automatically cleans up old conversation data to manage database
growth. Archived conversations and their associated files are periodically
purged based on a configurable retention period.
Conversations become eligible for purging only after they are archived. Old
conversations can be archived manually, or automatically. See
[Auto-Archive](./chat-auto-archive.md) for how the two controls interact.
## How it works
A background process runs approximately every 10 minutes to remove expired