An agentic AI coding app for the Mac, with the TensorFold engine built in. It also works with any OpenAI‑compatible LLM endpoint, local or cloud. The look comes from the TensorFold brand: graphite and navy surfaces, an electric-blue primary and the fold gradient.
The current desktop release is beta 9. It launches the bundled native Zig engine directly.
It reads, edits, and runs your code through a tool‑use loop, with streaming responses, live diffs, and per‑action approval.
The Mac app requires Apple silicon and macOS 13.3 or later. The bundled offline voice runtime sets this minimum; isolated bot VMs additionally require macOS 26 or later.
- Bring your own model. Any OpenAI‑compatible
/chat/completionsendpoint works:- Local — oMLX, Ollama (
http://127.0.0.1:11434/v1), LM Studio (http://127.0.0.1:1234/v1), vLLM, llama.cpp. - Cloud — OpenAI, OpenRouter, z.ai GLM, or any compatible provider. Configure as many as you like.
- Rooms — pin each room seat to a different provider/model so local and cloud models can collaborate on the same task.
- Local — oMLX, Ollama (
- Full agentic loop. The model can
read_file,write_file,edit_file,list_dir,search, andrun_commandinside a workspace folder you choose. - Inline visualizations. Chat and Code can render structured
visualizewidgets for flow diagrams, bar comparisons, metric cards, and timelines. CLI-style providers can usevontra-widgetJSON fences for the same renderer. - Approval gates. Three modes: Approve edits (reads run freely, writes/commands ask), Full auto, and Read only. Reasoning, streaming, tool cards, and unified diffs all render live.
- Native chrome. Frameless window with inset traffic lights, draggable titlebar, a task sidebar grouped by time, and a project‑grouped home launcher.
- Local‑first. Settings and conversations are stored only on your machine.
- Local AI workspace. Open Models for the installed library, downloads, Workers and the local API, with engine state, memory requirements and context visible together. Chip and memory details guide a recommendation after compatibility checks; download it, then Start chatting loads it; first-run setup finishes only after the welcome guide. Decode/prefill speed and first-token time show the latest completed TensorFold request, using reported engine timings or measured stream usage; unknown values stay blank.
- Independent local workers. Choose a local Git project and the loaded TensorFold model, then give each task a verified worktree and branch. Workers remain visible while you use the app, with approval, Stop, recovery and Changes review actions; a document-task preset supports repository documentation work.
- Use the local API. Loading a model starts its authenticated loopback endpoint. The API tab copies the base URL or an authenticated streaming curl example on request; the port and key change when the engine restarts, and the key is excluded from normal snapshots and events.
npm ci # npm 10.9.8; lifecycle scripts stay disabled by policy
npm run deps:trusted-scripts # explicitly install Electron and rebuild node-pty
npm run dev # starts Vite + launches the Electron appUse npm run install:verify-clean to validate the lock with a real isolated npm ci, the
package-scoped Electron install/node-pty rebuild, and a native import smoke check without touching the
current node_modules. If install:check reports pnpm or a
self-referential/hybrid install, move/remove that node_modules and run npm ci.
After recreating the tree, rerun npm run deps:trusted-scripts before starting TensorFold; the
project-level ignore-scripts=true intentionally prevents npm ci from installing native
Electron/node-pty runtime pieces by itself.
Then open Settings → Providers, paste an API key (or point at a local endpoint), pick a model in the composer, choose a workspace folder, and start a task.
Ask the built-in agent to “Use Parallel to search the web” or “Use Parallel to read this URL.”
The web_search and web_fetch tools accept provider: "parallel" for
Parallel Search MCP: general web results
and readable page excerpts, with no account or API key. Anonymous search uses Fast mode and
has service rate limits. Queries and requested URLs go to Parallel when this provider is selected.
Omitting provider keeps Google News RSS search and direct page fetching. Explicit
provider: "google_news" for search and provider: "direct" for fetching select those paths.
There is no automatic provider fallback after an error. Existing web-tool permissions and
local-only conversation restrictions apply. In the built-in agent loop, Parallel calls share
an opaque conversation ID across search, fetch and follow-up turns; a new chat gets a new ID. MCP requests time out
after 20 seconds and stop when the owning run is cancelled. Fetches return excerpts rather
than a complete page; max_chars still caps the displayed text.
Open http://localhost:5173 in a browser (no Electron) and TensorFold runs in demo mode with
sample data and a simulated agent — handy for previewing the UI. Real tool execution only happens
in the desktop app.
npm run start:headlessHeadless startup builds the app, enables the remote-control server, persists a strong token, and
binds 127.0.0.1 by default. The first successful bootstrap prints the host token and a ten-minute
browser pairing link once; later starts never reprint secrets. For a remote host, use the printed
SSH tunnel command and open the loopback pairing URL locally.
If the bootstrap credential is lost, explicitly rotate it (and revoke existing paired sessions):
VONTRA_HEADLESS_BOOTSTRAP=1 npm run start:headlessVONTRA_HEADLESS_PORT changes the port. The built-in server is always loopback-only:
VONTRA_HEADLESS_BIND_HOST accepts only 127.0.0.1, and any other value fails startup. For
external access, keep TensorFold on loopback and expose it through the SSH tunnel or a separate TLS
reverse proxy you control; do not bind TensorFold itself to 0.0.0.0.
npm test # guarded agent/runtime suite
npm run typecheck # TypeScript check without emitting files
npm run verify # non-build preflight: tests + typecheck + git diff checksnpm test includes guards that fail when a *.test.ts, *.test.tsx, or *.test.mjs file is not
wired into the main test suite, or when source/docs contain non-placeholder email addresses.
Open Phone beside the model picker in a chat, refresh devices and connect your phone. Android works through installed Android Platform Tools over USB or paired Wireless debugging; iPhone uses Apple's Wi-Fi Mirroring or your own signed WebDriverAgent over a loopback USB forward or private Wi-Fi endpoint. TensorFold does not bundle, copy or install phone-harness.
Choose Allow this chat to give one local conversation access for up to 15 minutes. That conversation then stays on local models with Cortheon and public web research off, including after disconnection, so derived phone text cannot silently move to a cloud route. Screen previews stay in memory and are captured only on request; tool output contains bounded accessible/OCR labels and bounds rather than screenshot images. Device/app text is untrusted. Every model input action asks for approval, checks the complete live screen before input, and reads the result; Stop cancels work and clears its grant and preview. Chat control pauses after 40 dispatched actions or three unchanged results. Protected, unlabelled or committing steps need manual takeover, and Android typing supports printable ASCII only.
See phone connection and privacy details for platform prerequisites, known limits and the distinction between implemented adapters and physical-device qualification.
First-run setup offers Minimal or Full, then model setup and a short guide. Minimal has a native menu-bar icon, server controls, downloads and authenticated localhost API connection details; Full opens Chat, Code and Assistant. Switch in Settings → App & onboarding or reopen the guide there. Setup does not download or start a model without a deliberate action. Closing the Minimal window keeps the server running; Quit stops it, including a safe wait for an active learning checkpoint save. Relaunching Minimal restores its menu-bar controls without automatically allocating model memory.
Settings → Model folders optionally scans the configured and default Hugging Face cache or folders chosen in the native picker. Scans stay local, are bounded and cancellable, and check architecture, quantization, required shards and memory before enabling Load. Supported models load from their existing location. Removing a search folder unlinks its models without deleting files or interrupting an already running API; moved folders must be selected again. Checkpoints borrowed from another cache are read-only in TensorFold, and unsupported formats remain labelled. The API connection page copies the URL, generated session key or authenticated example only on request; restart the model and copy fresh details when the port and key change.
With the built-in TensorFold model selected, AI settings beside the chat model picker opens the Living Weights controls and Cortheon's status. Cortheon web research remains disabled in this beta, including previously saved On choices and old API enable calls. Local chat and API serving remain available; independently configured servers and the standalone CLI keep their own settings. Changing a supported engine mode restarts a loaded model and is blocked while managed local work or a learning checkpoint save is active.
Settings → Voice → Kokoro downloads a pinned, checksum-verified 92 MB speech model; subsequent playback runs offline in a managed native CPU worker. This version offers American English voices, preview/Stop, retry/cancel and removal of the managed download. No API key, Python installation or separately running speech server is required. Its bundled English phonemizer uses the MIT HeadTTS implementation and BSD CMU dictionary; it does not ship or run the upstream eSpeak phonemizer, and required notices ship with the app.
Local AI → Learning shows the experimental Living Weights learner and its engine-reported fact graph, recall checks and neuron/layer details. Only the exact Nemotron 3.5 Lightning MLX 4-bit checkpoint on Apple Silicon is supported. Enabling it prepares an independent private copy; lessons are submitted explicitly, never taken automatically from chat history. Learned facts persist when teaching is turned off and across restarts; there is no per-fact undo. The shared original checkpoint stays untouched. The private checkpoint and graph are local files without app-level encryption. API credential sharing pauses while learning is enabled so external clients cannot bypass the managed checkpoint-save protections; turn learning off to share the ordinary chat API again. A graceful Quit waits for any lesson to stop and save. The owner-confirmed Erase local memory operation also removes the complete private learning copy, graph and lesson history, while preserving the original downloaded checkpoint.
Plugins separates searchable built-in tools, installed registrations, connections and advanced MCP configuration. Worker-required tools and configured trust records are labelled explicitly; configuring a connection opens its real setup flow. Registered plugins still use the existing execution/trust policy, and configured permissions are not a sandbox guarantee.
npm run build # builds renderer (dist/) + electron main & preload (dist-electron/)
npm run pack:mac:dev # explicit unsigned local .app; never distribute it
npm run smoke:mac:packaged # isolated loopback smoke for the current packaged .app
npm run pack:mac # Developer ID signed + notarized unpacked release app
npm run dist:mac # Developer ID signed + notarized dmg + zip release artifactsThe pack scripts first run scripts/fetch-engine.mjs, which stages the TensorFold engine pinned in
build/engine.json into build/engine/. That manifest pins the engine version independently of the app version.
It takes the archive from
TENSORFOLD_ENGINE_ARCHIVE when set, otherwise from the pinned release URL (cached in
build/.engine-cache/ after the first download). Anything whose sha256 or SHA256SUMS does not match
is refused. The app ships it at Contents/Resources/engine/{bin,lib};
packaging checks that --version prints the pinned version, and release mode also checks each engine
Mach-O for the pinned Developer ID signer, team and hardened runtime. For npm run dev, run
npm run engine:fetch once; TENSORFOLD_STUDIO_ENGINE_BIN points a dev build at another binary and is
ignored by packaged apps. Models download into the shared Hugging Face cache (HF_HUB_CACHE, else
HF_HOME/hub, else ~/.cache/huggingface/hub), the same folder the engine's pull uses.
Release packaging requires CSC_NAME and APPLE_TEAM_ID to exactly match the production-ready
identity pinned in build/release-trust.json, plus either a pre-stored notarytool keychain profile
(APPLE_KEYCHAIN_PROFILE, optionally APPLE_KEYCHAIN) or the App Store Connect API-key credential
set. The repository pins the production public update-verification key and Developer ID signer;
private signing keys and notarization credentials must stay outside the repository. Raw Apple
ID/app-specific-password mode is refused so the password cannot leak through process arguments or
command-failure logs. The wrapper verifies the unpacked app and the app independently extracted from
each DMG/ZIP, including exact signer, team, bundle metadata, notarization, and CDHash equality.
Release update manifests require --private-key; only release:manifest:dev permits an explicitly
unsigned local fixture.
After packaging, npm run smoke:mac:packaged launches the current-architecture app headlessly with
an internally generated mode-0700 temporary VONTRA_HOME. The command accepts no profile-path
override, suppresses one-time bootstrap credentials, checks only a newly selected loopback port
(health 200, nonempty UI 200, and unauthenticated snapshot 401), then terminates the app,
verifies the listener closed, and removes only that exact temporary profile. It never repurposes
HOME for isolation and must not be replaced with a hand-written HOME=... smoke command.
Before Electron becomes ready, that VONTRA_HOME also redirects Electron appData, userData,
sessionData (including Chromium cookies and cache), temporary files, logs, and crash dumps into
private subdirectories of the disposable root. With isolation active, legacy application-data
migration is skipped without looking up or copying from the real macOS app-data directory.
Security release and operations controls are tracked in
docs/security-operations.md.
The app, bundle identifier, package and release artifacts now use TensorFold. Existing
Electron data is migrated from the previous app names without overwriting current files. The
primary preload bridge is window.tensorfoldStudio; window.vontra remains a
compatibility alias. The existing ~/.vontra profile, VONTRA_* environment controls, local
storage keys and wire-format names remain stable so installed integrations, encrypted data and
saved sessions continue to work. These are compatibility identifiers rather than displayed
product names. The signed update feed is https://tensorfold.dev/studio/update.json; publishing
and verifying that feed remains a separate release prerequisite.
electron/
main.ts Window, IPC, run orchestration (abort + approval plumbing)
preload.ts contextBridge → window.tensorfoldStudio
store.ts Settings + threads persistence (userData JSON)
agent/
openai.ts Streaming OpenAI-compatible client (SSE, tool-call accumulation)
tools.ts Sandboxed fs + shell tools, OpenAI function schemas
loop.ts The agentic loop: stream → tool calls → approval → execute → repeat
diff.ts LCS line diff → unified hunks
types.ts Shared types (also used type-only by the renderer)
src/
styles/index.css The TensorFold design system (Tailwind v4 @theme)
lib/ store (zustand), ipc bridge, icons, formatting, browser mock
components/ Sidebar, TitleBar, Home, Composer, Thread, timeline, visualizations, Settings, ui
Stack: Electron · React + TypeScript · Vite · Tailwind CSS v4 · Zustand.