Skip to content

fix(ui): remove scope=project from session list requests#571

Closed
JDis03 wants to merge 15 commits into
NeuralNomadsAI:devfrom
JDis03:fix/session-list-directory-scope
Closed

fix(ui): remove scope=project from session list requests#571
JDis03 wants to merge 15 commits into
NeuralNomadsAI:devfrom
JDis03:fix/session-list-directory-scope

Conversation

@JDis03

@JDis03 JDis03 commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Problem

PR #565 (commit e29c3a0) added scope=project to session list requests to avoid pagination limits. With OpenCode 1.17.x, the /session endpoint ignores the directory filter when scope=project is present and returns all sessions from the global SQLite database instead.

This makes the same session appear in every project tab. Deleting a session from one tab deletes it from the shared DB, affecting all of them.

Reproduction

# Pick any opencode workspace port
PORT=4096
curl -s -u "codenomad:$PASSWORD" \
  "http://127.0.0.1:$PORT/session?directory=/home/user/project-a&scope=project" \
  | jq 'length'
# Returns sessions from project-b too — should only return project-a's

curl -s -u "codenomad:$PASSWORD" \
  "http://127.0.0.1:$PORT/session?directory=/home/user/project-a" \
  | jq 'length'
# Correct: only project-a's sessions

Related: anomalyco/opencode#33113

Fix

Drop scope: "project" from buildProjectSessionListOptions(). The directory parameter alone correctly filters sessions to the requested folder on OpenCode 1.17.x. scope: "project" can be reintroduced once the backend respects directory alongside it.

Validation

  • directory=/test → 1 session (correct)
  • directory=/idm → 1 session (correct)
  • Same session no longer appears in multiple project tabs
  • Deleting a session in one project no longer affects others

JDis03 and others added 15 commits June 15, 2026 20:35
Implements immediate visual feedback when SSE transport disconnects (wifi drops, Cloudflare idle timeouts, mobile network switches).

## Problem
Per-instance status dots remained frozen at 'connected' (green) when SSE transport dropped because:
- Instance status updates travel over SSE (impossible when SSE is down)
- EventSource.onerror doesn't fire immediately on mobile wifi drops
- No user-facing indication of workspace-level SSE state

## Solution
1. server-events.ts: Add onDisconnect() handler (mirrors existing onOpen)
   - Fire disconnectHandlers in scheduleReconnect() when connection drops

2. sse-manager.ts: Register SSE lifecycle handlers
   - onDisconnect: set ALL instances to 'connecting' (amber dot)
   - onOpen: clear 'connecting' status (green dot restored by events)
   - Log transitions for debug visibility

3. AGENTS.md: Add PR Review Principles section
   - Check regressions, look for better implementations
   - Be the PR gatekeeper, ruthless code quality
   - Test before responding, UI/server version parity

## Verification
- TypeScript compilation clean (pre-existing SDK errors unrelated)
- Vite build successful
- Mobile testing: SSE disconnect → immediate amber dots
- Reconnect → green dots restored

## Edge Cases Handled
- Transient drop: amber → green on reconnect (no false modal)
- Instance dies during outage: amber → green → 'disconnected' event → red
- 0 instances: Map empty, loop is no-op
- Rapid reconnect cycles: idempotent (setting 'connecting' on 'connecting' is no-op)

Complements PR NeuralNomadsAI#519 (pong retry with timeout) - merged upstream.
Track workspace event transport reachability separately from per-instance stream status so a temporary SSE outage can show an amber connecting overlay without overwriting connected, disconnected, error, or genuinely connecting instance state.

Expose shared transport status from both browser EventSource and the native Tauri event transport, including transient native disconnected and error states, so desktop and browser paths drive the same UI behavior.

Remove the unrelated AGENTS.md review-guidance change from this PR and add focused node:test coverage for display-status derivation and native transport status mapping. Validated with UI typecheck, targeted tests, and npm run build:ui.
Fixes NeuralNomadsAI#441

Critical bug: Locking screen while wake-lock active causes system hang
on KDE Wayland + Electron 39, requiring hard reboot.

Solution: Detect Wayland session (XDG_SESSION_TYPE) and disable wake-lock
feature on Electron + Wayland combination.

