Skip to content

feat: add persistent workspace state, first-class terminals, and setup streaming#197

Open
boudra wants to merge 55 commits intomainfrom
merge/main-into-dev
Open

feat: add persistent workspace state, first-class terminals, and setup streaming#197
boudra wants to merge 55 commits intomainfrom
merge/main-into-dev

Conversation

@boudra
Copy link
Copy Markdown
Collaborator

@boudra boudra commented Apr 5, 2026

Summary

This PR overhauls how Paseo models workspaces, terminals, and persisted agent state.

From main, it introduces SQLite-backed persistence for agents, timelines, projects, and workspaces; replaces terminal-agent flows with first-class terminals; adds a streaming workspace setup flow; and makes workspace status more informative with service health/routing and CI visibility in the sidebar and hover card.

It also includes the app and daemon changes needed to make those systems work together cleanly, plus parity fixes so the newer composer, provider, and workspace UX continue to behave correctly on top of the new architecture.

User-facing changes

  • Workspaces, projects, and agent state persist across daemon restarts with faster, more stable reconnect behavior.
  • Creating a workspace now uses a dedicated setup flow with streaming progress, direct tab creation, and automatic setup-tab visibility while work is in progress.
  • Workspace rows are richer: the sidebar and hover card surface workspace services, branch-aware service status, and CI / PR status.
  • Terminals are first-class sessions with shell integration and command / args support instead of being modeled as terminal agents.
  • Workspace and tab behavior is cleaner: better archive / close flows, better workspace routing, and fewer stale or confusing transitions.
  • Composer and agent UX are more complete again: restored shortcut behavior, improved focus handling, draft-agent flows, combined model selection, and provider diagnostics.

Technical implementation

  • Add a SQLite + Drizzle persistence layer for agent snapshots, timeline rows, project/workspace registries, and legacy import / migration paths.
  • Introduce DB-backed agent loading and timeline storage services so session bootstrap, resume, and reconnect no longer depend on the old in-memory / file-only flow.
  • Replace the old workspace identity model with directory-based workspace IDs and add backward-compatible schema fields so existing clients can continue to interoperate during rollout.
  • Remove the terminal-agent code path and route terminal creation/listing/streaming through the terminal manager and terminal session runtime instead.
  • Add daemon-owned service proxying, service health monitoring, branch-aware service routing, and service status projection into workspace payloads.
  • Refactor workspace registration, reconciliation, git metadata detection, worktree setup, and setup-status streaming to support the new workspace execution model.
  • Warm workspace git shortstat in the background and push follow-up workspace updates instead of blocking the initial workspace fetch path.
  • Expand automated coverage across app e2e flows, daemon bootstrap, SQLite contracts/migrations, workspace setup/session flows, service proxy behavior, and timeline reconnect behavior.

Notes for reviewers

  • The largest conceptual shifts are: persistent SQLite-backed state, first-class terminals, and the workspace setup / execution model.
  • The app and server changes are tightly coupled in this branch; reviewing by subsystem (db, session/workspace, terminal, service proxy, then app workspace UX) will be easier than reading strictly commit-by-commit.

boudra and others added 30 commits March 29, 2026 23:20
- Remove orphaned PID lock code from bootstrap (moved to supervisor)
- Fix worktree archive adapter to look up workspace by directory
- Replace registerWorktreeWorkspaceRecord with inline SQLite implementation
- Remove unused imports (stat, createPersistedWorkspaceRecord, PersistedProjectRecord)
Services defined in paseo.json get reverse-proxied through the daemon
via hostname-based routing on *.localhost. Each service receives $PORT,
$HOST, and $PASEO_SERVICE_URL env vars, and is accessible at
{service}.localhost:6767 (main) or {branch}.{service}.localhost:6767
(worktrees).
Built-in service proxy with branch-based URLs, service health
monitoring, workspace hover card with service status, and
"Forget about ports" homepage section.
# Conflicts:
#	nix/package.nix
#	packages/app/src/app/_layout.tsx
#	packages/app/src/components/sidebar-workspace-list.tsx
#	packages/app/src/hooks/use-command-center.ts
#	packages/app/src/screens/agent/draft-agent-screen.tsx
#	packages/app/src/screens/workspace/workspace-desktop-tabs-row.tsx
#	packages/app/src/screens/workspace/workspace-screen.tsx
#	packages/server/src/server/session.ts
#	packages/server/src/server/session.workspaces.test.ts
#	packages/server/src/terminal/terminal.test.ts
#	packages/server/src/terminal/terminal.ts
# Conflicts:
#	packages/app/e2e/helpers/workspace-setup.ts
#	packages/app/src/components/sidebar-workspace-list.tsx
#	packages/app/src/contexts/session-context.tsx
#	packages/app/src/hooks/use-sidebar-workspaces-list.test.ts
#	packages/app/src/screens/workspace/workspace-tab-menu.ts
#	packages/app/src/stores/workspace-setup-store.ts
#	packages/app/src/stores/workspace-tabs-store.ts
#	packages/app/src/utils/sidebar-project-row-model.test.ts
#	packages/app/src/utils/sidebar-shortcuts.test.ts
#	packages/app/src/utils/workspace-tab-identity.ts
#	packages/server/src/server/bootstrap.ts
#	packages/server/src/server/worktree-session.ts
- Align portless code with storage branch's numeric workspace IDs and new field names
- Update workspace kind comparisons (local_checkout → checkout/worktree)
- Add missing services, supportsTerminalMode, terminal fields to test fixtures
- Fix archive timestamp assertions to match dynamic archiveSnapshot flow
- Fix dictation, voice runtime, and service health monitor test timing
- Add xterm-addon-ligatures type declaration for terminal emulator
Remove the "agent can be a terminal" branching from the entire codebase.
An agent is now always a session-backed chat agent. Standalone terminal
infrastructure (terminal component, ANSI handling, terminal-stream-protocol)
is preserved.

