Skip to content

feat(cli): mm agent register/list/share + hidden debug-resolve#461

Merged
memtomem merged 1 commit intomainfrom
feat/mm-agent-cli-register-list-share
Apr 24, 2026
Merged

feat(cli): mm agent register/list/share + hidden debug-resolve#461
memtomem merged 1 commit intomainfrom
feat/mm-agent-cli-register-list-share

Conversation

@memtomem
Copy link
Copy Markdown
Owner

Summary

Adds operator-facing CLI mirrors of the multi-agent MCP tools so
running multi-agent workflows no longer requires an MCP client. The
hidden debug-resolve subcommand dumps the namespace filter
mem_agent_search would resolve as JSON, given simulated
AppContext state — so multi-agent integration scripts can verify
namespace resolution end-to-end without standing up an MCP server.

Stacked on top of #459 (PR-2: session→agent_id inheritance) because
debug-resolve reuses the _resolve_agent_namespace helper added
there. Merge order: #459 first, then this PR.

Commands

  • mm agent register <id> [--description ...] [--color ...] — mirrors
    mem_agent_register. Sanitizes the agent_id, creates the
    agent-runtime:<id> namespace, ensures the cross-agent shared
    namespace exists.
  • mm agent list [--json] — table by default (agents section + shared
    section); --json for scripts. Non-agent namespaces (default,
    claude-memory:*, etc.) are intentionally not surfaced — the
    command is scoped to the multi-agent surface.
  • mm agent share <chunk-id> [--target ...] — mirrors mem_agent_share.
    Reuses _build_shared_tags from fix(multi-agent): admit mem_agent_share is a copy + add shared-from audit tag #458 (with a fallback for branches
    that pre-date it) so the audit-tag dedup contract stays in lock-step.
  • mm agent debug-resolve [--agent-id ...] [--current-agent-id ...] [--current-namespace ...] [--include-shared/--no-include-shared]
    — hidden, JSON-only. Re-uses _resolve_agent_namespace so the CLI
    reports exactly what the MCP tool would compute given the same
    inputs. Designed for use in PR-6's integration scripts and the
    manual e2e in the multi-agent guide.

Test plan

  • 14 cases in test_agent_cmd.py:
    • TestAgentRegister — creates NS + ensures shared, skips shared
      when it already exists, rejects empty agent_id.
    • TestAgentList — table grouping, JSON shape, empty state.
    • TestAgentDebugResolve — explicit-id + shared, current_agent_id
      fallback, legacy current_namespace fallback, all-None → null
      filter.
  • CliRunner mocks cli_components per
    feedback_clirunner_isatty_seam guidance.
  • uv run pytest -m "not ollama" — 2349 passed, 46 deselected.
  • uv run ruff check + ruff format --check — clean.

The share command itself is exercised end-to-end once PR-6 lands
(needs real index_engine wiring); the unit-style coverage above
guards the command-construction surface.

🤖 Generated with Claude Code

memtomem pushed a commit that referenced this pull request Apr 24, 2026
Addresses three nits from the PR #457 review:

1. Drop the unused CLAUDE_/GEMINI_/CODEX_MEMORY_NAMESPACE_PREFIX exports
   from `constants.py`. Promoting them while `cli/ingest_cmd.py` still
   hardcodes the literals would have created a *new* parallel-literal
   seam — exactly what `feedback_drift_close_must_derive` warns against.
   Wiring the ingest CLI is a separate change, not this PR's scope.

