Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ Do not push, publish releases, or modify remote issues/PRs unless the maintainer
- See `docs/extensions/README.md` for the extension architecture and `docs/extensions/adding-a-tool.md` for the step-by-step guide; every tool or feature extension must include a `README.md`.
- `extensions/tools/<tool>/spec.yaml` (`kind: sandbox`) defines tool runtime, auth, network, settings, and provider behavior.
- `extensions/features/<feature>/spec.yaml` (`kind: mixin`) defines optional packages, runtime setup, auth, network, and published-port behavior; a feature may also ship a `skills/` directory that is composed into the tool's skills when the feature is enabled.
- Memory scope, disable arguments, and preserved runtime state are declared with `sandbox.memoryScope`, `sandbox.noMemoryArgs`, and `sandbox.statePaths`; see [Agent Memory](docs/runtime/stores.md#agent-memory) for lifecycle and cleanup behavior.
- Tool settings templates live under `extensions/tools/<tool>/templates/` and are baked into the image.
- Full host config overrides use `~/.config/enclave/tools/<tool>/` globally and `~/.config/enclave/projects/<hash>/<tool>/config/` per project.
- JSON/TOML patches mirror native paths under `~/.config/enclave/patches/<tool>/` globally and `~/.config/enclave/projects/<hash>/patches/<tool>/` per project.
Expand All @@ -87,7 +88,7 @@ Enclave follows platform-standard roots: the XDG base directories on Linux and o
- State: `~/.local/state/enclave/` (`$XDG_STATE_HOME`)
- Cache: `~/.cache/enclave/` (`$XDG_CACHE_HOME`)
- Embedded runtime assets: `~/.cache/enclave/assets/<content-hash>/`
- Per-project agent memory: `~/.local/state/enclave/projects/<hash>/<tool>/memory/` (Claude only; agent-writable, never shared between projects or agents)
- Per-project agent memory: `~/.local/state/enclave/projects/<hash>/<tool>/memory/` (agent-writable; Claude uses this root, Codex uses `<key>/` matching its config-store key; never shared between projects or agents)
- User-defined subcommands: `~/.config/enclave/commands/{host,session}/` (executable files become `enclave <name>` verbs)

Per-project config/state is keyed by project hash and kept outside the worktree. See `docs/configuration.md` and `docs/runtime/stores.md` for details.
4 changes: 2 additions & 2 deletions docs/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ The restricted network request flow has a separate

## Key Concepts

- **Profiles** (`extensions/tools/<tool>/spec.yaml`, `kind: sandbox`): define tool command, session continuation args (`continueArgs`, `resumeArgs`), config location, optional settings/skills metadata (`settingsFile`, `settingsTarget`, `skillsDir`), optional host passthrough allow-list (`passthroughPaths`), optional QEMU bundle minimum memory (`qemuMinMemoryMiB`) and config-store cache hint (`qemuStoreCacheMmap`), declared credential sources (`credentials.sources`) including API-key metadata, YOLO flag, and per-provider auth configuration (`providers`: credentials, auth files, auth session checks, OAuth ports).
- **Profiles** (`extensions/tools/<tool>/spec.yaml`, `kind: sandbox`): define tool command, session continuation args (`continueArgs`, `resumeArgs`), config location, optional settings/skills metadata (`settingsFile`, `settingsTarget`, `skillsDir`), optional agent-memory policy (`memoryDir`, `memoryScope`, `noMemoryArgs`) and pinned runtime state (`statePaths`), optional host passthrough allow-list (`passthroughPaths`), optional QEMU bundle minimum memory (`qemuMinMemoryMiB`) and config-store cache hint (`qemuStoreCacheMmap`), declared credential sources (`credentials.sources`) including API-key metadata, YOLO flag, and per-provider auth configuration (`providers`: credentials, auth files, auth session checks, OAuth ports).
- **Runtime assets** (`runtime-assets/gateway-allowlists/`, `runtime-assets/build-scripts/`, `runtime-assets/auth-reconcile.sh`, `runtime-assets/net.sh`): DNS allowlists, Docker weaving scripts, and shared entrypoint helpers baked into the image. Tool templates live in `extensions/tools/<tool>/templates/` and are aggregated during build.
- **Asset discovery**: `ENCLAVE_HOME` has explicit precedence, followed by a valid app root above the resolved executable path. This keeps package-managed installs on `/usr/share/enclave` and in-tree builds on live checkout files. Other binaries extract their embedded assets into an append-only `assets/<content-hash>/` directory under the platform cache root. The former unversioned data-root lookup is not used.
- **Image selection**: images are per-tool. The default tag is `enclave-<tool>:latest` for the selected `--tool` (default `claude`); `--slim` uses `enclave-<tool>:slim`. When running from a git checkout on a non-default branch, the tag is prefixed with the branch name and hash (for example, `enclave-codex:branch-<name>-<hash>-latest`) to avoid overwriting main images. `--base-image` or devcontainer mode derives a separate tag (e.g., `enclave-codex:base-<hash>-latest`) unless `--image-name` is set.
Expand Down Expand Up @@ -220,7 +220,7 @@ Other host-side data:
- `yarn/` - Yarn cache
- `bun/` - Bun cache
- **History**: `~/.local/state/enclave/projects/<hash>/<tool>/history/` for shell history.
- **Agent memory**: `~/.local/state/enclave/projects/<hash>/<tool>/memory/` for per-project, agent-writable memory (Claude only). Bind-mounted into the harness's native memory path (writable), never shared between projects or agents, disabled with `--no-memory`, and skipped for ephemeral (`--ephemeral`) sessions.
- **Agent memory**: `~/.local/state/enclave/projects/<hash>/<tool>/memory/` for per-project, agent-writable memory, bind-mounted into the harness's native memory path (`sandbox.memoryDir`). Never shared between projects or agents, disabled with `--no-memory`, and skipped for ephemeral (`--ephemeral`) sessions. `sandbox.memoryScope: session` keys it by config-store key (`memory/<key>/`), and `sandbox.statePaths` protects tool runtime state during config overlays. See [Agent Memory](runtime/stores.md#agent-memory) for isolation and cleanup policy.
- **Home config files**: `~/.local/state/enclave/projects/<hash>/<tool>/home-config/` for host-home files created in the container:
- `npmrc` → `~/.npmrc`
- `yarnrc` → `~/.yarnrc`
Expand Down
10 changes: 8 additions & 2 deletions docs/cli-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -189,9 +189,15 @@ Mutation commands (`add-domain`, `remove-domain`, `set-mode`) apply the new poli
| `enclave cleanup --all` | All projects and tools |
| `enclave cleanup --ephemeral` | Remove stopped containers and ephemeral session stores |
| `enclave cleanup --dry-run` | Preview what would be removed |
| `enclave cleanup --keep cache,history,auth,memory` | Preserve the listed stores (comma-separated or repeated `--keep`): `cache` (package caches), `history` (shell history), `auth` (auth stores, with `--all`), `memory` (per-project agent memory, no selective effect with `--all`) |
| `enclave cleanup --keep cache,history,auth,memory` | Preserve the listed stores (comma-separated or repeated `--keep`): `cache` (package caches), `history` (shell history and the config store, including conversation history), `auth` (auth stores, with `--all`), `memory` (per-project agent memory, no selective effect with `--all`) |
| `enclave cleanup --build-cache` | Prune Docker build cache (requires confirmation) |

For a tool with session-scoped memory (Codex), `history` and `memory` name one
unit: `--keep memory` also preserves the config store and `--keep history` also
preserves memory. With `--ephemeral`, `--keep memory` preserves each session
store that holds memory; the other `--keep` kinds do not apply there. See
[Agent Memory](runtime/stores.md#agent-memory).

---

## Flags
Expand Down Expand Up @@ -266,7 +272,7 @@ Mutation commands (`add-domain`, `remove-domain`, `set-mode`) apply the new poli
|------|-------------|
| `--no-cache` | Disable package caches |
| `--no-history` | Disable shell history |
| `--no-memory` | Disable per-project agent memory |
| `--no-memory` | Disable per-project agent memory; see [memory controls](runtime/stores.md#agent-memory) |

---

Expand Down
2 changes: 1 addition & 1 deletion docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ supported under `tool_overrides.<tool>`.
| `allow_domains` | Extra domains added to the gateway allowlist (bare DNS names; ignored when `allow_all_network=true`) |
| `no_cache` | Disable package caches |
| `no_history` | Disable shell history |
| `no_memory` | Disable per-project agent memory |
| `no_memory` | Disable per-project agent memory; see [memory controls](runtime/stores.md#agent-memory) |
| `session_monitor` | Run agents under the managed tmux session (enables `status` snapshots) |
| `session_tint` | Terminal background color marking a session-owned terminal, as `#rrggbb` (unset: no tint) |
| `base_image` | Docker base image override |
Expand Down
29 changes: 23 additions & 6 deletions docs/extensions/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -304,12 +304,29 @@ providers:
- { file: .credentials.json, type: file_exists }
```

`sandbox.*` fields (`configDir`, `skillsDir`, `memoryDir`,
`settingsFile`, `settingsTarget`, `yoloFlag`, `yoloEnabled`, `continueArgs`, `resumeArgs`,
`passthroughPaths`, `qemuMinMemoryMiB`, `qemuStoreCacheMmap`,
`hostConfigDir`, `hostCredentialsFile`, and `hostOauthJson`) are enclave-native
tool metadata. `sandbox.entrypoint.run` is the shared sbx-style command to
launch the tool.
`sandbox.*` fields (`configDir`, `skillsDir`, `memoryDir`, `memoryScope`,
`noMemoryArgs`, `statePaths`, `settingsFile`, `settingsTarget`, `yoloFlag`,
`yoloEnabled`, `continueArgs`, `resumeArgs`, `passthroughPaths`,
`qemuMinMemoryMiB`, `qemuStoreCacheMmap`, `hostConfigDir`, `hostCredentialsFile`,
and `hostOauthJson`) are enclave-native tool metadata. `sandbox.entrypoint.run`
is the shared sbx-style command to launch the tool.

Memory and runtime state policy is declared in the spec, including for installed
third-party tools:

- `memoryDir`: home-relative native memory directory.
- `memoryScope`: `project` (default) shares memory within a project/tool;
`session` requires `memoryDir` and `configDir` and pairs memory with each config
store for writer isolation and cleanup.
- `noMemoryArgs`: argv inserted before user arguments for `--no-memory` and
`--ephemeral`. Use native controls to disable memory use and generation.
Entries are trimmed and blank ones dropped, as for `continueArgs`/`resumeArgs`.
- `statePaths`: config-relative runtime-state paths to preserve during overlays
and exclude from host config passthrough. Requires `configDir`. A trailing `/`
matches a directory tree; globs match paths or basenames. Absolute paths,
traversal, and selection directives are rejected.

See [Agent Memory](../runtime/stores.md#agent-memory) for lifecycle semantics.

When using `templates/`, set `sandbox.configDir`, `sandbox.settingsFile`
(aggregated name like `<tool>-settings.json`: the `<tool>-` prefix followed by
Expand Down
6 changes: 6 additions & 0 deletions docs/extensions/adding-a-tool.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,12 @@ the complete set and semantics):
- `skillsDir`: (optional) path below `configDir` where shared and tool-specific
managed skills are composed. It may be home-relative or absolute, matching
`configDir`.
- `memoryDir` / `memoryScope` / `noMemoryArgs`: (optional) native memory path,
project or session scope, and native disable arguments. See
[Agent Memory](../runtime/stores.md#agent-memory) before enabling background
memory generation.
- `statePaths`: (optional) config-relative runtime state preserved across config
overlays and excluded from host config passthrough (including database sidecars).
- `settingsFile` / `settingsTarget`: aggregated template filename under
`/usr/local/share/enclave/templates/` and its target below `configDir`.
- `yoloFlag` / `yoloEnabled`: flag to skip approvals, and whether yolo mode is on
Expand Down
11 changes: 7 additions & 4 deletions docs/extensions/installing.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,10 +91,13 @@ runs in a container on each automatic update check, widens the network allowlist
or denies domains, publishes a container port on the host, declares credentials
and where they're released as HTTP headers, whether it flips on the
approval-bypass flag (`sandbox.yoloFlag`, active by default once declared) or
appends its own argv to the agent for `--continue`/`--resume`
(`sandbox.continueArgs`/`resumeArgs`, which can carry the same flag), launches
an IDE on your host after start (`postStart.openIDE`), ships skills, host
config/credential passthrough, or seeds files into your project directory. An
appends its own argv to the agent for `--continue`/`--resume`/`--no-memory`
(`sandbox.continueArgs`/`resumeArgs`/`noMemoryArgs`, which can carry the same
flag), keeps a writable agent-memory directory between sessions and at what
scope (`sandbox.memoryDir`/`memoryScope`), pins store paths the config overlay
may not replace (`sandbox.statePaths`), launches an IDE on your host after start
(`postStart.openIDE`), ships skills, host config/credential passthrough, or
seeds files into your project directory. An
update shows the same information as a diff against what's currently installed,
so a newly granted capability is visible before you accept it.

Expand Down
6 changes: 3 additions & 3 deletions docs/persistence.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ Per-project data is stored on the host and reused across sessions:
|------|----------|
| Package caches | `~/.cache/enclave/<tool>/<project-hash>/` |
| Shell history | `~/.local/state/enclave/projects/<project-hash>/<tool>/history/` |
| Agent memory | `~/.local/state/enclave/projects/<project-hash>/<tool>/memory/` (Claude) |
| Agent memory | `~/.local/state/enclave/projects/<project-hash>/<tool>/memory/` (Claude); `memory/<key>/` (Codex, matching the config-store key) |
| Config/env/auth stores | Host directories under `~/.local/state/enclave/` (bind-mounted; no Docker volumes) |
| Embedded runtime assets | `~/.cache/enclave/assets/<content-hash>/` |

Expand All @@ -58,8 +58,8 @@ the standard Apple locations, in a reverse-DNS application directory: config
and state under `~/Library/Application Support/org.eclipse.enclave/`
(`config/`, `state/`) and caches under `~/Library/Caches/org.eclipse.enclave/`.

Agent memory is skipped for `--ephemeral` sessions: memory written during them
is discarded with the session's config store.
See [Agent Memory](runtime/stores.md#agent-memory) for memory isolation,
`--no-memory`, ephemeral runs, and cleanup retention rules.

Disable specific persistence:

Expand Down
71 changes: 70 additions & 1 deletion docs/runtime/stores.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,83 @@ the `config-store/default` directory. Additional concurrent sessions that would
otherwise clobber the same writable config instead get a suffixed store keyed by
session or worktree identity, while shared auth remains in the tool-global auth
store. Tool state such as conversation history, settings, and cached tokens
persists between runs.
persists between runs. A tool's `sandbox.statePaths` declares config-relative
runtime state that must survive config-source overlays and stay out of host
config passthrough. Codex pins the database indexing its memories there, along
with its `state_*.sqlite*` and `thread_history_*.sqlite*` thread databases. The
thread databases are rebuildable projections of the preserved `sessions/`
rollouts, pinned to avoid re-deriving them on every overlaid run.

**Ephemeral mode** (`--ephemeral`): A fresh store directory is created with a
unique suffix key for each session and removed after the container exits. The
`default` store, if one exists, is left untouched.

Source: [`internal/runtime/volume_manager.go`](../../internal/runtime/volume_manager.go) `BuildPrep` (intent), [`internal/backend/docker/prepare.go`](../../internal/backend/docker/prepare.go) `prepareConfigStore` (mechanics)

## Agent Memory

Tools declare their native memory directory with `sandbox.memoryDir`. Host memory
lives under `~/.local/state/enclave/projects/<hash>/<tool>/memory/`:

| Scope | Host layout | Bundled tool |
|-------|-------------|--------------|
| `project` (default) | `memory/` | Claude (`~/.claude/memory`) |
| `session` | `memory/<config-store-key>/` | Codex (`~/.codex/memories`) |

Session scope keeps each memory repository with the config-store database that
coordinates its writers. Reusing a named session reuses its memory; changing the
name starts with a separate store. Worktrees and concurrent-session suffixes
also have separate stores. This deliberately favors writer isolation over
sharing learned context. An ad-hoc concurrent store may never be reused and
remains until cleanup.

Because the key is the config-store key, memory follows the config store
wherever it goes. In a linked worktree that means the key is `default` until the
project has a config override or patch, and the worktree's own key afterwards:
adding the first override moves the worktree's memory (and its conversation
history) to a fresh store, so the agent starts over. Nothing is deleted. The
previous store stays until cleanup.

Enabling `memoryScope` on a tool that already ran without one does not migrate
anything. Memory the tool previously wrote inside its config store stays there,
shadowed by the new mount at the same path; remove it by hand, or run
`enclave cleanup` for the project, if the split state is a problem.

`--no-memory` omits the memory mount and applies the tool's `sandbox.noMemoryArgs`
at launch. Codex declares `-c features.memories=false` and Claude
`--settings '{"autoMemoryEnabled": false}'`, disabling memory use and generation
without changing saved settings or deleting existing memories. `--ephemeral`
applies the same launch arguments and discards the config store. Tools without
`noMemoryArgs`, or tools started manually from a shell, retain their native
behavior: without the mount their writes land in the config store, which
persists unless the session is ephemeral.

For session-scoped memory, project cleanup treats memory and the entire config
store as a unit: `--keep memory` also retains the config store (including
conversation history), and `--keep history` also retains memory. Default cleanup
removes both. With `cleanup --all`, the existing whole-project-tree behavior
applies: `--keep memory` has no selective effect.

`cleanup --ephemeral` removes memory alongside each selected non-default config
store, which includes the store of a named session even though its container is
left alone. The same pairing applies: `--keep memory` retains each session store
that holds memory, together with that memory. Stores with no memory to keep are
removed either way: every store of a project-scoped tool, and the throwaway
stores of `--ephemeral` runs, which never get a memory mount. The other
`--keep` kinds have no effect on an ephemeral cleanup.

Selective cleanup reads the scope from the tool spec. A tool with no spec,
meaning state left behind by an extension that has since been removed, cleans up
under the default scope rather than failing. A spec that exists but does not load
aborts a project cleanup before anything is deleted, but only when `--keep memory`
or `--keep history` couples memory to the config store. The ephemeral sweep
handles such a tool by itself instead of aborting for every other tool: with
`--keep memory` the tool's stores are skipped, since the scope decides what would
be kept; otherwise they are removed with a warning and its session memory stays
behind until the spec loads again. Every other plan warns and proceeds under the
default scope, which is the plan a working spec would have produced anyway, so a
broken extension never blocks the removal of its own state.

## Managed Skills

Tools opt into managed skills with `sandbox.skillsDir` in
Expand Down
7 changes: 4 additions & 3 deletions extensions/tools/claude/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,10 @@ Claude Code's auto memory is enabled by default (`autoMemoryEnabled`), pinned to
`~/.claude/memory` inside the container. That directory is bind-mounted from the
per-project host location `~/.local/state/enclave/projects/<hash>/claude/memory/`, so
memory is scoped per project and stored outside the working directory (never
committed). Pass `--no-memory` to disable the mount. Ephemeral
(`--ephemeral`) sessions skip the host mount, so memory written during them
is discarded with the session's config store.
committed). Pass `--no-memory` to skip the mount and disable memory generation
(`--settings '{"autoMemoryEnabled": false}'` is added to the launch arguments).
Ephemeral (`--ephemeral`) sessions do the same, so no memory is written or
carried over.

## Network Access

Expand Down
Loading
Loading