lc ("只可列出?不可删除?agent claude code") saw Claude Code sessions named by
uuid under "No folder" at "0 B", with "These Claude Code sessions are read
only; magpie can list them, but cannot resume or delete them." Those rows are
gateway projections from the usage ledger: Claude Code ran with its own files
somewhere magpie can't see (another computer, a container, another config
folder), so nothing native won on the same agent+id. An agent with only such
rows was sent as not deletable, and every row was read_only, so the page had
no Delete at all.
What changed:
- sessions.DeleteGateway moves the session's recorded text folders
(gateway-conversations/<date>/<identity hash>) into
trash/sessions/gateway/<ts>-<hash>/files/<date> with a note marked gateway,
and records {agent, id, last} in gateway-sessions-hidden.json.
/api/sessions and /api/sessions/manage leave it out up to that last
request; a newer request lists it again. The usage ledger is untouched, so
Usage, /api/sessions/one and stats keep what it spent. A session with a
request in the last minute is refused as active.
- Restore moves the text back (skipping what expired or was cleared, refusing
rather than overwriting a same-day re-recording), checks each item is this
session's identity folder inside the store, and unhides it. Delete forever
keeps it hidden.
- The recording's promises hold in the trash: the hourly prune erases
trashed gateway text past 7 days, "Delete saved conversations…" erases it
too, and HasGatewayConversations counts it.
- The manage route marks an agent with gateway rows deletable and sends
those rows deletable, with gateway: true and the size of their recorded
text; the delete route sends gateway ids to DeleteGateway and native ones
to Delete as before.
- Sessions page: gateway rows group under "Seen through the gateway"
instead of "No folder", show "{size} recorded" only when something was
recorded (no "0 B"), and the note says why they can't be resumed and what
Delete takes and keeps. The delete dialog says the same for gateway rows
instead of "Their files are moved…"; the Trash marks them and notes the
7-day expiry. New strings in zh, zh-TW, ja and de.
- docs/subsystems/gateway-sessions.md describes the delete, hidden list,
restore, expiry and clear.
Verified:
- go test -tags nogui ./... (sandbox HOME) green; gofmt -l clean; go vet
./...; GOOS=linux and GOOS=windows go vet -tags nogui ./internal/sessions
./internal/gui; go build, GOOS=linux -tags nogui and GOOS=windows builds.
- TestSessionsDeleteGatewaySession fails on the old code with "Claude Code
with only gateway sessions isn't deletable: [{Agent:claude
Deletable:false}]"; sessions-gateway-delete.test.cjs fails there too.
TestGatewayDeleteTrashExpires, TestGatewayPurgeKeepsItHidden,
TestGatewayDeleteNothingRecorded and TestGatewayRestoreRefusesForeignItems
cover expiry, purge, an unrecorded session and a forged note.
- GUI: sessions-gateway-delete.test.cjs in Chromium and WebKit, en, zh,
zh-TW, ja, de, at 1000 and 390px (in-app dialog, Cancel sends nothing,
click doesn't scroll, no left-border accents, no page overflow); all
sessions-*, gateway-conversations, checkbox-look, old-webkit,
sess-agent-strip, i18n-keys, gui-ja, gui-de and gui-zh-tw tests pass.
- Real try: magpie web from this tree on a sandbox HOME seeded with three
gateway-only Claude Code sessions (two recorded): deleted a recorded and an
unrecorded one, checked the store, trash and hidden list on disk, restored
the recorded one (its transcript reads again), purged the other (stays
hidden). That try found the Trash page breaking on an unrecorded session's
null items; DeleteGateway now writes [] (TestGatewayDeleteNothingRecorded
fails without it) and the page reads a missing list as empty.
Subsystem design references
These pages describe current responsibilities, runtime paths, state, and contracts. Start with the subsystem relevant to your task, then follow its source links to verify the implementation you change.
| Subsystem | Reference |
|---|---|
| Built-in subscriptions moving to plugins | Provider and plugin ownership |
| Plugins that run on the gateway's requests and replies | Gateway middleware |
| Claude subscription turns across Claude Code runs | Claude subscription bridge |
| How the gateway picks a provider, key or account, and falls back | Gateway routing and fallback |
| Gateway Session Aggregation | Gateway Session Aggregation |
| Connecting an agent's config to the gateway, drift, disconnect, WSL twins | Agent wiring |
| Providers, keys, subscription accounts, refresh and allowance | Providers and accounts |
| Instructions, MCP servers, skills and RTK written into agents | Library |
Tray, panel, window, magpie web and the page's JSON API |
GUI app shell |
A subsystem can span several packages or repositories. Its reference describes the behavior those parts provide together; it does not need to list every function.
Maintain a reference
Update the owning reference in the same PR when a change alters a documented responsibility, interface, state transition, runtime path, compatibility rule, or failure behavior. An internal refactor that preserves these contracts does not require a prose change.
Keep each fact in one reference and link to it from agent instructions or other pages. Link to source files and named symbols rather than copying implementation details that change frequently. Explain field meanings and constraints that signatures alone do not express.
For a new reference, include the responsibilities and boundaries, source of truth, main runtime paths, state transitions, important constraints, and existing verification commands. Include failure and compatibility behavior where relevant. Document current behavior separately from proposed changes; the proposal belongs in its issue or PR.
Describe a PR's semantic changes
For each affected subsystem, explain the observable change with its trigger and before/after behavior. Include changes to interfaces, state, compatibility, and failure behavior when relevant. Link the affected reference, implementation, and verification evidence. State when a refactor preserves the documented behavior.
For example, a change to plugin adoption should explain which migration state and account condition trigger it, which implementation handles requests before and after adoption, and which test exercises that transition.
This is an author-provided explanation that a reviewer verifies against the diff and surrounding code. It is not an automatically proven compatibility verdict.
Review a change
- Identify the affected subsystem and the actual runtime path, including plugin or built-in ownership.
- Compare the claimed before/after behavior with the base and head implementation. Trace relevant callers and state transitions beyond the changed lines.
- Check that changed contracts appear in the owning reference and that source links still resolve.
- Check that the cited tests or runtime evidence exercise the claimed behavior. Report checks that were not run.
A documentation update alone does not prove the implementation. Existing tests and runtime evidence remain necessary for behavior changes.