Skip to content

feat: session daemon - persistent remote terminal sessions#3360

Closed
lyx-tec wants to merge 19 commits into
wavetermdev:mainfrom
lyx-tec:feat/shared_session
Closed

feat: session daemon - persistent remote terminal sessions#3360
lyx-tec wants to merge 19 commits into
wavetermdev:mainfrom
lyx-tec:feat/shared_session

Conversation

@lyx-tec

@lyx-tec lyx-tec commented Jun 8, 2026

Copy link
Copy Markdown

Summary

Implements Session Daemon architecture: decouple remote SSH sessions from block views, enabling multi-block attach/detach, named sessions, idle timeout, and persistent reconnection.

Replaces DurableShellController with a SessionDaemonController + SessionDaemonManager architecture.

Key Changes

  • Data Model: New SessionDaemon struct in waveobj, DB migration 000012, MetaKey_SessionDaemonId constant
  • Core Package: pkg/sessiondaemon/sessiondaemon.go — SessionDaemon + SessionDaemonManager with idle reaper
  • Controller: pkg/blockcontroller/sessiondaemoncontroller.go — new block controller for session-backed blocks
  • ResyncController: SSH blocks with no daemonId auto-create anonymous daemon (1h idle timeout)
  • Removed: durableshellcontroller.go, IsBlockTermDurable/IsBlockIdTermDurable
  • wsh CLI: wsh session create/delete/list/attach/detach/info/tag
  • RPC: 7 new session commands with generated bindings
  • Frontend: TermWrap �ttachToDaemon/detachFromDaemon, SessionDaemonIndicator in header, context menu
  • Migration: Startup migration for existing Block.JobId → SessionDaemon records
  • Bug fixes: GetRuntimeStatus 'init' return, TOCTOU race, per-block detach, controller resync after detach

Design

V2 design:
unOutputLoop stays in JobController. SessionDaemon is a thin orchestration layer (naming, multi-block tracking, idle timeout). Does not manage PTY, output streams, or processes.

See docs/design/session-daemon-design-v2.md for full design document.

