mirror of
https://github.com/getpaseo/paseo.git
synced 2026-07-29 12:01:31 +00:00
* refactor(server): move checkout write handlers into CheckoutSession The checkout read side moved into session/checkout/checkout-session.ts in #1644; the 17 inline write/PR/stash handlers stayed on the Session god-object. This carves them into the same subsystem behind an expanded CheckoutSessionHost, so dispatchCheckoutMessage becomes pure delegation and session.ts drops ~800 lines. Moved into CheckoutSession (verbatim, only this.X -> this.host.X rewiring): switch/rename branch, commit, merge, merge-from-base, pull, push, PR create/merge, github auto-merge/check-details, PR status/timeline, github search, stash save/pop/list, plus resolveCurrentPullRequest and the PR-timeline helpers. CheckoutSessionHost gains the Session-owned, non-checkout collaborators these handlers orchestrate (notifyGitMutation, emitWorkspaceUpdateForCwd, handleWorkspaceGitBranchSnapshot, renameCurrentBranch, checkoutExistingBranch, and the LLM commit/PR-text generators); paseoHome/worktreesRoot join the options bag. GitMutationRefreshReason moves to utils/checkout-git.ts so both the shell and the subsystem share one canonical type. session.test.ts checkout tests now drive the handlers through the public handleMessage boundary instead of reaching into private methods; new mock-free fake-host tests cover the moved handlers directly in checkout-session.test.ts. Behavior-preserving: full session.test.ts + checkout-session.test.ts green, typecheck/lint/format clean. * refactor(server): extract chat/schedule/loop handlers into ChatScheduleLoopSession Move the 7 chat/*, 9 schedule/*, and 5 loop/* request handlers, the three rpc-error emitters (kept separate), and toScheduleSummary out of the Session god-object into session/chat/chat-schedule-loop-session.ts, mirroring the shipped CheckoutSession/VoiceSession deep-module-with-narrow-Host pattern. The least-coupled remaining domain: stateless request/response over chatService/scheduleService/ loopService, reaching the shell only through a narrow ChatScheduleLoopSessionHost (emit + agent-roster reads + the mention-fanout send). session.ts drops ~590 lines. The two dispatch switches collapse into one: schedule/* was previously reached only via the chat dispatcher's default fall-through arm — now all 21 types are explicit delegation cases, removing that fragility. Behavior-preserving: a new routing test drives the real Session.handleMessage for all 21 types (guards against a silently-dropped case), green before and after the move; a collocated subsystem test covers the mention-fanout send seam, the fanout-limit error code, the self->agent target remap, and the toScheduleSummary runs-stripping. * refactor(server): extract provider catalog handlers into ProviderCatalogSession Move the 8 provider-catalog handlers (model/mode/feature listing, providers snapshot pull/refresh, diagnostic, usage) plus emitProviderDisabledResponse, getProviderSnapshotEntryForRead, buildDraftAgentSessionConfig, the two mode-icon downgrade helpers, and the providers_snapshot_update PUSH wiring out of session.ts into session/provider/provider-catalog-session.ts behind a narrow ProviderCatalogSessionHost seam — the same deep-module pattern as ChatScheduleLoopSession, CheckoutSession, and VoiceSession. The PUSH (start) and every PULL gate provider visibility and downgrade mode icons through the SAME injected predicates (isProviderVisibleToClient + supportsCustomModeIcons), both reading appVersion/clientCapabilities live — the COMPAT invariant the shell could only enforce by code proximity before. dispatchProviderMessage collapses to delegation; start()/dispose() wire into subscribeToOptionalManagers/cleanup at the same ordinal. session.ts drops ~372 lines. Behavior-preservation gate (provider routing through the real Session.handleMessage) green before and after; full session.test.ts 115/115; new collocated subsystem test covers the PUSH/PULL parity seam, the disabled-provider path, and the usage/feature error envelopes. * refactor(server): extract workspace file-access handlers into WorkspaceFilesSession Move the file-explorer, file-upload, file-transfer-frame, project-icon and file-download-token handlers out of session.ts into session/files/workspace-files-session.ts behind a narrow WorkspaceFilesSessionHost seam (emit/emitBinary/hasBinaryChannel), matching the deep-module-with-Host shape used by CheckoutSession/ChatScheduleLoopSession/ProviderCatalogSession. The new module owns the FileUploadStore and is injected the daemon's DownloadTokenStore; the shell's dispatch cases and the file_transfer binary route collapse to delegation. The five handlers touch no workspace-git observer, registry or subscription state, so this is a clean self-contained slice of the workspace domain. session.ts drops 240 lines (7316 -> 7076). Behavior-preservation tests (file explorer list/read/binary, download token success + empty-cwd, project icon, upload round-trip) added to session.test.ts and run green against the pre- and post-refactor Session; a collocated workspace-files-session.test.ts exercises the module via a fake host with real upload/download stores (no module mocks). * refactor(server): extract agent-config setters into AgentConfigSession Move the four agent-config setters (set_agent_mode/model/feature/thinking) out of the session god-object into session/agent-config/, collapsing their four near-identical try/log/emit envelopes into one applyConfigChange helper behind a narrow { emit } host plus an AgentConfigOperations port. Behavior-preserving: the dispatch cases delegate unchanged, setAgentModeCommand still routes the mode path, and model/feature still emit no notice. Adds the previously-absent failure-envelope coverage: 8 gate tests in session.test.ts (success + forced failure per setter, asserting the *_response envelope and the activity_log-before-response ordering) green before and after the carve, plus a collocated agent-config-session.test.ts (typed fake operations + fake host, no module mocks). session.ts drops ~173 lines. * refactor(server): extract project-config handlers into ProjectConfigSession Move read/write_project_config handlers, their failure emitters, and the root-resolution helpers (resolveKnownProjectRootForConfig, canonicalizeConfigRoot, stripTrailingPathSeparators) out of session.ts into session/project-config/, behind a narrow { emit } host plus a projectRegistry port — matching the established session/<domain>/ pattern. Also corrects a misfiling: project-config was dispatched inside dispatchAgentConfigMessage despite being its own domain. session.ts drops 155 lines (6903 -> 6748). The handleMessage-driven gate in session.test.ts (project config RPC authorization) is green before and after; new collocated project-config-session.test.ts covers the subsystem with a fake host and fake registry over a real filesystem (no module mocks). * refactor(server): extract daemon status/pairing handlers into DaemonSession Move the two daemon.* RPC handlers (daemon.get_status, daemon.get_pairing_offer) out of session.ts into session/daemon/daemon-session.ts behind a narrow { emit } host + injected reads (paseoHome, serverId, daemonVersion, daemonRuntimeConfig, listProviderAvailability). These were misfiled inside dispatchAgentConfigMessage despite being daemon- not agent-scoped; the slice also removes the now-dead serverId/daemonVersion/daemonRuntimeConfig fields and the getPidLockInfo/generateLocalPairingOffer imports from the god-file, and collapses the inline daemonRuntimeConfig type into a named DaemonRuntimeConfig. The daemon-config get/set handlers stay inline: daemonConfigStore is a listener-bearing, cross-domain hub (read by structured-generation, websocket-server, bootstrap, archive-if-safe), so moving it would double-own it. session.ts: 6748 -> 6662 lines. Gate: new session.test.ts daemon round-trips (status success + listing-rejects fallback, pairing relay-disabled) pass before and after; new collocated daemon-session.test.ts (fake host + injected reads over a real temp dir, no module mocks) 4/4. typecheck/lint/format clean.
Voice Assistant
A voice-controlled terminal assistant that runs as a single local service.
Quick Start
# Install dependencies
npm install
# Copy environment variables
cp .env.example .env
# Edit .env and add your API keys (OpenAI, Deepgram)
# Run development servers
npm run dev
# Open browser to http://localhost:5173
Architecture
- Express Server (port 3000) - Serves API and built UI in production
- Vite Dev Server (port 5173) - Hot-reload React UI in development
- WebSocket (
/ws) - Real-time bidirectional communication - Agent - STT → LLM → TTS pipeline with terminal control
- Daemon - tmux-based terminal management (in-process)
Development
# Run both servers (recommended)
npm run dev
# Or run separately:
npm run dev:server # Express on port 3000
npm run dev:ui # Vite on port 5173
# Type checking
npm run typecheck
# Build for production
npm run build
# Start production server
npm start
Project Status
✅ Completed (Phases 1-2):
- Package setup and configuration
- Express server with WebSocket
- React UI with Vite
- WebSocket client with ping/pong testing
⏳ In Progress (Phase 3):
- Terminal control (tmux integration)
📋 Planned (Phases 4-9):
- LLM integration (OpenAI GPT-4)
- Agent orchestrator
- Speech-to-Text (Deepgram)
- Text-to-Speech (OpenAI)
- Audio streaming
- UI polish
See IMPLEMENTATION_PLAN.md for complete details.
Environment Variables
OPENAI_API_KEY=your-openai-key-here # GPT-4 and TTS
DEEPGRAM_API_KEY=your-deepgram-key-here # Streaming STT
STT_MODEL=whisper-1 # Optional: override to gpt-4o-transcribe, etc.
STT_CONFIDENCE_THRESHOLD=-3.0 # Optional: reject low-confidence clips
STT_DEBUG_AUDIO_DIR=.stt-debug # Optional: persist raw dictation audio for debugging
PASEO_HOME=~/.paseo # Runtime state directory (agents/, etc.)
PASEO_LISTEN=127.0.0.1:6767 # Listen address (host:port or /path/to/socket)
PASEO_HOME defaults to ~/.paseo and isolates runtime artifacts like agents/. PASEO_LISTEN controls the daemon listen address. For blue/green testing you can run a parallel server without touching production state:
PASEO_HOME=~/.paseo-blue PASEO_LISTEN=127.0.0.1:7777 npm run dev
Tech Stack
- Server: Express, TypeScript, ws (WebSocket)
- Client: React 18, Vite, TypeScript
- Terminal: tmux (via child_process)
- AI: OpenAI (LLM + TTS), Deepgram (STT)
Testing
Currently manual testing via:
- Start servers:
npm run dev - Open http://localhost:5173
- Test WebSocket connection (green status indicator)
- Click "Send Ping" button to test communication
More testing guidance as features are implemented.
License
MIT