@mnemom/mnemom 0.16.1 → 0.17.0-next.0

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 (48) hide show
  1. package/README.md +1 -0
  2. package/dist/commands/agents.d.ts +14 -0
  3. package/dist/commands/agents.js +100 -2
  4. package/dist/commands/card.d.ts +43 -0
  5. package/dist/commands/card.js +153 -102
  6. package/dist/commands/code-config.d.ts +17 -0
  7. package/dist/commands/code-config.js +147 -0
  8. package/dist/commands/code-doctor.d.ts +18 -0
  9. package/dist/commands/code-doctor.js +138 -0
  10. package/dist/commands/code-setup.d.ts +97 -0
  11. package/dist/commands/code-setup.js +330 -0
  12. package/dist/commands/code.d.ts +133 -0
  13. package/dist/commands/code.js +661 -0
  14. package/dist/commands/logs.js +11 -1
  15. package/dist/commands/onboard.d.ts +59 -0
  16. package/dist/commands/onboard.js +395 -0
  17. package/dist/commands/org.d.ts +13 -0
  18. package/dist/commands/org.js +63 -2
  19. package/dist/commands/protection.d.ts +10 -0
  20. package/dist/commands/protection.js +109 -0
  21. package/dist/commands/status.js +5 -0
  22. package/dist/commands/try-me.js +16 -1
  23. package/dist/commands/usage.d.ts +35 -0
  24. package/dist/commands/usage.js +265 -0
  25. package/dist/commands/wrap.d.ts +28 -0
  26. package/dist/commands/wrap.js +331 -0
  27. package/dist/index.js +315 -7
  28. package/dist/lib/agent-config.d.ts +27 -0
  29. package/dist/lib/agent-config.js +86 -0
  30. package/dist/lib/api.d.ts +139 -1
  31. package/dist/lib/api.js +132 -183
  32. package/dist/lib/cli-config.d.ts +33 -0
  33. package/dist/lib/cli-config.js +70 -0
  34. package/dist/lib/code-config.d.ts +78 -0
  35. package/dist/lib/code-config.js +281 -0
  36. package/dist/lib/code.d.ts +154 -0
  37. package/dist/lib/code.js +252 -0
  38. package/dist/lib/config.d.ts +12 -0
  39. package/dist/lib/config.js +55 -3
  40. package/dist/lib/keyed-identity.d.ts +35 -0
  41. package/dist/lib/keyed-identity.js +363 -0
  42. package/dist/lib/protection-drift.d.ts +117 -0
  43. package/dist/lib/protection-drift.js +180 -0
  44. package/dist/lib/skills.js +25 -12
  45. package/dist/lib/version-gate.d.ts +37 -0
  46. package/dist/lib/version-gate.js +84 -0
  47. package/dist/rc-proxy.mjs +341 -0
  48. package/package.json +9 -7
@@ -0,0 +1,331 @@
1
+ /**
2
+ * `mnemom wrap` — instrument an existing production agent through the Mnemom gateway (MNE-935, A4).
3
+ *
4
+ * Asks the developer for their provider + framework + agent name, makes a birth
5
+ * call to the gateway to provision the agent identity, seeds starter alignment +
6
+ * protection cards, and emits a drop-in code example. No reasoning change to the
7
+ * host agent.
8
+ *
9
+ * AUTH MODEL: Two credentials are involved:
10
+ * - Mnemom auth (MNEMOM_API_KEY or `mnemom login` JWT) — used for card writes
11
+ * - Provider API key (ANTHROPIC_API_KEY / OPENAI_API_KEY / GOOGLE_API_KEY) — used
12
+ * for the one-time gateway birth call; this is the same key the user's agent
13
+ * already holds, so the birth requires no new credential
14
+ *
15
+ * See SKILL-RUNNER-CONTRACT.md §2–§4 for the lifecycle + output contract.
16
+ */
17
+ import { requireAuth } from "../lib/auth.js";
18
+ import { putAlignmentCard, putProtectionCard, MnemomApiError } from "../lib/api.js";
19
+ import { mergeAgentConfig } from "../lib/agent-config.js";
20
+ import { askSelect, askInput, isInteractive } from "../lib/prompt.js";
21
+ import { getGatewayUrl } from "../lib/config.js";
22
+ import { fmt } from "../lib/format.js";
23
+ const AGENT_HEADER = "x-mnemom-agent";
24
+ const PROVIDERS = {
25
+ anthropic: {
26
+ label: "Anthropic",
27
+ keyEnv: "ANTHROPIC_API_KEY",
28
+ keyHeader: "x-api-key",
29
+ defaultModel: "claude-haiku-4-5-20251001",
30
+ basePath: "/anthropic",
31
+ },
32
+ openai: {
33
+ label: "OpenAI",
34
+ keyEnv: "OPENAI_API_KEY",
35
+ keyHeader: "Authorization",
36
+ defaultModel: "gpt-4o-mini",
37
+ basePath: "/openai",
38
+ },
39
+ gemini: {
40
+ label: "Google Gemini",
41
+ keyEnv: "GOOGLE_API_KEY",
42
+ keyHeader: "x-goog-api-key",
43
+ defaultModel: "gemini-2.0-flash",
44
+ basePath: "/gemini",
45
+ },
46
+ };
47
+ export async function wrapCommand(options = {}) {
48
+ const json = !!options.json;
49
+ const nonInteractive = !!options.yes || json || !isInteractive();
50
+ const steps = [];
51
+ const say = (line = "") => {
52
+ if (!json)
53
+ console.log(line);
54
+ };
55
+ say(fmt.header("Mnemom — wrap"));
56
+ say();
57
+ say(fmt.dim("Instrument your existing agent through the Mnemom gateway."));
58
+ say();
59
+ // Phase 1 — Resolve + Auth (Mnemom session for card writes)
60
+ await requireAuth();
61
+ const gatewayUrl = getGatewayUrl().replace(/\/$/, "");
62
+ const provider = await resolveProvider(options, nonInteractive);
63
+ const framework = await resolveFramework(options, nonInteractive);
64
+ const agentName = await resolveAgentName(options, nonInteractive);
65
+ const providerKey = await resolveProviderKey(options, PROVIDERS[provider], nonInteractive, say);
66
+ steps.push({
67
+ step: "resolve",
68
+ status: "ok",
69
+ detail: `provider=${provider} framework=${framework} name=${agentName}`,
70
+ });
71
+ // Phase 2 — Birth: one gateway call to provision the identity
72
+ say();
73
+ say(`${fmt.badge("birth", "cyan")} Provisioning ${fmt.badge(agentName, "magenta")} through the gateway…`);
74
+ let agentId;
75
+ try {
76
+ agentId = await birthThroughGateway(gatewayUrl, provider, agentName, providerKey);
77
+ steps.push({ step: "birth", status: "ok", detail: agentId });
78
+ say(fmt.success(`Provisioned → ${agentId}`));
79
+ }
80
+ catch (err) {
81
+ const msg = err instanceof Error ? err.message : String(err);
82
+ steps.push({ step: "birth", status: "error", detail: msg });
83
+ if (json) {
84
+ console.log(JSON.stringify({
85
+ skill: "wrap",
86
+ verdict: "error",
87
+ agent_id: null,
88
+ gateway_base_url: gatewayUrl,
89
+ next_step: null,
90
+ steps,
91
+ }, null, 2));
92
+ }
93
+ throw new Error(`Gateway birth failed: ${msg}`, { cause: err });
94
+ }
95
+ mergeAgentConfig({ agent_id: agentId, agent_name: agentName, gateway_url: gatewayUrl });
96
+ // Phase 2 — Seed alignment card
97
+ say();
98
+ say(`${fmt.badge("alignment", "cyan")} Seeding starter alignment card…`);
99
+ try {
100
+ await putAlignmentCard(agentId, JSON.stringify(buildStarterAlignmentCard(agentId)), "application/json");
101
+ steps.push({ step: "alignment", status: "ok" });
102
+ say(fmt.success("Alignment card set."));
103
+ }
104
+ catch (err) {
105
+ const msg = err instanceof Error ? err.message : String(err);
106
+ steps.push({ step: "alignment", status: "error", detail: msg });
107
+ say(fmt.warn(`Alignment card skipped — set it later with \`mnemom card publish\`. (${msg})`));
108
+ }
109
+ // Phase 2 — Seed protection card
110
+ say();
111
+ say(`${fmt.badge("protection", "cyan")} Seeding starter protection card…`);
112
+ try {
113
+ await putProtectionCard(agentId, JSON.stringify(buildStarterProtectionCard(agentId)), "application/json");
114
+ steps.push({ step: "protection", status: "ok" });
115
+ say(fmt.success("Protection card set."));
116
+ }
117
+ catch (err) {
118
+ const msg = err instanceof Error ? err.message : String(err);
119
+ steps.push({ step: "protection", status: "error", detail: msg });
120
+ say(fmt.warn(`Protection card skipped — set it later with \`mnemom protection publish\`. (${msg})`));
121
+ }
122
+ // Phase 3 — Report
123
+ say();
124
+ say(renderCodeExample(framework, provider, gatewayUrl, agentName));
125
+ say();
126
+ say(renderClaimInstructions(agentId));
127
+ if (json) {
128
+ console.log(JSON.stringify({
129
+ skill: "wrap",
130
+ verdict: "ok",
131
+ agent_id: agentId,
132
+ gateway_base_url: gatewayUrl,
133
+ next_step: "claim",
134
+ steps,
135
+ }, null, 2));
136
+ }
137
+ }
138
+ // ── Phase 1 resolvers ─────────────────────────────────────────────────────────
139
+ async function resolveProvider(options, nonInteractive) {
140
+ if (options.provider) {
141
+ const p = options.provider.toLowerCase();
142
+ if (!PROVIDERS[p]) {
143
+ throw new Error(`Unknown provider "${options.provider}". Choose: anthropic, openai, gemini`);
144
+ }
145
+ return p;
146
+ }
147
+ if (nonInteractive)
148
+ return "anthropic";
149
+ const choice = await askSelect("Which AI provider does your agent use?", [
150
+ "Anthropic (Claude)",
151
+ "OpenAI (GPT)",
152
+ "Google Gemini",
153
+ ]);
154
+ if (choice?.startsWith("OpenAI"))
155
+ return "openai";
156
+ if (choice?.startsWith("Google"))
157
+ return "gemini";
158
+ return "anthropic";
159
+ }
160
+ async function resolveFramework(options, nonInteractive) {
161
+ if (options.framework) {
162
+ const f = options.framework.toLowerCase();
163
+ if (f === "python" || f === "py")
164
+ return "python";
165
+ if (f === "node" || f === "nodejs" || f === "js" || f === "ts" || f === "typescript")
166
+ return "node";
167
+ throw new Error(`Unknown framework "${options.framework}". Choose: python, node`);
168
+ }
169
+ if (nonInteractive)
170
+ return "python";
171
+ const choice = await askSelect("Which language/framework is your agent using?", [
172
+ "Python",
173
+ "Node.js / TypeScript",
174
+ ]);
175
+ return choice?.startsWith("Node") ? "node" : "python";
176
+ }
177
+ async function resolveAgentName(options, nonInteractive) {
178
+ if (options.name?.trim())
179
+ return options.name.trim();
180
+ if (nonInteractive)
181
+ return "my-agent";
182
+ const name = (await askInput("What should we call this agent? (x-mnemom-agent value)")).trim();
183
+ return name || "my-agent";
184
+ }
185
+ async function resolveProviderKey(options, prov, nonInteractive, say) {
186
+ if (options.providerKey?.trim())
187
+ return options.providerKey.trim();
188
+ const fromEnv = process.env[prov.keyEnv]?.trim();
189
+ if (fromEnv) {
190
+ say(fmt.dim(`Using ${prov.keyEnv} from environment.`));
191
+ return fromEnv;
192
+ }
193
+ if (nonInteractive) {
194
+ throw new Error(`No ${prov.label} API key found. Set ${prov.keyEnv} or pass --provider-key <key>.`);
195
+ }
196
+ const key = (await askInput(`Enter your ${prov.label} API key (one-time birth call — never stored):`)).trim();
197
+ if (!key)
198
+ throw new Error(`A ${prov.label} API key is required to birth the agent.`);
199
+ return key;
200
+ }
201
+ // ── Birth call ────────────────────────────────────────────────────────────────
202
+ export async function birthThroughGateway(gatewayUrl, provider, agentName, providerKey) {
203
+ const prov = PROVIDERS[provider];
204
+ const authValue = provider === "openai" ? `Bearer ${providerKey}` : providerKey;
205
+ const headers = {
206
+ "Content-Type": "application/json",
207
+ Accept: "application/json",
208
+ [prov.keyHeader]: authValue,
209
+ [AGENT_HEADER]: agentName,
210
+ };
211
+ if (provider === "anthropic") {
212
+ headers["anthropic-version"] = "2023-06-01";
213
+ }
214
+ let url;
215
+ let body;
216
+ switch (provider) {
217
+ case "openai":
218
+ url = `${gatewayUrl}${prov.basePath}/v1/chat/completions`;
219
+ body = JSON.stringify({
220
+ model: prov.defaultModel,
221
+ max_tokens: 16,
222
+ messages: [{ role: "user", content: "hello" }],
223
+ });
224
+ break;
225
+ case "gemini":
226
+ url = `${gatewayUrl}${prov.basePath}/v1beta/models/${prov.defaultModel}:generateContent`;
227
+ body = JSON.stringify({ contents: [{ parts: [{ text: "hello" }] }] });
228
+ break;
229
+ case "anthropic":
230
+ default:
231
+ url = `${gatewayUrl}${prov.basePath}/v1/messages`;
232
+ body = JSON.stringify({
233
+ model: prov.defaultModel,
234
+ max_tokens: 16,
235
+ messages: [{ role: "user", content: "hello" }],
236
+ });
237
+ }
238
+ const response = await fetch(url, { method: "POST", headers, body });
239
+ const agentId = response.headers.get(AGENT_HEADER);
240
+ if (!response.ok && !agentId) {
241
+ const text = await response.text().catch(() => "");
242
+ throw new MnemomApiError(response.status, `Gateway returned ${response.status}: ${text.slice(0, 300)}`);
243
+ }
244
+ if (!agentId || !/^mnm-/.test(agentId)) {
245
+ throw new Error(`Gateway returned no mnm- agent id on the ${AGENT_HEADER} response header (status ${response.status}).`);
246
+ }
247
+ return agentId;
248
+ }
249
+ // ── Starter cards ─────────────────────────────────────────────────────────────
250
+ function buildStarterAlignmentCard(agentId) {
251
+ return {
252
+ card_version: "unified/2026-04-26",
253
+ agent_id: agentId,
254
+ autonomy_mode: "observe",
255
+ integrity_mode: "observe",
256
+ principal: { type: "agent", identifier: agentId, relationship: "delegated_authority" },
257
+ values: { declared: ["transparency", "safety", "honesty"] },
258
+ autonomy: {
259
+ bounded_actions: ["respond_to_prompts"],
260
+ forbidden_actions: [],
261
+ escalation_triggers: [],
262
+ },
263
+ audit: { retention_days: 30, queryable: false, trace_format: "otel" },
264
+ };
265
+ }
266
+ function buildStarterProtectionCard(agentId) {
267
+ return {
268
+ card_version: "protection/2026-04-26",
269
+ agent_id: agentId,
270
+ mode: "observe",
271
+ thresholds: { warn: 0.3, quarantine: 0.6, block: 0.9 },
272
+ screen_surfaces: {
273
+ incoming: true,
274
+ outgoing: true,
275
+ tool_calls: true,
276
+ tool_responses: true,
277
+ },
278
+ trusted_sources: { domains: [], agent_ids: [], ip_ranges: [] },
279
+ };
280
+ }
281
+ // ── Code example rendering ────────────────────────────────────────────────────
282
+ function renderCodeExample(framework, provider, gatewayUrl, agentName) {
283
+ const prov = PROVIDERS[provider];
284
+ const lines = [fmt.section("Drop-in gateway wrapper"), ""];
285
+ if (framework === "python") {
286
+ renderPythonSnippet(lines, provider, prov, gatewayUrl, agentName);
287
+ }
288
+ else {
289
+ renderNodeSnippet(lines, provider, prov, gatewayUrl, agentName);
290
+ }
291
+ lines.push("", fmt.dim("No other changes needed — your agent logic is unchanged."));
292
+ return lines.join("\n");
293
+ }
294
+ function renderPythonSnippet(lines, provider, prov, gatewayUrl, agentName) {
295
+ const base = `${gatewayUrl}${prov.basePath}`;
296
+ if (provider === "anthropic") {
297
+ lines.push(" import anthropic", "", " client = anthropic.Anthropic(", ` base_url="${base}",`, ` default_headers={"x-mnemom-agent": "${agentName}"},`, " )");
298
+ }
299
+ else if (provider === "openai") {
300
+ lines.push(" from openai import OpenAI", "", " client = OpenAI(", ` base_url="${base}/v1",`, ` default_headers={"x-mnemom-agent": "${agentName}"},`, " )");
301
+ }
302
+ else {
303
+ lines.push(" # Google Gemini via Mnemom gateway", ` # Set base URL to: ${base}`, ` # Add header: x-mnemom-agent: ${agentName}`);
304
+ }
305
+ }
306
+ function renderNodeSnippet(lines, provider, prov, gatewayUrl, agentName) {
307
+ const base = `${gatewayUrl}${prov.basePath}`;
308
+ if (provider === "anthropic") {
309
+ lines.push(' import Anthropic from "@anthropic-ai/sdk";', "", " const client = new Anthropic({", ` baseURL: "${base}",`, ` defaultHeaders: { "x-mnemom-agent": "${agentName}" },`, " });");
310
+ }
311
+ else if (provider === "openai") {
312
+ lines.push(' import OpenAI from "openai";', "", " const client = new OpenAI({", ` baseURL: "${base}/v1",`, ` defaultHeaders: { "x-mnemom-agent": "${agentName}" },`, " });");
313
+ }
314
+ else {
315
+ lines.push(" // Google Gemini via Mnemom gateway", ` // Set base URL to: ${base}`, ` // Add header: "x-mnemom-agent": "${agentName}"`);
316
+ }
317
+ }
318
+ function renderClaimInstructions(agentId) {
319
+ const lines = [
320
+ fmt.section("Next step — claim this agent"),
321
+ "",
322
+ fmt.label(" Agent ID:", agentId),
323
+ "",
324
+ fmt.dim(" Claim this agent so you own its identity on the platform:"),
325
+ "",
326
+ ` mnemom agents claim ${agentId}`,
327
+ "",
328
+ fmt.dim(" After claiming, traces appear in `mnemom logs`."),
329
+ ];
330
+ return lines.join("\n");
331
+ }