An open-source, cross-platform AI chat desktop app.
Bring your own API keys. Chat with any LLM. Run agents. Connect bots to Telegram, Discord, and Slack.
Download
Just pick your platform and run the installer. That's it.
| Platform | Link | What to do |
|---|---|---|
| Windows | Download .exe installer | Run the .exe → click "Install" → done |
| macOS | Download .dmg | Open the .dmg → drag to Applications → done |
| Linux | Download .AppImage | chmod +x the file → double-click → done |
Don't see your platform? Check the all downloads page for
.deb, other architectures, and older versions.
On first launch, the onboarding wizard walks you through selecting a provider and entering your API key.
Features
LLM & Providers
- Multi-Provider LLM Support — OpenAI, Anthropic, Google Gemini, Groq, Mistral, Ollama (local), OpenRouter, x.AI, DeepSeek, Qwen, Together, Fireworks, and Cohere
- Profiles and Roles — Named sets of models per role (main, worker, utility, and more); each chat or case picks a profile, and a failing model fails over to the next in its role
- Prompt Caching — Anthropic
cache_controlblocks on system prompts reduce input token costs by 50–90% on multi-turn conversations, with cache-aware cost tracking - Extended Thinking — Claude 3.7+ models can use extended thinking with configurable budget tokens for deeper reasoning on complex tasks
Agents & Tools
- Agentic Tool Use — Agents can execute shell commands, read/write/edit files, search the web, automate browsers, and more
- Agent Streaming — Real-time token-by-token streaming during agent loop iterations instead of waiting for the full response
- Workflow Engine — Durable, multi-step workflow execution with pause/resume, parallel task execution, dependency ordering, and persistent state across sessions
- Planner Agent — Decomposes high-level goals ("build a REST API with auth and tests") into structured task graphs that the workflow engine executes automatically
- Dynamic Sub-Agents — Agents can spawn specialized sub-agents mid-execution to handle subtasks with different models, tools, or system prompts
- Background Tasks — Spawn agent tasks that run asynchronously in the background while the main conversation continues, with output logging and status checking
- Worktree Isolation — Background tasks can run in isolated git worktrees to prevent file conflicts with the main workspace
- Advisor Mode — Optional second-model code review that automatically reviews agent-generated code changes for bugs, security issues, and performance problems
- Multi-Agent Orchestration — Run agents in parallel, serial, or dependency-based workflows
Tools
- 20+ Built-in Tools — Bash, Read, Write, Edit, MultiEdit, Glob, Grep, Git, WebSearch, WebFetch, Browser, ToolSearch, BackgroundTask, TaskStatus, SpawnAgent, and more
- MultiEdit — Batch edit multiple files in a single tool call with cascading failure isolation and per-file content caching
- Deferred Tool Loading — Core tools load inline; others are deferred behind a ToolSearch meta-tool that supports keyword search and exact selection — stabilizes prompts for caching
- MCP Support — Model Context Protocol client with stdio transport connects to any MCP server, automatically registering its tools into the tool registry
- Git Safety Guards — Blocks
--amend,--force,--no-verify, interactive flags,git add ., and sensitive file patterns (.env, credentials, keys) - Structured Diffs — Edit and Write tools generate unified diffs with line stats, displayed as colored diff blocks in the UI
- Git Context Injection — Current branch, working tree status, and recent commits are automatically injected into the system prompt
Context & Performance
- API-Native Context Compaction — Clears old tool result content when approaching token limits (Anthropic provider), with embedding-based fallback for other providers
- Semantic Context Assembly — Dynamic per-turn tool and prompt section selection via embeddings, cutting token overhead by 30–60%
- Result Persistence — Oversized tool results are persisted to disk with a preview marker; the model can Read the full file if needed
Integrations & Infrastructure
- System App Discovery — Auto-detects installed desktop applications (Excel, Photoshop, VS Code, etc.) so agents use local software instead of generating content via LLM
- Extensible Skill System — Install, remove, enable, and pin custom skill plugins
- Mesh Networking — Secure peer-to-peer communication between King Louie instances across machines
- Channel Integrations — Bridge conversations to Telegram and Discord bots, behind a deny-by-default sender allowlist (Slack is connected but its inbound path is not wired to the agent yet)
- Cron Scheduling — Schedule recurring or one-time agent tasks with cron expressions
- Semantic Memory — Embedding-based memory with hot/warm/cold tiering and recall
- Voice / TTS — System TTS or ElevenLabs for voice responses
- Webhooks — HTTP endpoints for external automation triggers
User Experience
- Command Palette —
Ctrl+Kopens a searchable command palette for quick access to all actions, commands, and settings - Keyboard Shortcuts —
Ctrl+N(new chat),Ctrl+L(clear input),Ctrl+,(settings),Ctrl+Shift+E(export) - Chat Search — Real-time search box in the sidebar filters chats by title and preview text
- Thinking Indicator — Animated "Thinking..." appears while waiting for the LLM to respond, replaced when streaming begins
- Agent Progress Bar — Shows iteration count, current tool, and elapsed time during agent execution
- Copy Code Button — One-click copy button on code blocks with language badge and "Copied!" feedback
- Diff Display — Edit/Write tool results render as syntax-highlighted colored diffs instead of raw JSON
- Retry on Error — Failed messages show a "Retry" button to resend without retyping
- Markdown Export — Export conversations as readable Markdown with collapsible tool results
- Welcome Card — First-run quick-start tips for new users (API key setup, agent mode, commands)
- Dark Theme UI — Two-pane chat interface with syntax highlighting, markdown rendering, and image attachments
- Cross-Platform Builds — Windows (NSIS), macOS (DMG), and Linux (AppImage/DEB) via GitHub Actions
Demo
Building from Source
Troubleshooting: Cannot read properties of undefined (reading 'registerSchemesAsPrivileged')
If npm start fails immediately with this error from main.js, something in
your environment has set ELECTRON_RUN_AS_NODE=1. That variable tells the
Electron binary to behave as plain Node, so there is no app, no protocol,
and no browser process — the main process crashes on the first Electron API it
touches.
It is most often inherited from another Electron-based program that spawned
your shell (VS Code's integrated terminal and Electron-based CLI agents both
set it). Setting the variable to an empty string is not enough — Electron
treats a present-but-empty value the same as 1. It has to be removed from
the environment entirely:
unset ELECTRON_RUN_AS_NODE && npm start # bash / zsh $env:ELECTRON_RUN_AS_NODE=$null; npm start # PowerShell
The same applies when driving the app with Playwright's _electron.launch —
delete the key from the env you pass to the child, or it will fail with
"Process failed to launch!".
Running as a Service
King Louie can also run headless, with no Electron and no UI — driven instead
by chat channels (Telegram, Discord, Slack), cron, and, from later stages, a
multi-machine fleet. This is a separate entry point (bin/king-louie-service.js)
from the desktop app; the two can run side by side on one machine without
sharing data.
Install
Always try --dry-run first — it prints every step the installer would take
without touching the system.
# Linux (systemd), from a root shell sudo node bin/king-louie-service.js install --profile agent # macOS (LaunchDaemon) — create a dedicated standard (non-root) account first, # then install under it sudo node bin/king-louie-service.js install --user <account> # Windows, from an elevated (Run as administrator) shell — a boot-time # Scheduled Task node bin\king-louie-service.js install
install (and uninstall) require an elevated/root shell on every platform —
they write a systemd unit, a LaunchDaemon, or a Scheduled Task, none of which a
standard user can register.
Reinstalling is safe: it skips creating the service account if one already
exists, and it never overwrites an existing master key — a second install
run reuses the key the first one generated. It also picks up the new service
definition: on Linux the unit is restarted after enable --now, and on macOS
any previously loaded LaunchDaemon is booted out before it is loaded again.
Linux and macOS: before anything is created, the installer checks that
every ancestor of the data directory is a real, root-owned directory, that the
data directory's immediate parent is neither group- nor world-writable, and
that the data directory itself is not a symlink. A missing parent (the default
macOS …/KingLouie) is created root-owned 0755, so the service account
cannot later replace the data directory with a link. --data-dir /tmp/… and
any other path under a world-writable parent is refused: install -d -o <account> chowns by name and would follow such a link, handing its target to
the service account on the next reinstall. No install step creates or chowns
anything inside the data directory — the service creates logs/ and
cache/ itself, under its own account.
Windows only: the installer creates the data directory itself with a
locked-down ACL (Full Control limited to LOCAL SERVICE, SYSTEM and
Administrators, with inheritance disabled). If its parent
(%ProgramData%\KingLouie for the default data dir) doesn't exist, it is
created Administrators-owned with its own protected ACL (Full Control to
SYSTEM and Administrators, read-only to LOCAL SERVICE). If either directory
already exists, the installer only verifies it — a hand-created directory, or
one with looser permissions, is refused rather than silently relocked. Fix or
remove it manually before retrying.
Linux only: /etc/king-louie is created root-owned with mode 0755
(later stages keep read-only configuration there); only
/etc/king-louie/credentials, which holds the master key, is 0700.
On every platform, if node or the entry script (bin/king-louie-service.js)
lives under a home directory (/home, /root, /Users, or C:\Users\), the
installer prints a warning: the service account (or anyone with access to it)
could then rewrite the binary it runs. Move the install to a system path.
Configure
-
<configDir>/service.jsonsetsfeaturesandports. The config directory is owned by root/Administrators and is only readable by the service account — the data directory is not, so nothing that decides whether a network listener exists is read from there:Platform configDirLinux /etc/king-louiemacOS /Library/Application Support/KingLouie/configWindows %ProgramData%\KingLouie\configThe Linux and macOS installers create it. On Windows, create it from the elevated shell after
install(mkdir %ProgramData%\KingLouie\config); it inherits the parent's protected ACL, which grantsLOCAL SERVICEread and execute only. If the file is missing, every feature stays off.The table shows the default data directory's config directory. An instance installed with a
--data-dirof its own gets a config directory of its own,configbeside that data directory — including on Linux, where every instance used to read the same/etc/king-louieand so inherited the first instance'sports. Two services on one machine can therefore carry different ports; the installer creates each one root-owned, and a config file the service account owns or could write is still refused.Every feature (
gateway,webhooks,mesh,channels,appDiscovery,desktopBridge) is off by default, and each one that is on is logged at startup naming the file that enabled it.meshcannot be enabled in service mode yet: it is forced off (with a warning in the log) whatever the config says. On Linux and macOS the service refuses to read aservice.jsonthat is group- or world-writable, or one owned by the account the service runs as.portsdefaults to{ "gateway": 18793, "webhook": 18794, "desktopBridge": 18796 }— clear of the desktop app's 18789/18790 and of the mesh port 18791, which the desktop app binds on0.0.0.0by default. A listener that is enabled but cannot bind its port is fatal: the service refuses to start rather than run without the listener you asked for. -
profile(agentorrunbook) comes from<configDir>/service.jsonor the unit's--profile, never from<dataDir>/service.json: which profile runs decides whether the agent stack loads at all.features,portsandprofilein the service-writable<dataDir>/service.jsonare all ignored, with a warning naming the file. -
king-louie-service token set anthropic < keyfile— stores a provider API key (read from stdin, never a CLI argument, so it doesn't end up in shell history orps). The provider must be one king-louie knows (openai,anthropic,groq, …); an unknown name is rejected. -
king-louie-service vault set <key> < valuefile— stores an arbitrary secret in the vault the same way. -
king-louie-service channel list|allow|remove|approval <channel> …— manages who may drive the agent through Telegram or Discord, and where that channel's approvals go. A channel with an empty allowlist refuses everyone, so this is required before a channel does anything at all. See Channel Integrations.
token set, vault set and the mutating channel subcommands against an
installed service:
- Stop the service first. They refuse (exit 1) while the service is
running on that data dir, because the running service would overwrite the
change with its own in-memory copy.
channel listis read-only and stays available. - Run them as root (Linux, macOS) or from an elevated shell (Windows). On
Linux, root reads the same
<configDir>/credentials/kl-master-keythe unit hands the service; on Windows the key is DPAPI-protected in the machine scope — unwrappable by anything on the box, so what keeps it private is only the data dir's ACL, which grantsLOCAL SERVICE,SYSTEMand Administrators (see the known gap below). On Linux and macOS, files the CLI creates in the data dir are handed back to the data dir's owner. - Every data dir holds a
key-checkfile written on first use. If a command resolves a different master key than the one the data dir was encrypted with (for example, run under the wrong account), it stops with an error naming the key source instead of writing secrets the service can't read.
The CLI's flag parsing is strict: an unknown --flag, a flag given with no
value, and an empty --data-dir all exit with status 2 rather than silently
falling back to a default. Both --flag value and --flag=value are
accepted.
Default Data Directory
| Platform | Default --data-dir |
|---|---|
| Windows | %ProgramData%\KingLouie\data |
| macOS | /Library/Application Support/KingLouie/data |
| Linux | /var/lib/king-louie |
The service's working directory is <dataDir>/workspace, not the data
directory itself: the data directory holds the master key, the gateway
token and the encrypted stores, and the agent's read tools (Read,
Grep, Glob) are not approval-gated, so anything reachable from the
working directory is reachable from a chat message. The secret files are
additionally denied outright, whatever the working directory is. The
service process chdirs into that workspace at startup, so anything it
spawns without an explicit working directory — a stdio MCP server, for
instance — starts there too rather than in the data directory.
Operate
king-louie-service status [--data-dir DIR]— reports whether the service is running, by checking that the process named in<dataDir>/service.pidis alive.king-louie-service doctor [--data-dir DIR]— checks Node version, data directory permissions, and (on Windows) that the DPAPI-wrapped master key is present.- Logs: on every platform the service appends its log to
<dataDir>/logs/service.log(mode0600on Linux and macOS). Alsojournalctl -u king-louieon Linux and/var/log/king-louie/service.out.log/service.err.logon macOS (the LaunchDaemon's stdout/stderr, in a root-owned directory: launchd opens those paths itself and follows symlinks, so they must not sit anywhere the service account can write). On Windows,service.logis the only log: Task Scheduler's History tab records only that the task started and stopped, not its output. - Stopping on Windows:
schtasks /End /TN KingLouie(anduninstall, which runs it) terminates the process immediately — in stage 1 there is no graceful shutdown on Windows, so in-flight work is cut off. Linux and macOS send SIGTERM and the service shuts down cleanly.
Security Notes
- Master key location, per OS: a systemd credential (
kl-master-key, Linux with systemd; root outside the unit reads the same file from/etc/king-louie/credentials); Windows DPAPI in theLocalMachinescope inmaster.key.dpapi, kept private only by the data directory's ACL (LocalMachinescope means any code on the machine can unwrap it — see the known gap below); otherwise a0600key file at<configDir>/credentials/kl-master-key(macOS, and Linux without systemd credentials). That file is outside the data directory on purpose: a backup, a snapshot or atarof the data directory would otherwise carry both the ciphertext and the key that opens it. The directory is root-owned and not writable by the service account, so the key can be read by the service but not replaced by it. A key already at the old location (<dataDir>/master.key) keeps working and the service logs where to move it; if there is no config directory at all, the key still lands in the data directory, with a warning saying so. - The gateway (when enabled) requires a bearer token, kept encrypted in the
store. A cleartext copy is written atomically to
<dataDir>/gateway-tokenso local clients and CLI tooling can read it — but only while the listener is actually bound: the file is written after the bind succeeds and removed again when the gateway stops, so a failed start never leaves a valid credential on disk for a port nothing is listening on. On Linux and macOS it is mode0600; on Windows the mode bits are meaningless and its only protection is the data directory's ACL (LOCAL SERVICE,SYSTEMand Administrators). If secure storage is unavailable on that host, the token falls back to session-only: it still works for the current run but is regenerated (and every existing client rejected) on the next restart, rather than silently persisting in the clear. - The service denies every action that requires interactive approval —
stage 1 has no remote approver (phone approvals arrive in a later stage),
so anything gated on approval simply fails. That includes requests from
chat channels: if you turn
channelson, a channel is never asked to approve (no Approve button is sent) and approval-gated tools are still denied. Setting a channel's approval target does not change that in service mode — it only matters in the desktop app. - Chat channels are closed by default: a channel whose allowlist is empty
refuses every sender, so turning
channelson does not by itself let anyone in.king-louie-service channel allow …opens it one id at a time. - On Windows the service runs as
LOCAL SERVICE. It is low-privilege and has no access to any user's profile folders — but it is a shared, built-in account, not a dedicated identity for King Louie. Every other service on the machine that runs asLOCAL SERVICE(a third-party updater, an OEM agent, anything an attacker gets code execution inside) has the same SID, so the data directory's ACL grants it the same access: it can read the cleartext<dataDir>\gateway-tokenand drive the gateway, and it can readmaster.key.dpapiand runProtectedData.Unprotecton it — the blob isLocalMachine-scoped with null entropy, so nothing beyond that ACL keeps it private — and from there decrypt every provider API key and vault entry. Treat "anything on this machine running asLOCAL SERVICE" as inside King Louie's trust boundary on Windows.
Known gap — Windows service identity
The shared-account problem above is a known gap, deferred to a dedicated Windows-hardening stage rather than patched around. The fix is a dedicated identity end to end:
- The installer creates a low-privilege local account for the service (or
uses a virtual service account,
NT SERVICE\KingLouie, which Windows gives its own per-service SID). - The Scheduled Task's
<UserId>becomes that SID instead ofS-1-5-19. master.key.dpapiis protected in theCurrentUserscope of that account (orLocalMachinewith a per-install entropy blob) rather than plainLocalMachine+ null entropy.- The data directory's SDDL names that SID alone in place of
(A;OICI;FA;;;LS), so no other service on the box can read the data dir.
Linux and macOS already have this: the installer creates a dedicated
king-louie / --user <account> service account, and nothing else on the
machine runs as it.
Fleet setup and examples
examples/ holds a complete, invented fleet: node.yaml and service.json
for four roles (gpu-box, laptop, mac, web-01), six runbooks, the
sudoers file and Windows ACL script those runbooks need, and Claude Desktop
configs that start the stdio MCP server in a pinned working directory.
docs/install-guide.md walks through setting up a node
from them: the service, the admin-owned config dirs, the privileges, doctor,
and a first runbook over stdio MCP.
doctor also checks every runbook step's program: that it exists and is
not a .cmd/.bat file; on Windows, that it is an absolute path (a bare
name is looked up in the current directory first); on Linux and macOS, that
a bare name is on PATH (the examples still use absolute paths), and that
each sudo -n step is allowed by sudoers (sudo -n -l, which lists and
never runs). Run it as the account that runs the runbooks.
Supported Providers
| Provider | Models | Local |
|---|---|---|
| OpenAI | GPT-4o, GPT-5, o1, o3-mini, etc. | No |
| Anthropic | Claude Sonnet 4, Opus, Haiku, etc. | No |
| Google Gemini | Gemini 2.0 Flash, 2.5 Pro, etc. | No |
| Groq | Llama, Mixtral (ultra-fast inference) | No |
| Mistral | Mistral Large, etc. | No |
| Ollama | Any Ollama-hosted model | Yes |
| OpenRouter | Multi-provider router | No |
| x.AI | Grok 3, Grok 3 Mini | No |
| DeepSeek | DeepSeek Chat | No |
| Qwen | Qwen Plus | No |
| Together AI | Llama, open-source models | No |
| Fireworks AI | Llama, fast inference | No |
| Cohere | Command R+ | No |
Configure providers and API keys in Settings.
Profiles and roles
King Louie uses only the models you place in a role. A profile is a named set of those assignments; manage profiles in Settings > Models.
- Roles.
mainanswers chats and runs the agent loop,workerhandles sub-agent work such as code exploration, andutilityhandles quick jobs such as case orientation and classification.visionandimageGenerationare optional specialist roles. An emptyworkerborrows frommain, and an emptyutilityfromworker, thenmain. - Ordered lists. Each role holds a list of models in the order to try them. A model is used only when its provider's connection test passes and it can do what the call needs (tool calls in agent mode, image input when you attach images).
- Default profile. New chats use the default profile. The chat header's profile picker gives one chat or case a different profile, and its main switcher sets a different main model for that chat or case alone.
- Frozen per turn. A turn keeps the models it started with, sub-agents included; a switch or a profile edit applies from the next turn.
- Failover. A failing model hands over to the next one in the role's list. The turn's first model call may move to any provider; later calls stay on the same provider, since the conversation is then in that provider's format.
- Retry with… On the last reply, pick another model to switch main and send the last message again.
- Cases map their roles onto these: orient and classify use
utility, draft usesworker, judge and verify usemain. - Headless runs (the service, channels, cron and the gateway) use the default profile.
Earlier versions chose models by fast, standard and smart tiers, with
optional keyword, regex and prefix routing rules and an LLM-powered router.
The tiers move once, at the first start of this version, into a profile
named "Migrated settings", and its card in the Models tab lists what the
move changed. The routing rules and the LLM router are gone:
messages go to main as written.
Workflow Engine
King Louie includes a durable workflow engine for executing complex, multi-step goals. Instead of manually breaking work into prompts, describe the outcome you want and let the system figure out the steps.
How It Works
- Go to Settings > Workflows
- Enter a goal (e.g., "Build a REST API for user authentication with tests and documentation")
- Click Plan & Execute
The system:
- Runs the Planner Agent to decompose the goal into a structured task graph
- Creates a durable workflow with dependency ordering and parallel groups
- Executes tasks through the appropriate agents (code-writer, code-explorer, main)
- Streams progress events to the UI in real-time
Workflow Lifecycle
| Status | Meaning |
|---|---|
pending |
Created but not started |
running |
Tasks are being executed |
paused |
Execution suspended — can be resumed |
completed |
All tasks finished successfully |
failed |
A critical task failed |
cancelled |
Manually cancelled by user |
Workflows persist to disk and survive app restarts. A workflow that was running when the app closed will resume as paused on next launch.
Task Graph
The planner outputs a JSON task graph with:
- Tasks — Each with a title, description, assigned agent, and priority
- Dependencies — Tasks only run after their dependencies complete
- Parallel groups — Independent tasks execute concurrently
- Model preferences — Tasks can suggest specific models (e.g., Gemini for research, Opus for deep reasoning)
Workflow Controls
From the Workflows panel, you can:
- Resume a paused workflow
- Pause a running workflow
- Cancel a workflow entirely
- Delete a workflow and its saved state
- Plan Only — Generate the task graph without executing it
Dynamic Sub-Agents
Agents can spawn specialized sub-agents mid-execution using the SpawnAgent tool. This enables recursive problem-solving — when an agent hits a subtask that needs different capabilities, it creates a new agent for it.
Example
User: "Refactor the auth module and update the docs"
Main Agent:
→ SpawnAgent(agentId: "code-writer", task: "Refactor auth module to use JWT")
→ SpawnAgent(agentId: "code-writer", task: "Update API documentation to reflect auth changes")
SpawnAgent Parameters
| Parameter | Description |
|---|---|
task |
The instruction for the sub-agent (required) |
agentId |
Which agent to use: main, code-explorer, code-writer, planner |
model |
Override the model (e.g., gpt-4o, claude-sonnet-4-20250514) |
provider |
Override the provider (e.g., openai, anthropic, gemini) |
maxIterations |
Max tool iterations (default: 10) |
systemPromptAppend |
Additional instructions for the sub-agent |
tools |
Restrict to specific tools (e.g., ["Read", "Grep"]) |
Sub-agents run in their own conversation context and return results inline to the parent agent.
System App Discovery
King Louie auto-detects installed desktop applications on your system and makes them available to agents. When a task can be done with local software (creating a spreadsheet, editing an image), agents will use the installed app instead of trying to generate content through the LLM.
Auto-Detected Apps
The discovery engine checks platform-specific locations:
| Platform | Detection Method |
|---|---|
| Windows | PATH, Registry (COM), Start Menu, Program Files |
| macOS | /Applications, Spotlight (mdfind), PATH |
| Linux | .desktop files, PATH |
Categories include office (Excel, Word, LibreOffice), development (VS Code, Cursor), browsers, graphics (Photoshop, GIMP, Figma), media (OBS, VLC, FFmpeg), communication (Slack, Discord, Teams, Zoom), and more.
Managing Apps
Go to Settings > System Apps to:
- View all discovered apps grouped by category with their launch commands
- Re-scan to refresh after installing new software
- Add custom apps for software in non-standard locations
- Remove custom app entries
Custom apps persist across restarts and are merged with auto-detected apps in the agent system prompt.
How Agents Use It
The discovered app list is injected into every agent's system prompt. When you ask "create a spreadsheet of Q1 sales data", the agent will:
- Generate the
.xlsxfile content using a library - Launch Excel (or whatever spreadsheet app is installed) to open it
Instead of trying to render a table in chat or generating a CSV via the LLM.
Built-in Tools
Agents have access to a suite of tools that can be individually approved or auto-approved:
| Tool | Description |
|---|---|
Bash |
Execute shell commands (platform-aware) |
Read |
Read file contents |
Write |
Create or overwrite files (generates diff for overwrites) |
Edit |
Exact string replacement in files (generates unified diff) |
MultiEdit |
Batch edit multiple files in a single call with cascading failure isolation |
Grep |
Regex content search across files |
Glob |
File pattern matching |
Git |
Git operations with safety guards (blocks --amend, --force, --no-verify, sensitive files) |
WebSearch |
Search the web (Brave, Tavily) |
WebFetch |
Fetch and parse web pages |
Browser |
Headless browser automation via CDP |
ToolSearch |
Search and load deferred tool schemas on demand (keyword, exact, or prefix match) |
SpawnAgent |
Dynamically spawn sub-agents with different models, tools, or specializations |
BackgroundTask |
Spawn agent tasks that run asynchronously with optional worktree isolation |
TaskStatus |
Check status, read output, list, or stop background tasks |
Cron |
Manage scheduled tasks |
RemoteDispatch |
Dispatch tasks to remote King Louie peers on the mesh network |
AskUser |
Request user input during execution |
Skill |
Invoke installed skill plugins |
RequestTools |
Legacy escape hatch for requesting additional tools mid-conversation |
Browser Tool — Using Your Chrome Profile
By default, the browser tool launches with an isolated temporary profile (no saved logins or cookies). To browse authenticated sites like Reddit, GitHub, or Gmail, you can start the browser with your existing Chrome profile:
User: "Check my Reddit for new comments on my posts"
Agent: [starts browser with your Chrome profile, navigates to reddit.com — already logged in]
The start action accepts three optional parameters:
| Parameter | Description | Example |
|---|---|---|
userDataPath |
Path to your Chrome User Data directory | C:\Users\you\AppData\Local\Google\Chrome\User Data |
profileDirectory |
Which profile folder to use | Default, Profile 1, Profile 2 |
headless |
Show the browser window (default: true) |
false to see the browser |
Finding your profile path:
- Open Chrome with the profile you want to use
- Navigate to
chrome://version - Look at the Profile Path — it will be something like:
C:\Users\you\AppData\Local\Google\Chrome\User Data\Profile 1- The User Data directory is the
userDataPath - The last segment (
Profile 1) is theprofileDirectory
- The User Data directory is the
Important: Chrome locks its profile directory while running. You must close Chrome completely before King Louie can launch with that profile. If Chrome is open, the browser will fail to start or start without your session data.
Typical paths by platform:
| Platform | User Data Path |
|---|---|
| Windows | C:\Users\<you>\AppData\Local\Google\Chrome\User Data |
| macOS | ~/Library/Application Support/Google/Chrome |
| Linux | ~/.config/google-chrome |
Agent System
King Louie ships with four built-in agents, each with their own system prompt template and tool allowlist:
- main-assistant — General-purpose chat and task execution
- code-explorer — Code analysis, search, and explanation
- code-writer — Code generation and implementation
- planner — Decomposes high-level goals into structured task graphs for the workflow engine
Agents run in an agentic loop with configurable max iterations and token tracking. The orchestrator supports parallel, serial, and dependency-based multi-agent execution. Agents can also spawn sub-agents dynamically using the SpawnAgent tool.
Skills
Skills are plugins that extend King Louie with custom commands, tools, and behaviors. They live in the skills/ directory and can be managed through the UI.
Each skill can:
- Register slash commands
- Provide custom tools to agents
- Maintain persistent state
- Be pinned to a chat session to handle free-form messages
- Expose configurable settings
- Declare system dependencies with platform-specific install instructions
System Dependencies
Skills can declare external CLI tools they require (e.g., gh, docker, git) in their metadata via systemDependencies. When a skill is loaded:
- The loader checks each declared dependency against the host system
- The skill still loads even if dependencies are missing (so it appears in the UI)
- Commands are blocked at execution time with a user-friendly error that includes install instructions for the current platform
- The UI can re-check dependencies on demand after the user installs a tool (
skill:checkDeps)
Example dependency declaration:
getMetadata() { return { id: 'my-skill', // ... systemDependencies: [ { command: 'docker', name: 'Docker', required: true, installUrl: 'https://docs.docker.com/get-docker/', install: { win: 'winget install --id Docker.DockerDesktop', mac: 'brew install --cask docker', linux: 'sudo apt install docker.io' } } ] }; }
Dependencies can be required: true (blocks commands when missing) or required: false (warns but allows execution).
Building a Skill
A skill is a Node.js module that extends the Skill base class from king-louie/skill-interface:
const { Skill } = require('king-louie/skill-interface'); class MySkill extends Skill { getMetadata() { return { id: 'my-skill', name: 'My Skill', version: '1.0.0', description: 'What it does', author: 'you', commands: ['my'], systemDependencies: [] // external CLI tools needed }; } async initialize(context) { /* setup */ } async handleCommand(command, args, context) { /* handle /my <args> */ } } module.exports = MySkill;
Required methods: getMetadata(), initialize(), handleCommand()
Optional methods: resolveCode(), resolveCli(), resolvePrompt(), handleMessage(), getSettingsSchema(), getHelp(), cleanup()
Installing Skills
# From a GitHub repo # Settings > Skills > Install, then paste the URL # From a local directory (symlinked) # Settings > Skills > Install, then paste the path
Skills are auto-discovered from the skills/ directory on startup. User-installed skills go to %APPDATA%/King Louie/skills/ (Windows) or ~/.config/King Louie/skills/ (Linux/macOS).
Channel Integrations
A chat bot's handle is not a secret. Anyone who finds it can message it, so King Louie treats a channel as a front door that starts locked: a channel nobody has configured refuses every sender, including you. Set up the token first, then allowlist yourself.
Telegram
- Create a bot via @BotFather
- Add the token in Settings or via
/llm telegram add <token> - The bridge starts automatically
- Message the bot. It replies once with your user id and ignores you.
- Add that id under Settings > Channels > Telegram Access — it is waiting there under "Recently refused" with an Allow User button.
Discord
- Create a Discord application and bot
- Add the bot token in Settings
- Allowlist yourself under Settings > Channels > Discord Access, the same way (Developer Mode → Copy User ID, or use the id the bot replies with)
- Mention gating (Require @mention) is a separate, narrower control: it decides when an already allowed sender's message is answered in a group.
Slack
- Create a Slack app with Socket Mode enabled
- Add the bot and app-level tokens in Settings
Slack has no allowlist and no approval routing — its inbound path is not wired up to the agent yet, so nothing a Slack user sends reaches a tool. Do not treat it as gated; treat it as not finished.
Who may message the bot
| Where | How |
|---|---|
| Desktop | Settings > Channels > channel Access — allowed users, allowed groups/channels, add and remove, plus a one-click Allow for whoever was just refused |
| Service | king-louie-service channel list telegramking-louie-service channel allow telegram 123456789king-louie-service channel allow discord <channel-id> --groupking-louie-service channel remove telegram 123456789 |
The allowlist holds user ids and group/channel ids: a message is
accepted if its sender is allowed, or if it arrives in an allowed group.
Allowing a group therefore trusts everyone in it. There is no "allow
everyone" switch in either surface, and none on disk either: a stored
default: "allow" — which every build before this one wrote for any policy
that did not say otherwise — is ignored and rewritten to deny the first
time it is read, with a warning naming the channel. The explicit ids are
kept.
An unrecognised sender who addressed the bot gets one reply telling them
their id, and is ignored after that, so the refusal is discoverable without
handing a stranger a message pump. In a group, someone who never addressed
the bot gets no reply at all — the notice names their id and the group id,
and that is not published into a room on a bystander's behalf. The owner
still learns the id: the desktop pane lists whoever was just refused, and on
a headless install the same ids are in <dataDir>/logs/service.log (the
first message from each unknown sender logs at warn).
Tool approvals from a channel
An approval prompt is never sent back to the chat that asked for the tool —
that would let a sender approve their own Bash calls. It goes only to an
owner chat you name explicitly:
| Where | How |
|---|---|
| Desktop | Settings > Channels > channel Access > Approvals |
| Service | king-louie-service channel approval telegram <your-chat-id>king-louie-service channel approval telegram --clear |
Until you set it, every approval-gated tool call from that channel is denied — no Approve button is sent anywhere. The target must not be the chat the request came from; if it is, the approval is denied rather than self-served. Only the configured approver's button press counts; a press from the requesting chat is refused.
In service mode this is moot: stage 1 has no remote approver at all and
denies everything that needs approval, whatever approvalChatId says.
Common Commands (all channels)
/help— Available agents and commands/status— Gateway and session status/clear— Clear session history/agent <name>— Switch agent/pin <skill-id>— Pin a skill to the chat/unpin— Remove pinned skill
Mesh Networking
King Louie instances can securely communicate with each other over an encrypted peer-to-peer mesh network. This lets you dispatch tasks from one machine to another — for example, sending a GPU-heavy build from your laptop to your desktop, or coordinating a swarm of instances across a cluster.
How It Works
Each King Louie instance generates a unique cryptographic identity (Ed25519 keypair + self-signed TLS certificate) on first launch. Instances establish trust through a pairing code exchange, then communicate over TLS-encrypted WebSocket connections with mutual authentication.
Setting Up Two Machines
Step 1: Open Settings > Mesh Network on both machines
Give each machine a descriptive name and capability tags:
- Desktop: Display Name =
Desktop GPU Rig, Capabilities =gpu, build-server - Laptop: Display Name =
Work Laptop, Capabilities =portable
Click Save Identity on each.
Step 2: Pair the machines
On your desktop, click Generate Code. You'll get a 6-word code like:
bamboo crystal dolphin garden silver thunder
On your laptop, click Enter Code and type in the code, the desktop's IP address (e.g., 192.168.1.50), and port (18791).
The machines exchange public keys and TLS certificate fingerprints. Once paired, they'll auto-connect whenever both are online.
Step 3: Dispatch tasks
In any chat on your laptop, ask the agent to run something remotely:
"Build the release binary on my desktop"
The agent uses the RemoteDispatch tool to send the task to the desktop's King Louie, which executes it and returns the result.
LAN Auto-Discovery
If bonjour-service is installed (npm install bonjour-service), King Louie automatically discovers other instances on the same local network via mDNS. Discovered peers appear in the Mesh Network settings panel. You still need to pair before they can communicate — discovery just makes finding each other easier.
Manual Peer Connection
For machines on different networks (VPN, Tailscale, WireGuard), add peers manually:
- Go to Settings > Mesh Network > Add Peer Manually
- Enter the remote machine's IP address and mesh port (default:
18791) - Click Connect
The machines must already be paired (via pairing code) for the connection to succeed.
Swarm Mode
For distributed workloads like model training, King Louie supports swarm coordination:
- A coordinator instance decomposes work into sub-tasks with dependencies
- Sub-tasks are proposed to capable peers based on their capability tags
- Peers accept or reject based on current load
- The coordinator dispatches tasks as dependencies resolve, collecting results
The orchestrator's existing dependency graph (executeWithDependencies) handles task ordering — tasks with metadata.targetPeer are dispatched remotely instead of locally.
Agent Usage Examples
The RemoteDispatch tool is available to agents in any chat:
User: "List my connected peers"
Agent: [calls RemoteDispatch with action: "peers"]
→ 1 peer connected: Desktop GPU Rig (kl-a1b2c3d4e5f6) — capabilities: gpu, build-server
User: "Run cargo build --release on the desktop"
Agent: [calls RemoteDispatch with action: "dispatch", peer: "Desktop GPU Rig", message: "Run cargo build --release in /projects/myapp"]
→ Task dispatched to Desktop GPU Rig, completed in 45s
User: "Train the model across all GPU machines"
Agent: [decomposes into sub-tasks, dispatches to capable peers via swarm]
→ Swarm completed: 3 sub-tasks across 2 peers
Security
All mesh communication is secured with multiple layers:
| Layer | Protection |
|---|---|
| TLS 1.3 | Encrypts all traffic — prevents eavesdropping |
| Certificate pinning | Each peer's TLS cert fingerprint is pinned at pairing time — prevents MITM |
| Ed25519 mutual auth | Challenge-response on every connection — proves identity |
| Signed envelopes | Every message is cryptographically signed — prevents tampering |
| Nonce + expiry | Messages expire after 5 minutes, nonces tracked — prevents replay |
| Trusted peers only | Connections from unknown peers rejected at TLS handshake |
A peer's Ed25519 private key and its TLS private key are encrypted at rest
under the host's cipher — Electron safeStorage in the desktop app, the
service's master key headless — and only the public halves (peer id, public
key, certificate, fingerprint) are stored in the clear. An identity created by
an older build, which wrote both private keys in plaintext, is re-encrypted in
place the first time it is loaded; the peer id and certificate fingerprint do
not change, so existing pairings survive. On a host with no secure storage at
all the keys fall back to plaintext with a warning in the log — a cipher that
is available but fails is an error, never a silent fallback.
Configuration
| Setting | Default | Description |
|---|---|---|
| Mesh Port | 18791 |
WebSocket server port (binds to 0.0.0.0) |
| LAN Discovery | Enabled | mDNS broadcast/browse for local peers |
| TLS | Enabled | Self-signed cert encryption (disable only for debugging) |
| Task Timeout | 5 minutes | Max time to wait for a remote task result |
Ports used across the project, so nothing collides on a machine running both hosts:
| Port | Used by | Binds |
|---|---|---|
18789 / 18790 |
desktop app gateway / webhooks | 127.0.0.1 |
18791 |
mesh (desktop app; off in service mode) | 0.0.0.0 |
18793 / 18794 |
service-mode gateway / webhooks (default) | 127.0.0.1 |
Network Requirements
- Same LAN: Works out of the box. mDNS handles discovery, direct connection over local IP.
- VPN / Tailscale / WireGuard: Add peers manually by VPN IP address. mDNS may not cross subnets.
- Different NATs: Requires port forwarding or a VPN. A relay server is planned for future releases.
Cron Scheduling
Schedule agent tasks to run automatically:
at— One-time execution at a specific timeevery— Recurring intervalcron— Standard cron expressions
Configurable concurrency limits and tick intervals. Manage via the cron tool or UI.
Memory System
King Louie includes an embedding-based semantic memory system:
- Capture — Save successes, failures, preferences, and context
- Recall — Vector similarity search across memory
- Tiering — Automatic hot (≤7 days), warm (8–90 days), and cold (>90 days) classification
- Session-scoped — Memory can be scoped to specific sessions
Webhooks
Register HTTP webhooks for external automation:
POST /webhooks/{webhookId}— Trigger a webhookGET /health— Health check- Signature verification via
X-Hub-Signature-256 - No browser may reach it. A request carrying an
Originheader, or anySec-Fetch-*fetch metadata, is refused with403— including ano-corsGET /health, which sends noOriginand would otherwise tell any page you visit that something is listening on that port.
The webhook server runs on the gateway port + 1 unless a port is set
explicitly — in service mode that is ports.webhook in the admin-owned
<configDir>/service.json, defaulting to 18794.
Voice / TTS
Optional text-to-speech for responses:
- System TTS — Native platform speech (Windows SAPI, macOS AVFoundation, Linux espeak)
- ElevenLabs — High-quality cloud voices with configurable voice ID, speed, and style
Supports automatic voice responses for long messages in Telegram.
Hooks
Hooks run custom logic before or after tool execution:
- security-validator — Blocks dangerous shell commands before execution
- log-tool-usage — Logs all tool executions
- memory-failure-capture — Captures failures into memory
Add custom hooks in the hooks/ directory with a hook.json manifest and index.js handler.
MCP (Model Context Protocol)
King Louie includes an MCP client that connects to any MCP-compatible server over stdio, giving agents access to databases, APIs, and specialized tools without custom code.
Configuration
Add MCP servers in your settings:
{
"mcpServers": {
"sqlite": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-sqlite", "mydb.db"]
},
"github": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": { "GITHUB_TOKEN": "ghp_..." }
}
}
}How It Works
- On startup, King Louie spawns each configured MCP server as a child process
- Connects via the MCP JSON-RPC 2.0 protocol (Content-Length framing over stdin/stdout)
- Lists the server's tools and registers them in the tool registry as
mcp__<server>__<tool> - Tools become available to agents via ToolSearch (deferred loading)
- When an agent calls an MCP tool, King Louie proxies the request to the server and returns the result
Supported Transports
Currently stdio only. SSE and WebSocket transports are planned.
Advisor Mode
An optional second-model review pass that catches bugs before they land.
Setup
In Settings, configure the advisor:
{
"advisor": {
"enabled": true,
"model": "claude-sonnet-4-20250514"
}
}How It Works
- After the agent loop completes, the advisor reviews all code changes (Edit, Write, MultiEdit diffs)
- A focused review prompt checks for bugs, security issues, and performance problems
- Returns a verdict: LGTM or ISSUES FOUND with specific feedback
- The review is appended to the chat so you can see it inline
The advisor uses the same provider as the main agent but can target a different model. Its cost is tracked separately.
Keyboard Shortcuts
| Shortcut | Action |
|---|---|
Ctrl+K |
Open command palette |
Ctrl+N |
New chat |
Ctrl+L |
Clear input |
Ctrl+, |
Open settings |
Ctrl+Shift+E |
Export chat |
Enter |
Send message |
Shift+Enter |
New line in input |
Escape |
Close palette/settings/modals |
The command palette (Ctrl+K) provides fuzzy search across all commands and actions.
Notifications
Configurable notification routing based on response duration:
- UI Toast — In-app notifications for short responses
- Ntfy — External push notifications via ntfy.sh for long-running tasks
Project Structure
main.js # Electron main process
preload.js # Secure IPC bridge (context isolation)
renderer.js # Frontend UI logic (command palette, search, streaming, diffs)
index.html # App layout
styles.css # Dark theme styles + UX enhancement CSS
src/
agents/ # Agent definitions and orchestrator
browser/ # Headless browser automation
channels/ # Telegram, Discord, Slack bridges
context/ # Context assembly, compaction, and system prompt sections
context-assembler.js # Deferred/semantic tool loading and prompt assembly
api-compaction.js # API-native context compaction (clears old tool results)
system-sections.js # System prompt sections (environment, git context, CLI tools)
cron/ # Scheduled task system
execution/ # Agent loop, tool executor, sandbox, app discovery
agent-loop.js # Core agent-tool loop with streaming and compaction
advisor.js # Optional second-model code review
worktree.js # Git worktree isolation for concurrent agents
gateway/ # WebSocket gateway and session manager
hooks/ # Pre/post tool execution hooks
ipc/ # IPC handler registration
mcp/ # Model Context Protocol client
mcp-client.js # JSON-RPC 2.0 over stdio with Content-Length framing
mcp-manager.js # Multi-server lifecycle and tool registration
media/ # Image handling and multimodal support
memory/ # Semantic memory and vector store
mesh/ # Peer-to-peer mesh networking
notifications/ # Notification routing
providers/ # LLM provider implementations (prompt caching, extended thinking)
skills/ # Skill loader, registry, and pinning
tasks/ # Task manager and background task system
tools/ # Tool registry and 20+ built-in tools
builtin/ # Bash, Read, Edit, MultiEdit, Write, Git, ToolSearch, etc.
builtin/diff-utils.js# Unified diff generation for Edit/Write results
tracking/ # Token usage and cost tracking
voice/ # TTS engines
web-search/ # Search provider integrations
webhooks/ # Webhook server and handlers
workflows/ # Durable workflow engine and planner executor
wizard/ # Onboarding wizard
skills/ # Installable skill plugins
hooks/ # Custom hook plugins
templates/ # Agent system prompt templates
tests/ # Test suite (90+ test files)
build/ # Build configuration and signing
Development
# Run tests npm test # Build for current platform npm run build # Platform-specific builds npm run build:win npm run build:mac npm run build:linux
Security
- Context isolation enabled — renderer has no direct Node.js access
- All IPC calls validated through the preload bridge
- HTML sanitized with DOMPurify
- Tool execution requires approval. Your own permission rules decide what runs
unattended; an explicit
askordenyrule beats every auto-approve list, including agent mode's and an agent definition's own - The host's own secrets (
master.key,master.key.dpapi,key-check,gateway-token, and the store/vault JSON) are out of bounds for every path-gated tool —Read,GrepandGlobrefuse them whatever the working directory and allowed directories say - Chat channels deny unknown senders, and a channel's approval prompt goes only to an owner chat you configured — never back to the requester
- Pre-execution security hooks block dangerous commands
- Git safety guards — Blocks
--amend(always creates new commits),--force,--no-verify, interactive flags,git add ./-A(must stage specific files), and sensitive file patterns (.env, .pem, credentials.json, etc.) - Worktree isolation — Background agents can run in isolated git worktrees to prevent file conflicts
- Pattern-based permission rules — First-match-wins rules with allow/ask/deny actions and denial tracking
- Webhook signature verification
- Mesh networking: TLS 1.3 encryption, Ed25519 signed messages, certificate pinning, replay protection
Breaking Changes
Changes on this branch that will alter behaviour on an existing install.
Chat channels refuse unknown senders
A Telegram or Discord channel whose allowlist is empty now denies every
sender, where it used to allow everyone by default. Any stranger who found
the bot's handle could previously drive the agent. That includes a channel
carrying a stored default: "allow" from an earlier build: it is ignored and
rewritten to deny, so a channel that looked closed in the settings pane
while being open to everyone is now closed in fact.
If you were using a channel, it stops answering until you allowlist yourself
— Settings > Channels > channel Access on the desktop, or
king-louie-service channel allow … headless. The bot replies once to an
unrecognised sender with the id to add.
Channel tool approvals need an owner target
An approval prompt used to be sent to the chat that asked for the tool, which
meant an attacker approved their own Bash calls. It now goes only to
channels.<channel>.approvalChatId, and every channel approval is denied
until that is set (and denied if it names the requesting chat). Set it in
the same two places as the allowlist.
Desktop agent mode prompts again
Agent mode hard-coded Bash, Read, Edit, Write, Glob, Grep, Git as
auto-approved, which silently overrode the user's own ask rules for exactly
the seven most dangerous tools. It no longer sets an auto-approve list at all:
what runs unattended is decided by your permission rules and the persisted
"always approve" list. Expect approval prompts in agent mode where there
were none. Add allow rules for what you want unattended.
Service features and ports moved out of <dataDir>/service.json
features and ports are now read only from <configDir>/service.json,
which is root/Administrators-owned and read-only to the service account
(/etc/king-louie, /Library/Application Support/KingLouie/config,
%ProgramData%\KingLouie\config). The data dir is writable by the service
account, so one write_file from a prompt injection could otherwise re-enable
a network listener at the next restart.
features/ports left in <dataDir>/service.json are ignored, with a
warning naming the file. profile still comes from there. If the admin file
is missing, every feature stays off.
Default service ports moved 18791/18792 → 18793/18794
The old service defaults collided with the documented mesh port 18791, which
the desktop app binds on 0.0.0.0. Update anything pointing at the old ports,
or set ports in <configDir>/service.json.
Relatedly, a listener the operator explicitly enabled that cannot bind is now fatal — the service refuses to start rather than running without it.
node.yaml rejects unknown keys
node.yaml used to ignore any key it did not know. A misspelled
always_confirm or allowed_roots therefore fell back silently to the
defaults, which can be looser than what you wrote. That applies to the copy in
<configDir> and to the one in a stdio MCP instance's config dir. A node whose node.yaml carries a stray or
misspelled key now refuses to start (mcp exits at startup, and the service
refuses to start). The error names the key and the keys allowed at that level:
Invalid /etc/king-louie/node.yaml: unknown key "policy.allowed_root" (known: allowed_roots, remote_sessions, max_concurrent_jobs)
king-louie-service doctor shows the same message on its
node config / runbooks health row. Fix it by removing or correcting the key.
Unknown features and ports keys in service.json are errors
<configDir>/service.json used to accept any feature name and ignore the
ones it did not know, so "webhook": true (for webhooks) quietly left the
listener off. An unknown key under features or ports now stops the service
from starting, with the same wording as node.yaml:
Invalid /etc/king-louie/service.json: unknown key "features.webhook" (known: gateway, webhooks, mesh, channels, appDiscovery, desktopBridge)
The service-writable <dataDir>/service.json is unchanged: its features and
ports are still ignored with a warning.
<dataDir>/gateway-token exists only while the gateway is up
The cleartext bearer-token file is written after the listener binds and removed when it stops. Tooling that reads it at an arbitrary time, or that assumed it persists across a stopped service, needs to handle its absence.
Mesh identities are re-encrypted on first load
Ed25519 and TLS private keys were written in plaintext despite the README saying otherwise. They are now encrypted at rest and an existing identity is upgraded in place on first load. The peer id and TLS fingerprint are unchanged, so pairings survive — but the on-disk record is no longer readable by an older build.
License
MIT — see LICENSE.txt

