Skip to content

fix(desktop): pass owning profile to server on session delete/archive#44138

Open
AIalliAI wants to merge 3 commits into
NousResearch:mainfrom
AIalliAI:fix/44117-session-delete-profile
Open

fix(desktop): pass owning profile to server on session delete/archive#44138
AIalliAI wants to merge 3 commits into
NousResearch:mainfrom
AIalliAI:fix/44117-session-delete-profile

Conversation

@AIalliAI

Copy link
Copy Markdown
Contributor

Fixes #44117

Problem

Deleting a session in the default profile from the desktop sidebar fails with "Delete failed — Session not found", and the session stays in the list.

Root cause

The desktop sidebar lists sessions via GET /api/profiles/sessions, which the primary backend serves by reading every profile's state.db directly from disk — so it always shows each profile's rows regardless of which profile the serving process itself runs as.

The mutations, however, are inconsistent about telling the server which profile owns the session:

Operation Profile reaches the server?
Read transcript (getSessionMessages) ?profile= query param
Rename (renameSession) profile in PATCH body
Delete (deleteSession) ❌ only request.profile (Electron's backend-process router)
Archive (setSessionArchived) ❌ only request.profile

When the dashboard process is actually scoped to a different profile than the desktop believes — e.g. after "Set as active" on the Profiles page, which flips the sticky active_profile file that the next legacy desktop launch (no --profile flag, see _apply_profile_override step 2) honors — DELETE /api/sessions/{id} opens the serving process's own state.db, doesn't find the row, and 404s. The same profile-confusion class was already fixed for the skills/toolsets endpoints (see the _profile_scope comment block in hermes_cli/web_server.py); session delete/archive were missed.

Fix

Match the established read/rename pattern — pass the owning profile to the server, not just to Electron's process router:

  • deleteSession(): append ?profile= to the path (the endpoint already accepts it). request.profile is kept so per-profile remote overrides still route correctly; interceptSessionRequestForRemote reads the param from searchParams and global-remote mode re-appends it, so both remote shapes are unaffected.
  • setSessionArchived(): include profile in the PATCH body, matching renameSession() (the endpoint's SessionRename model already reads body.profile).
  • delete_session_endpoint: resolve the id via resolve_session_id() first, for parity with its GET/PATCH siblings (they all resolve; DELETE was the only one doing a raw exact match).

Tests

  • tests/hermes_cli/test_web_server.py: DELETE with ?profile= deletes from that profile's state.db (regression test for this issue); DELETE resolves unique id prefixes; unknown id still 404s. Full file passes (265 passed).
  • apps/desktop/src/hermes.test.ts: delete sends ?profile= + request.profile; no param without an owning profile; archive sends body.profile. (vitest, jsdom)
  • tsc -b clean.

🤖 Generated with Claude Code

The desktop sidebar lists every profile's sessions straight from each
profile's on-disk state.db (GET /api/profiles/sessions), but
deleteSession() only passed the owning profile to Electron's backend
router — never to the server. When the serving process is scoped to a
different profile than the desktop believes (the sticky active_profile
file, flipped by "Set as active" on the Profiles page, is honored on a
legacy launch with no --profile flag), the DELETE looks up the session
in the wrong profile's state.db and 404s with "Session not found".

Reads already work because getSessionMessages() passes ?profile=, and
rename works because it puts profile in the PATCH body — delete and
archive were the odd ones out. Align them:

- deleteSession(): append ?profile= to the path (the endpoint already
  accepts it), keeping request.profile for Electron's process routing.
- setSessionArchived(): include profile in the PATCH body, matching
  renameSession() (the endpoint already reads body.profile).
- delete_session_endpoint: resolve the session id via
  resolve_session_id() first, for parity with its GET/PATCH siblings.

Fixes NousResearch#44117

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Regression test for the archive half of NousResearch#44117, prompted by a report
that the PATCH allowlist might 400 on 'archived'. That allowlist is in
gateway/platforms/api_server.py (the OpenAI-compatible platform API),
which the desktop never calls; the dashboard server's PATCH handler
already supports archived + profile, and this test pins that down.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ete-profile

# Conflicts:
#	apps/desktop/src/hermes.test.ts
@AIalliAI

Copy link
Copy Markdown
Contributor Author

Requesting maintainer review — this is ready to land from my side. Standalone fork CI is pending first-run approval here; the rollup branch in #44061 carrying this session's batch is fully green on upstream CI (all test shards, typecheck, e2e).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

P3 Low — cosmetic, nice to have type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Cannot delete sessions in default proffile / ID not found

2 participants