feat(cli): warn about public AGENTRUSH memories before first add

Adds a one-time PII warning shown before the first `mem0 agent-rush
add`. Persists acknowledgment in config so we never ask the same
machine twice.

Interactive (TTY):
  ⚠️  AGENTRUSH memories are PUBLIC — visible to any other player.
     Do not include real names, emails, secrets, work content, or PII.
     Continue? [y/N]
  → on "y" the timestamp lands in `agent_rush.acknowledged_at`.
  → anything else aborts with no add.

Non-interactive (agent invocation, no TTY): the warning prints to
stderr (visible in the agent transcript so the human notices) and
the add proceeds. Agents can't answer y/N; blocking them would defeat
the entire launch flow. Ack is intentionally not persisted in this
path so a later human run still sees the prompt.

Mirrored across Node + Python CLIs with matching JSON schema:
  config.json adds `agent_rush.acknowledged_at: ""` (ISO timestamp).
This commit is contained in:
Mgeeeek
2026-05-20 18:04:24 +05:30
parent abc57323c2
commit 4ca9d13c47
4 changed files with 126 additions and 2 deletions
@@ -6,16 +6,26 @@ Hardcoded routing; no flags needed.
from __future__ import annotations
import sys
from datetime import datetime, timezone
import httpx
import typer
from rich.console import Console
from mem0_cli.branding import BRAND_COLOR, print_error, print_success
from mem0_cli.config import load_config
from mem0_cli.config import load_config, save_config
console = Console()
err_console = Console(stderr=True)
_PII_WARNING_LINES = (
"",
"[yellow]⚠️ AGENTRUSH memories are PUBLIC — visible to any other player.[/yellow]",
"[yellow] Do not include real names, emails, secrets, work content, or PII.[/yellow]",
"",
)
_SOURCE_HEADERS = {
"X-Mem0-Source": "cli",
"X-Mem0-Client-Language": "python",
@@ -69,7 +79,39 @@ def _call(path: str, body: dict) -> dict:
return data
def _ensure_warning_acknowledged() -> None:
"""Block the first interactive add on the PII warning; pass-through for agents.
Interactive (TTY): show prompt, require explicit 'y', persist
`agent_rush.acknowledged_at` so we never ask the same machine twice.
Non-interactive (no TTY — typical when an agent runs the CLI): surface
the warning to stderr for the human reading the agent transcript and
proceed without prompting (agents can't answer y/N).
"""
config = load_config()
if config.agent_rush.acknowledged_at:
return
is_tty = sys.stdin.isatty() and sys.stdout.isatty()
if not is_tty:
for line in _PII_WARNING_LINES:
err_console.print(line)
return
for line in _PII_WARNING_LINES:
console.print(line)
answer = typer.prompt(" Continue? [y/N]", default="N", show_default=False).strip().lower()
if answer not in ("y", "yes"):
print_error(err_console, "Aborted.")
raise typer.Exit(1)
config.agent_rush.acknowledged_at = datetime.now(timezone.utc).isoformat()
save_config(config)
def run_agent_rush_add(content: str) -> None:
_ensure_warning_acknowledged()
result = _call("/v1/agent-rush/memories/", {"content": content})
event_id = result.get("event_id", "?")
print_success(console, f"Memory submitted (event_id: {event_id})")
+14
View File
@@ -51,12 +51,20 @@ class TelemetryConfig:
anonymous_id: str = ""
@dataclass
class AgentRushConfig:
# ISO timestamp the human acknowledged the "memories are public" warning.
# Empty until first interactive `mem0 agent-rush add`.
acknowledged_at: str = ""
@dataclass
class Mem0Config:
version: int = CONFIG_VERSION
defaults: DefaultsConfig = field(default_factory=DefaultsConfig)
platform: PlatformConfig = field(default_factory=PlatformConfig)
telemetry: TelemetryConfig = field(default_factory=TelemetryConfig)
agent_rush: AgentRushConfig = field(default_factory=AgentRushConfig)
SHORT_KEY_ALIASES: dict[str, str] = {
@@ -105,6 +113,9 @@ def load_config() -> Mem0Config:
telemetry = data.get("telemetry", {})
config.telemetry.anonymous_id = telemetry.get("anonymous_id", "")
agent_rush = data.get("agent_rush", {})
config.agent_rush.acknowledged_at = agent_rush.get("acknowledged_at", "")
# Environment variable overrides
env_key = os.environ.get("MEM0_API_KEY")
if env_key:
@@ -158,6 +169,9 @@ def save_config(config: Mem0Config) -> None:
"telemetry": {
"anonymous_id": config.telemetry.anonymous_id,
},
"agent_rush": {
"acknowledged_at": config.agent_rush.acknowledged_at,
},
}
with open(CONFIG_FILE, "w") as f: