Skip to content

Feat/dev 112 desktop tauri shell#8

Merged
ogarciarevett merged 6 commits into
mainfrom
feat/dev-112-desktop-tauri-shell
Jun 4, 2026
Merged

Feat/dev 112 desktop tauri shell#8
ogarciarevett merged 6 commits into
mainfrom
feat/dev-112-desktop-tauri-shell

Conversation

@ogarciarevett

@ogarciarevett ogarciarevett commented Jun 4, 2026

Copy link
Copy Markdown
Owner

Summary

The Desktop phase (DEV-112): Vesper becomes a real native desktop app. This PR delivers the native
Tauri shell over the Bun daemon, a from-scratch premium dark-glass UI (replacing the pixel-art "Vesper
World"), a macOS menu-bar popover, the connections layer (messaging channels + MCP), and refreshed
docs/screenshots.

108 files changed, +9,697 / −3,605.

What's in it

Native desktop shell — Tauri 2 (39eaf65, extended in 43c5db4 / ea8453e)

  • A thin Rust shell over the Bun daemon sidecar (bun build --compile): spawn → health-wait on the
    UI port → open the window → clean shutdown; attach-if-already-running via the single-instance guard.
  • macOS overlay titlebar (native traffic lights inset over our custom titlebar), system tray, single
    instance. Rust holds no business logic; the daemon serves the full UI on 127.0.0.1.
  • Design record: specs/tauri-migration.md. Amends Hard rule 14 (Tauri now in-tree, scoped to the shell);
    the Bun host is unchanged.

Dark-glass desktop shell + "Vesper World" rebuild (43c5db4)

  • @vesper/ui is now an app shell: a custom draggable titlebar (Cmd+E command search + live status
    pills off /api/status), a grouped sidebar, a client-side SectionRouter, and a chrome-only theme
    system (dark default; light/hearth opt-in) that replaces the canvas-coupled WorldTheme.
  • 14 sections: Chat + Runtime / Helper CLIs / Permissions / Sandbox / Settings / Diagnostics / About
    (live data) + Pipelines / Channels / Schedule (thin views) + Skills / Memory / Voice (honest stubs).
  • Chat scoped to "only Vesper": a transcript + a Vesper-only activity rail that follows the
    conversation's run tree via the existing /api/chat + run-trace APIs (no backend rewrite;
    subscribe-before-backfill + de-dupe preserved). The machine-wide agent presence moved off the home into
    Diagnostics.
  • Retired the pixel-art renderer (sprite / render / world / themes / brand) via controlled git rm
    (recoverable). New read-only routes /api/status, /api/presence, /api/runs; /api/world removed.
    Net −890 lines in vesper-ui.
  • Design record: specs/desktop-app-shell.md + specs/vesper-world-rebuild.md.

macOS menu-bar popover (ea8453e)

  • Left-click the tray icon → a compact dark-glass popover (daemon status, default CLI, run/chat counts,
    recent activity, Open Vesper + Quit) anchored under the icon; click-away dismiss. Right-click
    keeps the existing Show/Quit menu.
  • One UI, two modes: the same client renders at /?panel=1; native actions over the Tauri bridge
    (withGlobalTauri). Design record: specs/menubar-app.md.

Connections layer (da2b566)

  • vesper-core/src/connections: a curated, code-reviewed channel + MCP catalog (Telegram + Discord
    ready; WhatsApp + Signal deferred; 10 MCP servers), opted into by id — never an arbitrary host.
  • Deny-by-default host-allowlisted fetch (allowlistedFetch asserts NETWORK_FETCH and refuses any
    host a descriptor did not declare — first-party hosts only, never an LLM provider).
  • Telegram long-poll handler (the receive loop yields to the event loop each iteration — fixes a
    microtask-starvation hang); registry + audit (with sensitive-value redaction) + typed errors.