2. Drop the `_AGENT_NAMESPACE_PREFIX = AGENT_NAMESPACE_PREFIX` re-export
   alias in `multi_agent.py`. The "Re-export for callers that previously
   imported the local-private symbol" comment was misleading —
   underscore-private symbols have no external callers by definition,
   and grep confirms zero in-repo references on this branch. Stacked
   PRs (#459 / #461) that import the underscore-private symbol get
   updated when they rebase onto this PR; the public
   `AGENT_NAMESPACE_PREFIX` is the right import target.

3. Update the comment above `config.search.system_namespace_prefixes =
   ["archive:"]` in `test_trust_ux.py:68`. After this PR's default flip
   the override is *narrowing* (default has both `archive:` and
   `agent-runtime:`), not "spelling out" — the new comment says so and
   notes that multi-agent isolation has its own coverage in
   `test_multi_agent`.

No behavior change. 2335 tests still pass.

Co-Authored-By: Claude <[email protected]>
@memtomem
Copy link
Copy Markdown
Owner Author

Merge-order coordination note (raised in #457 review): once #457
lands on main, this PR should rebase and replace the local
_CURRENT_PREFIX = "agent-runtime:" and _SHARED_NAMESPACE = "shared"
constants in cli/agent_cmd.py with imports from memtomem.constants
(which #457 introduces as the canonical source for AGENT_NAMESPACE_PREFIX
and SHARED_NAMESPACE). The hardcoded literals in this PR were
necessary because the branch was opened before constants.py existed,
but leaving them after #457 lands would re-create the parallel-literal
seam that #457's feedback_drift_close_must_derive audit just closed.

I'll handle the rebase + literal replacement before requesting merge here.

memtomem added a commit that referenced this pull request Apr 24, 2026
* fix(multi-agent): hide agent-runtime: from default mem_search

`search.system_namespace_prefixes` default was `["archive:"]` only, so
agent A's private chunks (created by `mem_agent_register` /
`mem_agent_search`) leaked into agent B's `mem_search(namespace=None)`
results — directly contradicting the namespace-based isolation
guarantee documented on the multi-agent guide.

Extends the default to `["archive:", "agent-runtime:"]` and consolidates
the literal in a new `memtomem.constants` module so config, MCP tools,
and CLI all derive the prefix from one source. `mem_agent_search` is
unaffected because it builds an explicit namespace filter; the hidden
count surfaces through the existing `hidden_system_ns` hint.

Behavior change: callers relying on plain `mem_search` to read
`agent-runtime:*` chunks must now either pin `namespace=` or override
`search.system_namespace_prefixes: []` in `config.json`. Documented
under [Unreleased] / Changed.

Threat model: `agent-runtime:<id>` is a *convenience* isolation
boundary, not a security boundary. Direct storage access (raw
`namespace=`, ingest `--namespace`, list_namespaces) still reaches
private chunks; this PR closes the default-search UX leak only.

Test surface (test_multi_agent.py):
- `TestDefaultIsolation` pins constants, default factory freshness,
  and `Mem2MemConfig` default — derives from the constant per
  `feedback_pin_test_constant_over_source_scan`.
- `TestAgentRuntimeIsolationPipeline` end-to-end: `agent-runtime:planner`
  chunks excluded from `namespace=None` search but reachable via
  pinned `namespace=`; `shared` namespace stays surfaceable.

Co-Authored-By: Claude <[email protected]>

* review fixup: drop dead alias/symbols + correct trust_ux comment

Addresses three nits from the PR #457 review:

1. Drop the unused CLAUDE_/GEMINI_/CODEX_MEMORY_NAMESPACE_PREFIX exports
   from `constants.py`. Promoting them while `cli/ingest_cmd.py` still
   hardcodes the literals would have created a *new* parallel-literal
   seam — exactly what `feedback_drift_close_must_derive` warns against.
   Wiring the ingest CLI is a separate change, not this PR's scope.

2. Drop the `_AGENT_NAMESPACE_PREFIX = AGENT_NAMESPACE_PREFIX` re-export
   alias in `multi_agent.py`. The "Re-export for callers that previously
   imported the local-private symbol" comment was misleading —
   underscore-private symbols have no external callers by definition,
   and grep confirms zero in-repo references on this branch. Stacked
   PRs (#459 / #461) that import the underscore-private symbol get
   updated when they rebase onto this PR; the public
   `AGENT_NAMESPACE_PREFIX` is the right import target.

3. Update the comment above `config.search.system_namespace_prefixes =
   ["archive:"]` in `test_trust_ux.py:68`. After this PR's default flip
   the override is *narrowing* (default has both `archive:` and
   `agent-runtime:`), not "spelling out" — the new comment says so and
   notes that multi-agent isolation has its own coverage in
   `test_multi_agent`.

No behavior change. 2335 tests still pass.

Co-Authored-By: Claude <[email protected]>

---------

Co-authored-by: pandas-studio <[email protected]>
Co-authored-by: Claude <[email protected]>
@memtomem memtomem force-pushed the fix/multi-agent-session-inherits-agent-id branch from dc59cc0 to a1f47e4 Compare April 24, 2026 21:51
@memtomem memtomem force-pushed the feat/mm-agent-cli-register-list-share branch from 651e4f6 to 4e57536 Compare April 24, 2026 21:53
@memtomem
Copy link
Copy Markdown
Owner Author

Done — rebased onto the post-#457 main (via the rebased #459 base) and
replaced the local literals with imports from memtomem.constants:

  • _SHARED_NAMESPACE = "shared" → dropped; SHARED_NAMESPACE imported
    from memtomem.constants and used at all 9 call sites
    (get_namespace_meta, click --target default, debug-resolve filter
    composition, etc.).
  • _CURRENT_PREFIX kept as a local alias paired with _LEGACY_PREFIX
    so the migration mapping reads as (old, new), but the value derives
    from AGENT_NAMESPACE_PREFIX (no parallel literal).

uv run pytest -m "not ollama" — 2355 passed, 46 deselected. CI
re-running on the force-pushed branch.

@memtomem memtomem changed the base branch from fix/multi-agent-session-inherits-agent-id to main April 24, 2026 22:01
Adds operator-facing CLI mirrors of the multi-agent MCP tools so
running multi-agent workflows no longer requires an MCP client. The
hidden `debug-resolve` subcommand dumps the namespace filter
`mem_agent_search` would resolve as JSON, given simulated
AppContext state — so multi-agent integration scripts can verify
namespace resolution end-to-end without standing up an MCP server.

Stacked on top of PR-2 (session→agent_id inheritance) because
`debug-resolve` reuses the `_resolve_agent_namespace` helper added
there.

Commands:

* `mm agent register <id> [--description ...] [--color ...]` — mirrors
  `mem_agent_register`. Sanitizes the agent_id, creates the
  `agent-runtime:<id>` namespace, and ensures the cross-agent `shared`
  namespace exists.
* `mm agent list [--json]` — table by default with two sections
  (agents and shared); `--json` for scripts. Non-agent namespaces
  (`default`, `claude-memory:*`, etc.) are intentionally not
  surfaced — the command is scoped to the multi-agent surface.
* `mm agent share <chunk-id> [--target ...]` — mirrors
  `mem_agent_share`. Reuses `_build_shared_tags` from PR-3 (with a
  fallback for branches that pre-date it) so the audit-tag dedup
  contract stays in lock-step.
* `mm agent debug-resolve [--agent-id ...] [--current-agent-id ...]
  [--current-namespace ...] [--include-shared/--no-include-shared]`
  — hidden, JSON-only. Re-uses `_resolve_agent_namespace` so the CLI
  reports exactly what the MCP tool would compute given the same
  inputs.

Test surface (test_agent_cmd.py): 14 cases total —
- TestAgentRegister: creates NS + ensures shared, skips shared when
  it already exists, rejects empty agent_id.
- TestAgentList: table grouping, JSON shape, empty state.
- TestAgentDebugResolve: explicit-id+shared, current_agent_id
  fallback, legacy current_namespace fallback, all-None → null
  filter.

CliRunner mocks `cli_components` per `feedback_clirunner_isatty_seam`
guidance. The share command is exercised via integration once PR-6
lands (it needs real index_engine wiring).

Co-Authored-By: Claude <[email protected]>
@memtomem memtomem force-pushed the feat/mm-agent-cli-register-list-share branch from 4e57536 to 811714e Compare April 24, 2026 22:07
@memtomem memtomem merged commit 1bf9d2a into main Apr 24, 2026
1 check passed
@memtomem memtomem deleted the feat/mm-agent-cli-register-list-share branch April 24, 2026 22:07
@github-actions github-actions Bot locked and limited conversation to collaborators Apr 24, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants