From 8aa07c2f62b83511be78fc820963491117a483e2 Mon Sep 17 00:00:00 2001 From: Mgeeeek Date: Thu, 14 May 2026 17:40:52 +0530 Subject: [PATCH] refactor(cli): self-declared agent_caller via --agent-caller flag MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Switch agent identity from env-var sniffing to explicit self-declaration (Proof Editor-style). The agent now passes its own name when running mem0 init --agent --agent-caller . Why: env-var detection was speculative for everything but Claude Code — CLAUDECODE=1 is documented and verified, but CURSOR_AGENT, CODEX_CLI, CLINE_AGENT etc. were plausible-sounding picks without upstream confirmation. Self-declaration is honest, future-proof (no whitelist treadmill as new agents emerge), and matches how Proof handles agent join: explicit identity from the agent itself. CLI changes: - New --agent-caller option on `mem0 init` (Python + Node) - Removed detect_agent_caller() as the source for agent_caller field; still used as a context trigger ("does this look like an agent?") for Rule 3 auto-bootstrap and for telemetry-only event property — identity goes to NULL unless --agent-caller is passed - runInit / run_init accept agent_caller kwarg, forward to backend - PostHog cli.init event uses the self-declared value (not env-sniffed) Companion backend change (mem0ai/platform#2784) drops the whitelist normalizer for an open sanitize so any sensible name is accepted. --- cli/node/src/commands/init.ts | 11 ++++++++--- cli/node/src/index.ts | 5 +++++ cli/python/src/mem0_cli/app.py | 8 +++++++- cli/python/src/mem0_cli/commands/init_cmd.py | 8 ++++++-- 4 files changed, 26 insertions(+), 6 deletions(-) diff --git a/cli/node/src/commands/init.ts b/cli/node/src/commands/init.ts index da5ec95b8..ceec3774d 100644 --- a/cli/node/src/commands/init.ts +++ b/cli/node/src/commands/init.ts @@ -270,6 +270,7 @@ export async function runInit( force?: boolean; agent?: boolean; source?: string; + agentCaller?: string; } = {}, ): Promise { const { detectAgentCaller } = await import("../agent-detect.js"); @@ -282,8 +283,8 @@ export async function runInit( claimed = false, ) => { const props: Record = { command: "init", mode }; - const caller = detectAgentCaller(); - if (caller) props.agent_caller = caller; + // Self-declared via --agent-caller; not sniffed from env vars. + if (opts.agentCaller) props.agent_caller = opts.agentCaller; if (opts.source) props.signup_source = opts.source; if (claimed) props.claimed_agent_mode = true; captureEvent("cli.init", props); @@ -367,9 +368,13 @@ export async function runInit( return; } // Rule 3: mint a fresh shadow (no valid key to reuse). + // agent_caller is self-declared via --agent-caller (Proof Editor-style), + // not derived from env-var sniffing. detectAgentCaller() above is still + // used as a context trigger (does this look like an agent?) but never + // to fill identity. await bootstrapViaBackend(config, { source: opts.source ?? null, - agentCaller: detectAgentCaller(), + agentCaller: opts.agentCaller ?? null, }); fireInit("agent"); return; diff --git a/cli/node/src/index.ts b/cli/node/src/index.ts index d50e9a029..9f4ed3431 100644 --- a/cli/node/src/index.ts +++ b/cli/node/src/index.ts @@ -220,6 +220,10 @@ program "--source ", "Channel attribution for signup (e.g. github, hn, ph).", ) + .option( + "--agent-caller ", + "Self-declared agent identity (e.g. claude-code, cursor). Used with --agent to attribute Agent Mode signups.", + ) // Accept `--json` at the init level too so the PRD-documented form // `mem0 init --agent --json` works without requiring users to move it // before the subcommand. Effect is identical to the global `--json`: @@ -242,6 +246,7 @@ program force: opts.force, agent: opts.agent, source: opts.source, + agentCaller: opts.agentCaller, }); }); diff --git a/cli/python/src/mem0_cli/app.py b/cli/python/src/mem0_cli/app.py index 2f1fe4e4a..59f314918 100644 --- a/cli/python/src/mem0_cli/app.py +++ b/cli/python/src/mem0_cli/app.py @@ -867,6 +867,11 @@ def init( "--source", help="Channel attribution for signup (e.g. github, hn, ph).", ), + agent_caller: str | None = typer.Option( + None, + "--agent-caller", + help="Self-declared agent identity (e.g. claude-code, cursor). Used with --agent to attribute Agent Mode signups.", + ), ) -> None: """Interactive setup wizard for mem0 CLI. @@ -875,7 +880,7 @@ def init( mem0 init --api-key m0-xxx --user-id alice mem0 init --email alice@company.com mem0 init --email alice@company.com --code 482901 - mem0 init --agent # Bootstrap an Agent Mode account (unattended) + mem0 init --agent --agent-caller claude-code # AI agent self-identifies on Agent Mode bootstrap mem0 init --email alice@company.com # Claims an existing Agent Mode key when one is present """ from mem0_cli.commands.init_cmd import run_init @@ -888,6 +893,7 @@ def init( force=force, source=source, agent=agent_signal, + agent_caller=agent_caller, ) diff --git a/cli/python/src/mem0_cli/commands/init_cmd.py b/cli/python/src/mem0_cli/commands/init_cmd.py index 1de422439..5bc32feac 100644 --- a/cli/python/src/mem0_cli/commands/init_cmd.py +++ b/cli/python/src/mem0_cli/commands/init_cmd.py @@ -197,6 +197,7 @@ def run_init( force: bool = False, source: str | None = None, agent: bool = False, + agent_caller: str | None = None, ) -> None: """Interactive setup wizard for mem0 CLI. @@ -223,8 +224,8 @@ def run_init( def _fire_init(mode: str, *, claimed: bool = False) -> None: """Fire cli.init telemetry with M1-M6 properties.""" props: dict = {"command": "init", "mode": mode} - agent_caller = detect_agent_caller() if agent_caller: + # Self-declared via --agent-caller; not sniffed from env vars. props["agent_caller"] = agent_caller if source: props["signup_source"] = source @@ -295,7 +296,10 @@ def run_init( _fire_init("existing_key") return # Rule 3: mint a fresh shadow (no valid key to reuse). - bootstrap_via_backend(config, source=source, agent_caller=detect_agent_caller()) + # agent_caller is the agent's self-declared identity from --agent-caller + # (Proof Editor-style). Env-var auto-detect is still used above to + # decide we're in an agent context, but never to fill identity. + bootstrap_via_backend(config, source=source, agent_caller=agent_caller) _fire_init("agent") return