Implementation:

1. Platform Detection (IPC):
   - Add platform:getInfo IPC handler in electron/main/ipc.ts
   - Exposes XDG_SESSION_TYPE and process.platform
   - Returns { sessionType?: string; platform: string }

2. Preload API:
   - Add getPlatformInfo() to electronAPI in preload/index.cjs
   - Bridges main process env vars to renderer process

3. Wayland Detection:
   - Add isWaylandSession() in ui/lib/native/wake-lock.ts
   - Checks XDG_SESSION_TYPE via Electron API
   - Fallback to user agent check
   - Cached for performance (avoid repeated async calls)

4. Conditional Disable:
   - Modify hasAnyWakeLockSupport() to be async
   - Returns false on Electron + Wayland combination
   - Logs clear warning message with workarounds
   - Updated all callers to await

Behavior After Fix:

| Platform  | Session | Wake-Lock | Screen Lock | Result |
|-----------|---------|-----------|-------------|--------|
| Electron  | Wayland | Disabled  | Safe        | No crash ✅ |
| Electron  | X11     | Active    | Safe        | Works ✅ |
| Tauri     | Any     | Active    | Safe        | Works ✅ |

User Impact:
- Electron + Wayland users: Wake-lock disabled, app stable
  - Workaround 1: Switch to X11 session (wake-lock works)
  - Workaround 2: Use Tauri build (better Wayland support)
  - Workaround 3: Disable system auto-sleep
- Electron + X11 users: No changes, feature works
- Tauri users: No changes, feature works

Validation:
- npm run typecheck --workspace @codenomad/ui: PASSED ✅
- npm run typecheck --workspace @neuralnomads/codenomad-electron-app: PASSED ✅
- Manual testing: PENDING (requires X11/Wayland session switching)

Testing Plan:
1. Electron + X11: Verify wake-lock works, screen lock safe
2. Electron + Wayland: Verify wake-lock disabled, no crash
3. Tauri + Wayland: Verify wake-lock works, screen lock safe

Edge Cases Handled:
- Electron API not available: Fallback to user agent
- getPlatformInfo() fails: Error caught, fallback
- Repeated detection calls: Result cached
- Remote windows: API available in remote context

Documentation:
- Created WAKE-LOCK-WAYLAND-FIX.md with full implementation details
- Includes testing procedures for X11/Wayland
- User workarounds documented
- Known limitations listed

Known Limitations:
- Electron + Wayland users lose wake-lock feature
- Detection requires XDG_SESSION_TYPE environment variable
- Runtime session switching requires CodeNomad restart
- Upstream Electron fix needed for proper Wayland support

Future Improvements:
- Report to Electron project (may be fixed in Electron 40+)
- Consider Tauri as primary build (better Wayland support)
- Alternative Wayland protocol implementation

Related:
- Issue: NeuralNomadsAI#441
- Tasks: 055, 056, 057
- Investigation: wake-lock-verification-report.md
- Bug Report: BUG-REPORT-SCREEN-LOCK-CRASH.md
Created installation guide for X11 on Arch Linux with KDE Plasma:

Requirements:
- xorg-server: X11 display server
- plasma-workspace-x11: KDE Plasma session for X11

Purpose: Testing wake-lock fix on X11 before upstream PR.

Current state: Wayland only (xorg-server not installed)
X11 needed to verify wake-lock works safely without screen lock crash.

Installation:
- Simple pacman install command
- Verification steps included
- Session switching instructions

Testing workflow:
1. Install X11 packages
2. Logout → Login with X11 session
3. Test wake-lock functionality (WAKE-LOCK-X11-TESTING-PLAN.md)
4. Capture screenshots
5. Create PR upstream
6. Return to Wayland

Notes:
- X11 only needed temporarily for testing
- Can switch back to Wayland after
- Both sessions will coexist
- Choice at login screen

Related: WAKE-LOCK-X11-TESTING-PLAN.md, Issue NeuralNomadsAI#441
Created comprehensive feature request for sudo password input capability:

Problem:
- AI cannot execute sudo commands (terminal required for password)
- User must manually copy/paste commands
- Breaks workflow continuity
- Common pain point