Server: delete ManagedTerminalAgent, AgentKind, TerminalExitDetails,
launchTerminalAgent, registerTerminalAgent, handleTerminalAgentExited,
supportsTerminalMode capability, buildTerminalCreate/ResumeCommand from
all providers, terminal agent persistence/projections.

App: delete terminal-agent-panel.tsx, terminal-agent-reopen-store.ts,
terminal/terminalExit fields on agent state, "Terminal Agents" launcher
section, terminal-agent workspace setup flow, terminal badge in agent list.

CLI: remove terminal column from ls, terminal-agent error from send.

60 files changed, -3592 lines
Reuse existing project when a matching git remote is found instead of
creating duplicates. Detect git worktrees via --git-common-dir and set
workspace kind accordingly.
# Conflicts:
#	nix/package.nix
#	packages/app/src/components/icons/opencode-icon.tsx
#	packages/app/src/components/provider-icons.ts
#	packages/app/src/panels/agent-panel.tsx
#	packages/cli/src/commands/provider/ls.ts
#	packages/server/src/server/agent/provider-manifest.ts
#	packages/server/src/server/agent/provider-registry.ts
#	packages/server/src/server/agent/providers/claude-agent.test.ts
#	packages/server/src/server/agent/providers/claude-agent.ts
#	packages/server/src/server/persistence-hooks.ts
#	packages/server/src/server/session.ts
Surgical merge of 74 commits from main into dev. Key features ported:

- Provider visibility gating (appVersion filtering for Pi/Copilot)
- Pi agent provider + Copilot re-enabled
- Provider-declared features system (Codex fast mode)
- Workspace dedup by worktree root (adapted to integer IDs)
- Bulk close archiving fix for stored agents
- Agent creation timeout increase to 60s
- Audio/voice crash fixes (external buffer copies)
- Multi-host setup fix
- Reload agent tab action

Dev architecture preserved: SQLite storage, service proxy,
workspace setup dialog, hover cards, removed launcher tabs.
Migration hardening:
- Back up JSON to $PASEO_HOME/backup/pre-migration/ before import
- Deduplicate projects/workspaces by path before insert
- Log per-batch progress during agent snapshot import
- Clear error messages for corrupt JSON files
- 5 new test cases for backup, dedup, progress, and error clarity

Post-merge fixes:
- Add worktreeRoot to session checkout result (type error fix)
- Port reload agent tab action from main
- Remove deleted useDelayedHistoryRefreshToast hook usage
return await initPromise;
} finally {
const current = pendingAgentBootstrapLoads.get(options.agentId);
if (current === initPromise) {
const hasLog = commandLog.trim().length > 0;

// All non-running commands are expandable (completed/failed)
const isExpandable = command.status !== "running" || hasLog || !!hasError;
);
}

const isArchivingCurrentAgent = Boolean(agentId && isArchivingAgent({ serverId, agentId }));
boudra and others added 2 commits April 5, 2026 21:26
Brings in 8 main commits (0.1.48 release, provider overhaul, keyboard
shortcuts, desktop login shell env, question form Enter key) with proper
git history. Resolves conflicts by keeping dev branch architecture where
it subsumes main's changes, and main's provider snapshot COMPAT comments.
},
});
return true;
case "message-input.send":
case "message-input.dictation-cancel":
messageInputRef.current?.runKeyboardAction("dictation-cancel");
return true;
case "message-input.dictation-confirm":
boudra added 19 commits April 5, 2026 21:56
…ouping

- DbProjectRegistry/DbWorkspaceRegistry: use ON CONFLICT(directory) instead
  of ON CONFLICT(id) so inserts and upserts handle duplicate directories
  gracefully instead of crashing
- Bootstrap: wrap legacy imports in try/catch (non-fatal), move
  reconciliation start after imports so first pass cleans up stale data
- Legacy project/workspace import: deduplicate by rootPath (prefer git
  over non_git), derive gitRemote from legacy projectId field
- Legacy agent snapshot import: detect git metadata and group agents by
  remote/toplevel instead of creating one project per cwd, clamp
  running/initializing statuses to closed
- Timeline hydration: set historyPrimed based on whether durable store
  has rows (not just whether it exists), remove hard gate so imported
  agents get their timelines populated lazily from provider history
- Durable timeline append: use bulkInsert with pre-assigned seq instead
  of appendCommitted which recalculates seq, fixing UNIQUE constraint
  failures during concurrent hydration writes
The reconciliation service was skipping worktree workspaces when
updating displayName from the current git branch, causing the sidebar
to show the initial random animal name instead of the actual branch.
- Replace workspace setup dialog with full-screen new workspace route
- Restyle Combobox SearchInput to match CombinedModelSelector Level 2 design:
  borderless sticky search bar above scroll area instead of inline bordered box
- CombinedModelSelector now uses shared SearchInput, removing duplicate ProviderSearchInput
- Fix TooltipTrigger children type to accept render functions (remove PropsWithChildren wrapper)
- Fix empty rightControls View causing gap between dictation and send buttons
- Add branch picker with searchable Combobox and GitBranch icons
- Muted icon buttons (attachment, dictation, voice mode) that brighten on hover
- Archive agents optimistically on close with rollback on error
- Close tabs immediately instead of waiting for RPC response
- Kill terminals in background with cache invalidation on failure
- Bulk close fires RPC in background, closes all tabs upfront
- Move new-tab button out of scroll area, rename to "New agent tab"
- Support invalidateQueries option in applyArchivedAgentCloseResults
- Fix workspace descriptor id/projectId types (string not number)
- Change workspace id/projectId from z.number() to z.union([z.string(), z.number()]).transform(String) for backward compat
- Add "non_git" to projectKind, "local_checkout" to workspaceKind enums
- Session converts numeric IDs to strings at the descriptor boundary
- Update tests and daemon-client event types to match
Add onComplete callback to warmCheckoutShortstatInBackground so session
can push a workspace_update once diff stats resolve.
getCurrentPathname used window.location.pathname instead of Expo Router's
pathname, which returns a file path on Electron — blocking navigation from
the index route. Also gate the bootstrap progress UI to desktop-only since
web has no local server to start/connect.
Numeric string IDs like "164" are valid base64 that decodes to garbage
(Hebrew character "׮"), causing silent workspace lookup failures.
Skip base64 encoding/decoding for numeric IDs and only use it for
legacy path-based workspace identifiers.
No-op clearDraft so the prompt stays while worktree + agent are being
created. Screen navigates away on success; text remains for retry on error.
Extend the existing gh pr view call with statusCheckRollup and
reviewDecision fields so check data arrives in a single request.
The workspace row now shows an aggregate check icon (green checkmark,
red X, or amber dot) next to the PR badge, and the hover card lists
each individual check with its status and a link to details.
Skip the confirmation dialog when archiving idle agents — archive
immediately. Show a warning that the agent will be stopped only when
it is running or initializing.
…nd agent form state improvements

- Add service-route-branch-handler for routing requests to branch-specific services
- Add service-hostname utility for generating hostnames from branch names
- Redesign workspace hover card with CI check status display
- Update combined model selector and sidebar workspace list
- Improve agent form state and input draft hooks
- Update checkout-git with enhanced branch handling
- Add workspace git watch and bootstrap improvements
…based workspace IDs, and add backward-compatible schema fields
Comment on lines +18 to +23
import {
connectTerminalClient,
waitForTerminalContent,
setupDeterministicPrompt,
type TerminalPerfDaemonClient,
} from "./helpers/terminal-perf";
@boudra boudra changed the title Integrate dev branch: storage, portless, terminal removal + restore main parity sqlite + better workspaces + portless Apr 6, 2026
@boudra boudra changed the title sqlite + better workspaces + portless feat: add persistent workspace state, first-class terminals, and setup streaming Apr 6, 2026
resolveWorkspaceExecutionDirectory,
} from "@/utils/workspace-execution";
import { CheckStatusIndicator, WorkspaceHoverCard } from "@/components/workspace-hover-card";
import { createNameId } from "mnemonic-id";
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.

1 participant