The CLI's detect_agent_caller() already canonicalizes the caller (env-var
sniff returning "claude-code", "cursor", etc.) and reports it to PostHog —
but the value never reached the backend, so APIKey.agent_caller stayed
NULL. Join: send it in the bootstrap request body so the platform can
persist it (see mem0ai/platform#2784).
Wire-up:
- bootstrap_via_backend / bootstrapViaBackend gain an agent_caller kwarg,
pass it as request body field "agent_caller"
- init_cmd / init.ts call detect_agent_caller() once and forward
- platform.agent_caller persisted to ~/.mem0/config.json so the local
view matches what the backend stored
No change to created_via — that stays the channel enum ("agent_mode" /
"email" / "api_key"). agent_caller is the orthogonal "who started this".
When saveConfig writes a fresh api_key (e.g. agent-mode bootstrap, OTP
signup), propagate the value into other ecosystem locations that hold
the same key:
- ~/.claude/settings.json::env::MEM0_API_KEY (Claude Code env injection)
- ~/.zshrc / ~/.bashrc / ~/.bash_profile `export MEM0_API_KEY="..."`
Without this, agent-mode bootstrap mints a new shadow into config.json
but the Claude plugin's MCP server keeps using the OLD env-var key —
silent surprise.
Hard guarantees:
1. **Update-only**, never create. If a target file doesn't already
contain a MEM0_API_KEY entry, we leave it alone. The user's
existing setup decides which surfaces are managed; we don't
unilaterally start writing to new files.
2. **Preserve surrounding content.** JSON files keep all other keys.
Shell rc files keep all other lines, comments, and the trailing
newline (regex uses [ \t]* not \s*, which would eat the final \n
when MEM0_API_KEY is the last line of .zshrc).
3. **Atomic writes.** tmpfile + rename, so a crash mid-write leaves
the original intact.
4. **Idempotent.** If the target already has this value, no-op.
5. **Best-effort.** Any IOError in the sync is swallowed; the
canonical config.json write is never blocked by plugin-state.
Implemented identically in Python (plugin_sync.py) and Node
(plugin-sync.ts). Hooked into save_config() / saveConfig() so every
api_key change propagates without any caller plumbing.
Verified on a sandbox copy of real ~/.claude/settings.json and
~/.zshrc: only the MEM0_API_KEY values changed; all 36 other lines
in settings.json and 50+ lines in .zshrc preserved byte-for-byte
including the trailing newline.
Out of scope (deliberate non-changes):
- ~/.codex/config.toml — no mem0 server entry to update
- ~/.cursor/mcp.json — no mem0 server entry to update
- <plugin-install-dir>/.api_key — plugin-managed, different schema
New behavior on `mem0 init`:
- With no `--email`/`--api-key` AND a positive agent signal (`--agent`,
global `--json`/`--agent`, or one of the recognized agent env vars
CLAUDECODE / CURSOR_AGENT / CODEX_CLI / CLINE / CONTINUE / AIDER /
GOOSE / WINDSURF), bootstrap an unattended Agent Mode account via
POST /api/v1/auth/agent_mode/. No email, no OTP, no dashboard.
- With `--email <addr>` AND an existing config that has agent_mode=true,
run the claim device-flow against the existing key instead of minting
a fresh one. The raw API key never leaves the device; backend confirms
claim via the existing CLILoginRequest poll path. Config flips
agent_mode=false and stamps claimed_at on success.
- Bare `mem0 init` with no signal + no TTY still errors out — auto-bootstrap
requires a positive agent signal to avoid surprising pipe-using humans.
New flags:
--agent Force unattended Agent Mode bootstrap.
--source Channel attribution string for signup_source PostHog property.
Config schema extensions on PlatformConfig:
agent_mode, created_via, claimed_at, default_user_id.
Telemetry (M1-M6 from the growth doc):
- cli.init: mode (agent|email|api_key|existing_key), agent_caller,
signup_source, claimed_agent_mode (bool when --email claims an
existing agent-mode config).
- All cli.* events: agent_mode reflects config.platform.agent_mode (the
bootstrap flag), not the output-format flag — per the growth-doc spec.