Skip to content

[Feature] Let ACP clients exempt sessions they still reference from retention (age and count limits) #1895

Description

@addyCooks

Description

Nanocoder deletes saved sessions that have not been accessed for retentionDays (default 30) and, beyond maxSessions (default 100), the least recently accessed ones. A client that keeps a durable list of conversations, such as Nanocoder Desktop, has no way to tell Nanocoder "I still reference this session, don't delete it". When retention removes a session, the client's thread silently becomes unusable for continuing.

I'd like:

  1. A way for an ACP client to mark sessions as retained, so retention skips them.
  2. A way to read the effective retention settings, so a client can tell users what will happen to threads that are not retained.

Tracked from Nano-Collective/nanocoder-desktop#12 (item 9).

Use Case

Nanocoder Desktop keeps one durable thread per conversation and uses the Nanocoder session ID as the join key (its success item SP-06 is "close and relaunch everything, then continue the same chat"). A thread idle for more than 30 days is deleted the next time any Nanocoder process starts, along with its plan, task and walkthrough artifacts. After that the desktop can only show the thread view-only and offer "start a new thread".

The same applies to any ACP client that stores session IDs, such as an editor extension with a thread list.

How the desktop would use it:

  • When it creates or keeps a thread, it marks the session retained.
  • On "Remove from desktop" it clears the mark, so removed threads don't stay forever.
  • On "Delete chat history" it calls session/delete.
  • When a thread's session is missing, it reads the retention settings to explain the likely cause ("not opened for N days") and to tell users up front how retention treats old threads.

Today the only way to avoid this is to raise retentionDays and maxSessions in the user's nanocoder-preferences.json. That is the user's own configuration, and a client must not rewrite it. Neither setting can be turned off, since both have a minimum of 1.

Milestone 4 of the desktop plans for this. Until then the fallback is the view-only state with an explanation ("accept and explain"), so this is not blocking.

Proposed Solution

  1. A retained mark on a session. An extension method, for example _nanocoder/pinSession {sessionId, pinned: boolean}, stores it in the session file and index. The method name follows the convention settled in [Feature] Rename ACP extension methods to the reserved _ prefix and advertise them under _meta in agentCapabilities #1893.
  2. Retention skips retained sessions. cleanupOldSessions and enforceSessionLimits leave them alone. Whether they count toward maxSessions is an open question below.
  3. Explicit deletion still works. session/delete and the CLI's delete remove a retained session, with its artifacts.
  4. Report it. session/list entries (or their _meta) say whether a session is retained, so a client can find marks it no longer needs.
  5. Expose the settings (read-only). For example in initialize's _meta, or a separate method: the effective retentionDays and maxSessions.

Alternatives Considered

  • A "touch" method that refreshes lastAccessedAt without replaying the session. It is small and reuses the age logic, but it only delays deletion, and the client has to call it for every thread on a schedule.
  • Resume every thread periodically. session/resume already refreshes lastAccessedAt, but it replays history and loads the session into memory. That is too heavy to do for every thread.
  • Do nothing and document it ("accept and explain"). The client tells users that old threads may disappear, and they raise the limits themselves. This is the desktop's fallback today.
  • A client-side registry of "referenced" IDs that Nanocoder checks. More flexible, but stale references would keep sessions forever, and the mark is simpler to reason about.
  • Edit the user's retention settings. Ruled out: it conflicts with Nanocoder owning its configuration.

Additional Context

  • I have searched existing issues to ensure this is not a duplicate
  • This feature aligns with the project's goals (local-first AI assistance)