lyx-tec and others added 19 commits May 30, 2026 09:19
* fix: copy-on-select overwrites clipboard when search is open (#1)

Change the guard in termwrap.ts from checking focused element
(document.activeElement.closest) to checking DOM presence
(document.querySelector) to prevent copy-on-select from firing
whenever search is open, regardless of focus state.

* chore: mark verification tasks complete, archive openspec change, sync specs
* wip

* add search input ref

* feat: add Playwright E2E testing (#3)

---------

Co-authored-by: carl2027 <carl.2027@outlook.com>
WaveTerm previously ignored the ProxyCommand directive in ~/.ssh/config,
causing connections via SSH config Host entries with ProxyCommand to fail
with 'Disconnected from xxx'.

This change:
- Parses ProxyCommand from SSH config in findSshConfigKeywords
- Executes the proxy command via os/exec with %h/%p/%r expansion
- Wraps command stdin/stdout as net.Conn via net.Pipe()
- Cleans up the subprocess on connection close via proxyCommandConn

Affects: pkg/wconfig/settingsconfig.go, pkg/remote/sshclient.go,
frontend/types/gotypes.d.ts
- Change 'providedInputRef' to 'searchInputRef' in search.tsx
- Add ssh:proxycommand to schema/connections.json
- Run go mod tidy
When SSH config has ProxyCommand like 'ssh -W %h:%p jump-host',
parse and convert it to ProxyJump internally. Falls back to
subprocess-based execution for non-convertible ProxyCommands.
Remove expandProxyCommand, proxyCommandConn type, and ProxyCommand
execution from connectInternal. ProxyCommand is now solely handled
by parseProxyCommandAsJump conversion to ProxyJump.
- computeDirPart returns empty string for Windows drive roots

- Add listDrives() to enumerate available drives

- Handle empty path for drive listing in list/fileInfo commands

- Fix getParentDirectory for backslash paths

- Fix back button, bookmark, and parent entry for drives
…state display (#9)

* feat: term block command config dialog with Save & Restart and error state display

- Add CommandConfigModal dialog (Command, Run on startup, Clear on start, Env vars)
- Replace 'Run On Startup' submenu with 'Configure Command...' menu item
- Inject command as keystrokes into interactive shell (not -c mode)
- Write cmd:lasterror meta key on non-zero exit, clear on exit 0
- Show red error icon in block header when cmd:lasterror is set
- Add MetaKey_CmdLastError to MetaTSType / metaconsts.go

* fix: clear cmd:lasterror before injection, add env var injection, fix error icon guard

- Clear cmd:lasterror meta at shell start and before each injection
- Add shell-specific env var injection (export for bash, envvar for pwsh, set -gx for fish)
- Fix error icon: only set cmd:lasterror when InjectedCommand flag is true
- Use backslash-r (0x0D) instead of backslash-r-backslash-n for ConPTY keystroke injection
- Add 1500ms delay for shell raw mode initialization
- Convert backslash-n to backslash-r for multi-line command injection
- Add clickable error icon that opens MessageModal with details
- Set controller double-quote shell double-quote in Save and Restart meta (was null)
- Update OpenSpec skills-workflows to v1.4.1

* feat: inject cmd via shellInputCh, remove cmd:lasterror

- Replace direct PTY write with shellInputCh channel (same path as user keystrokes)
- Remove InjectedCommand tracking and cmd:lasterror meta mechanism
- Remove red error icon UI for cmd blocks
- Remove MetaKey_CmdLastError/CmdLastError type and generated files
- Clean up unused useEffect import
- Rename 'Configure Command...' to 'Startup Command...'
Add SessionDaemon data model, DB migration, meta key.
Implement sessiondaemon package (Manager, idle reaper).
Implement SessionDaemonController (Start/Stop/SendInput).
Replace DurableShellController with SessionDaemonController.
ResyncController dispatch: SSH blocks auto-create anonymous daemon.
Simplify handleAppendJobFile (job-only write), remove IsBlockTermDurable.
Frontend: TermWrap attachToDaemon/detachFromDaemon, zoneId switching.
Add wsh session CLI (create/delete/list/attach/detach/info/tag).
Add RPC types, handlers, generated bindings for session commands.
Frontend: SessionDaemonIndicator in header + context menu items.
Add startup migration for existing Block.JobId to SessionDaemon.
Fix critical: GetRuntimeStatus returns init when no JobId.
Fix TOCTOU race in Manager AttachBlock/DetachBlock.
Add BlockId to SessionDetachData for per-block detach.
Add resyncBlockController after detach.
Add Version field to controller runtime status.
@CLAassistant

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

@coderabbitai

coderabbitai Bot commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: f47bc9f8-327a-4b6e-b16e-ec2e7af5b18d

📥 Commits

Reviewing files that changed from the base of the PR and between a5ac096 and f9b7f3f.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (91)
  • .github/prompts/opsx-apply.prompt.md
  • .github/prompts/opsx-archive.prompt.md
  • .github/prompts/opsx-explore.prompt.md
  • .github/prompts/opsx-propose.prompt.md
  • .github/skills/openspec-apply-change/SKILL.md
  • .github/skills/openspec-archive-change/SKILL.md
  • .github/skills/openspec-explore/SKILL.md
  • .github/skills/openspec-propose/SKILL.md
  • .github/workflows/playwright.yml
  • .gitignore
  • .kilocode/skills/openspec-apply-change/SKILL.md
  • .kilocode/skills/openspec-archive-change/SKILL.md
  • .kilocode/skills/openspec-explore/SKILL.md
  • .kilocode/skills/openspec-propose/SKILL.md
  • .kilocode/workflows/opsx-apply.md
  • .kilocode/workflows/opsx-archive.md
  • .kilocode/workflows/opsx-explore.md
  • .kilocode/workflows/opsx-propose.md
  • .roo/commands/opsx-apply.md
  • .roo/commands/opsx-archive.md
  • .roo/commands/opsx-explore.md
  • .roo/commands/opsx-propose.md
  • .roo/skills/openspec-apply-change/SKILL.md
  • .roo/skills/openspec-archive-change/SKILL.md
  • .roo/skills/openspec-explore/SKILL.md
  • .roo/skills/openspec-propose/SKILL.md
  • PROJECT_SUMMARY.md
  • cmd/server/main-server.go
  • cmd/wsh/cmd/wshcmd-session.go
  • db/migrations-wstore/000012_sessiondaemon.down.sql
  • db/migrations-wstore/000012_sessiondaemon.up.sql
  • docs/design/session-daemon-design-v1.md
  • docs/design/session-daemon-design-v2.md
  • e2e/helpers/launch.ts
  • e2e/search-clipboard.spec.ts
  • frontend/app/block/blockframe-header.tsx
  • frontend/app/block/session-daemon-indicator.tsx
  • frontend/app/element/search.tsx
  • frontend/app/store/keymodel.ts
  • frontend/app/store/wshclientapi.ts
  • frontend/app/view/preview/preview-directory.tsx
  • frontend/app/view/preview/preview-model.tsx
  • frontend/app/view/term/term-model.ts
  • frontend/app/view/term/term.tsx
  • frontend/app/view/term/termwrap.ts
  • frontend/types/gotypes.d.ts
  • frontend/util/historyutil.ts
  • frontend/util/waveutil.ts
  • openspec/changes/add-playwright-e2e/.openspec.yaml
  • openspec/changes/add-playwright-e2e/design.md
  • openspec/changes/add-playwright-e2e/proposal.md
  • openspec/changes/add-playwright-e2e/specs/e2e-search-clipboard/spec.md
  • openspec/changes/add-playwright-e2e/specs/playwright-electron-setup/spec.md
  • openspec/changes/add-playwright-e2e/tasks.md
  • openspec/changes/archive/2026-05-30-fix-search-copyonselect-clipboard/.openspec.yaml
  • openspec/changes/archive/2026-05-30-fix-search-copyonselect-clipboard/design.md
  • openspec/changes/archive/2026-05-30-fix-search-copyonselect-clipboard/proposal.md
  • openspec/changes/archive/2026-05-30-fix-search-copyonselect-clipboard/specs/search-clipboard-guard/spec.md
  • openspec/changes/archive/2026-05-30-fix-search-copyonselect-clipboard/tasks.md
  • openspec/changes/session-daemon/.openspec.yaml
  • openspec/changes/session-daemon/design.md
  • openspec/changes/session-daemon/proposal.md
  • openspec/changes/session-daemon/specs/session-attach-detach/spec.md
  • openspec/changes/session-daemon/specs/session-auto-create/spec.md
  • openspec/changes/session-daemon/specs/session-create-delete/spec.md
  • openspec/changes/session-daemon/specs/session-idle-timeout/spec.md
  • openspec/changes/session-daemon/specs/session-reconnect/spec.md
  • openspec/changes/session-daemon/specs/session-wsh-cli/spec.md
  • openspec/changes/session-daemon/tasks.md
  • openspec/specs/search-clipboard-guard/spec.md
  • package.json
  • pkg/blockcontroller/blockcontroller.go
  • pkg/blockcontroller/durableshellcontroller.go
  • pkg/blockcontroller/sessiondaemoncontroller.go
  • pkg/jobcontroller/jobcontroller.go
  • pkg/remote/connutil.go
  • pkg/remote/sshclient.go
  • pkg/sessiondaemon/sessiondaemon.go
  • pkg/waveobj/metaconsts.go
  • pkg/waveobj/wtype.go
  • pkg/waveobj/wtypemeta.go
  • pkg/wconfig/defaultconfig/settings.json
  • pkg/wconfig/settingsconfig.go
  • pkg/wshrpc/wshclient/wshclient.go
  • pkg/wshrpc/wshremote/wshremote_file.go
  • pkg/wshrpc/wshrpctypes.go
  • pkg/wshrpc/wshserver/wshserver.go
  • pkg/wstore/wstore_dbsessionmigration.go
  • playwright.config.ts
  • schema/connections.json
  • test-results/playwright/.last-run.json

Walkthrough

This PR adds OPSX prompt and skill documentation in multiple locations, adds a new session-daemon feature across backend, RPC, CLI, and frontend code, introduces a database migration and OpenSpec artifacts for that feature, and adds Playwright E2E setup plus a clipboard-search test. It also updates related types, configuration, remote SSH parsing, and several frontend preview/search utilities.

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~90+ minutes

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

@lyx-tec lyx-tec closed this Jun 8, 2026
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