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:
@@ -3,10 +3,18 @@
|
||||
* Project routing is implicit (server-side); zero flags needed.
|
||||
*/
|
||||
|
||||
import readline from "node:readline";
|
||||
import { colors, printError, printSuccess } from "../branding.js";
|
||||
import { loadConfig } from "../config.js";
|
||||
import { loadConfig, saveConfig } from "../config.js";
|
||||
import { CLI_VERSION } from "../version.js";
|
||||
|
||||
const PII_WARNING = [
|
||||
"",
|
||||
"⚠️ AGENTRUSH memories are PUBLIC — visible to any other player.",
|
||||
" Do not include real names, emails, secrets, work content, or PII.",
|
||||
"",
|
||||
].join("\n");
|
||||
|
||||
const ERROR_HINTS: Record<string, string> = {
|
||||
agentrush_search_first:
|
||||
"Run 3 'mem0 agent-rush search' commands before adding.",
|
||||
@@ -66,7 +74,52 @@ async function callEndpoint(
|
||||
return json;
|
||||
}
|
||||
|
||||
function promptLine(question: string): Promise<string> {
|
||||
const rl = readline.createInterface({
|
||||
input: process.stdin,
|
||||
output: process.stdout,
|
||||
});
|
||||
return new Promise((resolve) => {
|
||||
rl.question(question, (answer) => {
|
||||
rl.close();
|
||||
resolve(answer.trim());
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure the human has acknowledged that AGENTRUSH memories are PUBLIC.
|
||||
*
|
||||
* Interactive (TTY): show the prompt; on "y" persist `agentRush.acknowledgedAt`
|
||||
* so we never ask the same machine twice. On anything else, abort.
|
||||
*
|
||||
* Non-interactive (agent invocation, no TTY): print the warning to stderr
|
||||
* for the human reading the agent's transcript and proceed — agents can't
|
||||
* answer y/N prompts.
|
||||
*/
|
||||
async function ensureWarningAcknowledged(): Promise<void> {
|
||||
const config = loadConfig();
|
||||
if (config.agentRush?.acknowledgedAt) return;
|
||||
|
||||
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
||||
// Agent context: surface the warning to stderr, don't block.
|
||||
console.error(PII_WARNING);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(PII_WARNING);
|
||||
const answer = (await promptLine(" Continue? [y/N]: ")).toLowerCase();
|
||||
if (answer !== "y" && answer !== "yes") {
|
||||
printError("Aborted.");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
config.agentRush.acknowledgedAt = new Date().toISOString();
|
||||
saveConfig(config);
|
||||
}
|
||||
|
||||
export async function cmdAgentRushAdd(content: string): Promise<void> {
|
||||
await ensureWarningAcknowledged();
|
||||
const result = await callEndpoint("/v1/agent-rush/memories/", { content });
|
||||
printSuccess(
|
||||
`Memory submitted (event_id: ${(result as { event_id?: string }).event_id ?? "?"})`,
|
||||
|
||||
@@ -40,11 +40,18 @@ export interface TelemetryConfig {
|
||||
anonymousId: string;
|
||||
}
|
||||
|
||||
export interface AgentRushConfig {
|
||||
// ISO timestamp the human acknowledged the "memories are public" warning.
|
||||
// Empty until first interactive `mem0 agent-rush add`.
|
||||
acknowledgedAt: string;
|
||||
}
|
||||
|
||||
export interface Mem0Config {
|
||||
version: number;
|
||||
defaults: DefaultsConfig;
|
||||
platform: PlatformConfig;
|
||||
telemetry: TelemetryConfig;
|
||||
agentRush: AgentRushConfig;
|
||||
}
|
||||
|
||||
export function createDefaultConfig(): Mem0Config {
|
||||
@@ -69,6 +76,9 @@ export function createDefaultConfig(): Mem0Config {
|
||||
telemetry: {
|
||||
anonymousId: "",
|
||||
},
|
||||
agentRush: {
|
||||
acknowledgedAt: "",
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -103,6 +113,8 @@ export function loadConfig(): Mem0Config {
|
||||
config.defaults.runId = defaults.run_id ?? "";
|
||||
const telemetry = data.telemetry ?? {};
|
||||
config.telemetry.anonymousId = telemetry.anonymous_id ?? "";
|
||||
const agentRush = data.agent_rush ?? {};
|
||||
config.agentRush.acknowledgedAt = agentRush.acknowledged_at ?? "";
|
||||
}
|
||||
|
||||
// Environment variable overrides
|
||||
@@ -143,6 +155,9 @@ export function saveConfig(config: Mem0Config): void {
|
||||
telemetry: {
|
||||
anonymous_id: config.telemetry.anonymousId,
|
||||
},
|
||||
agent_rush: {
|
||||
acknowledged_at: config.agentRush.acknowledgedAt,
|
||||
},
|
||||
};
|
||||
|
||||
fs.writeFileSync(CONFIG_FILE, JSON.stringify(data, null, 2));
|
||||
|
||||
Reference in New Issue
Block a user