Current behavior, checked against main at fc01e23:

  • source/session/session-manager.ts (~L117-135): initialize() runs cleanupOldSessions() once per process. ACP calls sessionManager.initialize() in newSession, loadSession and resumeSession (source/acp/acp-agent.ts ~L142, L173, L677).
  • source/session/session-manager.ts (~L567-607): cleanupOldSessions deletes every session whose lastAccessedAt is older than retentionDays (default 30, minimum 1), together with its artifacts.
  • source/session/session-manager.ts (~L521-565): enforceSessionLimits deletes the least recently accessed sessions beyond maxSessions (default 100, minimum 1). It is called only from createSession (~L164), which the terminal UI's autosave uses (source/hooks/useSessionAutosave.ts). ACP saves through saveSession (source/acp/acp-agent.ts ~L1110, L1140). The store is shared between the CLI and ACP processes, so the count cap can still remove ACP sessions.
  • source/session/session-manager.ts (~L418-430): loadSession refreshes lastAccessedAt. readSession (~L404) does not.
  • source/session/session-manager.ts (~L75-86, L189-200) and source/session/session-validation.ts: the index entry is built from an explicit list of fields, and validation requires only the existing fields, so a new field is allowed in a session file but must be added to the index projection.
  • docs/features/session-management.md: retentionDays and maxSessions have a minimum of 1, so retention cannot be turned off, and deleting or expiring a session deletes its artifacts too.

Implementation Notes (optional)

Add an optional field to Session and SessionMetadata (see question 1), include it in toSessionMetadata and in the index entry built by saveSession, and filter retained sessions out of both cleanup functions. renameSession already shows the pattern for an optional flag that survives autosave (titleManuallySet).

A limitation to document: protection applies only when the process running cleanup knows about the field. An older Nanocoder sharing the same store could still delete a retained session.

Open questions for maintainers:

  1. Boolean or holder list? A plain retained: true is simplest. A retainedBy: string[] list of client names (from clientInfo.name at initialize) lets the VS Code extension and the desktop share a store without one clearing the other's protection. The session stays protected while the list is non-empty.
  2. Pin flag or touch method? I prefer the flag because it protects against both rules, and the touch method is the fallback if a smaller change is wanted.
  3. Do retained sessions count toward maxSessions? If they do, many retained sessions can push unretained recent ones out.
  4. Privacy. Retention exists partly so conversation data doesn't sit on disk forever. Should retained sessions be limited in number, or visible in a CLI list so users can unpin them?
  5. Who may clear the mark? Only the client that set it, or any client or the CLI?
  6. Method names and whether to read settings through initialize _meta or a separate method.

Acceptance criteria:

  • A client can mark a session retained and clear the mark.
  • Neither the age rule nor the count rule deletes a retained session.
  • session/delete still deletes a retained session, together with its artifacts.
  • session/list shows whether a session is retained.
  • The mark survives autosave, rename and resume.
  • Unretained sessions are cleaned up exactly as before.
  • A client can read the effective retentionDays and maxSessions.
  • Docs describe the mark, the settings, the limits, and the older-process caveat.

Test plan:

  • Unit (session-manager): cleanupOldSessions and enforceSessionLimits keep retained sessions and delete the others.
  • Unit: the mark round-trips through the session file and the index.
  • ACP spec: set the mark, age the session past the cutoff, restart, and confirm the session is still listed and resumable.
  • ACP spec: session/delete removes a retained session.
  • ACP spec: the retention settings are reported with their effective values.
  • Regression: sessions without the mark are deleted as before.

Out of scope: changing the default limits, a UI for managing retained sessions, and the timeline snapshots and checkpoints, which have their own pruning (7 days, 20 sessions).

Activity

  1. added
    area:agentAgent loop: context, compaction, subagents, memory, sessions
    on Oct 26, 2026
  2. self-assigned this
    on Oct 26, 2026
  3. added
    needs-triageQueued for automated issue triage
    architectureNeeds a structural design change
    area:vscodeVS Code extension and host integration
    and removed
    needs-triageQueued for automated issue triage
    on Oct 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

architectureNeeds a structural design changearea:agentAgent loop: context, compaction, subagents, memory, sessionsarea:vscodeVS Code extension and host integrationenhancementNew feature or request

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions