@mnemom/mnemom 0.17.24 → 0.17.26

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 (43) hide show
  1. package/dist/commands/agent-config.js +9 -0
  2. package/dist/commands/agent-doctor.d.ts +3 -2
  3. package/dist/commands/agent-doctor.js +22 -7
  4. package/dist/commands/agent-goal.js +7 -2
  5. package/dist/commands/agent-sessions.js +2 -1
  6. package/dist/commands/agent-setup.d.ts +104 -4
  7. package/dist/commands/agent-setup.js +414 -18
  8. package/dist/commands/agent-slack.d.ts +14 -1
  9. package/dist/commands/agent-slack.js +108 -23
  10. package/dist/commands/agent.d.ts +29 -1
  11. package/dist/commands/agent.js +89 -12
  12. package/dist/commands/wrap.d.ts +2 -0
  13. package/dist/commands/wrap.js +13 -1
  14. package/dist/index.js +19 -5
  15. package/dist/lib/agent-identity-route.d.ts +21 -3
  16. package/dist/lib/agent-identity-route.js +42 -15
  17. package/dist/lib/agent-ledger.d.ts +5 -0
  18. package/dist/lib/agent.d.ts +62 -1
  19. package/dist/lib/agent.js +55 -0
  20. package/dist/lib/api.d.ts +33 -1
  21. package/dist/lib/api.js +10 -0
  22. package/dist/lib/experimental.js +14 -2
  23. package/dist/lib/goal-api.d.ts +3 -3
  24. package/dist/lib/goal-api.js +5 -2
  25. package/dist/lib/launcher-config.d.ts +18 -0
  26. package/dist/lib/launcher-config.js +47 -1
  27. package/dist/lib/oauth.d.ts +7 -0
  28. package/dist/lib/oauth.js +92 -17
  29. package/dist/lib/served-agent-cache.d.ts +8 -1
  30. package/dist/lib/served-agent-cache.js +11 -2
  31. package/dist/lib/slack-channel.d.ts +1 -0
  32. package/dist/lib/slack-channel.js +30 -12
  33. package/dist/lib/slack-route.d.ts +72 -9
  34. package/dist/lib/slack-route.js +113 -24
  35. package/dist/lib/slack-router.d.ts +37 -2
  36. package/dist/lib/slack-router.js +98 -13
  37. package/dist/lib/slack-state.d.ts +105 -4
  38. package/dist/lib/slack-state.js +230 -11
  39. package/dist/lib/subscription-login.d.ts +12 -3
  40. package/dist/lib/subscription-login.js +19 -7
  41. package/dist/mcp/handoff-server.mjs +31 -3
  42. package/dist/rc-proxy.mjs +14 -0
  43. package/package.json +1 -1
@@ -72,30 +72,42 @@ export function resolveAgentAuth(flag, env) {
72
72
  return fromEnv ? coerceAgentAuth(fromEnv) : "api-key";
73
73
  }
74
74
  /**
75
- * The Mnemom key a subscription launch sends: MNEMOM_AGENT_MNEMOM_KEY, else the stored
76
- * key, else mint one (gateway scope only) and store it. The key is never printed.
75
+ * The Mnemom key this machine already has — MNEMOM_AGENT_MNEMOM_KEY, else the stored key — or
76
+ * undefined. NEVER mints. An unpinned api-key launch sends it (`x-mnemom-api-key`) alongside its
77
+ * Anthropic key when there is one, so the gateway can serve the key owner's claimed agent of that
78
+ * name instead of a fresh one per rotated Anthropic key; with none it launches exactly as before.
77
79
  */
78
- export async function resolveSubscriptionMnemomKey(deps) {
80
+ export function readMnemomKey(deps) {
79
81
  const fromEnv = headerSafe(deps.env[MNEMOM_KEY_ENV] ?? "");
80
82
  if (fromEnv)
81
83
  return fromEnv;
82
84
  const stored = headerSafe(deps.readStored() ?? "");
83
- if (stored)
84
- return stored;
85
+ return stored || undefined;
86
+ }
87
+ /**
88
+ * The Mnemom key a subscription launch (or a pinned launch, which the gateway honours only with
89
+ * a verified Mnemom key) sends: MNEMOM_AGENT_MNEMOM_KEY, else the stored key, else mint one
90
+ * (gateway scope only) and store it. The key is never printed. `purpose` names why in the
91
+ * one-line notes ("subscription login" by default).
92
+ */
93
+ export async function resolveSubscriptionMnemomKey(deps, purpose = "subscription login") {
94
+ const existing = readMnemomKey(deps);
95
+ if (existing)
96
+ return existing;
85
97
  let minted;
86
98
  try {
87
99
  minted = headerSafe(await deps.mint());
88
100
  }
89
101
  catch (err) {
90
102
  const msg = err instanceof Error ? err.message : String(err);
91
- throw new Error(`mnemom agent: subscription login needs a Mnemom API key and minting one failed (${msg}).\n` +
103
+ throw new Error(`mnemom agent: ${purpose} needs a Mnemom API key and minting one failed (${msg}).\n` +
92
104
  ` Create one with \`mnemom api-key create --name mnemom-agent --scopes gateway\` and set ${MNEMOM_KEY_ENV}.`, { cause: err });
93
105
  }
94
106
  if (!minted)
95
107
  throw new Error("mnemom agent: the API returned an empty Mnemom key.");
96
108
  try {
97
109
  deps.store(minted);
98
- deps.say("mnemom agent: minted a gateway-only Mnemom API key for subscription login (saved to ~/.mnemom/launcher.json, 0600).");
110
+ deps.say(`mnemom agent: minted a gateway-only Mnemom API key for ${purpose} (saved to ~/.mnemom/launcher.json, 0600).`);
99
111
  }
100
112
  catch {
101
113
  deps.say("mnemom agent: minted a gateway-only Mnemom API key for this launch but could not save it — the next launch mints another.");
@@ -85,11 +85,31 @@ function resolveKeyFromEnvName(name, env) {
85
85
  return value.trim();
86
86
  }
87
87
 
88
+ /** A full Mnemom agent id (mirrors AGENT_ID_RE in cli/src/lib/agent.ts). */
89
+ const AGENT_ID_RE = /^mnm-[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
90
+
91
+ /**
92
+ * The Mnemom key the launch's own requests carry, when it has one: the `x-mnemom-api-key:` line
93
+ * of the inherited `ANTHROPIC_CUSTOM_HEADERS` (terminal), else the dedicated
94
+ * `MNEMOM_HANDOFF_MNEMOM_KEY` the Remote Control launcher sets. "" when there is none. A pinned
95
+ * call needs it: the gateway honours `x-mnemom-agent-id` only with a verified Mnemom key.
96
+ * @param {string} keyEnvName
97
+ * @param {NodeJS.ProcessEnv} env
98
+ * @returns {string}
99
+ */
100
+ function resolveMnemomKey(keyEnvName, env) {
101
+ if (keyEnvName === "ANTHROPIC_CUSTOM_HEADERS") {
102
+ const m = (env.ANTHROPIC_CUSTOM_HEADERS ?? "").match(/^x-mnemom-api-key:\s*(.+)$/im);
103
+ return m ? m[1].trim() : "";
104
+ }
105
+ return (env.MNEMOM_HANDOFF_MNEMOM_KEY ?? "").trim();
106
+ }
107
+
88
108
  /**
89
109
  * Read the tool's config from env. Throws (message never contains the key value) when a
90
110
  * required var is missing, or when the named key-source var resolves to no usable key.
91
111
  * @param {NodeJS.ProcessEnv} env
92
- * @returns {{gatewayHost: string, anthropicKey: string, agent: string, conversationId: string, sessionConfigB64?: string}}
112
+ * @returns {{gatewayHost: string, anthropicKey: string, mnemomKey?: string, agent: string, agentId?: string, conversationId: string, sessionConfigB64?: string}}
93
113
  */
94
114
  export function configFromEnv(env) {
95
115
  const missing = REQUIRED_ENV.filter((k) => !(env[k] ?? "").trim());
@@ -104,10 +124,16 @@ export function configFromEnv(env) {
104
124
  "process's own inherited environment has no usable value for it.",
105
125
  );
106
126
  }
127
+ const agentId = (env.MNEMOM_HANDOFF_AGENT_ID ?? "").trim().toLowerCase();
128
+ if (agentId && !AGENT_ID_RE.test(agentId)) {
129
+ throw new Error("mnemom-handoff: MNEMOM_HANDOFF_AGENT_ID is not a full mnm-<uuid> agent id.");
130
+ }
107
131
  return {
108
132
  gatewayHost: env.MNEMOM_HANDOFF_GATEWAY_HOST.trim(),
109
133
  anthropicKey,
134
+ mnemomKey: resolveMnemomKey(keyEnvName, env) || undefined,
110
135
  agent: env.MNEMOM_HANDOFF_AGENT.trim(),
136
+ agentId: agentId || undefined,
111
137
  conversationId: env.MNEMOM_HANDOFF_CONVERSATION_ID.trim(),
112
138
  sessionConfigB64: env.MNEMOM_HANDOFF_SESSION_CONFIG?.trim() || undefined,
113
139
  };
@@ -115,7 +141,7 @@ export function configFromEnv(env) {
115
141
 
116
142
  /**
117
143
  * Build the fetch call. Exported so a test can assert the wire shape without mocking fetch.
118
- * @param {{gatewayHost: string, anthropicKey: string, agent: string, conversationId: string, sessionConfigB64?: string}} cfg
144
+ * @param {{gatewayHost: string, anthropicKey: string, mnemomKey?: string, agent: string, agentId?: string, conversationId: string, sessionConfigB64?: string}} cfg
119
145
  * @param {string} note
120
146
  */
121
147
  export function buildHandoffRequest(cfg, note) {
@@ -124,6 +150,8 @@ export function buildHandoffRequest(cfg, note) {
124
150
  "x-api-key": cfg.anthropicKey,
125
151
  "x-mnemom-agent": cfg.agent,
126
152
  };
153
+ if (cfg.mnemomKey) headers["x-mnemom-api-key"] = cfg.mnemomKey;
154
+ if (cfg.agentId) headers["x-mnemom-agent-id"] = cfg.agentId;
127
155
  if (cfg.sessionConfigB64) headers["x-mnemom-session-config"] = cfg.sessionConfigB64;
128
156
  return {
129
157
  url: `${cfg.gatewayHost.replace(/\/+$/, "")}${HANDOFF_PATH}`,
@@ -150,7 +178,7 @@ function extractGatewayError(body) {
150
178
  * Call the route and render the result. NEVER throws: a network failure, a non-2xx status, or
151
179
  * an unparseable body all come back as `{ok:false, message}` for the caller to surface as an
152
180
  * MCP tool error — the tool must never crash the server or the session over a bad response.
153
- * @param {{gatewayHost: string, anthropicKey: string, agent: string, conversationId: string, sessionConfigB64?: string}} cfg
181
+ * @param {{gatewayHost: string, anthropicKey: string, mnemomKey?: string, agent: string, agentId?: string, conversationId: string, sessionConfigB64?: string}} cfg
154
182
  * @param {string} note
155
183
  * @param {typeof fetch} fetchImpl
156
184
  */
package/dist/rc-proxy.mjs CHANGED
@@ -62,6 +62,9 @@
62
62
  // CONFIG (env, none secret)
63
63
  // MNEMOM_AGENT_RC_DOOR gateway door URL (required), e.g. https://gateway.mnemom.ai/anthropic
64
64
  // MNEMOM_AGENT_RC_AGENT x-mnemom-agent value (required)
65
+ // MNEMOM_AGENT_RC_AGENT_ID x-mnemom-agent-id value (optional; a full mnm-<uuid> pin — the
66
+ // gateway then serves exactly that agent or refuses; needs the
67
+ // Mnemom key on stdin line 2)
65
68
  // MNEMOM_AGENT_RC_CONVERSATION_ID x-mnemom-conversation-id value (required)
66
69
  // MNEMOM_AGENT_RC_CONTRACT_B64 base64 contract for x-mnemom-contract (optional)
67
70
  // MNEMOM_AGENT_RC_GOAL_MODE x-mnemom-goal-mode value (optional; `--implicit` — no contract,
@@ -111,6 +114,12 @@ try {
111
114
  }
112
115
  if (GATEWAY.protocol !== "https:") die("MNEMOM_AGENT_RC_DOOR must be https");
113
116
  const AGENT = env.MNEMOM_AGENT_RC_AGENT || die("MNEMOM_AGENT_RC_AGENT is required");
117
+ const AGENT_ID = (env.MNEMOM_AGENT_RC_AGENT_ID || "").trim().toLowerCase();
118
+ if (
119
+ AGENT_ID &&
120
+ !/^mnm-[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/.test(AGENT_ID)
121
+ )
122
+ die("MNEMOM_AGENT_RC_AGENT_ID must be a full mnm-<uuid> agent id");
114
123
  const CONV =
115
124
  env.MNEMOM_AGENT_RC_CONVERSATION_ID || die("MNEMOM_AGENT_RC_CONVERSATION_ID is required");
116
125
  const CONTRACT_B64 = env.MNEMOM_AGENT_RC_CONTRACT_B64 || "";
@@ -168,6 +177,8 @@ try {
168
177
  } catch {
169
178
  die("expected three lines of secrets on stdin (anthropic, mnemom, openai)");
170
179
  }
180
+ if (AGENT_ID && !secrets.mnemom)
181
+ die("a pinned agent (MNEMOM_AGENT_RC_AGENT_ID) needs the Mnemom key (stdin line 2)");
171
182
  if (SUBSCRIPTION) {
172
183
  if (!secrets.mnemom) die("subscription login needs the Mnemom key (stdin line 2)");
173
184
  } else if (!secrets.anthropic) die("the Anthropic key (stdin line 1) is empty");
@@ -519,6 +530,9 @@ function handle(req, res, viaPlain) {
519
530
  headers["x-api-key"] = secrets.anthropic;
520
531
  }
521
532
  headers["x-mnemom-agent"] = AGENT;
533
+ // The pin is this proxy's to state: a client-supplied one is dropped, like x-mnemom-turn.
534
+ delete headers["x-mnemom-agent-id"];
535
+ if (AGENT_ID) headers["x-mnemom-agent-id"] = AGENT_ID;
522
536
  headers["x-mnemom-conversation-id"] = CONV;
523
537
  if (secrets.mnemom) headers["x-mnemom-api-key"] = secrets.mnemom;
524
538
  if (PROVIDER) headers["x-mnemom-provider"] = PROVIDER;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mnemom/mnemom",
3
- "version": "0.17.24",
3
+ "version": "0.17.26",
4
4
  "description": "Transparent AI agent tracing",
5
5
  "type": "module",
6
6
  "bin": {