The worktree symlink test compared canonicalized output against a raw
tempdir path; on macOS `/var` is a symlink to `/private/var`, so the
expectation never matched. The scratch-launch spec re-listed sessions
after its poll, which can observe the transient empty list the daemon's
2s reconcile tick produces.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Web Push: the VAPID keypair, subscription store, `/api/push/*`, the
service-worker push handlers, the presence heartbeat that only existed to
suppress pushes, the Notifications settings tab, and the `[web]` config
section. Drops p256, hkdf, aes-gcm, jsonwebtoken and getrandom.
Session triage: per-session color, web pin, and archive are gone from the
Instance, the REST API, the CLI (`session color` / `archive` / `unarchive`),
the TUI (the `z` and `U` keybinds, the Archived sidebar section, group
archive-all) and the web sidebar (row badges, context menu, bulk actions,
sort tiers, optimistic overlay). Unread stays: the marker, the auto-mark on a
finished turn, dwell-to-read and the mark-read-on-open endpoint are all
unchanged; only the manual toggle is removed.
Claude Code session import: the on-disk scanner, `aoe session import`,
`/api/claude-sessions`, and the wizard's import tab.
Repo clone: `/api/git/clone`, `clone_repo` / `clone_bare_repo`, the wizard's
Clone URL tab and the dashboard action.
Markdown: ToolCards now renders through react-markdown like MarkdownFileView
instead of carrying a second stack, dropping marked and dompurify.
Dead dependencies with no call sites: portable-pty, serde_yaml, glob, clsx.
Web multi-select is left in place but no longer drives anything; it existed
only for bulk pin/archive.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Five feature removals, ~27.8k lines:
- Docker/Podman sandbox: src/containers/, container_config, acp::sandbox,
SandboxInfo, the [sandbox] config section, container terminals, in-container
hooks, volume_ignores, the sandbox-image banner (and [updates], which existed
only to gate it), /api/docker/status, and docker/.
- ACP Node download: the pinned-tarball fetch and NodeSource::Bundled. Drops
the tar and xz2 deps, and with xz2 the system liblzma requirement.
- session_tee: the per-session tracing mirror. Agent stderr still reaches
acp-workers/<id>.log via the runner; daemon events stay in debug.log.
- qrcode: dep plus the serve dialog's QR block.
- session.tools: tool sessions, picker dialog, ViewMode::Tool, hotkeys.
Legacy tmux prefixes (aoe_tool_*, aoe_cterm_*) are kept as sweep-only
constants so sessions left by an older build are still torn down rather than
mistaken for agent panes.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The dashboard-auth removal left `--no-auth`, `--auth`, and `--passphrase`
in the e2e harness invocations and two passphrase login tests behind, so
those tests drove flags clap no longer accepts. Removed, along with the
`auth_mode` / `auth_required` / `passphrase_enabled` fields the web
`ServerAbout` mocks still carried and the dead `auth.*` tracing targets.
Also fixes fallout from the feature removals on this branch: a duplicated
`#[serde(default)]` on `Config::worktree`, a stale `App::new` parameter,
leftover `mcp_servers` / `mcp_hash` arguments in the integration tests,
and two `aoe mcp list` e2e tests.
AoE no longer models, merges, or forwards MCP servers. Removed:
- the layered resolver (`mcp_model`), the AoE-owned `mcp.json` overrides
writer, and the drift store behind conflict/keep/drop;
- `aoe mcp`, the `/api/mcp/*` routes, and the web Settings "MCP servers"
tab;
- ACP forwarding: `mcp_config`, `project_mcp`, and the `mcp_servers`
plumbing through SpawnConfig into `session/new`, `session/load`, and
`session/fork`.
Agents that read their own native MCP config still get their servers; AoE
just stops injecting any. Repo trust is now hooks-only, so the trust
dialogs and `trust_repo` lose their second surface.
The structured view still renders `mcp__server__verb` tool calls.
Drops AoE's own model of agent skills and every surface over it: the
`aoe skill` subcommand, the `/api/skills` routes, the TUI manager dialog,
the web Settings "Skills" tab, and the `skills.auto_propagate` setting
that copied managed skills into agent config dirs and sandboxes.
Agents keep reading their own skill directories; AoE no longer discovers,
edits, or propagates them. The structured view still renders a Skill tool
call, now without the provenance badge that came from `/api/skills`.
Drops the post-creation "attach another repo to this session" flow: the
`aoe session add-project` subcommand, `POST /api/sessions/:id/projects`,
the TUI picker and its background poller, and the web sidebar modal.
Multi-repo sessions are still created multi-repo (`aoe add -w --project`);
only converting an existing single-repo session is gone.
`WorkspaceRepo.branch_preexisting` stays so records written by the old
flow still delete their branches correctly.
Drops three unrelated-but-small surfaces:
- the TUI intro dialog and the web ThemeIntro modal with its welcome-phase
gate, plus `app_state.has_seen_welcome` / `last_seen_version`, which
nothing else read;
- the Age of Empires cheat-code easter eggs in both clients;
- `tui::remote_home`, the cross-machine structured-view picker that `aoe`
swapped to when `AOE_DAEMON_URL` was set. That variable still retargets
the daemon-backed subcommands; the TUI is local-only.
The home view now defaults to Project grouping for everyone rather than
branching on whether the welcome screen was dismissed.
Drops `aoe serve --remote`, `--tunnel-name`, `--tunnel-url`, and
`--no-tailscale`, along with the Tailscale Funnel and Cloudflare
transports behind them. The serve dialog collapses to the local bind it
already had; `serve.mode` and the daemon passphrase machinery go with it.
Remote access is now a reverse proxy in front of the daemon
(`--behind-proxy` plus `--allowed-host`), which the DNS-rebinding gate
already supported. `ServerAbout.behind_tunnel` is renamed `behind_proxy`.
`src/github/` was the typed REST surface behind the update checker and the
changelog dialog, both removed upstream. Nothing constructed a
`GitHubClient` any more; the module survived only because
`containers::image_update` borrowed its `DEFAULT_USER_AGENT` constant.
That constant moves next to its one caller.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Deletes `session::conversation_summary`, the `POST /api/sessions/{id}/summarize`
endpoint, the sidebar's "Summarize conversation" action, and the
turn-boundary trigger in the ACP reconciler.
The feature shipped off by default (`session.conversation_summary = false`)
and spent a one-shot agent call per turn boundary to produce a recap the
transcript already contains.
The agent's own `ConversationSummary` ACP event is untouched; only aoe's
generated recap is gone.
BREAKING CHANGE: the `session.conversation_summary` setting is removed and
`POST /api/sessions/{id}/summarize` now 404s.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Deletes v001 through v026 and the agent-hook machinery only those
migrations still reached: the codex `config.toml` hook installers, the
marker-presence walkers, and the `SidecarHooks` dispatch (no remaining
agent declared one).
`.schema_version` and the runner stay so a future breaking change has a
version to branch on; the array is empty and `run_migrations` just stamps
the baseline.
BREAKING CHANGE: an install older than schema 26 is no longer migrated.
It jumps straight to the baseline, leaving stale config keys and hook
strings in place.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Drops cursor, copilot, droid, gemini, vibe, settl, hermes, kiro, qwen,
antigravity, kimi, and omp, along with everything that existed only to
serve them: their status detectors, session-id capture backends, hook
installers, container config mounts, ACP profiles and registry entries,
install hints, and skill roots.
The OMP launch-capture machinery goes with it: the marker gating, the
routing fingerprint, the generation CAS, and the `SessionIdGuard`
variants that only OMP ever produced.
BREAKING CHANGE: sessions using a dropped agent no longer resolve an
`AgentDef`, so they fall back to the generic launch and status paths.
The positional index of a settings entry may shift.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Deletes token auth, passphrase login, elevation, the device/session
manager, and the request rate limiter. The daemon now serves every
request that clears the DNS-rebinding gate.
The gate, the security headers, read-only mode, and the push/VAPID
crypto stay. `x-aoe-device-binding` stays too, now purely as a
per-browser id for presence tracking.
BREAKING CHANGE: `--auth`, `--no-auth`, and `--passphrase` are gone,
`serve.url` no longer embeds a token, and the Security and Devices
settings tabs are removed. Anyone who can reach the port controls the
agent sessions; run it behind a VPN or a reverse proxy that
authenticates.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Merges origin/main's plugin-system and sound-effects removal (which
also drops aoe-plugin-api and bundled_sounds), then removes the
remaining named leftovers directly.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
All three cargo steps now run at `serve,e2e-tests`. They previously used
three different sets, so the lib and the ~144 crates `serve` adds compiled
once per step. clippy gains `--all-targets` so `cargo test` reuses the test
targets instead of rebuilding them.
The test run is split by thread budget rather than feature set, so neither
half recompiles: the lib suite spawns no tmux and gets 8 threads, the
integration and e2e binaries keep the cap at 3.
git2 drops `vendored-openssl`, its only non-default feature. git2's
`default` is empty, so `https`/`ssh` were never on and openssl-sys was in
the tree solely to be vendored — building OpenSSL from C source on every
cold cache. Nothing reaches a remote through libgit2: src/git/remote.rs
reads origin's URL, and `Repository::clone` appears only in tests cloning a
tempdir over the built-in local transport. flake.nix loses `perl` with it.
Linting `serve` for the first time surfaced 16 pre-existing violations,
fixed here. The `result_large_err` family is allowed at the `server::api`
module root: those handlers return `Response` in the `Err` arm, and boxing
it would put an allocation on every error path.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Both features are gone; the pages were still published by the website
sync script and listed in the docs nav.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Deletes the plugin host, the aoe-plugin-api crate, the bundled plugins
directory, the CLI subcommands, the TUI plugin pane, and the dashboard's
plugin slots, panes, commands, and sort/filter contributions.
BREAKING CHANGE: `aoe plugin` and `aoe graft` are gone, installed plugins
are no longer loaded, and the `default-plugins` cargo feature is removed.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
None were reachable from the build.
- `scripts/install.sh` downloaded release tarballs from
`github.com/agent-of-empires/agent-of-empires`. With `.github/` gone
nothing publishes releases for this fork, so it installed upstream's
binary over a local build.
- `scripts/update-formula.sh` (Homebrew formula bump) and
`scripts/release_metrics.sh` (hardcoded to upstream's repo) served the
same release flow.
- `contrib/{openclaw,hermes}-skill/SKILL.md` packaged the CLI docs for
two third-party skill registries, plus `cargo xtask check-skill` and
its CI step, which validated their registry-specific frontmatter.
Dropping check-skill leaves xtask with no use for the lib, so
`agent-of-empires`, `aoe-plugin-api`, regex, serde, tempfile, and toml
come off its manifest. `cargo xtask dev` now builds in ~1s instead of
compiling the whole crate first.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Breaks from upstream: this fork no longer tracks agent-of-empires and
no longer ships or installs its releases.
Removed:
- `FORK.md`, the rebase-onto-upstream patch-stack policy it described.
- `.github/` (11 workflows), `CHANGELOG.md`, `cliff.toml`, `codecov.yml`,
`marketing/`, `assets/`. None were reachable from the build; CI runs
from `.gitea/`.
- The self-updater: `src/update/`, `aoe update`, the TUI update banner
and confirm dialog, `GET /api/system/update-status`, the web
`UpdateBanner`, and the telemetry fields fed by the update cache
(`update_status`, `update_releases_behind`, the `auto_update` feature
flag). All of it pointed at upstream's GitHub releases.
- The release-notes changelog dialog. Plugin changelogs are untouched.
- CityHall client mode: `--cityhall`, `AOE_CITYHALL_MODE`, the
`cityhall_gate` middleware and its allow/deny route tables, 56
per-handler guards, the config bundle, and the web capability layer.
`updates.update_check_mode` stays: it still gates the sandbox-image
registry probe. `auto` is now a legacy alias for `notify` so existing
configs keep parsing.
Verified: cargo fmt, clippy -D warnings, cargo test --features
serve,e2e-tests (6123 lib + all integration binaries), vitest 3314,
tsc, eslint, oxfmt, coverage-matrix validation. Two failures are
environmental and reproduce on an untouched main:
`tmux::session::tests::test_is_attached_true_with_live_client` (passes
in isolation, flaky under parallel tmux contention) and
`runner_reports_native_prompt_complete_over_control_socket`.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Removes SessionCaller, the plugin create-idempotency subsystem, and the
created_by_plugin / plugin_create_idempotency fields on Instance. The
plugin sessions.create and sessions.turn.send RPCs now refuse; the
plugin system is removed next.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The fork has no public website to publish them to, so the tree carried
11.7k lines of prose nothing rendered.
Takes the machinery that existed only to feed them: the "Check CLI docs
are up-to-date" CI step on both forges, `cargo xtask gen-docs` and its
`clap-markdown` dependency (its sole output was `docs/cli/reference.md`),
and the `docs/development.md` pointer in the dev-dir migration notice.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Drops snoozed_until from the session record, the TUI duration dialog and
h/H keybinding, the context-menu entry, PATCH /api/sessions/{id}/snooze,
the aoe session snooze/unsnooze commands, the sidebar snooze modal and
bulk actions, and session.snooze_duration_minutes.
The Attention-sort context guard and the sink predicates collapse to
archive-only; h now always collapses a group. The sidebar's
"Snoozed & archived" section becomes "Archived".
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Five surfaces go: the TUI diff view and its `[diff]` config, the web
diff pane and its review comments, the web Files pane, the first-run
dashboard tour, and the tips system on both surfaces.
Removed with them: `aoe session set-base` and the per-session diff-base
override, `GET /diff/files`, `GET /diff/file`, `PATCH /diff-base`,
`POST /acp/prompt/diff-comments`, `GET /api/tips`, `POST /api/tips/show`,
`POST /api/app-state/tip-seen`, and `POST /api/app-state/web-tour-seen`.
Kept: the transcript file-ref viewer, the @-mention file index, the
structured view's tool-card edit diffs and the theme `diff_*` tokens they
use, and branch listing for the new-session wizard, extracted from the
deleted `src/git/diff.rs` into `src/git/branches.rs`.
Two behavior notes. A stored pane layout naming the removed panes is now
filtered on load, so an existing dashboard does not open onto an id with
no descriptor. Transcript file links pass an absolute path through to
`GET /file` untouched rather than rewriting it relative to one repo,
which a multi-repo workspace would resolve against the wrong root; the
cited line no longer scrolls into view, since that was the diff viewer's.
Migration v026 strips `[diff]` and `session.show_tips` from global and
profile configs. Without it every launch would warn about unrecognized
keys. `state.toml` and `sessions.json` need no migration: their dropped
keys are never probed and disappear on the next write. A transcript that
used "Send diff comments" loses that one prompt row on replay; the event
no longer deserializes and the store skips it.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Five removals, no coverage lost except where noted:
- mobile-keyboard: two tests gated on `browserName !== "webkit"`, but
playwright.config.ts only configures chromium, so they never ran. Their
two private helpers went with them.
- sidebar-resize-quota: the content-split test skipped itself whenever the
handle was absent, so it could not fail.
- acp-memory-recall (mocked): the happy path duplicates
tests/live/acp-memory-recall.spec.ts, which drives the same card through
a real serve backend. The malformed-payload case has no live equivalent
and stays.
- sandbox.rs, opencode_sandbox_resume.rs, and the brew probe in
update_command.rs: all `#[ignore]`, so CI never ran them. This does drop
the regression guards for #1989, #2605, and the duplicate-mount bug.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Drops src/telemetry, the consent flow (intro wizard page, dialogs,
web modal), the aoe telemetry CLI, /api/telemetry* routes, all
fire-and-forget counters, and the Telemetry settings tab on both
surfaces.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Drops website/ (Astro marketing site), its build script, deploy and CI
workflows, dependabot block, and doc references. docs/ stays canonical.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Drops the sound module, bundled wavs, aoe sounds CLI, the Sound
settings tab (TUI + web), the /api/sounds endpoints, and the web
approval chime.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(core): enforce rename uniqueness (#3389)
* refactor: consolidate tied-rename derivation and bound worktree lock effects
Single-source the tied-rename duplicate-path derivation into
worktree_edit::derived_worktree_path, replacing the tripled inline
slug plus target-path logic in the CLI, server, and TUI rename paths
(the deliberate tied vs tied_edit gate difference is preserved). Factor
the rename cache-patch's identical path and branch reconciliation into
reconcile_unowned_identity.
Bound the two git effects held under the app-wide title-mutation lock
(git worktree move, git branch -m) with a 30s timeout via a new
run_git_with_timeout, so a stalled filesystem can no longer pin the
cross-process lock indefinitely. Document on the lock that it is held
across these bounded external effects and that (title, project_path)
uniqueness is intentionally not a global invariant, since smart_rename
and non-guarded creation surfaces run outside it.
Fix two stale web doc refs (is_duplicate_session moved to
session/instance.rs), drop a stray blank line before acp_reject_tests,
and justify the grouped rename tests.
* docs(worktrees): align tied rename behavior
* fix(git): bound worktree mutation commands
* fix(git): bound the remaining worktree mutation subprocesses
`git worktree move` and `git branch -m` were bounded, but three git
subprocesses on the same path were not, and one of them runs inside the
function that got the timeout: `move_worktree` calls `unlock_worktree`
immediately before its bounded move, and both touch the same
`.git/worktrees/<name>/` metadata. `lock_worktree` runs on every outcome
branch, and `branch_exists` runs whenever a rename touches the branch.
The profile-move transaction executes all of these while holding the
app-global session identity flock, the per-session title and lifecycle
flocks, and both profile storage flocks, so a stalled filesystem in any
one of them pins every rename and every `aoe add` in every profile,
across every aoe process. That is the failure the timeouts exist to
prevent, reached through the call that runs first.
Route all three through a bounded helper. `lock_worktree` and
`unlock_worktree` reuse the existing 30s worktree mutation timeout;
`branch_exists` reuses the 5s observation timeout, matching
`observe_local_branch_ref`, which issues the identical `show-ref`. A
timeout in `branch_exists` surfaces as `Err`, preserving its documented
fail-closed tri-state contract so an undeterminable ref is never read as
absent. `run_git_quiet_with_timeout` keeps `unlock_worktree`'s routine
non-zero exit at DEBUG, mirroring the existing `run_git_quiet` pair; a
killed child still logs at WARN.
Also correct six comments that still describe the pipe-and-drain-threads
implementation this PR replaced with temporary regular files, including
the docstring on `run_git_with_timeout` itself.
---------
Co-authored-by: njbrake <nathan@mozilla.ai>
Stop/remove killed only what the visible tmux server could reach. An
agent whose pid escaped the pane's process tree, or one spawned on a
tmux server this process cannot see (e.g. a service with a private
/tmp and thus a private tmux socket), survived removal and kept
replying invisibly.
After tmux teardown, scan the process table with the same identity
needles as the recovery orphan guard (AOE_INSTANCE_ID env entry /
agent_session_id argv) and kill any surviving tree.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Plain 'aoe add <dir>' on a linked worktree stored no worktree_info, so
the sidebar grouped the session under the worktree folder name instead
of the main repo. Detect the main repo via the existing .git-file
detection and record it, matching what the --worktree attach flow
writes.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(web): keep plugin panes closed by default
Change the default for autoOpenPluginPanes from true to false so that
installing a plugin (e.g. github-lite) does not automatically open its
pane every time a chat is selected. Users can still open plugin panes
on demand from the activity bar, and the setting toggle still opts in
explicitly.
Updates the PanelsSettings test to expect the plugin toggle to default
off while diff/terminal remain on.
* test(web): open plugin panes from the activity bar in pane-docking specs
The two plugin-pane specs clicked `pane-tab-<id>` straight after loading the
session, which only worked because `autoOpenPluginPanes` defaulted on and
pushed the tab in unasked. With the default off the tab does not exist yet and
both specs fail on a missing locator.
Open the pane from its activity-bar toggle instead. `togglePlugin` routes to
`openOrRevealTab`, which adds the tab and activates it, so the pane body still
mounts from the one click. This also covers the on-demand path the new default
depends on, which had no test before.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Karl <karlasgerjuhl@gmail.com>
Co-authored-by: Test <nathan@mozilla.ai>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
The `docker exec` snippet accepted an entry on its `.jsonl` glob and a
fresh mtime without checking what it is, so a FIFO, a dangling symlink or
a symlink cycle named `<uuid>.jsonl` under the container's
`$CLAUDE_CONFIG_DIR/projects/<encoded>/` was handed back as a resume id
with nothing behind it. `find` on a named argument lstats it and reports
a modification time that never advances, so these read as fresh.
This is the container mirror of the host-side guard added in #3454, and
Claude's was the only one of the five container list snippets without a
type check: pi and vibe use `[ -f ]`, codex and gemini use `find -type f`.
All three checks now dereference. `[ -f ]` already did; `find -L` and
`ls -tL` did not, and a symlink's own mtime is frozen at creation, so a
link older than the five-minute gate was dropped while its target was
still being appended, and `ls -t` ordered by the link rather than the
target. #3454 chose `fs::metadata` on the host for that same reason, so
without `-L` this mirrored the type check and inverted the freshness one.
The snippet moves into `claude_container_list_snippet` so the test can
run the production string under `sh` against a real directory, following
the `PI_CONTAINER_LIST_SCRIPT` precedent; no Docker needed. The symlink
row ages the link past the gate so it pins the deref rather than passing
on a link too young to tell lstat from stat, and skips itself where
`touch` cannot age the link (BSD `touch` rejects that date form, so on
macOS the row was passing without testing anything).
Verified independently under dash and busybox: `[ -f ]`, `find -L` and
`ls -tL` behave identically on both, so the Alpine and Debian bases agree.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>