@mnemom/mnemom 0.17.0-next.6 → 0.17.0-next.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/README.md +26 -0
  2. package/dist/commands/agent-config.d.ts +29 -0
  3. package/dist/commands/agent-config.js +193 -0
  4. package/dist/commands/{code-doctor.d.ts → agent-doctor.d.ts} +2 -2
  5. package/dist/commands/{code-doctor.js → agent-doctor.js} +27 -22
  6. package/dist/commands/{code-invite.d.ts → agent-invite.d.ts} +18 -18
  7. package/dist/commands/{code-invite.js → agent-invite.js} +30 -30
  8. package/dist/commands/agent-session.d.ts +68 -0
  9. package/dist/commands/agent-session.js +114 -0
  10. package/dist/commands/{code-setup.d.ts → agent-setup.d.ts} +13 -13
  11. package/dist/commands/{code-setup.js → agent-setup.js} +46 -42
  12. package/dist/commands/{code.d.ts → agent.d.ts} +50 -14
  13. package/dist/commands/{code.js → agent.js} +144 -86
  14. package/dist/commands/agents.d.ts +2 -2
  15. package/dist/commands/agents.js +5 -5
  16. package/dist/commands/onboard.js +1 -1
  17. package/dist/commands/wrap.js +1 -1
  18. package/dist/index.js +59 -51
  19. package/dist/lib/{code.d.ts → agent.d.ts} +52 -10
  20. package/dist/lib/{code.js → agent.js} +94 -15
  21. package/dist/lib/api.d.ts +4 -4
  22. package/dist/lib/api.js +9 -9
  23. package/dist/lib/auth.js +21 -1
  24. package/dist/lib/{code-config.d.ts → launcher-config.d.ts} +25 -23
  25. package/dist/lib/{code-config.js → launcher-config.js} +50 -31
  26. package/dist/lib/oauth.d.ts +26 -4
  27. package/dist/lib/oauth.js +98 -29
  28. package/dist/lib/version-gate.js +1 -1
  29. package/dist/rc-proxy.mjs +309 -136
  30. package/package.json +7 -3
  31. package/dist/commands/code-config.d.ts +0 -17
  32. package/dist/commands/code-config.js +0 -147
  33. package/dist/commands/code-session.d.ts +0 -52
  34. package/dist/commands/code-session.js +0 -78
package/README.md CHANGED
@@ -44,6 +44,32 @@ That's it. `mnemom init` detects your configured AI provider API keys (Anthropic
44
44
  | `mnemom card publish <file>` | Publish alignment card from JSON file |
45
45
  | `mnemom card validate <file>` | Validate card JSON locally |
46
46
  | `mnemom usage --org <id>` | Show per-person token and request consumption |
47
+ | `mnemom agent [name]` | Launch a governed coding agent through the gateway |
48
+
49
+ ### `mnemom agent` — goal alignment
50
+
51
+ `mnemom agent` launches your coding-agent CLI (Claude Code today) with its model traffic
52
+ routed through the Mnemom gateway under a governed identity. Goal alignment — the judge
53
+ nudging the agent back when it drifts from your goal — comes in three modes; a bare
54
+ `mnemom agent` in a terminal asks which one you want (Enter = off, for now):
55
+
56
+ | Mode | How | What is sent |
57
+ | ------------ | -------------------------------------------------------------------------------------------------------------- | ------------------------------------------- |
58
+ | **Implicit** | `--implicit` — the gateway infers the goal from your own messages and revises it as you steer; nothing to type | `x-mnemom-goal-mode: implicit`, no contract |
59
+ | **Sealed** | `--goal "<statement>"` (+ `--requirement/--allow/--forbid/--goal-id`) — sealed as a contract on the first turn | `x-mnemom-contract` |
60
+ | **Off** | neither | neither header |
61
+
62
+ `--implicit` cannot be combined with the sealed-contract flags: a sealed contract always
63
+ wins on the gateway and would silently switch implicit mode off, so the launch refuses the
64
+ mix. The guardrail ceilings (`--budget`, `--max-turns`, `--stall`, or `guardrails.*` in
65
+ `~/.mnemom/launcher.toml`) apply in both modes — in implicit mode they ride on the mode
66
+ header. In implicit mode the goal lags your steering by one request: a response to a new
67
+ message carries `x-mnemom-goal-steer: queued` and the revision the agent acted under;
68
+ the next request reads the new revision (`x-mnemom-goal-revision`).
69
+
70
+ Make one mode the standing default with `mnemom agent config set goal_mode implicit`
71
+ (or `off`); `--goal` still seals for that launch and `--no-implicit` turns it off for one
72
+ launch. A sealed contract needs a per-launch statement, so it cannot be the default.
47
73
 
48
74
  ## How It Works
49
75
 
@@ -0,0 +1,29 @@
1
+ /**
2
+ * `mnemom agent config …` — read and write the persistent per-customer settings
3
+ * for `mnemom agent` (see lib/code-config.ts). A sub-verb of `mnemom agent`, so a
4
+ * customer configures the launcher once and never retypes their flags:
5
+ *
6
+ * mnemom agent config list # show the current settings
7
+ * mnemom agent config get <key> # print one value
8
+ * mnemom agent config set <key> <value> # validate + persist one value
9
+ * mnemom agent config unset <key> # remove one value
10
+ * mnemom agent config edit # open ~/.mnemom/launcher.toml in $EDITOR
11
+ * mnemom agent config path # print the settings file path
12
+ *
13
+ * Settable keys are the single source of truth in lib/code-config.ts CONFIG_KEYS.
14
+ * The secret Anthropic key is NOT a config key — it lives in ~/.mnemom/launcher.json.
15
+ */
16
+ /** Entry point for `mnemom agent config <verb> [args...]`. Throws on misuse. */
17
+ export declare function agentConfigCommand(argv: string[]): Promise<void>;
18
+ /**
19
+ * `$VISUAL` / `$EDITOR` is a program plus optional plain flags (`code --wait`, `emacsclient -t`),
20
+ * never shell syntax: the value is split on whitespace and every token must be a bare
21
+ * program name, path, or flag. Anything else (quotes, `;`, `$(`, backticks, redirections, …) is
22
+ * refused with a remediation rather than handed to spawnSync — the editor is launched WITHOUT a
23
+ * shell, so such a value could only ever fail confusingly or, via a lookalike binary on PATH, run
24
+ * something the user did not mean. Exported for the tests.
25
+ */
26
+ export declare function parseEditorSpec(spec: string): {
27
+ bin: string;
28
+ args: string[];
29
+ };
@@ -0,0 +1,193 @@
1
+ /**
2
+ * `mnemom agent config …` — read and write the persistent per-customer settings
3
+ * for `mnemom agent` (see lib/code-config.ts). A sub-verb of `mnemom agent`, so a
4
+ * customer configures the launcher once and never retypes their flags:
5
+ *
6
+ * mnemom agent config list # show the current settings
7
+ * mnemom agent config get <key> # print one value
8
+ * mnemom agent config set <key> <value> # validate + persist one value
9
+ * mnemom agent config unset <key> # remove one value
10
+ * mnemom agent config edit # open ~/.mnemom/launcher.toml in $EDITOR
11
+ * mnemom agent config path # print the settings file path
12
+ *
13
+ * Settable keys are the single source of truth in lib/code-config.ts CONFIG_KEYS.
14
+ * The secret Anthropic key is NOT a config key — it lives in ~/.mnemom/launcher.json.
15
+ */
16
+ import { spawnSync } from "node:child_process";
17
+ import { existsSync } from "node:fs";
18
+ import { LAUNCHER_CONFIG_PATH, CONFIG_KEYS, configKeySpec, loadLauncherConfig, writeLauncherConfig, } from "../lib/launcher-config.js";
19
+ const out = (line = "") => console.log(line);
20
+ const err = (line = "") => console.error(line);
21
+ /** Entry point for `mnemom agent config <verb> [args...]`. Throws on misuse. */
22
+ export async function agentConfigCommand(argv) {
23
+ const [verb, ...rest] = argv;
24
+ switch (verb) {
25
+ case undefined:
26
+ case "list":
27
+ return listConfig();
28
+ case "path":
29
+ out(LAUNCHER_CONFIG_PATH);
30
+ return;
31
+ case "get":
32
+ return getConfig(rest[0]);
33
+ case "set":
34
+ return setConfig(rest[0], rest[1]);
35
+ case "unset":
36
+ return unsetConfig(rest[0]);
37
+ case "edit":
38
+ return editConfig();
39
+ default:
40
+ throw new Error(`mnemom agent config: unknown verb '${verb}'. Use list | get | set | unset | edit | path.`);
41
+ }
42
+ }
43
+ function listConfig() {
44
+ const cfg = loadLauncherConfig();
45
+ const rows = [];
46
+ for (const spec of CONFIG_KEYS) {
47
+ const v = spec.get(cfg);
48
+ if (v !== undefined)
49
+ rows.push([spec.key, String(v)]);
50
+ }
51
+ if (!rows.length) {
52
+ out(`No settings yet at ${LAUNCHER_CONFIG_PATH}.`);
53
+ out("Set one with: mnemom agent config set <key> <value>");
54
+ out("");
55
+ out("Settable keys:");
56
+ for (const spec of CONFIG_KEYS)
57
+ out(` ${spec.key.padEnd(22)} ${spec.describe}`);
58
+ return;
59
+ }
60
+ const width = Math.max(...rows.map(([k]) => k.length));
61
+ out(`# ${LAUNCHER_CONFIG_PATH}`);
62
+ for (const [k, v] of rows)
63
+ out(`${k.padEnd(width)} ${v}`);
64
+ }
65
+ function getConfig(key) {
66
+ if (!key)
67
+ throw new Error("mnemom agent config get: a <key> is required.");
68
+ const spec = requireSpec(key);
69
+ const value = spec.get(loadLauncherConfig());
70
+ if (value === undefined) {
71
+ err(`mnemom agent config: '${key}' is not set.`);
72
+ process.exitCode = 1;
73
+ return;
74
+ }
75
+ out(String(value));
76
+ }
77
+ function setConfig(key, value) {
78
+ if (!key)
79
+ throw new Error("mnemom agent config set: a <key> is required.");
80
+ if (value === undefined)
81
+ throw new Error(`mnemom agent config set: a value for '${key}' is required.`);
82
+ const spec = requireSpec(key);
83
+ const cfg = loadLauncherConfig();
84
+ spec.set(cfg, value); // validates + coerces; throws on invalid
85
+ writeLauncherConfig(cfg);
86
+ out(`${key} = ${String(spec.get(cfg))} (saved to ${LAUNCHER_CONFIG_PATH})`);
87
+ }
88
+ function unsetConfig(key) {
89
+ if (!key)
90
+ throw new Error("mnemom agent config unset: a <key> is required.");
91
+ requireSpec(key);
92
+ const cfg = loadLauncherConfig();
93
+ deleteKey(cfg, key);
94
+ writeLauncherConfig(cfg);
95
+ out(`unset ${key} (saved to ${LAUNCHER_CONFIG_PATH})`);
96
+ }
97
+ function editConfig() {
98
+ // Seed a header-only file so $EDITOR opens something with guidance, not a blank.
99
+ if (!existsSync(LAUNCHER_CONFIG_PATH))
100
+ writeLauncherConfig(loadLauncherConfig());
101
+ const { bin: editor, args: editorArgs } = parseEditorSpec(process.env.VISUAL || process.env.EDITOR || fallbackEditor());
102
+ const res = spawnSync(editor, [...editorArgs, LAUNCHER_CONFIG_PATH], { stdio: "inherit" });
103
+ if (res.error) {
104
+ throw new Error(`mnemom agent config edit: could not launch editor '${editor}': ${res.error.message}\n` +
105
+ ` Set $EDITOR, or edit ${LAUNCHER_CONFIG_PATH} directly.`);
106
+ }
107
+ // Re-parse so a typo surfaces now, not at the next launch.
108
+ loadLauncherConfig();
109
+ out(`Saved ${LAUNCHER_CONFIG_PATH}.`);
110
+ }
111
+ /**
112
+ * `$VISUAL` / `$EDITOR` is a program plus optional plain flags (`code --wait`, `emacsclient -t`),
113
+ * never shell syntax: the value is split on whitespace and every token must be a bare
114
+ * program name, path, or flag. Anything else (quotes, `;`, `$(`, backticks, redirections, …) is
115
+ * refused with a remediation rather than handed to spawnSync — the editor is launched WITHOUT a
116
+ * shell, so such a value could only ever fail confusingly or, via a lookalike binary on PATH, run
117
+ * something the user did not mean. Exported for the tests.
118
+ */
119
+ export function parseEditorSpec(spec) {
120
+ const tokens = spec.trim().split(/\s+/).filter(Boolean);
121
+ const SAFE = /^[A-Za-z0-9_./+=:@,-]+$/;
122
+ if (tokens.length === 0 || !tokens.every((t) => SAFE.test(t))) {
123
+ throw new Error(`mnemom agent config edit: refusing to run $VISUAL/$EDITOR value '${spec}' — only a program name or path plus plain flags is accepted (no shell syntax).\n` +
124
+ ` Set $EDITOR to e.g. 'code --wait', or edit ${LAUNCHER_CONFIG_PATH} directly.`);
125
+ }
126
+ return { bin: tokens[0], args: tokens.slice(1) };
127
+ }
128
+ function fallbackEditor() {
129
+ for (const cand of ["nano", "vim", "vi"]) {
130
+ const r = spawnSync("sh", ["-c", `command -v "$1" >/dev/null 2>&1`, "sh", cand], {
131
+ stdio: "ignore",
132
+ });
133
+ if (r.status === 0)
134
+ return cand;
135
+ }
136
+ return "vi";
137
+ }
138
+ function requireSpec(key) {
139
+ const spec = configKeySpec(key);
140
+ if (!spec) {
141
+ throw new Error(`mnemom agent config: unknown key '${key}'.\n Known keys: ${CONFIG_KEYS.map((k) => k.key).join(", ")}`);
142
+ }
143
+ return spec;
144
+ }
145
+ /** Remove a (possibly dotted) key from a config object, pruning an emptied table. */
146
+ function deleteKey(cfg, key) {
147
+ if (key.startsWith("guardrails.")) {
148
+ const g = cfg.guardrails;
149
+ if (!g)
150
+ return;
151
+ const field = key.slice("guardrails.".length);
152
+ if (field === "max_turns")
153
+ delete g.maxTurns;
154
+ if (field === "budget_usd")
155
+ delete g.budgetUsd;
156
+ if (field === "stall_turns")
157
+ delete g.stallTurns;
158
+ if (g.maxTurns === undefined && g.budgetUsd === undefined && g.stallTurns === undefined) {
159
+ delete cfg.guardrails;
160
+ }
161
+ return;
162
+ }
163
+ // Static, exhaustive: every top-level key the schema knows, so a typo can never
164
+ // delete an unrelated property (and the lint rule on dynamic delete stays honest).
165
+ switch (key) {
166
+ case "gateway":
167
+ delete cfg.gateway;
168
+ break;
169
+ case "model":
170
+ delete cfg.model;
171
+ break;
172
+ case "agent":
173
+ delete cfg.agent;
174
+ break;
175
+ case "effort":
176
+ delete cfg.effort;
177
+ break;
178
+ case "goal_mode":
179
+ delete cfg.goalMode;
180
+ break;
181
+ case "cli":
182
+ delete cfg.cli;
183
+ break;
184
+ case "launch":
185
+ delete cfg.launch;
186
+ break;
187
+ case "key_source":
188
+ delete cfg.keySource;
189
+ break;
190
+ default:
191
+ requireSpec(key); // throws the known-keys message for anything unknown
192
+ }
193
+ }
@@ -1,5 +1,5 @@
1
1
  /**
2
- * `mnemom code doctor` — a read-only preflight for `mnemom code`. It checks the
2
+ * `mnemom agent doctor` — a read-only preflight for `mnemom agent`. It checks the
3
3
  * things a governed launch needs and prints a checklist with clear, actionable
4
4
  * remediation, so a customer can see at a glance why a launch would fail before
5
5
  * it does. Writes nothing and reads no secret VALUE (only whether a key is
@@ -16,7 +16,7 @@ export interface DoctorOptions {
16
16
  cli?: string;
17
17
  }
18
18
  /** Run the preflight and print the checklist. Sets process.exitCode on failure. */
19
- export declare function codeDoctorCommand(options?: DoctorOptions): Promise<void>;
19
+ export declare function agentDoctorCommand(options?: DoctorOptions): Promise<void>;
20
20
  export interface CodeAccessProbe {
21
21
  mark: Mark;
22
22
  text: string;
@@ -1,5 +1,5 @@
1
1
  /**
2
- * `mnemom code doctor` — a read-only preflight for `mnemom code`. It checks the
2
+ * `mnemom agent doctor` — a read-only preflight for `mnemom agent`. It checks the
3
3
  * things a governed launch needs and prints a checklist with clear, actionable
4
4
  * remediation, so a customer can see at a glance why a launch would fail before
5
5
  * it does. Writes nothing and reads no secret VALUE (only whether a key is
@@ -13,27 +13,27 @@
13
13
  */
14
14
  import { spawnSync } from "node:child_process";
15
15
  import { existsSync } from "node:fs";
16
- import { anthropicKeySource, checkRcCapability, resolveCliBin, } from "./code.js";
17
- import { isProdGateway, resolveAnthropicDoor, resolveGatewayHost, } from "../lib/code.js";
18
- import { CODE_CONFIG_PATH } from "../lib/code-config.js";
16
+ import { anthropicKeySource, checkRcCapability, resolveCliBin } from "./agent.js";
17
+ import { isProdGateway, resolveAnthropicDoor, resolveGatewayHost } from "../lib/agent.js";
18
+ import { LAUNCHER_CONFIG_PATH } from "../lib/launcher-config.js";
19
19
  import { getCodeEntitlement, MnemomApiError } from "../lib/api.js";
20
20
  import { isLoggedIn } from "../lib/auth.js";
21
21
  import { getActiveOrg } from "../lib/cli-config.js";
22
- import { isCodeNotEnabled, sourceLabel } from "./code-invite.js";
22
+ import { isCodeNotEnabled, sourceLabel } from "./agent-invite.js";
23
23
  const MARK = { ok: "[ ok ]", warn: "[warn]", miss: "[MISS]", info: "[info]" };
24
24
  const out = (line = "") => console.log(line);
25
25
  /** Run the preflight and print the checklist. Sets process.exitCode on failure. */
26
- export async function codeDoctorCommand(options = {}) {
26
+ export async function agentDoctorCommand(options = {}) {
27
27
  const env = process.env;
28
28
  const lines = [];
29
29
  const add = (mark, text, hint) => {
30
30
  lines.push({ mark, text, hint });
31
31
  };
32
32
  // ── settings file ──
33
- if (existsSync(CODE_CONFIG_PATH))
34
- add("ok", `settings: ${CODE_CONFIG_PATH}`);
33
+ if (existsSync(LAUNCHER_CONFIG_PATH))
34
+ add("ok", `settings: ${LAUNCHER_CONFIG_PATH}`);
35
35
  else
36
- add("info", `settings: none yet (${CODE_CONFIG_PATH}) — using built-in defaults`, "save your defaults with `mnemom code config set <key> <value>`");
36
+ add("info", `settings: none yet (${LAUNCHER_CONFIG_PATH}) — using built-in defaults`, "save your defaults with `mnemom agent config set <key> <value>`");
37
37
  // ── coding-agent CLI ──
38
38
  let cli;
39
39
  try {
@@ -41,16 +41,16 @@ export async function codeDoctorCommand(options = {}) {
41
41
  add("ok", `coding-agent CLI: ${cli.name} (${cli.bin})`);
42
42
  }
43
43
  catch (err) {
44
- add("miss", `coding-agent CLI: ${err instanceof Error ? err.message.split("\n")[0] : err}`, "install Claude Code, or pass --cli <name|/full/path> / set it with `mnemom code config set cli <path>`");
44
+ add("miss", `coding-agent CLI: ${err instanceof Error ? err.message.split("\n")[0] : err}`, "install Claude Code, or pass --cli <name|/full/path> / set it with `mnemom agent config set cli <path>`");
45
45
  }
46
46
  // ── Anthropic key ──
47
47
  const keySrc = anthropicKeySource(env);
48
48
  if (keySrc === "env")
49
49
  add("ok", "Anthropic key: found in the environment");
50
50
  else if (keySrc === "store")
51
- add("ok", "Anthropic key: found in ~/.mnemom/code.json");
51
+ add("ok", "Anthropic key: found in ~/.mnemom/launcher.json");
52
52
  else
53
- add("miss", "Anthropic key: none found", "export MNEMOM_CODE_ANTHROPIC_KEY=sk-ant-… (or ANTHROPIC_API_KEY), or run `mnemom code` once and accept the save prompt");
53
+ add("miss", "Anthropic key: none found", "export MNEMOM_AGENT_ANTHROPIC_KEY=sk-ant-… (or ANTHROPIC_API_KEY), or run `mnemom agent` once and accept the save prompt");
54
54
  // ── gateway health ──
55
55
  const host = resolveGatewayHost(env);
56
56
  const cell = isProdGateway(env) ? "us-2/prod" : "custom";
@@ -62,7 +62,7 @@ export async function codeDoctorCommand(options = {}) {
62
62
  add("warn", `gateway: ${host} [${cell}] responded ${health.status} but without an x-mnemom-verdict header`, "the cell is up but the governance pipeline may not be wired — traces/nudges may not appear");
63
63
  }
64
64
  else {
65
- add("warn", `gateway: ${host} [${cell}] unreachable (${health.detail})`, "check the host (MNEMOM_CODE_GATEWAY / `mnemom code config set gateway <url>`) and your network");
65
+ add("warn", `gateway: ${host} [${cell}] unreachable (${health.detail})`, "check the host (MNEMOM_AGENT_GATEWAY / `mnemom agent config set gateway <url>`) and your network");
66
66
  }
67
67
  // ── Remote Control capability ──
68
68
  if (cli) {
@@ -82,12 +82,12 @@ export async function codeDoctorCommand(options = {}) {
82
82
  add("info", "claude.ai login: could not determine");
83
83
  }
84
84
  }
85
- // ── Mnemom Code access (invited cohort gate) ──
85
+ // ── Mnemom Agent access (invited cohort gate) ──
86
86
  const gate = await probeCodeAccess();
87
87
  add(gate.mark, gate.text, gate.hint);
88
88
  // ── render ──
89
89
  const door = resolveAnthropicDoor(env);
90
- out("mnemom code doctor");
90
+ out("mnemom agent doctor");
91
91
  out(` door: ${door}`);
92
92
  out("");
93
93
  for (const l of lines) {
@@ -98,7 +98,7 @@ export async function codeDoctorCommand(options = {}) {
98
98
  out("");
99
99
  const launchReady = !lines.some((l) => l.mark === "miss");
100
100
  if (launchReady) {
101
- out(" Ready to launch: mnemom code");
101
+ out(" Ready to launch: mnemom agent");
102
102
  }
103
103
  else {
104
104
  out(" Not launch-ready — resolve the [MISS] items above.");
@@ -153,26 +153,31 @@ export async function probeCodeAccess() {
153
153
  if (!(await isLoggedIn())) {
154
154
  return {
155
155
  mark: "info",
156
- text: "Mnemom Code access: sign in to check",
157
- hint: "run `mnemom login`; @mnemom.ai accounts are enabled by default, others need an invitation (mnemom code --invite <token>)",
156
+ text: "Mnemom Agent access: sign in to check",
157
+ hint: "run `mnemom login`; @mnemom.ai accounts are enabled by default, others need an invitation (mnemom agent --invite <token>)",
158
158
  };
159
159
  }
160
160
  const org = getActiveOrg();
161
161
  const orgLabel = org ? `${org.name} (${org.slug})` : "your personal org";
162
162
  try {
163
163
  const ent = await getCodeEntitlement(org?.org_id);
164
- return { mark: "ok", text: `Mnemom Code access: enabled for ${orgLabel} (${sourceLabel(ent.source)})` };
164
+ return {
165
+ mark: "ok",
166
+ text: `Mnemom Agent access: enabled for ${orgLabel} (${sourceLabel(ent.source)})`,
167
+ };
165
168
  }
166
169
  catch (err) {
167
170
  if (isCodeNotEnabled(err)) {
168
171
  const hint = err.details?.hint;
169
172
  return {
170
173
  mark: "miss",
171
- text: `Mnemom Code access: not enabled for ${orgLabel}`,
172
- hint: typeof hint === "string" && hint ? hint : "redeem an invitation: mnemom code --invite <token>",
174
+ text: `Mnemom Agent access: not enabled for ${orgLabel}`,
175
+ hint: typeof hint === "string" && hint
176
+ ? hint
177
+ : "redeem an invitation: mnemom agent --invite <token>",
173
178
  };
174
179
  }
175
180
  const detail = err instanceof MnemomApiError || err instanceof Error ? err.message : String(err);
176
- return { mark: "info", text: `Mnemom Code access: could not determine (${detail})` };
181
+ return { mark: "info", text: `Mnemom Agent access: could not determine (${detail})` };
177
182
  }
178
183
  }
@@ -1,7 +1,7 @@
1
1
  /**
2
- * Mnemom Code — the invited-cohort gate + `mnemom code --invite <token>`.
2
+ * Mnemom Agent — the invited-cohort gate + `mnemom agent --invite <token>`.
3
3
  *
4
- * `mnemom code` is open by default to Mnemom's own people (@mnemom.ai) and to
4
+ * `mnemom agent` is open by default to Mnemom's own people (@mnemom.ai) and to
5
5
  * customers who redeemed an invitation. The api decides
6
6
  * (`GET /v1/code/entitlement`); this module is the CLI side of that:
7
7
  *
@@ -10,13 +10,13 @@
10
10
  * `code_not_enabled` BLOCKS with the api's own hint; anything inconclusive
11
11
  * (network, 5xx, a membership 403) fails OPEN — the gate is a friendly
12
12
  * pre-check, the api/gateway remain the real enforcement.
13
- * - `codeInviteCommand(token)` redeems a `pinv_…` invitation
13
+ * - `agentInviteCommand(token)` redeems a `pinv_…` invitation
14
14
  * (`POST /v1/invites/accept`): signs the customer in first (creating a free
15
- * account in the same flow if needed), unlocks Mnemom Code for them, and
16
- * reports whether the starter µ landed. Backs both `mnemom code --invite
17
- * <token> [scenario]` and the `mnemom code invite <token>` sub-verb.
15
+ * account in the same flow if needed), unlocks Mnemom Agent for them, and
16
+ * reports whether the starter µ landed. Backs both `mnemom agent --invite
17
+ * <token> [scenario]` and the `mnemom agent invite <token>` sub-verb.
18
18
  *
19
- * All narration goes to stderr (the `mnemom code` convention — stdout is for
19
+ * All narration goes to stderr (the `mnemom agent` convention — stdout is for
20
20
  * the dry-run plan only). Copy says "µ", never "MU".
21
21
  */
22
22
  import { MnemomApiError, type AcceptProductInviteResult, type CodeEntitlement } from "../lib/api.js";
@@ -37,7 +37,7 @@ export declare function sourceLabel(source: CodeEntitlement["source"]): string;
37
37
  /** The api's `code_not_enabled` 403 — the ONE case that blocks. */
38
38
  export declare function isCodeNotEnabled(err: unknown): err is MnemomApiError;
39
39
  /**
40
- * Enforce the Mnemom Code gate for the org the launch targets. Blocks (prints
40
+ * Enforce the Mnemom Agent gate for the org the launch targets. Blocks (prints
41
41
  * guidance + throws CodeNotEnabledError) ONLY on a determined `code_not_enabled`;
42
42
  * every other failure is inconclusive and fails open with a dim note.
43
43
  */
@@ -51,15 +51,15 @@ export declare function explainAcceptError(err: unknown): string | null;
51
51
  /** Print the outcome of a successful accept. */
52
52
  export declare function printAccepted(res: AcceptProductInviteResult): void;
53
53
  /**
54
- * `mnemom code --invite <token>` / `mnemom code invite <token>`: sign in, redeem,
54
+ * `mnemom agent --invite <token>` / `mnemom agent invite <token>`: sign in, redeem,
55
55
  * report. Throws with a clear message on a bad token, no session, or an accept
56
56
  * failure the customer can act on; rethrows anything else.
57
57
  */
58
- export declare function codeInviteCommand(token: string | undefined, opts?: {
58
+ export declare function agentInviteCommand(token: string | undefined, opts?: {
59
59
  interactive?: boolean;
60
60
  ensureSession?: EnsureSession;
61
61
  }): Promise<AcceptProductInviteResult>;
62
- export interface CodeInvocation {
62
+ export interface AgentInvocation {
63
63
  /** The positional `[scenario]` (may be a reserved sub-verb such as "invite"). */
64
64
  scenario?: string;
65
65
  /** Everything after the scenario. */
@@ -67,7 +67,7 @@ export interface CodeInvocation {
67
67
  /** `--invite <token>`. */
68
68
  inviteFlag?: string;
69
69
  }
70
- export interface CodeInvocationDeps {
70
+ export interface AgentInvocationDeps {
71
71
  redeem: (token: string | undefined) => Promise<unknown>;
72
72
  /** The launch half; `undefined` = no session name given (the launcher prompts in a terminal). */
73
73
  launch: (scenario: string | undefined) => Promise<void>;
@@ -75,10 +75,10 @@ export interface CodeInvocationDeps {
75
75
  /**
76
76
  * How the `code` command's positional + `--invite` compose — kept OUT of
77
77
  * index.ts so it is unit-testable:
78
- * - `mnemom code invite <token>` → redeem, done (sub-verb; "invite" is reserved)
79
- * - `mnemom code --invite <token>` → redeem, done
80
- * - `mnemom code --invite <token> <scen>` → redeem, then launch
81
- * - `mnemom code <scen>` → launch
82
- * - `mnemom code` → launch (the launcher asks for a name + goal)
78
+ * - `mnemom agent invite <token>` → redeem, done (sub-verb; "invite" is reserved)
79
+ * - `mnemom agent --invite <token>` → redeem, done
80
+ * - `mnemom agent --invite <token> <scen>` → redeem, then launch
81
+ * - `mnemom agent <scen>` → launch
82
+ * - `mnemom agent` → launch (the launcher asks for a name + goal)
83
83
  */
84
- export declare function runCodeInvocation(input: CodeInvocation, deps: CodeInvocationDeps): Promise<void>;
84
+ export declare function runAgentInvocation(input: AgentInvocation, deps: AgentInvocationDeps): Promise<void>;
@@ -1,7 +1,7 @@
1
1
  /**
2
- * Mnemom Code — the invited-cohort gate + `mnemom code --invite <token>`.
2
+ * Mnemom Agent — the invited-cohort gate + `mnemom agent --invite <token>`.
3
3
  *
4
- * `mnemom code` is open by default to Mnemom's own people (@mnemom.ai) and to
4
+ * `mnemom agent` is open by default to Mnemom's own people (@mnemom.ai) and to
5
5
  * customers who redeemed an invitation. The api decides
6
6
  * (`GET /v1/code/entitlement`); this module is the CLI side of that:
7
7
  *
@@ -10,13 +10,13 @@
10
10
  * `code_not_enabled` BLOCKS with the api's own hint; anything inconclusive
11
11
  * (network, 5xx, a membership 403) fails OPEN — the gate is a friendly
12
12
  * pre-check, the api/gateway remain the real enforcement.
13
- * - `codeInviteCommand(token)` redeems a `pinv_…` invitation
13
+ * - `agentInviteCommand(token)` redeems a `pinv_…` invitation
14
14
  * (`POST /v1/invites/accept`): signs the customer in first (creating a free
15
- * account in the same flow if needed), unlocks Mnemom Code for them, and
16
- * reports whether the starter µ landed. Backs both `mnemom code --invite
17
- * <token> [scenario]` and the `mnemom code invite <token>` sub-verb.
15
+ * account in the same flow if needed), unlocks Mnemom Agent for them, and
16
+ * reports whether the starter µ landed. Backs both `mnemom agent --invite
17
+ * <token> [scenario]` and the `mnemom agent invite <token>` sub-verb.
18
18
  *
19
- * All narration goes to stderr (the `mnemom code` convention — stdout is for
19
+ * All narration goes to stderr (the `mnemom agent` convention — stdout is for
20
20
  * the dry-run plan only). Copy says "µ", never "MU".
21
21
  */
22
22
  import { acceptProductInvite, CODE_NOT_ENABLED, getCodeEntitlement, MnemomApiError, } from "../lib/api.js";
@@ -28,7 +28,7 @@ const say = (line = "") => console.error(line);
28
28
  export class CodeNotEnabledError extends Error {
29
29
  orgLabel;
30
30
  constructor(orgLabel) {
31
- super("Mnemom Code is not enabled for this organization");
31
+ super("Mnemom Agent is not enabled for this organization");
32
32
  this.orgLabel = orgLabel;
33
33
  this.name = "CodeNotEnabledError";
34
34
  }
@@ -38,7 +38,7 @@ export const INVITE_TOKEN_RE = /^pinv_[A-Za-z0-9_-]{8,}$/;
38
38
  export function looksLikeInviteToken(value) {
39
39
  return typeof value === "string" && INVITE_TOKEN_RE.test(value.trim());
40
40
  }
41
- const FALLBACK_HINT = "Ask your Mnemom contact for a Mnemom Code invitation, then run: mnemom code --invite <token>";
41
+ const FALLBACK_HINT = "Ask your Mnemom contact for a Mnemom Agent invitation, then run: mnemom agent --invite <token>";
42
42
  /** Drop control characters (ANSI escapes included) from an api-supplied string before
43
43
  * it reaches the terminal — defense in depth; the value is first-party but printed raw. */
44
44
  export function sanitizeTerminalText(s) {
@@ -70,16 +70,16 @@ function hintFrom(err) {
70
70
  }
71
71
  function printNotEnabled(orgLabel, hint) {
72
72
  say();
73
- say(fmt.warn(`Mnemom Code isn't enabled for ${orgLabel} yet.`));
74
- say(fmt.dim(" mnemom code is rolling out to an invited cohort. If you have an invitation token"));
73
+ say(fmt.warn(`Mnemom Agent isn't enabled for ${orgLabel} yet.`));
74
+ say(fmt.dim(" mnemom agent is rolling out to an invited cohort. If you have an invitation token"));
75
75
  say(fmt.dim(" (pinv_…), redeem it and you're in:"));
76
76
  say();
77
- say(" mnemom code --invite <token>");
77
+ say(" mnemom agent --invite <token>");
78
78
  say();
79
79
  say(fmt.dim(` ${hint}`));
80
80
  }
81
81
  /**
82
- * Enforce the Mnemom Code gate for the org the launch targets. Blocks (prints
82
+ * Enforce the Mnemom Agent gate for the org the launch targets. Blocks (prints
83
83
  * guidance + throws CodeNotEnabledError) ONLY on a determined `code_not_enabled`;
84
84
  * every other failure is inconclusive and fails open with a dim note.
85
85
  */
@@ -92,13 +92,13 @@ export async function assertCodeEnabled(orgId, orgLabel) {
92
92
  printNotEnabled(orgLabel, hintFrom(err));
93
93
  throw new CodeNotEnabledError(orgLabel);
94
94
  }
95
- say(fmt.dim(` (Could not verify Mnemom Code access — ${err instanceof Error ? err.message : String(err)}. Continuing.)`));
95
+ say(fmt.dim(` (Could not verify Mnemom Agent access — ${err instanceof Error ? err.message : String(err)}. Continuing.)`));
96
96
  }
97
97
  }
98
98
  async function defaultEnsureSession(opts) {
99
99
  // Lazy import: code-setup imports assertCodeEnabled from THIS module, so a static
100
100
  // import here would be a cycle.
101
- const { ensureMnemomSession } = await import("./code-setup.js");
101
+ const { ensureMnemomSession } = await import("./agent-setup.js");
102
102
  return ensureMnemomSession(opts);
103
103
  }
104
104
  /** Map an accept failure to the sentence the customer needs. Null = not ours to explain. */
@@ -121,10 +121,10 @@ export function explainAcceptError(err) {
121
121
  /** Print the outcome of a successful accept. */
122
122
  export function printAccepted(res) {
123
123
  if (res.already_accepted) {
124
- say(fmt.success("This invitation was already accepted — Mnemom Code is unlocked for you."));
124
+ say(fmt.success("This invitation was already accepted — Mnemom Agent is unlocked for you."));
125
125
  }
126
126
  else {
127
- say(fmt.success("Mnemom Code is unlocked for your account."));
127
+ say(fmt.success("Mnemom Agent is unlocked for your account."));
128
128
  }
129
129
  if (res.grant === "fired") {
130
130
  say(fmt.dim(" Your starter µ has been credited."));
@@ -132,27 +132,27 @@ export function printAccepted(res) {
132
132
  else if (res.grant === "deferred") {
133
133
  say(fmt.dim(" Your starter µ credit is pending — you can start now; if it hasn't landed shortly, contact support@mnemom.ai."));
134
134
  }
135
- say(fmt.dim(" Start a governed session: mnemom code"));
135
+ say(fmt.dim(" Start a governed session: mnemom agent"));
136
136
  }
137
137
  /**
138
- * `mnemom code --invite <token>` / `mnemom code invite <token>`: sign in, redeem,
138
+ * `mnemom agent --invite <token>` / `mnemom agent invite <token>`: sign in, redeem,
139
139
  * report. Throws with a clear message on a bad token, no session, or an accept
140
140
  * failure the customer can act on; rethrows anything else.
141
141
  */
142
- export async function codeInviteCommand(token, opts = {}) {
142
+ export async function agentInviteCommand(token, opts = {}) {
143
143
  const trimmed = (token ?? "").trim();
144
144
  if (!looksLikeInviteToken(trimmed)) {
145
- throw new Error("mnemom code --invite: expected a Mnemom Code invitation token (pinv_…) — copy it from your invitation email.");
145
+ throw new Error("mnemom agent --invite: expected a Mnemom Agent invitation token (pinv_…) — copy it from your invitation email.");
146
146
  }
147
147
  const interactive = opts.interactive ?? isInteractive();
148
148
  const ensureSession = opts.ensureSession ?? defaultEnsureSession;
149
149
  const haveSession = await ensureSession({ interactive });
150
150
  if (!haveSession) {
151
- throw new Error("mnemom code --invite: redeeming needs a Mnemom account.\n" +
151
+ throw new Error("mnemom agent --invite: redeeming needs a Mnemom account.\n" +
152
152
  " Sign in: mnemom login\n" +
153
153
  " Then run the same --invite command again.");
154
154
  }
155
- say(fmt.dim(" Redeeming your Mnemom Code invitation…"));
155
+ say(fmt.dim(" Redeeming your Mnemom Agent invitation…"));
156
156
  let result;
157
157
  try {
158
158
  result = await acceptProductInvite(trimmed);
@@ -160,7 +160,7 @@ export async function codeInviteCommand(token, opts = {}) {
160
160
  catch (err) {
161
161
  const explained = explainAcceptError(err);
162
162
  if (explained)
163
- throw new Error(`mnemom code --invite: ${explained}`, { cause: err });
163
+ throw new Error(`mnemom agent --invite: ${explained}`, { cause: err });
164
164
  throw err;
165
165
  }
166
166
  printAccepted(result);
@@ -169,13 +169,13 @@ export async function codeInviteCommand(token, opts = {}) {
169
169
  /**
170
170
  * How the `code` command's positional + `--invite` compose — kept OUT of
171
171
  * index.ts so it is unit-testable:
172
- * - `mnemom code invite <token>` → redeem, done (sub-verb; "invite" is reserved)
173
- * - `mnemom code --invite <token>` → redeem, done
174
- * - `mnemom code --invite <token> <scen>` → redeem, then launch
175
- * - `mnemom code <scen>` → launch
176
- * - `mnemom code` → launch (the launcher asks for a name + goal)
172
+ * - `mnemom agent invite <token>` → redeem, done (sub-verb; "invite" is reserved)
173
+ * - `mnemom agent --invite <token>` → redeem, done
174
+ * - `mnemom agent --invite <token> <scen>` → redeem, then launch
175
+ * - `mnemom agent <scen>` → launch
176
+ * - `mnemom agent` → launch (the launcher asks for a name + goal)
177
177
  */
178
- export async function runCodeInvocation(input, deps) {
178
+ export async function runAgentInvocation(input, deps) {
179
179
  if (input.scenario === "invite") {
180
180
  await deps.redeem(input.passthrough[0]);
181
181
  return;