Proposed Solution:
Three options analyzed, recommending password modal approach:
- Option A: Password prompt modal (recommended)
- Option B: PTY with interaction (complex)
- Option C: Session caching (future enhancement)

Implementation Plan:
Phase 1 (8h): Basic modal + password input
Phase 2 (4h): Session caching (15 min timeout)
Phase 3 (40h): Full PTY support (future)

Security Design:
- Never log passwords
- Clear from memory immediately
- Secure input (type=password)
- No persistence
- HTTPS transport only

UX Flow:
Current: 7 steps with context switch
Proposed: 3 steps, no context switch

Similar Features:
- VS Code Remote SSH (password prompts)
- JetBrains IDEs (interactive terminal)
- GitHub Desktop (sudo for git operations)

Priority: Medium
Effort: 12 hours total (Phase 1 + 2)
ROI: High (better UX, competitive feature)

Session Error Evidence:
Added screenshot and documentation of "Error: Aborted" bug:
- pictures/error_20260514_135657.png (191KB)
- SESSION-ERROR-ABORTED-EVIDENCE.md (400+ lines)

Screenshot shows:
- Modal: "Session error - Error: Aborted"
- Timestamp: May 14 13:56:57
- Context: PC web browser
- Evidence of browser timeout → abort → auto-reload

Analysis confirms:
- Root cause: No server-side timeout
- Browser aborts after 60-90s
- Server left in stuck state
- PM2 restart required

This completes the session stuck bug documentation with visual evidence.

Next Steps:
1. Discuss feature with upstream maintainers
2. Create GitHub issue for sudo password input
3. Implement if approved (Phase 1: 8 hours)

Related: Issue NeuralNomadsAI#441 (wake-lock), session stuck bug investigation
…hase 1

Session idle notifications were silently blocked when the browser tab was
visible and osNotificationsAllowWhenVisible was falsy, because preferences
default to `osNotificationsEnabled=false` on the server and the UI
checks visibility before sending.  Users never saw idle-triggered OS
notifications regardless of their settings toggles.

Changes in session-events.ts:
- Remove verbose logging that existed purely for Issue NeuralNomadsAI#378 debugging
- Restore concise boolean guard chain in shouldSendOsNotification
- Drop stale comment in shouldSendOsNotificationForSession
- Localise the idle notification body to Spanish, matching the rest of the UI

Symbol Attachments Phase 1 (in attachment.ts):
- Add SYMBOL_KIND_LABELS map for the 26 LSP SymbolKind values
- Export symbolKindLabel(kind) lookup helper
- Implement createSymbolAttachment() that builds a symbol-type Attachment
  with file URL, display label, and source metadata

i18n (7 locales: en, es, fr, ru, ja, zh-Hans, he):
- Insert attachmentChip.symbol.ariaLabel and .kindLabel strings
- Append @symbol to promptInput.placeholder.default

All changes are additive and backward-compatible; the notification path
now honours the same preferences but produces visible results when
osNotificationsEnabled=true in server-side config.
When a workspace is cleaned up or deleted, the background process manager
was throwing 'Workspace not found' error when trying to finalize records.
This caused the server to crash in an infinite loop.

The fix wraps the finalization operations in try-catch blocks to gracefully
handle the case where a workspace no longer exists in the workspace manager.
This allows the server to continue running even when old sessions reference
deleted workspaces.

Fixes the infinite loop crash that occurred when sessions were terminated.
The init.sh was trying to run 'pnpm test' which doesn't exist in the
project, causing the harness initialization to fail. Updated the script
to check if a test script is defined before attempting to run it.
Add a keyboard-toggleable debug overlay (Ctrl+Shift+D) that surfaces
real-time session state from CodeNomad's stores. This is a diagnostic
tool used to investigate cross-project session leakage where a single
session appears in multiple workspace UIs.

The overlay shows:
- All active instances with their workspace IDs and folders
- Sessions stored under each instance map (id, title, directory, parent)
- The currently active instance ID
- Per-session and per-instance copy-to-clipboard buttons
- A "Share" button that copies the full state as JSON for issue reports
- Minimize/Close controls and a keyboard shortcut reminder input

Activation: Ctrl+Shift+D toggles the overlay globally; rendered from
App.tsx so it works from any view.

Useful for debugging session-instance mismatches without restarting
the server or rebuilding the UI for each investigation cycle.

Co-Authored-By: Claude <noreply@anthropic.com>
The debug overlay had a single scrollable container that included both
the header (title + control buttons) and the content. When scrolling
through long session lists, the header scrolled out of view, making
the Share/Minimize/Close controls unreachable without scrolling back
up.

Restructure the overlay so the header is rendered as a separate flex
child with flex-shrink: 0 and its own background, while only the
content area scrolls. The header now stays pinned at the top of the
overlay regardless of scroll position.

Also remove the duplicate <Show> wrapper around the content body that
was introduced when restructuring.

Co-Authored-By: Claude <noreply@anthropic.com>
The WorkspaceManager.create() method generated a new workspace ID on
every call without checking whether a workspace already existed for
the same folder path. Combined with the UI recreating instances on
each refresh, this caused dozens of duplicate workspaces per project
(74+ for test, 29+ for idm in one session).

Each duplicate workspace spawned its own OpenCode process pointed at
the same SQLite database (~/.local/share/opencode/opencode.db). The
UI then showed the same session in every workspace that targeted the
same folder, and deleting it from one workspace removed it from the
shared DB — affecting all the others.

Add an early-return path: if a workspace already exists for the
resolved folder path, return it instead of spawning a new one. This
matches the frontend's getExistingInstanceForFolder() intent and
keeps one OpenCode process per project folder.

Co-Authored-By: Claude <noreply@anthropic.com>
The backend deduplication added in cf0128f only worked within a single
server lifetime. After a restart, the backend's in-memory workspace
map was empty, so the UI calling createInstance() for each open
project tab spawned a fresh workspace every time.

Make createInstance() fetch the current workspace list from the
backend and reuse any workspace whose resolved path matches the
folder we're trying to open. Only call POST /api/workspaces when no
match is found. This keeps one OpenCode process per folder across
server restarts and prevents the "same session in multiple
projects" symptom.

Co-Authored-By: Claude <noreply@anthropic.com>
Build on the workspace deduplication fix with the observability tools
needed to verify it works and detect regressions.

- packages/server/src/workspaces/manager.ts: structured pino logging
  on every create() call with an `action` discriminator
  (create_request / reused / created / dedup_missed). `dedup_missed`
  is logged at error level and fires if `pathOccurrences > 0` but the
  dedup loop missed them — tripwire for normalization bugs. Add
  getStats() returning total + byPath counts.

- packages/server/src/server/routes/workspaces.ts: new
  GET /api/workspaces/stats endpoint exposing getStats() for easy
  health checks (curl it and confirm byPath values are all 1).

- scripts/monitor-workspaces.sh: tail PM2 out log and color-code
  workspace activity in real time (cyan request, yellow reuse,
  green new, red dedup_missed). A healthy run is mostly yellow.

- docs/WORKSPACE_DEBUG.md: documents the bug, the two-commit fix,
  the new endpoint, the in-browser debug overlay, and the monitor
  script. Lists every file touched so future contributors know what
  to look at.

Co-Authored-By: Claude <noreply@anthropic.com>
OpenCode 1.17.10 has a bug in the /session endpoint: when scope=project
is passed, it ignores the directory filter and returns ALL sessions
across every project. This made the same session appear in every
project tab in CodeNomad — deleting a session from one tab deleted
it from all of them.

The scope=project parameter was added in upstream PR NeuralNomadsAI#565
(commit e29c3a0) to avoid pagination limits on the default endpoint.
With the directory filter ignored, that optimization becomes a
correctness bug, so drop scope=project until OpenCode fixes the
backend.

With directory alone, the endpoint correctly returns only the
sessions for the requested folder. Verified via curl on the running
process: directory=/test returns 1 session (Hola greeting),
directory=/idm returns 1 session (up).

Co-Authored-By: Claude <noreply@anthropic.com>
@JDis03 JDis03 closed this Jun 30, 2026
@JDis03 JDis03 deleted the fix/session-list-directory-scope branch June 30, 2026 21:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants