feat: InClave desktop app (Tauri + React + Tailwind + shadcn) - #18
Merged
Conversation
…bundle-aware sandbox runtime Pull the streaming + auto-run-sandbox orchestration out of the CLI's chat.py into a headless, event-yielding chat_engine so a non-terminal front-end (the desktop bridge) can drive the exact same behavior. chat.py becomes a thin Rich renderer over the engine's events; existing test_chat.py still passes. - chat_engine.run_turn yields Token/MessageDone/RunStart/RunOutput/Error/TurnDone events; stream_fn is injectable so the CLI keeps its patchable seam. - add inclave_core.delete_session (UI needs delete; CLI never had it). - runtime.runtime_root() honors INCLAVE_SANDBOX_RUNTIME so the Seatbelt runtime resolves when the engine is bundled inside a desktop app.
A new uv-workspace package that exposes the engine to the desktop app over newline-delimited JSON-RPC 2.0 on stdin/stdout — no listening socket, so the only network connection remains the engine's talk to local Ollama. - protocol.py is the single source of truth (methods + events); export_schema.py serializes it to JSON for TypeScript generation. - handlers for system/config/models/files/sessions/chat reuse the engine and chat_engine; chat.send streams chat.* notifications and runs the auto-run loop. - server.py dispatches requests, maps the InClaveError hierarchy to JSON-RPC error codes, and forwards streamed events. 20 tests.
…tract Stand up the JS toolchain alongside the existing uv workspace (one repo, two ecosystems). @inclave/ipc-contract generates typed methods/events + zod validators from the bridge's schema.json (pnpm gen:ipc); the generated index.ts is gitignored and rebuilt in CI, with a freshness test guarding against drift. gitignore covers node_modules, turbo cache, Rust target, dist, PyInstaller output, and the bundled sidecar binaries.
The macOS desktop app. React 19/TS frontend (chat with token streaming, shiki code blocks, the sandbox output card, drag-and-drop, models manager with VRAM badges, onboarding, command palette) talks to the engine through a single allow-listed Rust command that forwards to the bridge sidecar over stdio; sidecar notifications are re-emitted as Tauri events. A browser mock backend lets the UI run without Tauri for fast iteration. Branding: a custom InClave mark (an inward arrow held inside a rounded enclosure — data enters, stays on-device) replaces the generic spark across the titlebar, onboarding, empty state, and assistant avatar. Full icon set (.icns/.ico/PNG/iOS/Android) generated from icons/icon.svg. Privacy: CSP locked to self, capability allow-list, no outbound network beyond the engine's local Ollama. Frontend lint/typecheck/9 tests + Rust fmt/clippy.
- ci.yml: a desktop job (eslint/tsc/vitest/vite build + cargo fmt/clippy) and a step that regenerates the IPC contract and fails on drift. Privacy guard now also greps the frontend + Rust shell for non-localhost URLs. - release-desktop.yml: PyInstaller sidecar build + universal Tauri bundle, optional Developer-ID signing/notarization, .dmg attached to the release. - README + DESKTOP_PLAN document the architecture.
- pnpm/action-setup no longer passes version: 10 — it conflicted with package.json's packageManager field (ERR_PNPM_BAD_PM_VERSION / 'Multiple versions of pnpm specified'). Version is read from package.json now. - privacy guard allow-lists www.w3.org: the only non-localhost URL the grep finds in desktop source is the SVG/XML namespace (xmlns="http://www.w3.org/ 2000/svg") in the inline logo — an identifier, not a network call.
…ise UI polish CI fix: move bundle.resources (sidecar + sandbox runtime) out of tauri.conf.json into a packaging-only overlay (tauri.bundle.conf.json) merged at release time. The base config no longer references the gitignored PyInstaller artifacts, so cargo clippy / dev builds stop failing with 'resource path doesn't exist'. UI/UX: raise the design system to an enterprise bar — - bundle Inter Variable (UI, tabular figures) + JetBrains Mono (code); no runtime font fetch, privacy-clean. - deeper layered dark palette (4 elevation steps), hairline borders tuned per layer, a physical shadow scale, subtle-foreground token. - refined titlebar (model chip), status bar (pulsing live dot, mono ⌘K), and composer (stronger elevation + focus ring). Upgraded dialog/dropdown shadows.
- titlebar badge bumped 18px→28px with tighter logo geometry (thicker frame, bigger arrow) so the enclave mark stays legible at small inline sizes instead of reading as a dot. - user messages no longer dump the raw <<<FILE …>>> prompt block into the bubble. cleanUserText() strips embedded file blocks (covers live sends and resumed sessions where the engine stored the assembled prompt) and surfaces attachments as chips; show only the question. Tested.
emre-kocyigit
approved these changes
Jun 8, 2026
emre-kocyigit
left a comment
Collaborator
There was a problem hiding this comment.
Looks good overall.
Privacy promise still holds.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Turns InClave into a native-feeling macOS desktop app without forking the engine. The privacy-first Python packages stay the single source of truth; the app drives them through a JSON-RPC sidecar. The CLI is unchanged in behavior.
Architecture
127.0.0.1:11434).ipc_request); it cannot invoke arbitrary Rust or Python.inclave_sandbox(Seatbelt, no network, rlimits).What's in here (by commit)
chat_engine(event-yielding turn loop) shared by the CLI and the bridge;chat.pybecomes a thin renderer. Adddelete_session; make the sandbox runtime bundle-aware viaINCLAVE_SANDBOX_RUNTIME.protocol.pyas the single contract source.@inclave/ipc-contractgenerates typed methods/events + zod validators from the bridge schema (drift-guarded in CI).Verification
-D warningsclean.Notes for reviewers
packages-js/ipc-contract/index.tsis gitignored and rebuilt bypnpm gen:ipc(CI regenerates + diffs).release-desktop.yml; it produces real.dmgs only on a signing-capable runner.