Commit Graph
2265 Commits
Author SHA1 Message Date
luisfnicolauandClaude Opus 5.5 d9f8e2f5ec docs: changelog, marker file and boundary inventory for repository identity (#708)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014aqFKAuGVkuBoewmpA3cx9
2026-09-28 09:43:31 -04:00
luisfnicolauandClaude Opus 5.5 a0f43ead5d feat(identity): resolve the identity in every hook client (#708)
The native hook, hooks/_lib.sh, hooks/lib/ai-memory-hook.ps1 and the
generated TypeScript plugins resolve the identity host-side, skip it
when the marker declares project or identity, and are each checked
against the core's shared fixture. New marker key: identity.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014aqFKAuGVkuBoewmpA3cx9
2026-09-28 09:43:22 -04:00
luisfnicolauandClaude Opus 5.5 2185d74116 feat(identity): route captures by the identity the client resolved (#708)
/hook, /hook/batch items and /handoff accept identity / identity_src;
only the explicit and git_remote rungs route by identity, anything else
routes by name as before. The path-keyed project cache includes the
identity, so one path holding two repositories on two machines does
not answer for both.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014aqFKAuGVkuBoewmpA3cx9
2026-09-28 09:43:22 -04:00
luisfnicolauandClaude Opus 5.5 b2621692b1 feat(identity): key a project by its repository, not its folder name (#708)
ai-memory-core gains the repository-identity resolution chain (marker
identity, marker project, upstream/origin remote normalised with
credentials stripped, folder name) and a shared fixture of remote-URL
cases. V70 adds projects.identity / identity_source with a partial
unique index per workspace and no backfill (case-only name clashes
would fail the upgrade). resolve_project_by_identity matches, claims in
place when the caller may write (slice 2's resolve_project_authz on
the same transaction), leaves it unclaimed otherwise, or splits a
different repository into owner-named projects; created projects
record created_by.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014aqFKAuGVkuBoewmpA3cx9
2026-09-28 09:43:22 -04:00
AkitaOnRails bec4b4856f Merge PR #947 into release/2.5
docs(reranker): choice-contrastive variant of the Jev reranker adapter
2026-09-28 01:34:09 -03:00
AkitaOnRails 45c840464b Merge PR #945 into release/2.5
feat(cli): purge-project previews what it would delete before --confirm
2026-09-28 01:34:09 -03:00
evanmaranzano 3cc78985d2 docs(reranker): ask the choice adapter for the specific answering page
The example used to ask which document was most relevant, which pulled
index pages to the top. The replacement asks for the page that contains
the answer and keeps the choice-versus-rubric numbers labeled separately.
2026-09-28 10:29:25 +08:00
Maxsuel Einstein c8aab5bf5e feat(cli): purge-project previews what it would delete before --confirm
Without --confirm, purge-project only refused with a generic
"destructive and irreversible" message; the counts of what would be
deleted appeared only after a confirmed run actually deleted them. An
operator purged a project believing it held 0 sessions / 0 pages, and
it actually held 1,063 observations reachable only through a different
project's session (a pre-#871 Windows path-casing split) -- recovered
only from a backup taken a minute earlier.

POST /admin/purge-project now accepts an optional "dry_run": true,
which always wins over "confirm" -- {"confirm": true, "dry_run": true}
still only previews, the same way reclaim-ledger-versions treats its
own dry_run field, so a preview request can never become destructive.
The preview runs the same lookups and counts a confirmed purge uses to
decide what to delete (same 404/409), including two new counts for
rows a purge collaterally deletes or orphans in a DIFFERENT project via
its sessions cascading (collateral_observations_deleted,
collateral_handoffs_denulled -- observations.session_id is ON DELETE
CASCADE and handoffs.from_session_id/accepted_by_session are ON DELETE
SET NULL, neither scoped to the purged project_id). It never issues the
DELETE: PurgeMode::{Commit, Preview} replaces the earlier commit: bool,
and Preview returns right after counting instead of running the delete
and rolling it back, so a preview of a large project doesn't hold the
single-writer actor for as long as a real purge would and starve every
hook capture queued behind it. Preview also skips wiki file removal,
admission webhook dispatch, and both checkpoints; a 200 preview is
therefore not a guarantee the confirmed purge will succeed, since
admission only runs on the confirmed path.

The CLI asks for this preview (bounded to a few seconds, including auth
refresh) before refusing, and prints "Would purge <ws>/<proj>: N pages,
..." plus any collateral-damage counts. A 404/409/403 (or anything else
unexpected) prints the server's own error before the refusal; a plain
400 (older server), a timeout, or an unreachable server fall back
silently to the existing refusal, so no existing script's behavior
changes except gaining information.

Store-level tests cover: preview reports the same counts a confirmed
purge then produces; preview changes nothing (every project-scoped
table's row count, including purged_scopes and audit_log); the incident
shape (an observation stamped into the purged project from a session
that lives elsewhere); and the mirror shape (purging a project
collaterally deletes an observation, and orphans a handoff's session
reference, in a different project). Admin-level tests add the HTTP
equivalents, confirm {"confirm": true, "dry_run": true} deletes
nothing, no webhook dispatch on preview, 404 parity, a 409 on a live
managed-run lease without --force, and root-vs-DB-user-vs-anonymous
auth on the preview payload. CLI tests cover the summary-line wording,
the pure outcome-to-message mapping, and run_preview's HTTP-status
classification against a real local server (400/404/200/connection
failure).
2026-09-27 19:49:12 -03:00
evanmaranzano f64ef68dbf docs(reranker): choice-contrastive variant of the Jev reranker adapter
One choice question over the whole candidate list instead of one rubric
score per candidate; choice probability maps to relevance, which is sound
because the reranker leg is sort-only. +14/+21 hit@1 over the rubric shape
on the same 35B/4B backends (live golden set), 3x lower rerank latency,
and the only shape where replay-trained small judges hold their quality.
Scopes the absolute-relevance caveat to consumers that read absolute
values.
2026-09-28 01:06:53 +08:00
Caminhar 3d84623b98 feat: install Hermes Agent lifecycle hooks
Hermes runs a configured hook command through shlex.split with the event JSON on stdin and no shell, so its integration is exec-form (like Zero and ZCode) rather than a .sh bundle: AgentChoice::Hermes (alias hermes-agent) makes install-hooks, setup-agent and finalize-session accept it, and no script subdir is staged.

build_hermes_hooks_yaml emits the ready-to-paste hooks: block for ~/.hermes/config.yaml with the two events ai-memory can act on - pre_tool_call -> pre-tool-use and post_tool_call -> post-tool-use. Their tool_name / tool_input payload is the envelope the router already maps for agent=hermes, which is what gives Hermes sessions tool observations, tool-family titles and capture exclusions.

install-hooks --agent hermes never writes the config file: it is YAML the operator also edits and Hermes gates user hooks behind its own acceptance prompt (hooks_auto_accept), so the block is printed for pasting - the same choice made for Pool. Session lifecycle is deliberately left to the memory-provider plugin, so no hook-driven session-end is installed and a session cannot be closed twice.

Verified against Hermes v0.21.4: the block parses through Hermes own hermes_yaml, _parse_hooks_block registers both events, split_command_line yields an argv that runs the native hook subcommand, and that command spools the event with rc=0. Docs updated in docs/install.md, docs/support-matrix.md and docs/mcp-install.md. Follow-up to #623.

(cherry picked from commit 18155089cd)
2026-09-27 12:57:03 -03:00
AkitaOnRailsandClaude Opus 4.8 414fe45e74 docs(lifecycle-ops): document reclaim-ledger-versions
Add the online reclaim command to the safety matrix and a
command-by-command section (dry-run default, content gate, --compact
to reclaim bytes), the operator guide AGENTS.md requires for new
destructive lifecycle ops. Follow-up to #927 (#914).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MDbhmszrjG9s5MrPrTuNtm
2026-09-27 12:53:53 -03:00
AkitaOnRails 6add9b727b Merge PR #927 into release/2.5
feat(cli): reclaim-ledger-versions drops the pre-#660 ledger residue (#914)

# Conflicts:
#	docs/ARCHITECTURE.md
2026-09-27 12:52:54 -03:00
AkitaOnRailsandClaude Opus 4.8 d7201243b1 chore(changelog): correct #708 authz enforcement to the third slice
The enforcement PR (#924) is slice 3 of #708 (slice 1 = design + choke
point, slice 2 = project_grants); the changelog entry said 'second'.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MDbhmszrjG9s5MrPrTuNtm
2026-09-27 12:52:13 -03:00
AkitaOnRails 20967297ce Merge PR #924 into release/2.5
Per-project authorization, slice 3: enforce restricted end to end (#708)
2026-09-27 12:51:51 -03:00
AkitaOnRails 56a06fd68f Merge PR #898 into release/2.5
fix(omp): use profile paths for hooks, MCP and sessions (#820)
2026-09-27 12:51:43 -03:00
Éverton Toffanetto fed7d80140 docs(omp): clarify profile selection and install paths 2026-09-27 01:16:35 -03:00
Éverton Toffanetto 1fc6cf7f22 test(run): preserve Pi blank environment coverage 2026-09-27 01:15:09 -03:00
Éverton Toffanetto 86f001c6bf fix(omp): follow OMP's profile rules, PI_CONFIG_DIR and XDG session dir
A named profile (`--profile`, `OMP_PROFILE`, or the legacy `PI_PROFILE`) owns
`~/.omp/profiles/<name>/agent` and ignores `PI_CODING_AGENT_DIR`, as OMP does;
`install-hooks` wrote the extension into `PI_CODING_AGENT_DIR` instead. Profile
names are normalized and refused like OMP does, `PI_CONFIG_DIR` renames the
`~/.omp` root, and on Linux and macOS sessions move to `$XDG_DATA_HOME/omp`
when that directory exists. `install-mcp`, auto-wire, session import,
`backfill`, `doctor` and the `uninstall` sweep follow the same agent dir.

Refs #820
2026-09-27 00:24:41 -03:00
Yi-111-a d2b9896876 feat(cli): reclaim-ledger-versions drops the pre-#660 ledger residue
#660 stopped the indexer from superseding an OKF-conformed ledger on every
hook append, but the rows it already wrote stay and nothing removed them:
`compact` deletes nothing, `forget-sweep` only hard-deletes decay
tombstones (only `decay` writes `superseded_at`), and `reindex` loses the
DB-only state. One reported store holds 6,539 versions of 101 live pages,
95% of its rows.

Adds `ai-memory reclaim-ledger-versions` (`POST
/admin/reclaim-ledger-versions`), an online command that deletes only the
superseded versions of paths whose *content* opens with a hook log entry.
Dry run unless `--confirm`; `--drop-latest` also removes each ledger's live
row; `--compact` rebuilds the FTS index and VACUUMs to return the bytes.

The content gate is the load-bearing part and is now one definition:
`ai_memory_core::log_ledger` owns the predicates, `ai_memory_wiki::ledger`
keeps the file-reading half and delegates, and the store decides from a
`pages.body` with no filesystem access. A real page a human named
`log-2026-09.md` keeps its whole version chain. Only `is_latest=0 AND
superseded_at IS NULL` rows are eligible, so decay-owned rows stay with the
sweep that reasons about them.

Derived FTS/entity/vector/link rows go with the page through the existing
ON DELETE CASCADE. The FTS delete trigger is stood down for the bulk delete —
its DDL read back from sqlite_master and re-executed, so it cannot drift
from the schema — and pages_fts is rebuilt wholesale, which is what keeps
this from re-tokenizing tens of gigabytes of ledger body row by row.

Reading the gate needs a bounded prefix, not the whole body: GATE_PREFIX_BYTES
lives with the predicate, shared by the file reader and the store.

Refs #914
2026-09-26 14:16:04 +08:00
luisfnicolauandClaude Opus 5.5 d1e50a9966 docs(security): record #708 slice 3 in the boundary inventory
Row 14 now lists the enforcing code and the adversarial tests, each
proven to fail with the choke point (18 tests) or the unscoped-read
SQL filter (9 tests) neutralized.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014aqFKAuGVkuBoewmpA3cx9
2026-09-25 19:17:10 -04:00
luisfnicolauandClaude Opus 5.5 9da6f52e37 docs: changelog, users guide and design status for #708 slice 3
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014aqFKAuGVkuBoewmpA3cx9
2026-09-25 18:51:09 -04:00
luisfnicolauandClaude Opus 5.5 57c98b44d3 feat(cli): grant and access-mode commands; 403 is final for captures (#708)
ai-memory user grant|revoke|grants, ai-memory project access|grants,
[auth] new_projects_restricted, and the native hook client drops an
event the server refused with 403 instead of retrying it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014aqFKAuGVkuBoewmpA3cx9
2026-09-25 18:51:08 -04:00
luisfnicolauandClaude Opus 5.5 41ca270b98 feat(auth): enforce per-project access on every surface (#708)
Every database user is stamped as an AuthorizedViewer (never root) and
every surface attaches ScopeResolver::with_project_authz for them: MCP
tools, /api/v1 and the web pages, hook routes and captures. Read-shaped
tools that mutate need write; managed-run, workstream and session-id
entry points authorize the project the id resolves to; message queues
need write to deliver, pop and cancel. Captures into a project the
author may not write are dropped and counted (dropped_unauthorized).
Root-only /admin routes manage access modes and grants.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014aqFKAuGVkuBoewmpA3cx9
2026-09-25 18:51:08 -04:00
luisfnicolauandClaude Opus 5.5 84cc8f3347 feat(store): per-project creator, grant storage and guarded resolution (#708)
Slice 3 on top of slice 2's choke point:

- V69 projects.created_by; resolve_project_authz derives is_creator
  from it, so one principal per request is right for every project.
- ScopeResolver::resolve_existing_args[_traced] take a ProjectAccess,
  so read-shaped mutations can demand write; resolve_read_args* stay
  Read. resolve_many_existing authorizes every scope, and
  resolve_write_args records the creator.
- authorize_scope_for and *_guarded free functions for routes that
  name a project directly. A refusal names the project and both levels.
- Unscoped reads filtered in SQL before LIMIT with the choke point's
  rule (not restricted, creator, or any grant; global scope shared).
- Grant storage: grant/revoke/list, audit_log rows, access-mode
  setter, [auth] new_projects_restricted, grants follow a project move.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014aqFKAuGVkuBoewmpA3cx9
2026-09-25 18:51:07 -04:00
AkitaOnRailsandClaude Opus 4.8 dde5806b23 Merge PR #912 into release/2.5
feat(web): add a root-only /web/pending triage page (#855)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MDbhmszrjG9s5MrPrTuNtm
2026-09-25 17:29:50 -03:00
Jayson ReisandClaude Opus 5.5 a84d66be60 feat(web): add a root-only /web/pending triage page (#855)
Add a server-rendered page under /web that lists the pending
auto-improvement proposals of all projects. The page has a project
filter and a sort. It shows the rationale, the proposed body, and a
warning for proposals that write under _rules/ or replace a page.

The approve and reject buttons post from the browser to the existing
/admin/pending-writes/{id}/approve|reject handlers with the session
cookie and the CSRF header. Admission, audit, attribution, and the
single writer stay the same. ai-memory-web adds no write route.

The page uses the same Capability::Admin decision as /admin, and
serve passes the trusted-proxy setting to it. A non-root session gets
a 403 page. The HTML auth redirect does not send that session to the
change-password form.

Two store reads feed the page. One counts pending proposals per
project, so the total and the project filter include every project.
The other lists proposals, optionally for one project, with a cap of
500 rows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SwU4vj4tZLYVLR7vEKeh2W
2026-09-25 18:11:02 +02:00
AkitaOnRailsandClaude Opus 4.8 47af33a79c Merge PR #899 into release/2.5
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MDbhmszrjG9s5MrPrTuNtm
2026-09-25 11:32:41 -03:00
AkitaOnRailsandClaude Opus 4.8 a0836577df Merge PR #900 into release/2.5
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MDbhmszrjG9s5MrPrTuNtm
2026-09-25 11:32:41 -03:00
AkitaOnRailsandClaude Opus 4.8 d5c3d89f8c Merge PR #901 into release/2.5
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MDbhmszrjG9s5MrPrTuNtm
2026-09-25 11:32:41 -03:00
AkitaOnRailsandClaude Opus 4.8 3d85b650c0 Merge PR #881 into release/2.5
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MDbhmszrjG9s5MrPrTuNtm
2026-09-25 11:32:24 -03:00
AkitaOnRailsandClaude Opus 4.8 b2817e451a Merge PR #907 into release/2.5
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MDbhmszrjG9s5MrPrTuNtm
2026-09-25 11:32:24 -03:00
Éverton Toffanetto d3d05d4405 test(consolidate): write the oversized eval stdout in one printf
The fixture printed 70,000 bytes one byte per shell loop iteration, which on a loaded machine outran the eval gate's 2s timeout and failed the test as eval_gate_timeout instead of the stdout cap it checks. One zero-padded printf writes the same bytes at once.
2026-09-25 02:56:24 -03:00
Éverton Toffanetto 2d16133024 Merge branch 'fix/autowire-run-env' into fix/uninstall-sentinel-sweep 2026-09-25 02:53:35 -03:00
Éverton Toffanetto 2dc6ce56ad docs(changelog): keep one Changed section under Unreleased 2026-09-25 02:53:35 -03:00
Éverton Toffanetto cb7ebf3072 Merge branch 'fix/autowire-run-env' into fix/crush-launch-context 2026-09-25 02:53:34 -03:00
Éverton Toffanetto cd70385bc9 Merge branch 'fix/autowire-run-env' into fix/kiro-v3-resume 2026-09-25 02:53:34 -03:00
Éverton Toffanetto 021735bda2 docs(changelog): keep one Changed section under Unreleased 2026-09-25 02:53:29 -03:00
Éverton Toffanetto 84d4a1e406 test(e2e): restart a spawned serve on a fresh port when another socket took it
The e2e suites picked a free port, released it, and started `serve` on it; a
socket opened in between made the server exit and the test time out. The
shared `start_serve` helper now waits for the server's ready line, restarts on
a fresh port when the bind failed, and fails at once with the server's stderr
on any other exit. The Node plugin harness in `external_capture_ts` retries a
taken port the same way.
2026-09-25 02:44:42 -03:00
AkitaOnRailsandClaude Opus 4.8 39f19ee4f2 docs(changelog): merge the duplicate [Unreleased] Changed heading from the forward-merge
The main->release/2.5 forward-merge left two ### Changed headings (release/2.5's
#840/#865 and main's #894). Keep a Changelog allows a heading once per version;
fold #894 under the single Changed heading.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MDbhmszrjG9s5MrPrTuNtm
2026-09-25 00:56:56 -03:00
Éverton Toffanetto 041899a1c0 Merge branch 'fix/autowire-run-env' into fix/crush-launch-context 2026-09-25 00:49:53 -03:00
Éverton Toffanetto dbacca6b49 Merge branch 'fix/autowire-run-env' into fix/uninstall-sentinel-sweep 2026-09-25 00:44:52 -03:00
Éverton Toffanetto 8b04b37ba5 Merge branch 'fix/autowire-run-env' into fix/kiro-v3-resume 2026-09-25 00:44:28 -03:00
Éverton Toffanetto 407d55d0f8 Merge remote-tracking branch 'upstream/release/2.5' into fix/autowire-run-env 2026-09-25 00:44:04 -03:00
AkitaOnRailsandClaude Opus 4.8 f04962ef69 Merge #708 slice 2 (inert per-project-authz) into release/2.5
feat(auth): inert project-grant schema and authorize_project choke point (#708)
Migration renumbered to V68 (V67 is #880's managed_run_session_link).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MDbhmszrjG9s5MrPrTuNtm
2026-09-25 00:36:06 -03:00
AkitaOnRails 73772bf42c Merge release/2.5 into feat/708-authz-slice2b
Bring the inert per-project-authz slice up to current release/2.5 before landing (migration renumbers off #880's V67).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MDbhmszrjG9s5MrPrTuNtm

# Conflicts:
#	docs/security-boundaries.md
2026-09-25 00:30:31 -03:00
AkitaOnRailsandClaude Opus 4.8 b6c39804ad fix(hooks): pass turn_checkpoint to build_auto_handoff in #895's tests after forward-merge
The forward-merge combined main's #895 tests (added when build_auto_handoff took
7 args) with release/2.5's #869/#883 turn_checkpoint parameter (the 8th). Update
the two new test callers to pass turn_checkpoint=false.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MDbhmszrjG9s5MrPrTuNtm
2026-09-25 00:24:28 -03:00
AkitaOnRails 8f55486e5f Merge branch 'main' into release/2.5
Forward-merge the 2.4.x fix batch (#877,#891,#893,#888,#820,#885,#886,#884,#890,#894,#895) so main stays a subset of release/2.5.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MDbhmszrjG9s5MrPrTuNtm

# Conflicts:
#	CHANGELOG.md
#	crates/ai-memory-cli/src/commands/run.rs
#	crates/ai-memory-store/tests/suite/multi_session.rs
2026-09-25 00:22:09 -03:00
AkitaOnRailsandClaude Opus 4.8 8629adc665 fix(consolidate): do not append .md to an empty path component (#885)
Appending the extension to an empty final component fabricated `.md` from a
missing path, which slipped past auto-improve's empty-path rejection and
surfaced as `unsupported_path_prefix` instead of `invalid_path`. Skip the
append when the final component is empty so a missing/invalid path stays empty
and is rejected upstream as before.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MDbhmszrjG9s5MrPrTuNtm
2026-09-25 00:11:56 -03:00
AkitaOnRails fe5b6ca789 Merge fix/885-886-884-consolidation into main
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MDbhmszrjG9s5MrPrTuNtm

# Conflicts:
#	CHANGELOG.md
2026-09-25 00:07:00 -03:00
Éverton Toffanetto 66b7567aae fix(uninstall): clear auto-wire sentinels and keep the hook bearer for partial uninstalls
`ai-memory uninstall` left the `ai-memory run` auto-wire sentinels behind, so
the next managed launch skipped wiring and captured nothing. Removing hooks or
MCP now deletes every sentinel and lists them in the dry-run plan.
`--only mcp|instructions|skills` no longer deletes the stored hook bearer the
still-installed hooks read.

Refs #820
2026-09-25 00:06:30 -03:00