Docs (e2151d3)

  • README rewritten for the new app with real screenshots (chat hero, Runtime, Diagnostics, Settings);
    presence config now points at the Diagnostics section.

Verification

  • biome ci clean.
  • bun test (scoped): vesper-ui 46, vesper-cli 104, connections 30 — all pass.
  • cargo build (desktop shell) clean; the compiled daemon sidecar verified serving the new UI end-to-end.
  • No LLM provider SDK in the lockfile (Rule 12); the host shells out to the user's CLI only.

Test plan

  • cd packages/vesper-desktop && bun run dev → native window opens on the dark-glass app; sidebar nav,
    Cmd+E search, theme switch, chat → activity rail streams live.
  • Left-click the menu-bar icon → popover appears; click away → dismiss; Open Vesper / Quit work.
  • vesper daemon start + open http://127.0.0.1:4317 → identical UI in the browser.
  • Connections: store a Telegram token in the vault, start the handler, confirm the long-poll receive
    loop does not spin the CPU.

Notes

  • Linear is issue-capped; the design record is specs/ + cycle-log.md (Rule 11 fallback) — reconcile to
    DEV-112 when the cap lifts.
  • Known follow-up: the menu-bar popover's stat values clip at the 380px panel width (minor panel.ts CSS).
  • Pre-existing connections WIP from a prior session was folded into da2b566 (formatting normalized so CI
    is green).

ogarciarevett and others added 6 commits June 1, 2026 17:44
The post-onboarding home is a simple chatbot; the canvas demotes to a side
activity panel. Builds on the shipped orchestration + live-trace backbone
(consumed, not modified). specs/chatbot-home.md (#9 + #4).

Storage (migration 007_chat_home): chat_sessions, chat_turns, pipeline_templates
+ a turns index, plus 6 synchronous Store methods (createSession, appendTurn,
listSessions, listTurns, getTemplate, upsertTemplate). chat_turns.run_id links an
assistant turn to the run that produced it (transcript bubble == activity-tree root).

Router pipeline (packages/pipelines/router/): a chat message is a manual
scheduler.run("router", {params}) (the EXISTING run path — no new execution). The
handler classifies via ctx.complete to one label, maps it through a FIXED ALLOWLIST
to a registered handler id, and ctx.spawns it; an unmapped/free-form label produces a
clarify turn (no spawn, no dynamic handler id, preserving no-eval). The router merges
the target's editable template default_params UNDER the user message, so an edited
template configures its runs.

Routes + WS: POST /api/chat, GET /api/chat/sessions, GET /api/chat/sessions/:id/turns,
GET /api/pipelines, GET|PUT /api/pipelines/:id/template; a chat:<sessionId> WS topic
next to the backbone's agent:<runId> (one socket, UUID-guarded). Client: transcript
home + demoted activity panel (reuses the runTree render) + a templates screen;
prefers-reduced-motion + WCAG-AA honored.

Security: a minimal out-of-band approval-token module (vesper-core/src/approval/,
CSPRNG single-use) gates PUT /template; POST /api/approval/request mints a code and
prints it to the daemon TTY (out-of-band — never in the HTTP response, so a local app
can mint but not read it). POST /api/chat is isLocalRequest-only (deliberate parity
with the existing run route). No provider SDK.

724 tests / 0 fail; Biome clean; no new tsc errors. No Linear issue (workspace
issue-capped) — cycle-log.md + this commit are the record (Rule 11 fallback).
…ices 1-2)

Add packages/vesper-desktop — a thin Tauri 2 native window over the existing
Bun-hosted Vesper World UI. The host runtime stays Bun; the Rust core holds no
business logic. This is the consumer surface (double-click app); the vesper CLI
remains the developer surface.

Slice 1 — scaffold the Tauri app: window config + entrypoint, generated icons,
Rust build verified.

Slice 2 — sidecar auto-start:
- vesper-ui: startUiServer gains an optional clientAssets dep + a process-wide
  setEmbeddedClientAssets() fallback + an exported buildClientAssets(). The
  on-disk read + Bun.build stays as the fallback, so `vesper daemon run` from
  source is unchanged.
- vesper-cli/compiled-entry.ts: embeds the prebuilt client (index.html + app.js)
  via an import attribute so a `bun build --compile` daemon serves the UI without
  client source files or a runtime bundler.
- scripts/build-daemon.ts (root build:daemon): builds the client assets, then
  compiles the daemon to src-tauri/binaries/vesper-daemon-<triple>.
- Rust core (tauri-plugin-shell): spawns the sidecar, health-waits on
  127.0.0.1:4317, opens the window onto it, and stops the sidecar on exit.
  attach-if-already-running is free via the daemon's single-instance guard.

Verified: the compiled daemon serves the full UI headlessly (GET / 200, /app.js,
/api/world); cargo build clean; vesper-ui + vesper-cli suites green; biome clean
on the changed files. Adds the Rust/cargo toolchain to the repo, scoped to the
desktop package; the Hard-rule-14 contract amendment lands with a later slice.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Rebuild the Vesper desktop UI as an OpenClaw-style native companion and rebuild
"Vesper World" from scratch. Replaces the floating-pill nav over a pixel-art canvas
with a real app shell; dark glass is now the default theme (light/hearth opt-in).

Shell
- Custom draggable titlebar: Cmd+E command search + live status pills (/api/status).
- Grouped sidebar + client-side SectionRouter; chrome-only theme system that
  replaces the canvas-coupled WorldTheme.
- 14 sections: Chat, Runtime, Helper CLIs, Permissions, Sandbox, Settings,
  Diagnostics, About (live data); Pipelines/Channels/Schedule (thin views);
  Skills/Memory/Voice (honest stubs naming their owning specs).

Vesper World rebuild
- Chat scoped to "only Vesper": a transcript + a Vesper-only activity rail that
  follows the conversation's run tree (existing /api/chat + run-trace APIs, no
  backend rewrite; subscribe-before-backfill + de-dupe preserved).
- Machine-wide agent presence moves off the home into Diagnostics.
- Retires the pixel-art renderer (sprite/render/world/themes/brand) via controlled
  git rm (recoverable). Net -890 lines tracked in vesper-ui.

Server
- New read-only /api/status, /api/presence, /api/runs; /api/world + snapshot removed.

Native
- macOS overlay titlebar (TitleBarStyle::Overlay + hidden_title, cfg-gated) so the
  custom titlebar shows with the traffic lights inset; tray + single-instance shell.

Specs: specs/desktop-app-shell.md + specs/vesper-world-rebuild.md (Omar-authorized;
Linear issue-capped, recorded in cycle-log.md). biome ci clean; vesper-ui 46 /
vesper-cli 104 pass; compiled sidecar verified serving the new shell. No provider SDKs.
Left-clicking the Vesper menu-bar icon now toggles a compact dark-glass popover
anchored under the icon; clicking away dismisses it. Right-click keeps the existing
Show/Quit menu.

- Web: panel.ts renders a quick-glance view (daemon status, default CLI, run/chat
  counts, recent runs, Open Vesper + Quit) at /?panel=1; main.ts branches panel-mode
  vs the full shell (bootShell).
- Native: a hidden borderless always-on-top "panel" window; tray left-click toggles +
  positions it under the icon (clamped to the monitor work-area); focus-loss hide;
  open_main / quit_app commands over the Tauri bridge (withGlobalTauri).

One UI, two modes (no second front-end); the native actions hide in a plain browser.
cargo build clean; biome ci clean; vesper-ui 46 tests pass; compiled sidecar serves
/?panel=1. Spec: specs/menubar-app.md.
…listed fetch

Add the connections layer (vesper-core/src/connections): a curated, code-reviewed
catalog of messaging channels and MCP servers that a pipeline opts into by id, with
all egress gated by a deny-by-default host allowlist and every action audited.

- catalog: CHANNEL_CATALOG (Telegram + Discord ready; WhatsApp + Signal deferred) and
  MCP_CATALOG (10 seed servers). The user selects an id, never an arbitrary host; each
  entry declares allowedHosts + vaultKeys (key names only, never values).
- fetch: allowlistedFetch asserts NETWORK_FETCH and refuses any host a descriptor did
  not declare — Vesper only ever reaches the first-party hosts a catalog entry names,
  never an LLM provider.
- telegram: TelegramHandler long-poll receive loop yields to the event loop each
  iteration (setTimeout 0) so a cooperative stop() and timers fire — fixes a
  microtask-starvation hang.
- registry + audit (recordConnectionEvent + stripSensitive redaction) + typed
  ConnectionError + the channel/MCP types.
- index: export the connections public surface.

bun test: 30 pass (connections). biome ci clean. No provider SDK.
@ogarciarevett ogarciarevett self-assigned this Jun 4, 2026
@linear-code

linear-code Bot commented Jun 4, 2026

Copy link
Copy Markdown

DEV-112

@ogarciarevett ogarciarevett merged commit 3a49a16 into main Jun 4, 2026
1 check passed
@ogarciarevett ogarciarevett deleted the feat/dev-112-desktop-tauri-shell branch June 4, 2026 11:00
ogarciarevett added a commit that referenced this pull request Jun 4, 2026
Reconciles the local chatbot-home + editable-pipeline-templates work (dfd01bd)
with the desktop dark-glass Tauri shell rebuild merged on origin/main (3a49a16,
PR #8). The shared chatbot backend (approval tokens, storage migration 007,
pipelines/router handler) is identical on both sides and auto-merged cleanly;
only the UI surface and the cycle log diverged.

Conflict resolution (4 files):
- client/main.ts, client/index.html: took the PR #8 new-shell versions. The
  old canvas-world + bolted-on chat home is SUPERSEDED by the shell's
  sections/chat.ts (full chat home over the same /api/chat + chat:<sessionId>
  socket) and sections/pipelines.ts (template view) + sections/activity-rail.ts
  ("watch it work"). No chat/template feature is lost.
- server/server.ts: kept PR #8's additive clientAssets field (compiled-daemon
  asset injection) alongside the shared approvalTokens field.
- cycle-log.md: kept both the chatbot-home and the desktop-shell-redesign entries.

Removed the now-orphaned old-architecture client files (superseded; templates.ts
imported the deleted brand/index.ts): client/chat.ts, client/templates.ts.
Kept client/chat-types.ts (imported by the new sections) and client/theme-store.ts
(imported by shell/themes.ts).

Verified: Biome clean (2 known cosmetic warnings); 733 tests / 0 fail; tsc shows
31 pre-existing errors, all byte-identical to origin/main (zero introduced by this
merge — CI gate is biome + bun test). No provider SDKs.
ogarciarevett added a commit that referenced this pull request Jun 5, 2026
… Diagnostics

The "Vesper World" rebuild (specs/vesper-world-rebuild.md) already shipped via
the dark-glass shell (PR #8). This finishes the spec's leftover (task #4):
gate the presence poll server-side and retire the dead /api/world.

- /api/presence now detects on demand (only consumer is the Diagnostics section,
  fetched on mount), bounded by a presencePollMs cache TTL. Removes the always-on
  3s background poll and its dead {type:"presence"} WS frame.
- The /api/world SceneGraph route was already gone; fixed stale doc-comments.
- Drop the now-orphaned presenceSignature (its only consumer was the poll).
- The world WS topic stays (run:completed + run:event:lite feed the activity rail);
  the presence detector + Diagnostics view are unchanged.

917 tests / 0 fail (+1 cache test); Biome clean; no new tsc errors.
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