mirror of
https://github.com/akitaonrails/ai-memory.git
synced 2026-10-02 03:24:46 +08:00
@@ -12,6 +12,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
provider. (#1026)
|
||||
|
||||
### Fixed
|
||||
- Fixed the recommended Linux/macOS Docker-wrapper `install-hooks` path
|
||||
silently installing shell hooks that cannot enforce client-side capture
|
||||
controls such as `[capture] ignore_paths` and allowlist mode. The wrapper now
|
||||
uses its existing checksum-verified native host client for hook installation
|
||||
and refresh, while an explicit `AI_MEMORY_HOOK_PLATFORM=posix|windows` keeps
|
||||
the documented compatibility fallback and warning. Wrapper upgrades refresh
|
||||
that stable native client before rewriting existing hook registrations.
|
||||
(#1002)
|
||||
- Fixed managed Codex sessions losing their startup continuity when a shared
|
||||
Codex app-server daemon reports a finished run's stale `AI_MEMORY_RUN_ID`.
|
||||
SessionStart now recovers only the sole live, undelivered Codex run in the
|
||||
|
||||
@@ -318,6 +318,14 @@ ai-memory install-mcp --client claude-code --apply
|
||||
ai-memory install-hooks --agent claude-code --apply
|
||||
```
|
||||
|
||||
On Linux and macOS, the Docker wrapper runs `install-hooks` through its
|
||||
checksum-verified native host client. The installed hooks therefore enforce
|
||||
client-side capture controls such as `[capture] ignore_paths` and allowlist
|
||||
mode before an event reaches the spool or network. Set
|
||||
`AI_MEMORY_HOOK_PLATFORM=posix` explicitly only when you need the legacy shell
|
||||
compatibility path; the installer warns that path cannot enforce capture
|
||||
policy v1.
|
||||
|
||||
The examples use `docker`; replace it with `podman` on a Podman host. The
|
||||
wrapper automatically uses Podman when Docker is not installed. Set
|
||||
`AI_MEMORY_DOCKER=podman` to force Podman when both engines are available.
|
||||
|
||||
+29
-8
@@ -31,7 +31,7 @@
|
||||
# likewise
|
||||
# AI_MEMORY_NO_TTY=1 force non-interactive even on a real tty
|
||||
# AI_MEMORY_NO_VERSION_CHECK=1 skip the once-per-day update check
|
||||
# AI_MEMORY_NATIVE_BIN native binary used for managed host commands (optional)
|
||||
# AI_MEMORY_NATIVE_BIN native binary used for host commands and hooks (optional)
|
||||
# AI_MEMORY_WRAPPER_URL wrapper release asset override (optional; its
|
||||
# .sha256 companion is required)
|
||||
# AI_MEMORY_WRAPPER_SHA256_URL wrapper checksum URL override (optional)
|
||||
@@ -359,6 +359,11 @@ cmd_upgrade() {
|
||||
echo "→ pulling ${IMAGE}"
|
||||
"${DOCKER}" pull "${IMAGE}"
|
||||
rm -f "${NATIVE_RUNNER_DIR}/last-check"
|
||||
echo "→ refreshing checksum-verified native host client"
|
||||
native_host_binary >/dev/null || {
|
||||
echo "ai-memory: upgrade stopped before hook refresh because the native host client could not be updated" >&2
|
||||
return 1
|
||||
}
|
||||
|
||||
local found_agents=()
|
||||
if [ -d "${HOOKS_STAGE_DIR}" ]; then
|
||||
@@ -466,6 +471,10 @@ native_host_binary() {
|
||||
echo "ai-memory: AI_MEMORY_NATIVE_BIN is not executable: ${AI_MEMORY_NATIVE_BIN}" >&2
|
||||
return 1
|
||||
}
|
||||
if [ "${AI_MEMORY_NATIVE_BIN}" -ef "$0" ]; then
|
||||
echo "ai-memory: AI_MEMORY_NATIVE_BIN points to the container wrapper itself; use the native release binary" >&2
|
||||
return 1
|
||||
fi
|
||||
printf '%s\n' "${AI_MEMORY_NATIVE_BIN}"
|
||||
return 0
|
||||
fi
|
||||
@@ -475,8 +484,8 @@ native_host_binary() {
|
||||
Linux) os="linux" ;;
|
||||
Darwin) os="macos" ;;
|
||||
*)
|
||||
echo "ai-memory: the container wrapper cannot provide managed host launches on this OS" >&2
|
||||
echo "install the native release binary to use ai-memory run/show/continue/resume/workstreams/rename-workstream" >&2
|
||||
echo "ai-memory: the container wrapper cannot provide native host commands on this OS" >&2
|
||||
echo "install the native release binary to use managed launches or native lifecycle hooks" >&2
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
@@ -564,6 +573,19 @@ case "${1:-}" in
|
||||
NATIVE_HOST_BIN=$(native_host_binary)
|
||||
exec "${NATIVE_HOST_BIN}" "${NATIVE_HOST_COMMAND}" "$@"
|
||||
;;
|
||||
install-hooks)
|
||||
# Capture exclusions and allowlist mode are client-side privacy controls.
|
||||
# The wrapper's stable, checksum-verified host client can enforce them;
|
||||
# the helper-container script bundle cannot. Keep explicit compatibility
|
||||
# selections available, but never choose the weaker path by default.
|
||||
case "${AI_MEMORY_HOOK_PLATFORM:-}" in
|
||||
posix | windows) ;;
|
||||
*)
|
||||
NATIVE_HOST_BIN=$(native_host_binary)
|
||||
exec "${NATIVE_HOST_BIN}" "$@"
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
esac
|
||||
|
||||
# ---- normal pass-through to the binary inside a container ----------------
|
||||
@@ -578,11 +600,10 @@ if [ -z "${AI_MEMORY_NO_TTY:-}" ] && [ -t 0 ] && [ -t 1 ]; then
|
||||
TTY_ARGS+=(-t)
|
||||
fi
|
||||
|
||||
# The CLI runs INSIDE the container but renders hook config for the HOST, which
|
||||
# has no local ai-memory binary. Native installs default to `posix-native` (the
|
||||
# binary hook command); the wrapper must NOT inherit that or it would bake the
|
||||
# *container's* binary path into the host's hook config. Force the shell-script
|
||||
# platform (`posix`) unless the operator chose one explicitly.
|
||||
# Commands that still reach the helper container must never bake its binary
|
||||
# path into host config. Ordinary `install-hooks` was intercepted above and
|
||||
# uses the stable native host client; an explicit script compatibility choice
|
||||
# reaches here and remains script-based.
|
||||
export AI_MEMORY_HOOK_PLATFORM="${AI_MEMORY_HOOK_PLATFORM:-posix}"
|
||||
|
||||
ENV_ARGS=()
|
||||
|
||||
+29
-22
@@ -479,10 +479,13 @@ a script fallback.
|
||||
|
||||
`[capture] ignore_paths` is enforced only by native `ai-memory hook` commands
|
||||
and generated OpenCode/OMP/Pi/OpenClaw integrations. Local installers select
|
||||
native commands where supported; legacy `.sh`/`.ps1` hooks and remote-only or
|
||||
Docker script bundles do not enforce it. Re-run `install-hooks --agent <agent>
|
||||
--apply` or refresh/reinstall generated plugins after upgrading; installer
|
||||
capability output reflects the selected integration. See the canonical
|
||||
native commands where supported. The Linux/macOS Docker wrapper also routes
|
||||
ordinary `install-hooks` through its checksum-verified native host client, so
|
||||
the quick-start path is covered. Legacy `.sh`/`.ps1` hooks, an explicit
|
||||
`AI_MEMORY_HOOK_PLATFORM=posix|windows`, `setup-agent`, and remote-only/manual
|
||||
Docker script bundles do not enforce it. Re-run `install-hooks --agent
|
||||
<agent> --apply` or refresh/reinstall generated plugins after upgrading;
|
||||
installer capability output reflects the selected integration. See the canonical
|
||||
[capture exclusions reference](marker-file.md#capture-exclusions).
|
||||
|
||||
Lifecycle observation bodies are bounded separately from the 10 MiB HTTP
|
||||
@@ -538,9 +541,10 @@ Windows, so the usual install is covered. It is the *script* installs that are
|
||||
not: the bundled shell/PowerShell hooks POST to the server directly and never
|
||||
execute the binary, so nothing reads the mode. In practice that means the
|
||||
legacy `posix`/`windows` platform override (`AI_MEMORY_HOOK_PLATFORM`), the
|
||||
Docker host wrapper, and `setup-agent` snippets, which emit script commands by
|
||||
design. `install-hooks --apply` prints the mode and warns when the install it
|
||||
is writing cannot enforce it.
|
||||
remote `setup-agent` flow, and manual Docker script-bundle installs. The
|
||||
Linux/macOS Docker wrapper's ordinary `install-hooks` path uses its native host
|
||||
client and is covered. `install-hooks --apply` prints the mode and warns when
|
||||
the install it is writing cannot enforce it.
|
||||
|
||||
Within that boundary the mode is not per-agent: it is stored once in the data
|
||||
directory and every native hook command reads it, whichever agent invoked it.
|
||||
@@ -607,17 +611,14 @@ it flows (consolidation/reviewer prompts, and out to a cloud LLM provider if one
|
||||
is configured) before enabling it.
|
||||
|
||||
Upgrading the binary is sufficient for native Claude Code installs, and pending
|
||||
spooled events drain with the raw field stripped as well. Installs that run the
|
||||
`.sh`/`.ps1` script fallback (the Docker script bundle or an explicit
|
||||
`AI_MEMORY_HOOK_PLATFORM=posix`) cannot sanitize the assistant text, so a `Stop`
|
||||
payload still carrying the raw field is dropped whole by the script rather than
|
||||
POSTed verbatim. The Docker wrapper deliberately keeps script commands because a
|
||||
binary path inside its helper container is not valid on the host; running
|
||||
`install-hooks` through that wrapper refreshes the scripts but does not convert
|
||||
them. To capture assistant text safely, install a native ai-memory client on the
|
||||
agent host, then use that native executable to run
|
||||
`install-hooks --agent claude-code --apply`. Even if the script fallback is
|
||||
retained, the server still strips any raw field on receipt before persistence.
|
||||
spooled events drain with the raw field stripped as well. The Linux/macOS Docker
|
||||
wrapper installs the same native hook command through its checksum-verified
|
||||
host client. Installs that explicitly select the `.sh`/`.ps1` compatibility
|
||||
fallback (`AI_MEMORY_HOOK_PLATFORM=posix|windows`), or use a remote/manual
|
||||
Docker script bundle, cannot sanitize the assistant text, so a `Stop` payload
|
||||
still carrying the raw field is dropped whole by the script rather than POSTed
|
||||
verbatim. Even when that fallback is retained, the server strips any raw field
|
||||
on receipt before persistence.
|
||||
|
||||
Native `ai-memory hook --event ...` commands spool events locally. The POSIX
|
||||
shell bundle spools too, but only on failure: it POSTs first and writes the
|
||||
@@ -799,7 +800,9 @@ including Pi and Zero, have lifecycle capture paths through `install-hooks`.
|
||||
> real `ai-memory.exe`, `args` = argv tokens for `hook --event ...`); other
|
||||
> agents use native single command strings according to their hook schema.
|
||||
> PowerShell/Git Bash script bundles are compatibility fallbacks and do not
|
||||
> enforce capture-policy v1. Remote-only/Docker script installs still use the
|
||||
> enforce capture-policy v1. The Linux/macOS Docker wrapper downloads a
|
||||
> checksum-verified native host client for ordinary `install-hooks` calls.
|
||||
> Remote-only/manual Docker script installs still use the
|
||||
> two-step path: (1) `docker cp` bundled scripts to your home dir, (2)
|
||||
> `docker run --rm install-hooks` renders the config snippet.
|
||||
> OpenClaw, OpenCode, OMP, and Pi are different: they use generated
|
||||
@@ -2508,7 +2511,11 @@ ai-memory install-hooks --agent claude-code --apply
|
||||
```
|
||||
|
||||
The installed Docker wrapper runs CLI commands inside a short-lived
|
||||
helper container. For local loopback servers, it automatically bridges
|
||||
helper container for server/store operations, but runs `install-hooks` through
|
||||
the stable checksum-verified native client under
|
||||
`~/.local/share/ai-memory/native-runner/`. This keeps hook command paths valid
|
||||
on the host and enforces capture exclusions before spooling. For local loopback
|
||||
servers, the wrapper automatically bridges
|
||||
that helper back to the host's `127.0.0.1:49374`, so `ai-memory status`,
|
||||
`ai-memory search`, and `ai-memory bootstrap` work with the same default
|
||||
URL as the generated agent config.
|
||||
@@ -2585,8 +2592,8 @@ ai-memory upgrade
|
||||
|
||||
The command downloads the wrapper and its SHA-256 checksum from the latest
|
||||
GitHub Release, refuses an unverified update, pulls the latest Docker
|
||||
image, re-stages hook scripts under
|
||||
`~/.local/share/ai-memory/hooks/<agent>/` for configured agents, and
|
||||
image, refreshes the checksum-verified native host client, rewrites previously
|
||||
staged hook registrations to that native command when supported, and
|
||||
prints how to restart the server container so the new binary is used.
|
||||
Re-running `install-hooks --apply` remains idempotent: ai-memory
|
||||
replaces only the hook entries it owns and leaves unrelated hooks alone.
|
||||
|
||||
+5
-3
@@ -37,9 +37,11 @@ normal Terminal.
|
||||
which works from the host agent.
|
||||
- Hooks are rendered for one of two platforms:
|
||||
- `posix-native` — a direct `ai-memory hook --event …` call. The default for
|
||||
native macOS/Linux Claude Code installs (cargo / release binary); it uses
|
||||
the local event spool + OIDC-token fallback.
|
||||
- `posix` — `sh` runs the bundled `.sh` script. The Docker wrapper's default.
|
||||
native macOS/Linux Claude Code installs (cargo / release binary) and the
|
||||
Docker wrapper's checksum-verified host client; it uses the local event
|
||||
spool + OIDC-token fallback and enforces capture policy v1.
|
||||
- `posix` — `sh` runs the bundled `.sh` script. This is an explicit
|
||||
compatibility fallback for the Docker wrapper.
|
||||
|
||||
Set `AI_MEMORY_HOOK_PLATFORM` before wiring hooks to override the default.
|
||||
|
||||
|
||||
+7
-4
@@ -414,10 +414,13 @@ Capture policy v1 is enforced by native `ai-memory hook` commands (including
|
||||
native POSIX/Windows hook commands) and generated OpenCode, OMP, Pi, and
|
||||
OpenClaw integrations, including the lexical shell-command matching above.
|
||||
Local installers default to native commands where that
|
||||
path is supported. Legacy `.sh`/`.ps1` hooks and remote-only/Docker script
|
||||
bundles do **not** enforce it. Reinstall hooks or refresh/reinstall generated
|
||||
plugins after upgrading; existing hooks/plugins keep their prior behavior.
|
||||
Installer capability output describes the selected integration.
|
||||
path is supported. The Linux/macOS Docker wrapper's ordinary `install-hooks`
|
||||
path uses its checksum-verified native host client and is supported too.
|
||||
Legacy `.sh`/`.ps1` hooks, explicit compatibility overrides, and
|
||||
remote-only/manual Docker script bundles do **not** enforce it. Reinstall hooks
|
||||
or refresh/reinstall generated plugins after upgrading; existing hooks/plugins
|
||||
keep their prior behavior. Installer capability output describes the selected
|
||||
integration.
|
||||
|
||||
New clients remain safe with old servers because stripping and dropping happen
|
||||
on the client. Old clients talking to new servers retain old behavior and cannot
|
||||
|
||||
+4
-1
@@ -35,7 +35,10 @@ stdout (or their equivalent context-injection result); Grok and Zero must call
|
||||
Capture exclusions are separate from MCP registration. Native hook commands and
|
||||
generated OpenCode/OMP/Pi/OpenClaw integrations enforce `[capture]
|
||||
ignore_paths`; legacy shell/PowerShell and remote-only/Docker script bundles do
|
||||
not. Reinstall/refresh an existing hook or plugin to gain it; see
|
||||
not. The Linux/macOS Docker wrapper's ordinary `install-hooks` command uses its
|
||||
checksum-verified native host client and is covered; this limitation applies to
|
||||
manual container/script extraction and explicit compatibility overrides.
|
||||
Reinstall/refresh an existing hook or plugin to gain it; see
|
||||
[Capture exclusions](marker-file.md#capture-exclusions).
|
||||
|
||||
Claude Desktop, VS Code Copilot, Zed, and Muse Code are **MCP-only** here:
|
||||
|
||||
@@ -53,6 +53,7 @@ boundary not yet built.
|
||||
| 10f | Session-time repair is scope-bound (no cross-project timestamp rewrite) | `ai-memory-store/src/ops.rs` `repair_session_times` — every candidate is read back and updated with `WHERE id=? AND workspace_id=? AND project_id=?`; a row outside that 3-tuple is reported `NotFound` and untouched, same code path as an id that does not exist at all; `ai-memory-mcp/src/admin.rs` `handle_repair_session_times` passes the caller's `(workspace, project)` straight through, never the candidate's own | `ops.rs` `repair_session_times_does_not_touch_a_session_of_another_project` (control: sibling session in-scope repaired in the same batch); `admin_repair_session_times.rs` `cross_project_session_is_not_found_and_untouched`; both bite-checked by dropping the `project_id` filter | STRONG |
|
||||
| 11a | Hook backpressure (202/429) + bounded fan-out (invariant #5) | `ai-memory-hooks/src/router.rs` semaphore→429, 202 immediately, `MAX_HOOK_BATCH_ITEMS`, bounded LRU limiter | `router.rs` `handle_hook_returns_429_when_ingest_saturated`, `ingest_rate_limiter_is_bounded` | STRONG |
|
||||
| 11b | Capture exclusions drop before storage | `ai-memory-hooks` `capture_policy.rs` `inspect`→`Drop` (before semaphore/spawn), including shell commands whose arguments name an ignored path (`match_command`; argv elements matched whole and tokenized); an invalid marker makes file and shell calls metadata-only, and the server admits a metadata-only shell body only under an invalid marker (`router.rs` `metadata_protocol_is_legal`); generated OpenCode/OMP/Pi/OpenClaw integrations mirror it in `render_shared.rs` `ts_capture_policy_v1` (`captureMatchCommand`). Candidate/argument flavor (`Flavor::Posix` vs `Flavor::Windows`, used to pick which `ignore_paths` patterns even apply) is derived from the **host** (the cwd), never from the candidate string alone — a POSIX-host candidate spelled with a leading `//` is collapsed to a single `/` before flavor detection (`normalize_candidate`; TS `captureNormalize`'s `windowsHost` parameter, sourced from `captureHostWindows(cwd)`), fixing GHSA-vh98 (a `//`-prefixed candidate used to self-classify as `Flavor::Windows` regardless of host, matching zero POSIX patterns and being captured instead of dropped); a genuine Windows/UNC host (cwd itself windows-flavored) is unaffected | `capture_policy.rs` per-agent `…honors_exclusions` tests, `shell_fixture_vectors` (shared `capture-policy.json` `shell` drop/keep vectors: tool aliases incl. OpenClaw/Devin `exec`, `workdir`, glob directories, Windows paths, and a POSIX-host leading-`//` command via `/{root}/docs/adr/x.md`), `shell_tool_shapes_of_every_adapter_honor_exclusions`, `fixture_vectors` (incl. TS-adapter `bash`/`exec` vectors, and `normalization`'s leading-`//` POSIX vectors alongside the kept Windows-cwd UNC vectors as the no-regression control); `shell_matching_is_off_when_inactive_and_fails_closed_when_invalid_or_over_budget`; `router.rs` `capture_protocol_shell_decisions_survive_server_reinspection`, `capture_protocol_invalid_marker_shell_is_metadata_only` (active metadata-only shell refused, older client's invalid-marker keep stripped, commandless control kept), `capture_protocol_unparseable_marker_strips_shell_events` (server fallback for an unparseable marker), `capture_protocol_invalid_shell_metadata_claim_must_be_canonical` (a stripped shell claim with a path count or non-`extracted` state is refused); `capture_policy.rs` `invalid_marker_strips_shell_calls_with_unparseable_commands`, `long_bash_lc_script_in_argv_is_not_dropped_by_the_match_budget`, **`a_leading_double_slash_candidate_does_not_escape_posix_ignore_paths_via_flavor_mismatch`** (GHSA-vh98 adversarial: attempts the `//repo/secret/...` violation on a POSIX host, proves it now drops, with a plain-single-slash control and a Windows-hosted genuine-UNC control both still correct — fails on the pre-fix code); `hook.rs` `shell_command_reading_an_ignored_path_is_dropped_before_spool`; `render_shared.rs` `generated_capture_policy_v1_node_runtime_evidence` (runs the same fixture sections, including the GHSA-vh98 vectors, against the emitted TypeScript; `#[ignore]` locally, run with `--ignored` under Node 24 by the Linux CI test job) | STRONG |
|
||||
| 11c | Docker-wrapper capture-policy capability (#1002) | `bin/ai-memory` routes ordinary Linux/macOS `install-hooks` and upgrade refreshes through the stable checksum-verified native host client, so the hook process executes the capture-policy boundary in 11b before spool/network. Only an explicit `AI_MEMORY_HOOK_PLATFORM=posix|windows` retains the documented script compatibility path and its warning; `AI_MEMORY_NATIVE_BIN` may not point back to the wrapper | `scripts/check-native-packaging.sh` `Checking host-launch wrapper routing`: a default allowlist install reaches the fake native client while a Docker executable that always fails proves no container fallback occurred; the explicit `posix` control reaches fake Docker and adds no native invocation; the self-referential native override is refused | STRONG on the shipped Linux/macOS wrapper. Windows and remote/manual script installers remain explicitly outside capture-policy v1 |
|
||||
| 11c | Capture hook ≤200ms budget (invariant #5) | `hooks/_lib.sh` capture path `curl --max-time 0.2` (context-fetch 1.0s and background drain 2.0s are separate, larger-budget paths) | none (shell-script timeout; hard to unit-test) — watch on any capture-path change | WATCH |
|
||||
| 11d | Hook server-profile routing: a marker-selected server gets only its own capture and its own token (#992) | `ai-memory-cli/src/server_profiles.rs` `resolve` (validated `ProfileName`, strict `servers.toml` parse, `roots` required once two profiles exist, component-wise root match) and `marker.rs` `find_server_selection` (inherited down the tree, any value shape counts); `commands/hook.rs` `resolve_hook_route` drops a `Rejected` route before spool, handoff and backfill, and hands the drainer no live token for a profile route; `commands/hook_spool.rs` `static_retry_token` (a profile entry retries only with its own stored token), the loopback reroot skip, and chunk splitting on `profile`; generated TS `captureServerRouted` drops a routed repository and gates `fetchHandoff`; `hooks/_lib.sh` `ai_memory_server_routed` (flag refused by `ai_memory_post_hook`/`ai_memory_get_handoff`) and `hooks/lib/ai-memory-hook.ps1` `Test-AiMemoryServerRouted` drop it in the script hooks | `hook.rs` `each_repository_spools_to_its_own_profile_with_its_own_token`, `a_selection_that_does_not_resolve_emits_nothing` (unknown / tokenless / outside roots / roots required / invalid name, plus a resolving control), `session_start_handoff_comes_from_the_profile_server_only`, `a_repository_without_a_server_key_keeps_the_install_default`; `hook_spool.rs` `a_profile_entry_is_never_retried_with_the_install_live_token` (server B accepts exactly the install's live token and must still not get it), `a_profile_entry_recovers_with_its_own_rotated_token` (control), `a_profile_entry_on_a_dead_loopback_port_is_not_rerouted_to_the_default`, `profile_and_default_entries_at_one_address_ride_separate_batches`; `server_profiles.rs` roots/registry/name tests; `marker.rs` `nested_markers_without_server_inherit_the_ancestor_selection`; `install_hooks.rs` `generated_integrations_fail_closed_on_a_server_profile_marker`, `openclaw_plugin.rs` `openclaw_plugin_fails_closed_on_a_server_profile_marker`, and the `server-routed-*` checks in `generated_capture_policy_v1_node_runtime_evidence`; `hook.rs` `an_event_without_a_payload_cwd_routes_by_the_process_cwd`, `a_refused_route_prints_nothing_for_kimi_user_prompts`; `hook_spool.rs` `a_profile_entry_is_not_retried_with_a_token_issued_for_a_new_url`; `server_profiles.rs` `changing_the_url_without_a_token_discards_the_old_token`, `omitted_roots_keep_the_registered_ones`; `marker.rs` `outside_home_the_walk_reaches_a_marker_above_the_checkout_root`, `encoding_noise_cannot_hide_a_server_key`, `an_unreadable_marker_is_a_refused_selection`; `backfill.rs` `a_spawned_backfill_authenticates_like_the_hook_that_spawned_it`; `tests/hooks/test_lib.sh` "server profiles (#992)" section; `hook.rs` `a_mixed_spool_drains_each_event_only_to_its_own_server` (two token-gated servers, one spool, one drain: each server receives exactly its own event with exactly its own bearer); `tests/suite/server_profiles.rs` (the built binary: `server add` → `hook` spools to the profile with its token, unknown profile spools nothing, `uninstall` removes the tokens; `two_real_servers_each_receive_only_their_own_repository` runs two real `ai-memory serve` children with different root tokens and checks on each server which repository landed there); `ai-memory-hooks` `powershell_server_routed.rs` `server_routed_guard_mirrors_the_native_walk` (runs `Test-AiMemoryServerRouted` under real PowerShell: inherited, BOM, look-alike keys, the `$HOME` boundary with its control, past a checkout root outside home, current directory); `tests/hooks/test_lib.sh` and `powershell_home.rs` `a_trailing_separator_on_home_keeps_the_walk_boundary` (a `$HOME` ending in `/` or `\` keeps both script walks at home: a `server` marker above it routes nothing, with a root-home control that does) | STRONG for native hooks, generated TS, `.sh` and `.ps1` hooks. Known gap: an older binary draining a shared spool ignores `profile` |
|
||||
| 12 | Network/auth posture | `config.rs` loopback `DEFAULT_BIND`; `serve.rs` `validate_http_exposure`, `require_allowed_host`; `auth.rs` `require_bearer` | `serve.rs` host-guard (missing→400 / forged→403), non-loopback-requires-token; `auth.rs` wrong-token 401 | STRONG |
|
||||
|
||||
+5
-5
@@ -541,15 +541,15 @@ native on an i7-6700HQ). Notes:
|
||||
- `windows-bash` — `bash -c` + `.sh` through Git Bash (the previous
|
||||
default; set this to opt back in, or as a fallback for older Claude Code
|
||||
builds that do not support exec form).
|
||||
- `posix` — POSIX `.sh`. The Linux/macOS Docker-wrapper default (the host has
|
||||
no local binary); set it explicitly to opt a native install back into the
|
||||
scripts.
|
||||
- `posix` — POSIX `.sh`. An explicit Linux/macOS Docker-wrapper compatibility
|
||||
fallback; set it to opt a native install back into the scripts.
|
||||
- `posix-native` — direct binary call on macOS / Linux (`<exe> hook
|
||||
--event …`) instead of the `.sh` script, so the hook uses the local event
|
||||
spool + OIDC-token fallback. The **default for native macOS / Linux
|
||||
Claude Code installs** (cargo / release binary), mirroring
|
||||
`windows-native`. The Linux/macOS Docker wrapper forces `posix`, so its
|
||||
host-rendered config keeps the `.sh` scripts.
|
||||
`windows-native`. The Linux/macOS Docker wrapper uses its
|
||||
checksum-verified native host client by default; set `posix` explicitly to
|
||||
keep the `.sh` scripts.
|
||||
|
||||
Set the env var before running `install-hooks` so the chosen platform
|
||||
is baked into the rendered hook commands.
|
||||
|
||||
@@ -88,13 +88,16 @@ main() {
|
||||
trap cleanup EXIT
|
||||
|
||||
log "Checking host-launch wrapper routing"
|
||||
local fake_docker fake_native wrapper_log
|
||||
local fake_docker fake_compat_docker fake_native wrapper_log docker_log native_lines
|
||||
fake_docker="${TMP_ROOT}/forbidden-docker"
|
||||
fake_compat_docker="${TMP_ROOT}/fake-compat-docker"
|
||||
fake_native="${TMP_ROOT}/fake-ai-memory"
|
||||
wrapper_log="${TMP_ROOT}/wrapper.log"
|
||||
docker_log="${TMP_ROOT}/docker.log"
|
||||
printf '%s\n' '#!/usr/bin/env bash' 'exit 97' >"${fake_docker}"
|
||||
printf '%s\n' '#!/usr/bin/env bash' 'printf '\''%s\n'\'' "$*" >>"${AI_MEMORY_DOCKER_TEST_LOG}"' >"${fake_compat_docker}"
|
||||
printf '%s\n' '#!/usr/bin/env bash' 'printf '\''%s\n'\'' "$*" >>"${AI_MEMORY_WRAPPER_TEST_LOG}"' >"${fake_native}"
|
||||
chmod 0755 "${fake_docker}" "${fake_native}"
|
||||
chmod 0755 "${fake_docker}" "${fake_compat_docker}" "${fake_native}"
|
||||
AI_MEMORY_DOCKER="${fake_docker}" AI_MEMORY_NATIVE_BIN="${fake_native}" \
|
||||
AI_MEMORY_WRAPPER_TEST_LOG="${wrapper_log}" \
|
||||
bin/ai-memory run codex --yolo
|
||||
@@ -110,11 +113,29 @@ main() {
|
||||
AI_MEMORY_DOCKER="${fake_docker}" AI_MEMORY_NATIVE_BIN="${fake_native}" \
|
||||
AI_MEMORY_WRAPPER_TEST_LOG="${wrapper_log}" \
|
||||
bin/ai-memory rename-workstream --from typo-nmae --to refactor-db
|
||||
AI_MEMORY_DOCKER="${fake_docker}" AI_MEMORY_NATIVE_BIN="${fake_native}" \
|
||||
AI_MEMORY_WRAPPER_TEST_LOG="${wrapper_log}" \
|
||||
bin/ai-memory install-hooks --agent claude-code --capture-mode allowlist --apply
|
||||
assert_contains "${wrapper_log}" "run codex --yolo"
|
||||
assert_contains "${wrapper_log}" "show --json --no-scan"
|
||||
assert_contains "${wrapper_log}" "continue --workspace work --yolo"
|
||||
assert_contains "${wrapper_log}" "workstreams --limit 5 --json"
|
||||
assert_contains "${wrapper_log}" "rename-workstream --from typo-nmae --to refactor-db"
|
||||
assert_contains "${wrapper_log}" "install-hooks --agent claude-code --capture-mode allowlist --apply"
|
||||
|
||||
native_lines="$(wc -l <"${wrapper_log}")"
|
||||
AI_MEMORY_DOCKER="${fake_compat_docker}" AI_MEMORY_NATIVE_BIN="${fake_native}" \
|
||||
AI_MEMORY_HOOK_PLATFORM=posix AI_MEMORY_DOCKER_TEST_LOG="${docker_log}" \
|
||||
AI_MEMORY_WRAPPER_TEST_LOG="${wrapper_log}" \
|
||||
bin/ai-memory install-hooks --agent claude-code --apply
|
||||
assert_contains "${docker_log}" "install-hooks --agent claude-code --apply"
|
||||
test "$(wc -l <"${wrapper_log}")" = "${native_lines}" \
|
||||
|| fail "an explicit posix hook install must retain the container/script path"
|
||||
|
||||
if AI_MEMORY_DOCKER="${fake_docker}" AI_MEMORY_NATIVE_BIN="$(pwd)/bin/ai-memory" \
|
||||
bin/ai-memory run codex 2>/dev/null; then
|
||||
fail "AI_MEMORY_NATIVE_BIN pointing to the wrapper must be refused instead of recursing"
|
||||
fi
|
||||
|
||||
log "Creating temporary alternate root"
|
||||
mkdir -p \
|
||||
|
||||
Reference in New Issue
Block a user