Lake Terminal
A cross-platform execution control plane for humans and coding agents.
Persistent terminals, process attribution, durable command history, normalized events, and one observable state model for CLI tools, IDE agents, automation, and remote execution hosts.
macOS · Linux · Windows · VS Code-compatible editors · Claude Code · Codex · Cursor · Devin Local Agent
Why Lake Terminal exists
Software development is no longer driven by one person typing into one terminal. A modern workspace can have a developer, an IDE-hosted coding agent, a CLI agent, background automation, remote SSH hosts, and several shells all changing the same project at once.
The hard problem is no longer merely starting a terminal. It is knowing:
- which execution belongs to which agent or human;
- which commands are still running;
- whether a terminal is actually reconnectable;
- what happened before an agent disappeared or a window closed;
- how to inspect several execution surfaces without maintaining separate state;
- how an editor integration can observe terminals without scraping UI state;
- and how to retain useful history without casually persisting secrets.
Lake Terminal turns those executions into one local control plane. It uses a canonical session model, SQLite-backed state, normalized lifecycle events, process-tree inspection, provider hooks, and platform-specific terminal backends. CLI, TUI, web, daemon, and editor integrations all consume that same state instead of inventing their own registries.
Human terminal ───────┐
Claude / Codex ───────┤
Cursor / Devin ───────┤ ┌───────────────┐
IDE processes ────────┼───────▶│ Lake Terminal control │
Remote hosts ─────────┘ │ plane │
└───────┬───────┘
│
┌──────────────────────┼──────────────────────┐
▼ ▼ ▼
persistent PTY SQLite state event stream
tmux / ConPTY sessions/commands/... daemon / TUI / web
What you get
| Capability | What Lake Terminal provides |
|---|---|
| Persistent execution | tmux-backed sessions on macOS/Linux and ConPTY-backed sessions on Windows when the platform supports them. |
| Honest fallback | Direct IDE/extension-host processes remain observable in process-only mode instead of being falsely labelled reconnectable. |
| Agent attribution | Native hooks for Claude Code, Codex, Cursor and Devin Local Agent, plus configurable process/environment signatures. |
| One state model | SQLite is the only persistent session/control-plane database. There is no JSON session registry or state mirror. |
| Command lifecycle | Durable command start/finish/exit information with process-tree observation as a fallback. |
| Observability | CLI status, interactive TUI, read-only views, event subscriptions, history and a live web dashboard. |
| Editor integration | A machine-readable local API and daemon event stream suitable for VS Code-compatible extensions. |
| Remote hosts | Named SSH execution hosts and aggregated status without recursively confusing local and remote state. |
| Privacy controls | Redaction before persistence, configurable retention, bounded transient output buffers and explicit purge. |
| Recovery | Transactional schema upgrades, backend reconciliation, diagnostics and SQLite integrity handling. |
Quick start
From this checkout:
python lake_terminal/install.py
The installer supports an interactive component wizard as well as non-interactive install, plan, status, and uninstall operations. A normal installed command is lake.
# Create and attach to a managed persistent shell.
lake
# Create a managed shell, run a command, then keep the shell alive.
lake pytest -q
# See everything Lake Terminal currently knows about.
lake --status
# Interactive multi-session monitoring.
lake --watch
# Browser dashboard backed by the same control-plane state.
lake --web
# Deep diagnostics.
lake --doctor --full
Designed for the agentic development loop
A coding agent should not need a special, invisible terminal universe. Lake Terminal makes agent execution inspectable through the same primitives used for human execution.
Provider hooks record the strongest attribution available: provider, provider session, workspace and lifecycle state. When a native provider hook is unavailable, Lake Terminal can progressively fall back to editor/environment/process evidence. The resulting attribution is stored with the session rather than inferred independently by every UI.
This matters when several agents work concurrently. An editor can ask for machine-readable local status:
lake --status --json --local-only
Automation can consume normalized events:
lake --subscribe
An agent can idempotently obtain/reuse its managed session:
lake --agent
# or
lake --ensure
The result is a useful separation of concerns: agents execute work; Lake Terminal owns execution identity, lifecycle and observability.
Platform model
Lake Terminal detects the execution host rather than assuming the desktop operating system is the execution environment.
macOS and Linux
When tmux is available, Lake Terminal uses it for persistent, reconnectable terminals and read-only capture. Process-tree inspection augments terminal state with workload information.
Windows
When the Windows pseudoconsole API is available, Lake Terminal uses ConPTY for persistent PowerShell/session execution. The ConPTY layer supports creation, input, output, resize, liveness and termination.
Process-only environments
If no persistent PTY backend is available—or an IDE runs a process directly—Lake Terminal still records and observes the execution, but marks it as non-reconnectable. This is intentional: observability does not imply terminal persistence.
One database, one truth
Canonical state lives in:
~/.lake_terminal/lake.db
The normalized SQLite model stores sessions, commands, events, observed processes, remote hosts, web-server registration and agent context. Updates are transactional. Stable session/host/process identities protect the model from PID reuse and short-lived process ambiguity.
JSON is used only where it is appropriate—for user configuration or explicit JSON transport/output—not as a parallel state database.
Everyday commands
lake # create/attach
lake <id> # attach to a reconnectable session
lake <id> --exec <cmd...> # execute in an existing session
lake <id> --send <text...> # type text into a session and press Enter
lake <id> --send-key <key> # press one key (Enter, C-c, Escape, Up, ...)
lake <id> --kill # terminate/remove
lake --list # list local sessions
lake --status [--json] # local + configured remote status
lake --watch [--once] # TUI dashboard
lake --view <id> # read-only session view
lake --history # durable command history
lake --events # normalized events
lake --web # live web dashboard
lake --hosts # configured remote hosts
lake --doctor --full # diagnostics
lake --purge # apply retention policy immediately
lake --mcp # resident MCP stdio server for agent clients
lake --help is intentionally a complete operational manual, including agent-routing, daemon, remote-host and internal integration endpoints.
MCP server
lake --mcp starts a resident MCP (Model Context Protocol) stdio server. The server keeps LakeTerminal in-process for the client's lifetime, so every call avoids interpreter and service startup cost; requests execute directly against the live session backends.
lake --mcp
Exposed tools:
| Tool | Purpose |
|---|---|
lake_exec |
Run a shell command in a session; returns captured output and exit status. |
lake_send |
Type text and/or press keys (C-c, Enter, Escape, Up, Tab, ...) to answer prompts, drive REPLs or interrupt processes. Enter defaults to true when text is given, false for key-only input. |
lake_output |
Read the recent pane output of a session. |
lake_sessions |
List live sessions. |
lake_new |
Create a detached session; optional command and cwd. |
lake_kill |
Destroy a session and its backend. |
lake_events |
Recent normalized activity events; optional limit and filter. |
lake_commands |
Recorded command history; optional limit and session_id. |
Tools that target a session accept an optional session_id; it defaults only when exactly one live session exists. Every tool returns human-readable content plus machine-readable structuredContent matching its declared outputSchema (protocol 2025-06-18). Sessions created through lake_new are attributed as agent-initiated.
The installer registers the server for you (python lake_terminal/install.py install --with mcp): it edits Claude Desktop's claude_desktop_config.json (only when Claude Desktop is installed) and uses claude mcp add-json --scope user for Claude Code, falling back to ~/.claude.json. lake --doctor reports whether it is registered. lake --mcp --read-only (or LAKE_MCP_READ_ONLY=1) exposes only the inspection tools (lake_sessions, lake_output, lake_events, lake_commands).
To register the server manually with an MCP client such as Claude Desktop, add it to the client's server configuration:
{
"mcpServers": {
"lake-terminal": {
"command": "lake",
"args": ["--mcp"]
}
}
}
Agent identity: one variable, every provider
Lake resolves who is running a shell from the provider's own environment, so no
hook is needed for attribution. LAKE_AGENT / LAKE_AGENT_SESSION win when set;
otherwise the first native session variable present is used:
| Provider | Session variable(s), strongest first | Surface |
|---|---|---|
| Claude Code | CLAUDE_CODE_SESSION_ID, CLAUDE_SESSION_ID |
CLAUDE_CODE_ENTRYPOINT: cli, claude-vscode → vscode, claude-desktop → desktop, sdk-* → sdk |
| Codex | CODEX_SESSION_ID, CODEX_THREAD_ID |
|
| Cursor | CURSOR_SESSION_ID |
|
| Devin | DEVIN_SESSION_ID |
Claude Code and Devin run shell commands in their own non-interactive
processes, so their native hooks also record each Bash/exec tool call into a
non-reconnectable hook session (state, command, exit status) and lake --doctor shows the identity resolved for the current process. VS Code forks
(Devin, Cursor, Windsurf, VSCodium) are told apart from the IPC hook path.
Control-plane architecture
┌─────────────────────────────┐
│ provider hooks │
│ Claude · Codex · Cursor · │
│ Devin · configurable agents │
└──────────────┬──────────────┘
│ attribution
▼
┌─────────────┐ ┌──────────────────────┐ ┌─────────────┐
│ tmux / PTY │◀────────▶│ LakeTerminal │◀────────▶│ process tree│
│ ConPTY │ │ backend-neutral core │ │ inspection │
└─────────────┘ └──────────┬───────────┘ └─────────────┘
│
┌─────────▼──────────┐
│ LakeDatabase │
│ SQLite-only state │
└─────────┬──────────┘
│
┌────────────────────┼─────────────────────┐
▼ ▼ ▼
daemon / IPC CLI / TUI web dashboard
event stream history/status WebSocket actions
The core deliberately keeps presentation surfaces downstream of the canonical model. The web dashboard, TUI and editor bridge do not maintain independent session registries.
Privacy and retention
Lake Terminal redacts common credential forms before persistence, including token/password assignments, authorization headers and common secret-token formats. Additional patterns can be configured in:
~/.lake_terminal/privacy.json
Default policy values are 30 days of command history, 90 days of events, no persisted terminal output, and a 256 KiB output limit when persistence is enabled. lake --purge applies the active retention policy immediately.
Remote and editor workflows
Remote machines are modeled as execution hosts. Add them with:
lake --host-add build-box user@example-host lake
lake --hosts
lake --status
--status may aggregate configured hosts over SSH. --local-only disables aggregation and is the correct mode for editor integrations and remote recursion prevention.
For VS Code-compatible integrations, the intended machine interface is the SQLite-backed CLI plus daemon events—not direct database mutation and not a JSON registry.
Installation model
The component installer can install only what a machine needs: launcher, tmux, shell lifecycle hooks, agent routing, provider hooks, web startup and the native lake-terminal:// URI handler.
python lake_terminal/install.py plan --preset default
python lake_terminal/install.py install --preset default --yes
python lake_terminal/install.py status
python lake_terminal/install.py uninstall
Two deployment modes are supported:
- live — the launcher points at the checkout; useful while developing Lake Terminal;
- copy — the package is copied into the configured library directory.
The install manifest is stored at ~/.lake_terminal/install.json so uninstall removes Lake Terminal-owned artifacts rather than indiscriminately deleting user configuration.
Python API
Lake Terminal is also usable as a Python library. The main programmatic entry points are LakeTerminal, SessionRepository, LakeDatabase, CommandLifecycle, RuntimePlatform, ProcessInspector, AgentConfiguration, WebDashboard, EventServer, and the provider hook adapters.
from pathlib import Path
from lake_terminal.core import SessionRepository, LakeTerminal
from lake_terminal.storage import LakeDatabase
home = Path.home()
database = LakeDatabase(home=home)
repository = SessionRepository(home=home)
service = LakeTerminal(repository=repository)
entry = service.create(initial_command=("python", "-m", "pytest"))
print(entry.id)
print(database.schema_version())
For the complete API, data model, CLI semantics, configuration files, daemon protocol, platform backends and every public class/function, see DOCS.md.
Project principles
Lake Terminal is intentionally built around a few constraints:
- One state store. SQLite is canonical; there is no JSON state path.
- No fake persistence. A process is reconnectable only when its backend actually supports it.
- Provider-neutral core. Provider-specific behavior stays in hook adapters.
- Execution-host truth. Capabilities belong to the machine/container/remote host running the command.
- One event model. CLI, TUI, web and editor surfaces consume normalized lifecycle state.
- Privacy before persistence. Redaction and retention are control-plane concerns, not UI afterthoughts.
- Headless first. Status, execution, subscriptions and lifecycle endpoints are automation-friendly.
Development
The repository includes Python tests for the control plane and Node tests for the web assets. The project is designed to run across macOS, Linux and Windows, with backend-specific tests isolated behind capability/fake layers.
python -m pytest tests/tools/lake_terminal
node --test tools/lake_terminal/lake_terminal/web_assets/app.test.mjs
The test suite is hermetic: tests/conftest.py strips CLAUDE*, CODEX_*,
CURSOR_*, DEVIN_*, LAKE_* and VSCODE_* variables, so it gives the same
result when run from inside Claude Code, Devin or Cursor.
Documentation
- DOCS.md — complete architecture, capabilities, configuration and Python API reference.
lake --help— complete command-line operational manual.
Lake Terminal makes concurrent agent execution observable, attributable and controllable without forcing every agent, editor and terminal to invent its own state.
Directory contents
Files
LICENSE— Provide LICENSE project content.PLATFORMS.md— Lake Terminal is OS- and CPU-architecture independent at the control-plane layer. Backend capabilities are discovered at runtime.SECURITY.md— Lake Terminal is a local execution control plane. Session metadata, command history, process information, IPC authentication material, and optional terminal output are private user data.__init__.py— Runnable wrapper exposing the Lake Terminal control-plane entry points.__main__.py— Execute the Lake Terminal control plane as a module or directory.install.py— Install, inspect, and remove the Lake Terminal control-plane integration.lake_entry.py— Entry script frozen bytools.release_builderinto native installers.pyproject.toml— Provide pyproject toml content.pyz_main.py— Entry point of the self-containedlake-terminalarchive.release-builder.toml— Provide release builder toml content.
Subdirectories
integration/— This harness validates the part of Lake Terminal most likely to fail in real agentic development: execution attribution under concurrent providers and surfaces.lake_terminal/— Cross-platform coding-agent execution observability and control plane.
Integration qualification
Lake includes a first-class qualification harness for the ambiguity that matters most in multi-agent development: provider/session attribution under concurrent execution.
lake test integration synthetic all
lake test integration concurrent
lake test integration real codex
lake test integration real claude
lake test integration real cursor
lake test integration real devin
lake explain SESSION_ID
Synthetic qualification is unattended and suitable for CI/Docker. Real mode prints a unique probe command for the selected agent and waits for the provider metadata propagated by Lake's actual hook/environment integration. It therefore verifies observed identity rather than trusting which provider the operator said they were testing.
See integration/README.md for the concurrency matrix, Docker workflow, and
Docker Sandboxes workflow.
Arrow URI control
Lake Terminal uses the independent tools/arrow framework as its URI trust
boundary. LakeArrow creates signed, expiring, one-use lake-terminal://
control URIs and dispatches validated operations into the existing authenticated
IPC daemon. The installer registers the URI scheme with Launch Services on
macOS, XDG on Linux, and the per-user URL Protocol registry on Windows.
Metadata
Release files for lake-terminal 1.0.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| lake_terminal-1.0.1.tar.gz | 168.7 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| lake_terminal-1.0.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 358.0 kB
Release files / lake_terminal-1.0.1.tar.gz
| Download URL | lake_terminal-1.0.1.tar.gz |
|---|---|
| Size | 168.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
9d9fb3e95ad2ecbeb6a899e577b2d452bb3f9e8ff9760f3e071b31e39c652bfc
|
|
BLAKE2b-256 checksum How to use checksums |
81df01ee72bdb2cc9b73061c8cfc5ef9763ec0baeaef1aef3c06143593bed8d2
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.9
|
Release files / lake_terminal-1.0.1-py3-none-any.whl
| Download URL | lake_terminal-1.0.1-py3-none-any.whl |
|---|---|
| Size | 189.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
327aa96a86d04a5dbb38e357fd474ab66d52c80178de2008cda19c5cfb817a17
|
|
BLAKE2b-256 checksum How to use checksums |
96fab5a97fb8d360c6eccad64067871d08b01b2a4dd352dd3cb0e5394d77bf7b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.9
|
Security for lake-terminal 1.0.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.