Status: current implementation model
Last reviewed: 2026-08-29
CleanerX persists one active target Agent and exposes the selector in the left side of the bottom status deck. Switching targets atomically saves the preference, clears the old selection/detail state, and creates a fresh Agent-specific snapshot. A cleanup plan is bound to that snapshot, and execution dispatches from the snapshot's AgentKind; paths and backups from different Agents cannot be mixed.
Startup and manual settings refresh probe all four compiled adapters and return an explicit Rust-owned detection state. installed means the adapter found its executable or, for Codex, a recognized desktop application root; dataOnly means the executable is absent but recognized local storage remains available; notFound means neither installation evidence nor recognized storage was found. The target selector lists only installed Agents and omits redundant installation labels. dataOnly and notFound states remain visible in Settings, which always shows all four bounded home-directory overrides so a non-default installation can be configured, saved, and probed again; the frontend never promotes a discovered path into availability on its own.
The complete metadata-only inventory snapshot remains behind the Rust boundary. The initial scan response contains non-session cleanup items, aggregate session counts, source values, and per-project count/size summaries, but no session records or session cleanup items. It also carries a compact selection index containing only each selectable session item's ID, thread ID, project association, and size; this lets current-scope and project selection include unloaded rows without transferring their display metadata. The Sessions view requests pages of at most 100 direct matches only for an expanded visible project or the visible flat-list range. Tree pages may additionally include the already-inventoried ancestors required to preserve hierarchy, while separately identifying the actual filtered matches so context rows remain non-selectable. Automatic pages are prefetched ahead of the viewport without inserting visible loading rows; only a failed page adds an inline retry row. Every page and project-summary request is bound to the current snapshot ID and validated filters; cleanup planning still resolves selected IDs against the complete backend snapshot.
Codex Home resolution order is user override, CODEX_HOME, then ~/.codex. The executable is resolved from PATH, known macOS application resources, /usr/local/bin and /usr/bin on Unix, and common NVM/Volta/asdf/Bun/npm/pnpm user locations. On Windows, discovery includes .exe, .cmd, and .bat launchers beneath the bounded roaming/local npm and pnpm roots. Linux and Windows desktop launches therefore do not depend on an interactive shell PATH. Recognized macOS and Linux application-data directories are separate optional allowlisted roots. CleanerX does not guess private Windows app-container paths; Windows cleanup remains rooted in the public CODEX_HOME layout and official App Server operations, never a source or project directory.
CleanerX initializes an App Server JSON-RPC connection with the required initialize request and initialized notification, then probes mutation methods independently with invalid parameters that identify no real session or memory scope. On Unix it prefers the existing control socket through codex app-server proxy; a missing, stale, or unresponsive socket falls back to a temporary stdio server. Windows uses the documented stdio transport directly and does not interpret a filesystem entry named ipc.sock as a live writer. Failure of the available transports disables session mutations. It calls thread/list for active and archived pages and thread/loaded/list for runtime blockers. No message preview or turn body is retained in the inventory model. After an explicit detail action, thread/read with includeTurns: true loads a bounded read-only preview without resuming the thread; a recognized rollout parser is the read-only fallback.
A session display title is resolved from read-only metadata in this exact order: non-empty threads.name, non-empty session_index.thread_name, non-empty threads.title, non-empty threads.first_user_message, then 未命名会话. session_index.jsonl and recognized state-database columns may supplement App Server results without enabling private database writes. Title resolution trims blank metadata, never uses the final component of cwd, and retains only the chosen display string rather than an additional message body.
When App Server listing fails, CleanerX may query a recognized state database read-only. Required columns are checked before queries. This fallback always disables mutations.
Claude Code Home resolution order is user override, CLAUDE_CONFIG_DIR, then ~/.claude. CleanerX recognizes only the application-data layout documented by Claude Code: UUID-named transcripts directly beneath projects/<project>/, the matching session directory and per-session tasks, file-history, image-cache, uploads, session-env, and debug entries, project memory/, and the documented cache/temporary roots. It never scans a source directory; transcript cwd values are grouping metadata only.
Inventory reads transcript top-level metadata with a 2 MiB bound and retains only the session ID, title, absolute working directory, entrypoint, and timestamps. Unknown JSON fields, including message/tool bodies, are skipped by the typed metadata decoder and never enter the snapshot. An explicit detail action may read a bounded transcript or project-memory Markdown preview. UUID/schema mismatches, symbolic links, foreign ownership, and unknown paths are skipped or made read-only.
Claude Code currently exposes project-wide claude project purge, but not a per-session deletion command. Its official application-data documentation also explicitly permits deleting the listed local paths by hand. CleanerX therefore performs per-session and per-project-memory deletion through its existing fixed-root file transaction: capture a metadata-only source revision, reject active writers, optionally commit and verify an encrypted backup, revalidate the revision and filesystem identity, remove only the snapshot-owned paths, then rescan. If a recognized Claude Code process is present, every writable Claude item is blocked. Files in the legacy or undocumented ~/.claude/sessions directory are not treated as live-writer evidence because they can outlive the process and Claude does not publish them as a supported per-session writer contract. CleanerX never invokes project purge because its project-wide scope would silently include unselected history and configuration state.
Protected Claude Code data includes ~/.claude.json (outside the cleanup root), credentials, settings, CLAUDE.md, keybindings, rules, commands, agents, skills, plugins, themes, hooks, policy caches, and configuration backups. Project .claude/ directories and source trees are never visited.
OpenCode data resolution uses the user override, then XDG_DATA_HOME/opencode, then ~/.local/share/opencode on every supported platform (where ~ is %USERPROFILE% on Windows). The executable is resolved from PATH and the same bounded set of common user package-manager locations used by the other CLI adapters, including Windows .exe, .cmd, and .bat shims. CleanerX recognizes the Windows desktop opencode-cli.exe sidecar only as a conservative read-only writer signal; because its location and embedding are not a documented public CLI contract, it is never selected as a mutation executable. The regenerable cache root is resolved separately from XDG_CACHE_HOME/opencode or ~/.cache/opencode; it never expands the data-directory allowlist into a project directory.
CleanerX recognizes the official project and session SQLite columns before reading metadata from opencode.db or one unambiguous channel database. Session title, directory, parent ID, project ID, archive time, and timestamps are read without loading transcript bodies. Logical per-session size is estimated from recognized session-owned projection and event rows; official deletion can make database pages reusable without immediately shrinking the SQLite file, and CleanerX never runs a private VACUUM or edits SQLite rows. Unknown or ambiguous schemas stay read-only while logs, cache metadata, and protected paths remain inventoryable.
OpenCode session deletion rechecks a durable database metadata revision and expands descendants in the review before invoking an official mutation route for the minimal root set. The revision covers opencode.db and a present opencode.db-wal; it deliberately excludes opencode.db-shm, whose reader-lock metadata can change during CleanerX's own read-only inventory and does not contain durable session state. When no related OpenCode writer is running, CleanerX uses opencode session delete <sessionID> with the recognized database selected through the official OPENCODE_DB override. While OpenCode is running, CleanerX permits deletion only when every related writer exposes an explicit loopback --port, its public health and session routes verify the expected database session, and GET /session/status reports the full deletion scope as idle; deletion then uses DELETE /session/:id on that same verified server. A busy or retrying root/descendant is blocked. Default in-process TUI transport, dynamic or non-loopback ports, authentication challenges, endpoint failures, excessive directory scopes, and any unverified writer fail closed and block all session deletion. CleanerX never probes arbitrary ports, sends credentials, writes SQL, or deletes the database file.
When backup is selected, CleanerX runs the documented opencode export <sessionID> route for the full expanded set into a private temporary directory. Export, import, log cleanup, and cache cleanup continue to require every related OpenCode writer to exit even when inactive session deletion is available through a verified server. The verified JSON exports are encrypted into the .cxb archive before deletion and the plaintext staging directory is removed. Restore extracts exports only into private staging, rejects duplicate or existing session IDs and unavailable original working directories before import, then uses opencode import <file> from the recorded directory. A failed multi-session import compensates by deleting sessions already imported through the same official CLI route. OpenCode does not expose a supported automatic-memory capability, so CleanerX shows no OpenCode memory cleanup item.
Protected OpenCode data includes authentication, configuration, plugins, skills, rules, commands, agents, source-managed worktrees/repositories, legacy or unmigrated JSON storage, plans, and the SQLite database/WAL files themselves. These may be shown as protected metadata but are never opened or selected. An explicit session detail action may query bounded message/part projections read-only; an explicit log detail action may read bounded lines and warns that logs can contain prompts, paths, and tool output.
pi agent-directory resolution uses the user override, then PI_CODING_AGENT_DIR, then ~/.pi/agent. The executable is resolved from PATH and the same bounded set of common user package-manager locations used by the other CLI adapters; it is used only for version reporting because pi's documented deletion route is file-based. Project-local .pi/ directories are never visited; a session cwd is grouping metadata only.
CleanerX recognizes the documented session layout: one JSONL file per session beneath sessions/--<working-directory>--/<timestamp>_<uuid>.jsonl. Inventory reads a bounded 2 MiB prefix of each file and retains only the session header (UUID ID, absolute cwd, creation timestamp, optional parentSession reference), the latest session_info display name, and—only when no explicit name remains—a normalized first-user-message title limited to 96 Unicode characters. This matches pi's own session selector without retaining the rest of the transcript. Other message, tool, and compaction bodies are parsed transiently and never enter the snapshot. Files with an unrecognized first line, duplicate session IDs, symbolic links, and unknown entries are skipped or warned about without blocking the rest of the inventory. Sessions forked through /fork or /clone reference their parent file via parentSession; CleanerX shows that lineage in the tree but never treats a fork as a deletion descendant, because removing one pi session file never removes another.
pi's official documentation states that sessions are removed by deleting their .jsonl files. CleanerX therefore performs session deletion through its fixed-root preflighted path transaction: capture a metadata-only source revision, block every mutable pi item while a pi process is running, optionally commit and verify an encrypted backup, revalidate the revision and filesystem identity, remove only the snapshot-owned session file, then rescan. The regenerable models-store.json provider-catalog cache is the only non-session writable item; it is removed through the same transaction while pi is not running.
Protected pi data includes auth.json credentials, settings.json, models.json provider configuration, trust.json project trust decisions, keybindings.json, AGENTS.md/SYSTEM.md/APPEND_SYSTEM.md rules and system prompts, and the prompts, skills, extensions, themes, git, and npm extension and package directories. These may be shown as protected metadata but are never opened or selected. pi exposes no supported automatic-memory capability, so CleanerX shows no pi memory cleanup item. An explicit session detail action may read a bounded read-only preview of user/assistant text and tool output blocks from the session JSONL.
| Category | Initial selection | Backup | Codex route | Claude Code route | OpenCode route | pi route |
|---|---|---|---|---|---|---|
| Current/archived session | Off | Claude/OpenCode/pi only | App Server thread/delete; no supported import route |
Snapshot-owned session paths beneath Claude Code Home | Official session delete; backup/restore through export / import |
Documented session-file removal beneath the pi agent directory |
| Automatic memory | Off | Claude Code project memory only | App Server memory/reset (global); no supported import route |
Selected project memory/ directory |
Unsupported; no item shown | Unsupported; no item shown |
| Attachment/generated content | Off | Only with a restorable owning session | Removed only after successful owning-session deletion; orphan media is inspect-only | Session-owned documented attachment/cache paths only | Not independently targeted | Not independently targeted |
| Logs/history | Off | Agent-specific | Validated SQLite transaction | Recognized history.jsonl path removal |
Recognized log-directory path removal while offline | Not currently targeted |
| Regenerable cache | Off | No | Allowlisted path removal | Documented cache roots beneath Claude Code Home | Fixed XDG cache root only | models-store.json provider-catalog cache |
| Temporary data | Off | No | Allowlisted path removal | Documented temporary roots beneath Claude Code Home | Not currently targeted | Not currently targeted |
| Auth/config/rules/skills/plugins/source-managed data | Never | N/A | Protected | Protected | Protected | Protected |
Title, working directory, and project association are three independent fields. Projects require positive association evidence: a non-empty absolute session cwd must fall beneath a root in Codex's recognized project registry or beneath an ancestor with a .git marker. An arbitrary absolute working directory is recognition metadata, not a project root; in particular, a standalone desktop chat workspace such as Documents/Codex/<date>/<name> stays unassigned unless it independently satisfies one of those checks. A child with no recorded cwd may inherit an already resolved parent association; CleanerX never resolves an empty or relative cwd against its own process directory. Sessions that still have no association remain outside projects and are displayed under the UI-only “No project” virtual root. If the project registry schema is unavailable or unrecognized, CleanerX keeps the inventory available and falls back only to verifiable Git roots. Project cleanup selects linked Agent records only. It never makes a project directory an allowed mutation root.
Content preview is separate from inventory. The Tauri boundary accepts only an item ID from the current snapshot, selects the adapter from that snapshot, resolves its known paths on the Rust side, rejects symlinks and paths outside the selected Agent's fixed roots, and applies block/byte limits. Recognized memory and log schemas are queried read-only; unknown SQLite/JSONL schemas and unsupported binaries produce an explicit unavailable preview instead of a guessed query or raw dump. The Codex media gallery uses a separate thumbnail command that reads at most the first supported image (PNG, JPEG, GIF, or WebP) up to 5 MB from a visible attachment/generated item; it cannot return text or enumerate content into the frontend. Protected authentication and configuration content is never opened.
Memory semantics and entry-editing safety requirements are specified in the Agent memory capability and safety model. Unfinished implementation work is tracked only in the development roadmap. Instructions and rules remain protected even when an Agent presents them alongside memory in its own UI.
planned ──┬──────────────────────────────→ deleting[n] → verifying → verified → complete
└→ backup phases → committed ─↗
any non-terminal state → failed/incomplete → startup Agent rescan
├→ observed applied → complete
├→ verified restore → recovered
└→ user safe-close → terminated
Journal format v2 records the immutable cleanup plan, selected Agent and snapshot, expanded session scope, item categories, backup phase, and the pre/post state of every planned mutation. Each transition is written through an atomic JSON replacement before execution continues. Backup is an explicit option and defaults off. It is offered only when every item in the complete expanded plan has a supported restore path; mixed restorable/irreversible selections must be split if the user wants a backup. The review always warns when cleanup is irreversible, including routes such as Codex session deletion and global memory reset that have no supported import path.
When backup is selected, eligible items are collected relative to a named fixed root, hashed with SHA-256, archived as tar + zstd, encrypted with age X25519, and written to a sibling .partial file. Unix uses same-directory rename and Windows uses MoveFileExW replacement with write-through semantics for journals, catalogs, and archives. CleanerX records archive creation, verification, and catalog commit separately; it reopens the committed archive, decrypts it, and verifies the exact manifest plus every payload path, size, and hash before entering it in the catalog or starting mutation. If a later cleanup step fails, the journal retains the committed backup identity. A catalog-bound candidate is also recoverable when a crash occurred after catalog commit but before the final journal update. .partial, unverified, mismatched-operation, and mismatched-Agent backups are never offered for recovery.
The age identity is stored in the operating system's native credential store: macOS Keychain, the Linux desktop Secret Service, or Windows Credential Manager. Credential-store failure aborts backup creation before mutation. Direct filesystem targets must stay on the same filesystem device or Windows volume as their category-specific allowlisted root. Symbolic links, Windows reparse points (including junctions), nested mount/volume boundaries, foreign ownership, and changed Unix device/inode or Windows volume/file-index identities are rejected before mutation and checked again at execution. Windows ownership is matched to the current process token's default owner SID, including an owner-capable group selected by an elevated token, rather than assuming every valid file owner is the token's user SID.
After cleanup, CleanerX scans the same Agent again. A selected session that remains is a failed verification; CleanerX does not try to repair a private database or broaden the deletion set.
At startup, a recognized non-terminal v2 journal blocks new cleanup only for its owning Agent. CleanerX first rescans that Agent and compares current adapter-owned IDs with the immutable plan; journal progress alone never proves success. The recovery dialog can be dismissed for browsing and work with other Agents, while the affected Agent's mutation command remains blocked until the operation is accepted, restored, or safely closed. Partial or conflicting current data is never overwritten or automatically merged. Strictly recognized pre-v2 status-only journals are obsolete metadata and are removed during inventory instead of entering recovery. Unknown or malformed committed journals remain visible and block mutation because CleanerX cannot prove their format; non-authoritative sibling partial files are ignored because initial committed journal creation precedes every mutation.
An archive records its AgentKind and is decrypted into a private staging directory. Every entry must be a unique regular relative path; the payload set, sizes, and SHA-256 hashes must match the manifest exactly. The catalog Agent, manifest Agent, and restore adapter must match.
Before commit, CleanerX resolves every target beneath its fixed Agent root and rejects lexical escape, symbolic links, Windows reparse points, ownership or filesystem/volume mismatches, duplicate destinations, and any existing destination. Each verified payload is then copied to a uniquely named sibling partial file and flushed. The final commit uses a platform-native no-replace rename (renamex_np(RENAME_EXCL) on macOS, renameat2(RENAME_NOREPLACE) on Linux, and MoveFileExW without replacement on Windows), so a concurrent destination cannot be overwritten and cross-filesystem copy never writes directly to a final path.
If an in-process error occurs before or after any destination commit, CleanerX revalidates the created file identities, removes committed files in reverse order, removes staged sibling files, and removes only the empty directories created by that restore. Fault-injection tests cover both sides of every destination commit and prove unrelated source-tree bytes remain unchanged. OpenCode session exports use private import staging and official ID/directory preflight instead of becoming permanent filesystem destinations. Startup recovery is marked recovered only after a post-restore scan rediscovers every planned restored item.
CleanerX does not offer a restorable backup for Codex session deletion or global memory reset. Restoring rollout or memory files alone cannot re-create the corresponding private App Server state, and CleanerX never repairs private Codex SQLite records. These mutations remain available with an explicit irreversible warning. A future read-only archive/export feature may preserve content for inspection, but it must not be presented as Agent restoration.
Official Codex references: App Server protocol and transports and the native Windows app execution model.
Official Claude Code references: application data and project purge, sessions and transcript location, background-agent runtime state, and project auto memory.
Official OpenCode references: storage locations, CLI session/delete/export/import commands, server session APIs, and the official session schema.
Official pi references: session storage and deletion, the session file format, agent directory override via PI_CODING_AGENT_DIR, and the models-store catalog cache.
Backup deletion is an explicit, separately confirmed action. CleanerX accepts only a backup ID from the current catalog and derives the sole valid archive path as <backup-directory>/<id>.cxb; altered paths, symlinks, and non-file entries are rejected. The encrypted archive is removed before its catalog entry. If an earlier interruption already removed the archive, retrying the action removes the stale catalog entry without touching any other path.